Skip to content
fandhe-frontendcore v0.4.3
GitHub

contact-form-testimonial

問い合わせフォームと推薦文を横に並べた 2 カラム構成です。heading / text / field / input / textarea / button / blockquote / image / icon の 9 部品を合成します。

lg(64rem)以上でフォーム列と推薦文列の 2 列、それ未満ではフォーム → 推薦文の順に縦へ積む 1 列になります。入力欄グリッドは md(48rem)以上で 2 列になり、本文欄(複数行)と送信ボタンは全幅で表示されます。

文言はすべて架空のもので、送信・データ取得は行わない静的な表示例です。<form> は使わず、送信ボタンは type="button" のままです。

Demo

プロジェクトのご相談

まずはお気軽にお問い合わせください。担当より折り返しご連絡いたします。

送信すると、プライバシーポリシーに同意したものとみなされます。

サポートの対応が丁寧で、困ったときにいつも助けてもらっています。

使用部品

Rust コード

Rust
use crate::blocks::dummy_assets;
use fandhe_frontend_core::{div, el, text, Node};
use fandhe_frontend_pre_styled_ui::blockquote::{self, BlockquoteVariant};
use fandhe_frontend_pre_styled_ui::button::{self, ButtonProps};
use fandhe_frontend_pre_styled_ui::field::{self, FieldRootProps};
use fandhe_frontend_pre_styled_ui::heading::{
    heading, HeadingLevel, HeadingProps, HeadingSize, HeadingWeight,
};
use fandhe_frontend_pre_styled_ui::icon::{icon, IconProps};
use fandhe_frontend_pre_styled_ui::image::{self, ImageFit, ImageProps};
use fandhe_frontend_pre_styled_ui::input::{self, FieldIds, FieldProps, InputProps};
use fandhe_frontend_pre_styled_ui::text::{self as styled_text, TextProps, TextSize, TextVariant};
use fandhe_frontend_pre_styled_ui::textarea::{self, TextareaProps};
use fandhe_frontend_pre_styled_ui::ColorPalette;

