ui: More component previews, UI component cleanup (#25302)
- Don't require ui component docs (this isn't really working) - Add more component previews - Update component preview style & navigation Release Notes: - N/A
This commit is contained in:
parent
c9235ff916
commit
5397ca23a1
52 changed files with 813 additions and 856 deletions
|
@ -2458,8 +2458,8 @@ impl CollabPanel {
|
|||
Avatar::new(contact.user.avatar_uri.clone())
|
||||
.indicator::<AvatarAvailabilityIndicator>(if online {
|
||||
Some(AvatarAvailabilityIndicator::new(match busy {
|
||||
true => ui::Availability::Busy,
|
||||
false => ui::Availability::Free,
|
||||
true => ui::CollaboratorAvailability::Busy,
|
||||
false => ui::CollaboratorAvailability::Free,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -173,9 +173,9 @@ pub enum ExampleLabelSide {
|
|||
Left,
|
||||
/// Right side
|
||||
Right,
|
||||
#[default]
|
||||
/// Top side
|
||||
Top,
|
||||
#[default]
|
||||
/// Bottom side
|
||||
Bottom,
|
||||
}
|
||||
|
@ -200,10 +200,10 @@ impl RenderOnce for ComponentExample {
|
|||
ExampleLabelSide::Top => base.flex_col_reverse(),
|
||||
};
|
||||
|
||||
base.gap_1()
|
||||
base.gap_2()
|
||||
.p_2()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.flex_1())
|
||||
.child(self.element)
|
||||
.child(self.variant_name)
|
||||
|
@ -245,12 +245,13 @@ impl RenderOnce for ComponentExampleGroup {
|
|||
.text_color(cx.theme().colors().text_muted)
|
||||
.when(self.grow, |this| this.w_full().flex_1())
|
||||
.when_some(self.title, |this, title| {
|
||||
this.gap_4().pb_5().child(
|
||||
this.gap_4().child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(div().h_px().w_4().bg(cx.theme().colors().border_variant))
|
||||
.pb_1()
|
||||
.child(div().h_px().w_4().bg(cx.theme().colors().border))
|
||||
.child(
|
||||
div()
|
||||
.flex_none()
|
||||
|
@ -271,7 +272,7 @@ impl RenderOnce for ComponentExampleGroup {
|
|||
.flex()
|
||||
.items_start()
|
||||
.w_full()
|
||||
.gap_8()
|
||||
.gap_6()
|
||||
.children(self.examples)
|
||||
.into_any_element(),
|
||||
)
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
//! A view for exploring Zed components.
|
||||
|
||||
use component::{components, ComponentMetadata};
|
||||
use gpui::{prelude::*, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||
use ui::prelude::*;
|
||||
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
||||
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
||||
use ui::{prelude::*, ListItem};
|
||||
|
||||
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
||||
|
||||
|
@ -12,7 +13,7 @@ pub fn init(cx: &mut App) {
|
|||
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
||||
workspace.register_action(
|
||||
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
||||
let component_preview = cx.new(ComponentPreview::new);
|
||||
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(component_preview),
|
||||
None,
|
||||
|
@ -28,124 +29,161 @@ pub fn init(cx: &mut App) {
|
|||
|
||||
struct ComponentPreview {
|
||||
focus_handle: FocusHandle,
|
||||
_view_scroll_handle: ScrollHandle,
|
||||
nav_scroll_handle: UniformListScrollHandle,
|
||||
components: Vec<ComponentMetadata>,
|
||||
component_list: ListState,
|
||||
selected_index: usize,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let components = components().all_sorted();
|
||||
let initial_length = components.len();
|
||||
|
||||
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
|
||||
let this = cx.entity().downgrade();
|
||||
move |ix, window: &mut Window, cx: &mut App| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.render_preview(ix, window, cx).into_any_element()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
_view_scroll_handle: ScrollHandle::new(),
|
||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||
components,
|
||||
component_list,
|
||||
selected_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn render_sidebar(&self, _window: &Window, _cx: &Context<Self>) -> impl IntoElement {
|
||||
let components = components().all_sorted();
|
||||
let sorted_components = components.clone();
|
||||
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
||||
self.component_list.scroll_to_reveal_item(ix);
|
||||
self.selected_index = ix;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
v_flex()
|
||||
.max_w_48()
|
||||
.gap_px()
|
||||
.p_1()
|
||||
.children(
|
||||
sorted_components
|
||||
.into_iter()
|
||||
.map(|component| self.render_sidebar_entry(&component, _cx)),
|
||||
)
|
||||
.child(
|
||||
Label::new("These will be clickable once the layout is moved to a gpui::List.")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::XSmall)
|
||||
.italic(),
|
||||
)
|
||||
fn get_component(&self, ix: usize) -> ComponentMetadata {
|
||||
self.components[ix].clone()
|
||||
}
|
||||
|
||||
fn render_sidebar_entry(
|
||||
&self,
|
||||
component: &ComponentMetadata,
|
||||
_cx: &Context<Self>,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
h_flex()
|
||||
.w_40()
|
||||
.px_1p5()
|
||||
.py_0p5()
|
||||
.text_sm()
|
||||
.child(component.name().clone())
|
||||
let component = self.get_component(ix);
|
||||
|
||||
ListItem::new(ix)
|
||||
.child(Label::new(component.name().clone()).color(Color::Default))
|
||||
.selectable(true)
|
||||
.toggle_state(selected)
|
||||
.inset(true)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.scroll_to_preview(ix, cx);
|
||||
}))
|
||||
}
|
||||
|
||||
fn render_preview(
|
||||
&self,
|
||||
component: &ComponentMetadata,
|
||||
ix: usize,
|
||||
window: &mut Window,
|
||||
cx: &Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let component = self.get_component(ix);
|
||||
|
||||
let name = component.name();
|
||||
let scope = component.scope();
|
||||
|
||||
let description = component.description();
|
||||
|
||||
v_flex()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.w_full()
|
||||
.gap_3()
|
||||
.py_6()
|
||||
.py_2()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_md()
|
||||
.w_full()
|
||||
.gap_4()
|
||||
.py_4()
|
||||
.px_6()
|
||||
.flex_none()
|
||||
.child(
|
||||
h_flex()
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.text_2xl()
|
||||
.child(div().child(name))
|
||||
.when_some(scope, |this, scope| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_xl()
|
||||
.child(div().child(name))
|
||||
.when_some(scope, |this, scope| {
|
||||
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(description, |this, description| {
|
||||
this.child(
|
||||
div()
|
||||
.text_ui_sm(cx)
|
||||
.text_color(cx.theme().colors().text_muted)
|
||||
.max_w(px(600.0))
|
||||
.child(description),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.child(preview(window, cx))
|
||||
}),
|
||||
)
|
||||
.when_some(component.preview(), |this, preview| {
|
||||
this.child(preview(window, cx))
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_previews(&self, window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.id("component-previews")
|
||||
.size_full()
|
||||
.overflow_y_scroll()
|
||||
.p_4()
|
||||
.gap_4()
|
||||
.children(
|
||||
components()
|
||||
.all_previews_sorted()
|
||||
.iter()
|
||||
.map(|component| self.render_preview(component, window, cx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ComponentPreview {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.id("component-preview")
|
||||
.key_context("ComponentPreview")
|
||||
.items_start()
|
||||
.overflow_hidden()
|
||||
.size_full()
|
||||
.max_h_full()
|
||||
.track_focus(&self.focus_handle)
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(self.render_sidebar(window, cx))
|
||||
.child(self.render_previews(window, cx))
|
||||
.child(
|
||||
uniform_list(
|
||||
cx.entity().clone(),
|
||||
"component-nav",
|
||||
self.components.len(),
|
||||
move |this, range, _window, cx| {
|
||||
range
|
||||
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(self.nav_scroll_handle.clone())
|
||||
.pt_4()
|
||||
.w(px(240.))
|
||||
.h_full()
|
||||
.flex_grow(),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.id("component-list")
|
||||
.px_8()
|
||||
.pt_4()
|
||||
.size_full()
|
||||
.child(
|
||||
list(self.component_list.clone())
|
||||
.flex_grow()
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,13 +213,13 @@ impl Item for ComponentPreview {
|
|||
fn clone_on_split(
|
||||
&self,
|
||||
_workspace_id: Option<WorkspaceId>,
|
||||
_window: &mut Window,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<gpui::Entity<Self>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Some(cx.new(Self::new))
|
||||
Some(cx.new(|cx| Self::new(window, cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||
|
|
|
@ -14,8 +14,6 @@ use ui::prelude::*;
|
|||
pub enum ComponentStory {
|
||||
ApplicationMenu,
|
||||
AutoHeightEditor,
|
||||
Avatar,
|
||||
Button,
|
||||
CollabNotification,
|
||||
ContextMenu,
|
||||
Cursor,
|
||||
|
@ -47,8 +45,6 @@ impl ComponentStory {
|
|||
.new(|cx| title_bar::ApplicationMenuStory::new(window, cx))
|
||||
.into(),
|
||||
Self::AutoHeightEditor => AutoHeightEditorStory::new(window, cx).into(),
|
||||
Self::Avatar => cx.new(|_| ui::AvatarStory).into(),
|
||||
Self::Button => cx.new(|_| ui::ButtonStory).into(),
|
||||
Self::CollabNotification => cx
|
||||
.new(|_| collab_ui::notifications::CollabNotificationStory)
|
||||
.into(),
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# Building UI with GPUI
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Method ordering
|
||||
|
||||
- id
|
||||
- Flex properties
|
||||
- Position properties
|
||||
- Size properties
|
||||
- Style properties
|
||||
- Handlers
|
||||
- State properties
|
||||
|
||||
### Using the Label Component to Create UI Text
|
||||
|
||||
The `Label` component helps in displaying text on user interfaces. It creates an interface where specific parameters such as label color, line height style, and strikethrough can be set.
|
||||
|
||||
Firstly, to create a `Label` instance, use the `Label::new()` function. This function takes a string that will be displayed as text in the interface.
|
||||
|
||||
```rust
|
||||
Label::new("Hello, world!");
|
||||
```
|
||||
|
||||
Now let's dive a bit deeper into how to customize `Label` instances:
|
||||
|
||||
- **Setting Color:** To set the color of the label using various predefined color options such as `Default`, `Muted`, `Created`, `Modified`, `Deleted`, etc, the `color()` function is called on the `Label` instance:
|
||||
|
||||
```rust
|
||||
Label::new("Hello, world!").color(LabelColor::Default);
|
||||
```
|
||||
|
||||
- **Setting Line Height Style:** To set the line height style, the `line_height_style()` function is utilized:
|
||||
|
||||
```rust
|
||||
Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel);
|
||||
```
|
||||
|
||||
- **Adding a Strikethrough:** To add a strikethrough in a `Label`, the `set_strikethrough()` function is used:
|
||||
|
||||
```rust
|
||||
Label::new("Hello, world!").set_strikethrough(true);
|
||||
```
|
||||
|
||||
That's it! Now you can use the `Label` component to create and customize text on your application's interface.
|
||||
|
||||
## Building a new component
|
||||
|
||||
TODO
|
|
@ -1,160 +0,0 @@
|
|||
# Hello World
|
||||
|
||||
Let's work through the prototypical "Build a todo app" example to showcase how we might build a simple component from scratch.
|
||||
|
||||
## Setup
|
||||
|
||||
We'll create a headline, a list of todo items, and a form to add new items.
|
||||
|
||||
~~~rust
|
||||
struct TodoList<V: 'static> {
|
||||
headline: SharedString,
|
||||
items: Vec<TodoItem>,
|
||||
submit_form: ClickHandler<V>
|
||||
}
|
||||
|
||||
struct TodoItem<V: 'static> {
|
||||
text: SharedString,
|
||||
completed: bool,
|
||||
delete: ClickHandler<V>
|
||||
}
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
pub fn new(
|
||||
// Here we impl Into<SharedString>
|
||||
headline: impl Into<SharedString>,
|
||||
items: Vec<TodoItem>,
|
||||
submit_form: ClickHandler<V>
|
||||
) -> Self {
|
||||
Self {
|
||||
// and here we call .into() so we can simply pass a string
|
||||
// when creating the headline. This pattern is used throughout
|
||||
// outr components
|
||||
headline: headline.into(),
|
||||
items: Vec::new(),
|
||||
submit_form,
|
||||
}
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
All of this is relatively straightforward.
|
||||
|
||||
We use [gpui::SharedString] in components instead of [std::string::String]. This allows us to efficiently handle shared string data across multiple components and threads without the performance overhead of copying strings.
|
||||
|
||||
When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`.
|
||||
|
||||
~~~rust
|
||||
use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0))
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen.
|
||||
|
||||
A couple of questions might come to mind:
|
||||
|
||||
**Why is `size_4()` 16px, not 4px?**
|
||||
|
||||
gpui's style system is based on conventions created by [Tailwind CSS](https://tailwindcss.com/). Here is an example of the list of sizes for `width`: [Width - TailwindCSS Docs](https://tailwindcss.com/docs/width).
|
||||
|
||||
I'll quote from the Tailwind [Core Concepts](https://tailwindcss.com/docs/utility-first) docs here:
|
||||
|
||||
> Now I know what you’re thinking, “this is an atrocity, what a horrible mess!”
|
||||
> and you’re right, it’s kind of ugly. In fact it’s just about impossible to
|
||||
> think this is a good idea the first time you see it —
|
||||
> you have to actually try it.
|
||||
|
||||
As you start using the Tailwind-style conventions you will be surprised how quick it makes it to build out UIs.
|
||||
|
||||
**Why `50.0/360.0` in `hsla()`?**
|
||||
|
||||
gpui [gpui::Hsla] use `0.0-1.0` for all its values, but it is common for tools to use `0-360` for hue.
|
||||
|
||||
This may change in the future, but this is a little trick that let's you use familiar looking values.
|
||||
|
||||
## Building out the container
|
||||
|
||||
Let's grab our [theme::colors::ThemeColors] from the theme and start building out a basic container.
|
||||
|
||||
We can access the current theme's colors like this:
|
||||
|
||||
~~~rust
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0)
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Now we have access to the complete set of colors defined in the theme.
|
||||
|
||||
~~~rust
|
||||
use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div().size_4().bg(color.surface)
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Let's finish up some basic styles for the container then move on to adding the other elements.
|
||||
|
||||
~~~rust
|
||||
use gpui::hsla
|
||||
|
||||
impl<V: 'static> TodoList<V> {
|
||||
// ...
|
||||
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
let color = cx.theme().colors()
|
||||
|
||||
div()
|
||||
// Flex properties
|
||||
.flex()
|
||||
.flex_col() // Stack elements vertically
|
||||
.gap_2() // Add 8px of space between elements
|
||||
// Size properties
|
||||
.w_96() // Set width to 384px
|
||||
.p_4() // Add 16px of padding on all sides
|
||||
// Color properties
|
||||
.bg(color.surface) // Set background color
|
||||
.text_color(color.text) // Set text color
|
||||
// Border properties
|
||||
.rounded_md() // Add 4px of border radius
|
||||
.border_1() // Add a 1px border
|
||||
.border_color(color.border)
|
||||
.child(
|
||||
"Hello, world!"
|
||||
)
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
### Headline
|
||||
|
||||
TODO
|
||||
|
||||
### List of todo items
|
||||
|
||||
TODO
|
||||
|
||||
### Input
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
### End result
|
||||
|
||||
TODO
|
|
@ -1,7 +1,302 @@
|
|||
mod avatar;
|
||||
mod avatar_audio_status_indicator;
|
||||
mod avatar_availability_indicator;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use avatar_audio_status_indicator::*;
|
||||
pub use avatar_availability_indicator::*;
|
||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
|
||||
/// An element that renders a user avatar with customizable appearance options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png")
|
||||
/// .shape(AvatarShape::Circle)
|
||||
/// .grayscale(true)
|
||||
/// .border_color(gpui::red());
|
||||
/// ```
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
pub struct Avatar {
|
||||
image: Img,
|
||||
size: Option<AbsoluteLength>,
|
||||
border_color: Option<Hsla>,
|
||||
indicator: Option<AnyElement>,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
/// Creates a new avatar element with the specified image source.
|
||||
pub fn new(src: impl Into<ImageSource>) -> Self {
|
||||
Avatar {
|
||||
image: img(src),
|
||||
size: None,
|
||||
border_color: None,
|
||||
indicator: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies a grayscale filter to the avatar image.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
||||
/// ```
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.image = self.image.grayscale(grayscale);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the border color of the avatar.
|
||||
///
|
||||
/// This might be used to match the border to the background color of
|
||||
/// the parent element to create the illusion of cropping another
|
||||
/// shape underneath (for example in face piles.)
|
||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Size overrides the avatar size. By default they are 1rem.
|
||||
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
||||
self.size = size.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the current indicator to be displayed on the avatar, if any.
|
||||
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
||||
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let border_width = if self.border_color.is_some() {
|
||||
px(2.)
|
||||
} else {
|
||||
px(0.)
|
||||
};
|
||||
|
||||
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
||||
let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.;
|
||||
|
||||
div()
|
||||
.size(container_size)
|
||||
.rounded_full()
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border(border_width).border_color(color)
|
||||
})
|
||||
.child(
|
||||
self.image
|
||||
.size(image_size)
|
||||
.rounded_full()
|
||||
.bg(cx.theme().colors().ghost_element_background),
|
||||
)
|
||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||
}
|
||||
}
|
||||
|
||||
use gpui::AnyView;
|
||||
|
||||
/// The audio status of an player, for use in representing
|
||||
/// their status visually on their avatar.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum AudioStatus {
|
||||
/// The player's microphone is muted.
|
||||
Muted,
|
||||
/// The player's microphone is muted, and collaboration audio is disabled.
|
||||
Deafened,
|
||||
}
|
||||
|
||||
/// An indicator that shows the audio status of a player.
|
||||
#[derive(IntoElement)]
|
||||
pub struct AvatarAudioStatusIndicator {
|
||||
audio_status: AudioStatus,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
}
|
||||
|
||||
impl AvatarAudioStatusIndicator {
|
||||
/// Creates a new `AvatarAudioStatusIndicator`
|
||||
pub fn new(audio_status: AudioStatus) -> Self {
|
||||
Self {
|
||||
audio_status,
|
||||
tooltip: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tooltip for the indicator.
|
||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for AvatarAudioStatusIndicator {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let icon_size = IconSize::Indicator;
|
||||
|
||||
let width_in_px = icon_size.rems() * window.rem_size();
|
||||
let padding_x = px(4.);
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.bottom(rems_from_px(-3.))
|
||||
.right(rems_from_px(-6.))
|
||||
.w(width_in_px + padding_x)
|
||||
.h(icon_size.rems())
|
||||
.child(
|
||||
h_flex()
|
||||
.id("muted-indicator")
|
||||
.justify_center()
|
||||
.px(padding_x)
|
||||
.py(px(2.))
|
||||
.bg(cx.theme().status().error_background)
|
||||
.rounded_md()
|
||||
.child(
|
||||
Icon::new(match self.audio_status {
|
||||
AudioStatus::Muted => IconName::MicMute,
|
||||
AudioStatus::Deafened => IconName::AudioOff,
|
||||
})
|
||||
.size(icon_size)
|
||||
.color(Color::Error),
|
||||
)
|
||||
.when_some(self.tooltip, |this, tooltip| {
|
||||
this.tooltip(move |window, cx| tooltip(window, cx))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the availability status of a collaborator.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum CollaboratorAvailability {
|
||||
Free,
|
||||
Busy,
|
||||
}
|
||||
|
||||
/// Represents the availability and presence status of a collaborator.
|
||||
#[derive(IntoElement)]
|
||||
pub struct AvatarAvailabilityIndicator {
|
||||
availability: CollaboratorAvailability,
|
||||
avatar_size: Option<Pixels>,
|
||||
}
|
||||
|
||||
impl AvatarAvailabilityIndicator {
|
||||
/// Creates a new indicator
|
||||
pub fn new(availability: CollaboratorAvailability) -> Self {
|
||||
Self {
|
||||
availability,
|
||||
avatar_size: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on.
|
||||
pub fn avatar_size(mut self, size: impl Into<Option<Pixels>>) -> Self {
|
||||
self.avatar_size = size.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for AvatarAvailabilityIndicator {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size());
|
||||
|
||||
// HACK: non-integer sizes result in oval indicators.
|
||||
let indicator_size = (avatar_size * 0.4).round();
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.size(indicator_size)
|
||||
.rounded(indicator_size)
|
||||
.bg(match self.availability {
|
||||
CollaboratorAvailability::Free => cx.theme().status().created,
|
||||
CollaboratorAvailability::Busy => cx.theme().status().deleted,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
impl ComponentPreview for Avatar {
|
||||
fn preview(_window: &mut Window, cx: &App) -> 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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
use crate::{prelude::*, Indicator};
|
||||
|
||||
use gpui::{img, AnyElement, Hsla, ImageSource, Img, IntoElement, Styled};
|
||||
|
||||
/// An element that renders a user avatar with customizable appearance options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// Avatar::new("path/to/image.png")
|
||||
/// .shape(AvatarShape::Circle)
|
||||
/// .grayscale(true)
|
||||
/// .border_color(gpui::red());
|
||||
/// ```
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
pub struct Avatar {
|
||||
image: Img,
|
||||
size: Option<AbsoluteLength>,
|
||||
border_color: Option<Hsla>,
|
||||
indicator: Option<AnyElement>,
|
||||
}
|
||||
|
||||
impl Avatar {
|
||||
/// Creates a new avatar element with the specified image source.
|
||||
pub fn new(src: impl Into<ImageSource>) -> Self {
|
||||
Avatar {
|
||||
image: img(src),
|
||||
size: None,
|
||||
border_color: None,
|
||||
indicator: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies a grayscale filter to the avatar image.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ui::{Avatar, AvatarShape};
|
||||
///
|
||||
/// let avatar = Avatar::new("path/to/image.png").grayscale(true);
|
||||
/// ```
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.image = self.image.grayscale(grayscale);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the border color of the avatar.
|
||||
///
|
||||
/// This might be used to match the border to the background color of
|
||||
/// the parent element to create the illusion of cropping another
|
||||
/// shape underneath (for example in face piles.)
|
||||
pub fn border_color(mut self, color: impl Into<Hsla>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Size overrides the avatar size. By default they are 1rem.
|
||||
pub fn size<L: Into<AbsoluteLength>>(mut self, size: impl Into<Option<L>>) -> Self {
|
||||
self.size = size.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the current indicator to be displayed on the avatar, if any.
|
||||
pub fn indicator<E: IntoElement>(mut self, indicator: impl Into<Option<E>>) -> Self {
|
||||
self.indicator = indicator.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Avatar {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let border_width = if self.border_color.is_some() {
|
||||
px(2.)
|
||||
} else {
|
||||
px(0.)
|
||||
};
|
||||
|
||||
let image_size = self.size.unwrap_or_else(|| rems(1.).into());
|
||||
let container_size = image_size.to_pixels(window.rem_size()) + border_width * 2.;
|
||||
|
||||
div()
|
||||
.size(container_size)
|
||||
.rounded_full()
|
||||
.when_some(self.border_color, |this, color| {
|
||||
this.border(border_width).border_color(color)
|
||||
})
|
||||
.child(
|
||||
self.image
|
||||
.size(image_size)
|
||||
.rounded_full()
|
||||
.bg(cx.theme().colors().ghost_element_background),
|
||||
)
|
||||
.children(self.indicator.map(|indicator| div().child(indicator)))
|
||||
}
|
||||
}
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
impl ComponentPreview for Avatar {
|
||||
fn preview(_window: &mut Window, _cx: &App) -> 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("https://avatars.githubusercontent.com/u/1714999?v=4")
|
||||
.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(gpui::red())
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"With Indicator",
|
||||
vec![single_example(
|
||||
"Dot",
|
||||
Avatar::new(example_avatar)
|
||||
.indicator(Indicator::dot().color(Color::Success))
|
||||
.into_any_element(),
|
||||
)],
|
||||
),
|
||||
])
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
use gpui::AnyView;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
/// The audio status of an player, for use in representing
|
||||
/// their status visually on their avatar.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum AudioStatus {
|
||||
/// The player's microphone is muted.
|
||||
Muted,
|
||||
/// The player's microphone is muted, and collaboration audio is disabled.
|
||||
Deafened,
|
||||
}
|
||||
|
||||
/// An indicator that shows the audio status of a player.
|
||||
#[derive(IntoElement)]
|
||||
pub struct AvatarAudioStatusIndicator {
|
||||
audio_status: AudioStatus,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
}
|
||||
|
||||
impl AvatarAudioStatusIndicator {
|
||||
/// Creates a new `AvatarAudioStatusIndicator`
|
||||
pub fn new(audio_status: AudioStatus) -> Self {
|
||||
Self {
|
||||
audio_status,
|
||||
tooltip: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tooltip for the indicator.
|
||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for AvatarAudioStatusIndicator {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let icon_size = IconSize::Indicator;
|
||||
|
||||
let width_in_px = icon_size.rems() * window.rem_size();
|
||||
let padding_x = px(4.);
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.bottom(rems_from_px(-3.))
|
||||
.right(rems_from_px(-6.))
|
||||
.w(width_in_px + padding_x)
|
||||
.h(icon_size.rems())
|
||||
.child(
|
||||
h_flex()
|
||||
.id("muted-indicator")
|
||||
.justify_center()
|
||||
.px(padding_x)
|
||||
.py(px(2.))
|
||||
.bg(cx.theme().status().error_background)
|
||||
.rounded_md()
|
||||
.child(
|
||||
Icon::new(match self.audio_status {
|
||||
AudioStatus::Muted => IconName::MicMute,
|
||||
AudioStatus::Deafened => IconName::AudioOff,
|
||||
})
|
||||
.size(icon_size)
|
||||
.color(Color::Error),
|
||||
)
|
||||
.when_some(self.tooltip, |this, tooltip| {
|
||||
this.tooltip(move |window, cx| tooltip(window, cx))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub enum Availability {
|
||||
Free,
|
||||
Busy,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct AvatarAvailabilityIndicator {
|
||||
availability: Availability,
|
||||
avatar_size: Option<Pixels>,
|
||||
}
|
||||
|
||||
impl AvatarAvailabilityIndicator {
|
||||
pub fn new(availability: Availability) -> Self {
|
||||
Self {
|
||||
availability,
|
||||
avatar_size: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the [`Avatar`](crate::Avatar) this indicator appears on.
|
||||
pub fn avatar_size(mut self, size: impl Into<Option<Pixels>>) -> Self {
|
||||
self.avatar_size = size.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for AvatarAvailabilityIndicator {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let avatar_size = self.avatar_size.unwrap_or_else(|| window.rem_size());
|
||||
|
||||
// HACK: non-integer sizes result in oval indicators.
|
||||
let indicator_size = (avatar_size * 0.4).round();
|
||||
|
||||
div()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.right_0()
|
||||
.size(indicator_size)
|
||||
.rounded(indicator_size)
|
||||
.bg(match self.availability {
|
||||
Availability::Free => cx.theme().status().created,
|
||||
Availability::Busy => cx.theme().status().deleted,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use component::{example_group_with_title, single_example, ComponentPreview};
|
||||
use gpui::{AnyElement, AnyView, DefiniteLength};
|
||||
use ui_macros::IntoComponent;
|
||||
|
@ -81,6 +80,7 @@ use super::button_icon::ButtonIcon;
|
|||
/// ```
|
||||
///
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "input")]
|
||||
pub struct Button {
|
||||
base: ButtonLike,
|
||||
label: SharedString,
|
||||
|
@ -463,7 +463,7 @@ impl ComponentPreview for Button {
|
|||
.gap_6()
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Styles",
|
||||
"Button Styles",
|
||||
vec![
|
||||
single_example(
|
||||
"Default",
|
||||
|
@ -481,6 +481,12 @@ impl ComponentPreview for Button {
|
|||
.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")
|
||||
|
@ -490,7 +496,7 @@ impl ComponentPreview for Button {
|
|||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Tinted",
|
||||
"Tint Styles",
|
||||
vec![
|
||||
single_example(
|
||||
"Accent",
|
||||
|
@ -519,7 +525,7 @@ impl ComponentPreview for Button {
|
|||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"States",
|
||||
"Special States",
|
||||
vec![
|
||||
single_example(
|
||||
"Default",
|
||||
|
@ -540,7 +546,7 @@ impl ComponentPreview for Button {
|
|||
],
|
||||
),
|
||||
example_group_with_title(
|
||||
"With Icons",
|
||||
"Buttons with Icons",
|
||||
vec![
|
||||
single_example(
|
||||
"Icon Start",
|
||||
|
@ -563,16 +569,6 @@ impl ComponentPreview for Button {
|
|||
.icon_color(Color::Accent)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Tinted Icons",
|
||||
Button::new("tinted_icons", "Error")
|
||||
.style(ButtonStyle::Tinted(TintColor::Error))
|
||||
.color(Color::Error)
|
||||
.icon_color(Color::Error)
|
||||
.icon(IconName::Trash)
|
||||
.icon_position(IconPosition::Start)
|
||||
.into_any_element(),
|
||||
),
|
||||
],
|
||||
),
|
||||
])
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::{prelude::*, Icon, IconName, IconSize, IconWithIndicator, Indicator};
|
||||
use gpui::Hsla;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{relative, CursorStyle, DefiniteLength, MouseButton};
|
||||
use gpui::{transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems};
|
||||
use smallvec::SmallVec;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{AnyView, DefiniteLength, Hsla};
|
||||
|
||||
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
||||
use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton};
|
||||
use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton, TintColor};
|
||||
use crate::{IconName, IconSize};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
|
@ -14,7 +13,8 @@ pub enum IconButtonShape {
|
|||
Wide,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "input")]
|
||||
pub struct IconButton {
|
||||
base: ButtonLike,
|
||||
shape: IconButtonShape,
|
||||
|
@ -200,3 +200,160 @@ impl RenderOnce for IconButton {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for IconButton {
|
||||
fn preview(_window: &mut Window, _cx: &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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{AnyView, ClickEvent};
|
||||
|
||||
use crate::{prelude::*, ButtonLike, ButtonLikeRounding, ElevationIndex};
|
||||
|
@ -16,7 +15,8 @@ pub enum ToggleButtonPosition {
|
|||
Last,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "input")]
|
||||
pub struct ToggleButton {
|
||||
base: ButtonLike,
|
||||
position_in_group: Option<ToggleButtonPosition>,
|
||||
|
@ -142,3 +142,130 @@ impl RenderOnce for ToggleButton {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentPreview for ToggleButton {
|
||||
fn preview(_window: &mut Window, _cx: &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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::{
|
||||
h_flex, prelude::*, utils::WithRemSize, v_flex, Icon, IconName, IconSize, KeyBinding, Label,
|
||||
List, ListItem, ListSeparator, ListSubHeader,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{ClickEvent, CursorStyle};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{Hsla, IntoElement};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
|
||||
|
||||
use crate::{prelude::*, ContextMenu, PopoverMenu};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::prelude::*;
|
||||
use crate::{prelude::*, Avatar};
|
||||
use gpui::{AnyElement, StyleRefinement};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -7,7 +7,7 @@ use smallvec::SmallVec;
|
|||
///
|
||||
/// Facepiles are used to display a group of people or things,
|
||||
/// such as a list of participants in a collaboration session.
|
||||
#[derive(IntoElement)]
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
pub struct Facepile {
|
||||
base: Div,
|
||||
faces: SmallVec<[AnyElement; 2]>,
|
||||
|
@ -60,60 +60,57 @@ impl RenderOnce for Facepile {
|
|||
}
|
||||
}
|
||||
|
||||
// impl ComponentPreview for Facepile {
|
||||
// fn description() -> impl Into<Option<&'static str>> {
|
||||
// "A facepile is a collection of faces stacked horizontally–\
|
||||
// always with the leftmost face on top and descending in z-index.\
|
||||
// \n\nFacepiles are used to display a group of people or things,\
|
||||
// such as a list of participants in a collaboration session."
|
||||
// }
|
||||
// fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
|
||||
// let few_faces: [&'static str; 3] = [
|
||||
// "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
|
||||
// "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
|
||||
// "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
|
||||
// ];
|
||||
impl ComponentPreview for Facepile {
|
||||
fn preview(_window: &mut Window, _cx: &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",
|
||||
];
|
||||
|
||||
// let many_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",
|
||||
// ];
|
||||
|
||||
// vec![example_group_with_title(
|
||||
// "Examples",
|
||||
// vec![
|
||||
// single_example(
|
||||
// "Few Faces",
|
||||
// Facepile::new(
|
||||
// few_faces
|
||||
// .iter()
|
||||
// .map(|&url| Avatar::new(url).into_any_element())
|
||||
// .collect(),
|
||||
// ),
|
||||
// ),
|
||||
// single_example(
|
||||
// "Many Faces",
|
||||
// Facepile::new(
|
||||
// many_faces
|
||||
// .iter()
|
||||
// .map(|&url| Avatar::new(url).into_any_element())
|
||||
// .collect(),
|
||||
// ),
|
||||
// ),
|
||||
// single_example(
|
||||
// "Custom Size",
|
||||
// Facepile::new(
|
||||
// few_faces
|
||||
// .iter()
|
||||
// .map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
|
||||
// .collect(),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )]
|
||||
// }
|
||||
// }
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![
|
||||
example_group_with_title(
|
||||
"Facepile Examples",
|
||||
vec![
|
||||
single_example(
|
||||
"Default",
|
||||
Facepile::new(
|
||||
faces
|
||||
.iter()
|
||||
.map(|&url| Avatar::new(url).into_any_element())
|
||||
.collect(),
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Custom Size",
|
||||
Facepile::new(
|
||||
faces
|
||||
.iter()
|
||||
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
|
||||
.collect(),
|
||||
)
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
mod decorated_icon;
|
||||
mod icon_decoration;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{svg, App, IntoElement, Rems, RenderOnce, Size, Styled, Window};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{EnumIter, EnumString, IntoStaticStr};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use std::{cmp::Ordering, ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::{prelude::*, AnyIcon};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use crate::PlatformStyle;
|
||||
use crate::{h_flex, prelude::*, Icon, IconName, IconSize};
|
||||
use gpui::{
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use gpui::{FontWeight, HighlightStyle, StyledText};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::AnyElement;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{h_flex, prelude::*, Disclosure, Label};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{h_flex, Icon, IconName, IconSize, Label};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::{
|
||||
h_flex, v_flex, Clickable, Color, DynamicSpacing, Headline, HeadlineSize, IconButton,
|
||||
IconButtonShape, IconName, Label, LabelCommon, LabelSize,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::ClickEvent;
|
||||
|
||||
use crate::{prelude::*, IconButtonShape};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::v_flex;
|
||||
use gpui::{
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use std::{any::Any, cell::Cell, fmt::Debug, ops::Range, rc::Rc, sync::Arc};
|
||||
|
||||
use crate::{prelude::*, px, relative, IntoElement};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::AnyElement;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::AnyElement;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::{div, Div};
|
||||
|
||||
use crate::StyledExt;
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// We allow missing docs for stories as the docs will more or less be
|
||||
// "This is the ___ story", which is not very useful.
|
||||
#![allow(missing_docs)]
|
||||
mod avatar;
|
||||
mod button;
|
||||
mod context_menu;
|
||||
mod disclosure;
|
||||
mod icon;
|
||||
|
@ -15,8 +10,6 @@ mod tab;
|
|||
mod tab_bar;
|
||||
mod toggle_button;
|
||||
|
||||
pub use avatar::*;
|
||||
pub use button::*;
|
||||
pub use context_menu::*;
|
||||
pub use disclosure::*;
|
||||
pub use icon::*;
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
use gpui::Render;
|
||||
use story::{Story, StoryItem, StorySection};
|
||||
|
||||
use crate::{prelude::*, AudioStatus, Availability, AvatarAvailabilityIndicator};
|
||||
use crate::{Avatar, AvatarAudioStatusIndicator};
|
||||
|
||||
pub struct AvatarStory;
|
||||
|
||||
impl Render for AvatarStory {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
Story::container()
|
||||
.child(Story::title_for::<Avatar>())
|
||||
.child(
|
||||
StorySection::new()
|
||||
.child(StoryItem::new(
|
||||
"Default",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4"),
|
||||
))
|
||||
.child(StoryItem::new(
|
||||
"Default",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4"),
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
StorySection::new()
|
||||
.child(StoryItem::new(
|
||||
"With free availability indicator",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
|
||||
))
|
||||
.child(StoryItem::new(
|
||||
"With busy availability indicator",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.indicator(AvatarAvailabilityIndicator::new(Availability::Busy)),
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
StorySection::new()
|
||||
.child(StoryItem::new(
|
||||
"With info border",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.border_color(cx.theme().status().info_border),
|
||||
))
|
||||
.child(StoryItem::new(
|
||||
"With error border",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.border_color(cx.theme().status().error_border),
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
StorySection::new()
|
||||
.child(StoryItem::new(
|
||||
"With muted audio indicator",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
|
||||
))
|
||||
.child(StoryItem::new(
|
||||
"With deafened audio indicator",
|
||||
Avatar::new("https://avatars.githubusercontent.com/u/326587?v=4")
|
||||
.indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
use gpui::Render;
|
||||
use story::Story;
|
||||
|
||||
use crate::{prelude::*, IconName};
|
||||
use crate::{Button, ButtonStyle};
|
||||
|
||||
pub struct ButtonStory;
|
||||
|
||||
impl Render for ButtonStory {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
Story::container()
|
||||
.child(Story::title_for::<Button>())
|
||||
.child(Story::label("Default"))
|
||||
.child(Button::new("default_filled", "Click me"))
|
||||
.child(Story::label("Selected"))
|
||||
.child(Button::new("selected_filled", "Click me").toggle_state(true))
|
||||
.child(Story::label("Selected with `selected_label`"))
|
||||
.child(
|
||||
Button::new("selected_label_filled", "Click me")
|
||||
.toggle_state(true)
|
||||
.selected_label("I have been selected"),
|
||||
)
|
||||
.child(Story::label("With `label_color`"))
|
||||
.child(Button::new("filled_with_label_color", "Click me").color(Color::Created))
|
||||
.child(Story::label("With `icon`"))
|
||||
.child(Button::new("filled_with_icon", "Click me").icon(IconName::FileGit))
|
||||
.child(Story::label("Selected with `icon`"))
|
||||
.child(
|
||||
Button::new("filled_and_selected_with_icon", "Click me")
|
||||
.toggle_state(true)
|
||||
.icon(IconName::FileGit),
|
||||
)
|
||||
.child(Story::label("Default (Subtle)"))
|
||||
.child(Button::new("default_subtle", "Click me").style(ButtonStyle::Subtle))
|
||||
.child(Story::label("Default (Transparent)"))
|
||||
.child(Button::new("default_transparent", "Click me").style(ButtonStyle::Transparent))
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use gpui::{AnyElement, IntoElement, Stateful};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
use gpui::{AnyElement, ScrollHandle};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::{Action, AnyElement, AnyView, AppContext as _, FocusHandle, IntoElement, Render};
|
||||
use settings::Settings;
|
||||
use theme::ThemeSettings;
|
||||
|
|
|
@ -237,40 +237,37 @@ impl Headline {
|
|||
impl ComponentPreview for Headline {
|
||||
fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![example_group_with_title(
|
||||
"Headline Sizes",
|
||||
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(),
|
||||
),
|
||||
],
|
||||
)])
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
//! # UI – Zed UI Primitives & Components
|
||||
//!
|
||||
//! This crate provides a set of UI primitives and components that are used to build all of the elements in Zed's UI.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// This won't be documented further as it is intended to be removed, or merged with the `time_format` crate.
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use chrono::{DateTime, Local, NaiveDateTime};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use gpui::Pixels;
|
||||
|
||||
pub struct SearchInputWidth;
|
||||
|
|
|
@ -3,10 +3,10 @@ use gpui::{actions, hsla, AnyElement, App, Entity, EventEmitter, FocusHandle, Fo
|
|||
use strum::IntoEnumIterator;
|
||||
use theme::all_theme_colors;
|
||||
use ui::{
|
||||
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus,
|
||||
Availability, Avatar, AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike,
|
||||
Checkbox, CheckboxWithLabel, ContentGroup, DecoratedIcon, ElevationIndex, Facepile,
|
||||
IconDecoration, Indicator, KeybindingHint, Switch, Table, TintColor, Tooltip,
|
||||
element_cell, prelude::*, string_cell, utils::calculate_contrast_ratio, AudioStatus, Avatar,
|
||||
AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, Checkbox,
|
||||
CheckboxWithLabel, CollaboratorAvailability, ContentGroup, DecoratedIcon, ElevationIndex,
|
||||
Facepile, IconDecoration, Indicator, KeybindingHint, Switch, Table, TintColor, Tooltip,
|
||||
};
|
||||
|
||||
use crate::{Item, Workspace};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue