fork: rename and reversion

This commit is contained in:
Ashley Williams 2022-12-15 15:24:41 -06:00
parent e18d328951
commit ddc8bc12d0
56 changed files with 77 additions and 1704 deletions

View File

@ -2,9 +2,5 @@
members = [
"typed-html",
"macros",
"examples/stdweb",
"examples/iron",
"examples/dodrio/counter",
"examples/dodrio/todomvc",
"ui",
"tests",
]

View File

@ -1,13 +1,22 @@
[![Build Status](https://travis-ci.org/bodil/typed-html.svg?branch=master)](https://travis-ci.org/bodil/typed-html)
# typed-html
# axohtml
This crate provides the `html!` macro for building fully type checked HTML documents inside your
Rust code using roughly [JSX] compatible syntax.
This crate provides the `html!` macro for building fully type checked HTML
documents inside your Rust code using roughly [JSX] compatible syntax.
## Not Currently Maintained
This crate is a fork of the great [Bodil Stokke's] [typed-html] crate. Opted
for a fork instead of maintainership because not currently intending to use or
maintain the Wasm compatibility (for now).
This project is not currently being maintained. If you would like to take over maintainership, please [leave an issue](https://github.com/bodil/typed-html/issues/new).
[Bodil Stokke's]: https://github.com/bodil
typed-html: https://github.com/bodil/typed-html
## Company-focused Maintenance
This project is currently being maintained soley for use in [@axodotdev's]
projects. Feel free to file issues or PRs but anticipate that they won't be
prioritized.
## Quick Preview
@ -15,21 +24,21 @@ This project is not currently being maintained. If you would like to take over m
let mut doc: DOMTree<String> = html!(
<html>
<head>
<title>"Hello Kitty"</title>
<meta name=Metadata::Author content="Not Sanrio Co., Ltd"/>
<title>"Hello Axo"</title>
<meta name=Metadata::Author content="Axo Developer Co."/>
</head>
<body>
<h1>"Hello Kitty"</h1>
<h1>">o_o<"</h1>
<p class="official">
"She is not a cat. She is a human girl."
"The tool company for tool companies"
</p>
{ (0..3).map(|_| html!(
<p class="emphasis">
"Her name is Kitty White."
">o_o<"
</p>
)) }
<p class="citation-needed">
"We still don't know how she eats."
"Every company should be a developer experience company"
</p>
</body>
</html>
@ -137,10 +146,10 @@ ensure you're not using any event handlers that can't be printed.
```rust
let doc: DOMTree<String> = html!(
<p>"Hello Kitty"</p>
<p>"Hello Axo"</p>
);
let doc_str = doc.to_string();
assert_eq!("<p>Hello Kitty</p>", doc_str);
assert_eq!("<p>Hello Axo</p>", doc_str);
```
### Render to a virtual DOM
@ -151,20 +160,15 @@ with every attribute value rendered into `String`s. You can walk this virtual
DOM tree and use it to build an actual DOM tree with `stdweb` or pass it on to
your favourite virtual DOM system.
## Licence
## License
Copyright 2018 Bodil Stokke
This software is subject to the terms of the Mozilla Public License, v. 2.0. If
a copy of the MPL was not distributed with this file, You can obtain one at
<http://mozilla.org/MPL/2.0/>.
## Code of Conduct
Copyright 2018 Bodil Stokke, 2022 Axo Developer Co.
Please note that this project is released with a [Contributor Code of
Conduct][coc]. By participating in this project you agree to abide by its terms.
[coc]: https://www.contributor-covenant.org/version/1/4/code-of-conduct
[JSX]: https://reactjs.org/docs/introducing-jsx.html
[Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
[String]: https://doc.rust-lang.org/std/string/struct.String.html

View File

@ -1,34 +0,0 @@
[package]
name = "dodrio-counter"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[features]
[dependencies]
console_error_panic_hook = "0.1.6"
console_log = "0.2.0"
dodrio = "0.2.0"
log = "0.4.6"
wasm-bindgen = "0.2"
typed-html = { path = "../../../typed-html", features = ["dodrio_macro"] }
[dependencies.web-sys]
version = "0.3.15"
features = [
"console",
"Document",
"Event",
"EventTarget",
"HtmlElement",
"MouseEvent",
"Node",
"Window",
]
[dev-dependencies]
wasm-bindgen-test = "0.3"

View File

@ -1,21 +0,0 @@
# Counter
A counter that can be incremented and decremented.
## Source
See `src/lib.rs`.
## Build
```
wasm-pack build --target no-modules
```
## Serve
Use any HTTP server, for example:
```
python -m SimpleHTTPServer
```

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>Counter</title>
</head>
<body>
<script src="pkg/dodrio_counter.js"></script>
<script>
wasm_bindgen("pkg/dodrio_counter_bg.wasm");
</script>
</body>
</html>

View File

@ -1,84 +0,0 @@
#![recursion_limit = "128"]
use dodrio::builder::text;
use dodrio::bumpalo::{self};
use dodrio::Render;
use log::*;
use typed_html::dodrio;
use wasm_bindgen::prelude::*;
/// A counter that can be incremented and decrmented!
struct Counter {
count: isize,
}
impl Counter {
/// Construct a new, zeroed counter.
fn new() -> Counter {
Counter { count: 0 }
}
/// Increment this counter's count.
fn increment(&mut self) {
self.count += 1;
}
/// Decrement this counter's count.
fn decrement(&mut self) {
self.count -= 1;
}
}
// The `Render` implementation for `Counter`s displays the current count and has
// buttons to increment and decrement the count.
impl<'a> Render<'a> for Counter {
fn render(&self, cx: &mut dodrio::RenderContext<'a>) -> dodrio::Node<'a> {
// Stringify the count as a bump-allocated string.
let count = bumpalo::format!(in cx.bump, "{}", self.count);
let bump = cx.bump;
dodrio!(bump,
<div id="counter">
<button onclick={|root, vdom, _event| {
// Cast the root render component to a `Counter`, since
// we know that's what it is.
let counter = root.unwrap_mut::<Counter>();
// Increment the counter.
counter.increment();
// Since the count has updated, we should re-render the
// counter on the next animation frame.
vdom.schedule_render();
}}>"+"</button>
{ vec![text(count.into_bump_str())] }
<button onclick={|root, vdom, _event| {
// Same as above, but decrementing instead of incrementing.
root.unwrap_mut::<Counter>().decrement();
vdom.schedule_render();
}}>"-"</button>
</div>
)
}
}
#[wasm_bindgen(start)]
pub fn run() {
// Initialize debug logging for if/when things go wrong.
console_error_panic_hook::set_once();
console_log::init_with_level(Level::Trace).expect("should initialize logging OK");
// Get the document's `<body>`.
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let body = document.body().unwrap();
// Construct a new counter component.
let counter = Counter::new();
// Mount our counter component to the `<body>`.
let vdom = dodrio::Vdom::new(&body, counter);
// Run the virtual DOM and its listeners forever.
vdom.forget();
}

View File

@ -1,49 +0,0 @@
[package]
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
edition = "2018"
name = "dodrio-todomvc"
version = "0.1.0"
[dependencies]
cfg-if = "1"
dodrio = "0.2.0"
futures = "0.3"
js-sys = "0.3.15"
serde = { features = ["derive"], version = "1.0.89" }
serde_json = "1.0.39"
wasm-bindgen = "0.2.38"
wasm-bindgen-futures = "0.4"
typed-html = { path = "../../../typed-html", features = ["dodrio_macro"] }
# Optional dependencies for logging.
console_error_panic_hook = { optional = true, version = "0.1.6" }
console_log = { optional = true, version = "0.2.0" }
log = { optional = true, version = "0.4.6" }
[dependencies.web-sys]
version = "0.3.15"
features = [
"Document",
"Event",
"EventTarget",
"HtmlElement",
"HtmlInputElement",
"KeyboardEvent",
"Location",
"Storage",
"Node",
"Window",
]
[dev-dependencies]
wasm-bindgen-test = "0.3"
[features]
logging = [
"console_log",
"console_error_panic_hook",
"log",
]
[lib]
crate-type = ["cdylib"]

View File

@ -1,33 +0,0 @@
# TodoMVC
`dodrio` implementation of the popular [TodoMVC](http://todomvc.com/) app. It
correctly and completely fulfills [the
specification](https://github.com/tastejs/todomvc/blob/master/app-spec.md) to
the best of my knowledge.
## Source
There are a number of modules in this `dodrio` implementation of TodoMVC. The
most important are:
* `src/lib.rs`: The entry point to the application.
* `src/todos.rs`: Definition of `Todos` model and its rendering.
* `src/todo.rs`: Definition of `Todo` model and its rendering.
* `src/controller.rs`: The controller handles UI interactions and translates
them into updates on the model. Finally, it triggers re-rendering after those
updates.
* `src/router.rs`: A simple URL hash-based router.
## Build
```
wasm-pack build --target no-modules
```
## Serve
Use any HTTP server, for example:
```
python -m SimpleHTTPServer
```

View File

@ -1,21 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>dodrio • TodoMVC</title>
<link rel="stylesheet" href="https://unpkg.com/todomvc-common@^1.0.0/base.css">
<link rel="stylesheet" href="https://unpkg.com/todomvc-app-css@^2.0.0/index.css">
</head>
<body>
<section class="todoapp">
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
</footer>
<script src="pkg/dodrio_todomvc.js"></script>
<script>
wasm_bindgen("pkg/dodrio_todomvc_bg.wasm");
</script>
</body>
</html>

View File

@ -1,140 +0,0 @@
//! The controller handles UI events, translates them into updates on the model,
//! and schedules re-renders.
use crate::todo::{Todo, TodoActions};
use crate::todos::{Todos, TodosActions};
use crate::visibility::Visibility;
use dodrio::{RootRender, VdomWeak};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
/// The controller for the TodoMVC app.
///
/// This `Controller` struct is never actually instantiated. It is only used for
/// its `*Actions` trait implementations, none of which take a `self` parameter.
///
/// One could imagine alternative controller implementations with `*Actions`
/// trait implementations for (e.g.) testing that will assert various expected
/// action methods are called after rendering todo items and sending DOM events.
#[derive(Default, Deserialize, Serialize)]
pub struct Controller;
impl TodosActions for Controller {
fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak) {
let mut todos = AutoCommitTodos::new(root, vdom);
let all_complete = todos.todos().iter().all(|t| t.is_complete());
for t in todos.todos_mut() {
t.set_complete(!all_complete);
}
}
fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String) {
let mut todos = AutoCommitTodos::new(root, vdom);
todos.set_draft(draft);
}
fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak) {
let mut todos = AutoCommitTodos::new(root, vdom);
let title = todos.take_draft();
let title = title.trim();
if !title.is_empty() {
let id = todos.todos().len();
let new = Todo::new(id, title);
todos.add_todo(new);
}
}
fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility) {
let mut todos = AutoCommitTodos::new(root, vdom);
todos.set_visibility(vis);
}
fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak) {
let mut todos = AutoCommitTodos::new(root, vdom);
todos.delete_completed();
}
}
impl TodoActions for Controller {
fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
let mut todos = AutoCommitTodos::new(root, vdom);
let t = &mut todos.todos_mut()[id];
let completed = t.is_complete();
t.set_complete(!completed);
}
fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
let mut todos = AutoCommitTodos::new(root, vdom);
todos.delete_todo(id);
}
fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
let mut todos = AutoCommitTodos::new(root, vdom);
let t = &mut todos.todos_mut()[id];
let desc = t.title().to_string();
t.set_edits(Some(desc));
}
fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String) {
let mut todos = AutoCommitTodos::new(root, vdom);
let t = &mut todos.todos_mut()[id];
t.set_edits(Some(edits));
}
fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
let mut todos = AutoCommitTodos::new(root, vdom);
let t = &mut todos.todos_mut()[id];
if let Some(edits) = t.take_edits() {
let edits = edits.trim();
if edits.is_empty() {
todos.delete_todo(id);
} else {
t.set_title(edits);
}
}
}
fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize) {
let mut todos = AutoCommitTodos::new(root, vdom);
let t = &mut todos.todos_mut()[id];
let _ = t.take_edits();
}
}
/// An RAII type that dereferences to a `Todos` and once it is dropped, saves
/// the (presumably just modified) todos to local storage, and schedules a new
/// `dodrio` render.
pub struct AutoCommitTodos<'a> {
todos: &'a mut Todos,
vdom: VdomWeak,
}
impl AutoCommitTodos<'_> {
/// Construct a new `AutoCommitTodos` from the root rendering component and
/// `vdom` handle.
pub fn new(root: &mut dyn RootRender, vdom: VdomWeak) -> AutoCommitTodos {
let todos = root.unwrap_mut::<Todos>();
AutoCommitTodos { todos, vdom }
}
}
impl Drop for AutoCommitTodos<'_> {
fn drop(&mut self) {
self.todos.save_to_local_storage();
self.vdom.schedule_render();
}
}
impl Deref for AutoCommitTodos<'_> {
type Target = Todos;
fn deref(&self) -> &Todos {
self.todos
}
}
impl DerefMut for AutoCommitTodos<'_> {
fn deref_mut(&mut self) -> &mut Todos {
self.todos
}
}

View File

@ -1,7 +0,0 @@
//! Constants for `KeyboardEvent::key_code`.`
/// The key code for the enter key.
pub const ENTER: u32 = 13;
/// The key code for the escape key.
pub const ESCAPE: u32 = 27;

View File

@ -1,62 +0,0 @@
//! TodoMVC implemented in `dodrio`!
#![recursion_limit = "1024"]
#![deny(missing_docs)]
#![allow(unused_braces)]
pub mod controller;
pub mod keys;
pub mod router;
pub mod todo;
pub mod todos;
pub mod utils;
pub mod visibility;
use crate::controller::Controller;
use crate::todos::Todos;
use dodrio::Vdom;
use wasm_bindgen::prelude::*;
/// Run the TodoMVC app!
///
/// Since this is marked `#[wasm_bindgen(start)]` it is automatically invoked
/// once the wasm module instantiated on the Web page.
#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
// Set up the logging for debugging if/when things go wrong.
init_logging();
// Grab the todo app container.
let document = utils::document();
let container = document
.query_selector(".todoapp")?
.ok_or_else(|| js_sys::Error::new("could not find `.todoapp` container"))?;
// Create a new `Todos` render component.
let todos = Todos::<Controller>::new();
// Create a virtual DOM and mount it and the `Todos` render component.
let vdom = Vdom::new(&container, todos);
// Start the URL router.
router::start(vdom.weak());
// Run the virtual DOM forever and don't unmount it.
vdom.forget();
Ok(())
}
cfg_if::cfg_if! {
if #[cfg(feature = "logging")] {
fn init_logging() {
console_error_panic_hook::set_once();
console_log::init_with_level(log::Level::Trace)
.expect_throw("should initialize logging OK");
}
} else {
fn init_logging() {
// Do nothing.
}
}
}

