Colocate component stories with their components

This commit is contained in:
Marshall Bowers 2023-10-10 15:52:58 -04:00
parent b1d88ced61
commit 30088afa89
52 changed files with 1085 additions and 1012 deletions

1
Cargo.lock generated
View file

@ -9060,6 +9060,7 @@ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"gpui3", "gpui3",
"itertools 0.11.0",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"settings", "settings",

View file

@ -1,3 +1,2 @@
pub mod components;
pub mod elements; pub mod elements;
pub mod kitchen_sink; pub mod kitchen_sink;

View file

@ -1,23 +0,0 @@
pub mod breadcrumb;
pub mod buffer;
pub mod chat_panel;
pub mod collab_panel;
pub mod command_palette;
pub mod context_menu;
pub mod facepile;
pub mod keybinding;
pub mod language_selector;
pub mod multi_buffer;
pub mod palette;
pub mod panel;
pub mod project_panel;
pub mod recent_projects;
pub mod tab;
pub mod tab_bar;
pub mod terminal;
pub mod theme_selector;
pub mod title_bar;
pub mod toast;
pub mod toolbar;
pub mod traffic_lights;
pub mod workspace;

View file

@ -1,54 +0,0 @@
use std::marker::PhantomData;
use std::path::PathBuf;
use std::str::FromStr;
use ui::prelude::*;
use ui::{Breadcrumb, HighlightedText, Symbol};
use crate::story::Story;
#[derive(Element)]
pub struct BreadcrumbStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> BreadcrumbStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, Breadcrumb<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Breadcrumb::new(
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
vec![
Symbol(vec![
HighlightedText {
text: "impl ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "BreadcrumbStory".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
Symbol(vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "render".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
],
))
}
}

View file

@ -1,46 +0,0 @@
use std::marker::PhantomData;
use gpui3::rems;
use ui::prelude::*;
use ui::{
empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Buffer,
};
use crate::story::Story;
#[derive(Element)]
pub struct BufferStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> BufferStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, Buffer<S>>(cx))
.child(Story::label(cx, "Default"))
.child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
.child(Story::label(cx, "Hello World (Rust)"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_example(&theme)),
)
.child(Story::label(cx, "Hello World (Rust) with Status"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_with_status_example(&theme)),
)
}
}

View file

@ -1,56 +0,0 @@
use std::marker::PhantomData;
use chrono::DateTime;
use ui::prelude::*;
use ui::{ChatMessage, ChatPanel, Panel};
use crate::story::Story;
#[derive(Element)]
pub struct ChatPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ChatPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ChatPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| vec![ChatPanel::new(ScrollState::default()).into_any()],
Box::new(()),
))
.child(Story::label(cx, "With Mesages"))
.child(Panel::new(
ScrollState::default(),
|_, _| {
vec![ChatPanel::new(ScrollState::default())
.with_messages(vec![
ChatMessage::new(
"osiewicz".to_string(),
"is this thing on?".to_string(),
DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
.unwrap()
.naive_local(),
),
ChatMessage::new(
"maxdeviant".to_string(),
"Reading you loud and clear!".to_string(),
DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
.unwrap()
.naive_local(),
),
])
.into_any()]
},
Box::new(()),
))
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::CollabPanel;
use crate::story::Story;
#[derive(Element)]
pub struct CollabPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> CollabPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, CollabPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(CollabPanel::new(ScrollState::default()))
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::CommandPalette;
use crate::story::Story;
#[derive(Element)]
pub struct CommandPaletteStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> CommandPaletteStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, CommandPalette<S>>(cx))
.child(Story::label(cx, "Default"))
.child(CommandPalette::new(ScrollState::default()))
}
}

View file

@ -1,30 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{ContextMenu, ContextMenuItem, Label};
use crate::story::Story;
#[derive(Element)]
pub struct ContextMenuStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ContextMenuStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ContextMenu<S>>(cx))
.child(Story::label(cx, "Default"))
.child(ContextMenu::new([
ContextMenuItem::header("Section header"),
ContextMenuItem::Separator,
ContextMenuItem::entry(Label::new("Some entry")),
]))
}
}

View file

@ -1,35 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{static_players, Facepile};
use crate::story::Story;
#[derive(Element)]
pub struct FacepileStory<S: 'static + Send + Sync> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync> FacepileStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let players = static_players();
Story::container(cx)
.child(Story::title_for::<_, Facepile<S>>(cx))
.child(Story::label(cx, "Default"))
.child(
div()
.flex()
.gap_3()
.child(Facepile::new(players.clone().into_iter().take(1)))
.child(Facepile::new(players.clone().into_iter().take(2)))
.child(Facepile::new(players.clone().into_iter().take(3))),
)
}
}

View file

@ -1,74 +0,0 @@
use std::marker::PhantomData;
use itertools::Itertools;
use strum::IntoEnumIterator;
use ui::prelude::*;
use ui::{Keybinding, ModifierKey, ModifierKeys};
use crate::story::Story;
#[derive(Element)]
pub struct KeybindingStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> KeybindingStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let all_modifier_permutations = ModifierKey::iter().permutations(2);
Story::container(cx)
.child(Story::title_for::<_, Keybinding<S>>(cx))
.child(Story::label(cx, "Single Key"))
.child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
.child(Story::label(cx, "Single Key with Modifier"))
.child(
div()
.flex()
.gap_3()
.children(ModifierKey::iter().map(|modifier| {
Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
})),
)
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
.child(
div().flex().flex_col().children(
all_modifier_permutations
.chunks(4)
.into_iter()
.map(|chunk| {
div()
.flex()
.gap_4()
.py_3()
.children(chunk.map(|permutation| {
let mut modifiers = ModifierKeys::new();
for modifier in permutation {
modifiers = modifiers.add(modifier);
}
Keybinding::new("X".to_string(), modifiers)
}))
}),
),
)
.child(Story::label(cx, "Single Key with All Modifiers"))
.child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
.child(Story::label(cx, "Chord"))
.child(Keybinding::new_chord(
("A".to_string(), ModifierKeys::new()),
("Z".to_string(), ModifierKeys::new()),
))
.child(Story::label(cx, "Chord with Modifier"))
.child(Keybinding::new_chord(
("A".to_string(), ModifierKeys::new().control(true)),
("Z".to_string(), ModifierKeys::new().shift(true)),
))
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::LanguageSelector;
use crate::story::Story;
#[derive(Element)]
pub struct LanguageSelectorStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> LanguageSelectorStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, LanguageSelector<S>>(cx))
.child(Story::label(cx, "Default"))
.child(LanguageSelector::new())
}
}

View file

@ -1,34 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{hello_world_rust_buffer_example, MultiBuffer};
use crate::story::Story;
#[derive(Element)]
pub struct MultiBufferStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> MultiBufferStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, MultiBuffer<S>>(cx))
.child(Story::label(cx, "Default"))
.child(MultiBuffer::new(vec![
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
]))
}
}

View file

@ -1,63 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Keybinding, ModifierKeys, Palette, PaletteItem};
use crate::story::Story;
#[derive(Element)]
pub struct PaletteStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> PaletteStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Palette<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Palette::new(ScrollState::default()))
.child(Story::label(cx, "With Items"))
.child(
Palette::new(ScrollState::default())
.placeholder("Execute a command...")
.items(vec![
PaletteItem::new("theme selector: toggle").keybinding(
Keybinding::new_chord(
("k".to_string(), ModifierKeys::new().command(true)),
("t".to_string(), ModifierKeys::new().command(true)),
),
),
PaletteItem::new("assistant: inline assist").keybinding(Keybinding::new(
"enter".to_string(),
ModifierKeys::new().command(true),
)),
PaletteItem::new("assistant: quote selection").keybinding(Keybinding::new(
">".to_string(),
ModifierKeys::new().command(true),
)),
PaletteItem::new("assistant: toggle focus").keybinding(Keybinding::new(
"?".to_string(),
ModifierKeys::new().command(true),
)),
PaletteItem::new("auto update: check"),
PaletteItem::new("auto update: view release notes"),
PaletteItem::new("branches: open recent").keybinding(Keybinding::new(
"b".to_string(),
ModifierKeys::new().command(true).alt(true),
)),
PaletteItem::new("chat panel: toggle focus"),
PaletteItem::new("cli: install"),
PaletteItem::new("client: sign in"),
PaletteItem::new("client: sign out"),
PaletteItem::new("editor: cancel")
.keybinding(Keybinding::new("escape".to_string(), ModifierKeys::new())),
]),
)
}
}

