Add Link (#91)

This commit is contained in:
Jason Lee 2024-07-31 14:24:57 +08:00 committed by GitHub
parent d659713555
commit d1c19c5912
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 118 additions and 12 deletions

View file

@ -22,6 +22,7 @@ A UI components for building desktop application by using [GPUI](https://gpui.rs
- [x] IconButton - [x] IconButton
- [x] Glost / Outline Button - [x] Glost / Outline Button
- [x] Loading - [x] Loading
- [x] Link
- [x] Label - [x] Label
- [x] Icon - [x] Icon
- [x] Checkbox - [x] Checkbox

View file

@ -1,9 +1,9 @@
use gpui::*; use gpui::*;
use prelude::FluentBuilder as _; use prelude::FluentBuilder as _;
use story::{ use story::{
ButtonStory, CheckboxStory, DropdownStory, IconStory, ImageStory, InputStory, ListStory, ButtonStory, DropdownStory, IconStory, ImageStory, InputStory, ListStory, PickerStory,
PickerStory, PopoverStory, ProgressStory, ResizableStory, ScrollableStory, StoryContainer, PopoverStory, ProgressStory, ResizableStory, ScrollableStory, StoryContainer, SwitchStory,
SwitchStory, TableStory, TooltipStory, TableStory, TextStory, TooltipStory,
}; };
use workspace::{TitleBar, Workspace}; use workspace::{TitleBar, Workspace};
@ -60,9 +60,9 @@ impl StoryWorkspace {
.detach(); .detach();
StoryContainer::add_pane( StoryContainer::add_pane(
"Checkbox", "Text",
"A control that allows the user to toggle between checked and not checked.", "Links, paragraphs, checkboxes, and more.",
CheckboxStory::view(cx).into(), TextStory::view(cx).into(),
workspace.clone(), workspace.clone(),
cx, cx,
) )

View file

@ -1,5 +1,4 @@
mod button_story; mod button_story;
mod checkbox_story;
mod dropdown_story; mod dropdown_story;
mod icon_story; mod icon_story;
mod image_story; mod image_story;
@ -12,11 +11,11 @@ mod resizable_story;
mod scrollable_story; mod scrollable_story;
mod switch_story; mod switch_story;
mod table_story; mod table_story;
mod text_story;
mod tooltip_story; mod tooltip_story;
mod webview_story; mod webview_story;
pub use button_story::ButtonStory; pub use button_story::ButtonStory;
pub use checkbox_story::CheckboxStory;
pub use dropdown_story::DropdownStory; pub use dropdown_story::DropdownStory;
pub use icon_story::IconStory; pub use icon_story::IconStory;
pub use image_story::ImageStory; pub use image_story::ImageStory;
@ -29,6 +28,7 @@ pub use resizable_story::ResizableStory;
pub use scrollable_story::ScrollableStory; pub use scrollable_story::ScrollableStory;
pub use switch_story::SwitchStory; pub use switch_story::SwitchStory;
pub use table_story::TableStory; pub use table_story::TableStory;
pub use text_story::TextStory;
pub use tooltip_story::TooltipStory; pub use tooltip_story::TooltipStory;
pub use webview_story::WebViewStory; pub use webview_story::WebViewStory;

View file

@ -8,13 +8,14 @@ use ui::{
checkbox::Checkbox, checkbox::Checkbox,
h_flex, h_flex,
label::Label, label::Label,
link::Link,
radio::Radio, radio::Radio,
v_flex, Clickable, Disableable as _, IconName, Selection, StyledExt, v_flex, Clickable, Disableable as _, IconName, Selection, StyledExt,
}; };
use crate::section; use crate::section;
pub struct CheckboxStory { pub struct TextStory {
check1: Selection, check1: Selection,
check2: Selection, check2: Selection,
check3: Selection, check3: Selection,
@ -23,7 +24,7 @@ pub struct CheckboxStory {
masked: bool, masked: bool,
} }
impl CheckboxStory { impl TextStory {
pub(crate) fn new(_cx: &mut WindowContext) -> Self { pub(crate) fn new(_cx: &mut WindowContext) -> Self {
Self { Self {
check1: Selection::Unselected, check1: Selection::Unselected,
@ -45,7 +46,7 @@ impl CheckboxStory {
} }
} }
impl Render for CheckboxStory { impl Render for TextStory {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex() v_flex()
.gap_6() .gap_6()
@ -74,7 +75,19 @@ impl Render for CheckboxStory {
.text_left() .text_left()
.line_height(rems(1.8)), .line_height(rems(1.8)),
), ),
), )
)
.child(
section("Link", cx).child(
h_flex().items_start().gap_3()
.child(Link::new("link1").href("https://github.com").child("GitHub"))
.child(Link::new("link2").href("https://github.com").text_color(ui::red_500()).text_decoration_color(ui::red_500()).child("Red Link"))
.child(Link::new("link3").child(h_flex().gap_1().child(IconName::GitHub).child("GitHub")).on_click(cx.listener(|_, _, cx| {
cx.open_url("https://google.com")
})))
.child(div().w(px(250.)).child(Link::new("link4").child("https://github.com/huacnlee/gpui-component").href("https://github.com/huacnlee/gpui-component")))
)
) )
.child( .child(
section("Maksed Label", cx).child( section("Maksed Label", cx).child(

View file

@ -17,6 +17,7 @@ pub mod dropdown;
pub mod indicator; pub mod indicator;
pub mod input; pub mod input;
pub mod label; pub mod label;
pub mod link;
pub mod list; pub mod list;
pub mod popover; pub mod popover;
pub mod popup_menu; pub mod popup_menu;

86
crates/ui/src/link.rs Normal file
View file

@ -0,0 +1,86 @@
use gpui::{
div, ClickEvent, Div, ElementId, InteractiveElement, IntoElement, MouseButton, ParentElement,
RenderOnce, SharedString, Stateful, StatefulInteractiveElement, Styled,
};
use crate::theme::{ActiveTheme as _, Colorize};
#[derive(IntoElement)]
pub struct Link {
base: Stateful<Div>,
href: Option<SharedString>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut gpui::WindowContext) + 'static>>,
}
impl Link {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
base: div().id(id),
href: None,
on_click: None,
}
}
pub fn href(mut self, href: impl Into<SharedString>) -> Self {
self.href = Some(href.into());
self
}
pub fn on_click(
mut self,
handler: impl Fn(&ClickEvent, &mut gpui::WindowContext) + 'static,
) -> Self {
self.on_click = Some(Box::new(handler));
self
}
}
impl Styled for Link {
fn style(&mut self) -> &mut gpui::StyleRefinement {
self.base.style()
}
}
impl ParentElement for Link {
fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
self.base.extend(elements)
}
}
impl RenderOnce for Link {
fn render(self, cx: &mut gpui::WindowContext) -> impl IntoElement {
let href = self.href.clone();
let on_click = self.on_click;
div()
.text_color(cx.theme().link)
.text_decoration_1()
.text_decoration_color(cx.theme().link)
.hover(|this| {
this.text_color(cx.theme().link.opacity(0.8))
.text_decoration_1()
})
.cursor_pointer()
.child(
self.base
.active(|this| {
this.text_color(cx.theme().link.opacity(0.6))
.text_decoration_1()
})
.on_mouse_down(MouseButton::Left, |_, cx| {
cx.prevent_default();
cx.stop_propagation();
})
.on_click({
move |e, cx| {
if let Some(href) = &href {
cx.open_url(&href.clone());
}
if let Some(on_click) = &on_click {
on_click(e, cx);
}
}
}),
)
}
}

View file

@ -161,6 +161,7 @@ struct Colors {
pub list_even: Hsla, pub list_even: Hsla,
pub list_active: Hsla, pub list_active: Hsla,
pub list_head: Hsla, pub list_head: Hsla,
pub link: Hsla,
} }
impl Colors { impl Colors {
@ -201,6 +202,7 @@ impl Colors {
list_even: hsl(240.0, 5.0, 96.0), list_even: hsl(240.0, 5.0, 96.0),
list_active: hsl(240.0, 7., 88.0), list_active: hsl(240.0, 7., 88.0),
list_head: hsl(240.0, 0., 94.), list_head: hsl(240.0, 0., 94.),
link: hsl(221.0, 83.0, 53.0),
} }
} }
@ -241,6 +243,7 @@ impl Colors {
list_even: hsl(240.0, 3.7, 8.0), list_even: hsl(240.0, 3.7, 8.0),
list_active: hsl(240.0, 3.7, 15.0), list_active: hsl(240.0, 3.7, 15.0),
list_head: hsl(240.0, 3.7, 10.9), list_head: hsl(240.0, 3.7, 10.9),
link: hsl(221.0, 83.0, 53.0),
} }
} }
} }
@ -304,6 +307,7 @@ pub struct Theme {
pub table_head: Hsla, pub table_head: Hsla,
pub table_active: Hsla, pub table_active: Hsla,
pub table_hover: Hsla, pub table_hover: Hsla,
pub link: Hsla,
} }
impl Global for Theme {} impl Global for Theme {}
@ -379,6 +383,7 @@ impl From<Colors> for Theme {
table_even: colors.list_even, table_even: colors.list_even,
table_active: colors.list_active, table_active: colors.list_active,
table_hover: colors.list_active.opacity(0.6), table_hover: colors.list_active.opacity(0.6),
link: colors.link,
} }
} }
} }