View File

@ -1,67 +0,0 @@
//! A simple `#`-fragment router.
use crate::todos::Todos;
use crate::utils;
use crate::visibility::Visibility;
use dodrio::VdomWeak;
use std::str::FromStr;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
/// Start the router.
pub fn start(vdom: VdomWeak) {
// Callback fired whenever the URL's hash fragment changes. Keeps the root
// todos collection's visibility in sync with the `#` fragment.
let on_hash_change = move || {
let new_vis = utils::hash()
.and_then(|hash| {
if hash.starts_with("#/") {
Visibility::from_str(&hash[2..]).ok()
} else {
None
}
})
.unwrap_or_else(|| {
// If we couldn't parse a visibility, make sure we canonicalize
// it back to our default hash.
let v = Visibility::default();
utils::set_hash(&format!("#/{}", v));
v
});
let vdom = vdom.clone();
wasm_bindgen_futures::spawn_local(async move {
vdom.with_component({
let vdom = vdom.clone();
move |root| {
let todos = root.unwrap_mut::<Todos>();
// If the todos' visibility already matches the event's
// visibility, then there is nothing to do (ha). If they
// don't match, then we need to update the todos' visibility
// and re-render.
if todos.visibility() != new_vis {
todos.set_visibility(new_vis);
vdom.schedule_render();
}
}
})
.await
.ok();
});
};
// Call it once to handle the initial `#` fragment.
on_hash_change();
// Now listen for hash changes forever.
//
// Note that if we ever intended to unmount our todos app, we would want to
// provide a method for removing this router's event listener and cleaning
// up after ourselves.
let on_hash_change = Closure::wrap(Box::new(on_hash_change) as Box<dyn FnMut()>);
let window = utils::window();
window
.add_event_listener_with_callback("hashchange", on_hash_change.as_ref().unchecked_ref())
.unwrap_throw();
on_hash_change.forget();
}

View File