View file

@ -1,35 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Label, Panel};
use crate::story::Story;
#[derive(Element)]
pub struct PanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> PanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Panel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| {
vec![div()
.overflow_y_scroll(ScrollState::default())
.children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
.into_any()]
},
Box::new(()),
))
}
}

View file

@ -1,30 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Panel, ProjectPanel};
use crate::story::Story;
#[derive(Element)]
pub struct ProjectPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ProjectPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ProjectPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()],
Box::new(()),
))
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::RecentProjects;
use crate::story::Story;
#[derive(Element)]
pub struct RecentProjectsStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> RecentProjectsStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, RecentProjects<S>>(cx))
.child(Story::label(cx, "Default"))
.child(RecentProjects::new())
}
}

View file

@ -1,101 +0,0 @@
use std::marker::PhantomData;
use strum::IntoEnumIterator;
use ui::prelude::*;
use ui::{h_stack, v_stack, Tab};
use crate::story::Story;
#[derive(Element)]
pub struct TabStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TabStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let git_statuses = GitStatus::iter();
let fs_statuses = FileSystemStatus::iter();
Story::container(cx)
.child(Story::title_for::<_, Tab<S>>(cx))
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Default"))
.child(Tab::new()),
),
)
.child(
h_stack().child(
v_stack().gap_2().child(Story::label(cx, "Current")).child(
h_stack()
.gap_4()
.child(Tab::new().title("Current".to_string()).current(true))
.child(Tab::new().title("Not Current".to_string()).current(false)),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Titled"))
.child(Tab::new().title("label".to_string())),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "With Icon"))
.child(
Tab::new()
.title("label".to_string())
.icon(Some(ui::Icon::Envelope)),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Close Side"))
.child(
h_stack()
.gap_4()
.child(
Tab::new()
.title("Left".to_string())
.close_side(IconSide::Left),
)
.child(Tab::new().title("Right".to_string())),
),
),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "Git Status"))
.child(h_stack().gap_4().children(git_statuses.map(|git_status| {
Tab::new()
.title(git_status.to_string())
.git_status(git_status)
}))),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "File System Status"))
.child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
Tab::new().title(fs_status.to_string()).fs_status(fs_status)
}))),
)
}
}

View file

@ -1,56 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Tab, TabBar};
use crate::story::Story;
#[derive(Element)]
pub struct TabBarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TabBarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TabBar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TabBar::new(vec![
Tab::new()
.title("Cargo.toml".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new()
.title("Channels Panel".to_string())
.current(false),
Tab::new()
.title("channels_panel.rs".to_string())
.current(true)
.git_status(GitStatus::Modified),
Tab::new()
.title("workspace.rs".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new()
.title("icon_button.rs".to_string())
.current(false),
Tab::new()
.title("storybook.rs".to_string())
.current(false)
.git_status(GitStatus::Created),
Tab::new().title("theme.rs".to_string()).current(false),
Tab::new()
.title("theme_registry.rs".to_string())
.current(false),
Tab::new()
.title("styleable_helpers.rs".to_string())
.current(false),
]))
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::Terminal;
use crate::story::Story;
#[derive(Element)]
pub struct TerminalStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TerminalStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Terminal<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Terminal::new())
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::ThemeSelector;
use crate::story::Story;
#[derive(Element)]
pub struct ThemeSelectorStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ThemeSelectorStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ThemeSelector<S>>(cx))
.child(Story::label(cx, "Default"))
.child(ThemeSelector::new())
}
}

View file

@ -1,26 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::TitleBar;
use crate::story::Story;
#[derive(Element)]
pub struct TitleBarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TitleBarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TitleBar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TitleBar::new(cx))
}
}

View file

@ -1,30 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Label, Toast, ToastOrigin};
use crate::story::Story;
#[derive(Element)]
pub struct ToastStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ToastStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Toast<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Toast::new(
ToastOrigin::Bottom,
|_, _| vec![Label::new("label").into_any()],
Box::new(()),
))
}
}

View file

