Skip to content

Commit

Permalink
#[component] macro (#92)
Browse files Browse the repository at this point in the history
* Move template! into sub-module

* Move ui tests into template/ folder

* Add component macro

* Fix intra-doc links

* Add component! to prelude

* Implement component macro

* Use #[component] attribute macro

* Fix ssr benchmarks

* Fix tests

* Update documentation
  • Loading branch information
lukechu10 authored Apr 7, 2021
1 parent 5644494 commit 2ef0a44
Show file tree
Hide file tree
Showing 44 changed files with 743 additions and 466 deletions.
5 changes: 0 additions & 5 deletions docs/markdown/advanced/higher_order_components.md

This file was deleted.

14 changes: 6 additions & 8 deletions docs/markdown/basics/components.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Components

Components in `maple` are simply functions that return `TemplateResult<G>`.
They receive their props through function arguments.
Components in `maple` are structs that implement `Component`. A component can automatically be
created with the `#[component(ComponentName<G>)]` attribute on a function.

Components receive their props through function arguments.

For components to automatically react to prop changes, they should accept a prop with type `StateHandle<T>` and call the function in the `template!` to subscribe to the state.
A `StateHandle<T>` is just a readonly `Signal<T>`.
Expand All @@ -11,14 +13,10 @@ Getting a `StateHandle<T>` from a `Signal<T>` is easy. Just call the `.handle()`
Here is an example of a simple component that displays the value of its prop:

```rust
// This is temporary and will later be removed.
// Currently, the template! macro assumes that all
// components start with an uppercase character.
#![allow(non_snake_case)]

use maple_core::prelude::*;

fn MyComponent<G: GenericNode>(value: StateHandle<i32>) -> TemplateResult<G> {
#[component(MyComponent<G>)]
fn my_component(value: StateHandle<i32>) -> TemplateResult<G> {
template! {
div(class="my-component") {
"Value: " (value.get())
Expand Down
3 changes: 2 additions & 1 deletion docs/src/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ extern "C" {
fn highlight_all();
}

pub fn Content<G: GenericNode>() -> TemplateResult<G> {
#[component(Content<G>)]
pub fn content() -> TemplateResult<G> {
let location = web_sys::window()
.unwrap()
.document()
Expand Down
3 changes: 2 additions & 1 deletion docs/src/header.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use maple_core::prelude::*;

pub fn Header<G: GenericNode>() -> TemplateResult<G> {
#[component(Header<G>)]
pub fn header() -> TemplateResult<G> {
template! {
header {
nav(class="navbar navbar-expand-sm navbar-dark bg-dark") {
Expand Down
3 changes: 2 additions & 1 deletion docs/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use maple_core::prelude::*;

pub fn Index<G: GenericNode>() -> TemplateResult<G> {
#[component(Index<G>)]
pub fn index() -> TemplateResult<G> {
template! {
div {
h1 {
Expand Down
5 changes: 2 additions & 3 deletions docs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#![allow(non_snake_case)]

mod content;
mod header;
mod index;
mod sidebar;

use maple_core::prelude::*;

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let location = web_sys::window()
.unwrap()
.document()
Expand Down
6 changes: 2 additions & 4 deletions docs/src/sidebar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use maple_core::prelude::*;

pub fn Sidebar<G: GenericNode>() -> TemplateResult<G> {
#[component(Sidebar<G>)]
pub fn sidebar() -> TemplateResult<G> {
template! {
div(class="p-3 bg-white", style="min-width: 180px") {
ul(class="list-unstyled ps-0") {
Expand Down Expand Up @@ -69,9 +70,6 @@ pub fn Sidebar<G: GenericNode>() -> TemplateResult<G> {
a(class="btn btn-sm btn-light btn-block", href="/advanced/js_interop") {
"JS Interop"
}
a(class="btn btn-sm btn-light btn-block", href="/advanced/higher_order_components") {
"Higher-Order Components"
}
}
}
li(class="mb-1") {
Expand Down
8 changes: 4 additions & 4 deletions examples/components/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#![allow(non_snake_case)]

use maple_core::prelude::*;

fn MyComponent<G: GenericNode>(num: StateHandle<i32>) -> TemplateResult<G> {
#[component(MyComponent<G>)]
fn my_component(num: StateHandle<i32>) -> TemplateResult<G> {
template! {
div(class="my-component") {
"My component"
Expand All @@ -14,7 +13,8 @@ fn MyComponent<G: GenericNode>(num: StateHandle<i32>) -> TemplateResult<G> {
}
}

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let state = Signal::new(1);

let increment = cloned!((state) => move |_| {
Expand Down
5 changes: 2 additions & 3 deletions examples/counter/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#![allow(non_snake_case)]

use maple_core::prelude::*;

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let counter = Signal::new(0);

create_effect(cloned!((counter) => move || {
Expand Down
5 changes: 2 additions & 3 deletions examples/hello/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#![allow(non_snake_case)]

use maple_core::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{Event, HtmlInputElement};

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let name = Signal::new(String::new());

let handle_change = cloned!((name) => move |event: Event| {
Expand Down
5 changes: 2 additions & 3 deletions examples/ssr/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#![allow(non_snake_case)]

use maple_core::prelude::*;

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let name = Signal::new(String::new());

let handle_change = move |_| unreachable!();
Expand Down
3 changes: 2 additions & 1 deletion examples/todomvc/src/copyright.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use maple_core::prelude::*;

pub fn Copyright<G: GenericNode>() -> TemplateResult<G> {
#[component(Copyright<G>)]
pub fn copyright() -> TemplateResult<G> {
template! {
footer(class="info") {
p { "Double click to edit a todo" }
Expand Down
5 changes: 3 additions & 2 deletions examples/todomvc/src/footer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use maple_core::prelude::*;

use crate::{AppState, Filter};

pub fn Footer<G: GenericNode>(app_state: AppState) -> TemplateResult<G> {
#[component(Footer<G>)]
pub fn footer(app_state: AppState) -> TemplateResult<G> {
let items_text = cloned!((app_state) => move || {
match app_state.todos_left() {
1 => "item",
Expand All @@ -24,7 +25,7 @@ pub fn Footer<G: GenericNode>(app_state: AppState) -> TemplateResult<G> {
span { " " (items_text()) " left" }
}
ul(class="filters") {
Indexed(IndexedProps {
Indexed<_, _>(IndexedProps {
iterable: Signal::new(vec![Filter::All, Filter::Active, Filter::Completed]).handle(),
template: cloned!((app_state2) => move |filter| {
let selected = cloned!((app_state2) => move || filter == *app_state2.filter.get());
Expand Down
3 changes: 2 additions & 1 deletion examples/todomvc/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use web_sys::{Event, HtmlInputElement, KeyboardEvent};

use crate::AppState;

pub fn Header<G: GenericNode>(app_state: AppState) -> TemplateResult<G> {
#[component(Header<G>)]
pub fn header(app_state: AppState) -> TemplateResult<G> {
let value = Signal::new(String::new());

let input_ref = NodeRef::<G>::new();
Expand Down
10 changes: 9 additions & 1 deletion examples/todomvc/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ use web_sys::{Event, HtmlInputElement, KeyboardEvent};

use crate::{AppState, Todo};

pub fn Item<G: GenericNode>(todo: Signal<Todo>, app_state: AppState) -> TemplateResult<G> {
pub struct ItemProps {
pub todo: Signal<Todo>,
pub app_state: AppState,
}

#[component(Item<G>)]
pub fn item(props: ItemProps) -> TemplateResult<G> {
let ItemProps { todo, app_state } = props;

let title = cloned!((todo) => move || todo.get().title.clone());
let completed = create_selector(cloned!((todo) => move || todo.get().completed));
let id = todo.get().id;
Expand Down
7 changes: 4 additions & 3 deletions examples/todomvc/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use web_sys::HtmlInputElement;

use crate::{AppState, Filter};

pub fn List<G: GenericNode>(app_state: AppState) -> TemplateResult<G> {
#[component(List<G>)]
pub fn list(app_state: AppState) -> TemplateResult<G> {
let todos_left = create_selector(cloned!((app_state) => move || {
app_state.todos_left()
}));
Expand Down Expand Up @@ -40,10 +41,10 @@ pub fn List<G: GenericNode>(app_state: AppState) -> TemplateResult<G> {
label(for="toggle-all")

ul(class="todo-list") {
Keyed(KeyedProps {
Keyed<_, _, _, _>(KeyedProps {
iterable: filtered_todos,
template: move |todo| template! {
crate::item::Item(todo, app_state.clone())
crate::item::Item(crate::item::ItemProps { todo, app_state: app_state.clone() })
},
key: |todo| todo.get().id,
})
Expand Down
5 changes: 2 additions & 3 deletions examples/todomvc/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![allow(non_snake_case)]

mod copyright;
mod footer;
mod header;
Expand Down Expand Up @@ -132,7 +130,8 @@ impl Filter {

const KEY: &str = "todos-maple";

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let local_storage = web_sys::window()
.unwrap()
.local_storage()
Expand Down
5 changes: 2 additions & 3 deletions examples/tweened/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#![allow(non_snake_case)]

use std::time::Duration;

use maple_core::reactive::Tweened;
use maple_core::{easing, prelude::*};

fn App<G: GenericNode>() -> TemplateResult<G> {
#[component(App<G>)]
fn app() -> TemplateResult<G> {
let progress = Tweened::new([0.0, 1.0], Duration::from_millis(250), easing::quad_out);
let progress0 = progress.clone();
let progress1 = progress.clone();
Expand Down
42 changes: 0 additions & 42 deletions maple-core-macro/src/component.rs

This file was deleted.

Loading

0 comments on commit 2ef0a44

Please sign in to comment.