fork: rename and reversion
This commit is contained in:
parent
e18d328951
commit
ddc8bc12d0
|
@ -2,9 +2,5 @@
|
|||
members = [
|
||||
"typed-html",
|
||||
"macros",
|
||||
"examples/stdweb",
|
||||
"examples/iron",
|
||||
"examples/dodrio/counter",
|
||||
"examples/dodrio/todomvc",
|
||||
"ui",
|
||||
"tests",
|
||||
]
|
||||
|
|
44
README.md
44
README.md
|
@ -1,13 +1,22 @@
|
|||
[](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
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
```
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
|
@ -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"]
|
|
@ -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
|
||||
```
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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 ≎ 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>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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 ≎ text(incomplete_count.into_bump_str())]
|
||||
}</strong>
|
||||
{ bumpalo::vec![in ≎ text(items_left)] }
|
||||
</span>
|
||||
<ul class="filters">
|
||||
{ bumpalo::vec![in ≎
|
||||
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 ≎ 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 ≎ 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 ≎
|
||||
self.header(cx), self.todos_list(cx), self.footer(cx)
|
||||
] }</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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();
|
||||
}
|
|
@ -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"
|
|
@ -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
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
default-target = "wasm32-unknown-unknown"
|
|
@ -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);
|
||||
}
|
|
@ -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/
|
|
@ -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>
|
|
@ -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);
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"name": "types-html-example",
|
||||
"version": "0.1.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.16.4"
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
];
|
||||
|
|
|
@ -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)> + '_ {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
|
@ -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"]
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[cfg(feature = "stdweb")]
|
||||
pub mod stdweb;
|
||||
#[cfg(feature = "dodrio_macro")]
|
||||
pub mod dodrio;
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub mod stdweb;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()?;
|
||||
|
|
Loading…
Reference in New Issue