@ -1,79 +0,0 @@
use std::marker::PhantomData;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use ui::prelude::*;
use ui::{theme, Breadcrumb, HighlightColor, HighlightedText, Icon, IconButton, Symbol, Toolbar};
use crate::story::Story;
#[derive(Element)]
pub struct ToolbarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ToolbarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
struct LeftItemsPayload {
pub theme: Arc<Theme>,
}
Story::container(cx)
.child(Story::title_for::<_, Toolbar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Toolbar::new(
|_, payload| {
let payload = payload.downcast_ref::<LeftItemsPayload>().unwrap();
let theme = payload.theme.clone();
vec![Breadcrumb::new(
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
vec![
Symbol(vec![
HighlightedText {
text: "impl ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "ToolbarStory".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
Symbol(vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "render".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
],
)
.into_any()]
},
Box::new(LeftItemsPayload {
theme: theme.clone(),
}),
|_, _| {
vec![
IconButton::new(Icon::InlayHint).into_any(),
IconButton::new(Icon::MagnifyingGlass).into_any(),
IconButton::new(Icon::MagicWand).into_any(),
]
},
Box::new(()),
))
}
}

View file

@ -1,28 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::TrafficLights;
use crate::story::Story;
#[derive(Element)]
pub struct TrafficLightsStory<S: 'static + Send + Sync> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync> TrafficLightsStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TrafficLights<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TrafficLights::new())
.child(Story::label(cx, "Unfocused"))
.child(TrafficLights::new().window_has_focus(false))
}
}

View file

@ -1,22 +0,0 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::WorkspaceElement;
#[derive(Element)]
pub struct WorkspaceStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> WorkspaceStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
// Just render the workspace without any story boilerplate.
WorkspaceElement::new()
}
}

View file

@ -68,39 +68,31 @@ pub enum ComponentStory {
impl ComponentStory { impl ComponentStory {
pub fn story<S: 'static + Send + Sync + Clone>(&self) -> AnyElement<S> { pub fn story<S: 'static + Send + Sync + Clone>(&self) -> AnyElement<S> {
use crate::stories::components;
match self { match self {
Self::AssistantPanel => ui::AssistantPanelStory::new().into_any(), Self::AssistantPanel => ui::AssistantPanelStory::new().into_any(),
Self::Buffer => components::buffer::BufferStory::new().into_any(), Self::Buffer => ui::BufferStory::new().into_any(),
Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::new().into_any(), Self::Breadcrumb => ui::BreadcrumbStory::new().into_any(),
Self::ChatPanel => components::chat_panel::ChatPanelStory::new().into_any(), Self::ChatPanel => ui::ChatPanelStory::new().into_any(),
Self::CollabPanel => components::collab_panel::CollabPanelStory::new().into_any(), Self::CollabPanel => ui::CollabPanelStory::new().into_any(),
Self::CommandPalette => { Self::CommandPalette => ui::CommandPaletteStory::new().into_any(),
components::command_palette::CommandPaletteStory::new().into_any() Self::ContextMenu => ui::ContextMenuStory::new().into_any(),
} Self::Facepile => ui::FacepileStory::new().into_any(),
Self::ContextMenu => components::context_menu::ContextMenuStory::new().into_any(), Self::Keybinding => ui::KeybindingStory::new().into_any(),
Self::Facepile => components::facepile::FacepileStory::new().into_any(), Self::LanguageSelector => ui::LanguageSelectorStory::new().into_any(),
Self::Keybinding => components::keybinding::KeybindingStory::new().into_any(), Self::MultiBuffer => ui::MultiBufferStory::new().into_any(),
Self::LanguageSelector => { Self::Palette => ui::PaletteStory::new().into_any(),
components::language_selector::LanguageSelectorStory::new().into_any() Self::Panel => ui::PanelStory::new().into_any(),
} Self::ProjectPanel => ui::ProjectPanelStory::new().into_any(),
Self::MultiBuffer => components::multi_buffer::MultiBufferStory::new().into_any(), Self::RecentProjects => ui::RecentProjectsStory::new().into_any(),
Self::Palette => components::palette::PaletteStory::new().into_any(), Self::Tab => ui::TabStory::new().into_any(),
Self::Panel => components::panel::PanelStory::new().into_any(), Self::TabBar => ui::TabBarStory::new().into_any(),
Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(), Self::Terminal => ui::TerminalStory::new().into_any(),
Self::RecentProjects => { Self::ThemeSelector => ui::ThemeSelectorStory::new().into_any(),
components::recent_projects::RecentProjectsStory::new().into_any() Self::TitleBar => ui::TitleBarStory::new().into_any(),
} Self::Toast => ui::ToastStory::new().into_any(),
Self::Tab => components::tab::TabStory::new().into_any(), Self::Toolbar => ui::ToolbarStory::new().into_any(),
Self::TabBar => components::tab_bar::TabBarStory::new().into_any(), Self::TrafficLights => ui::TrafficLightsStory::new().into_any(),
Self::Terminal => components::terminal::TerminalStory::new().into_any(), Self::Workspace => ui::WorkspaceStory::new().into_any(),
Self::ThemeSelector => components::theme_selector::ThemeSelectorStory::new().into_any(),
Self::TitleBar => components::title_bar::TitleBarStory::new().into_any(),
Self::Toast => components::toast::ToastStory::new().into_any(),
Self::Toolbar => components::toolbar::ToolbarStory::new().into_any(),
Self::TrafficLights => components::traffic_lights::TrafficLightsStory::new().into_any(),
Self::Workspace => components::workspace::WorkspaceStory::new().into_any(),
} }
} }
} }

