Skip to content

Commit

Permalink
Publish v0.17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Jan 20, 2024
1 parent ea35f56 commit b056deb
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 14 deletions.
6 changes: 3 additions & 3 deletions crates/concoct-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ struct WebContext {
parent: Node,
}

pub struct WebRoot<B> {
pub body: Rc<B>,
struct WebRoot<VB> {
pub body: Rc<VB>,
}

impl<B: ViewBuilder> ViewBuilder for WebRoot<B> {
impl<VB: ViewBuilder> ViewBuilder for WebRoot<VB> {
fn build(&self) -> impl View {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion crates/concoct/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "concoct"
version = "0.17.0-alpha.3"
version = "0.17.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Cross-platform UI framework"
Expand Down
2 changes: 2 additions & 0 deletions crates/concoct/src/hook/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Hooks to access render context.

mod use_context;
pub use use_context::{use_context, use_provider};

Expand Down
36 changes: 35 additions & 1 deletion crates/concoct/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
use std::borrow::Cow;
//! A UI framework for writing declarative apps on multiple platforms.
//!
//! Concoct uses static typing to describe your UI at compile-time
//! to create an efficient tree of components. Updates to state re-render
//! your application top-down, starting at the state's parent component.
//!
//! ```no_run
//! use concoct::{View, ViewBuilder};
//! use concoct::hook::use_state;
//! use concoct_web::html;
//!
//! struct App;
//!
//! impl ViewBuilder for App {
//! fn build(&self) -> impl View {
//! let (count, set_high) = use_state(|| 0);
//! let set_low = set_high.clone();
//!
//! (
//! format!("High five count: {}", count),
//! html::button("Up high!").on_click(move |_| set_high(count + 1)),
//! html::button("Down low!").on_click(move |_| set_low(count - 1)),
//! )
//! }
//! }
//! ```
//!

use std::borrow::Cow;
use std::cell::RefCell;

pub mod hook;
Expand All @@ -20,6 +47,7 @@ pub use self::view_builder::ViewBuilder;
pub mod view;
pub use self::view::View;

/// Run a view in a new virtual dom.
pub async fn run(view: impl ViewBuilder) {
let mut vdom = virtual_dom(view);
vdom.build();
Expand All @@ -29,6 +57,12 @@ pub async fn run(view: impl ViewBuilder) {
}
}

/// Provider for a platform-specific text view.
///
/// If you're writing a custom backend, you can use this to override
/// the default implementation of `View` for string types (like `&str` and `String`).
///
/// To expose it to child views, use [`use_provider`](`crate::hook::use_provider`).
pub struct TextViewContext {
view: RefCell<Box<dyn FnMut(Cow<'static, str>)>>,
}
Expand Down
3 changes: 3 additions & 0 deletions crates/concoct/src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use std::{any::Any, collections::HashSet, hash::Hash};
mod node;
pub use node::Node;

/// Statically-typed view tree.
///
/// This trait is unsafe and intended as the lower-level backend of [`View`](crate::View).
pub trait Tree: 'static {
unsafe fn build(&mut self);

Expand Down
4 changes: 4 additions & 0 deletions crates/concoct/src/vdom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ use std::{
time::{Duration, Instant},
};

/// Create a new virtual dom from a view.
pub fn virtual_dom(view: impl View) -> VirtualDom<impl Tree> {
VirtualDom {
cx: Runtime::default(),
tree: view.into_tree(),
}
}

/// A virtual dom that renders a view on any backend.
pub struct VirtualDom<T> {
cx: Runtime,
tree: T,
}

impl<T> VirtualDom<T> {
/// Build the initial virtual dom.
pub fn build(&mut self)
where
T: Tree,
Expand All @@ -26,6 +29,7 @@ impl<T> VirtualDom<T> {
unsafe { self.tree.build() }
}

/// Rebuild the virtual dom.
pub async fn rebuild(&mut self) {
futures::future::poll_fn(|cx| {
self.try_rebuild_with_limit_inner(None, Some(cx.waker().clone()));
Expand Down
14 changes: 10 additions & 4 deletions crates/concoct/src/view/child.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
use crate::{Tree, View};
use std::{cell::RefCell, rc::Rc};

pub struct Child<B> {
cell: Rc<RefCell<Option<B>>>,
/// Child view.
///
/// This type should be cloned and returned from a parent view to wrap its content.
///
/// ## Panics
/// This view can only be used once, then it will panic.
pub struct Child<V> {
cell: Rc<RefCell<Option<V>>>,
}

impl<B> Child<B> {
pub fn new(body: B) -> Self {
pub fn new(view: B) -> Self {
Self {
cell: Rc::new(RefCell::new(Some(body))),
cell: Rc::new(RefCell::new(Some(view))),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/concoct/src/view/empty.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{Tree, View};
use std::any::Any;

/// Empty view.
pub struct Empty;

impl View for Empty {
Expand Down
12 changes: 7 additions & 5 deletions crates/concoct/src/view/memo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ use std::{
hash::{Hash, Hasher},
};

pub fn memo<B>(input: impl Hash, body: B) -> Memo<B> {
/// Memoize a view, only rendering it when some input has changed.
pub fn memo<V: View>(input: impl Hash, view: V) -> Memo<V> {
let mut hasher = FxHasher::default();
input.hash(&mut hasher);
let hash = hasher.finish();

Memo { hash, body }
Memo { hash, body: view }
}

pub struct Memo<B> {
/// View for the [`memo`] function.
pub struct Memo<V> {
hash: u64,
body: B,
body: V,
}

impl<B: View> View for Memo<B> {
impl<V: View> View for Memo<V> {
fn into_tree(self) -> impl Tree {
Memo {
hash: self.hash,
Expand Down
9 changes: 9 additions & 0 deletions crates/concoct/src/view/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Viewable components of a user-interface.

use crate::{Node, Tree, ViewBuilder};
use std::hash::Hash;

Expand All @@ -13,6 +15,13 @@ pub use memo::{memo, Memo};
mod one_of;
pub use one_of::*;

/// Viewable component of a user-interface.
///
/// This trait creates a statically-typed tree of views
/// for efficient state updates.
///
/// Most implementations should come from [`ViewBuilder`], which this trait
/// is implemented for.
pub trait View: 'static {
fn into_tree(self) -> impl Tree;
}
Expand Down
1 change: 1 addition & 0 deletions crates/concoct/src/view/one_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::any::Any;

macro_rules! one_of {
($name:tt, $($t:tt),*) => {
/// Container view for children of multiple types.
pub enum $name<$($t),*> {
$($t($t)),*
}
Expand Down
3 changes: 3 additions & 0 deletions crates/concoct/src/view_builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{view::Empty, View};
use std::rc::Rc;

/// Builder for a [`View`].
///
/// [`View`] is implemented for anything that implements this trait.
pub trait ViewBuilder: 'static {
fn build(&self) -> impl View;
}
Expand Down

0 comments on commit b056deb

Please sign in to comment.