/// フォーム欄 1 個ぶんの `FieldProps` を組み立てる(`id` ごとに一意にし、
/// `field::root`/`field::label`/`input::input`/`textarea::textarea` へ
/// 使い回す。表示状態軸は持たない静的な合成例のためすべて既定値)。
fn contact_field(id: &'static str) -> FieldProps<'static> {
    FieldProps {
        id,
        ids: FieldIds::default(),
        disabled: false,
        invalid: false,
        required: false,
        readonly: false,
        has_helper_text: false,
    }
}

/// フォーム欄 1 個を `field::root` へ組み立てる(ラベル + コントロール、
/// `wide` が `true` のとき `data-blocks-contact-form-testimonial-field-wide`
/// を付与してグリッドの 2 列へまたがらせる)。
fn field_wrapper(
    field: &FieldProps<'_>,
    label_text: &'static str,
    control: Node,
    wide: bool,
) -> Node {
    let mut attrs = vec![("data-blocks-contact-form-testimonial-field", "")];
    if wide {
        attrs.push(("data-blocks-contact-form-testimonial-field-wide", ""));
    }
    field::root(
        &FieldRootProps::default(),
        field,
        attrs,
        vec![field::label(field, vec![], vec![text(label_text)]), control],
    )
}

/// 抽象的な六角形のロゴ相当マーク(実在ブランドのロゴ・商標を模さない、
/// モジュール doc「ロゴ相当のマーク」節参照)。
fn mark_icon() -> Node {
    icon(
        &IconProps::default(),
        vec![("data-blocks-contact-form-testimonial-mark", "")],
        vec![el(
            "path",
            vec![("d", "M12 2l8.66 5v10L12 22l-8.66-5V7z")],
            vec![],
        )],
    )
}

/// `contact-form-testimonial` の Demo 本体。呼び出しごとに同一の `Node` を
/// 返す純関数。
pub fn demo() -> Node {
    let first_name_field = contact_field("blocks-contact-form-testimonial-first-name");
    let last_name_field = contact_field("blocks-contact-form-testimonial-last-name");
    let budget_field = contact_field("blocks-contact-form-testimonial-budget");
    let website_field = contact_field("blocks-contact-form-testimonial-website");
    let message_field = contact_field("blocks-contact-form-testimonial-message");

    let fields = div(
        vec![("class", "blocks-contact-form-testimonial-fields")],
        vec![
            field_wrapper(
                &first_name_field,
                "名",
                input::input(
                    &InputProps::default(),
                    &first_name_field,
                    vec![("type", "text")],
                ),
                false,
            ),
            field_wrapper(
                &last_name_field,
                "姓",
                input::input(
                    &InputProps::default(),
                    &last_name_field,
                    vec![("type", "text")],
                ),
                false,
            ),
            field_wrapper(
                &budget_field,
                "ご予算",
                input::input(
                    &InputProps::default(),
                    &budget_field,
                    vec![("type", "text")],
                ),
                false,
            ),
            field_wrapper(
                &website_field,
                "Web サイト",
                input::input(
                    &InputProps::default(),
                    &website_field,
                    vec![("type", "url"), ("placeholder", "https://example.com")],
                ),
                false,
            ),
            field_wrapper(
                &message_field,
                "ご相談内容",
                textarea::textarea(
                    &TextareaProps::default(),
                    &message_field,
                    false,
                    vec![("rows", "4")],
                    vec![],
                ),
                true,
            ),
        ],
    );

    let form_col = div(
        vec![("class", "blocks-contact-form-testimonial-form")],
        vec![
            fields,
            button::button(
                &ButtonProps::default(),
                vec![("data-blocks-contact-form-testimonial-submit", "")],
                vec![text("送信する")],
            ),
            styled_text::text(
                &TextProps {
                    size: TextSize::Sm,
                    variant: TextVariant::Muted,
                    ..TextProps::default()
                },
                vec![],
                vec![text(
                    "送信すると、プライバシーポリシーに同意したものとみなされます。",
                )],
            ),
        ],
    );

    let aside = div(
        vec![("class", "blocks-contact-form-testimonial-aside")],
        vec![
            mark_icon(),
            blockquote::root(
                BlockquoteVariant::default(),
                ColorPalette::default(),
                vec![("data-blocks-contact-form-testimonial-quote", "")],
                vec![
                    blockquote::content(vec![], vec![text(dummy_assets::TESTIMONIAL_QUOTES[2])]),
                    blockquote::caption(
                        vec![("class", "blocks-contact-form-testimonial-meta")],
                        vec![
                            image::image(
                                &ImageProps {
                                    fit: ImageFit::Cover,
                                    ..ImageProps::new(dummy_assets::AVATAR_SRC, "")
                                },
                                vec![("data-blocks-contact-form-testimonial-photo", "")],
                            ),
                            div(
                                vec![("class", "blocks-contact-form-testimonial-byline")],
                                vec![
                                    div(vec![], vec![text(dummy_assets::PERSON_NAMES[3])]),
                                    div(vec![], vec![text(dummy_assets::JOB_TITLES[3])]),
                                ],
                            ),
                        ],
                    ),
                ],
            ),
        ],
    );

    let header = div(
        vec![("class", "blocks-contact-form-testimonial-header")],
        vec![
            heading(
                HeadingLevel::H3,
                &HeadingProps {
                    size: HeadingSize::Xl3,
                    weight: HeadingWeight::Bold,
                },
                vec![],
                vec![text("プロジェクトのご相談")],
            ),
            styled_text::text(
                &TextProps {
                    variant: TextVariant::Muted,
                    ..TextProps::default()
                },
                vec![],
                vec![text(
                    "まずはお気軽にお問い合わせください。担当より折り返しご連絡いたします。",
                )],
            ),
        ],
    );

    div(
        vec![("class", "blocks-contact-form-testimonial-layout")],
        vec![
            header,
            div(
                vec![("class", "blocks-contact-form-testimonial-grid")],
                vec![form_col, aside],
            ),
        ],
    )
}

原案差分メモ

対応表 ID R0858 の 1 件のみを参照元とします。取得手段・ファイル名・内部コンポーネント識別子はライセンス上の理由から記載しません。

参照元との差分:

  • 背景装飾(グリッド模様等の装飾レイヤ)は持ち込みません。
  • 同意文はリンクにせず、プレーンテキストのみで表示します。
  • ロゴは抽象的な icon(六角形の幾何図形)で置き換えます。
  • 写真は共通のダミー素材を使います。
  • 配色・文言は既存のトーンに揃えています。
  • <form> は出力しません。
  • 氏名は「名」「姓」の 2 欄に分けています(参照元の構造)。