Skip to content

Commit

Permalink
more comprehensive docs; tag 0.1.6
Browse files Browse the repository at this point in the history
  • Loading branch information
tiye committed Jun 18, 2024
1 parent 17aa1d8 commit f6b883f
Show file tree
Hide file tree
Showing 18 changed files with 118 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion demo_respo/src/counter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use std::fmt::Debug;

use respo::{button, div, span, ui::ui_button, util, CssColor, DispatchFn, RespoElement, RespoEvent, RespoStyle};
use respo::{
button,
css::{CssColor, RespoStyle},
div, span,
ui::ui_button,
util, DispatchFn, RespoElement, RespoEvent,
};
use respo_state_derive::RespoState;
use serde::{Deserialize, Serialize};

Expand Down
2 changes: 1 addition & 1 deletion demo_respo/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use std::rc::Rc;
use web_sys::Node;

use respo::ui::ui_global;
use respo::{css::RespoStyle, util, RespoApp, RespoNode, RespoStore};
use respo::{div, util::query_select_node};
use respo::{util, RespoApp, RespoNode, RespoStore, RespoStyle};

use self::counter::comp_counter;
pub use self::store::ActionOp;
Expand Down
2 changes: 1 addition & 1 deletion demo_respo/src/plugins.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use respo::ui::{ui_button_danger, ui_button_primary};
use respo::{space, ui::ui_row_parted, RespoStyle};
use respo::{css::RespoStyle, space, ui::ui_row_parted};
use respo::{RespoElement, RespoEvent};

