2 个不稳定版本
0.6.0 | 2021 年 3 月 6 日 |
---|---|
0.5.1 | 2020 年 5 月 9 日 |
#605 in GUI
230KB
6K SLoC
Savory
Rust / Wasm 前端库,用于构建用户界面。
Savory 是一个基于 Seed 的用户界面构建库
特性
- 设计系统:元素完全使用
DesignSystem
样式化。 - 可复用性:元素高度可复用/可组合。
- 解耦开发:设计系统可以在不接触元素代码的情况下独立开发,同样,元素开发也可以独立于设计系统进行,这要归功于
DesignSystemImpl
特性。 - 干净的视图:以干净和声明性的方式构建您的视图,不再需要任何宏。
- 基于特性的:拥抱 Rust 特性系统,所有 Savory 元素都实现了
Element
和/或View
特性。 - 类型化 HTML:使用类型化的 CSS 和 HTML 属性,Savory 尽量不依赖于字符串来创建 CSS 和 HTML 属性,因为这些可能导致难以调试的错误。
- UI 元素集合:Savory 随带了一系列可复用和可主题化的 UI 元素。
- 增强 Seed API:对 Seed API 的增强,使得使用
Node
、Orders
更加有趣。
Savory 努力使编写 UI 元素变得有趣且无需样板代码。
截图
入门
快速入门的最快方法是遵循 Savory 入门 仓库说明。
示例
在这里,我们将创建一个与Elm教程中相同的计数器应用程序,然后我们将编写同一个应用程序,但使用具有样式和可重用元素的。
简单计数器
use savory::prelude::*;
// app element (the model)
pub struct Counter(i32);
// app message
pub enum Msg {
Increment,
Decrement,
}
impl Element for Counter {
type Message = Msg;
type Config = Url;
// initialize the app in this function
fn init(_: Url, _: &mut impl Orders<Msg>) -> Self {
Self(0)
}
// handle app messages
fn update(&mut self, msg: Msg, _: &mut impl Orders<Msg>) {
match msg {
Msg::Increment => self.0 += 1,
Msg::Decrement => self.0 -= 1,
}
}
}
impl View<Node<Msg>> for Counter {
// view the app
fn view(&self) -> Node<Msg> {
let inc_btn = html::button().push("Increment").on_click(|_| Msg::Increment);
let dec_btn = html::button().push("Decrement").on_click(|_| Msg::Decrement);
html::div()
.push(inc_btn)
.push(self.0.to_string())
.push(dec_btn)
}
}
#[wasm_bindgen(start)]
pub fn view() {
// mount and start the app at `app` element
Counter::start();
}
预览:
作为元素的计数器
现在我们将创建计数器元素和应用程序元素,这展示了如何创建父元素和子元素,以及如何创建可重用和可定制的元素。
use savory::prelude::*;
use savory_elements::prelude::*;
use savory_style::{
css::{unit::px, values as val, Color, St},
prelude::*,
};
#[derive(Element)]
pub struct Counter {
#[element(config(default = "10"))]
value: i32,
}
pub enum Msg {
Increment,
Decrement,
}
impl Element for Counter {
type Message = Msg;
type Config = Config;
fn init(config: Self::Config, _: &mut impl Orders<Msg>) -> Self {
Self {
value: config.value,
}
}
fn update(&mut self, msg: Msg, _: &mut impl Orders<Msg>) {
match msg {
Msg::Increment => self.value += 1,
Msg::Decrement => self.value -= 1,
}
}
}
impl View<Node<Msg>> for Counter {
fn view(&self) -> Node<Msg> {
// sharde style for buttons
let style_btns = |conf: css::Style| {
conf.push(St::Appearance, val::None)
.background(Color::SlateBlue)
.text(Color::White)
.and_border(|conf| conf.none().radius(px(4)))
.margin(px(4))
.padding(px(4))
};
// increment button node
let inc_btn = html::button()
.class("inc-btn")
.and_style(style_btns)
.on_click(|_| Msg::Increment)
.push("Increment");
// decrement button node
let dec_btn = html::button()
.class("dec-btn")
.and_style(style_btns)
.on_click(|_| Msg::Decrement)
.push("Decrement");
// contianer node
html::div()
.push(dec_btn)
.push(self.value.to_string())
.push(inc_btn)
}
}
// convenient way to convert Config into Counter
impl Config {
pub fn init(self, orders: &mut impl Orders<Msg>) -> Counter {
Counter::init(self, orders)
}
}
// App Element ---
pub enum AppMsg {
Counter(Msg),
}
pub struct MyApp {
counter_element: Counter,
}
impl Element for MyApp {
type Message = AppMsg;
type Config = Url;
fn init(_: Url, orders: &mut impl Orders<AppMsg>) -> Self {
Self {
counter_element: Counter::config()
// give it starting value. 10 will be used as default value if
// we didn't pass value
.value(100)
.init(&mut orders.proxy(AppMsg::Counter)),
}
}
fn update(&mut self, msg: AppMsg, orders: &mut impl Orders<AppMsg>) {
match msg {
AppMsg::Counter(msg) => self
.counter_element
.update(msg, &mut orders.proxy(AppMsg::Counter)),
}
}
}
impl View<Node<AppMsg>> for MyApp {
fn view(&self) -> Node<AppMsg> {
self.counter_element.view().map_msg(AppMsg::Counter)
}
}
#[wasm_bindgen(start)]
pub fn view() {
// mount and start the app at `app` element
MyApp::start();
}
预览:
在这个示例中有许多事情发生,首先我们创建了一个元素结构Counter
,并定义了其属性、事件和样式类型,这一切都是通过Element
宏完成的,我们将在稍后解释它是如何工作的,然后我们定义了一个包含计数器元素的应用程序元素,并在init
函数中初始化它。最后,我们只需调用start
方法来挂载和启动应用程序。
使用Savory Elements的计数器!
Savory附带了一系列元素,我们将使用它们来构建计数器应用程序并查看Savory元素为我们提供了哪些功能。
待办事项:添加示例
生态系统
savory
- 构建用户界面的核心库savory-router
- 用于生成应用程序路由的Savory路由器savory-style
- Savory的强类型CSS样式savory-elements
- 基于 Savory 的 UI 元素集合savory-elements-derive
- 提供代码Element
派生的包
许可证
根据您选择,受Apache许可证版本2.0或MIT许可证的许可。
除非您明确声明,否则您提交给Savory的任何贡献,根据Apache-2.0许可证的定义,将如上双许可,不附加任何额外条款或条件。
依赖关系
~19MB
~343K SLoC