@ -1,153 +0,0 @@
//! Type definition and `dodrio::Render` implementation for a single todo item.
use crate::keys;
use dodrio::{Node, Render, RenderContext, RootRender, VdomWeak};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use typed_html::dodrio;
use wasm_bindgen::{prelude::*, JsCast};
/// A single todo item.
#[derive(Serialize, Deserialize)]
pub struct Todo<C> {
id: usize,
title: String,
completed: bool,
#[serde(skip)]
edits: Option<String>,
#[serde(skip)]
_controller: PhantomData<C>,
}
/// Actions on a single todo item that can be triggered from the UI.
pub trait TodoActions {
/// Toggle the completion state of the todo item with the given id.
fn toggle_completed(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
/// Delete the todo item with the given id.
fn delete(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
/// Begin editing the todo item with the given id.
fn begin_editing(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
/// Update the edits for the todo with the given id.
fn update_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize, edits: String);
/// Finish editing the todo with the given id.
fn finish_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
/// Cancel editing the todo with the given id.
fn cancel_edits(root: &mut dyn RootRender, vdom: VdomWeak, id: usize);
}
impl<C> Todo<C> {
/// Construct a new `Todo` with the given identifier and title.
pub fn new<S: Into<String>>(id: usize, title: S) -> Self {
let title = title.into();
let completed = false;
let edits = None;
Todo {
id,
title,
completed,
edits,
_controller: PhantomData,
}
}
/// Set this todo item's id.
pub fn set_id(&mut self, id: usize) {
self.id = id;
}
/// Is this `Todo` complete?
pub fn is_complete(&self) -> bool {
self.completed
}
/// Mark the `Todo` as complete or not.
pub fn set_complete(&mut self, to: bool) {
self.completed = to;
}
/// Get this todo's title.
pub fn title(&self) -> &str {
&self.title
}
/// Set this todo item's title.
pub fn set_title<S: Into<String>>(&mut self, title: S) {
self.title = title.into();
}
/// Set the edits for this todo.
pub fn set_edits<S: Into<String>>(&mut self, edits: Option<S>) {
self.edits = edits.map(Into::into);
}
/// Take this todo's edits, leaving `None` in their place.
pub fn take_edits(&mut self) -> Option<String> {
self.edits.take()
}
}
impl<'a, C: TodoActions> Render<'a> for Todo<C> {
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
use dodrio::{builder::text, bumpalo};
use typed_html::types::ClassList;
let id = self.id;
let title = self.edits.as_ref().unwrap_or(&self.title);
let title = bumpalo::collections::String::from_str_in(title, cx.bump).into_bump_str();
let bump = cx.bump;
dodrio!(bump,
<li class={
let mut class = ClassList::new();
if self.completed {
class.add("completed");
}
if self.edits.is_some() {
class.add("editing");
}
class
}>
<div class="view">
<input class="toggle" type="checkbox" checked={self.completed}
onclick={move |root, vdom, _event| {
C::toggle_completed(root, vdom, id)
}}
/>
<label ondblclick={move |root, vdom, _event| {
C::begin_editing(root, vdom, id)
}}>
{ bumpalo::vec![in &bump; text(title)] }
</label>
<button class="destroy" onclick={move |root, vdom, _event| {
C::delete(root, vdom, id)
}}/>
</div>
<input class="edit" value={title} name="title" id={
bumpalo::format!(in bump, "todo-{}", id).into_bump_str()
} oninput={move |root, vdom, event| {
let input = event
.target()
.unwrap_throw()
.unchecked_into::<web_sys::HtmlInputElement>();
C::update_edits(root, vdom, id, input.value());
}} onblur={move |root, vdom, _event| {
C::finish_edits(root, vdom, id)
}} onkeydown={move |root, vdom, event| {
let event = event.unchecked_into::<web_sys::KeyboardEvent>();
match event.key_code() {
keys::ENTER => C::finish_edits(root, vdom, id),
keys::ESCAPE => C::cancel_edits(root, vdom, id),
_ => {}
}
}}/>
</li>
)
}
}

View File

@ -1,271 +0,0 @@
//! Type definitions and `dodrio::Render` implementation for a collection of
//! todo items.
use crate::controller::Controller;
use crate::todo::{Todo, TodoActions};
use crate::visibility::Visibility;
use crate::{keys, utils};
use dodrio::RenderContext;
use dodrio::{
builder::text,
bumpalo::{self},
Node, Render, RootRender, VdomWeak,
};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use typed_html::dodrio;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
/// A collection of todos.
#[derive(Default, Serialize, Deserialize)]
#[serde(rename = "todos-dodrio", bound = "")]
pub struct Todos<C = Controller> {
todos: Vec<Todo<C>>,
#[serde(skip)]
draft: String,
#[serde(skip)]
visibility: Visibility,
#[serde(skip)]
_controller: PhantomData<C>,
}
/// Actions for `Todos` that can be triggered by UI interactions.
pub trait TodosActions: TodoActions {
/// Toggle the completion state of all todo items.
fn toggle_all(root: &mut dyn RootRender, vdom: VdomWeak);
/// Update the draft todo item's text.
fn update_draft(root: &mut dyn RootRender, vdom: VdomWeak, draft: String);
/// Finish the current draft todo item and add it to the collection of
/// todos.
fn finish_draft(root: &mut dyn RootRender, vdom: VdomWeak);
/// Change the todo item visibility filtering to the given `Visibility`.
fn change_visibility(root: &mut dyn RootRender, vdom: VdomWeak, vis: Visibility);
/// Delete all completed todo items.
fn delete_completed(root: &mut dyn RootRender, vdom: VdomWeak);
}
impl<C> Todos<C> {
/// Construct a new todos set.
///
/// If an existing set is available in local storage, then us that,
/// otherwise create a new set.
pub fn new() -> Self
where
C: Default,
{
Self::from_local_storage().unwrap_or_default()
}
/// Deserialize a set of todos from local storage.
pub fn from_local_storage() -> Option<Self> {
utils::local_storage()
.get("todomvc-dodrio")
.ok()
.and_then(|opt| opt)
.and_then(|json| serde_json::from_str(&json).ok())
}
/// Serialize this set of todos to local storage.
pub fn save_to_local_storage(&self) {
let serialized = serde_json::to_string(self).unwrap_throw();
utils::local_storage()
.set("todomvc-dodrio", &serialized)
.unwrap_throw();
}
/// Add a new todo item to this collection.
pub fn add_todo(&mut self, todo: Todo<C>) {
self.todos.push(todo);
}
/// Delete the todo with the given id.
pub fn delete_todo(&mut self, id: usize) {
self.todos.remove(id);
self.fix_ids();
}
/// Delete all completed todo items.
pub fn delete_completed(&mut self) {
self.todos.retain(|t| !t.is_complete());
self.fix_ids();
}
// Fix all todo identifiers so that they match their index once again.
fn fix_ids(&mut self) {
for (id, todo) in self.todos.iter_mut().enumerate() {
todo.set_id(id);
}
}
/// Get a shared slice of the underlying set of todo items.
pub fn todos(&self) -> &[Todo<C>] {
&self.todos
}
/// Get an exclusive slice of the underlying set of todo items.
pub fn todos_mut(&mut self) -> &mut [Todo<C>] {
&mut self.todos
}
/// Set the draft todo item text.
pub fn set_draft<S: Into<String>>(&mut self, draft: S) {
self.draft = draft.into();
}
/// Take the current draft text and replace it with an empty string.
pub fn take_draft(&mut self) -> String {
std::mem::take(&mut self.draft)
}
/// Get the current visibility for these todos.
pub fn visibility(&self) -> Visibility {
self.visibility
}
/// Set the visibility for these todoS.
pub fn set_visibility(&mut self, vis: Visibility) {
self.visibility = vis;
}
}
/// Rendering helpers.
impl<C: TodosActions> Todos<C> {
fn header<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
let bump = cx.bump;
dodrio!(bump,
<header class="header">
<h1>"todos"</h1>
<input oninput={|root, vdom, event| {
let input = event
.target()
.unwrap_throw()
.unchecked_into::<web_sys::HtmlInputElement>();
C::update_draft(root, vdom, input.value());
}} onkeydown={|root, vdom, event| {
let event = event.unchecked_into::<web_sys::KeyboardEvent>();
if event.key_code() == keys::ENTER {
C::finish_draft(root, vdom);
}
}} class="new-todo" placeholder="What needs to be done?" autofocus=true value={self.draft.as_str()}/>
</header>
)
}
fn todos_list<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
use dodrio::bumpalo::collections::Vec;
let mut todos = Vec::with_capacity_in(self.todos.len(), cx.bump);
todos.extend(
self.todos
.iter()
.filter(|t| match self.visibility {
Visibility::All => true,
Visibility::Active => !t.is_complete(),
Visibility::Completed => t.is_complete(),
})
.map(|t| t.render(cx)),
);
let bump = cx.bump;
dodrio!(bump,
<section class="main" style={
if self.todos.is_empty() {
"visibility: hidden"
} else {
"visibility: visible"
}
}>
<input
class="toggle-all" id="toggle-all" type="checkbox" name="toggle"
checked={self.todos.iter().all(|t| t.is_complete())}
onclick={|root, vdom, _event| C::toggle_all(root, vdom)}
/>
<label for="toggle-all">"Mark as complete"</label>
<ul class="todo-list">
{ todos }
</ul>
</section>
)
}
fn footer<'a>(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
let incomplete_count = self.todos.len() - completed_count;
let items_left = if incomplete_count == 1 {
" item left"
} else {
" items left"
};
let incomplete_count = bumpalo::format!(in cx.bump, "{}", incomplete_count);
let clear_completed_text = bumpalo::format!(
in cx.bump,
"Clear completed ({})",
self.todos.iter().filter(|t| t.is_complete()).count()
);
let bump = cx.bump;
dodrio!(bump,
<footer class="footer" hidden={self.todos.is_empty()}>
<span class="todo-count">
<strong>{
bumpalo::vec![in &bump; text(incomplete_count.into_bump_str())]
}</strong>
{ bumpalo::vec![in &bump; text(items_left)] }
</span>
<ul class="filters">
{ bumpalo::vec![in &bump;
self.visibility_swap(cx, "#/", Visibility::All),
self.visibility_swap(cx, "#/active", Visibility::Active),
self.visibility_swap(cx, "#/completed", Visibility::Completed)
] }
</ul>
<button class="clear-completed" hidden={completed_count == 0} onclick={|root, vdom, _event| {
C::delete_completed(root, vdom);
}}>{ bumpalo::vec![in &bump; text(clear_completed_text.into_bump_str())] }</button>
</footer>
)
}
fn visibility_swap<'a>(
&self,
cx: &mut RenderContext<'a>,
url: &'static str,
target_vis: Visibility,
) -> Node<'a> {
let bump = cx.bump;
dodrio!(bump,
<li onclick={move |root, vdom, _event| {
C::change_visibility(root, vdom, target_vis);
}}>
<a href={url} class={
if self.visibility == target_vis {
"selected"
} else {
""
}
}>{ bumpalo::vec![in &bump; text(target_vis.label())] }</a>
</li>
)
}
}
impl<'a, C: TodosActions> Render<'a> for Todos<C> {
fn render(&self, cx: &mut RenderContext<'a>) -> Node<'a> {
let bump = cx.bump;
dodrio!(bump,
<div>{ bumpalo::vec![in &bump;
self.header(cx), self.todos_list(cx), self.footer(cx)
] }</div>
)
}
}

View File

@ -1,32 +0,0 @@
//! Small utility functions.
use wasm_bindgen::UnwrapThrowExt;
/// Get the top-level window.
pub fn window() -> web_sys::Window {
web_sys::window().unwrap_throw()
}
/// Get the current location hash, if any.
pub fn hash() -> Option<String> {
window()
.location()
.hash()
.ok()
.and_then(|h| if h.is_empty() { None } else { Some(h) })
}
/// Set the current location hash.
pub fn set_hash(hash: &str) {
window().location().set_hash(hash).unwrap_throw();
}
/// Get the top-level document.
pub fn document() -> web_sys::Document {
window().document().unwrap_throw()
}
/// Get the top-level window's local storage.
pub fn local_storage() -> web_sys::Storage {
window().local_storage().unwrap_throw().unwrap_throw()
}

View File

@ -1,51 +0,0 @@
//! Visibility filtering.
use std::fmt;
use std::str::FromStr;
/// The visibility filtering for todo items.
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Visibility {
/// Show all todos.
All,
/// Show only active, incomplete todos.
Active,
/// Show only inactive, completed todos.
Completed,
}
impl Default for Visibility {
fn default() -> Visibility {
Visibility::All
}
}
impl FromStr for Visibility {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"all" => Ok(Visibility::All),
"active" => Ok(Visibility::Active),
"completed" => Ok(Visibility::Completed),
_ => Err(()),
}
}
}
impl fmt::Display for Visibility {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.label().to_lowercase())
}
}
impl Visibility {
/// Get a string label for this visibility.
pub fn label(self) -> &'static str {
match self {
Visibility::All => "All",
Visibility::Active => "Active",
Visibility::Completed => "Completed",
}
}
}