View file

@ -8,6 +8,7 @@ publish = false
anyhow.workspace = true anyhow.workspace = true
chrono = "0.4" chrono = "0.4"
gpui3 = { path = "../gpui3" } gpui3 = { path = "../gpui3" }
itertools = { version = "0.11.0", optional = true }
serde.workspace = true serde.workspace = true
settings = { path = "../settings" } settings = { path = "../settings" }
smallvec.workspace = true smallvec.workspace = true
@ -17,4 +18,4 @@ rand = "0.8"
[features] [features]
default = ["stories"] default = ["stories"]
stories = [] stories = ["dep:itertools"]

View file

@ -94,7 +94,7 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::story::Story; use crate::Story;
use super::*; use super::*;

View file

@ -75,3 +75,61 @@ impl<S: 'static + Send + Sync + Clone> Breadcrumb<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use std::str::FromStr;
use crate::Story;
use super::*;
#[derive(Element)]
pub struct BreadcrumbStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> BreadcrumbStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, Breadcrumb<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Breadcrumb::new(
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
vec![
Symbol(vec![
HighlightedText {
text: "impl ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "BreadcrumbStory".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
Symbol(vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "render".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
],
))
}
}
}

View file

@ -236,3 +236,54 @@ impl<S: 'static + Send + Sync + Clone> Buffer<S> {
.children(rows) .children(rows)
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use gpui3::rems;
use crate::{
empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Story,
};
use super::*;
#[derive(Element)]
pub struct BufferStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> BufferStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, Buffer<S>>(cx))
.child(Story::label(cx, "Default"))
.child(div().w(rems(64.)).h_96().child(empty_buffer_example()))
.child(Story::label(cx, "Hello World (Rust)"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_example(&theme)),
)
.child(Story::label(cx, "Hello World (Rust) with Status"))
.child(
div()
.w(rems(64.))
.h_96()
.child(hello_world_rust_buffer_with_status_example(&theme)),
)
}
}
}

View file

@ -106,3 +106,64 @@ impl<S: 'static + Send + Sync + Clone> ChatMessage<S> {
.child(div().child(Label::new(self.text.clone()))) .child(div().child(Label::new(self.text.clone())))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use chrono::DateTime;
use crate::{Panel, Story};
use super::*;
#[derive(Element)]
pub struct ChatPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ChatPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ChatPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| vec![ChatPanel::new(ScrollState::default()).into_any()],
Box::new(()),
))
.child(Story::label(cx, "With Mesages"))
.child(Panel::new(
ScrollState::default(),
|_, _| {
vec![ChatPanel::new(ScrollState::default())
.with_messages(vec![
ChatMessage::new(
"osiewicz".to_string(),
"is this thing on?".to_string(),
DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z")
.unwrap()
.naive_local(),
),
ChatMessage::new(
"maxdeviant".to_string(),
"Reading you loud and clear!".to_string(),
DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z")
.unwrap()
.naive_local(),
),
])
.into_any()]
},
Box::new(()),
))
}
}
}

View file

@ -158,3 +158,33 @@ impl<S: 'static + Send + Sync + Clone> CollabPanel<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct CollabPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> CollabPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, CollabPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(CollabPanel::new(ScrollState::default()))
}
}
}

View file

