diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 6a086d2..dcc8c0b 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -2,4 +2,16 @@ [Introduction](./intro.md) -- [Chapter 1](./chapter_1.md) +- [Examples](./examples/index.md) + - ["Hello world" without statue](./examples/hello_world_without_statue.md) + - ["Hello world" with statue](./examples/hello_world_with_statue.md) +- [Syntax](./syntax/index.md) + - [`initialize_elements` is a function-like proc macro](./syntax/initialize_elements_is_a_functionlike_proc_macro.md) + - [Structure of the accepted tokens](./syntax/structure_of_the_accepted_tokens.md) + - [The `html` field](./syntax/html_field.md) + - [The `elements` field](./syntax/elements_field.md) + - [Single selector kind](./syntax/single_selector_kind.md) + - [Multi selector kind](./syntax/multi_selector_kind.md) + - [The `opts` field](./syntax/opts_field.md) + - [Return type kinds](./syntax/return_type_kinds.md) +- [Supported types](./supported_types.md) diff --git a/docs/src/chapter_1.md b/docs/src/chapter_1.md deleted file mode 100644 index b743fda..0000000 --- a/docs/src/chapter_1.md +++ /dev/null @@ -1 +0,0 @@ -# Chapter 1 diff --git a/docs/src/examples/hello_world_with_statue.md b/docs/src/examples/hello_world_with_statue.md new file mode 100644 index 0000000..7a12869 --- /dev/null +++ b/docs/src/examples/hello_world_with_statue.md @@ -0,0 +1,131 @@ +# "Hello world" with statue + +Using ["Hello world" without statue](./hello_world_without_statue.md) as a starting point, we can update it to use `statue` for querying selectors. + +## wb-hello-world/wb-hello-world/Cargo.toml + +Find the dependencies in the `Cargo.toml` file: + +```toml +[dependencies] +wasm-bindgen = "0.2.84" +console_error_panic_hook = { version = "0.1.7", optional = true } + +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'HtmlInputElement', + 'Window', + 'EventTarget', + 'InputEvent' +] +``` + +And add the `statue` dependency: + +```toml +[dependencies] +wasm-bindgen = "0.2.84" +console_error_panic_hook = { version = "0.1.7", optional = true } +statue = "0.3" + +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'HtmlInputElement', + 'Window', + 'EventTarget', + 'InputEvent' +] +``` + +## wb-hello-world/wb-hello-world/src/lib.rs + +Replace the initialization of the elements with manual querying selectors and casting to the desired types: + +```rust +let window = web_sys::window().unwrap(); +let document = window.document().unwrap(); + +let header = document.query_selector("h1").unwrap().unwrap(); +let name_input = document.get_element_by_id("name-input").unwrap() + .dyn_into::().unwrap(); +``` + +to the invocation of the `statue::initialize_elements` macro: + +```rust +initialize_elements!{ + html: "../index.html", + elements: { + let header = Single("h1"); + let name_input = Single("#name-input"); + } +}; +``` + +*Note: the various aspects of the syntax of the macro will be discussed in the `Syntax` section of the guide*. + +Also, update the `use` statements from this: + +```rust +use utils::set_panic_hook; +use wasm_bindgen::prelude::*; +``` + +to this: + +```rust +use utils::set_panic_hook; +use wasm_bindgen::prelude::*; +use statue::initialize_elements; +``` + +## Build the project + +Once you have the `Cargo.toml` and `lib.rs` files set up, build the project: + +```console +cd wb-hello-world +wasm-pack build --target web +cd .. +``` + +This will create a `pkg` directory in the `wb-hello-world` directory, and the `pkg` directory will contain the `wb_hello_world.js` file, among others. + +## Run the project + +> **Note**: You cannot directly open `index.html` in your web browser due to [CORS][cors] +> limitations. Instead, you can set up a quick development environment using +> Python's built-in HTTP server: +> +> ```console +> wasm-pack build --target web +> python3 -m http.server 8080 +> ``` +> +> If you don't have Python installed, you can also use [miniserve][miniserve] which +> is installable via Cargo: +> +> ```console +> cargo install miniserve +> miniserve . --index "index.html" -p 8080 +> ``` + +[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[miniserve]: https://crates.io/crates/miniserve + +## About the advantages of using `statue` + +Even in this simple example, using `statue` has some benefits: + +* You can enjoy the benefits of using contextual suggestions and type inference in your IDE, since the `statue` macro invocation expands to strongly-typed expressions with inferred types. +* If you make a typo in the selector, the `statue` macro invocation will fail to compile, which will help you catch the errors at compile time. +* If there are breaking changes in `index.html`, the `statue` macro invocation will fail to compile, which will help you catch the errors early. +* You can use the familiar CSS selector syntax to query elements instead of picking the right method from the `web_sys` API. diff --git a/docs/src/examples/hello_world_without_statue.md b/docs/src/examples/hello_world_without_statue.md new file mode 100644 index 0000000..dc0ad9d --- /dev/null +++ b/docs/src/examples/hello_world_without_statue.md @@ -0,0 +1,193 @@ +# Hello world without statue + +First of all, we will create a simple webpage that displays `"Hello, "` with `wasm_bindgen` **but without `statue`**. This will help you understand the basics of Rust and WebAssembly for browsers. + +Go to an empty directory: + +```console +mkdir wb-hello-world +cd wb-hello-world +``` + +## wb-hello-world/index.html + +In this empty directory, create the `index.html` with the following contents: + +```html + + + + + + +

Hello, friend!