View File

@ -1,10 +0,0 @@
[package]
name = "typed-html-iron-test"
version = "0.1.0"
edition = "2018"
authors = ["Bodil Stokke <bodil@bodil.org>"]
[dependencies]
typed-html-macros = { path = "../../macros" }
typed-html = { path = "../../typed-html" }
iron = "0.6.0"

View File

@ -1,65 +0,0 @@
#![recursion_limit = "256"]
use iron::headers::ContentType;
use iron::modifier::Modifier;
use iron::prelude::*;
use iron::status;
use typed_html::elements::FlowContent;
use typed_html::types::LinkType;
use typed_html::{dom::DOMTree, html, text, OutputType};
struct Html(DOMTree<String>);
impl Modifier<Response> for Html {
fn modify(self, res: &mut Response) {
res.body = Some(Box::new(self.0.to_string()));
res.headers.set(ContentType::html());
}
}
// Function that wraps a DOM node in an HTML document, to demonstrate how you'd
// do this sort of templating.
//
// It's a bit more complicated than you'd hope because you need to take an input
// argument of the type that the element that you're inserting it into expects,
// which in the case of `<body>` is `FlowContent`, not just `Node`, so you can't
// pass it a `DOMTree<T>` or you'll get a type error.
fn doc<T: OutputType + 'static + Send>(tree: Box<dyn FlowContent<T>>) -> DOMTree<T> {
html!(
<html>
<head>
<title>"Hello Kitty!"</title>
<link rel=LinkType::StyleSheet href="lol.css"/>
</head>
<body>
{ tree }
</body>
</html>
)
}
fn index() -> Html {
let a = true;
Html(doc(html!(
<div>
<h1 data-lol="omg">"Hello Kitty!"</h1>
<p class="official-position-of-sanrio-ltd emphasis">
"She is not a "<em><a href="https://en.wikipedia.org/wiki/Cat">"cat"</a></em>". She is a "<em>"human girl"</em>"."
</p>
<p class=["urgent", "question"]>"But how does she eat?"</p>
{
(1..4).map(|i| {
html!(<p>{ text!("{}. Ceci n'est pas une chatte.", i) }</p>)
})
}
<p>"<img src=\"javascript:alert('pwned lol')\">"</p>
<button disabled=a onclick="alert('She is not a cat.')">"Click me!"</button>
</div>
: String)))
}
fn main() {
Iron::new(|_: &mut Request| Ok(Response::with((status::Ok, index()))))
.http("localhost:1337")
.unwrap();
}