@ -27,3 +27,33 @@ impl<S: 'static + Send + Sync + Clone> CommandPalette<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct CommandPaletteStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> CommandPaletteStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, CommandPalette<S>>(cx))
.child(Story::label(cx, "Default"))
.child(CommandPalette::new(ScrollState::default()))
}
}
}

View file

@ -65,3 +65,39 @@ impl<S: 'static + Send + Sync + Clone> ContextMenu<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use std::marker::PhantomData;
use crate::story::Story;
use super::*;
#[derive(Element)]
pub struct ContextMenuStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ContextMenuStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ContextMenu<S>>(cx))
.child(Story::label(cx, "Default"))
.child(ContextMenu::new([
ContextMenuItem::header("Section header"),
ContextMenuItem::Separator,
ContextMenuItem::entry(Label::new("Some entry")),
]))
}
}
}

View file

@ -30,3 +30,42 @@ impl<S: 'static + Send + Sync> Facepile<S> {
div().p_1().flex().items_center().children(player_list) div().p_1().flex().items_center().children(player_list)
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::{static_players, Story};
use super::*;
#[derive(Element)]
pub struct FacepileStory<S: 'static + Send + Sync> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync> FacepileStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let players = static_players();
Story::container(cx)
.child(Story::title_for::<_, Facepile<S>>(cx))
.child(Story::label(cx, "Default"))
.child(
div()
.flex()
.gap_3()
.child(Facepile::new(players.clone().into_iter().take(1)))
.child(Facepile::new(players.clone().into_iter().take(2)))
.child(Facepile::new(players.clone().into_iter().take(3))),
)
}
}
}

View file

@ -165,3 +165,81 @@ impl ModifierKeys {
self self
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use itertools::Itertools;
use crate::Story;
use super::*;
#[derive(Element)]
pub struct KeybindingStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> KeybindingStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let all_modifier_permutations = ModifierKey::iter().permutations(2);
Story::container(cx)
.child(Story::title_for::<_, Keybinding<S>>(cx))
.child(Story::label(cx, "Single Key"))
.child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
.child(Story::label(cx, "Single Key with Modifier"))
.child(
div()
.flex()
.gap_3()
.children(ModifierKey::iter().map(|modifier| {
Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
})),
)
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
.child(
div().flex().flex_col().children(
all_modifier_permutations
.chunks(4)
.into_iter()
.map(|chunk| {
div()
.flex()
.gap_4()
.py_3()
.children(chunk.map(|permutation| {
let mut modifiers = ModifierKeys::new();
for modifier in permutation {
modifiers = modifiers.add(modifier);
}
Keybinding::new("X".to_string(), modifiers)
}))
}),
),
)
.child(Story::label(cx, "Single Key with All Modifiers"))
.child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
.child(Story::label(cx, "Chord"))
.child(Keybinding::new_chord(
("A".to_string(), ModifierKeys::new()),
("Z".to_string(), ModifierKeys::new()),
))
.child(Story::label(cx, "Chord with Modifier"))
.child(Keybinding::new_chord(
("A".to_string(), ModifierKeys::new().control(true)),
("Z".to_string(), ModifierKeys::new().shift(true)),
))
}
}
}

View file

@ -38,3 +38,33 @@ impl<S: 'static + Send + Sync + Clone> LanguageSelector<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct LanguageSelectorStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> LanguageSelectorStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, LanguageSelector<S>>(cx))
.child(Story::label(cx, "Default"))
.child(LanguageSelector::new())
}
}
}

View file

@ -40,3 +40,41 @@ impl<S: 'static + Send + Sync + Clone> MultiBuffer<S> {
})) }))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::{hello_world_rust_buffer_example, Story};
use super::*;
#[derive(Element)]
pub struct MultiBufferStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> MultiBufferStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
Story::container(cx)
.child(Story::title_for::<_, MultiBuffer<S>>(cx))
.child(Story::label(cx, "Default"))
.child(MultiBuffer::new(vec![
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
hello_world_rust_buffer_example(&theme),
]))
}
}
}

View file

