38 个版本
0.2.0 | 2023年12月20日 |
---|---|
0.1.41 | 2023年8月13日 |
0.1.36 | 2023年3月4日 |
0.1.21 | 2023年2月28日 |
2205 在 网页编程 中
265 每月下载
14KB
Styled:Leptos 组件的简易样式化
如果您正在寻找一种轻松将局部作用域样式应用于您的 Leptos
组件的方法,那么 Styled
是您需要的 Leptos 宏。使用 Styled
,您可以应用高级选择器,如 button
或 div
,到特定的组件上,保持您的标记清洁和组织。
安装
在项目根目录中使用 cargo add
cargo add styled stylist
使用方法
首先创建一个基本的 Leptos
组件。这将是本指南的基础。
#[component]
pub fn MyComponent(cx: Scope) -> impl IntoView{
view! {
cx,
<div>"hello"</div>
}
}
接下来,导入由一个名为 Stylist
的出色 crate 提供的 style
宏,以创建您的样式。只需将此添加到文件顶部。
use styled::style;
然后,您可以使用 style
宏创建一个包含您的样式的 Result
。让我们修改我们的组件
#[component]
pub fn MyComponent(cx: Scope) -> impl IntoView{
let styles = style!(
div {
background-color: red;
color: white;
}
);
view! {
cx,
<div>"hello"</div>
}
}
现在,让我们使用我们的 styled::view!
宏来应用这些样式!
#[component]
pub fn MyComponent(cx: Scope) -> impl IntoView {
let styles = style!(
div {
background-color: red;
color: white;
}
);
styled::view! {
cx,
styles,
<div>"This text should be red with white text."</div>
}
}
现在我们可以定义另一个组件,它也使用 div
CSS 选择器,但它的样式只会应用于其封装的 styled::view!
宏内的元素。
#[component]
pub fn AnotherComponent(cx: Scope) -> impl IntoView {
// note were using a plain div selector and it wont clash with MyComponent's div style!
let styles = style!(
div {
background-color: blue;
color: gray;
}
);
styled::view! {
cx,
styles,
<div>"This text should be blue with gray text."</div>
}
}
更长的示例
// /src/components/button.rs
use crate::theme::get_theme;
use leptos::*;
use styled::style;
#[derive(PartialEq)]
pub enum Variant {
PRIMARY,
SECONDARY,
ALERT,
DISABLED,
}
impl Variant {
pub fn is(&self, variant: &Variant) -> bool {
self == variant
}
}
struct ButtonColors {
text: String,
background: String,
border: String,
}
fn get_colors(variant: &Variant) -> ButtonColors {
let theme = get_theme().unwrap();
match variant {
Variant::PRIMARY => ButtonColors {
text: theme.white(),
background: theme.black(),
border: theme.transparent(),
},
Variant::SECONDARY => ButtonColors {
text: theme.black(),
background: theme.white(),
border: theme.gray.lightest(),
},
Variant::ALERT => ButtonColors {
text: theme.white(),
background: theme.red(),
border: theme.transparent(),
},
Variant::DISABLED => ButtonColors {
text: theme.white(),
background: theme.red(),
border: theme.transparent(),
},
}
}
#[component]
pub fn Button(cx: Scope, variant: Variant) -> impl IntoView {
let disabled = variant.is(&Variant::DISABLED);
let styles = styles(&variant);
styled::view! {
cx,
styles,
<button disabled=disabled>"Button"</button>
}
}
fn styles<'a>(variant: &Variant) -> styled::Result<styled::Style> {
let colors = get_colors(variant);
style!(
button {
color: ${colors.text};
background-color: ${colors.background};
border: 1px solid ${colors.border};
outline: none;
height: 48px;
min-width: 154px;
font-size: 14px;
font-weight: 700;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 2px 0px;
position: relative;
box-sizing: border-box;
vertical-align: middle;
text-align: center;
text-overflow: ellipsis;
text-transform: uppercase;
overflow: hidden;
cursor: pointer;
transition: box-shadow 0.2s;
margin: 10px;
}
& button:active {
transform: scale(0.99);
}
& button::-moz-focus-inner {
border: none;
}
& button::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgb(255, 255, 255);
opacity: 0;
transition: opacity 0.2s;
}
& button::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
border-radius: 50%;
padding: 50%;
background-color: ${colors.text};
opacity: 0;
transform: translate(-50%, -50%) scale(1);
transition: opacity 1s, transform 0.5s;
}
& button:hover,
& button:focus {
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);
}
& button:hover::before {
opacity: 0.08;
}
& button:hover:focus::before {
opacity: 0.3;
}
& button:active {
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
& button:active::after {
opacity: 0.32;
transform: translate(-50%, -50%) scale(0);
transition: transform 0s;
}
& button:disabled {
color: rgba(0, 0, 0, 0.28);
background-color: rgba(0, 0, 0, 0.12);
box-shadow: none;
cursor: initial;
}
& button:disabled::before {
opacity: 0;
}
& button:disabled::after {
opacity: 0;
}
)
}
// /src/theme/mod.rs
use csscolorparser::Color;
pub fn get_theme() -> Result<Theme, csscolorparser::ParseColorError> {
let theme = Theme {
teal: Colors {
main: Color::from_html("#6FDDDB")?,
darker: Color::from_html("#2BB4B2")?,
lighter: Color::from_html("#7EE1DF")?,
lightest: Color::from_html("#B2EDEC")?,
},
pink: Colors {
main: Color::from_html("#E93EF5")?,
darker: Color::from_html("#C70BD4")?,
lighter: Color::from_html("#F5A4FA")?,
lightest: Color::from_html("#FCE1FD")?,
},
green: Colors {
main: Color::from_html("#54D072")?,
darker: Color::from_html("#30AF4F")?,
lighter: Color::from_html("#82DD98")?,
lightest: Color::from_html("#B4EAC1")?,
},
purple: Colors {
main: Color::from_html("#8C18FB")?,
darker: Color::from_html("#7204DB")?,
lighter: Color::from_html("#B162FC")?,
lightest: Color::from_html("#D0A1FD")?,
},
yellow: Colors {
main: Color::from_html("#E1E862")?,
darker: Color::from_html("#BAC31D")?,
lighter: Color::from_html("#EFF3AC")?,
lightest: Color::from_html("#FAFBE3")?,
},
gray: Colors {
main: Color::from_html("#4a4a4a")?,
darker: Color::from_html("#3d3d3d")?,
lighter: Color::from_html("#939393")?,
lightest: Color::from_html("#c4c4c4")?,
},
red: Color::from_html("#FF5854")?,
black: Color::from_html("#000000")?,
white: Color::from_html("#FFFFFF")?,
transparent: Color::from_html("transparent")?,
};
Ok(theme)
}
pub struct Theme {
pub teal: Colors,
pub pink: Colors,
pub green: Colors,
pub purple: Colors,
pub yellow: Colors,
pub gray: Colors,
pub red: Color,
pub black: Color,
pub white: Color,
pub transparent: Color,
}
pub struct Colors {
pub main: Color,
pub darker: Color,
pub lighter: Color,
pub lightest: Color,
}
impl Colors {
pub fn main(&self) -> String {
self.main.to_hex_string()
}
pub fn darker(&self) -> String {
self.darker.to_hex_string()
}
pub fn lighter(&self) -> String {
self.lighter.to_hex_string()
}
pub fn lightest(&self) -> String {
self.lightest.to_hex_string()
}
}
impl Theme {
pub fn red(&self) -> String {
self.red.to_hex_string()
}
pub fn black(&self) -> String {
self.black.to_hex_string()
}
pub fn white(&self) -> String {
self.white.to_hex_string()
}
pub fn transparent(&self) -> String {
self.transparent.to_hex_string()
}
}
// /src/app.rs
#[component]
fn HomePage(cx: Scope) -> impl IntoView {
view! { cx,
<Button variant={button::Variant::PRIMARY}/>
<Button variant={button::Variant::SECONDARY}/>
<Button variant={button::Variant::ALERT}/>
}
}
依赖项
~19–32MB
~524K SLoC