+ + + +``` + +This is a simple HTML file that has + +* An `ES6` module that imports the `hello_world` package and calls the `init` function, +* An `h1` element that in the absence of JS or WASM displays `"Hello, friend!"`, +* An `input` element that allows the user to enter their name. + +If we were to run a `tree` command in the `wb-hello-world` directory, we would see the following: + +```text +index.html +``` + +## wb-hello-world/wb-hello-world + +Ensure that you have [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) installed: + +```console +wasm-pack --version +``` + +With `wasm-pack` installed, create a new Rust WASM project: + +```console +wasm-pack new wb-hello-world +``` + +If you were to run a `tree` command in the `wb-hello-world` directory again, you would see the following: + +```text +index.html +wb-hello-world + ├───src + │ ├───lib.rs + │ └───utils.rs + ├───Cargo.toml + ................. + └───tests +``` + +What matters to us is that the nested `wb-hello-world` directory is a Rust library that we can build into a WASM package. + +## wb-hello-world/wb-hello-world/Cargo.toml + +Find the `[dependencies]` section in the `Cargo.toml` file of the Rust library: + +```toml +[dependencies] +wasm-bindgen = "0.2.84" + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.7", optional = true } +``` + +and add more dependencies: + +```toml +[dependencies] +wasm-bindgen = "0.2.84" +console_error_panic_hook = { version = "0.1.7", optional = true } + +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'HtmlInputElement', + 'Window', + 'EventTarget', + 'InputEvent' +] +``` + +Each of them will play a role in our project: + +* `wasm-bindgen` will allow us to expose Rust functions to JavaScript. +* `console_error_panic_hook` will provide better debugging of panics, if any occur, by logging them with `console.error`. +* `web-sys` will provide bindings to the Web APIs. + +## wb-hello-world/wb-hello-world/src/lib.rs + +Replace the contents of the `lib.rs` file with the following: + +```rust +mod utils; + +use utils::set_panic_hook; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(start)] +pub fn start() { + set_panic_hook(); + + let window = web_sys::window().unwrap(); + let document = window.document().unwrap(); + + let header = document.query_selector("h1").unwrap().unwrap(); + let name_input = document.get_element_by_id("name-input").unwrap() + .dyn_into::().unwrap(); + + { + let closure = Closure::::new(move |event: web_sys::InputEvent| { + let input_element = event.target().unwrap().dyn_into::().unwrap(); + let name = input_element.value(); + header.set_inner_html(&format!("Hello, {name}!")); + }); + name_input.add_event_listener_with_callback("input", closure.as_ref().unchecked_ref()).unwrap(); + closure.forget(); + } +} +``` + +This code does the following: + +* Adds `wb-hello-world/wb-hello-world/src/utils.rs` as a module called `utils`. +* Imports the `set_panic_hook` function from the `utils` module. +* Imports the [`wasm_bindgen` prelude](https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/prelude/index.html). +* Defines a `start` function that will be called when the WASM module is loaded. + +The `start` function does the following: + +* Calls the `set_panic_hook` function to set up better debugging of panics. +* Gets the `window` and `document` objects from the Web APIs. +* Queries the `h1` element and the `name-input` element. Since `web_sys` doesn't have a dedicated type for `h1` elements, we keep the type of the `header` variable as [`web_sys::Element`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Element.html). +* Creates a closure that is then added as a listener to the `input` event on the `name_input` element. The closure updates the `h1` element with the text `"Hello, !"` where `` is the value of the `name_input` element. This pattern is described in the [official `wasm-bindgen` guide](https://rustwasm.github.io/docs/wasm-bindgen/examples/closures.html). + +## Build the project + +Once you have the `Cargo.toml` and `lib.rs` files set up, build the project: + +```console +cd wb-hello-world +wasm-pack build --target web +cd .. +``` + +This will create a `pkg` directory in the `wb-hello-world` directory, and the `pkg` directory will contain the `wb_hello_world.js` file, among others. + +## Run the project + +> **Note**: You cannot directly open `index.html` in your web browser due to [CORS][cors] +> limitations. Instead, you can set up a quick development environment using +> Python's built-in HTTP server: +> +> ```console +> wasm-pack build --target web +> python3 -m http.server 8080 +> ``` +> +> If you don't have Python installed, you can also use [miniserve][miniserve] which +> is installable via Cargo: +> +> ```console +> cargo install miniserve +> miniserve . --index "index.html" -p 8080 +> ``` + +[cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[miniserve]: https://crates.io/crates/miniserve diff --git a/docs/src/examples/index.md b/docs/src/examples/index.md new file mode 100644 index 0000000..b0e8492 --- /dev/null +++ b/docs/src/examples/index.md @@ -0,0 +1,3 @@ +# Examples of using `statue` + +This section contains examples of using the `statue`, `wasm-bindgen`, `js-sys`, and `web-sys` crates, as well as `wasm-pack` CLI tool. Each example should have more information about what it's doing. diff --git a/docs/src/supported_types.md b/docs/src/supported_types.md new file mode 100644 index 0000000..6ab56b6 --- /dev/null +++ b/docs/src/supported_types.md @@ -0,0 +1,82 @@ +# Supported types + +At the time of writing, the `statue` library supports the following types of HTML elements: + +* `HtmlElement` - the most generic type that represents an HTML element. +* `HtmlDialogElement` - represents a `` element. +* `HtmlDivElement` - represents a `
` element. +* `HtmlImageElement` - represents an `` element. +* `HtmlButtonElement` - represents a `