@ -150,3 +150,72 @@ impl<S: 'static + Send + Sync + Clone> PaletteItem<S> {
.children(self.keybinding.clone()) .children(self.keybinding.clone())
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::{ModifierKeys, Story};
use super::*;
#[derive(Element)]
pub struct PaletteStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> PaletteStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Palette<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Palette::new(ScrollState::default()))
.child(Story::label(cx, "With Items"))
.child(
Palette::new(ScrollState::default())
.placeholder("Execute a command...")
.items(vec![
PaletteItem::new("theme selector: toggle").keybinding(
Keybinding::new_chord(
("k".to_string(), ModifierKeys::new().command(true)),
("t".to_string(), ModifierKeys::new().command(true)),
),
),
PaletteItem::new("assistant: inline assist").keybinding(
Keybinding::new(
"enter".to_string(),
ModifierKeys::new().command(true),
),
),
PaletteItem::new("assistant: quote selection").keybinding(
Keybinding::new(">".to_string(), ModifierKeys::new().command(true)),
),
PaletteItem::new("assistant: toggle focus").keybinding(
Keybinding::new("?".to_string(), ModifierKeys::new().command(true)),
),
PaletteItem::new("auto update: check"),
PaletteItem::new("auto update: view release notes"),
PaletteItem::new("branches: open recent").keybinding(Keybinding::new(
"b".to_string(),
ModifierKeys::new().command(true).alt(true),
)),
PaletteItem::new("chat panel: toggle focus"),
PaletteItem::new("cli: install"),
PaletteItem::new("client: sign in"),
PaletteItem::new("client: sign out"),
PaletteItem::new("editor: cancel").keybinding(Keybinding::new(
"escape".to_string(),
ModifierKeys::new(),
)),
]),
)
}
}
}

View file

@ -143,3 +143,42 @@ impl<S: 'static + Send + Sync> Panel<S> {
panel_base.children_any((self.children)(cx, self.payload.as_ref())) panel_base.children_any((self.children)(cx, self.payload.as_ref()))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::{Label, Story};
use super::*;
#[derive(Element)]
pub struct PanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> PanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Panel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| {
vec![div()
.overflow_y_scroll(ScrollState::default())
.children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1))))
.into_any()]
},
Box::new(()),
))
}
}
}

View file

@ -56,3 +56,37 @@ impl<S: 'static + Send + Sync + Clone> ProjectPanel<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::{Panel, Story};
use super::*;
#[derive(Element)]
pub struct ProjectPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ProjectPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ProjectPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()],
Box::new(()),
))
}
}
}

View file

@ -34,3 +34,33 @@ impl<S: 'static + Send + Sync + Clone> RecentProjects<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct RecentProjectsStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> RecentProjectsStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, RecentProjects<S>>(cx))
.child(Story::label(cx, "Default"))
.child(RecentProjects::new())
}
}
}

View file

@ -133,3 +133,109 @@ impl<S: 'static + Send + Sync + Clone> Tab<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use strum::IntoEnumIterator;
use crate::{h_stack, v_stack, Icon, Story};
use super::*;
#[derive(Element)]
pub struct TabStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TabStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let git_statuses = GitStatus::iter();
let fs_statuses = FileSystemStatus::iter();
Story::container(cx)
.child(Story::title_for::<_, Tab<S>>(cx))
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Default"))
.child(Tab::new()),
),
)
.child(
h_stack().child(
v_stack().gap_2().child(Story::label(cx, "Current")).child(
h_stack()
.gap_4()
.child(Tab::new().title("Current".to_string()).current(true))
.child(Tab::new().title("Not Current".to_string()).current(false)),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Titled"))
.child(Tab::new().title("label".to_string())),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "With Icon"))
.child(
Tab::new()
.title("label".to_string())
.icon(Some(Icon::Envelope)),
),
),
)
.child(
h_stack().child(
v_stack()
.gap_2()
.child(Story::label(cx, "Close Side"))
.child(
h_stack()
.gap_4()
.child(
Tab::new()
.title("Left".to_string())
.close_side(IconSide::Left),
)
.child(Tab::new().title("Right".to_string())),
),
),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "Git Status"))
.child(h_stack().gap_4().children(git_statuses.map(|git_status| {
Tab::new()
.title(git_status.to_string())
.git_status(git_status)
}))),
)
.child(
v_stack()
.gap_2()
.child(Story::label(cx, "File System Status"))
.child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
Tab::new().title(fs_status.to_string()).fs_status(fs_status)
}))),
)
}
}
}

View file

@ -83,3 +83,63 @@ impl<S: 'static + Send + Sync + Clone> TabBar<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct TabBarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TabBarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TabBar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TabBar::new(vec![
Tab::new()
.title("Cargo.toml".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new()
.title("Channels Panel".to_string())
.current(false),
Tab::new()
.title("channels_panel.rs".to_string())
.current(true)
.git_status(GitStatus::Modified),
Tab::new()
.title("workspace.rs".to_string())
.current(false)
.git_status(GitStatus::Modified),
Tab::new()
.title("icon_button.rs".to_string())
.current(false),
Tab::new()
.title("storybook.rs".to_string())
.current(false)
.git_status(GitStatus::Created),
Tab::new().title("theme.rs".to_string()).current(false),
Tab::new()
.title("theme_registry.rs".to_string())
.current(false),
Tab::new()
.title("styleable_helpers.rs".to_string())
.current(false),
]))
}
}
}

View file

@ -87,3 +87,33 @@ impl<S: 'static + Send + Sync + Clone> Terminal<S> {
)) ))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct TerminalStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TerminalStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Terminal<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Terminal::new())
}
}
}

View file

@ -39,3 +39,33 @@ impl<S: 'static + Send + Sync + Clone> ThemeSelector<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct ThemeSelectorStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ThemeSelectorStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ThemeSelector<S>>(cx))
.child(Story::label(cx, "Default"))
.child(ThemeSelector::new())
}
}
}

View file

@ -117,3 +117,33 @@ impl<S: 'static + Send + Sync + Clone> TitleBar<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct TitleBarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> TitleBarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TitleBar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TitleBar::new(cx))
}
}
}

View file

@ -65,3 +65,39 @@ impl<S: 'static + Send + Sync> Toast<S> {
.children_any((self.children)(cx, self.payload.as_ref())) .children_any((self.children)(cx, self.payload.as_ref()))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use std::marker::PhantomData;
use crate::{Label, Story};
use super::*;
#[derive(Element)]
pub struct ToastStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ToastStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Toast<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Toast::new(
ToastOrigin::Bottom,
|_, _| vec![Label::new("label").into_any()],
Box::new(()),
))
}
}
}

View file

@ -47,3 +47,88 @@ impl<S: 'static + Send + Sync> Toolbar<S> {
) )
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use std::marker::PhantomData;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
use super::*;
#[derive(Element)]
pub struct ToolbarStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ToolbarStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
struct LeftItemsPayload {
pub theme: Arc<Theme>,
}
Story::container(cx)
.child(Story::title_for::<_, Toolbar<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Toolbar::new(
|_, payload| {
let payload = payload.downcast_ref::<LeftItemsPayload>().unwrap();
let theme = payload.theme.clone();
vec![Breadcrumb::new(
PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(),
vec![
Symbol(vec![
HighlightedText {
text: "impl ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "ToolbarStory".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
Symbol(vec![
HighlightedText {
text: "fn ".to_string(),
color: HighlightColor::Keyword.hsla(&theme),
},
HighlightedText {
text: "render".to_string(),
color: HighlightColor::Function.hsla(&theme),
},
]),
],
)
.into_any()]
},
Box::new(LeftItemsPayload {
theme: theme.clone(),
}),
|_, _| {
vec![
IconButton::new(Icon::InlayHint).into_any(),
IconButton::new(Icon::MagnifyingGlass).into_any(),
IconButton::new(Icon::MagicWand).into_any(),
]
},
Box::new(()),
))
}
}
}

View file

@ -82,3 +82,35 @@ impl<S: 'static + Send + Sync> TrafficLights<S> {
)) ))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use crate::Story;
use super::*;
#[derive(Element)]
pub struct TrafficLightsStory<S: 'static + Send + Sync> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync> TrafficLightsStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, TrafficLights<S>>(cx))
.child(Story::label(cx, "Default"))
.child(TrafficLights::new())
.child(Story::label(cx, "Unfocused"))
.child(TrafficLights::new().window_has_focus(false))
}
}
}

View file

@ -195,3 +195,29 @@ impl<S: 'static + Send + Sync + Clone> WorkspaceElement<S> {
)) ))
} }
} }
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
#[derive(Element)]
pub struct WorkspaceStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> WorkspaceStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
// Just render the workspace without any story boilerplate.
WorkspaceElement::new()
}
}
}