From 3f3634fdabe00710124db4b16cf36fd7439b8648 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sat, 26 Sep 2026 07:05:59 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Atributos=20class=20e=20id?= =?UTF-8?q?=20expl=C3=ADcitos=20en=20html!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se usa `class="..."` e `id="..."` en lugar de `div.a #x`. El HTML generado es idéntico. --- .../src/component/admin_frame.rs | 18 ++++++----- .../src/component/config_form.rs | 32 +++++++++---------- extensions/pagetop-htmx/README.md | 4 +-- extensions/pagetop-htmx/src/lib.rs | 4 +-- extensions/pagetop-htmx/src/response.rs | 4 +-- extensions/pagetop-htmx/tests/response.rs | 12 +++---- extensions/pagetop-user/src/component.rs | 2 +- .../src/component/admin/role_table.rs | 4 +-- .../src/component/admin/user_table.rs | 2 +- .../pagetop-user/src/component/login_form.rs | 8 ++--- .../pagetop-user/src/component/user_block.rs | 8 ++--- .../pagetop-user/src/handlers/account.rs | 2 +- extensions/pagetop-user/src/handlers/admin.rs | 2 +- .../src/handlers/admin/permissions.rs | 4 +-- .../pagetop-user/src/handlers/admin/roles.rs | 6 ++-- .../pagetop-user/src/handlers/admin/users.rs | 4 +-- src/base/component/breadcrumb/component.rs | 2 +- src/base/component/layout/template.rs | 2 +- src/base/component/pager.rs | 22 +++++++------ src/base/component/table/component.rs | 4 +-- src/core/theme/definition.rs | 8 ++--- 21 files changed, 80 insertions(+), 74 deletions(-) diff --git a/extensions/pagetop-admin/src/component/admin_frame.rs b/extensions/pagetop-admin/src/component/admin_frame.rs index 0be5c5d8..690d9074 100644 --- a/extensions/pagetop-admin/src/component/admin_frame.rs +++ b/extensions/pagetop-admin/src/component/admin_frame.rs @@ -53,14 +53,14 @@ impl Component for AdminFrame { Ok(html! { (breadcrumbs) - header.admin-header { - h1.admin-page-title { (self.title().using(cx)) } + header class="admin-header" { + h1 class="admin-page-title" { (self.title().using(cx)) } (local_acts) } @if !local_tasks.0.is_empty() { - nav.admin-local-tasks { (local_tasks) } + nav class="admin-local-tasks" { (local_tasks) } } - div.admin-content { + div class="admin-content" { (body) } }) @@ -117,7 +117,7 @@ fn render_local_tasks(cx: &Context, current_path: &str) -> Markup { return html! {}; } html! { - ul.admin-tasks-list { + ul class="admin-tasks-list" { @for task in tasks { @let is_active = current_path == task.path; li class=(if is_active { "admin-task admin-task-active" } else { "admin-task" }) { @@ -137,10 +137,12 @@ fn render_local_actions(cx: &Context, current_path: &str) -> Markup { return html! {}; } html! { - ul.admin-actions-list { + ul class="admin-actions-list" { @for action in actions { - li.admin-action { - a.admin-action-link href=(cx.route(action.url.as_str())) { (action.title.using(cx)) } + li class="admin-action" { + a class="admin-action-link" href=(cx.route(action.url.as_str())) { + (action.title.using(cx)) + } } } } diff --git a/extensions/pagetop-admin/src/component/config_form.rs b/extensions/pagetop-admin/src/component/config_form.rs index d8ea0da0..34f685b4 100644 --- a/extensions/pagetop-admin/src/component/config_form.rs +++ b/extensions/pagetop-admin/src/component/config_form.rs @@ -98,20 +98,20 @@ impl Component for ConfigForm { Ok(html! { @if *self.saved() { - div.admin-message."admin-message-ok" { (&saved_msg) } + div class="admin-message admin-message-ok" { (&saved_msg) } } @if *self.error() { - div.admin-message."admin-message-error" { (&error_msg) } + div class="admin-message admin-message-error" { (&error_msg) } } - form.admin-config-form method="post" action=(action) { + form class="admin-config-form" method="post" action=(action) { @for (i, field) in schema.fields().iter().enumerate() { @let pf = &prepared[i]; - div.admin-form-field { - label.admin-form-label for=(field.name()) { (field.label()) } + div class="admin-form-field" { + label class="admin-form-label" for=(field.name()) { (field.label()) } @match field.field_type() { SettingFieldType::Text { max_length } => { @if let Some(max) = max_length { - input.admin-form-input + input class="admin-form-input" type="text" id=(field.name()) name=(field.name()) @@ -119,7 +119,7 @@ impl Component for ConfigForm { maxlength=(max) required[*field.required()]; } @else { - input.admin-form-input + input class="admin-form-input" type="text" id=(field.name()) name=(field.name()) @@ -129,7 +129,7 @@ impl Component for ConfigForm { } SettingFieldType::Number { min, max } => { @if let (Some(lo), Some(hi)) = (min, max) { - input.admin-form-input + input class="admin-form-input" type="number" id=(field.name()) name=(field.name()) @@ -138,7 +138,7 @@ impl Component for ConfigForm { max=(hi) required[*field.required()]; } @else if let Some(lo) = min { - input.admin-form-input + input class="admin-form-input" type="number" id=(field.name()) name=(field.name()) @@ -146,7 +146,7 @@ impl Component for ConfigForm { min=(lo) required[*field.required()]; } @else if let Some(hi) = max { - input.admin-form-input + input class="admin-form-input" type="number" id=(field.name()) name=(field.name()) @@ -154,7 +154,7 @@ impl Component for ConfigForm { max=(hi) required[*field.required()]; } @else { - input.admin-form-input + input class="admin-form-input" type="number" id=(field.name()) name=(field.name()) @@ -163,7 +163,7 @@ impl Component for ConfigForm { } } SettingFieldType::Boolean => { - input.admin-form-checkbox + input class="admin-form-checkbox" type="checkbox" id=(field.name()) name=(field.name()) @@ -171,7 +171,7 @@ impl Component for ConfigForm { checked[pf.checked]; } SettingFieldType::Select { options } => { - select.admin-form-select + select class="admin-form-select" id=(field.name()) name=(field.name()) required[*field.required()] @@ -187,12 +187,12 @@ impl Component for ConfigForm { } } @if let Some(help) = field.help_text() { - small.admin-form-help { (help) } + small class="admin-form-help" { (help) } } } } - div.admin-form-actions { - button.admin-btn."admin-btn-primary" type="submit" { + div class="admin-form-actions" { + button class="admin-btn admin-btn-primary" type="submit" { (&save_label) } } diff --git a/extensions/pagetop-htmx/README.md b/extensions/pagetop-htmx/README.md index 47382b51..6fbc8144 100644 --- a/extensions/pagetop-htmx/README.md +++ b/extensions/pagetop-htmx/README.md @@ -58,7 +58,7 @@ async fn homepage(request: HttpRequest) -> Result { button hx-get="/api/hello" hx-target="#result" { "Say hello" } - div #result {} + div id="result" {} })) .render().await } @@ -79,7 +79,7 @@ async fn homepage(request: HttpRequest) -> Result { Page::new(request) .with_child(Html::with(move |cx| html! { button (props.unpack(cx)) { "Say hello" } - div #result {} + div id="result" {} })) .render().await } diff --git a/extensions/pagetop-htmx/src/lib.rs b/extensions/pagetop-htmx/src/lib.rs index 9404b49b..e7d6acf9 100644 --- a/extensions/pagetop-htmx/src/lib.rs +++ b/extensions/pagetop-htmx/src/lib.rs @@ -59,7 +59,7 @@ async fn homepage(request: HttpRequest) -> Result { button hx-get="/api/hello" hx-target="#result" { "Say hello" } - div #result {} + div id="result" {} })) .render().await } @@ -80,7 +80,7 @@ async fn homepage(request: HttpRequest) -> Result { Page::new(request) .with_child(Html::with(move |cx| html! { button (props.unpack(cx)) { "Say hello" } - div #result {} + div id="result" {} })) .render().await } diff --git a/extensions/pagetop-htmx/src/response.rs b/extensions/pagetop-htmx/src/response.rs index cf512633..c44505e0 100644 --- a/extensions/pagetop-htmx/src/response.rs +++ b/extensions/pagetop-htmx/src/response.rs @@ -21,7 +21,7 @@ use crate::hx; /// use pagetop_htmx::prelude::*; /// /// async fn add_item() -> impl IntoResponse { -/// let new_item = html! { li #item-42 { "New item" } }; +/// let new_item = html! { li id="item-42" { "New item" } }; /// /// HtmxResponse::new(new_item) /// .retarget("#list") @@ -123,7 +123,7 @@ impl HtmxResponse { /// /// # fn build_response(table: Markup, roles_count: i64) -> HtmxResponse { /// let counter = html! { - /// span #roles-count hx-swap-oob="true" { (roles_count) } + /// span id="roles-count" hx-swap-oob="true" { (roles_count) } /// }; /// HtmxResponse::new(table).oob(counter) /// # } diff --git a/extensions/pagetop-htmx/tests/response.rs b/extensions/pagetop-htmx/tests/response.rs index 1f958139..adfdb9f5 100644 --- a/extensions/pagetop-htmx/tests/response.rs +++ b/extensions/pagetop-htmx/tests/response.rs @@ -15,7 +15,7 @@ fn header<'a>(response: &'a web::Response, name: &str) -> Option<&'a str> { #[pagetop::test] async fn new_renders_the_given_markup_with_an_html_content_type() { - let response = HtmxResponse::new(html! { li #item-42 { "New item" } }).into_response(); + let response = HtmxResponse::new(html! { li id="item-42" { "New item" } }).into_response(); assert_eq!( header(&response, "content-type"), @@ -41,8 +41,8 @@ async fn empty_has_no_body_but_keeps_the_html_content_type() { #[pagetop::test] async fn oob_appends_markup_after_the_main_body() { - let response = HtmxResponse::new(html! { li #item-42 { "New item" } }) - .oob(html! { span #item-count hx-swap-oob="true" { "1" } }) + let response = HtmxResponse::new(html! { li id="item-42" { "New item" } }) + .oob(html! { span id="item-count" hx-swap-oob="true" { "1" } }) .into_response(); let body = web::test::read_body_text(response).await; @@ -58,8 +58,8 @@ async fn oob_appends_markup_after_the_main_body() { #[pagetop::test] async fn oob_can_be_called_several_times_to_accumulate_fragments() { let response = HtmxResponse::new(html! { p { "Main" } }) - .oob(html! { span #a hx-swap-oob="true" { "A" } }) - .oob(html! { span #b hx-swap-oob="true" { "B" } }) + .oob(html! { span id="a" hx-swap-oob="true" { "A" } }) + .oob(html! { span id="b" hx-swap-oob="true" { "B" } }) .into_response(); let body = web::test::read_body_text(response).await; @@ -76,7 +76,7 @@ async fn oob_can_be_called_several_times_to_accumulate_fragments() { #[pagetop::test] async fn oob_works_from_an_empty_response() { let response = HtmxResponse::empty() - .oob(html! { span #item-count hx-swap-oob="true" { "0" } }) + .oob(html! { span id="item-count" hx-swap-oob="true" { "0" } }) .into_response(); let body = web::test::read_body_text(response).await; diff --git a/extensions/pagetop-user/src/component.rs b/extensions/pagetop-user/src/component.rs index e52e00fa..179ac779 100644 --- a/extensions/pagetop-user/src/component.rs +++ b/extensions/pagetop-user/src/component.rs @@ -22,7 +22,7 @@ use pagetop::prelude::*; // formularios de autenticación y por los de administración. pub(crate) fn error_banner(error: Option) -> Html { Html::with(move |cx| match &error { - Some(e) => html! { div.user-form-error role="alert" { (e.clone().using(cx)) } }, + Some(e) => html! { div class="user-form-error" role="alert" { (e.clone().using(cx)) } }, None => html! {}, }) } diff --git a/extensions/pagetop-user/src/component/admin/role_table.rs b/extensions/pagetop-user/src/component/admin/role_table.rs index 231273d8..1516f975 100644 --- a/extensions/pagetop-user/src/component/admin/role_table.rs +++ b/extensions/pagetop-user/src/component/admin/role_table.rs @@ -111,13 +111,13 @@ impl Component for RoleTable { Ok(html! { div (self.props().unpack(cx)) { - div.user-admin-actions { + div class="user-admin-actions" { a href=(new_href) { (Lc::t("btn-create-role", &LOCALES_USER).using(cx)) } } @if let Some(message) = self.message() { - div.user-form-error role="alert" { (message.clone().using(cx)) } + div class="user-form-error" role="alert" { (message.clone().using(cx)) } } (table.render(cx).await) (pager) diff --git a/extensions/pagetop-user/src/component/admin/user_table.rs b/extensions/pagetop-user/src/component/admin/user_table.rs index 603dcf98..97664a13 100644 --- a/extensions/pagetop-user/src/component/admin/user_table.rs +++ b/extensions/pagetop-user/src/component/admin/user_table.rs @@ -87,7 +87,7 @@ impl Component for UserTable { Ok(html! { div (self.props().unpack(cx)) { - div.user-admin-actions { + div class="user-admin-actions" { a href=(new_href) { (Lc::t("btn-create-user", &LOCALES_USER).using(cx)) } diff --git a/extensions/pagetop-user/src/component/login_form.rs b/extensions/pagetop-user/src/component/login_form.rs index ec582d0b..ab5c8381 100644 --- a/extensions/pagetop-user/src/component/login_form.rs +++ b/extensions/pagetop-user/src/component/login_form.rs @@ -69,8 +69,8 @@ impl Component for LoginForm { let form = form.render(cx).await; Ok(html! { - div.user-login-page { - div.user-login-card { + div class="user-login-page" { + div class="user-login-card" { (form) } } @@ -82,13 +82,13 @@ fn links(allow_registration: bool) -> Html { Html::with(move |cx| { html! { @if allow_registration { - p.user-register-link { + p class="user-register-link" { a href=(cx.route(REGISTER_PATH)) { (Lc::t("link-register", &LOCALES_USER).using(cx)) } } } - p.user-reset-link { + p class="user-reset-link" { a href=(cx.route(PASSWORD_RESET_PATH)) { (Lc::t("link-forgot-password", &LOCALES_USER).using(cx)) } diff --git a/extensions/pagetop-user/src/component/user_block.rs b/extensions/pagetop-user/src/component/user_block.rs index d31ccac2..62945e52 100644 --- a/extensions/pagetop-user/src/component/user_block.rs +++ b/extensions/pagetop-user/src/component/user_block.rs @@ -19,10 +19,10 @@ impl Component for UserBlock { Ok(if user.is_authenticated() { let display = user.display_name().unwrap_or("?"); html! { - nav.user-block { - span.user-name { (display) } + nav class="user-block" { + span class="user-name" { (display) } " · " - form.user-logout-inline method="post" action=(cx.route(LOGOUT_PATH)) { + form class="user-logout-inline" method="post" action=(cx.route(LOGOUT_PATH)) { button type="submit" { (Lc::t("btn-logout", &LOCALES_USER).using(cx)) } @@ -31,7 +31,7 @@ impl Component for UserBlock { } } else { html! { - nav.user-block { + nav class="user-block" { a href=(cx.route(LOGIN_PATH)) { (Lc::t("btn-login", &LOCALES_USER).using(cx)) } diff --git a/extensions/pagetop-user/src/handlers/account.rs b/extensions/pagetop-user/src/handlers/account.rs index dac6f536..f89f7fd8 100644 --- a/extensions/pagetop-user/src/handlers/account.rs +++ b/extensions/pagetop-user/src/handlers/account.rs @@ -119,7 +119,7 @@ async fn profile_roles(roles: &[role::Model], cx: &mut Context) -> Block { @if items.is_empty() { "-" } @else { - ul.user-profile-roles { + ul class="user-profile-roles" { @for (label, system_badge) in &items { li { (label.as_str()) diff --git a/extensions/pagetop-user/src/handlers/admin.rs b/extensions/pagetop-user/src/handlers/admin.rs index e4aaba45..0ee29370 100644 --- a/extensions/pagetop-user/src/handlers/admin.rs +++ b/extensions/pagetop-user/src/handlers/admin.rs @@ -51,7 +51,7 @@ pub(crate) fn back_link(href: impl Into) -> Html { let href = href.into(); Html::with(move |cx| { html! { - p.user-admin-back-link { + p class="user-admin-back-link" { a href=(href.clone()) { (Lc::t("link-back-to-list", &LOCALES_USER).using(cx)) } } } diff --git a/extensions/pagetop-user/src/handlers/admin/permissions.rs b/extensions/pagetop-user/src/handlers/admin/permissions.rs index 62d6e1df..6f451114 100644 --- a/extensions/pagetop-user/src/handlers/admin/permissions.rs +++ b/extensions/pagetop-user/src/handlers/admin/permissions.rs @@ -26,12 +26,12 @@ pub(crate) async fn list_get(request: HttpRequest) -> Result Vec { .with_title(group_label.clone()) .with_child(Html::with(move |cx| { html! { - ul.user-admin-permission-list { + ul class="user-admin-permission-list" { @for (_key, label, granted) in &perms { @if *granted { - li.user-admin-permission-granted { (label.using(cx)) } + li class="user-admin-permission-granted" { (label.using(cx)) } } @else { - li.user-admin-permission-missing { (label.using(cx)) } + li class="user-admin-permission-missing" { (label.using(cx)) } } } } diff --git a/extensions/pagetop-user/src/handlers/admin/users.rs b/extensions/pagetop-user/src/handlers/admin/users.rs index c757fc35..54bcb7fb 100644 --- a/extensions/pagetop-user/src/handlers/admin/users.rs +++ b/extensions/pagetop-user/src/handlers/admin/users.rs @@ -92,7 +92,7 @@ fn search_bar(current_query: Option) -> Html { let value = current_query.unwrap_or_default(); Html::with(move |cx| { html! { - div.user-admin-search { + div class="user-admin-search" { input type="search" id="user-admin-search-input" @@ -588,7 +588,7 @@ async fn user_view_roles(roles: &[role::Model], cx: &mut Context) -> Block { @if items.is_empty() { "-" } @else { - table.user-admin-table { + table class="user-admin-table" { tbody { @for (id, machine_name, label, system_badge) in &items { @let href = cx.route(role_path(*id, "view")).to_string(); diff --git a/src/base/component/breadcrumb/component.rs b/src/base/component/breadcrumb/component.rs index 45a65882..cf598a2a 100644 --- a/src/base/component/breadcrumb/component.rs +++ b/src/base/component/breadcrumb/component.rs @@ -57,7 +57,7 @@ impl Component for Breadcrumb { Ok(html! { nav (self.props().unpack(cx)) aria-label=[Lc::l("breadcrumb_label").lookup(cx)] { - ol.breadcrumb { + ol class="breadcrumb" { @for crumb in self.crumbs() { (crumb.render_crumb(cx)) } diff --git a/src/base/component/layout/template.rs b/src/base/component/layout/template.rs index 3f1b85f1..a3c9bfeb 100644 --- a/src/base/component/layout/template.rs +++ b/src/base/component/layout/template.rs @@ -77,7 +77,7 @@ impl Component for Template { } Ok(html! { - div.wrapper { + div class="wrapper" { (body) } }) diff --git a/src/base/component/pager.rs b/src/base/component/pager.rs index f8eacb00..16afdbde 100644 --- a/src/base/component/pager.rs +++ b/src/base/component/pager.rs @@ -240,7 +240,7 @@ impl Component for Pager { @let items_per_page = self.items_per_page().max(1); @let first = (page - 1) * items_per_page + 1; @let last = (page * items_per_page).min(self.total_items()); - span.pager-summary { + span class="pager-summary" { (Lc::l("pager_summary") .with_arg("first", first.to_string()) .with_arg("last", last.to_string()) @@ -248,14 +248,16 @@ impl Component for Pager { .using(cx)) } } - ul.pagination { + ul class="pagination" { @if show_prev_next { li.page-item.page-previous.disabled[first_disabled] { - a.page-link + a class="page-link" href=[(!first_disabled).then(|| Self::page_route(&route, page - 1))] aria-disabled=[first_disabled.then_some("true")] aria-label=[Lc::l("pager_previous_aria_label").lookup(cx)] { - span.page-link-icon { (Lc::l("pager_previous_label").using(cx)) } + span class="page-link-icon" { + (Lc::l("pager_previous_label").using(cx)) + } } } } @@ -264,7 +266,7 @@ impl Component for Pager { PageItem::Number(n) => { @let href = Self::page_route(&route, n); li.page-item.active[n == page] { - a.page-link + a class="page-link" href=(href) aria-current=[(n == page).then_some("page")] { (n.to_string()) @@ -272,19 +274,21 @@ impl Component for Pager { } } PageItem::Ellipsis => { - li.page-item.page-ellipsis.disabled aria-hidden="true" { - span.page-link { "…" } + li class="page-item page-ellipsis disabled" aria-hidden="true" { + span class="page-link" { "…" } } } } } @if show_prev_next { li.page-item.page-next.disabled[last_disabled] { - a.page-link + a class="page-link" href=[(!last_disabled).then(|| Self::page_route(&route, page + 1))] aria-disabled=[last_disabled.then_some("true")] aria-label=[Lc::l("pager_next_aria_label").lookup(cx)] { - span.page-link-icon { (Lc::l("pager_next_label").using(cx)) } + span class="page-link-icon" { + (Lc::l("pager_next_label").using(cx)) + } } } } diff --git a/src/base/component/table/component.rs b/src/base/component/table/component.rs index 72b12ffe..951e63be 100644 --- a/src/base/component/table/component.rs +++ b/src/base/component/table/component.rs @@ -80,7 +80,7 @@ impl Component for Table { let colspan = self.columns().len().max(1).to_string(); Ok(html! { - div.table-responsive { + div class="table-responsive" { table (self.props().unpack(cx)) { @if !self.columns().is_empty() { thead { @@ -108,7 +108,7 @@ impl Component for Table { { tbody { tr { - td.table-empty colspan=(colspan) { (empty) } + td class="table-empty" colspan=(colspan) { (empty) } } } } diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs index 9792877a..63c9101d 100644 --- a/src/core/theme/definition.rs +++ b/src/core/theme/definition.rs @@ -312,8 +312,8 @@ pub trait Theme: Extension + Send + Sync { /// cx: &mut Context, /// ) -> Option> { /// render_component!(component, { - /// Button => |btn| Ok(html! { button.btn.btn-primary { (btn.label()) } }), - /// Heading => |h| Ok(html! { h2.display-4 { (h.text()) } }), + /// Button => |btn| Ok(html! { button class="btn btn-primary" { (btn.label()) } }), + /// Heading => |h| Ok(html! { h2 class="display-4" { (h.text()) } }), /// }) /// } /// ``` @@ -487,13 +487,13 @@ macro_rules! setup_component { /// cx: &mut Context, /// ) -> Option> { /// render_component!(component, { -/// Button => |btn| { Ok(html! { button.btn.btn-primary { (btn.label()) } }) }, +/// Button => |btn| { Ok(html! { button class="btn btn-primary" { (btn.label()) } }) }, /// Heading => |h| self.render_heading(h, cx), /// }) /// } /// /// fn render_heading(&self, h: &Heading, cx: &mut Context) -> Result { -/// Ok(html! { h2.display-4 { (h.text()) } }) +/// Ok(html! { h2 class="display-4" { (h.text()) } }) /// } /// ``` ///