diff --git a/examples/form-controls.rs b/examples/form-controls.rs new file mode 100644 index 00000000..e49844e8 --- /dev/null +++ b/examples/form-controls.rs @@ -0,0 +1,456 @@ +use pagetop::prelude::*; + +use pagetop_bootsier::prelude::*; + +include_locales!(LOC from "examples/locale"); + +struct FormControls; + +impl Extension for FormControls { + fn dependencies(&self) -> Vec { + vec![&pagetop_aliner::Aliner, &pagetop_bootsier::Bootsier] + } + + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { + scfg.route("/", service::web::get().to(form_controls)); + } +} + +async fn form_controls(request: HttpRequest) -> ResultPage { + Page::new(request) + .with_child( + Intro::default() + .with_opening(IntroOpening::Custom) + .with_title(L10n::t("title", &LOC)) + .with_slogan(L10n::t("slogan", &LOC)) + .with_button(None::<(L10n, FnPathByContext)>) + // Bloque 1: casillas, interruptores y botones de opción. + .with_child( + Block::new() + .with_title(L10n::t("block_selections", &LOC)) + .with_child( + Form::new() + .with_id("form-selections") + .with_action("/") + .with_method(form::Method::Post) + // Casillas e interruptores (form::Checkbox). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_checkbox", &LOC)) + .with_description(L10n::t("desc_checkbox", &LOC)) + .with_child( + form::Checkbox::new() + .with_name("accept_terms") + .with_label(L10n::t("label_terms", &LOC)) + .with_required(true), + ) + .with_child( + form::Checkbox::new() + .with_name("accept_marketing") + .with_label(L10n::t("label_marketing", &LOC)) + .with_checked(true) + .with_inline(true), + ) + .with_child( + form::Checkbox::new() + .with_name("newsletter") + .with_label(L10n::t("label_newsletter", &LOC)) + .with_inline(true), + ) + .with_child( + form::Checkbox::switch() + .with_name("notifications") + .with_label(L10n::t("label_notifications", &LOC)) + .with_checked(true) + .with_reverse(true), + ) + .with_child( + form::Checkbox::switch() + .with_name("dark_mode") + .with_label(L10n::t("label_dark_mode", &LOC)) + .with_disabled(true), + ), + ) + // Grupo de casillas de verificación (form::check::Field). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_checkgroup", &LOC)) + .with_child( + form::check::Field::new() + .with_name("interests") + .with_label(L10n::t("label_interests", &LOC)) + .with_help_text(L10n::t("help_interests", &LOC)) + .with_item( + form::check::Item::new( + "rust", + L10n::t("check_rust", &LOC), + ) + .with_checked(true), + ) + .with_item(form::check::Item::new( + "web", + L10n::t("check_web", &LOC), + )) + .with_item(form::check::Item::new( + "ai", + L10n::t("check_ai", &LOC), + )) + .with_item( + form::check::Item::new( + "games", + L10n::t("check_games", &LOC), + ) + .with_disabled(true), + ), + ), + ) + // Botones de opción (form::radio::Field). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_radio", &LOC)) + .with_child( + form::radio::Field::new() + .with_name("frequency") + .with_label(L10n::t("label_frequency", &LOC)) + .with_item(form::radio::Item::new( + "daily", + L10n::t("radio_daily", &LOC), + )) + .with_item( + form::radio::Item::new( + "weekly", + L10n::t("radio_weekly", &LOC), + ) + .with_checked(true), + ) + .with_item(form::radio::Item::new( + "monthly", + L10n::t("radio_monthly", &LOC), + )) + .with_item( + form::radio::Item::new( + "never", + L10n::t("radio_never", &LOC), + ) + .with_disabled(true), + ), + ), + ) + // Campo oculto (form::Hidden). + .with_child( + form::Hidden::new() + .with_name("origin") + .with_value("form-selections"), + ) + // Botones de acción. + .with_child( + Button::submit(L10n::t("btn_submit", &LOC)) + .with_color(ButtonColor::Background(Color::Primary)), + ) + .with_child( + Button::reset(L10n::t("btn_reset", &LOC)) + .with_color(ButtonColor::Outline(Color::Secondary)), + ) + .with_child( + Button::plain(L10n::t("btn_cancel", &LOC)) + .with_color(ButtonColor::Link), + ), + ), + ) + // Bloque 2: campos de texto, multilínea y rango. + .with_child( + Block::new() + .with_title(L10n::t("block_text", &LOC)) + .with_child( + Form::new() + .with_id("form-text") + .with_action("/") + .with_method(form::Method::Post) + // Campos de texto (form::input::Field). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_text", &LOC)) + .with_child( + form::input::Field::text() + .with_name("name") + .with_label(L10n::t("label_name", &LOC)) + .with_placeholder(L10n::t("placeholder_name", &LOC)) + .with_required(true), + ) + .with_child( + form::input::Field::email() + .with_name("email") + .with_label(L10n::t("label_email", &LOC)) + .with_placeholder(L10n::t( + "placeholder_email", + &LOC, + )) + .with_autocomplete( + Some(form::Autocomplete::email()), + ) + .with_required(true), + ) + .with_child( + form::input::Field::password() + .with_name("password") + .with_label(L10n::t("label_password", &LOC)) + .with_autocomplete(Some( + form::Autocomplete::new_password(), + )) + .with_required(true), + ) + .with_child( + form::input::Field::telephone() + .with_name("phone") + .with_label(L10n::t("label_phone", &LOC)) + .with_placeholder(L10n::t( + "placeholder_phone", + &LOC, + )), + ) + .with_child( + form::input::Field::url() + .with_name("website") + .with_label(L10n::t("label_url", &LOC)) + .with_placeholder(L10n::t("placeholder_url", &LOC)), + ) + .with_child( + form::input::Field::search() + .with_name("search") + .with_label(L10n::t("label_search", &LOC)) + .with_placeholder(L10n::t( + "placeholder_search", + &LOC, + )), + ), + ) + // Área de texto (form::Textarea). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_textarea", &LOC)) + .with_child( + form::Textarea::new() + .with_name("comment") + .with_label(L10n::t("label_comment", &LOC)) + .with_placeholder(L10n::t( + "placeholder_comment", + &LOC, + )) + .with_rows(Some(4)) + .with_help_text(L10n::t("help_comment", &LOC)), + ), + ) + // Control deslizante (form::Range). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_range", &LOC)) + .with_child( + form::Range::new() + .with_name("rating") + .with_label(L10n::t("label_rating", &LOC)) + .with_min(Some(1.0)) + .with_max(Some(10.0)) + .with_step(Some(1.0)) + .with_value(Some(5.0)) + .with_help_text(L10n::t("help_rating", &LOC)), + ), + ) + // Campo oculto (form::Hidden). + .with_child( + form::Hidden::new() + .with_name("origin") + .with_value("form-text"), + ) + // Botones de acción. + .with_child( + Button::submit(L10n::t("btn_submit", &LOC)) + .with_color(ButtonColor::Background(Color::Primary)), + ) + .with_child( + Button::reset(L10n::t("btn_reset", &LOC)) + .with_color(ButtonColor::Outline(Color::Secondary)), + ) + .with_child( + Button::plain(L10n::t("btn_cancel", &LOC)) + .with_color(ButtonColor::Link), + ), + ), + ) + // Bloque 3: listas de selección y etiquetas flotantes. + .with_child( + Block::new() + .with_title(L10n::t("block_lists", &LOC)) + .with_child( + Form::new() + .with_id("form-lists") + .with_action("/") + .with_method(form::Method::Post) + // Listas de selección (form::select::Field). + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_select", &LOC)) + .with_child( + form::select::Field::new() + .with_name("language") + .with_label(L10n::t("label_language", &LOC)) + .with_item( + form::select::Item::new( + "", + L10n::t("select_choose", &LOC), + ) + .with_selected(true), + ) + .with_group( + form::select::Group::new(L10n::t( + "select_group_europe", + &LOC, + )) + .with_item(form::select::Item::new( + "es", + L10n::t("select_spanish", &LOC), + )) + .with_item(form::select::Item::new( + "fr", + L10n::t("select_french", &LOC), + )), + ) + .with_group( + form::select::Group::new(L10n::t( + "select_group_americas", + &LOC, + )) + .with_item(form::select::Item::new( + "en", + L10n::t("select_english", &LOC), + )) + .with_item(form::select::Item::new( + "pt", + L10n::t("select_portuguese", &LOC), + )), + ) + .with_item( + form::select::Item::new( + "xx", + L10n::t("select_disabled", &LOC), + ) + .with_disabled(true), + ) + .with_required(true), + ) + .with_child( + form::select::Field::new() + .with_name("technologies") + .with_label(L10n::t("label_technologies", &LOC)) + .with_item( + form::select::Item::new( + "rust", + L10n::n("Rust"), + ) + .with_selected(true), + ) + .with_item( + form::select::Item::new( + "python", + L10n::n("Python"), + ) + .with_selected(true), + ) + .with_item(form::select::Item::new( + "javascript", + L10n::n("JavaScript"), + )) + .with_item(form::select::Item::new( + "go", + L10n::n("Go"), + )) + .with_item(form::select::Item::new( + "typescript", + L10n::n("TypeScript"), + )) + .with_multiple(true) + .with_rows(Some(4)) + .with_help_text(L10n::t("help_technologies", &LOC)), + ), + ) + // Etiquetas flotantes. + .with_child( + form::Fieldset::new() + .with_legend(L10n::t("fieldset_floating", &LOC)) + .with_child( + form::input::Field::text() + .with_name("fl_name") + .with_label(L10n::t("label_name", &LOC)) + .with_placeholder(L10n::t("placeholder_name", &LOC)) + .with_floating_label(true) + .with_required(true), + ) + .with_child( + form::Textarea::new() + .with_name("fl_comment") + .with_label(L10n::t("label_comment", &LOC)) + .with_placeholder(L10n::t( + "placeholder_comment", + &LOC, + )) + .with_floating_label(true), + ) + .with_child( + form::select::Field::new() + .with_name("fl_country") + .with_label(L10n::t("label_country", &LOC)) + .with_item( + form::select::Item::new( + "", + L10n::t("select_choose", &LOC), + ) + .with_selected(true), + ) + .with_item(form::select::Item::new( + "de", + L10n::t("select_germany", &LOC), + )) + .with_item(form::select::Item::new( + "es", + L10n::t("select_spain", &LOC), + )) + .with_item(form::select::Item::new( + "fr", + L10n::t("select_france", &LOC), + )) + .with_item(form::select::Item::new( + "pt", + L10n::t("select_portugal", &LOC), + )) + .with_floating_label(true) + .with_required(true), + ), + ) + // Campo oculto (form::Hidden). + .with_child( + form::Hidden::new() + .with_name("origin") + .with_value("form-lists"), + ) + // Botones de acción. + .with_child( + Button::submit(L10n::t("btn_submit", &LOC)) + .with_color(ButtonColor::Background(Color::Primary)), + ) + .with_child( + Button::reset(L10n::t("btn_reset", &LOC)) + .with_color(ButtonColor::Outline(Color::Secondary)), + ) + .with_child( + Button::plain(L10n::t("btn_cancel", &LOC)) + .with_color(ButtonColor::Link), + ), + ), + ), + ) + .render() +} + +#[pagetop::main] +async fn main() -> std::io::Result<()> { + Application::prepare(&FormControls).run()?.await +} diff --git a/examples/locale/en-US/form-controls.ftl b/examples/locale/en-US/form-controls.ftl new file mode 100644 index 00000000..7c2b3c96 --- /dev/null +++ b/examples/locale/en-US/form-controls.ftl @@ -0,0 +1,74 @@ +title = Form controls +slogan = Bootsier form components showcase +block_selections = Checkboxes, switches and radio buttons +block_text = Text fields, multiline and range +block_lists = Select lists and floating labels + +fieldset_text = Text fields +label_name = Full name +placeholder_name = e.g.: Jane Smith +label_email = Email address +placeholder_email = user@example.com +label_password = Password +label_phone = Phone number +placeholder_phone = +1 555 000 0000 +label_url = Website +placeholder_url = https://example.com +label_search = Search +placeholder_search = Search term... + +fieldset_textarea = Multiline text +label_comment = Comment +placeholder_comment = Write your comment here... +help_comment = Maximum 500 characters. + +fieldset_select = Selection lists +label_language = Language +label_country = Country +label_technologies = Preferred technologies +help_technologies = Hold Ctrl (or Cmd on Mac) to select multiple options. +select_choose = — Choose an option — +select_group_europe = Europe +select_spanish = Spanish +select_french = French +select_group_americas = Americas +select_english = English +select_portuguese = Portuguese +select_disabled = Not available +select_germany = Germany +select_spain = Spain +select_france = France +select_portugal = Portugal + +fieldset_checkbox = Checkboxes and switches +desc_checkbox = This group shows standard checkboxes, inline checkboxes, reverse-aligned options, and toggle switches for binary choices. +label_terms = I accept the terms and conditions +label_marketing = Commercial emails (inline) +label_newsletter = Newsletter (inline) +label_notifications = Enable notifications (reverse) +label_dark_mode = Dark mode (unavailable) + +fieldset_radio = Radio buttons +label_frequency = Newsletter frequency +radio_daily = Daily +radio_weekly = Weekly +radio_monthly = Monthly +radio_never = Never (disabled) + +fieldset_checkgroup = Checkbox group +label_interests = Areas of interest +help_interests = Select all options that apply. +check_rust = Rust programming +check_web = Web development +check_ai = Artificial intelligence +check_games = Game development (disabled) + +fieldset_floating = Floating labels + +fieldset_range = Slider +label_rating = Overall rating +help_rating = From 1 (very poor) to 10 (excellent). + +btn_submit = Submit +btn_reset = Reset +btn_cancel = Cancel diff --git a/examples/locale/es-ES/form-controls.ftl b/examples/locale/es-ES/form-controls.ftl new file mode 100644 index 00000000..67a0fd2c --- /dev/null +++ b/examples/locale/es-ES/form-controls.ftl @@ -0,0 +1,74 @@ +title = Controles de formulario +slogan = Componentes Bootsier para formularios +block_selections = Casillas, interruptores y botones de opción +block_text = Campos de texto, multilínea y rango +block_lists = Listas de selección y etiquetas flotantes + +fieldset_text = Campos de texto +label_name = Nombre completo +placeholder_name = Ej.: Ana García +label_email = Correo electrónico +placeholder_email = usuario@ejemplo.com +label_password = Contraseña +label_phone = Teléfono +placeholder_phone = +34 600 000 +label_url = Sitio web +placeholder_url = https://ejemplo.com +label_search = Búsqueda +placeholder_search = Término de búsqueda... + +fieldset_textarea = Texto multilínea +label_comment = Comentario +placeholder_comment = Escribe tu comentario aquí... +help_comment = Máximo 500 caracteres. + +fieldset_select = Listas de selección +label_language = Idioma +label_country = País +label_technologies = Tecnologías preferidas +help_technologies = Mantén Ctrl (o Cmd en Mac) para seleccionar varias opciones. +select_choose = — Elige una opción — +select_group_europe = Europa +select_spanish = Español +select_french = Francés +select_group_americas = América +select_english = Inglés +select_portuguese = Portugués +select_disabled = No disponible +select_germany = Alemania +select_spain = España +select_france = Francia +select_portugal = Portugal + +fieldset_checkbox = Casillas e interruptores +desc_checkbox = Este grupo muestra casillas de verificación estándar, casillas en línea, opciones alineadas a la derecha e interruptores para elecciones binarias. +label_terms = Acepto los términos y condiciones +label_marketing = Emails comerciales (en línea) +label_newsletter = Boletín (en línea) +label_notifications = Activar notificaciones (invertida) +label_dark_mode = Modo oscuro (no disponible) + +fieldset_radio = Botones de opción +label_frequency = Frecuencia del boletín +radio_daily = Diario +radio_weekly = Semanal +radio_monthly = Mensual +radio_never = Nunca (deshabilitado) + +fieldset_checkgroup = Grupo de casillas +label_interests = Áreas de interés +help_interests = Selecciona todas las opciones que correspondan. +check_rust = Programación en Rust +check_web = Desarrollo web +check_ai = Inteligencia artificial +check_games = Desarrollo de videojuegos (deshabilitado) + +fieldset_floating = Etiquetas flotantes + +fieldset_range = Control deslizante +label_rating = Valoración general +help_rating = De 1 (muy malo) a 10 (excelente). + +btn_submit = Enviar +btn_reset = Restablecer +btn_cancel = Cancelar diff --git a/extensions/pagetop-bootsier/src/theme/form/component.rs b/extensions/pagetop-bootsier/src/theme/form/component.rs index dd655702..e4c22a56 100644 --- a/extensions/pagetop-bootsier/src/theme/form/component.rs +++ b/extensions/pagetop-bootsier/src/theme/form/component.rs @@ -23,7 +23,7 @@ use crate::theme::form; /// .with_id("search") /// .with_action("/search") /// .with_method(form::Method::Get) -/// .with_child(form::Input::search().with_name("q")); +/// .with_child(form::input::Field::search().with_name("q")); /// ``` #[derive(AutoDefault, Clone, Debug, Getters)] pub struct Form {