English

ページを組み立てる

ページは Rust の関数で組み立てた El の値の木で、サーバーが HTML に描画します。

要素

html モジュールには div()、p()、button() のようによく使うタグごとの関数と、それ以外のタグに使う el("tag") があります。どれも El<S> を返します。S はページのシグナル構造体です。El のメソッドは要素そのものを返すので、木全体を 1 つの式で書けます。

use hypersig::Route;
use hypersig::html::{a, div, h1, p};

hypersig::routes! {
    fn pages(Router<()>);
    const ADA: Route = "/ada" => ada;
}

let card = div::<Profile>()
    .id("ada")
    .child(h1().text("Ada Lovelace"))
    .child(
        p().text("Wrote the first program. ")
            .child(a().href(ADA.link()).text("Read more")),
    );

assert_eq!(
    card.render(),
    r#"<div id="ada"><h1>Ada Lovelace</h1><p>Wrote the first program. <a href="/ada">Read more</a></p></div>"#,
);

text は固定のテキストを、child は要素か文字列を末尾に追加します。href は Route からページへのリンクを張ります。これは ページとリンク で説明します。attr は普通の属性を設定します。id と class には専用のメソッドがあり、attr にこの 2 つや data-hs- で始まる名前を渡すとパニックします。data-hs- の属性は型付きのメソッドだけが書きます。

ページ

Page は木を包んで完全な HTML 文書にします。Page::new はタイトルとシグナルの初期値を受け取り、child は <body> に要素を追加し、render はハンドラが送る HTML を返します。文書は RUNTIME_PATH からランタイムを読み込みます。このパスは /_hypersig/ の下にあり、ファイルのハッシュを名前に含みます。RouterExt::hypersig_runtime がそのパスでファイルを配信します。

シグナルに追従する値

ブラウザで変わる値を要素に表示できます。束縛はそれぞれシグナルを使った式を受け取り、最初は初期値で描画されるので、HTML にはランタイムが計算するのと同じ値が入っています。

use hypersig::html::{button, p, span};
use hypersig::{Signals, if_else};
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
struct Upload {
    progress: i32,
    busy: bool,
}

let progress = Upload::progress();
let busy = Upload::busy();

// Text that follows a signal.
let label = span::<Upload>().bind_text(progress.to_text().concat(" %"));
// An attribute that follows a signal, and one that is present while a flag is true.
let cancel = button::<Upload>()
    .bind_attr("aria-label", if_else(busy, "Cancel the upload", "Nothing to cancel"))
    .attr_when("disabled", !busy)
    .text("Cancel");
// An element shown only while the upload runs.
let notice = p::<Upload>().show(busy).text("Uploading…");
  • bind_text は要素のテキストを置き換えます。
  • bind_attr は属性を文字列の式の値に合わせ続けます。attr_when は、条件が成り立つ間だけ disabled のような真偽値の属性を付けます。
  • show は条件が偽の間、要素を隠します。when(cond, el) は要素を取り除くので、要素が戻るたびに on_load がもう一度実行され、入力欄は空から始まります。
  • class_when と css_when は、条件が成り立つ間だけクラスやスタイルを付けます。詳しくは スタイル で説明します。

このデモはテキスト欄を name に束縛し、空かどうかで 2 つの段落のどちらかを表示します。

実行結果

greeting.rs
use hypersig::html::{div, p, span};
use hypersig::style::Style;
use hypersig::style::pattern::stack;
use hypersig::style::value::rem;
use hypersig::{Page, Signals};
use hypersig_ui::TextField;
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
pub struct Greeting {
    name: String,
}

pub fn page() -> Page<Greeting> {
    let name = Greeting::name();
    let typed = !name.trim().is_empty();
    Page::new(
        "あいさつ",
        Greeting {
            name: String::new(),
        },
    )
    .child(
        div()
            .css(stack(rem(1)).max_width(rem(20)))
            .child(TextField::new(
                "名前",
                name,
            ))
            .child(p().show(!typed.clone()).text("上の欄に名前を入力してください。"))
            .child(
                p().show(typed)
                    .text("こんにちは、")
                    .child(span().bind_text(name.trim()))
                    .text("さん。"),
            )
            .child(
                p().css(Style::new().font_size(rem(0.875)))
                    .show(name.char_count().gt(12))
                    .text("長い名前です。"),
            ),
    )
}

コンポーネント

コンポーネントは El<S> を返す Rust の関数です。S についてジェネリックにし、読むシグナルを引数で受け取れば、どのページでも使えます。

use hypersig::html::{El, input, label, span};
use hypersig::{Sig, Signals};
use serde::{Deserialize, Serialize};

/// A labeled text field bound to `value`, on any page.
fn field<S>(text: &str, value: Sig<S, String>) -> El<S> {
    label()
        .child(span().text(text))
        .child(input().bind(value))
}

#[derive(Signals, Serialize, Deserialize)]
struct Signup {
    name: String,
    email: String,
}

let form = hypersig::html::form::<Signup>()
    .child(field("Name", Signup::name()))
    .child(field("Email", Signup::email()));

hypersig_ui は shadcn/ui に倣った見た目のこうした関数を集めたクレートです。このサイトのデモはそのボタンとテキスト欄を使っています。