View File

@ -1,9 +0,0 @@
[package]
name = "typed-html-stdweb-test"
version = "0.1.0"
edition = "2018"
authors = ["Bodil Stokke <bodil@bodil.org>"]
[dependencies]
typed-html = { path = "../../typed-html", features = ["stdweb"] }
stdweb = "0.4.10"

View File

@ -1,27 +0,0 @@
# typed-html WASM example
Simple example of compiling app consuming typed-html to WebAssembly.
## Configure & Build
Make sure you have `cargo-web` installed: [Instructions](https://github.com/koute/cargo-web/#installation)
Build using `cargo web build`
_Note: There may be an issue that can be worked around as described [here](https://github.com/bodil/typed-html/issues/6), due to a bug in stdweb_
## Serve Through HTTP
We've also provided some simple scaffolding to easily serve the app through http:
```
$ cd www
$ yarn
$ node index.js
```
If the build is in a different directory, than the default, you can provide the directory as an argument:
```
$ node index.js /path/to/build
```

View File

@ -1 +0,0 @@
default-target = "wasm32-unknown-unknown"

View File

@ -1,31 +0,0 @@
#![recursion_limit = "256"]
extern crate stdweb;
extern crate typed_html;
use stdweb::web::{self, INode};
use typed_html::dom::Node;
use typed_html::html;
use typed_html::output::stdweb::Stdweb;
fn main() {
let mut doc = html!(
<div role="main">
<h1>"Hello Kitty"</h1>
<p>
"She is not a "<em><a href="https://en.wikipedia.org/wiki/Cat">"cat"</a></em>
". She is a "<em>"human girl"</em>"."
</p>
<p>
<button onclick={ |_event| web::alert("Hello Joe!") }>
"Call Joe"
</button>
</p>
</div>
: Stdweb);
let vdom = doc.vnode();
let document = web::document();
let body = document.body().expect("no body element in doc");
let tree = Stdweb::build(&document, vdom).unwrap();
body.append_child(&tree);
}

View File

@ -1,76 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# FuseBox cache
.fusebox/

View File

@ -1,12 +0,0 @@
<!doctype <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>typed-html WASM example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./typed-html-wasm-test.js"></script>
</head>
<body>
</body>
</html>

View File

@ -1,10 +0,0 @@
var express = require('express');
var path = require('path');
var pathToBuild = process.argv[2] || '../../../target/wasm32-unknown-unknown/release';
var app = express();
app.use(express.static(path.resolve(pathToBuild), { index: false }));
app.use(express.static(path.resolve('./', __dirname)));
app.listen(8080);

View File

@ -1,9 +0,0 @@
{
"name": "types-html-example",
"version": "0.1.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"express": "^4.16.4"
}
}

View File

@ -1,295 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@~1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
dependencies:
mime-types "~2.1.18"
negotiator "0.6.1"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
body-parser@1.18.3:
version "1.18.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
dependencies:
bytes "3.0.0"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "~1.6.3"
iconv-lite "0.4.23"
on-finished "~2.3.0"
qs "6.5.2"
raw-body "2.3.3"
type-is "~1.6.16"
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
ms "2.0.0"
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
express@^4.16.4:
version "4.16.4"
resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e"
dependencies:
accepts "~1.3.5"
array-flatten "1.1.1"
body-parser "1.18.3"
content-disposition "0.5.2"
content-type "~1.0.4"
cookie "0.3.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.1.1"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.2"
path-to-regexp "0.1.7"
proxy-addr "~2.0.4"
qs "6.5.2"
range-parser "~1.2.0"
safe-buffer "5.1.2"
send "0.16.2"
serve-static "1.13.2"
setprototypeof "1.1.0"
statuses "~1.4.0"
type-is "~1.6.16"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@1.1.1:
version "1.1.1"
resolved "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.2"
statuses "~1.4.0"
unpipe "~1.0.0"
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
version "1.6.3"
resolved "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
iconv-lite@0.4.23:
version "0.4.23"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
ipaddr.js@1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
media-typer@0.3.0:
version "0.3.0"
resolved "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
mime-db@~1.37.0:
version "1.37.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8"
mime-types@~2.1.18:
version "2.1.21"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96"
dependencies:
mime-db "~1.37.0"
mime@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
dependencies:
ee-first "1.1.1"
parseurl@~1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
proxy-addr@~2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
dependencies:
forwarded "~0.1.2"
ipaddr.js "1.8.0"
qs@6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
range-parser@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
raw-body@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3"
dependencies:
bytes "3.0.0"
http-errors "1.6.3"
iconv-lite "0.4.23"
unpipe "1.0.0"
safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
send@0.16.2:
version "0.16.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
dependencies:
debug "2.6.9"
depd "~1.1.2"
destroy "~1.0.4"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "~1.6.2"
mime "1.4.1"
ms "2.0.0"
on-finished "~2.3.0"
range-parser "~1.2.0"
statuses "~1.4.0"
serve-static@1.13.2:
version "1.13.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.2"
send "0.16.2"
setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
"statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
type-is@~1.6.16:
version "1.6.16"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
dependencies:
media-typer "0.3.0"
mime-types "~2.1.18"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"

View File

@ -1,6 +1,6 @@
[package]
name = "typed-html-macros"
version = "0.2.2"
name = "axohtml-macros"
version = "0.1.0"
edition = "2018"
authors = ["Bodil Stokke <bodil@bodil.org>"]
build = "build.rs"

View File

@ -39,20 +39,6 @@ pub fn global_attrs(span: Span) -> StringyMap<Ident, TokenStream> {
}
pub static SELF_CLOSING: &[&str] = &[
"area",
"base",
"br",
"col",
"command",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
"area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link",
"meta", "param", "source", "track", "wbr",
];

View File

@ -34,11 +34,7 @@ impl Declare {
}
fn attr_type_name(&self) -> TokenTree {
Ident::new(
&format!("Attrs_{}", self.name),
self.name.span(),
)
.into()
Ident::new(&format!("Attrs_{}", self.name), self.name.span()).into()
}
fn attrs(&self) -> impl Iterator<Item = (TokenTree, TokenStream, TokenTree)> + '_ {

View File

@ -23,7 +23,7 @@ impl Node {
Node::Element(el) => el.into_token_stream(ty),
Node::Text(text) => {
let text = TokenTree::Literal(text);
Ok(quote!(Box::new(typed_html::dom::TextNode::new(#text.to_string()))))
Ok(quote!(Box::new(axohtml::dom::TextNode::new(#text.to_string()))))
}
Node::Block(group) => {
let span = group.span();
@ -271,12 +271,12 @@ impl Element {
let mut type_annotation = TokenStream::new();
if let Some(ty) = ty {
let type_var = to_stream(ty.clone());
type_annotation.extend(quote!(: typed_html::elements::#typename<#type_var>));
type_annotation.extend(quote!(: axohtml::elements::#typename<#type_var>));
}
Ok(quote!(
{
let mut element #type_annotation = typed_html::elements::#typename::new(#args);
let mut element #type_annotation = axohtml::elements::#typename::new(#args);
#body
Box::new(element)
}
@ -494,8 +494,8 @@ impl Element {
Ok(quote!(
{
#make_req_children
let mut element: typed_html::elements::#typename<typed_html::output::dodrio::Dodrio> =
typed_html::elements::#typename::new(#args);
let mut element: axohtml::elements::#typename<axohtml::output::dodrio::Dodrio> =
axohtml::elements::#typename::new(#args);
#set_attrs
#builder
}

View File

@ -1,6 +1,3 @@
pub fn from_unstable(span: proc_macro::Span) -> proc_macro2::Span {
let ident = proc_macro::Ident::new("_", span);
let tt = proc_macro::TokenTree::Ident(ident);

View File

@ -1,5 +1,5 @@
[package]
name = "typed-html-tests"
name = "axohtml-tests"
version = "0.0.0"
edition = "2018"
authors = ["Bodil Stokke <bodil@bodil.org>"]
@ -11,6 +11,6 @@ path = "main.rs"
[dev-dependencies]
compiletest_rs = { version = "0.7", features = ["stable"] }
typed-html = { path = "../typed-html" }
typed-html-macros = { path = "../macros" }
axohtml = { path = "../typed-html" }
axohtml-macros = { path = "../macros" }
version_check = "0.9.1"

View File

@ -1,37 +1,21 @@
[package]
name = "typed-html"
version = "0.2.2"
name = "axohtml"
version = "0.1.0"
edition = "2018"
authors = ["Bodil Stokke <bodil@bodil.org>"]
authors = ["Axo Developer Co <ashley@axo.dev>", "Bodil Stokke <bodil@bodil.org>"]
license = "MPL-2.0+"
description = "Type checked JSX for Rust"
repository = "https://github.com/bodil/typed-html"
documentation = "http://docs.rs/typed-html/"
repository = "https://github.com/axodotdev/axohtml"
documentation = "http://docs.rs/axohtml/"
readme = "../README.md"
categories = ["template-engine", "wasm", "web-programming"]
keywords = ["jsx", "html", "wasm"]
[badges]
travis-ci = { repository = "bodil/typed-html" }
maintenance = { status = "looking-for-maintainer" }
[package.metadata.docs.rs]
all-features = true
categories = ["template-engine", "web-programming"]
keywords = ["jsx", "html"]
[dependencies]
typed-html-macros = { path = "../macros" }
axohtml-macros = { path = "../macros" }
strum = "0.23"
strum_macros = "0.23"
mime = "0.3.13"
language-tags = "0.3"
htmlescape = "0.3.1"
proc-macro-nested = "0.1.3"
stdweb = { version = "0.4.14", optional = true }
dodrio = { version = "0.2.0", optional = true }
web-sys = { version = "0.3.16", optional = true, features = [
"Event",
"Element"
] }
[features]
dodrio_macro = ["web-sys", "dodrio", "typed-html-macros/dodrio"]

View File

@ -12,8 +12,8 @@ use htmlescape::encode_minimal;
/// # Examples
///
/// ```
/// # use typed_html::html;
/// # use typed_html::dom::DOMTree;
/// # use axohtml::html;
/// # use axohtml::dom::DOMTree;
/// # fn main() {
/// let tree: DOMTree<String> = html!(
/// <div class="hello">

View File

@ -5,7 +5,7 @@
use crate::dom::{Node, TextNode};
use crate::types::*;
use crate::OutputType;
use typed_html_macros::declare_elements;
use axohtml_macros::declare_elements;
// Marker traits for element content groups
@ -452,7 +452,7 @@ declare_elements! {
#[test]
fn test_data_attributes() {
use crate as typed_html;
use crate as axohtml;
use crate::{dom::DOMTree, html};
let frag: DOMTree<String> = html!(<div data-id="1234">"Boo!"</div>);

View File

@ -6,20 +6,20 @@
//!
//! ```
//! # #![recursion_limit = "128"]
//! # use typed_html::html;
//! # use typed_html::dom::{DOMTree, VNode};
//! # use typed_html::types::Metadata;
//! # use axohtml::html;
//! # use axohtml::dom::{DOMTree, VNode};
//! # use axohtml::types::Metadata;
//! # fn main() {
//! let mut doc: DOMTree<String> = html!(
//! <html>
//! <head>
//! <title>"Hello Kitty"</title>
//! <meta name=Metadata::Author content="Not Sanrio Co., Ltd"/>
//! <title>"Hello Axo"</title>
//! <meta name=Metadata::Author content="Axo Developer Co."/>
//! </head>
//! <body>
//! <h1>"Hello Kitty"</h1>
//! <h1>">o_o<"</h1>
//! <p class="official">
//! "She is not a cat. She is a human girl."
//! "The tool company for tool companies"
//! </p>
//! { (0..3).map(|_| html!(
//! <p class="emphasis">
@ -90,9 +90,9 @@
//!
//! ```
//! # use std::convert::{TryFrom, TryInto};
//! # use typed_html::html;
//! # use typed_html::dom::DOMTree;
//! # use typed_html::types::{Class, SpacedSet};
//! # use axohtml::html;
//! # use axohtml::dom::DOMTree;
//! # use axohtml::types::{Class, SpacedSet};
//! # fn main() -> Result<(), &'static str> {
//! let classList: SpacedSet<Class> = ["foo", "bar", "baz"].try_into()?;
//! # let doc: DOMTree<String> =
@ -120,8 +120,8 @@
//! ## Example
//!
//! ```
//! # use typed_html::{html, text};
//! # use typed_html::dom::DOMTree;
//! # use axohtml::{html, text};
//! # use axohtml::dom::DOMTree;
//! # fn main() {
//! # let doc: DOMTree<String> =
//! html!(
@ -147,14 +147,14 @@
//! ensure you're not using any event handlers that can't be printed.
//!
//! ```
//! # use typed_html::html;
//! # use typed_html::dom::DOMTree;
//! # use axohtml::html;
//! # use axohtml::dom::DOMTree;
//! # fn main() {
//! let doc: DOMTree<String> = html!(
//! <p>"Hello Kitty"</p>
//! <p>"Hello Axo"</p>
//! );
//! let doc_str = doc.to_string();
//! assert_eq!("<p>Hello Kitty</p>", doc_str);
//! assert_eq!("<p>Hello Axo</p>", doc_str);
//! # }
//! ```
//!
@ -166,20 +166,14 @@
//! DOM tree and use it to build an actual DOM tree with `stdweb` or pass it on to
//! your favourite virtual DOM system.
//!
//! # Licence
//! # License
//!
//! Copyright 2018 Bodil Stokke
//! Copyright 2018 Bodil Stokke, 2022 Axo Developer Co.
//!
//! This software is subject to the terms of the Mozilla Public License, v. 2.0. If
//! a copy of the MPL was not distributed with this file, You can obtain one at
//! <http://mozilla.org/MPL/2.0/>.
//!
//! # Code of Conduct
//!
//! Please note that this project is released with a [Contributor Code of
//! Conduct][coc]. By participating in this project you agree to abide by its terms.
//!
//! [coc]: https://www.contributor-covenant.org/version/1/4/code-of-conduct
//! [JSX]: https://reactjs.org/docs/introducing-jsx.html
//! [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [String]: https://doc.rust-lang.org/std/string/struct.String.html
@ -197,10 +191,10 @@ pub extern crate htmlescape;
use std::fmt::Display;
pub use typed_html_macros::html;
pub use axohtml_macros::html;
#[cfg(feature = "dodrio_macro")]
pub use typed_html_macros::dodrio;
pub use axohtml_macros::dodrio;
pub mod dom;
pub mod elements;

View File

@ -1,4 +1,4 @@
#[cfg(feature = "stdweb")]
pub mod stdweb;
#[cfg(feature = "dodrio_macro")]
pub mod dodrio;
#[cfg(feature = "stdweb")]
pub mod stdweb;

View File

@ -4,9 +4,9 @@ use std::marker::PhantomData;
use stdweb::web::event::*;
use stdweb::web::{self, Element, EventListenerHandle, IElement, IEventTarget, INode};
use crate::OutputType;
use crate::dom::VNode;
use crate::events::EventHandler;
use crate::OutputType;
/// DOM output using the stdweb crate
pub struct Stdweb;

View File

@ -26,7 +26,7 @@ impl Class {
let s = s.borrow();
Self::from_str(s).unwrap_or_else(|err| {
panic!(
"typed_html::types::Class: {:?} is not a valid class name: {}",
"axohtml::types::Class: {:?} is not a valid class name: {}",
s, err
)
})

View File

@ -24,9 +24,8 @@ impl Id {
/// Panics if the provided string is invalid.
pub fn new<S: Borrow<str>>(id: S) -> Self {
let id = id.borrow();
Self::from_str(id).unwrap_or_else(|err| {
panic!("typed_html::types::Id: {:?} is not a valid ID: {}", id, err)
})
Self::from_str(id)
.unwrap_or_else(|err| panic!("axohtml::types::Id: {:?} is not a valid ID: {}", id, err))
}
}

View File

@ -14,7 +14,7 @@ use std::str::FromStr;
///
/// ```
/// # use std::convert::{TryFrom, TryInto};
/// use typed_html::types::{Class, SpacedSet};
/// use axohtml::types::{Class, SpacedSet};
///
/// # fn main() -> Result<(), &'static str> {
/// let classList: SpacedSet<Class> = "foo bar baz".try_into()?;