Skip to content
fandhe-frontendcore v0.4.3
GitHub

example-preview-toolbar

fandhe-frontend-pre-styled-ui の tabs / select / button / clipboard / code / popover の 6 部品を合成した、部品のコード例を「プレビュー + ツールバー + コード」の 3 段構成 1 枠で見せる実例です。Blocks セクションは新規部品を追加するものではなく、既存の Themes/Primitives 部品を組み合わせた実例集であることに注意してください(主参照は対応表 ID R0094。出典の固有名・ファイル名は記載しません)。

上段にウィジェットの実演(開いた状態の popover)、中段にコード種別タブ(Rust/CSS)とスタイル選択・外部実行・コピーのツールバー、下段に選択中のコードを表示します。3 段は 1 つの枠にまとまり、狭い幅ではツールバー右側の操作群が折り返します。

本 Demo は静的な表示例であり、<form> 要素は一切持ちません。コード種別タブは Rust を選択状態に固定し、CSS タブは押下不能(disabled)です。スタイル選択も閉じた状態の固定表示で disabled にしています。「外部で実行」ボタンは遷移先・クリック処理を持たないため押下不能、コピーは未コピー(idle)状態の固定表示です(実際の切り替え・コピー動作には fandhe-frontend-wasm-full の JS 配線が必要で、docs サイトは JS ハイドレーションを行いません)。下段のコードは code のインライン用装飾(背景・余白・角丸)を外したブロック表示にし、枠に接するタブ・コード・コピー操作のフォーカスリングは枠の内側に描きます。文言はすべて独自に書いた架空のものであり、実企業名・実クレデンシャル・PII を含みません。

Demo

use fandhe_frontend_core::{div, text, Node};
use fandhe_frontend_pre_styled_ui::popover::{self, OpenState};

fn preview() -> Node {
    let trigger = popover::trigger(
        OpenState::Open,
        true,
        Some("demo-content"),
        vec![],
        vec![text("表示設定")],
    );
    let content = popover::content(
        OpenState::Open,
        Some("demo-content"),
        Some("demo-title"),
        None,
        vec![],
        vec![
            popover::title(Some("demo-title"), vec![], vec![text("表示設定")]),
            text("ウィジェットの見た目を切り替えます。"),
        ],
    );
    let positioner = popover::positioner(OpenState::Open, vec![], vec![content]);
    div(
        vec![],
        vec![popover::root(OpenState::Open, vec![], vec![trigger, positioner])],
    )
}

使用部品

Rust コード

Rust
use fandhe_frontend_core::{div, pre, text, Node};
use fandhe_frontend_pre_styled_ui::button::{self, ButtonProps, ButtonVariant};
use fandhe_frontend_pre_styled_ui::clipboard;
use fandhe_frontend_pre_styled_ui::code::{self, CodeProps};
use fandhe_frontend_pre_styled_ui::popover::{self, OpenState};
use fandhe_frontend_pre_styled_ui::select::{self, SelectProps};
use fandhe_frontend_pre_styled_ui::tabs::{
    self, ActivationMode, Orientation, TabItem, TabsProps, TabsVariant,
};
use fandhe_frontend_pre_styled_ui::{ColorPalette, Size};

/// プレビューに表示する popover のタイトル `id`(`content` の
/// `labelledby` と対にする)。
const PREVIEW_TITLE_ID: &str = "blocks-example-preview-toolbar-preview-title";
/// プレビューに表示する popover の `content`/`trigger` 間を結ぶ `id`。
const PREVIEW_CONTENT_ID: &str = "blocks-example-preview-toolbar-preview-content";

/// コード種別タブの `id`(`tabs::tabs` の root へ付与)。
const TABS_ID: &str = "blocks-example-preview-toolbar-tabs";

/// スタイル選択の `label`/`content` を結ぶ `id` の組。
const SELECT_LABEL_ID: &str = "blocks-example-preview-toolbar-select-label";
const SELECT_CONTENT_ID: &str = "blocks-example-preview-toolbar-select-content";

