hypersig_ui クレート
ボタン、入力欄、選択、タブ、ダイアログ、メニュー、ツールチップ、トーストを、ブラウザのネイティブ要素を使い、WAI-ARIA Authoring Practices に沿って作っています。どれも El を返すので、ほかの要素と同じようにハンドラや子要素を足せます。
<button>、<select>、<details>、<dialog>、popover にはブラウザがもともと持つ振る舞いがあり、コンポーネントはそれをそのまま使います。
タブ、リストボックスの選択肢、メニューの項目の間は、矢印キー、Home、End で移ります。コンボボックスはフォーカスをテキスト欄に置いたままにします。
shadcn/ui の zinc テーマをもとに、コントロールの境界線を 3:1、補足のテキストを 4.5:1 にし、フォーカスを単色の outline で描きます。
const のハンドルで、ダイアログとそれを開閉するボタンを対応付けます。ブラウザは invoker commands を通して、ダイアログをモーダルとして開きます。
実行結果
use hypersig::html::{div, p};
use hypersig::style::pattern::{stack, wrap};
use hypersig::style::value::rem;
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, Dialog, Side, TextField, dialog_footer};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
pub struct Project {
name: String,
status: String,
}
const RENAME: Dialog<Project> = Dialog::new("rename");
const DELETE: Dialog<Project> = Dialog::new("delete");
const DETAILS: Dialog<Project> = Dialog::new("details");
pub fn page() -> Page<Project> {
let name = Project::name();
let status = Project::status();
let initial = Project {
name: "hypersig".into(),
status: String::new(),
};
Page::new(
"プロジェクト",
initial,
)
.child(
div()
.css(stack(rem(1)))
.child(
div()
.css(wrap(rem(0.5)))
.child(
RENAME
.trigger(ButtonVariant::Outline, ButtonSize::Default)
.text("名前を変更"),
)
.child(
DETAILS
.trigger(ButtonVariant::Outline, ButtonSize::Default)
.text("詳細"),
)
.child(
DELETE
.trigger(ButtonVariant::Destructive, ButtonSize::Default)
.text("削除"),
),
)
.child(p().attr("role", "status").bind_text(status))
.child(
RENAME.dialog(
"プロジェクトの名前を変更",
"新しい名前は、プロジェクトが表示されるすべての場所に反映されます。",
div()
.css(stack(rem(1)))
.child(TextField::new(
"名前",
name,
))
.child(
dialog_footer()
.child(
RENAME
.close(ButtonVariant::Outline, ButtonSize::Default)
.text("キャンセル"),
)
.child(
RENAME
.close(ButtonVariant::Default, ButtonSize::Default)
.on_click(status.set("プロジェクトの名前を変更しました。"))
.text("保存"),
),
),
),
)
.child(
DETAILS.sheet(
Side::Right,
"プロジェクトの詳細",
"3 月 3 日に作成し、12 回デプロイしました。",
dialog_footer().child(
DETAILS
.close(ButtonVariant::Outline, ButtonSize::Default)
.text("完了"),
),
),
)
.child(
DELETE.alert_dialog(
"このプロジェクトを削除しますか?",
"プロジェクトとそのデプロイが削除されます。この操作は取り消せません。",
dialog_footer()
.child(
DELETE
.close(ButtonVariant::Outline, ButtonSize::Default)
.text("キャンセル"),
)
.child(
DELETE
.close(ButtonVariant::Destructive, ButtonSize::Default)
.on_click(status.set("プロジェクトを削除しました。"))
.text("削除"),
),
),
),
)
}