Extract UI elements from storybook
into new ui
crate (#3008)
This PR extracts the various UI elements from the `storybook` crate into a new `ui` library crate. Release Notes: - N/A
This commit is contained in:
parent
c252eae32e
commit
baa07e935e
35 changed files with 154 additions and 117 deletions
28
crates/ui/src/components/facepile.rs
Normal file
28
crates/ui/src/components/facepile.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use gpui2::elements::div;
|
||||
use gpui2::style::StyleHelpers;
|
||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
||||
|
||||
use crate::{theme, Avatar};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Facepile {
|
||||
players: Vec<Avatar>,
|
||||
}
|
||||
|
||||
pub fn facepile(players: Vec<Avatar>) -> Facepile {
|
||||
Facepile { players }
|
||||
}
|
||||
|
||||
impl Facepile {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let player_count = self.players.len();
|
||||
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
|
||||
let isnt_last = ix < player_count - 1;
|
||||
div()
|
||||
.when(isnt_last, |div| div.neg_mr_1())
|
||||
.child(player.clone())
|
||||
});
|
||||
div().p_1().flex().items_center().children(player_list)
|
||||
}
|
||||
}
|
52
crates/ui/src/components/follow_group.rs
Normal file
52
crates/ui/src/components/follow_group.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use gpui2::elements::div;
|
||||
use gpui2::style::StyleHelpers;
|
||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
||||
|
||||
use crate::{facepile, indicator, theme, Avatar};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct FollowGroup {
|
||||
player: usize,
|
||||
players: Vec<Avatar>,
|
||||
}
|
||||
|
||||
pub fn follow_group(players: Vec<Avatar>) -> FollowGroup {
|
||||
FollowGroup { player: 0, players }
|
||||
}
|
||||
|
||||
impl FollowGroup {
|
||||
pub fn player(mut self, player: usize) -> Self {
|
||||
self.player = player;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
let player_bg = theme.players[self.player].selection;
|
||||
|
||||
div()
|
||||
.h_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_px()
|
||||
.justify_center()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.justify_center()
|
||||
.w_full()
|
||||
.child(indicator().player(self.player)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_6()
|
||||
.px_1()
|
||||
.rounded_lg()
|
||||
.fill(player_bg)
|
||||
.child(facepile(self.players.clone())),
|
||||
)
|
||||
}
|
||||
}
|
88
crates/ui/src/components/list_item.rs
Normal file
88
crates/ui/src/components/list_item.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use gpui2::elements::div;
|
||||
use gpui2::geometry::rems;
|
||||
use gpui2::style::{StyleHelpers, Styleable};
|
||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{icon, theme, IconAsset, Label};
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct ListItem {
|
||||
label: Label,
|
||||
left_icon: Option<IconAsset>,
|
||||
indent_level: u32,
|
||||
state: InteractionState,
|
||||
toggle: Option<ToggleState>,
|
||||
}
|
||||
|
||||
pub fn list_item(label: Label) -> ListItem {
|
||||
ListItem {
|
||||
label,
|
||||
indent_level: 0,
|
||||
left_icon: None,
|
||||
state: InteractionState::default(),
|
||||
toggle: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl ListItem {
|
||||
pub fn indent_level(mut self, indent_level: u32) -> Self {
|
||||
self.indent_level = indent_level;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
|
||||
self.toggle = Some(toggle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn left_icon(mut self, left_icon: Option<IconAsset>) -> Self {
|
||||
self.left_icon = left_icon;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn state(mut self, state: InteractionState) -> Self {
|
||||
self.state = state;
|
||||
self
|
||||
}
|
||||
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.fill(theme.middle.base.default.background)
|
||||
.hover()
|
||||
.fill(theme.middle.base.hovered.background)
|
||||
.active()
|
||||
.fill(theme.middle.base.pressed.background)
|
||||
.relative()
|
||||
.child(
|
||||
div()
|
||||
.h_7()
|
||||
.px_2()
|
||||
// .ml(rems(0.75 * self.indent_level as f32))
|
||||
.children((0..self.indent_level).map(|_| {
|
||||
div().w(rems(0.75)).h_full().flex().justify_center().child(
|
||||
div()
|
||||
.w_px()
|
||||
.h_full()
|
||||
.fill(theme.middle.base.default.border)
|
||||
.hover()
|
||||
.fill(theme.middle.warning.default.border)
|
||||
.active()
|
||||
.fill(theme.middle.negative.default.border),
|
||||
)
|
||||
}))
|
||||
.flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.children(match self.toggle {
|
||||
Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)),
|
||||
Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)),
|
||||
None => None,
|
||||
})
|
||||
.children(self.left_icon.map(|i| icon(i)))
|
||||
.child(self.label.clone()),
|
||||
)
|
||||
}
|
||||
}
|
56
crates/ui/src/components/tab.rs
Normal file
56
crates/ui/src/components/tab.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use gpui2::elements::div;
|
||||
use gpui2::style::{StyleHelpers, Styleable};
|
||||
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
|
||||
|
||||
use crate::theme;
|
||||
|
||||
#[derive(Element)]
|
||||
pub struct Tab {
|
||||
title: &'static str,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
pub fn tab<V: 'static>(title: &'static str, enabled: bool) -> impl Element<V> {
|
||||
Tab { title, enabled }
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
|
||||
let theme = theme(cx);
|
||||
|
||||
div()
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded_lg()
|
||||
.fill(if self.enabled {
|
||||
theme.highest.on.default.background
|
||||
} else {
|
||||
theme.highest.base.default.background
|
||||
})
|
||||
.hover()
|
||||
.fill(if self.enabled {
|
||||
theme.highest.on.hovered.background
|
||||
} else {
|
||||
theme.highest.base.hovered.background
|
||||
})
|
||||
.active()
|
||||
.fill(if self.enabled {
|
||||
theme.highest.on.pressed.background
|
||||
} else {
|
||||
theme.highest.base.pressed.background
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(if self.enabled {
|
||||
theme.highest.base.default.foreground
|
||||
} else {
|
||||
theme.highest.variant.default.foreground
|
||||
})
|
||||
.child(self.title),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue