Story/Storybook Enhancements (#3674)
[[PR Description]] - Adds some Story components to reduce the amount of code needed to lay out stories - Added the ability to open a story in Zed using a link (see text story) - Added sections with descriptions and usage. Release Notes: - N/A
This commit is contained in:
Generated
+3
@@ -9263,6 +9263,8 @@ name = "story"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui2",
|
||||
"itertools 0.10.5",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9277,6 +9279,7 @@ dependencies = [
|
||||
"editor2",
|
||||
"fuzzy2",
|
||||
"gpui2",
|
||||
"indoc",
|
||||
"itertools 0.11.0",
|
||||
"language2",
|
||||
"log",
|
||||
|
||||
@@ -8,3 +8,5 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
smallvec.workspace = true
|
||||
itertools = {package = "itertools", version = "0.10"}
|
||||
|
||||
+363
-13
@@ -1,22 +1,199 @@
|
||||
use gpui::prelude::*;
|
||||
use gpui::{div, hsla, Div, SharedString};
|
||||
use gpui::{
|
||||
div, hsla, prelude::*, px, rems, AnyElement, Div, ElementId, Hsla, SharedString, Stateful,
|
||||
WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn reasonably_unique_id() -> String {
|
||||
let now = SystemTime::now();
|
||||
let timestamp = now.duration_since(UNIX_EPOCH).unwrap();
|
||||
|
||||
let cnt = COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let id = format!("{}_{}", timestamp.as_nanos(), cnt);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
pub struct StoryColor {
|
||||
pub primary: Hsla,
|
||||
pub secondary: Hsla,
|
||||
pub border: Hsla,
|
||||
pub background: Hsla,
|
||||
pub card_background: Hsla,
|
||||
pub divider: Hsla,
|
||||
pub link: Hsla,
|
||||
}
|
||||
|
||||
impl StoryColor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
primary: hsla(216. / 360., 11. / 100., 0. / 100., 1.),
|
||||
secondary: hsla(216. / 360., 11. / 100., 16. / 100., 1.),
|
||||
border: hsla(216. / 360., 11. / 100., 91. / 100., 1.),
|
||||
background: hsla(0. / 360., 0. / 100., 100. / 100., 1.),
|
||||
card_background: hsla(0. / 360., 0. / 100., 96. / 100., 1.),
|
||||
divider: hsla(216. / 360., 11. / 100., 86. / 100., 1.),
|
||||
link: hsla(206. / 360., 100. / 100., 50. / 100., 1.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn story_color() -> StoryColor {
|
||||
StoryColor::new()
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct StoryContainer {
|
||||
title: SharedString,
|
||||
relative_path: &'static str,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl StoryContainer {
|
||||
pub fn new(title: impl Into<SharedString>, relative_path: &'static str) -> Self {
|
||||
Self {
|
||||
title: title.into(),
|
||||
relative_path,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for StoryContainer {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for StoryContainer {
|
||||
type Rendered = Stateful<Div>;
|
||||
|
||||
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.id("story_container")
|
||||
.bg(story_color().background)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_none()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.p_2()
|
||||
.bg(story_color().background)
|
||||
.border_b()
|
||||
.border_color(story_color().border)
|
||||
.child(Story::title(self.title))
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(story_color().primary)
|
||||
.child(Story::open_story_link(self.relative_path)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.flex_1()
|
||||
.id("story_body")
|
||||
.overflow_hidden_x()
|
||||
.overflow_y_scroll()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.pb_4()
|
||||
.children(self.children),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Story {}
|
||||
|
||||
impl Story {
|
||||
pub fn container() -> Div {
|
||||
div().size_full().flex().flex_col().pt_2().px_4().bg(hsla(
|
||||
0. / 360.,
|
||||
0. / 100.,
|
||||
100. / 100.,
|
||||
1.,
|
||||
))
|
||||
div().size_full().overflow_hidden().child(
|
||||
div()
|
||||
.id("story_container")
|
||||
.overflow_y_scroll()
|
||||
.w_full()
|
||||
.min_h_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.bg(story_color().background),
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Move all stories to container2, then rename
|
||||
pub fn container2<T>(relative_path: &'static str) -> Div {
|
||||
div().size_full().child(
|
||||
div()
|
||||
.size_full()
|
||||
.id("story_container")
|
||||
.overflow_y_scroll()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.flex_none()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.p_2()
|
||||
.border_b()
|
||||
.border_color(story_color().border)
|
||||
.child(Story::title_for::<T>())
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(story_color().primary)
|
||||
.child(Story::open_story_link(relative_path)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.min_h_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.bg(story_color().background),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn open_story_link(relative_path: &'static str) -> impl Element {
|
||||
let path = PathBuf::from_iter([relative_path]);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.text_color(story_color().primary)
|
||||
.id(SharedString::from(format!("id_{}", relative_path)))
|
||||
.on_click({
|
||||
let path = path.clone();
|
||||
|
||||
move |_event, _cx| {
|
||||
let path = format!("{}:0:0", path.to_string_lossy());
|
||||
|
||||
std::process::Command::new("zed").arg(path).spawn().ok();
|
||||
}
|
||||
})
|
||||
.children(vec![div().child(Story::link("Open in Zed →"))])
|
||||
}
|
||||
|
||||
pub fn title(title: impl Into<SharedString>) -> impl Element {
|
||||
div()
|
||||
.text_xl()
|
||||
.text_color(hsla(0. / 360., 0. / 100., 0. / 100., 1.))
|
||||
.text_xs()
|
||||
.text_color(story_color().primary)
|
||||
.child(title.into())
|
||||
}
|
||||
|
||||
@@ -24,12 +201,185 @@ impl Story {
|
||||
Self::title(std::any::type_name::<T>())
|
||||
}
|
||||
|
||||
pub fn section() -> Div {
|
||||
div()
|
||||
.p_4()
|
||||
.m_4()
|
||||
.border()
|
||||
.border_color(story_color().border)
|
||||
}
|
||||
|
||||
pub fn section_title() -> Div {
|
||||
div().text_lg().text_color(story_color().primary)
|
||||
}
|
||||
|
||||
pub fn group() -> Div {
|
||||
div().my_2().bg(story_color().background)
|
||||
}
|
||||
|
||||
pub fn code_block(code: impl Into<SharedString>) -> Div {
|
||||
div()
|
||||
.size_full()
|
||||
.p_2()
|
||||
.max_w(rems(36.))
|
||||
.bg(gpui::black())
|
||||
.rounded_md()
|
||||
.text_sm()
|
||||
.text_color(gpui::white())
|
||||
.overflow_hidden()
|
||||
.child(code.into())
|
||||
}
|
||||
|
||||
pub fn divider() -> Div {
|
||||
div().my_2().h(px(1.)).bg(story_color().divider)
|
||||
}
|
||||
|
||||
pub fn link(link: impl Into<SharedString>) -> impl Element {
|
||||
div()
|
||||
.id(ElementId::from(SharedString::from(reasonably_unique_id())))
|
||||
.text_xs()
|
||||
.text_color(story_color().link)
|
||||
.cursor(gpui::CursorStyle::PointingHand)
|
||||
.child(link.into())
|
||||
}
|
||||
|
||||
pub fn description(description: impl Into<SharedString>) -> impl Element {
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(story_color().secondary)
|
||||
.min_w_96()
|
||||
.child(description.into())
|
||||
}
|
||||
|
||||
pub fn label(label: impl Into<SharedString>) -> impl Element {
|
||||
div()
|
||||
.mt_4()
|
||||
.mb_2()
|
||||
.text_xs()
|
||||
.text_color(hsla(0. / 360., 0. / 100., 0. / 100., 1.))
|
||||
.text_color(story_color().primary)
|
||||
.child(label.into())
|
||||
}
|
||||
|
||||
/// Note: Not ui::v_stack() as the story crate doesn't depend on the ui crate.
|
||||
pub fn v_stack() -> Div {
|
||||
div().flex().flex_col().gap_1()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct StoryItem {
|
||||
label: SharedString,
|
||||
item: AnyElement,
|
||||
description: Option<SharedString>,
|
||||
usage: Option<SharedString>,
|
||||
}
|
||||
|
||||
impl StoryItem {
|
||||
pub fn new(label: impl Into<SharedString>, item: impl IntoElement) -> Self {
|
||||
Self {
|
||||
label: label.into(),
|
||||
item: item.into_any_element(),
|
||||
description: None,
|
||||
usage: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn usage(mut self, code: impl Into<SharedString>) -> Self {
|
||||
self.usage = Some(code.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for StoryItem {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.my_2()
|
||||
.flex()
|
||||
.gap_4()
|
||||
.w_full()
|
||||
.child(
|
||||
Story::v_stack()
|
||||
.px_2()
|
||||
.w_1_2()
|
||||
.min_h_px()
|
||||
.child(Story::label(self.label))
|
||||
.child(
|
||||
div()
|
||||
.rounded_md()
|
||||
.bg(story_color().card_background)
|
||||
.border()
|
||||
.border_color(story_color().border)
|
||||
.py_1()
|
||||
.px_2()
|
||||
.overflow_hidden()
|
||||
.child(self.item),
|
||||
)
|
||||
.when_some(self.description, |this, description| {
|
||||
this.child(Story::description(description))
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Story::v_stack()
|
||||
.px_2()
|
||||
.flex_none()
|
||||
.w_1_2()
|
||||
.min_h_px()
|
||||
.when_some(self.usage, |this, usage| {
|
||||
this.child(Story::label("Example Usage"))
|
||||
.child(Story::code_block(usage))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct StorySection {
|
||||
description: Option<SharedString>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl StorySection {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
description: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for StorySection {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
|
||||
let children: SmallVec<[AnyElement; 2]> = SmallVec::from_iter(Itertools::intersperse_with(
|
||||
self.children.into_iter(),
|
||||
|| Story::divider().into_any_element(),
|
||||
));
|
||||
|
||||
Story::section()
|
||||
// Section title
|
||||
.py_2()
|
||||
// Section description
|
||||
.when_some(self.description.clone(), |section, description| {
|
||||
section.child(Story::description(description))
|
||||
})
|
||||
.child(div().flex().flex_col().gap_2().children(children))
|
||||
.child(Story::divider())
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for StorySection {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ dialoguer = { version = "0.11.0", features = ["fuzzy-select"] }
|
||||
editor = { package = "editor2", path = "../editor2" }
|
||||
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
indoc.workspace = true
|
||||
itertools = "0.11.0"
|
||||
language = { package = "language2", path = "../language2" }
|
||||
log.workspace = true
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use gpui::{
|
||||
blue, div, green, red, white, Div, HighlightStyle, InteractiveText, ParentElement, Render,
|
||||
Styled, StyledText, View, VisualContext, WindowContext,
|
||||
div, green, red, Component, HighlightStyle, InteractiveText, IntoElement, ParentElement,
|
||||
Render, Styled, StyledText, View, VisualContext, WindowContext,
|
||||
};
|
||||
use ui::v_stack;
|
||||
use indoc::indoc;
|
||||
use story::*;
|
||||
|
||||
pub struct TextStory;
|
||||
|
||||
@@ -13,62 +14,164 @@ impl TextStory {
|
||||
}
|
||||
|
||||
impl Render for TextStory {
|
||||
type Element = Div;
|
||||
type Element = Component<StoryContainer>;
|
||||
|
||||
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
v_stack()
|
||||
.bg(blue())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.child(div().max_w_96().bg(white()).child(concat!(
|
||||
"max-width: 96. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
))),
|
||||
)
|
||||
.child(div().h_5())
|
||||
.child(div().flex().flex_col().w_96().bg(white()).child(concat!(
|
||||
"flex-col. width: 96; The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
)))
|
||||
.child(div().h_5())
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.child(div().min_w_96().bg(white()).child(concat!(
|
||||
"min-width: 96. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
))))
|
||||
.child(div().h_5())
|
||||
.child(div().flex().w_96().bg(white()).child(div().overflow_hidden().child(concat!(
|
||||
"flex-row. width 96. overflow-hidden. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
))))
|
||||
// NOTE: When rendering text in a horizonal flex container,
|
||||
// Taffy will not pass width constraints down from the parent.
|
||||
// To fix this, render text in a praent with overflow: hidden, which
|
||||
.child(div().h_5())
|
||||
.child(div().flex().w_96().bg(red()).child(concat!(
|
||||
"flex-row. width 96. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
))).child(
|
||||
InteractiveText::new(
|
||||
"interactive",
|
||||
StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
|
||||
(6..11, HighlightStyle {
|
||||
background_color: Some(green()),
|
||||
..Default::default()
|
||||
StoryContainer::new("Text Story", "crates/storybook2/src/stories/text.rs")
|
||||
.children(
|
||||
vec![
|
||||
|
||||
StorySection::new()
|
||||
.child(
|
||||
StoryItem::new("Default", div().bg(gpui::blue()).child("Hello World!"))
|
||||
.usage(indoc! {r##"
|
||||
div()
|
||||
.child("Hello World!")
|
||||
"##
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
|
||||
println!("Clicked range {range_ix}");
|
||||
})
|
||||
)
|
||||
.child(
|
||||
StoryItem::new("Wrapping Text",
|
||||
div().max_w_96()
|
||||
.child(
|
||||
concat!(
|
||||
"The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
)
|
||||
)
|
||||
)
|
||||
.description("Set a width or max-width to enable text wrapping.")
|
||||
.usage(indoc! {r##"
|
||||
div()
|
||||
.max_w_96()
|
||||
.child("Some text that you want to wrap.")
|
||||
"##
|
||||
})
|
||||
)
|
||||
.child(
|
||||
StoryItem::new("tbd",
|
||||
div().flex().w_96().child(div().overflow_hidden().child(concat!(
|
||||
"flex-row. width 96. overflow-hidden. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
)))
|
||||
)
|
||||
)
|
||||
.child(
|
||||
StoryItem::new("Text in Horizontal Flex",
|
||||
div().flex().w_96().bg(red()).child(concat!(
|
||||
"flex-row. width 96. The quick brown fox jumps over the lazy dog. ",
|
||||
"Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
"He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
))
|
||||
)
|
||||
.usage(indoc! {r##"
|
||||
// NOTE: When rendering text in a horizonal flex container,
|
||||
// Taffy will not pass width constraints down from the parent.
|
||||
// To fix this, render text in a parent with overflow: hidden
|
||||
|
||||
div()
|
||||
.max_w_96()
|
||||
.child("Some text that you want to wrap.")
|
||||
"##
|
||||
})
|
||||
)
|
||||
.child(
|
||||
StoryItem::new("Interactive Text",
|
||||
InteractiveText::new(
|
||||
"interactive",
|
||||
StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
|
||||
(6..11, HighlightStyle {
|
||||
background_color: Some(green()),
|
||||
..Default::default()
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
|
||||
println!("Clicked range {range_ix}");
|
||||
})
|
||||
)
|
||||
.usage(indoc! {r##"
|
||||
InteractiveText::new(
|
||||
"interactive",
|
||||
StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
|
||||
(6..11, HighlightStyle {
|
||||
background_color: Some(green()),
|
||||
..Default::default()
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
|
||||
println!("Clicked range {range_ix}");
|
||||
})
|
||||
"##
|
||||
})
|
||||
)
|
||||
]
|
||||
).into_element()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check all were updated to new style and remove
|
||||
|
||||
// impl Render for TextStory {
|
||||
// type Element = Div;
|
||||
|
||||
// fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
|
||||
// v_stack()
|
||||
// .bg(blue())
|
||||
// .child(
|
||||
// div()
|
||||
// .flex()
|
||||
// .child(div().max_w_96().bg(white()).child(concat!(
|
||||
// "max-width: 96. The quick brown fox jumps over the lazy dog. ",
|
||||
// "Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
// "He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
// ))),
|
||||
// )
|
||||
// .child(div().h_5())
|
||||
// .child(div().flex().flex_col().w_96().bg(white()).child(concat!(
|
||||
// "flex-col. width: 96; The quick brown fox jumps over the lazy dog. ",
|
||||
// "Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
// "He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
// )))
|
||||
// .child(div().h_5())
|
||||
// .child(
|
||||
// div()
|
||||
// .flex()
|
||||
// .child(div().min_w_96().bg(white()).child(concat!(
|
||||
// "min-width: 96. The quick brown fox jumps over the lazy dog. ",
|
||||
// "Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
// "He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
// ))))
|
||||
// .child(div().h_5())
|
||||
// .child(div().flex().w_96().bg(white()).child(div().overflow_hidden().child(concat!(
|
||||
// "flex-row. width 96. overflow-hidden. The quick brown fox jumps over the lazy dog. ",
|
||||
// "Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
// "He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
// ))))
|
||||
// // NOTE: When rendering text in a horizonal flex container,
|
||||
// // Taffy will not pass width constraints down from the parent.
|
||||
// // To fix this, render text in a parent with overflow: hidden
|
||||
// .child(div().h_5())
|
||||
// .child(div().flex().w_96().bg(red()).child(concat!(
|
||||
// "flex-row. width 96. The quick brown fox jumps over the lazy dog. ",
|
||||
// "Meanwhile, the lazy dog decided it was time for a change. ",
|
||||
// "He started daily workout routines, ate healthier and became the fastest dog in town.",
|
||||
// ))).child(
|
||||
// InteractiveText::new(
|
||||
// "interactive",
|
||||
// StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
|
||||
// (6..11, HighlightStyle {
|
||||
// background_color: Some(green()),
|
||||
// ..Default::default()
|
||||
// }),
|
||||
// ]),
|
||||
// )
|
||||
// .on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
|
||||
// println!("Clicked range {range_ix}");
|
||||
// })
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -19,6 +19,7 @@ use ui::prelude::*;
|
||||
|
||||
use crate::assets::Assets;
|
||||
use crate::story_selector::{ComponentStory, StorySelector};
|
||||
pub use indoc::indoc;
|
||||
|
||||
// gpui::actions! {
|
||||
// storybook,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use gpui::{Div, Render};
|
||||
use story::Story;
|
||||
use gpui::{Component, Render};
|
||||
use story::{StoryContainer, StoryItem, StorySection};
|
||||
|
||||
use crate::{prelude::*, Tooltip};
|
||||
use crate::{Icon, IconButton};
|
||||
@@ -7,57 +7,167 @@ use crate::{Icon, IconButton};
|
||||
pub struct IconButtonStory;
|
||||
|
||||
impl Render for IconButtonStory {
|
||||
type Element = Div;
|
||||
type Element = Component<StoryContainer>;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
Story::container()
|
||||
.child(Story::title_for::<IconButton>())
|
||||
.child(Story::label("Default"))
|
||||
.child(div().w_8().child(IconButton::new("icon_a", Icon::Hash)))
|
||||
.child(Story::label("Selected"))
|
||||
.child(
|
||||
div()
|
||||
.w_8()
|
||||
.child(IconButton::new("icon_a", Icon::Hash).selected(true)),
|
||||
)
|
||||
.child(Story::label("Selected with `selected_icon`"))
|
||||
.child(
|
||||
div().w_8().child(
|
||||
IconButton::new("icon_a", Icon::AudioOn)
|
||||
.selected(true)
|
||||
.selected_icon(Icon::AudioOff),
|
||||
),
|
||||
)
|
||||
.child(Story::label("Disabled"))
|
||||
.child(
|
||||
div()
|
||||
.w_8()
|
||||
.child(IconButton::new("icon_a", Icon::Hash).disabled(true)),
|
||||
)
|
||||
.child(Story::label("With `on_click`"))
|
||||
.child(
|
||||
div()
|
||||
.w_8()
|
||||
.child(
|
||||
IconButton::new("with_on_click", Icon::Ai).on_click(|_event, _cx| {
|
||||
println!("Clicked!");
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Story::label("With `tooltip`"))
|
||||
.child(
|
||||
div().w_8().child(
|
||||
IconButton::new("with_tooltip", Icon::MessageBubbles)
|
||||
.tooltip(|cx| Tooltip::text("Open messages", cx)),
|
||||
),
|
||||
)
|
||||
.child(Story::label("Selected with `tooltip`"))
|
||||
.child(
|
||||
div().w_8().child(
|
||||
IconButton::new("selected_with_tooltip", Icon::InlayHint)
|
||||
.selected(true)
|
||||
.tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)),
|
||||
),
|
||||
)
|
||||
let default_button = StoryItem::new(
|
||||
"Default",
|
||||
IconButton::new("default_icon_button", Icon::Hash),
|
||||
)
|
||||
.description("Displays an icon button.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("default_icon_button", Icon::Hash)
|
||||
"#,
|
||||
);
|
||||
|
||||
let selected_button = StoryItem::new(
|
||||
"Selected",
|
||||
IconButton::new("selected_icon_button", Icon::Hash).selected(true),
|
||||
)
|
||||
.description("Displays an icon button that is selected.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("selected_icon_button", Icon::Hash).selected(true)
|
||||
"#,
|
||||
);
|
||||
|
||||
let selected_with_selected_icon = StoryItem::new(
|
||||
"Selected with `selected_icon`",
|
||||
IconButton::new("selected_with_selected_icon_button", Icon::AudioOn)
|
||||
.selected(true)
|
||||
.selected_icon(Icon::AudioOff),
|
||||
)
|
||||
.description(
|
||||
"Displays an icon button that is selected and shows a different icon when selected.",
|
||||
)
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("selected_with_selected_icon_button", Icon::AudioOn)
|
||||
.selected(true)
|
||||
.selected_icon(Icon::AudioOff)
|
||||
"#,
|
||||
);
|
||||
|
||||
let disabled_button = StoryItem::new(
|
||||
"Disabled",
|
||||
IconButton::new("disabled_icon_button", Icon::Hash).disabled(true),
|
||||
)
|
||||
.description("Displays an icon button that is disabled.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("disabled_icon_button", Icon::Hash).disabled(true)
|
||||
"#,
|
||||
);
|
||||
|
||||
let with_on_click_button = StoryItem::new(
|
||||
"With `on_click`",
|
||||
IconButton::new("with_on_click_button", Icon::Ai).on_click(|_event, _cx| {
|
||||
println!("Clicked!");
|
||||
}),
|
||||
)
|
||||
.description("Displays an icon button which triggers an event on click.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("with_on_click_button", Icon::Ai).on_click(|_event, _cx| {
|
||||
println!("Clicked!");
|
||||
})
|
||||
"#,
|
||||
);
|
||||
|
||||
let with_tooltip_button = StoryItem::new(
|
||||
"With `tooltip`",
|
||||
IconButton::new("with_tooltip_button", Icon::MessageBubbles)
|
||||
.tooltip(|cx| Tooltip::text("Open messages", cx)),
|
||||
)
|
||||
.description("Displays an icon button that has a tooltip when hovered.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("with_tooltip_button", Icon::MessageBubbles)
|
||||
.tooltip(|cx| Tooltip::text("Open messages", cx))
|
||||
"#,
|
||||
);
|
||||
|
||||
let selected_with_tooltip_button = StoryItem::new(
|
||||
"Selected with `tooltip`",
|
||||
IconButton::new("selected_with_tooltip_button", Icon::InlayHint)
|
||||
.selected(true)
|
||||
.tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)),
|
||||
)
|
||||
.description("Displays a selected icon button with tooltip.")
|
||||
.usage(
|
||||
r#"
|
||||
IconButton::new("selected_with_tooltip_button", Icon::InlayHint)
|
||||
.selected(true)
|
||||
.tooltip(|cx| Tooltip::text("Toggle inlay hints", cx))
|
||||
"#,
|
||||
);
|
||||
|
||||
let buttons = vec![
|
||||
default_button,
|
||||
selected_button,
|
||||
selected_with_selected_icon,
|
||||
disabled_button,
|
||||
with_on_click_button,
|
||||
with_tooltip_button,
|
||||
selected_with_tooltip_button,
|
||||
];
|
||||
|
||||
StoryContainer::new(
|
||||
"Icon Button",
|
||||
"crates/ui2/src/components/stories/icon_button.rs",
|
||||
)
|
||||
.children(vec![StorySection::new().children(buttons)])
|
||||
.into_element()
|
||||
|
||||
// Story::container()
|
||||
// .child(Story::title_for::<IconButton>())
|
||||
// .child(Story::label("Default"))
|
||||
// .child(div().w_8().child(IconButton::new("icon_a", Icon::Hash)))
|
||||
// .child(Story::label("Selected"))
|
||||
// .child(
|
||||
// div()
|
||||
// .w_8()
|
||||
// .child(IconButton::new("icon_a", Icon::Hash).selected(true)),
|
||||
// )
|
||||
// .child(Story::label("Selected with `selected_icon`"))
|
||||
// .child(
|
||||
// div().w_8().child(
|
||||
// IconButton::new("icon_a", Icon::AudioOn)
|
||||
// .selected(true)
|
||||
// .selected_icon(Icon::AudioOff),
|
||||
// ),
|
||||
// )
|
||||
// .child(Story::label("Disabled"))
|
||||
// .child(
|
||||
// div()
|
||||
// .w_8()
|
||||
// .child(IconButton::new("icon_a", Icon::Hash).disabled(true)),
|
||||
// )
|
||||
// .child(Story::label("With `on_click`"))
|
||||
// .child(
|
||||
// div()
|
||||
// .w_8()
|
||||
// .child(
|
||||
// IconButton::new("with_on_click", Icon::Ai).on_click(|_event, _cx| {
|
||||
// println!("Clicked!");
|
||||
// }),
|
||||
// ),
|
||||
// )
|
||||
// .child(Story::label("With `tooltip`"))
|
||||
// .child(
|
||||
// div().w_8().child(
|
||||
// IconButton::new("with_tooltip", Icon::MessageBubbles)
|
||||
// .tooltip(|cx| Tooltip::text("Open messages", cx)),
|
||||
// ),
|
||||
// )
|
||||
// .child(Story::label("Selected with `tooltip`"))
|
||||
// .child(
|
||||
// div().w_8().child(
|
||||
// IconButton::new("selected_with_tooltip", Icon::InlayHint)
|
||||
// .selected(true)
|
||||
// .tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)),
|
||||
// ),
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user