use respo::{button, div, span, ui::ui_button, util, DispatchFn};
Expand Down
14 changes: 9 additions & 5 deletions demo_respo/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ impl Hash for Task {
pub enum ActionOp {
#[default]
Noop,
Increment,
Decrement,
/// contains State and Value
StatesChange(RespoUpdateState),
Increment,
Decrement,
AddTask(String, String),
RemoveTask(String),
UpdateTask(String, String),
Expand All @@ -54,21 +54,25 @@ impl RespoAction for ActionOp {
impl RespoStore for Store {
type Action = ActionOp;

fn get_states(&mut self) -> &mut RespoStatesTree {
&mut self.states
}

fn update(&mut self, op: Self::Action) -> Result<(), String> {
use ActionOp::*;
match op {
Noop => {
// nothing to to
}
StatesChange(a) => {
self.update_states(a);
}
Increment => {
self.counted += 1;
}
Decrement => {
self.counted -= 1;
}
StatesChange(a) => {
self.states.set_in_mut(a);
}
AddTask(id, content) => self.tasks.push(Task {
id,
content,
Expand Down
6 changes: 4 additions & 2 deletions demo_respo/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use std::fmt::Debug;

use memoize::memoize;
use respo::{
button, div, input, space, span, static_styles,
button,
css::{CssColor, CssSize, RespoStyle},
div, input, space, span, static_styles,
ui::{ui_button, ui_center, ui_input, ui_row_middle},
util, CssColor, CssSize, DispatchFn, RespoComponent, RespoEffect, RespoEvent, RespoNode, RespoStyle,
util, DispatchFn, RespoComponent, RespoEffect, RespoEvent, RespoNode,
};

use respo::states_tree::{RespoState, RespoStatesTree};
Expand Down
2 changes: 1 addition & 1 deletion respo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "respo"
version = "0.1.5"
version = "0.1.6"
edition = "2021"
description = "a tiny virtual DOM library migrated from ClojureScript"
license = "Apache-2.0"
Expand Down
13 changes: 12 additions & 1 deletion respo/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use web_sys::{BeforeUnloadEvent, Node};

use renderer::render_node;

use crate::node::{DispatchFn, RespoAction, RespoNode};
use crate::{
node::{DispatchFn, RespoAction, RespoNode},
states_tree::{RespoStatesTree, RespoUpdateState},
};

const RESPO_APP_STORE_KEY: &str = "respo_app_respo_store_default";

Expand Down Expand Up @@ -132,6 +135,14 @@ pub trait RespoStore {
type Action: Debug + Clone + RespoAction;
fn update(&mut self, op: Self::Action) -> Result<(), String>;

/// a way to load states tree
fn get_states(&mut self) -> &mut RespoStatesTree;

/// public API for updating states tree
fn update_states(&mut self, op: RespoUpdateState) {
self.get_states().set_in_mut(op);
}

/// for backup
fn to_string(&self) -> String;

Expand Down
2 changes: 1 addition & 1 deletion respo/src/app/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use web_sys::console::warn_1;

use crate::node::{RespoComponent, RespoEffectType, RespoEvent, RespoEventMark, RespoEventMarkFn, RespoNode};

use crate::load_coord_target_tree;
use super::renderer::load_coord_target_tree;
use crate::node::dom_change::{ChildDomOp, DomChange, RespoCoord};

use crate::app::renderer::build_dom_tree;
Expand Down
4 changes: 2 additions & 2 deletions respo/src/app/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub(crate) fn mark_need_rerender() {
}

/// render elements
pub fn render_node<T, U>(
pub(crate) fn render_node<T, U>(
mount_target: Node,
// TODO it copies the whole store, need to optimize
get_store: Box<dyn Fn() -> U>,
Expand Down Expand Up @@ -150,7 +150,7 @@ where
Ok(())
}

pub fn load_coord_target_tree<T>(tree: &RespoNode<T>, coord: &[RespoCoord]) -> Result<RespoNode<T>, String>
pub(crate) fn load_coord_target_tree<T>(tree: &RespoNode<T>, coord: &[RespoCoord]) -> Result<RespoNode<T>, String>
where
T: Debug + Clone,
{
Expand Down
8 changes: 6 additions & 2 deletions respo/src/app/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use wasm_bindgen::prelude::Closure;
use wasm_bindgen::JsCast;
use web_sys::Node;

/// this one uses `requestAnimationFrame` for calling
#[allow(dead_code)]
pub fn raf_loop(mut cb: Box<dyn FnMut() -> Result<(), String>>) {
let f_ = Rc::new(RefCell::new(None));
Expand Down Expand Up @@ -30,7 +31,8 @@ fn window() -> web_sys::Window {
web_sys::window().expect("no global `window` exists")
}

/// this API is used for development, prefer `req_loop` for fast response
/// uses `requestAnimationFrame` for calling, but with a interval to reduce cost.
/// prefer `req_loop` if you want to be faster
#[allow(dead_code)]
pub fn raf_loop_slow(interval: i32, mut cb: Box<dyn FnMut() -> Result<(), String>>) {
let f = Rc::new(RefCell::new(None));
Expand Down Expand Up @@ -76,7 +78,9 @@ pub fn query_select_node(pattern: &str) -> Result<Node, String> {
}
}

/// wraps on top of `web_sys::console.log_1`, use it like:
/// wraps on top of `web_sys::console.log_1`.
///
/// use it like:
/// ```ignore
/// util::log!("a is {}", a);
/// ```
Expand Down
56 changes: 37 additions & 19 deletions respo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,53 @@
//! A tiny framework around a **virtual DOM** library, compiles to WebAssembly, runs in the browser, declarative UI for building interactive web apps.
//! Tiny **virtual DOM** library, compiles to WebAssembly, runs in browser, building interactive web apps with declarative code.
//!
//! Original design was [Respo.cljs](http://respo-mvc.org/), which is heavily influenced by React.js and ClojureScript.
//! This module is experimental since WebAssembly lacks of hot reloading.
//! This library is experimental, heavily influenced by React.js and ClojureScript.
//! Previously implementeded in [ClojureScript](https://github.com/Respo/respo.cljs) and [Calcit](https://github.com/Respo/respo.calcit).
//!
//! It features:
//! ![Respo Logo](https://cdn.tiye.me/logo/respo.png)
//!
//! - virtual DOM(however simplified in list diffing)
//! - components declaration with functions
//! - globals states with Store and Actions dispatching
//! - states tree with nested states(inherited from Respo.cljs , might be strange)
//! - CSS with Rust macros
//! - basic component effects of `Mounted, WillUpdate, Updated, WillUnmount`
//! - okay to [memoize](https://crates.io/crates/memoize) component functions
//! To build UI:
//!
//! Meanwhile it does not include features like:
//! - there's Virtual DOM, although simplified, still flexible for declarative UI
//! - CSS with Rust macros, I call it "CSS in Rust"
//! - Effects, flow from data to DOM, for patching DOM manually on data change
//! - `respo::ui` provides basic style. Also try Modal, dialog, drawer components.
//!
//! - ❌ macros for JSX syntax. Respo prefer types over tags
//! - ❌ updating component states in lifecycle. Respo enforces "unidirectional data flow"
//! - ❌ React-like hooks. Respo uses plain functions without tricky internal states
//! - ❌ Hot swapping. Respo.rs reload on edit and loads previous states from local storage.
//! To manage states:
//!
//! - Rust enum and pattern matching it really nice for Elm-style action dispatching
//! - global states tree and cursor to maintain states, may not be familiar but still being handy
//! - you may also write shared component like a "plugin" to manage states.
//!
//! To optimize:
//!
//! - components and elements are in functions, available for [memoize](https://crates.io/crates/memoize)
//! - well, it's Rust, you can do more...
//!
//! Meanwhile it does not support React features such as:
//!
//! - ❌ updating data from render. Respo enforces "unidirectional data flow". That's not allowed
//! - ❌ hooks API and context. Respo uses plain functions without tricky internal states
//! - ...does not have many other advanced features from React
//!
//! Rust and WebAssembly lacks tricks for hot reloading,
//! it's suggested to use [trunk](https://github.com/trunk-rs/trunk) to edit and reload the project during development.
//! App states including components states can be saved to local storage and reloaded.
//!
//! To start project, create your structs to implement traits:
//!
//! - `RespoStore` for global states and states tree, and `RespoAction` for updating
//! - `RespoApp` for MVC overview of the app, and more views, bind events
//!
//! say app is called `app`, you start app with `app.render_loop()`.
//! Check [Workflow](https://github.com/Respo/respo-rust-workflow/tree/c7cc0c0/src) for a working example.
mod app;
pub mod states_tree;

pub(crate) mod node;
pub mod ui;

pub use node::css::*;
pub use node::element::alias::*;
pub use node::*;

pub use app::renderer::*;

pub use app::{util, RespoApp, RespoStore};
4 changes: 2 additions & 2 deletions respo/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::states_tree::{DynEq, RespoStateBranch, RespoUpdateState};

use css::RespoStyle;

pub use dom_change::RespoCoord;
pub(crate) use dom_change::RespoCoord;
pub(crate) use dom_change::{ChildDomOp, DomChange};

pub use component::effect::{RespoEffect, RespoEffectType};
Expand Down Expand Up @@ -145,7 +145,7 @@ where

pub(crate) type StrDict = HashMap<Rc<str>, String>;

pub fn str_dict_to_cirrus_dict(dict: &StrDict) -> Cirru {
pub(crate) fn str_dict_to_cirrus_dict(dict: &StrDict) -> Cirru {
let mut xs = vec![];
for (k, v) in dict {
xs.push(vec![Cirru::from(k.as_ref()), Cirru::from(v)].into());
Expand Down
30 changes: 25 additions & 5 deletions respo/src/node/css.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
//! Define CSS styles in Rust and generate CSS class-name.
//!
//! ```rust
//! use respo::css::*;
//! respo::static_styles!(
//! style_done_button,
//! (
//! "&",
//! RespoStyle::default()
//! .width(CssSize::Px(24.0))
//! .height(CssSize::Px(24.0))
//! .margin(4.)
//! .cursor("pointer".to_owned())
//! .background_color(CssColor::Hsl(20, 90, 70)),
//! )
//! );
//! ```
//!
//! then `style_done_button()` returns the class name, while CSS is generated and injected into the `<style/>`.
use std::{
collections::HashSet,
fmt::{self, Display, Formatter, Write},
Expand Down Expand Up @@ -725,7 +745,7 @@ where
}
}

/// turns `src/a/b.rs` into `a_b`, used inside macro
/// turns `src/a/b.rs` into `a_b`, (used inside macro)
pub fn css_name_from_path(p: &str) -> String {
let mut s = p.to_owned();
if let Some(x) = s.strip_prefix("src/") {
Expand All @@ -741,7 +761,7 @@ pub fn css_name_from_path(p: &str) -> String {
/// ```rust
/// respo::static_style_seq!(the_name,
/// &[
/// ("&", respo::RespoStyle::default())
/// ("&", respo::css::RespoStyle::default())
/// ]
/// );
/// ```
Expand All @@ -754,16 +774,16 @@ macro_rules! static_style_seq {
($a:ident, $b:expr) => {
pub fn $a() -> String {
// let name = $crate::css_name_from_path(std::file!());
let name = $crate::css_name_from_path(std::module_path!());
$crate::declare_static_style(format!("{}__{}", name, stringify!($a)), $b)
let name = $crate::css::css_name_from_path(std::module_path!());
$crate::css::declare_static_style(format!("{}__{}", name, stringify!($a)), $b)
}
};
}

/// macro to create a public function of CSS rules(up to 5 tuples) at current file scope,
/// ```rust
/// respo::static_styles!(the_name,
/// ("&", respo::RespoStyle::default())
/// ("&", respo::css::RespoStyle::default())
/// );
/// ```
/// gets a function like:
Expand Down
2 changes: 1 addition & 1 deletion respo/src/node/dom_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ where

/// coordinate system on RespoNode, to lookup among elements and components
#[derive(Debug, Clone)]
pub enum RespoCoord {
pub(crate) enum RespoCoord {
Key(RespoIndexKey),
/// for indexing by component name, even though there's only one of that
Comp(Rc<str>),
Expand Down
2 changes: 1 addition & 1 deletion respo/src/node/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
rc::Rc,
};

use crate::{DispatchFn, RespoEvent, RespoIndexKey, RespoListenerFn, RespoNode, RespoStyle};
use crate::{css::RespoStyle, DispatchFn, RespoEvent, RespoIndexKey, RespoListenerFn, RespoNode};

/// internal abstraction for an element
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
3 changes: 2 additions & 1 deletion respo/src/node/element/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::css::{CssSize, RespoStyle};

/// a macro for creating a function with a named node
/// ```ignore
/// declare_tag!(div);
/// declare_tag!(div, "about `<div/>`");
/// ```
#[macro_export]
macro_rules! declare_tag {
Expand Down Expand Up @@ -46,6 +46,7 @@ declare_tag!(blockquote, "`<blockquote/>`");
declare_tag!(ul, "`<ul/>`");
declare_tag!(li, "`<li/>`");
declare_tag!(label, "`<label/>`");
declare_tag!(canvas, "`<canvas/>`");

/// special function to return `<div/>` with width/height that can be used as a space
pub fn space<T>(w: Option<i32>, h: Option<i32>) -> RespoElement<T>
Expand Down
6 changes: 5 additions & 1 deletion respo/src/states_tree.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! Respo does not provide local states in components, but a global states tree.
//! `RespoStatesTree` tree has concept of "cursor", which is a path to the current state in the tree.
//! use `branch.pick(name)` to get a child branch, and `branch.set_in_mut(change)` to update the tree.
mod dyn_eq;
mod state;

Expand Down Expand Up @@ -109,7 +113,7 @@ impl RespoStatesTree {
}

/// in-place mutation of state tree
pub fn set_in_mut(&mut self, change: RespoUpdateState) {
pub(crate) fn set_in_mut(&mut self, change: RespoUpdateState) {
if change.cursor.is_empty() {
change.data.clone_into(&mut self.data);
change.backup.clone_into(&mut self.backup);
Expand Down

0 comments on commit f6b883f

Please sign in to comment.