1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
//! Savory is library for building user interface. //! //! [![master docs](https://img.shields.io/badge/docs-master-blue.svg)](https://malrusayni.gitlab.io/savory/savory-core/) //! · //! [![crate info](https://img.shields.io/crates/v/savory.svg)](https://crates.io/crates/savory-core) //! · //! [![pipeline](https://gitlab.com/MAlrusayni/savory/badges/master/pipeline.svg)](https://gitlab.com/MAlrusayni/savory/pipelines) //! · //! [![rustc version](https://img.shields.io/badge/rustc-stable-green.svg)](https://crates.io/crates/savory) //! · //! [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) //! //! //! # Features //! //! - **Views**: Views can be any type implement `View` trait or any standalone //! function that returns `Node`, views can be trait object which make them very //! composable. //! - **Elements**: Savory uses elements as core building unit when building //! stateful UI. Elements owns thier state and handle user inputs via messages. //! - **Collection of UI elements**: Savory ships with collection of resuable and //! themeable UI elements. //! - **Theme**: UI elements can be themed by any type that implement `ThemeImpl` //! trait, themes have full control on the element appearance. //! - **Typed HTML**: Use typed CSS and HTML attributes, Savory try hard not to rely //! on strings when creating CSS and HTML attributes since these can produce hard //! to debug bugs. //! - **Enhance Seed API**: Enhancement on Seed API that makes working with `Node`, //! `Orders` fun. //! //! Savory tries to make writing UI elements fun and boilerplate free. //! //! Savory crates: //! - `savory`: savory CLI //! - [`savory-core`]: Library for building user interface (this crate) //! - [`savory-html`]: Typed HTML for Savory //! - [`savory-elements`]: UI Elements based on Savory //! - [`savory-derive`]: Helper derives //! //! # Core Concept //! //! Savory have two main types **View** and **Element**, View types produce //! static HTML, while Element types produce interactive HTML, as simple as //! that. //! //! Elements types must implemente [`Element`] and [`View`] traits, which would //! make them interactive. //! //! View types must implemente [`View`] trait, that would produce the static //! HTML. //! //! # Counter Example //! //! Here is very simple counter, that doesn't use all Savory features, but it's //! good as starting point for newcomers. //! //! ``` rust //! use savory_core::prelude::*; //! use savory_html::prelude::*; //! use wasm_bindgen::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().add("Increment").on_click(|_| Msg::Increment); //! let dec_btn = html::button().add("Decrement").on_click(|_| Msg::Decrement); //! //! html::div() //! .add(inc_btn) //! .add(self.0.to_string()) //! .add(dec_btn) //! } //! } //! //! #[wasm_bindgen(start)] //! pub fn view() { //! // mount and start the app at `app` element //! Counter::start(); //! } //! ``` //! //! [`View`]: crate::prelude::View //! [`Element`]: crate::prelude::Element //! [`savory-core`]: https://gitlab.com/MAlrusayni/savory/tree/master/core //! [`savory-html`]: https://gitlab.com/MAlrusayni/savory/tree/master/html //! [`savory-elements`]: https://gitlab.com/MAlrusayni/savory/tree/master/elements //! [`savory-derive`]: https://gitlab.com/MAlrusayni/savory/tree/master #![forbid(unsafe_code)] pub mod element; pub mod orders_ext; pub mod view; /// savory prelude. pub mod prelude { pub use crate::{ element::{AppElementExt, Element}, orders_ext::OrdersExt, view::View, }; pub use seed::prelude::{MessageMapper, Node, Orders, Url}; }