/// Rust タブのパネルに表示するコード片。[`preview`] と同じ popover 構成
/// (開いた状態・押下不能トリガー・タイトル + 本文)を、block 固有の
/// `id` 定数・CSS フック属性を除いて組み立てる自己完結のコード片
/// (コピーした片だけで成立する、`code_block_header` と同じ方針)。
const SNIPPET_RUST: &str = "use fandhe_frontend_core::{div, text, Node};\nuse fandhe_frontend_pre_styled_ui::popover::{self, OpenState};\n\nfn preview() -> Node {\n    let trigger = popover::trigger(\n        OpenState::Open,\n        true,\n        Some(\"demo-content\"),\n        vec![],\n        vec![text(\"表示設定\")],\n    );\n    let content = popover::content(\n        OpenState::Open,\n        Some(\"demo-content\"),\n        Some(\"demo-title\"),\n        None,\n        vec![],\n        vec![\n            popover::title(Some(\"demo-title\"), vec![], vec![text(\"表示設定\")]),\n            text(\"ウィジェットの見た目を切り替えます。\"),\n        ],\n    );\n    let positioner = popover::positioner(OpenState::Open, vec![], vec![content]);\n    div(\n        vec![],\n        vec![popover::root(OpenState::Open, vec![], vec![trigger, positioner])],\n    )\n}\n";

/// CSS タブのパネルに表示するコード片。
const SNIPPET_CSS: &str = "[data-scope=\"popover\"][data-part=\"content\"] {\n  min-width: 16rem;\n  padding: var(--fandhe-space-4);\n}\n";

/// 無 JS で固定表示するプレビュー(開いた状態の `popover`。モジュール doc
/// 「プレビューを開いた状態で固定する理由」節参照)。
fn preview() -> Node {
    let trigger = popover::trigger(
        OpenState::Open,
        true,
        Some(PREVIEW_CONTENT_ID),
        vec![("data-blocks-example-preview-toolbar-trigger", "")],
        vec![text("表示設定")],
    );
    let content = popover::content(
        OpenState::Open,
        Some(PREVIEW_CONTENT_ID),
        Some(PREVIEW_TITLE_ID),
        None,
        vec![("data-blocks-example-preview-toolbar-content", "")],
        vec![
            popover::title(Some(PREVIEW_TITLE_ID), vec![], vec![text("表示設定")]),
            text("ウィジェットの見た目を切り替えます。"),
        ],
    );
    let positioner = popover::positioner(
        OpenState::Open,
        vec![("data-blocks-example-preview-toolbar-positioner", "")],
        vec![content],
    );
    div(
        vec![("class", "blocks-example-preview-toolbar-preview")],
        vec![popover::root(
            OpenState::Open,
            vec![],
            vec![trigger, positioner],
        )],
    )
}

/// コード種別タブ(Rust/CSS)。先頭(Rust)を選択状態に固定し、非選択
/// タブ(CSS)は `disabled: true` にする(モジュール doc「コード種別
/// タブ・スタイル選択を固定状態で置く理由」節参照)。
fn toolbar_tabs() -> Node {
    let props = TabsProps {
        id: TABS_ID,
        selected: "rust",
        orientation: Orientation::Horizontal,
        activation_mode: ActivationMode::Automatic,
        loop_focus: true,
        indicator: false,
    };
    let items = vec![
        TabItem {
            value: "rust",
            trigger: vec![text("Rust")],
            content: vec![pre(
                vec![("class", "blocks-example-preview-toolbar-pre")],
                vec![code::code(
                    &CodeProps::default(),
                    vec![("data-blocks-example-preview-toolbar-code", "")],
                    vec![text(SNIPPET_RUST)],
                )],
            )],
            disabled: false,
        },
        TabItem {
            value: "css",
            trigger: vec![text("CSS")],
            content: vec![pre(
                vec![("class", "blocks-example-preview-toolbar-pre")],
                vec![code::code(
                    &CodeProps::default(),
                    vec![("data-blocks-example-preview-toolbar-code", "")],
                    vec![text(SNIPPET_CSS)],
                )],
            )],
            disabled: true,
        },
    ];
    tabs::tabs(
        TabsVariant::Line,
        Size::Sm,
        ColorPalette::default(),
        &props,
        items,
    )
}

/// スタイル選択(閉じた状態の `select`、モジュール doc「コード種別タブ・
/// スタイル選択を固定状態で置く理由」節参照。`card_form_footer::
/// closed_select` と同型だが private ヘルパーのため本ファイルへ再実装
/// する)。
fn style_select() -> Node {
    let props = SelectProps {
        disabled: true,
        ..SelectProps::default()
    };
    div(
        vec![("class", "blocks-example-preview-toolbar-select")],
        vec![
            select::label(
                &props,
                Some(SELECT_LABEL_ID),
                vec![],
                vec![text("スタイル")],
            ),
            select::root(
                Size::Sm,
                OpenState::Closed,
                &props,
                vec![],
                vec![
                    select::control(
                        OpenState::Closed,
                        &props,
                        vec![],
                        vec![select::trigger(
                            OpenState::Closed,
                            &props,
                            false,
                            Some(SELECT_CONTENT_ID),
                            Some(SELECT_LABEL_ID),
                            vec![("data-blocks-example-preview-toolbar-select-trigger", "")],
                            vec![
                                select::value_text(false, &props, vec![], vec![text("既定")]),
                                select::indicator(OpenState::Closed, &props, vec![], vec![]),
                            ],
                        )],
                    ),
                    select::positioner(
                        OpenState::Closed,
                        vec![],
                        vec![select::content(
                            OpenState::Closed,
                            Some(SELECT_CONTENT_ID),
                            Some(SELECT_LABEL_ID),
                            None,
                            vec![],
                            vec![],
                        )],
                    ),
                ],
            ),
        ],
    )
}

/// ツールバー右側の操作群(スタイル選択 + 外部実行 + コピー、モジュール
/// doc「『外部で実行』・コピー操作」節参照)。
fn toolbar_actions() -> Node {
    div(
        vec![("data-blocks-example-preview-toolbar-actions", "")],
        vec![
            style_select(),
            button::button(
                &ButtonProps {
                    variant: ButtonVariant::Ghost,
                    size: Size::Sm,
                    disabled: true,
                    ..ButtonProps::default()
                },
                vec![("data-blocks-example-preview-toolbar-run", "")],
                vec![text("外部で実行")],
            ),
            clipboard::root(
                SNIPPET_RUST,
                false,
                vec![],
                vec![clipboard::control(
                    false,
                    vec![],
                    vec![clipboard::trigger(
                        false,
                        vec![],
                        vec![
                            clipboard::indicator(false, false, vec![], vec![text("コピー")]),
                            clipboard::indicator(true, false, vec![], vec![text("コピー済み")]),
                        ],
                    )],
                )],
            ),
        ],
    )
}

/// `example-preview-toolbar` の Demo 本体。呼び出しごとに同一の `Node` を
/// 返す純関数。プレビュー(上段)→ コード種別タブ + 操作群(中段)→
/// タブのパネル(下段、タブ本体に内包)の順で DOM へ並べ、視覚順は
/// [`LAYOUT_CSS`] の `order` で並べ替える(モジュール doc「DOM 順と視覚順
/// の分離」節参照)。
pub fn demo() -> Node {
    div(
        vec![
            ("id", "blocks-example-preview-toolbar"),
            ("class", "blocks-example-preview-toolbar-frame"),
        ],
        vec![preview(), toolbar_tabs(), toolbar_actions()],
    )
}

原案差分メモ

  • 主参照(対応表 ID R0094)の 3 段構成(プレビュー + ツールバー + コード)をそのまま踏襲しています。集約元が R0094 の 1 件のみのため、状態違いのインスタンス併記は行っていません。
  • コード種別タブ・スタイル選択・プレビューの開閉状態はいずれも無 JS 制約のため静的に固定しています。
  • 文言(タブラベル・プレビュー見出し・コード片)はすべて独自に作成した架空のものです。

関連情報: Tabs / Select / Button / Clipboard / Code / Popover