From d0beb8ef400dd525a672f721b2d30262e01105df Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 12 Oct 2025 08:43:17 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=A8=20A=C3=B1ade=20soporte=20para?= =?UTF-8?q?=20el=20tema=20`pagetop-aliner`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ tools/changelog.sh | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/README.md b/README.md index c6c12e0..0e63518 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,11 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui * **[pagetop-macros](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-macros)**, proporciona una colección de macros que mejoran la experiencia de desarrollo con PageTop. +## Extensiones + + * **[pagetop-aliner](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner)**, + es un tema para demos y pruebas que muestra esquemáticamente la composición de las páginas HTML. + # 🧪 Pruebas diff --git a/tools/changelog.sh b/tools/changelog.sh index 722cdb7..253f22b 100755 --- a/tools/changelog.sh +++ b/tools/changelog.sh @@ -55,6 +55,10 @@ case "$CRATE" in --exclude-path "helpers/pagetop-macros/**/*" ) ;; + pagetop-aliner) + CHANGELOG_FILE="extensions/pagetop-aliner/CHANGELOG.md" + PATH_FLAGS=(--include-path "extensions/pagetop-aliner/**/*") + ;; *) echo "Error: unsupported crate '$CRATE'" >&2 exit 1 From ebf1828ea3cc4c51daf3fd860e735690722e1a38 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 12 Oct 2025 09:15:50 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactoriza=20p=C3=A1g?= =?UTF-8?q?ina=20de=20bienvenida=20y=20tema=20`Basic`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Actualiza `Welcome` para usar el nuevo componente `Intro`. - Simplifica el tema `Basic` apoyándose en la lógica de `Theme`. - Predefine los *assets* básicos como recursos de `Theme`. - Refactoriza archivos de localicación para reflejar los cambios de los componentes. --- extensions/pagetop-aliner/src/lib.rs | 2 +- src/base/component.rs | 3 + src/base/component/intro.rs | 343 +++++++++++++++++++++++++++ src/base/extension/welcome.rs | 43 ++-- src/base/theme/basic.rs | 205 +--------------- src/core/theme/definition.rs | 26 +- src/locale/en-US/base.ftl | 6 +- src/locale/en-US/welcome.ftl | 8 +- src/locale/es-ES/base.ftl | 6 +- src/locale/es-ES/welcome.ftl | 8 +- static/css/intro.css | 82 ++++--- static/css/root.css | 19 -- 12 files changed, 455 insertions(+), 296 deletions(-) create mode 100644 src/base/component/intro.rs diff --git a/extensions/pagetop-aliner/src/lib.rs b/extensions/pagetop-aliner/src/lib.rs index 8e9b03a..084678d 100644 --- a/extensions/pagetop-aliner/src/lib.rs +++ b/extensions/pagetop-aliner/src/lib.rs @@ -108,7 +108,7 @@ impl Extension for Aliner { impl Theme for Aliner { fn before_render_page_body(&self, page: &mut Page) { - page.alter_param("include_basic_css", true) + page.alter_param("include_basic_assets", true) .alter_assets(ContextOp::AddStyleSheet( StyleSheet::from("/aliner/css/styles.css") .with_version(env!("CARGO_PKG_VERSION")) diff --git a/src/base/component.rs b/src/base/component.rs index 4edfc9a..6c3b028 100644 --- a/src/base/component.rs +++ b/src/base/component.rs @@ -54,6 +54,9 @@ pub use html::Html; mod block; pub use block::Block; +mod intro; +pub use intro::{Intro, IntroOpening}; + mod poweredby; pub use poweredby::PoweredBy; diff --git a/src/base/component/intro.rs b/src/base/component/intro.rs new file mode 100644 index 0000000..5d3440e --- /dev/null +++ b/src/base/component/intro.rs @@ -0,0 +1,343 @@ +use crate::prelude::*; + +/// Tipo de apertura que se mostrará en la introducción del componente [`Intro`]. +/// +/// Permite elegir entre una apertura con textos predefinidos sobre PageTop (como hace la página de +/// bienvenida [`Welcome`](crate::base::extension::Welcome)) o una introducción completamente +/// personalizada. +#[derive(AutoDefault, Copy, Clone, Debug, Eq, PartialEq)] +pub enum IntroOpening { + /// Modo por defecto. Muestra una introducción estándar de PageTop e incluye automáticamente + /// *badges* con información de la última versión liberada, fecha del último lanzamiento y + /// licencia de uso. + #[default] + PageTop, + /// Modo totalmente personalizado. No añade *badges* ni textos predefinidos. Usa la imagen de + /// PageTop pero el contenido lo define el propio desarrollador. + Custom, +} + +/// Componente para presentar PageTop (como [`Welcome`](crate::base::extension::Welcome)), o mostrar +/// introducciones. +/// +/// Usa la imagen de PageTop para presentar contenidos con: +/// +/// - Una **imagen decorativa** (el *monster* de PageTop) antecediendo al contenido. +/// - Una vista destacada con **título + eslogan**. +/// - Un **botón opcional** de llamada a la acción con texto y enlace configurables. +/// - El **área de textos** con *badges* predefinidos (en modo [`IntroOpening::PageTop`]) y bloques +/// ([`Block`](crate::base::component::Block)) para crear párrafos vistosos de texto. Aunque +/// admite todo tipo de componentes. +/// +/// ### Ejemplos +/// +/// **Intro mínima por defecto** +/// +/// ```rust +/// # use pagetop::prelude::*; +/// let intro = Intro::default(); +/// ``` +/// +/// **Título, eslogan y botón personalizados** +/// +/// ```rust +/// # use pagetop::prelude::*; +/// let intro = Intro::default() +/// .with_title(L10n::l("intro_custom_title")) +/// .with_slogan(L10n::l("intro_custom_slogan")) +/// .with_button(Some(( +/// L10n::l("intro_learn_more"), +/// |_| "/learn-more" +/// ))); +/// ``` +/// +/// **Sin botón + modo *Custom* (sin *badges* predefinidos)** +/// +/// ```rust +/// # use pagetop::prelude::*; +/// let intro = Intro::default() +/// .with_button(None::<(L10n, FnPathByContext)>) +/// .with_opening(IntroOpening::Custom); +/// ``` +/// +/// **Añadir contenidos hijo** +/// +/// ```rust +/// # use pagetop::prelude::*; +/// let intro = Intro::default() +/// .add_component( +/// Block::new() +/// .with_title(L10n::l("intro_custom_block_title")) +/// .add_component(Html::with(move |cx| { +/// html! { +/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) } +/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) } +/// } +/// })), +/// ); +/// ``` +#[rustfmt::skip] +pub struct Intro { + title : L10n, + slogan : L10n, + button : Option<(L10n, FnPathByContext)>, + opening : IntroOpening, + children: Children, +} + +impl Default for Intro { + #[rustfmt::skip] + fn default() -> Self { + Intro { + title : L10n::l("intro_default_title"), + slogan : L10n::l("intro_default_slogan").with_arg("app", &global::SETTINGS.app.name), + button : Some((L10n::l("intro_default_button"), |_| "https://pagetop.cillero.es")), + opening : IntroOpening::default(), + children: Children::default(), + } + } +} + +impl Component for Intro { + fn new() -> Self { + Intro::default() + } + + fn setup_before_prepare(&mut self, cx: &mut Context) { + cx.alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/intro.css").with_version(env!("CARGO_PKG_VERSION")), + )); + } + + fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { + if self.opening() == IntroOpening::PageTop { + cx.alter_assets(ContextOp::AddJavaScript(JavaScript::on_load_async("intro-js", |cx| + util::indoc!(r#" + try { + const resp = await fetch("https://crates.io/api/v1/crates/pagetop"); + const data = await resp.json(); + const date = new Date(data.versions[0].created_at); + const formatted = date.toLocaleDateString("LANGID", { year: "numeric", month: "2-digit", day: "2-digit" }); + document.getElementById("intro-release").src = `https://img.shields.io/badge/Release%20date-${encodeURIComponent(formatted)}-blue?label=LABEL&style=for-the-badge`; + document.getElementById("intro-badges").style.display = "block"; + } catch (e) { + console.error("Failed to fetch release date from crates.io:", e); + } + "#) + .replace("LANGID", cx.langid().to_string().as_str()) + .replace("LABEL", L10n::l("intro_release_label").using(cx).as_str()) + ))); + } + + PrepareMarkup::With(html! { + div class="intro" { + div class="intro-header" { + section class="intro-header__body" { + h1 class="intro-header__title" { + span { (self.title().using(cx)) } + (self.slogan().using(cx)) + } + } + aside class="intro-header__image" aria-hidden="true" { + div class="intro-header__monster" { + picture { + source + type="image/avif" + src="/img/monster-pagetop_250.avif" + srcset="/img/monster-pagetop_500.avif 1.5x"; + source + type="image/webp" + src="/img/monster-pagetop_250.webp" + srcset="/img/monster-pagetop_500.webp 1.5x"; + img + src="/img/monster-pagetop_250.png" + srcset="/img/monster-pagetop_500.png 1.5x" + alt="Monster PageTop"; + } + } + } + } + div class="intro-content" { + section class="intro-content__body" { + div class="intro-text" { + @if let Some((txt, lnk)) = self.button() { + div class="intro-button" { + a + class="intro-button__link" + href=((lnk)(cx)) + target="_blank" + rel="noreferrer" + { + span {} span {} span {} + div class="intro-button__text" { + (txt.using(cx)) + } + } + } + } + div class="intro-text__children" { + @if self.opening() == IntroOpening::PageTop { + p { (L10n::l("intro_text1").using(cx)) } + div id="intro-badges" { + img + src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge" + alt=[L10n::l("intro_pagetop_label").lookup(cx)] {} (" ") + img + id="intro-release" + alt=[L10n::l("intro_release_label").lookup(cx)] {} (" ") + img + src=(format!( + "https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label={}&style=for-the-badge", + L10n::l("intro_license_label").lookup(cx).unwrap_or_default() + )) + alt=[L10n::l("intro_license_label").lookup(cx)] {} + } + p { (L10n::l("intro_text2").using(cx)) } + } + (self.children().render(cx)) + } + } + } + } + div class="intro-footer" { + section class="intro-footer__body" { + div class="intro-footer__logo" { + svg + viewBox="0 0 1614 1614" + xmlns="http://www.w3.org/2000/svg" + role="img" + aria-label=[L10n::l("pagetop_logo").lookup(cx)] + preserveAspectRatio="xMidYMid slice" + focusable="false" + { + path fill="rgb(255,255,255)" d="M 1573,357 L 1415,357 C 1400,357 1388,369 1388,383 L 1388,410 1335,410 1335,357 C 1335,167 1181,13 992,13 L 621,13 C 432,13 278,167 278,357 L 278,410 225,410 225,383 C 225,369 213,357 198,357 L 40,357 C 25,357 13,369 13,383 L 13,648 C 13,662 25,674 40,674 L 198,674 C 213,674 225,662 225,648 L 225,621 278,621 278,1256 C 278,1446 432,1600 621,1600 L 992,1600 C 1181,1600 1335,1446 1335,1256 L 1335,621 1388,621 1388,648 C 1388,662 1400,674 1415,674 L 1573,674 C 1588,674 1600,662 1600,648 L 1600,383 C 1600,369 1588,357 1573,357 L 1573,357 1573,357 Z M 66,410 L 172,410 172,621 66,621 66,410 66,410 Z M 1282,357 L 1282,488 C 1247,485 1213,477 1181,464 L 1196,437 C 1203,425 1199,409 1186,401 1174,394 1158,398 1150,411 L 1133,440 C 1105,423 1079,401 1056,376 L 1075,361 C 1087,352 1089,335 1079,324 1070,313 1054,311 1042,320 L 1023,335 C 1000,301 981,263 967,221 L 1011,196 C 1023,189 1028,172 1021,160 1013,147 997,143 984,150 L 953,168 C 945,136 941,102 940,66 L 992,66 C 1152,66 1282,197 1282,357 L 1282,357 1282,357 Z M 621,66 L 674,66 674,225 648,225 C 633,225 621,237 621,251 621,266 633,278 648,278 L 674,278 674,357 648,357 C 633,357 621,369 621,383 621,398 633,410 648,410 L 674,410 674,489 648,489 C 633,489 621,501 621,516 621,530 633,542 648,542 L 664,542 C 651,582 626,623 600,662 583,653 563,648 542,648 469,648 410,707 410,780 410,787 411,794 412,801 388,805 361,806 331,806 L 331,357 C 331,197 461,66 621,66 L 621,66 621,66 Z M 621,780 C 621,824 586,859 542,859 498,859 463,824 463,780 463,736 498,701 542,701 586,701 621,736 621,780 L 621,780 621,780 Z M 225,463 L 278,463 278,569 225,569 225,463 225,463 Z M 992,1547 L 621,1547 C 461,1547 331,1416 331,1256 L 331,859 C 367,859 400,858 431,851 454,888 495,912 542,912 615,912 674,853 674,780 674,747 662,718 642,695 675,645 706,594 720,542 L 780,542 C 795,542 807,530 807,516 807,501 795,489 780,489 L 727,489 727,410 780,410 C 795,410 807,398 807,383 807,369 795,357 780,357 L 727,357 727,278 780,278 C 795,278 807,266 807,251 807,237 795,225 780,225 L 727,225 727,66 887,66 C 889,111 895,155 905,196 L 869,217 C 856,224 852,240 859,253 864,261 873,266 882,266 887,266 891,265 895,263 L 921,248 C 937,291 958,331 983,367 L 938,403 C 926,412 925,429 934,440 939,447 947,450 954,450 960,450 966,448 971,444 L 1016,408 C 1043,438 1074,465 1108,485 L 1084,527 C 1076,539 1081,555 1093,563 1098,565 1102,566 1107,566 1116,566 1125,561 1129,553 L 1155,509 C 1194,527 1237,538 1282,541 L 1282,1256 C 1282,1416 1152,1547 992,1547 L 992,1547 992,1547 Z M 1335,463 L 1388,463 1388,569 1335,569 1335,463 1335,463 Z M 1441,410 L 1547,410 1547,621 1441,621 1441,410 1441,410 Z" {} + path fill="rgb(255,255,255)" d="M 1150,1018 L 463,1018 C 448,1018 436,1030 436,1044 L 436,1177 C 436,1348 545,1468 701,1468 L 912,1468 C 1068,1468 1177,1348 1177,1177 L 1177,1044 C 1177,1030 1165,1018 1150,1018 L 1150,1018 1150,1018 Z M 912,1071 L 1018,1071 1018,1124 912,1124 912,1071 912,1071 Z M 489,1071 L 542,1071 542,1124 489,1124 489,1071 489,1071 Z M 701,1415 L 700,1415 C 701,1385 704,1352 718,1343 731,1335 759,1341 795,1359 802,1363 811,1363 818,1359 854,1341 882,1335 895,1343 909,1352 912,1385 913,1415 L 912,1415 701,1415 701,1415 701,1415 Z M 1124,1177 C 1124,1296 1061,1384 966,1408 964,1365 958,1320 922,1298 894,1281 856,1283 807,1306 757,1283 719,1281 691,1298 655,1320 649,1365 647,1408 552,1384 489,1296 489,1177 L 569,1177 C 583,1177 595,1165 595,1150 L 595,1071 859,1071 859,1150 C 859,1165 871,1177 886,1177 L 1044,1177 C 1059,1177 1071,1165 1071,1150 L 1071,1071 1124,1071 1124,1177 1124,1177 1124,1177 Z" {} + path fill="rgb(255,255,255)" d="M 1071,648 C 998,648 939,707 939,780 939,853 998,912 1071,912 1144,912 1203,853 1203,780 1203,707 1144,648 1071,648 L 1071,648 1071,648 Z M 1071,859 C 1027,859 992,824 992,780 992,736 1027,701 1071,701 1115,701 1150,736 1150,780 1150,824 1115,859 1071,859 L 1071,859 1071,859 Z" {} + } + } + div class="intro-footer__links" { + a href="https://crates.io/crates/pagetop" target="_blank" rel="noreferrer" { ("Crates.io") } + a href="https://docs.rs/pagetop" target="_blank" rel="noreferrer" { ("Docs.rs") } + a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noreferrer" { (L10n::l("intro_code").using(cx)) } + em { (L10n::l("intro_have_fun").using(cx)) } + } + } + } + } + }) + } +} + +impl Intro { + // **< Intro BUILDER >************************************************************************** + + /// Establece el título de entrada. + /// + /// # Ejemplo + /// + /// ```rust + /// # use pagetop::prelude::*; + /// let intro = Intro::default().with_title(L10n::n("Título de entrada")); + /// ``` + #[builder_fn] + pub fn with_title(mut self, title: L10n) -> Self { + self.title = title; + self + } + + /// Establece el eslogan de entrada (línea secundaria del título). + /// + /// # Ejemplo + /// + /// ```rust + /// # use pagetop::prelude::*; + /// let intro = Intro::default().with_slogan(L10n::n("Un eslogan para la entrada")); + /// ``` + #[builder_fn] + pub fn with_slogan(mut self, slogan: L10n) -> Self { + self.slogan = slogan; + self + } + + /// Configura el botón opcional de llamada a la acción. + /// + /// - Usa `Some((texto, closure_url))` para mostrarlo, donde [`FnPathByContext`] recibe el + /// [`Context`] y devuelve la ruta o URL final al pulsar el botón. + /// - Usa `None` para ocultarlo. + /// + /// # Ejemplo + /// + /// ```rust + /// # use pagetop::prelude::*; + /// // Define un botón con texto y una URL fija. + /// let intro = Intro::default().with_button(Some((L10n::n("Pulsa este botón"), |_| "/start"))); + /// // Descarta el botón de la intro. + /// let intro_no_button = Intro::default().with_button(None); + /// ``` + #[builder_fn] + pub fn with_button(mut self, button: Option<(L10n, FnPathByContext)>) -> Self { + self.button = button; + self + } + + /// Selecciona el tipo de apertura: [`IntroOpening::PageTop`] (por defecto) o + /// [`IntroOpening::Custom`]. + /// + /// - `PageTop`: añade *badges* automáticos y una presentación de lo que es PageTop. + /// - `Custom`: introducción en blanco para añadir cualquier contenido. + /// + /// # Ejemplo + /// + /// ```rust + /// # use pagetop::prelude::*; + /// let intro = Intro::default().with_opening(IntroOpening::Custom); + /// ``` + #[builder_fn] + pub fn with_opening(mut self, opening: IntroOpening) -> Self { + self.opening = opening; + self + } + + /// Añade un nuevo componente hijo a la intro. + /// + /// Si es un bloque ([`Block`]) aplica estilos específicos para destacarlo. + pub fn add_component(mut self, component: impl Component) -> Self { + self.children + .alter_child(ChildOp::Add(Child::with(component))); + self + } + + /// Modifica la lista de hijos (`children`) aplicando una operación [`ChildOp`]. + #[builder_fn] + pub fn with_child(mut self, op: ChildOp) -> Self { + self.children.alter_child(op); + self + } + + // **< Intro GETTERS >************************************************************************** + + /// Devuelve el título de entrada. + pub fn title(&self) -> &L10n { + &self.title + } + + /// Devuelve el eslogan de la entrada. + pub fn slogan(&self) -> &L10n { + &self.slogan + } + + /// Devuelve el botón de llamada a la acción, si existe. + pub fn button(&self) -> Option<(&L10n, &FnPathByContext)> { + self.button.as_ref().map(|(txt, lnk)| (txt, lnk)) + } + + /// Devuelve el modo de apertura configurado. + pub fn opening(&self) -> IntroOpening { + self.opening + } + + /// Devuelve la lista de hijos (`children`) de la intro. + pub fn children(&self) -> &Children { + &self.children + } +} diff --git a/src/base/extension/welcome.rs b/src/base/extension/welcome.rs index 5c6fec5..865807a 100644 --- a/src/base/extension/welcome.rs +++ b/src/base/extension/welcome.rs @@ -26,30 +26,29 @@ async fn homepage(request: HttpRequest) -> ResultPage { Page::new(request) .with_theme("Basic") - .with_layout("PageTopIntro") .with_title(L10n::l("welcome_title")) - .with_description(L10n::l("welcome_intro").with_arg("app", app)) - .with_param("intro_button_txt", L10n::l("welcome_powered")) - .with_param("intro_button_lnk", "https://pagetop.cillero.es".to_string()) .add_component( - Block::new() - .with_title(L10n::l("welcome_status_title")) - .add_component(Html::with(move |cx| { - html! { - p { (L10n::l("welcome_status_1").using(cx)) } - p { (L10n::l("welcome_status_2").using(cx)) } - } - })), - ) - .add_component( - Block::new() - .with_title(L10n::l("welcome_support_title")) - .add_component(Html::with(move |cx| { - html! { - p { (L10n::l("welcome_support_1").using(cx)) } - p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) } - } - })), + Intro::new() + .add_component( + Block::new() + .with_title(L10n::l("welcome_status_title")) + .add_component(Html::with(move |cx| { + html! { + p { (L10n::l("welcome_status_1").using(cx)) } + p { (L10n::l("welcome_status_2").using(cx)) } + } + })), + ) + .add_component( + Block::new() + .with_title(L10n::l("welcome_support_title")) + .add_component(Html::with(move |cx| { + html! { + p { (L10n::l("welcome_support_1").using(cx)) } + p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) } + } + })), + ), ) .render() } diff --git a/src/base/theme/basic.rs b/src/base/theme/basic.rs index b6a982f..a671185 100644 --- a/src/base/theme/basic.rs +++ b/src/base/theme/basic.rs @@ -4,33 +4,7 @@ use crate::prelude::*; /// El tema básico usa las mismas regiones predefinidas por [`ThemeRegion`]. pub type BasicRegion = ThemeRegion; -/// Tema básico por defecto. -/// -/// Ofrece las siguientes composiciones (*layouts*): -/// -/// - **Composición predeterminada** -/// - Renderizado genérico con -/// [`ThemePage::render_body()`](crate::core::theme::ThemePage::render_body) usando las regiones -/// predefinidas en [`page_regions()`](crate::core::theme::Theme::page_regions). -/// -/// - **`Intro`** -/// - Página de entrada con cabecera visual, título y descripción y un botón opcional de llamada a -/// la acción. Ideal para una página de inicio o bienvenida en el contexto de PageTop. -/// - **Regiones:** `content` (se renderiza dentro de `.intro-content__body`). -/// - **Parámetros:** -/// - `intro_button_txt` (`L10n`) – Texto del botón. -/// - `intro_button_lnk` (`Option`) – URL del botón; si no se indica, el botón no se -/// muestra. -/// -/// - **`PageTopIntro`** -/// - Variante de `Intro` con textos predefinidos sobre PageTop al inicio del contenido. Añade una -/// banda de *badges* con la versión de [PageTop en crates.io](https://crates.io/crates/pagetop) -/// más la fecha de la última versión publicada y la licencia de uso. -/// - **Regiones:** `content` (igual que `Intro`). -/// - **Parámetros:** los mismos que `Intro`. -/// -/// **Nota:** si no se especifica `layout` o el valor no coincide con ninguno de los anteriores, se -/// aplica la composición predeterminada. +/// Tema básico por defecto que extiende el funcionamiento predeterminado de [`Theme`]. pub struct Basic; impl Extension for Basic { @@ -40,180 +14,7 @@ impl Extension for Basic { } impl Theme for Basic { - fn render_page_body(&self, page: &mut Page) -> Markup { - match page.layout() { - "Intro" => render_intro(page), - "PageTopIntro" => render_pagetop_intro(page), - _ => ::render_body(self, page, self.page_regions()), - } - } - - fn after_render_page_body(&self, page: &mut Page) { - let pkg_version = env!("CARGO_PKG_VERSION"); - - page.alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/normalize.css") - .with_version("8.0.1") - .with_weight(-99), - )) - .alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/root.css") - .with_version(pkg_version) - .with_weight(-99), - )) - .alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/basic.css") - .with_version(pkg_version) - .with_weight(-99), - )) - .alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/components.css") - .with_version(pkg_version) - .with_weight(-99), - )) - .alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/menu.css") - .with_version(pkg_version) - .with_weight(-99), - )) - .alter_assets(ContextOp::AddJavaScript( - JavaScript::defer("/js/menu.js") - .with_version(pkg_version) - .with_weight(-99), - )); + fn before_render_page_body(&self, page: &mut Page) { + page.alter_param("include_basic_assets", true); } } - -fn render_intro(page: &mut Page) -> Markup { - page.alter_assets(ContextOp::AddStyleSheet( - StyleSheet::from("/css/intro.css").with_version(env!("CARGO_PKG_VERSION")), - )); - - let title = page.title().unwrap_or_default(); - let intro = page.description().unwrap_or_default(); - - let theme = page.context().theme(); - let h = theme.render_page_region(page, &BasicRegion::Header); - let c = theme.render_page_region(page, &BasicRegion::Content); - let f = theme.render_page_region(page, &BasicRegion::Footer); - - let intro_button_txt: L10n = page.param_or_default("intro_button_txt"); - let intro_button_lnk: Option<&String> = page.param("intro_button_lnk"); - - html! { - header class="intro-header" { - section class="intro-header__body" { - h1 class="intro-header__title" { - span { (title) } - (intro) - } - } - aside class="intro-header__image" aria-hidden="true" { - div class="intro-header__monster" { - picture { - source - type="image/avif" - src="/img/monster-pagetop_250.avif" - srcset="/img/monster-pagetop_500.avif 1.5x"; - source - type="image/webp" - src="/img/monster-pagetop_250.webp" - srcset="/img/monster-pagetop_500.webp 1.5x"; - img - src="/img/monster-pagetop_250.png" - srcset="/img/monster-pagetop_500.png 1.5x" - alt="Monster PageTop"; - } - } - } - (h) - } - main class="intro-content" { - section class="intro-content__body" { - div class="intro-text" { - @if intro_button_lnk.is_some() { - div class="intro-button" { - a - class="intro-button__link" - href=[intro_button_lnk] - target="_blank" - rel="noreferrer" - { - span {} span {} span {} - div class="intro-button__text" { - (intro_button_txt.using(page)) - } - } - } - } - (c) - } - } - } - footer class="intro-footer" { - section class="intro-footer__body" { - div class="intro-footer__logo" { - svg - viewBox="0 0 1614 1614" - xmlns="http://www.w3.org/2000/svg" - role="img" - aria-label=[L10n::l("pagetop_logo").lookup(page)] - preserveAspectRatio="xMidYMid slice" - focusable="false" - { - path fill="rgb(255,255,255)" d="M 1573,357 L 1415,357 C 1400,357 1388,369 1388,383 L 1388,410 1335,410 1335,357 C 1335,167 1181,13 992,13 L 621,13 C 432,13 278,167 278,357 L 278,410 225,410 225,383 C 225,369 213,357 198,357 L 40,357 C 25,357 13,369 13,383 L 13,648 C 13,662 25,674 40,674 L 198,674 C 213,674 225,662 225,648 L 225,621 278,621 278,1256 C 278,1446 432,1600 621,1600 L 992,1600 C 1181,1600 1335,1446 1335,1256 L 1335,621 1388,621 1388,648 C 1388,662 1400,674 1415,674 L 1573,674 C 1588,674 1600,662 1600,648 L 1600,383 C 1600,369 1588,357 1573,357 L 1573,357 1573,357 Z M 66,410 L 172,410 172,621 66,621 66,410 66,410 Z M 1282,357 L 1282,488 C 1247,485 1213,477 1181,464 L 1196,437 C 1203,425 1199,409 1186,401 1174,394 1158,398 1150,411 L 1133,440 C 1105,423 1079,401 1056,376 L 1075,361 C 1087,352 1089,335 1079,324 1070,313 1054,311 1042,320 L 1023,335 C 1000,301 981,263 967,221 L 1011,196 C 1023,189 1028,172 1021,160 1013,147 997,143 984,150 L 953,168 C 945,136 941,102 940,66 L 992,66 C 1152,66 1282,197 1282,357 L 1282,357 1282,357 Z M 621,66 L 674,66 674,225 648,225 C 633,225 621,237 621,251 621,266 633,278 648,278 L 674,278 674,357 648,357 C 633,357 621,369 621,383 621,398 633,410 648,410 L 674,410 674,489 648,489 C 633,489 621,501 621,516 621,530 633,542 648,542 L 664,542 C 651,582 626,623 600,662 583,653 563,648 542,648 469,648 410,707 410,780 410,787 411,794 412,801 388,805 361,806 331,806 L 331,357 C 331,197 461,66 621,66 L 621,66 621,66 Z M 621,780 C 621,824 586,859 542,859 498,859 463,824 463,780 463,736 498,701 542,701 586,701 621,736 621,780 L 621,780 621,780 Z M 225,463 L 278,463 278,569 225,569 225,463 225,463 Z M 992,1547 L 621,1547 C 461,1547 331,1416 331,1256 L 331,859 C 367,859 400,858 431,851 454,888 495,912 542,912 615,912 674,853 674,780 674,747 662,718 642,695 675,645 706,594 720,542 L 780,542 C 795,542 807,530 807,516 807,501 795,489 780,489 L 727,489 727,410 780,410 C 795,410 807,398 807,383 807,369 795,357 780,357 L 727,357 727,278 780,278 C 795,278 807,266 807,251 807,237 795,225 780,225 L 727,225 727,66 887,66 C 889,111 895,155 905,196 L 869,217 C 856,224 852,240 859,253 864,261 873,266 882,266 887,266 891,265 895,263 L 921,248 C 937,291 958,331 983,367 L 938,403 C 926,412 925,429 934,440 939,447 947,450 954,450 960,450 966,448 971,444 L 1016,408 C 1043,438 1074,465 1108,485 L 1084,527 C 1076,539 1081,555 1093,563 1098,565 1102,566 1107,566 1116,566 1125,561 1129,553 L 1155,509 C 1194,527 1237,538 1282,541 L 1282,1256 C 1282,1416 1152,1547 992,1547 L 992,1547 992,1547 Z M 1335,463 L 1388,463 1388,569 1335,569 1335,463 1335,463 Z M 1441,410 L 1547,410 1547,621 1441,621 1441,410 1441,410 Z" {} - path fill="rgb(255,255,255)" d="M 1150,1018 L 463,1018 C 448,1018 436,1030 436,1044 L 436,1177 C 436,1348 545,1468 701,1468 L 912,1468 C 1068,1468 1177,1348 1177,1177 L 1177,1044 C 1177,1030 1165,1018 1150,1018 L 1150,1018 1150,1018 Z M 912,1071 L 1018,1071 1018,1124 912,1124 912,1071 912,1071 Z M 489,1071 L 542,1071 542,1124 489,1124 489,1071 489,1071 Z M 701,1415 L 700,1415 C 701,1385 704,1352 718,1343 731,1335 759,1341 795,1359 802,1363 811,1363 818,1359 854,1341 882,1335 895,1343 909,1352 912,1385 913,1415 L 912,1415 701,1415 701,1415 701,1415 Z M 1124,1177 C 1124,1296 1061,1384 966,1408 964,1365 958,1320 922,1298 894,1281 856,1283 807,1306 757,1283 719,1281 691,1298 655,1320 649,1365 647,1408 552,1384 489,1296 489,1177 L 569,1177 C 583,1177 595,1165 595,1150 L 595,1071 859,1071 859,1150 C 859,1165 871,1177 886,1177 L 1044,1177 C 1059,1177 1071,1165 1071,1150 L 1071,1071 1124,1071 1124,1177 1124,1177 1124,1177 Z" {} - path fill="rgb(255,255,255)" d="M 1071,648 C 998,648 939,707 939,780 939,853 998,912 1071,912 1144,912 1203,853 1203,780 1203,707 1144,648 1071,648 L 1071,648 1071,648 Z M 1071,859 C 1027,859 992,824 992,780 992,736 1027,701 1071,701 1115,701 1150,736 1150,780 1150,824 1115,859 1071,859 L 1071,859 1071,859 Z" {} - } - } - div class="intro-footer__links" { - a href="https://crates.io/crates/pagetop" target="_blank" rel="noreferrer" { ("Crates.io") } - a href="https://docs.rs/pagetop" target="_blank" rel="noreferrer" { ("Docs.rs") } - a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noreferrer" { (L10n::l("intro_code").using(page)) } - em { (L10n::l("intro_have_fun").using(page)) } - } - } - (f) - } - } -} - -fn render_pagetop_intro(page: &mut Page) -> Markup { - page.alter_assets(ContextOp::AddJavaScript(JavaScript::on_load_async("intro-js", |cx| - util::indoc!(r#" - try { - const resp = await fetch("https://crates.io/api/v1/crates/pagetop"); - const data = await resp.json(); - const date = new Date(data.versions[0].created_at); - const formatted = date.toLocaleDateString("LANGID", { year: "numeric", month: "2-digit", day: "2-digit" }); - document.getElementById("intro-release").src = `https://img.shields.io/badge/Release%20date-${encodeURIComponent(formatted)}-blue?label=LABEL&style=for-the-badge`; - document.getElementById("intro-badges").style.display = "block"; - } catch (e) { - console.error("Failed to fetch release date from crates.io:", e); - } - "#) - .replace("LANGID", cx.langid().to_string().as_str()) - .replace("LABEL", L10n::l("intro_release_label").using(cx).as_str()) - .to_string(), - ))) - .alter_child_in("content", ChildOp::Prepend(Child::with(Html::with(|cx| html! { - p { (L10n::l("intro_text1").using(cx)) } - div id="intro-badges" style="display: none; margin-bottom: 1.1rem;" { - img - src="https://img.shields.io/crates/v/pagetop.svg?label=PageTop&style=for-the-badge" - alt=[L10n::l("intro_pagetop_label").lookup(cx)] {} (" ") - img - id="intro-release" - alt=[L10n::l("intro_release_label").lookup(cx)] {} (" ") - img - src=(format!( - "https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label={}&style=for-the-badge", - L10n::l("intro_license_label").lookup(cx).unwrap_or_default() - )) - alt=[L10n::l("intro_license_label").lookup(cx)] {} - } - p { (L10n::l("intro_text2").using(cx)) } - })))); - - render_intro(page) -} diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs index 4e7db77..1eb7b22 100644 --- a/src/core/theme/definition.rs +++ b/src/core/theme/definition.rs @@ -1,6 +1,7 @@ +use crate::core::component::{ContextOp, Contextual}; use crate::core::extension::Extension; use crate::core::theme::{Region, RegionRef, REGION_CONTENT}; -use crate::html::{html, Markup}; +use crate::html::{html, Markup, StyleSheet}; use crate::locale::L10n; use crate::response::page::Page; use crate::{global, join}; @@ -241,7 +242,7 @@ pub trait Theme: Extension + ThemePage + Send + Sync { /// Renderiza el contenido del `` de la página. /// - /// Si se sobrescribe este método, se puede volver al comportamiento base con: + /// Si se sobrescribe este método, se puede volver al renderizado base con: /// `::render_body(self, page, self.page_regions())`. #[inline] fn render_page_body(&self, page: &mut Page) -> Markup { @@ -256,10 +257,29 @@ pub trait Theme: Extension + ThemePage + Send + Sync { /// Renderiza el contenido del `` de la página. /// - /// Si se sobrescribe este método, se puede volver al comportamiento base con: + /// Si se sobrescribe este método, se puede volver al renderizado base con: /// `::render_head(self, page)`. #[inline] fn render_page_head(&self, page: &mut Page) -> Markup { + if page.param_or("include_basic_assets", false) { + let pkg_version = env!("CARGO_PKG_VERSION"); + + page.alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/normalize.css") + .with_version("8.0.1") + .with_weight(-99), + )) + .alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/root.css") + .with_version(pkg_version) + .with_weight(-99), + )) + .alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/css/basic.css") + .with_version(pkg_version) + .with_weight(-99), + )); + } ::render_head(self, page) } diff --git a/src/locale/en-US/base.ftl b/src/locale/en-US/base.ftl index 16b1a3e..76baa12 100644 --- a/src/locale/en-US/base.ftl +++ b/src/locale/en-US/base.ftl @@ -1,4 +1,8 @@ -# Basic theme, intro layout. +# Intro component. +intro_default_title = Hello, world! +intro_default_slogan = Discover⚡{ $app } +intro_default_button = A web solution powered by PageTop + intro_pagetop_label = PageTop version on Crates.io intro_release_label = Release date intro_license_label = License diff --git a/src/locale/en-US/welcome.ftl b/src/locale/en-US/welcome.ftl index 20faf96..3402880 100644 --- a/src/locale/en-US/welcome.ftl +++ b/src/locale/en-US/welcome.ftl @@ -1,16 +1,12 @@ welcome_extension_name = Default Homepage welcome_extension_description = Displays a default homepage when none is configured. -welcome_page = Welcome page -welcome_title = Hello, world! - -welcome_intro = Discover⚡{ $app } -welcome_powered = A web solution powered by PageTop +welcome_title = Welcome page welcome_status_title = Status welcome_status_1 = If you can see this page, it means the PageTop server is running correctly, but the application is not fully configured. This may be due to routine maintenance or a temporary issue. welcome_status_2 = If the issue persists, please contact the system administrator. welcome_support_title = Support -welcome_support_1 = To report issues with the PageTop framework, use SoloGit. Remember, before opening a new issue, review the existing ones to avoid duplicates. +welcome_support_1 = To report issues with the PageTop framework, use GitHub. Remember, before opening a new issue, review the existing ones to avoid duplicates. welcome_support_2 = For issues specific to the application ({ $app }), please use its official repository or support channel. diff --git a/src/locale/es-ES/base.ftl b/src/locale/es-ES/base.ftl index fee21a9..09867d1 100644 --- a/src/locale/es-ES/base.ftl +++ b/src/locale/es-ES/base.ftl @@ -1,4 +1,8 @@ -# Basic theme, intro layout. +# Intro component. +intro_default_title = ¡Hola, mundo! +intro_default_slogan = Descubre⚡{ $app } +intro_default_button = Una solución web creada con PageTop + intro_pagetop_label = Versión de PageTop en Crates.io intro_release_label = Lanzamiento intro_license_label = Licencia diff --git a/src/locale/es-ES/welcome.ftl b/src/locale/es-ES/welcome.ftl index 13330db..605d1e2 100644 --- a/src/locale/es-ES/welcome.ftl +++ b/src/locale/es-ES/welcome.ftl @@ -1,16 +1,12 @@ welcome_extension_name = Página de inicio predeterminada welcome_extension_description = Muestra una página de inicio predeterminada cuando no hay ninguna configurada. -welcome_page = Página de bienvenida -welcome_title = ¡Hola, mundo! - -welcome_intro = Descubre⚡{ $app } -welcome_powered = Una solución web creada con PageTop +welcome_title = Página de bienvenida welcome_status_title = Estado welcome_status_1 = Si puedes ver esta página, es porque el servidor de PageTop está funcionando correctamente, pero la aplicación no está completamente configurada. Esto puede deberse a tareas de mantenimiento o a una incidencia temporal. welcome_status_2 = Si el problema persiste, por favor, contacta con el administrador del sistema. welcome_support_title = Soporte -welcome_support_1 = Para comunicar incidencias del propio entorno PageTop, utiliza SoloGit. Recuerda, antes de abrir una nueva incidencia, revisa las existentes para evitar duplicados. +welcome_support_1 = Para comunicar incidencias del propio entorno PageTop, utiliza GitHub. Recuerda, antes de abrir una nueva incidencia, revisa las existentes para evitar duplicados. welcome_support_2 = Para fallos específicos de la aplicación ({ $app }), utiliza su repositorio oficial o su canal de soporte. diff --git a/static/css/intro.css b/static/css/intro.css index 774bbb2..17ab5be 100644 --- a/static/css/intro.css +++ b/static/css/intro.css @@ -1,12 +1,24 @@ -html { - min-height: 100%; - background-color: black; +:root { + --intro-bg-img: url('/img/intro-header.jpg'); + --intro-bg-img-set: image-set(url('/img/intro-header.avif') type('image/avif'), url('/img/intro-header.webp') type('image/webp'), var(--intro-bg-img) type('image/jpeg')); + --intro-bg-img-sm: url('/img/intro-header-sm.jpg'); + --intro-bg-img-sm-set: image-set(url('/img/intro-header-sm.avif') type('image/avif'), url('/img/intro-header-sm.webp') type('image/webp'), var(--intro-bg-img-sm) type('image/jpeg')); + --intro-bg-color: #8c5919; + --intro-bg-block-1: #b689ff; + --intro-bg-block-2: #fecaca; + --intro-bg-block-3: #e6a9e2; + --intro-bg-block-4: #ffedca; + --intro-bg-block-5: #ffffff; + --intro-color: #1a202c; + --intro-color-gray: #e4e4e7; + --intro-color-link: #1e4eae; + --intro-focus-outline: 2px solid var(--intro-color-link); + --intro-focus-outline-offset: 2px; + --intro-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); } -body { - margin: auto; +.intro { position: relative; - min-height: 100%; min-width: 350px; color: var(--intro-color); background-color: var(--intro-bg-color); @@ -18,22 +30,22 @@ body { align-items: center; } -section { +.intro section { position: relative; text-align: center; } -a { +.intro a { color: currentColor; text-decoration: underline; transition: font-size 0.2s, text-decoration-color 0.2s; } -a:focus-visible { +.intro a:focus-visible { outline: var(--intro-focus-outline); outline-offset: var(--intro-focus-outline-offset); } -a:hover, -a:hover:visited { +.intro a:hover, +.intro a:hover:visited { text-decoration-color: var(--intro-color-link); } @@ -172,8 +184,8 @@ a:hover:visited { justify-content: space-between; font-size: 1.5rem; line-height: 1.3; - text-decoration: none; - transition: transform 0.3s ease-in-out; + text-decoration: none !important; + transition: transform 0.3s ease-in-out !important; position: relative; overflow: hidden; min-width: 28.875rem; @@ -307,7 +319,7 @@ a:hover:visited { } .intro-button__link:hover { transition: all .5s; - transform: rotate(-3deg) scale(1.1); + transform: rotate(-3deg) scale(1.125); } } @@ -326,11 +338,11 @@ a:hover:visited { background: #fff; position: relative; } -.region--content { +.intro-text__children { padding: 2.5rem 1.063rem 0.75rem; overflow: hidden; } -.region--content p { +.intro-text__children p { width: 100%; line-height: 150%; font-weight: 400; @@ -342,7 +354,7 @@ a:hover:visited { font-size: 1.375rem; line-height: 2rem; } - .intro-button + .region--content { + .intro-button + .intro-text__children { padding-top: 7rem; } } @@ -351,7 +363,7 @@ a:hover:visited { padding-bottom: 9rem;; } .intro-text, - .region--content { + .intro-text__children { border-radius: 0.75rem; } .intro-text { @@ -359,19 +371,19 @@ a:hover:visited { max-width: 60rem; margin: 0 auto 6rem; } - .region--content { + .intro-text__children { padding-left: 4.5rem; padding-right: 4.5rem; } } -.region--content .block { +.intro-text__children .block { position: relative; } -.region--content .block__title { +.intro-text__children .block__title { margin: 1em 0 .8em; } -.region--content .block__title span { +.intro-text__children .block__title span { display: inline-block; padding: 10px 30px 14px; margin: 30px 0 0 20px; @@ -382,7 +394,7 @@ a:hover:visited { border-color: orangered; transform: rotate(-3deg) translateY(-25%); } -.region--content .block__title:before { +.intro-text__children .block__title:before { content: ""; height: 5px; position: absolute; @@ -395,7 +407,7 @@ a:hover:visited { transform: rotate(2deg) translateY(-50%); transform-origin: top left; } -.region--content .block__title:after { +.intro-text__children .block__title:after { content: ""; height: 70rem; position: absolute; @@ -406,22 +418,28 @@ a:hover:visited { background: var(--intro-bg-block-1); transform: rotate(2deg); } -.region--content .block:nth-of-type(5n+1) .block__title:after { +.intro-text__children .block:nth-of-type(5n+1) .block__title:after { background: var(--intro-bg-block-1); } -.region--content .block:nth-of-type(5n+2) .block__title:after { +.intro-text__children .block:nth-of-type(5n+2) .block__title:after { background: var(--intro-bg-block-2); } -.region--content .block:nth-of-type(5n+3) .block__title:after { +.intro-text__children .block:nth-of-type(5n+3) .block__title:after { background: var(--intro-bg-block-3); } -.region--content .block:nth-of-type(5n+4) .block__title:after { +.intro-text__children .block:nth-of-type(5n+4) .block__title:after { background: var(--intro-bg-block-4); } -.region--content .block:nth-of-type(5n+5) .block__title:after { +.intro-text__children .block:nth-of-type(5n+5) .block__title:after { background: var(--intro-bg-block-5); } +#intro-badges { + display: none; + margin-bottom: 1.1rem; + text-align: center; +} + /* * Footer */ @@ -474,9 +492,3 @@ a:hover:visited { padding: 0 1rem 2rem; } } - -/* PoweredBy component */ - -.poweredby a:visited { - color: var(--intro-color-gray); -} diff --git a/static/css/root.css b/static/css/root.css index 270c1b3..aeab1c6 100644 --- a/static/css/root.css +++ b/static/css/root.css @@ -1,22 +1,3 @@ -:root { - --intro-bg-img: url('/img/intro-header.jpg'); - --intro-bg-img-set: image-set(url('/img/intro-header.avif') type('image/avif'), url('/img/intro-header.webp') type('image/webp'), var(--intro-bg-img) type('image/jpeg')); - --intro-bg-img-sm: url('/img/intro-header-sm.jpg'); - --intro-bg-img-sm-set: image-set(url('/img/intro-header-sm.avif') type('image/avif'), url('/img/intro-header-sm.webp') type('image/webp'), var(--intro-bg-img-sm) type('image/jpeg')); - --intro-bg-color: #8c5919; - --intro-bg-block-1: #b689ff; - --intro-bg-block-2: #fecaca; - --intro-bg-block-3: #e6a9e2; - --intro-bg-block-4: #ffedca; - --intro-bg-block-5: #ffffff; - --intro-color: #1a202c; - --intro-color-gray: #e4e4e7; - --intro-color-link: #1e4eae; - --intro-focus-outline: 2px solid var(--intro-color-link); - --intro-focus-outline-offset: 2px; - --intro-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); -} - :root { --val-font-sans: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; --val-font-serif: "Lora","georgia",serif; From c1ba4ac7ea703f5313ecd596d62ff43cd52b4ef2 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sun, 12 Oct 2025 12:07:02 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20A=C3=B1ade=20tema=20`Bootiser`?= =?UTF-8?q?=20basado=20en=20`Bootstrap`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 9 + Cargo.toml | 3 + extensions/pagetop-aliner/Cargo.toml | 2 +- extensions/pagetop-aliner/README.md | 2 +- extensions/pagetop-aliner/src/lib.rs | 2 +- extensions/pagetop-bootsier/Cargo.toml | 21 + extensions/pagetop-bootsier/README.md | 100 + extensions/pagetop-bootsier/build.rs | 22 + extensions/pagetop-bootsier/src/lib.rs | 121 + .../static/bootstrap-5.3.3/_accordion.scss | 158 + .../static/bootstrap-5.3.3/_alert.scss | 68 + .../static/bootstrap-5.3.3/_badge.scss | 38 + .../static/bootstrap-5.3.3/_breadcrumb.scss | 40 + .../static/bootstrap-5.3.3/_button-group.scss | 142 + .../static/bootstrap-5.3.3/_buttons.scss | 216 + .../static/bootstrap-5.3.3/_card.scss | 239 + .../static/bootstrap-5.3.3/_carousel.scss | 236 + .../static/bootstrap-5.3.3/_close.scss | 63 + .../static/bootstrap-5.3.3/_containers.scss | 41 + .../static/bootstrap-5.3.3/_dropdown.scss | 250 + .../static/bootstrap-5.3.3/_forms.scss | 9 + .../static/bootstrap-5.3.3/_functions.scss | 302 ++ .../static/bootstrap-5.3.3/_grid.scss | 39 + .../static/bootstrap-5.3.3/_helpers.scss | 12 + .../static/bootstrap-5.3.3/_images.scss | 42 + .../static/bootstrap-5.3.3/_list-group.scss | 197 + .../static/bootstrap-5.3.3/_maps.scss | 174 + .../static/bootstrap-5.3.3/_mixins.scss | 42 + .../static/bootstrap-5.3.3/_modal.scss | 236 + .../static/bootstrap-5.3.3/_nav.scss | 197 + .../static/bootstrap-5.3.3/_navbar.scss | 289 ++ .../static/bootstrap-5.3.3/_offcanvas.scss | 143 + .../static/bootstrap-5.3.3/_pagination.scss | 109 + .../static/bootstrap-5.3.3/_placeholders.scss | 51 + .../static/bootstrap-5.3.3/_popover.scss | 196 + .../static/bootstrap-5.3.3/_progress.scss | 68 + .../static/bootstrap-5.3.3/_reboot.scss | 611 +++ .../static/bootstrap-5.3.3/_root.scss | 187 + .../static/bootstrap-5.3.3/_spinners.scss | 85 + .../static/bootstrap-5.3.3/_tables.scss | 171 + .../static/bootstrap-5.3.3/_toasts.scss | 73 + .../static/bootstrap-5.3.3/_tooltip.scss | 119 + .../static/bootstrap-5.3.3/_transitions.scss | 27 + .../static/bootstrap-5.3.3/_type.scss | 106 + .../static/bootstrap-5.3.3/_utilities.scss | 806 +++ .../bootstrap-5.3.3/_variables-dark.scss | 87 + .../static/bootstrap-5.3.3/_variables.scss | 1751 +++++++ .../bootstrap-5.3.3/bootstrap-grid.scss | 62 + .../bootstrap-5.3.3/bootstrap-reboot.scss | 10 + .../bootstrap-5.3.3/bootstrap-utilities.scss | 19 + .../static/bootstrap-5.3.3/bootstrap.scss | 52 + .../forms/_floating-labels.scss | 95 + .../bootstrap-5.3.3/forms/_form-check.scss | 189 + .../bootstrap-5.3.3/forms/_form-control.scss | 214 + .../bootstrap-5.3.3/forms/_form-range.scss | 91 + .../bootstrap-5.3.3/forms/_form-select.scss | 80 + .../bootstrap-5.3.3/forms/_form-text.scss | 11 + .../bootstrap-5.3.3/forms/_input-group.scss | 132 + .../static/bootstrap-5.3.3/forms/_labels.scss | 36 + .../bootstrap-5.3.3/forms/_validation.scss | 12 + .../bootstrap-5.3.3/helpers/_clearfix.scss | 3 + .../bootstrap-5.3.3/helpers/_color-bg.scss | 7 + .../helpers/_colored-links.scss | 30 + .../bootstrap-5.3.3/helpers/_focus-ring.scss | 5 + .../bootstrap-5.3.3/helpers/_icon-link.scss | 25 + .../bootstrap-5.3.3/helpers/_position.scss | 36 + .../bootstrap-5.3.3/helpers/_ratio.scss | 26 + .../bootstrap-5.3.3/helpers/_stacks.scss | 15 + .../helpers/_stretched-link.scss | 15 + .../helpers/_text-truncation.scss | 7 + .../helpers/_visually-hidden.scss | 8 + .../static/bootstrap-5.3.3/helpers/_vr.scss | 8 + .../static/bootstrap-5.3.3/mixins/_alert.scss | 18 + .../bootstrap-5.3.3/mixins/_backdrop.scss | 14 + .../bootstrap-5.3.3/mixins/_banner.scss | 7 + .../mixins/_border-radius.scss | 78 + .../bootstrap-5.3.3/mixins/_box-shadow.scss | 18 + .../bootstrap-5.3.3/mixins/_breakpoints.scss | 127 + .../bootstrap-5.3.3/mixins/_buttons.scss | 70 + .../static/bootstrap-5.3.3/mixins/_caret.scss | 69 + .../bootstrap-5.3.3/mixins/_clearfix.scss | 9 + .../bootstrap-5.3.3/mixins/_color-mode.scss | 21 + .../bootstrap-5.3.3/mixins/_color-scheme.scss | 7 + .../bootstrap-5.3.3/mixins/_container.scss | 11 + .../bootstrap-5.3.3/mixins/_deprecate.scss | 10 + .../static/bootstrap-5.3.3/mixins/_forms.scss | 163 + .../bootstrap-5.3.3/mixins/_gradients.scss | 47 + .../static/bootstrap-5.3.3/mixins/_grid.scss | 151 + .../static/bootstrap-5.3.3/mixins/_image.scss | 16 + .../bootstrap-5.3.3/mixins/_list-group.scss | 26 + .../static/bootstrap-5.3.3/mixins/_lists.scss | 7 + .../bootstrap-5.3.3/mixins/_pagination.scss | 10 + .../bootstrap-5.3.3/mixins/_reset-text.scss | 17 + .../bootstrap-5.3.3/mixins/_resize.scss | 6 + .../mixins/_table-variants.scss | 24 + .../mixins/_text-truncate.scss | 8 + .../bootstrap-5.3.3/mixins/_transition.scss | 26 + .../bootstrap-5.3.3/mixins/_utilities.scss | 97 + .../mixins/_visually-hidden.scss | 33 + .../static/bootstrap-5.3.3/tests/jasmine.js | 16 + .../_auto-import-of-variables-dark.test.scss | 7 + .../tests/mixins/_color-modes.test.scss | 69 + .../_media-query-color-mode-full.test.scss | 8 + .../tests/mixins/_utilities.test.scss | 393 ++ .../tests/sass-true/register.js | 14 + .../bootstrap-5.3.3/tests/sass-true/runner.js | 17 + .../tests/utilities/_api.test.scss | 75 + .../bootstrap-5.3.3/utilities/_api.scss | 47 + .../static/bootstrap-5.3.3/vendor/_rfs.scss | 348 ++ .../pagetop-bootsier/static/js/bootstrap.js | 4494 +++++++++++++++++ .../static/js/bootstrap.js.map | 1 + .../static/js/bootstrap.min.js | 7 + .../static/js/bootstrap.min.js.map | 1 + src/base/extension/welcome.rs | 1 - tools/changelog.sh | 8 + 115 files changed, 15682 insertions(+), 4 deletions(-) create mode 100644 extensions/pagetop-bootsier/Cargo.toml create mode 100644 extensions/pagetop-bootsier/README.md create mode 100644 extensions/pagetop-bootsier/build.rs create mode 100644 extensions/pagetop-bootsier/src/lib.rs create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_accordion.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_alert.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_badge.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_breadcrumb.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_button-group.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_buttons.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_card.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_carousel.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_close.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_containers.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_dropdown.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_forms.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_functions.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_grid.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_helpers.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_images.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_list-group.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_maps.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_mixins.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_modal.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_nav.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_navbar.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_offcanvas.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_pagination.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_placeholders.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_popover.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_progress.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_reboot.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_root.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_spinners.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_tables.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_toasts.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_tooltip.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_transitions.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_type.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_utilities.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_variables-dark.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/_variables.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/bootstrap-grid.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/bootstrap-reboot.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/bootstrap-utilities.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/bootstrap.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_floating-labels.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_form-check.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_form-control.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_form-range.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_form-select.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_form-text.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_input-group.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_labels.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/forms/_validation.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_clearfix.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_color-bg.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_colored-links.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_focus-ring.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_icon-link.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_position.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_ratio.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_stacks.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_stretched-link.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_text-truncation.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_visually-hidden.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/helpers/_vr.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_alert.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_backdrop.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_banner.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_border-radius.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_box-shadow.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_breakpoints.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_buttons.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_caret.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_clearfix.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_color-mode.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_color-scheme.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_container.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_deprecate.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_forms.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_gradients.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_grid.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_image.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_list-group.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_lists.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_pagination.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_reset-text.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_resize.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_table-variants.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_text-truncate.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_transition.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_utilities.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/mixins/_visually-hidden.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/jasmine.js create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/mixins/_auto-import-of-variables-dark.test.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/mixins/_color-modes.test.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/mixins/_media-query-color-mode-full.test.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/mixins/_utilities.test.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/sass-true/register.js create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/sass-true/runner.js create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/tests/utilities/_api.test.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/utilities/_api.scss create mode 100644 extensions/pagetop-bootsier/static/bootstrap-5.3.3/vendor/_rfs.scss create mode 100644 extensions/pagetop-bootsier/static/js/bootstrap.js create mode 100644 extensions/pagetop-bootsier/static/js/bootstrap.js.map create mode 100644 extensions/pagetop-bootsier/static/js/bootstrap.min.js create mode 100644 extensions/pagetop-bootsier/static/js/bootstrap.min.js.map diff --git a/Cargo.lock b/Cargo.lock index cab741e..ebcebd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1561,6 +1561,7 @@ dependencies = [ "indoc", "itoa", "pagetop-aliner", + "pagetop-bootsier", "pagetop-build", "pagetop-macros", "pagetop-statics", @@ -1585,6 +1586,14 @@ dependencies = [ "pagetop-build", ] +[[package]] +name = "pagetop-bootsier" +version = "0.0.18" +dependencies = [ + "pagetop", + "pagetop-build", +] + [[package]] name = "pagetop-build" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index fe10143..103f6a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ testing = [] [dev-dependencies] tempfile = "3.23" pagetop-aliner.workspace = true +pagetop-bootsier.workspace = true [build-dependencies] pagetop-build.workspace = true @@ -65,6 +66,7 @@ members = [ "helpers/pagetop-statics", # Extensions "extensions/pagetop-aliner", + "extensions/pagetop-bootsier", ] [workspace.package] @@ -81,5 +83,6 @@ pagetop-macros = { version = "0.2", path = "helpers/pagetop-macros" } pagetop-statics = { version = "0.1", path = "helpers/pagetop-statics" } # Extensions pagetop-aliner = { version = "0.0", path = "extensions/pagetop-aliner" } +pagetop-bootsier = { version = "0.0", path = "extensions/pagetop-bootsier" } # PageTop pagetop = { version = "0.4", path = "." } diff --git a/extensions/pagetop-aliner/Cargo.toml b/extensions/pagetop-aliner/Cargo.toml index 1c1101f..603a630 100644 --- a/extensions/pagetop-aliner/Cargo.toml +++ b/extensions/pagetop-aliner/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.9" edition = "2021" description = """ - Tema para PageTop que muestra esquemáticamente la composición de las páginas HTML + Tema de PageTop que muestra esquemáticamente la composición de las páginas HTML """ categories = ["web-programming", "gui"] keywords = ["pagetop", "theme", "css"] diff --git a/extensions/pagetop-aliner/README.md b/extensions/pagetop-aliner/README.md index 88e3d78..ac6da91 100644 --- a/extensions/pagetop-aliner/README.md +++ b/extensions/pagetop-aliner/README.md @@ -2,7 +2,7 @@

PageTop Aliner

-

Tema para PageTop que muestra esquemáticamente la composición de las páginas HTML.

+

Tema de PageTop que muestra esquemáticamente la composición de las páginas HTML.

[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](#-licencia) [![Doc API](https://img.shields.io/docsrs/pagetop-aliner?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-aliner) diff --git a/extensions/pagetop-aliner/src/lib.rs b/extensions/pagetop-aliner/src/lib.rs index 084678d..92f9aa4 100644 --- a/extensions/pagetop-aliner/src/lib.rs +++ b/extensions/pagetop-aliner/src/lib.rs @@ -107,7 +107,7 @@ impl Extension for Aliner { } impl Theme for Aliner { - fn before_render_page_body(&self, page: &mut Page) { + fn after_render_page_body(&self, page: &mut Page) { page.alter_param("include_basic_assets", true) .alter_assets(ContextOp::AddStyleSheet( StyleSheet::from("/aliner/css/styles.css") diff --git a/extensions/pagetop-bootsier/Cargo.toml b/extensions/pagetop-bootsier/Cargo.toml new file mode 100644 index 0000000..8945e5a --- /dev/null +++ b/extensions/pagetop-bootsier/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pagetop-bootsier" +version = "0.0.18" +edition = "2021" + +description = """ + Tema de PageTop basado en Bootstrap para ofrecer su catálogo de estilos y componentes flexibles. +""" +categories = ["web-programming", "gui"] +keywords = ["pagetop", "theme", "bootstrap", "css", "js"] + +repository.workspace = true +homepage.workspace = true +license.workspace = true +authors.workspace = true + +[dependencies] +pagetop.workspace = true + +[build-dependencies] +pagetop-build.workspace = true diff --git a/extensions/pagetop-bootsier/README.md b/extensions/pagetop-bootsier/README.md new file mode 100644 index 0000000..87bd3c7 --- /dev/null +++ b/extensions/pagetop-bootsier/README.md @@ -0,0 +1,100 @@ +
+ +

PageTop Bootsier

+ +

Tema de PageTop basado en Bootstrap para ofrecer su catálogo de estilos y componentes flexibles.

+ +[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](#-licencia) +[![Doc API](https://img.shields.io/docsrs/pagetop-bootsier?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-bootsier) +[![Crates.io](https://img.shields.io/crates/v/pagetop-bootsier.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-bootsier) +[![Descargas](https://img.shields.io/crates/d/pagetop-bootsier.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-bootsier) + +
+
+ +## Sobre PageTop + +[PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la web +clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles y +configurables, basadas en HTML, CSS y JavaScript. + + +# ⚡️ Guía rápida + +Igual que con otras extensiones, **añade la dependencia** a tu `Cargo.toml`: + +```toml +[dependencies] +pagetop-bootsier = "..." +``` + +**Declara la extensión** en tu aplicación (o extensión que la requiera). Recuerda que el orden en +`dependencies()` determina la prioridad relativa frente a las otras extensiones: + +```rust,no_run +use pagetop::prelude::*; + +struct MyApp; + +impl Extension for MyApp { + fn dependencies(&self) -> Vec { + vec![ + // ... + &pagetop_bootsier::Bootsier, + // ... + ] + } +} + +#[pagetop::main] +async fn main() -> std::io::Result<()> { + Application::prepare(&MyApp).run()?.await +} +``` + +Y **selecciona el tema en la configuración** de la aplicación: + +```toml +[app] +theme = "Bootsier" +``` + +…o **fuerza el tema por código** en una página concreta: + +```rust,no_run +use pagetop::prelude::*; + +async fn homepage(request: HttpRequest) -> ResultPage { + Page::new(request) + .with_theme("Bootsier") + .add_component( + Block::new() + .with_title(L10n::l("sample_title")) + .add_component(Html::with(|cx| html! { + p { (L10n::l("sample_content").using(cx)) } + })), + ) + .render() +} +``` + + +# 🚧 Advertencia + +**PageTop** es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su +ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos +hasta que se libere la versión **1.0.0**. + + +# 📜 Licencia + +El código está disponible bajo una doble licencia: + + * **Licencia MIT** + ([LICENSE-MIT](LICENSE-MIT) o también https://opensource.org/licenses/MIT) + + * **Licencia Apache, Versión 2.0** + ([LICENSE-APACHE](LICENSE-APACHE) o también https://www.apache.org/licenses/LICENSE-2.0) + +Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el estándar de facto en +el ecosistema Rust. diff --git a/extensions/pagetop-bootsier/build.rs b/extensions/pagetop-bootsier/build.rs new file mode 100644 index 0000000..47cb474 --- /dev/null +++ b/extensions/pagetop-bootsier/build.rs @@ -0,0 +1,22 @@ +use pagetop_build::StaticFilesBundle; + +use std::env; +use std::path::Path; + +fn main() -> std::io::Result<()> { + StaticFilesBundle::from_scss( + "./static/bootstrap-5.3.3/bootstrap.scss", + "bootstrap.min.css", + ) + .with_name("bootsier") + .build()?; + StaticFilesBundle::from_dir("./static/js", Some(bootstrap_js_files)) + .with_name("bootsier_js") + .build() +} + +fn bootstrap_js_files(path: &Path) -> bool { + // No filtering during development, only on "release" compilation. + env::var("PROFILE").unwrap_or_else(|_| "release".to_string()) != "release" + || path.file_name().map_or(false, |n| n == "bootstrap.min.js") +} diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs new file mode 100644 index 0000000..8157ec2 --- /dev/null +++ b/extensions/pagetop-bootsier/src/lib.rs @@ -0,0 +1,121 @@ +/*! +
+ +

PageTop Bootsier

+ +

Tema de PageTop basado en Bootstrap para ofrecer su catálogo de estilos y componentes flexibles.

+ +[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](#-licencia) +[![Doc API](https://img.shields.io/docsrs/pagetop-bootsier?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-bootsier) +[![Crates.io](https://img.shields.io/crates/v/pagetop-bootsier.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-bootsier) +[![Descargas](https://img.shields.io/crates/d/pagetop-bootsier.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-bootsier) + +
+
+ +## Sobre PageTop + +[PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la web +clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles y +configurables, basadas en HTML, CSS y JavaScript. + + +# ⚡️ Guía rápida + +Igual que con otras extensiones, **añade la dependencia** a tu `Cargo.toml`: + +```toml +[dependencies] +pagetop-bootsier = "..." +``` + +**Declara la extensión** en tu aplicación (o extensión que la requiera). Recuerda que el orden en +`dependencies()` determina la prioridad relativa frente a las otras extensiones: + +```rust,no_run +use pagetop::prelude::*; + +struct MyApp; + +impl Extension for MyApp { + fn dependencies(&self) -> Vec { + vec![ + // ... + &pagetop_bootsier::Bootsier, + // ... + ] + } +} + +#[pagetop::main] +async fn main() -> std::io::Result<()> { + Application::prepare(&MyApp).run()?.await +} +``` + +Y **selecciona el tema en la configuración** de la aplicación: + +```toml +[app] +theme = "Bootsier" +``` + +…o **fuerza el tema por código** en una página concreta: + +```rust,no_run +use pagetop::prelude::*; + +async fn homepage(request: HttpRequest) -> ResultPage { + Page::new(request) + .with_theme("Bootsier") + .add_component( + Block::new() + .with_title(L10n::l("sample_title")) + .add_component(Html::with(|cx| html! { + p { (L10n::l("sample_content").using(cx)) } + })), + ) + .render() +} +``` +*/ + +use pagetop::prelude::*; + +/// El tema usa las mismas regiones predefinidas por [`ThemeRegion`]. +pub type BootsierRegion = ThemeRegion; + +// Versión de la librería Bootstrap. +const BOOTSTRAP_VERSION: &str = "5.3.3"; + +/// Tema basado en [Bootstrap](https://getbootstrap.com/) para los componentes base de PageTop. +/// +/// Ofrece composición de páginas *responsive*, utilidades y componentes listos para usar, con +/// estilos coherentes y enfoque en accesibilidad. +pub struct Bootsier; + +impl Extension for Bootsier { + fn theme(&self) -> Option { + Some(&Self) + } + + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { + static_files_service!(scfg, [bootsier] => "/bootsier/css"); + static_files_service!(scfg, [bootsier_js] => "/bootsier/js"); + } +} + +impl Theme for Bootsier { + fn after_render_page_body(&self, page: &mut Page) { + page.alter_assets(ContextOp::AddStyleSheet( + StyleSheet::from("/bootsier/css/bootstrap.min.css") + .with_version(BOOTSTRAP_VERSION) + .with_weight(-90), + )) + .alter_assets(ContextOp::AddJavaScript( + JavaScript::defer("/bootsier/js/bootstrap.min.js") + .with_version(BOOTSTRAP_VERSION) + .with_weight(-90), + )); + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_accordion.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_accordion.scss new file mode 100644 index 0000000..17e5436 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_accordion.scss @@ -0,0 +1,158 @@ +// +// Base styles +// + +.accordion { + // scss-docs-start accordion-css-vars + --#{$prefix}accordion-color: #{$accordion-color}; + --#{$prefix}accordion-bg: #{$accordion-bg}; + --#{$prefix}accordion-transition: #{$accordion-transition}; + --#{$prefix}accordion-border-color: #{$accordion-border-color}; + --#{$prefix}accordion-border-width: #{$accordion-border-width}; + --#{$prefix}accordion-border-radius: #{$accordion-border-radius}; + --#{$prefix}accordion-inner-border-radius: #{$accordion-inner-border-radius}; + --#{$prefix}accordion-btn-padding-x: #{$accordion-button-padding-x}; + --#{$prefix}accordion-btn-padding-y: #{$accordion-button-padding-y}; + --#{$prefix}accordion-btn-color: #{$accordion-button-color}; + --#{$prefix}accordion-btn-bg: #{$accordion-button-bg}; + --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon)}; + --#{$prefix}accordion-btn-icon-width: #{$accordion-icon-width}; + --#{$prefix}accordion-btn-icon-transform: #{$accordion-icon-transform}; + --#{$prefix}accordion-btn-icon-transition: #{$accordion-icon-transition}; + --#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon)}; + --#{$prefix}accordion-btn-focus-box-shadow: #{$accordion-button-focus-box-shadow}; + --#{$prefix}accordion-body-padding-x: #{$accordion-body-padding-x}; + --#{$prefix}accordion-body-padding-y: #{$accordion-body-padding-y}; + --#{$prefix}accordion-active-color: #{$accordion-button-active-color}; + --#{$prefix}accordion-active-bg: #{$accordion-button-active-bg}; + // scss-docs-end accordion-css-vars +} + +.accordion-button { + position: relative; + display: flex; + align-items: center; + width: 100%; + padding: var(--#{$prefix}accordion-btn-padding-y) var(--#{$prefix}accordion-btn-padding-x); + @include font-size($font-size-base); + color: var(--#{$prefix}accordion-btn-color); + text-align: left; // Reset button style + background-color: var(--#{$prefix}accordion-btn-bg); + border: 0; + @include border-radius(0); + overflow-anchor: none; + @include transition(var(--#{$prefix}accordion-transition)); + + &:not(.collapsed) { + color: var(--#{$prefix}accordion-active-color); + background-color: var(--#{$prefix}accordion-active-bg); + box-shadow: inset 0 calc(-1 * var(--#{$prefix}accordion-border-width)) 0 var(--#{$prefix}accordion-border-color); // stylelint-disable-line function-disallowed-list + + &::after { + background-image: var(--#{$prefix}accordion-btn-active-icon); + transform: var(--#{$prefix}accordion-btn-icon-transform); + } + } + + // Accordion icon + &::after { + flex-shrink: 0; + width: var(--#{$prefix}accordion-btn-icon-width); + height: var(--#{$prefix}accordion-btn-icon-width); + margin-left: auto; + content: ""; + background-image: var(--#{$prefix}accordion-btn-icon); + background-repeat: no-repeat; + background-size: var(--#{$prefix}accordion-btn-icon-width); + @include transition(var(--#{$prefix}accordion-btn-icon-transition)); + } + + &:hover { + z-index: 2; + } + + &:focus { + z-index: 3; + outline: 0; + box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow); + } +} + +.accordion-header { + margin-bottom: 0; +} + +.accordion-item { + color: var(--#{$prefix}accordion-color); + background-color: var(--#{$prefix}accordion-bg); + border: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color); + + &:first-of-type { + @include border-top-radius(var(--#{$prefix}accordion-border-radius)); + + > .accordion-header .accordion-button { + @include border-top-radius(var(--#{$prefix}accordion-inner-border-radius)); + } + } + + &:not(:first-of-type) { + border-top: 0; + } + + // Only set a border-radius on the last item if the accordion is collapsed + &:last-of-type { + @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); + + > .accordion-header .accordion-button { + &.collapsed { + @include border-bottom-radius(var(--#{$prefix}accordion-inner-border-radius)); + } + } + + > .accordion-collapse { + @include border-bottom-radius(var(--#{$prefix}accordion-border-radius)); + } + } +} + +.accordion-body { + padding: var(--#{$prefix}accordion-body-padding-y) var(--#{$prefix}accordion-body-padding-x); +} + + +// Flush accordion items +// +// Remove borders and border-radius to keep accordion items edge-to-edge. + +.accordion-flush { + > .accordion-item { + border-right: 0; + border-left: 0; + @include border-radius(0); + + &:first-child { border-top: 0; } + &:last-child { border-bottom: 0; } + + // stylelint-disable selector-max-class + > .accordion-header .accordion-button { + &, + &.collapsed { + @include border-radius(0); + } + } + // stylelint-enable selector-max-class + + > .accordion-collapse { + @include border-radius(0); + } + } +} + +@if $enable-dark-mode { + @include color-mode(dark) { + .accordion-button::after { + --#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon-dark)}; + --#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon-dark)}; + } + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_alert.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_alert.scss new file mode 100644 index 0000000..b8cff9b --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_alert.scss @@ -0,0 +1,68 @@ +// +// Base styles +// + +.alert { + // scss-docs-start alert-css-vars + --#{$prefix}alert-bg: transparent; + --#{$prefix}alert-padding-x: #{$alert-padding-x}; + --#{$prefix}alert-padding-y: #{$alert-padding-y}; + --#{$prefix}alert-margin-bottom: #{$alert-margin-bottom}; + --#{$prefix}alert-color: inherit; + --#{$prefix}alert-border-color: transparent; + --#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color); + --#{$prefix}alert-border-radius: #{$alert-border-radius}; + --#{$prefix}alert-link-color: inherit; + // scss-docs-end alert-css-vars + + position: relative; + padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x); + margin-bottom: var(--#{$prefix}alert-margin-bottom); + color: var(--#{$prefix}alert-color); + background-color: var(--#{$prefix}alert-bg); + border: var(--#{$prefix}alert-border); + @include border-radius(var(--#{$prefix}alert-border-radius)); +} + +// Headings for larger alerts +.alert-heading { + // Specified to prevent conflicts of changing $headings-color + color: inherit; +} + +// Provide class for links that match alerts +.alert-link { + font-weight: $alert-link-font-weight; + color: var(--#{$prefix}alert-link-color); +} + + +// Dismissible alerts +// +// Expand the right padding and account for the close button's positioning. + +.alert-dismissible { + padding-right: $alert-dismissible-padding-r; + + // Adjust close link position + .btn-close { + position: absolute; + top: 0; + right: 0; + z-index: $stretched-link-z-index + 1; + padding: $alert-padding-y * 1.25 $alert-padding-x; + } +} + + +// scss-docs-start alert-modifiers +// Generate contextual modifier classes for colorizing the alert +@each $state in map-keys($theme-colors) { + .alert-#{$state} { + --#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis); + --#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle); + --#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle); + --#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis); + } +} +// scss-docs-end alert-modifiers diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_badge.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_badge.scss new file mode 100644 index 0000000..cc3d269 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_badge.scss @@ -0,0 +1,38 @@ +// Base class +// +// Requires one of the contextual, color modifier classes for `color` and +// `background-color`. + +.badge { + // scss-docs-start badge-css-vars + --#{$prefix}badge-padding-x: #{$badge-padding-x}; + --#{$prefix}badge-padding-y: #{$badge-padding-y}; + @include rfs($badge-font-size, --#{$prefix}badge-font-size); + --#{$prefix}badge-font-weight: #{$badge-font-weight}; + --#{$prefix}badge-color: #{$badge-color}; + --#{$prefix}badge-border-radius: #{$badge-border-radius}; + // scss-docs-end badge-css-vars + + display: inline-block; + padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x); + @include font-size(var(--#{$prefix}badge-font-size)); + font-weight: var(--#{$prefix}badge-font-weight); + line-height: 1; + color: var(--#{$prefix}badge-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + @include border-radius(var(--#{$prefix}badge-border-radius)); + @include gradient-bg(); + + // Empty badges collapse automatically + &:empty { + display: none; + } +} + +// Quick fix for badges in buttons +.btn .badge { + position: relative; + top: -1px; +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_breadcrumb.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_breadcrumb.scss new file mode 100644 index 0000000..b8252ff --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_breadcrumb.scss @@ -0,0 +1,40 @@ +.breadcrumb { + // scss-docs-start breadcrumb-css-vars + --#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x}; + --#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y}; + --#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom}; + @include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size); + --#{$prefix}breadcrumb-bg: #{$breadcrumb-bg}; + --#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius}; + --#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color}; + --#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x}; + --#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color}; + // scss-docs-end breadcrumb-css-vars + + display: flex; + flex-wrap: wrap; + padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x); + margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom); + @include font-size(var(--#{$prefix}breadcrumb-font-size)); + list-style: none; + background-color: var(--#{$prefix}breadcrumb-bg); + @include border-radius(var(--#{$prefix}breadcrumb-border-radius)); +} + +.breadcrumb-item { + // The separator between breadcrumbs (by default, a forward-slash: "/") + + .breadcrumb-item { + padding-left: var(--#{$prefix}breadcrumb-item-padding-x); + + &::before { + float: left; // Suppress inline spacings and underlining of the separator + padding-right: var(--#{$prefix}breadcrumb-item-padding-x); + color: var(--#{$prefix}breadcrumb-divider-color); + content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"}; + } + } + + &.active { + color: var(--#{$prefix}breadcrumb-item-active-color); + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_button-group.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_button-group.scss new file mode 100644 index 0000000..55ae3f6 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_button-group.scss @@ -0,0 +1,142 @@ +// Make the div behave like a button +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; // match .btn alignment given font-size hack above + + > .btn { + position: relative; + flex: 1 1 auto; + } + + // Bring the hover, focused, and "active" buttons to the front to overlay + // the borders properly + > .btn-check:checked + .btn, + > .btn-check:focus + .btn, + > .btn:hover, + > .btn:focus, + > .btn:active, + > .btn.active { + z-index: 1; + } +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + + .input-group { + width: auto; + } +} + +.btn-group { + @include border-radius($btn-border-radius); + + // Prevent double borders when buttons are next to each other + > :not(.btn-check:first-child) + .btn, + > .btn-group:not(:first-child) { + margin-left: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list + } + + // Reset rounded corners + > .btn:not(:last-child):not(.dropdown-toggle), + > .btn.dropdown-toggle-split:first-child, + > .btn-group:not(:last-child) > .btn { + @include border-end-radius(0); + } + + // The left radius should be 0 if the button is: + // - the "third or more" child + // - the second child and the previous element isn't `.btn-check` (making it the first child visually) + // - part of a btn-group which isn't the first child + > .btn:nth-child(n + 3), + > :not(.btn-check) + .btn, + > .btn-group:not(:first-child) > .btn { + @include border-start-radius(0); + } +} + +// Sizing +// +// Remix the default button sizing classes into new ones for easier manipulation. + +.btn-group-sm > .btn { @extend .btn-sm; } +.btn-group-lg > .btn { @extend .btn-lg; } + + +// +// Split button dropdowns +// + +.dropdown-toggle-split { + padding-right: $btn-padding-x * .75; + padding-left: $btn-padding-x * .75; + + &::after, + .dropup &::after, + .dropend &::after { + margin-left: 0; + } + + .dropstart &::before { + margin-right: 0; + } +} + +.btn-sm + .dropdown-toggle-split { + padding-right: $btn-padding-x-sm * .75; + padding-left: $btn-padding-x-sm * .75; +} + +.btn-lg + .dropdown-toggle-split { + padding-right: $btn-padding-x-lg * .75; + padding-left: $btn-padding-x-lg * .75; +} + + +// The clickable button for toggling the menu +// Set the same inset shadow as the :active state +.btn-group.show .dropdown-toggle { + @include box-shadow($btn-active-box-shadow); + + // Show no shadow for `.btn-link` since it has no other button styles. + &.btn-link { + @include box-shadow(none); + } +} + + +// +// Vertical button groups +// + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; + + > .btn, + > .btn-group { + width: 100%; + } + + > .btn:not(:first-child), + > .btn-group:not(:first-child) { + margin-top: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list + } + + // Reset rounded corners + > .btn:not(:last-child):not(.dropdown-toggle), + > .btn-group:not(:last-child) > .btn { + @include border-bottom-radius(0); + } + + > .btn ~ .btn, + > .btn-group:not(:first-child) > .btn { + @include border-top-radius(0); + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_buttons.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_buttons.scss new file mode 100644 index 0000000..caa4518 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_buttons.scss @@ -0,0 +1,216 @@ +// +// Base styles +// + +.btn { + // scss-docs-start btn-css-vars + --#{$prefix}btn-padding-x: #{$btn-padding-x}; + --#{$prefix}btn-padding-y: #{$btn-padding-y}; + --#{$prefix}btn-font-family: #{$btn-font-family}; + @include rfs($btn-font-size, --#{$prefix}btn-font-size); + --#{$prefix}btn-font-weight: #{$btn-font-weight}; + --#{$prefix}btn-line-height: #{$btn-line-height}; + --#{$prefix}btn-color: #{$btn-color}; + --#{$prefix}btn-bg: transparent; + --#{$prefix}btn-border-width: #{$btn-border-width}; + --#{$prefix}btn-border-color: transparent; + --#{$prefix}btn-border-radius: #{$btn-border-radius}; + --#{$prefix}btn-hover-border-color: transparent; + --#{$prefix}btn-box-shadow: #{$btn-box-shadow}; + --#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity}; + --#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5); + // scss-docs-end btn-css-vars + + display: inline-block; + padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x); + font-family: var(--#{$prefix}btn-font-family); + @include font-size(var(--#{$prefix}btn-font-size)); + font-weight: var(--#{$prefix}btn-font-weight); + line-height: var(--#{$prefix}btn-line-height); + color: var(--#{$prefix}btn-color); + text-align: center; + text-decoration: if($link-decoration == none, null, none); + white-space: $btn-white-space; + vertical-align: middle; + cursor: if($enable-button-pointers, pointer, null); + user-select: none; + border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color); + @include border-radius(var(--#{$prefix}btn-border-radius)); + @include gradient-bg(var(--#{$prefix}btn-bg)); + @include box-shadow(var(--#{$prefix}btn-box-shadow)); + @include transition($btn-transition); + + &:hover { + color: var(--#{$prefix}btn-hover-color); + text-decoration: if($link-hover-decoration == underline, none, null); + background-color: var(--#{$prefix}btn-hover-bg); + border-color: var(--#{$prefix}btn-hover-border-color); + } + + .btn-check + &:hover { + // override for the checkbox/radio buttons + color: var(--#{$prefix}btn-color); + background-color: var(--#{$prefix}btn-bg); + border-color: var(--#{$prefix}btn-border-color); + } + + &:focus-visible { + color: var(--#{$prefix}btn-hover-color); + @include gradient-bg(var(--#{$prefix}btn-hover-bg)); + border-color: var(--#{$prefix}btn-hover-border-color); + outline: 0; + // Avoid using mixin so we can pass custom focus shadow properly + @if $enable-shadows { + box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); + } @else { + box-shadow: var(--#{$prefix}btn-focus-box-shadow); + } + } + + .btn-check:focus-visible + & { + border-color: var(--#{$prefix}btn-hover-border-color); + outline: 0; + // Avoid using mixin so we can pass custom focus shadow properly + @if $enable-shadows { + box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow); + } @else { + box-shadow: var(--#{$prefix}btn-focus-box-shadow); + } + } + + .btn-check:checked + &, + :not(.btn-check) + &:active, + &:first-child:active, + &.active, + &.show { + color: var(--#{$prefix}btn-active-color); + background-color: var(--#{$prefix}btn-active-bg); + // Remove CSS gradients if they're enabled + background-image: if($enable-gradients, none, null); + border-color: var(--#{$prefix}btn-active-border-color); + @include box-shadow(var(--#{$prefix}btn-active-shadow)); + + &:focus-visible { + // Avoid using mixin so we can pass custom focus shadow properly + @if $enable-shadows { + box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); + } @else { + box-shadow: var(--#{$prefix}btn-focus-box-shadow); + } + } + } + + .btn-check:checked:focus-visible + & { + // Avoid using mixin so we can pass custom focus shadow properly + @if $enable-shadows { + box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow); + } @else { + box-shadow: var(--#{$prefix}btn-focus-box-shadow); + } + } + + &:disabled, + &.disabled, + fieldset:disabled & { + color: var(--#{$prefix}btn-disabled-color); + pointer-events: none; + background-color: var(--#{$prefix}btn-disabled-bg); + background-image: if($enable-gradients, none, null); + border-color: var(--#{$prefix}btn-disabled-border-color); + opacity: var(--#{$prefix}btn-disabled-opacity); + @include box-shadow(none); + } +} + + +// +// Alternate buttons +// + +// scss-docs-start btn-variant-loops +@each $color, $value in $theme-colors { + .btn-#{$color} { + @if $color == "light" { + @include button-variant( + $value, + $value, + $hover-background: shade-color($value, $btn-hover-bg-shade-amount), + $hover-border: shade-color($value, $btn-hover-border-shade-amount), + $active-background: shade-color($value, $btn-active-bg-shade-amount), + $active-border: shade-color($value, $btn-active-border-shade-amount) + ); + } @else if $color == "dark" { + @include button-variant( + $value, + $value, + $hover-background: tint-color($value, $btn-hover-bg-tint-amount), + $hover-border: tint-color($value, $btn-hover-border-tint-amount), + $active-background: tint-color($value, $btn-active-bg-tint-amount), + $active-border: tint-color($value, $btn-active-border-tint-amount) + ); + } @else { + @include button-variant($value, $value); + } + } +} + +@each $color, $value in $theme-colors { + .btn-outline-#{$color} { + @include button-outline-variant($value); + } +} +// scss-docs-end btn-variant-loops + + +// +// Link buttons +// + +// Make a button look and behave like a link +.btn-link { + --#{$prefix}btn-font-weight: #{$font-weight-normal}; + --#{$prefix}btn-color: #{$btn-link-color}; + --#{$prefix}btn-bg: transparent; + --#{$prefix}btn-border-color: transparent; + --#{$prefix}btn-hover-color: #{$btn-link-hover-color}; + --#{$prefix}btn-hover-border-color: transparent; + --#{$prefix}btn-active-color: #{$btn-link-hover-color}; + --#{$prefix}btn-active-border-color: transparent; + --#{$prefix}btn-disabled-color: #{$btn-link-disabled-color}; + --#{$prefix}btn-disabled-border-color: transparent; + --#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows + --#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb}; + + text-decoration: $link-decoration; + @if $enable-gradients { + background-image: none; + } + + &:hover, + &:focus-visible { + text-decoration: $link-hover-decoration; + } + + &:focus-visible { + color: var(--#{$prefix}btn-color); + } + + &:hover { + color: var(--#{$prefix}btn-hover-color); + } + + // No need for an active state here +} + + +// +// Button Sizes +// + +.btn-lg { + @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg); +} + +.btn-sm { + @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm); +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_card.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_card.scss new file mode 100644 index 0000000..d3535a9 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_card.scss @@ -0,0 +1,239 @@ +// +// Base styles +// + +.card { + // scss-docs-start card-css-vars + --#{$prefix}card-spacer-y: #{$card-spacer-y}; + --#{$prefix}card-spacer-x: #{$card-spacer-x}; + --#{$prefix}card-title-spacer-y: #{$card-title-spacer-y}; + --#{$prefix}card-title-color: #{$card-title-color}; + --#{$prefix}card-subtitle-color: #{$card-subtitle-color}; + --#{$prefix}card-border-width: #{$card-border-width}; + --#{$prefix}card-border-color: #{$card-border-color}; + --#{$prefix}card-border-radius: #{$card-border-radius}; + --#{$prefix}card-box-shadow: #{$card-box-shadow}; + --#{$prefix}card-inner-border-radius: #{$card-inner-border-radius}; + --#{$prefix}card-cap-padding-y: #{$card-cap-padding-y}; + --#{$prefix}card-cap-padding-x: #{$card-cap-padding-x}; + --#{$prefix}card-cap-bg: #{$card-cap-bg}; + --#{$prefix}card-cap-color: #{$card-cap-color}; + --#{$prefix}card-height: #{$card-height}; + --#{$prefix}card-color: #{$card-color}; + --#{$prefix}card-bg: #{$card-bg}; + --#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding}; + --#{$prefix}card-group-margin: #{$card-group-margin}; + // scss-docs-end card-css-vars + + position: relative; + display: flex; + flex-direction: column; + min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106 + height: var(--#{$prefix}card-height); + color: var(--#{$prefix}body-color); + word-wrap: break-word; + background-color: var(--#{$prefix}card-bg); + background-clip: border-box; + border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + @include border-radius(var(--#{$prefix}card-border-radius)); + @include box-shadow(var(--#{$prefix}card-box-shadow)); + + > hr { + margin-right: 0; + margin-left: 0; + } + + > .list-group { + border-top: inherit; + border-bottom: inherit; + + &:first-child { + border-top-width: 0; + @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); + } + + &:last-child { + border-bottom-width: 0; + @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); + } + } + + // Due to specificity of the above selector (`.card > .list-group`), we must + // use a child selector here to prevent double borders. + > .card-header + .list-group, + > .list-group + .card-footer { + border-top: 0; + } +} + +.card-body { + // Enable `flex-grow: 1` for decks and groups so that card blocks take up + // as much space as possible, ensuring footers are aligned to the bottom. + flex: 1 1 auto; + padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x); + color: var(--#{$prefix}card-color); +} + +.card-title { + margin-bottom: var(--#{$prefix}card-title-spacer-y); + color: var(--#{$prefix}card-title-color); +} + +.card-subtitle { + margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); // stylelint-disable-line function-disallowed-list + margin-bottom: 0; + color: var(--#{$prefix}card-subtitle-color); +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link { + &:hover { + text-decoration: if($link-hover-decoration == underline, none, null); + } + + + .card-link { + margin-left: var(--#{$prefix}card-spacer-x); + } +} + +// +// Optional textual caps +// + +.card-header { + padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); + margin-bottom: 0; // Removes the default margin-bottom of + color: var(--#{$prefix}card-cap-color); + background-color: var(--#{$prefix}card-cap-bg); + border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + + &:first-child { + @include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0); + } +} + +.card-footer { + padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x); + color: var(--#{$prefix}card-cap-color); + background-color: var(--#{$prefix}card-cap-bg); + border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color); + + &:last-child { + @include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius)); + } +} + + +// +// Header navs +// + +.card-header-tabs { + margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list + margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); // stylelint-disable-line function-disallowed-list + margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list + border-bottom: 0; + + .nav-link.active { + background-color: var(--#{$prefix}card-bg); + border-bottom-color: var(--#{$prefix}card-bg); + } +} + +.card-header-pills { + margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list + margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list +} + +// Card image +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: var(--#{$prefix}card-img-overlay-padding); + @include border-radius(var(--#{$prefix}card-inner-border-radius)); +} + +.card-img, +.card-img-top, +.card-img-bottom { + width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch +} + +.card-img, +.card-img-top { + @include border-top-radius(var(--#{$prefix}card-inner-border-radius)); +} + +.card-img, +.card-img-bottom { + @include border-bottom-radius(var(--#{$prefix}card-inner-border-radius)); +} + + +// +// Card groups +// + +.card-group { + // The child selector allows nested `.card` within `.card-group` + // to display properly. + > .card { + margin-bottom: var(--#{$prefix}card-group-margin); + } + + @include media-breakpoint-up(sm) { + display: flex; + flex-flow: row wrap; + // The child selector allows nested `.card` within `.card-group` + // to display properly. + > .card { + // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4 + flex: 1 0 0%; + margin-bottom: 0; + + + .card { + margin-left: 0; + border-left: 0; + } + + // Handle rounded corners + @if $enable-rounded { + &:not(:last-child) { + @include border-end-radius(0); + + .card-img-top, + .card-header { + // stylelint-disable-next-line property-disallowed-list + border-top-right-radius: 0; + } + .card-img-bottom, + .card-footer { + // stylelint-disable-next-line property-disallowed-list + border-bottom-right-radius: 0; + } + } + + &:not(:first-child) { + @include border-start-radius(0); + + .card-img-top, + .card-header { + // stylelint-disable-next-line property-disallowed-list + border-top-left-radius: 0; + } + .card-img-bottom, + .card-footer { + // stylelint-disable-next-line property-disallowed-list + border-bottom-left-radius: 0; + } + } + } + } + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_carousel.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_carousel.scss new file mode 100644 index 0000000..3a13522 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_carousel.scss @@ -0,0 +1,236 @@ +// Notes on the classes: +// +// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically) +// even when their scroll action started on a carousel, but for compatibility (with Firefox) +// we're preventing all actions instead +// 2. The .carousel-item-start and .carousel-item-end is used to indicate where +// the active slide is heading. +// 3. .active.carousel-item is the current slide. +// 4. .active.carousel-item-start and .active.carousel-item-end is the current +// slide in its in-transition state. Only one of these occurs at a time. +// 5. .carousel-item-next.carousel-item-start and .carousel-item-prev.carousel-item-end +// is the upcoming slide in transition. + +.carousel { + position: relative; +} + +.carousel.pointer-event { + touch-action: pan-y; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; + @include clearfix(); +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + backface-visibility: hidden; + @include transition($carousel-transition); +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; +} + +.carousel-item-next:not(.carousel-item-start), +.active.carousel-item-end { + transform: translateX(100%); +} + +.carousel-item-prev:not(.carousel-item-end), +.active.carousel-item-start { + transform: translateX(-100%); +} + + +// +// Alternate transitions +// + +.carousel-fade { + .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; + } + + .carousel-item.active, + .carousel-item-next.carousel-item-start, + .carousel-item-prev.carousel-item-end { + z-index: 1; + opacity: 1; + } + + .active.carousel-item-start, + .active.carousel-item-end { + z-index: 0; + opacity: 0; + @include transition(opacity 0s $carousel-transition-duration); + } +} + + +// +// Left/right controls for nav +// + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + // Use flex for alignment (1-3) + display: flex; // 1. allow flex styles + align-items: center; // 2. vertically center contents + justify-content: center; // 3. horizontally center contents + width: $carousel-control-width; + padding: 0; + color: $carousel-control-color; + text-align: center; + background: none; + border: 0; + opacity: $carousel-control-opacity; + @include transition($carousel-control-transition); + + // Hover/focus state + &:hover, + &:focus { + color: $carousel-control-color; + text-decoration: none; + outline: 0; + opacity: $carousel-control-hover-opacity; + } +} +.carousel-control-prev { + left: 0; + background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null); +} +.carousel-control-next { + right: 0; + background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null); +} + +// Icons for within +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: $carousel-control-icon-width; + height: $carousel-control-icon-width; + background-repeat: no-repeat; + background-position: 50%; + background-size: 100% 100%; +} + +.carousel-control-prev-icon { + background-image: escape-svg($carousel-control-prev-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-next-icon-bg) + "*/"}; +} +.carousel-control-next-icon { + background-image: escape-svg($carousel-control-next-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-prev-icon-bg) + "*/"}; +} + +// Optional indicator pips/controls +// +// Add a container (such as a list) with the following class and add an item (ideally a focusable control, +// like a button) with data-bs-target for each slide your carousel holds. + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 2; + display: flex; + justify-content: center; + padding: 0; + // Use the .carousel-control's width as margin so we don't overlay those + margin-right: $carousel-control-width; + margin-bottom: 1rem; + margin-left: $carousel-control-width; + + [data-bs-target] { + box-sizing: content-box; + flex: 0 1 auto; + width: $carousel-indicator-width; + height: $carousel-indicator-height; + padding: 0; + margin-right: $carousel-indicator-spacer; + margin-left: $carousel-indicator-spacer; + text-indent: -999px; + cursor: pointer; + background-color: $carousel-indicator-active-bg; + background-clip: padding-box; + border: 0; + // Use transparent borders to increase the hit area by 10px on top and bottom. + border-top: $carousel-indicator-hit-area-height solid transparent; + border-bottom: $carousel-indicator-hit-area-height solid transparent; + opacity: $carousel-indicator-opacity; + @include transition($carousel-indicator-transition); + } + + .active { + opacity: $carousel-indicator-active-opacity; + } +} + + +// Optional captions +// +// + +.carousel-caption { + position: absolute; + right: (100% - $carousel-caption-width) * .5; + bottom: $carousel-caption-spacer; + left: (100% - $carousel-caption-width) * .5; + padding-top: $carousel-caption-padding-y; + padding-bottom: $carousel-caption-padding-y; + color: $carousel-caption-color; + text-align: center; +} + +// Dark mode carousel + +@mixin carousel-dark() { + .carousel-control-prev-icon, + .carousel-control-next-icon { + filter: $carousel-dark-control-icon-filter; + } + + .carousel-indicators [data-bs-target] { + background-color: $carousel-dark-indicator-active-bg; + } + + .carousel-caption { + color: $carousel-dark-caption-color; + } +} + +.carousel-dark { + @include carousel-dark(); +} + +@if $enable-dark-mode { + @include color-mode(dark) { + @if $color-mode-type == "media-query" { + .carousel { + @include carousel-dark(); + } + } @else { + .carousel, + &.carousel { + @include carousel-dark(); + } + } + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_close.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_close.scss new file mode 100644 index 0000000..4d6e73c --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_close.scss @@ -0,0 +1,63 @@ +// Transparent background and border properties included for button version. +// iOS requires the button element instead of an anchor tag. +// If you want the anchor version, it requires `href="#"`. +// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile + +.btn-close { + // scss-docs-start close-css-vars + --#{$prefix}btn-close-color: #{$btn-close-color}; + --#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg) }; + --#{$prefix}btn-close-opacity: #{$btn-close-opacity}; + --#{$prefix}btn-close-hover-opacity: #{$btn-close-hover-opacity}; + --#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow}; + --#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity}; + --#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity}; + --#{$prefix}btn-close-white-filter: #{$btn-close-white-filter}; + // scss-docs-end close-css-vars + + box-sizing: content-box; + width: $btn-close-width; + height: $btn-close-height; + padding: $btn-close-padding-y $btn-close-padding-x; + color: var(--#{$prefix}btn-close-color); + background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements + border: 0; // for button elements + @include border-radius(); + opacity: var(--#{$prefix}btn-close-opacity); + + // Override 's hover style + &:hover { + color: var(--#{$prefix}btn-close-color); + text-decoration: none; + opacity: var(--#{$prefix}btn-close-hover-opacity); + } + + &:focus { + outline: 0; + box-shadow: var(--#{$prefix}btn-close-focus-shadow); + opacity: var(--#{$prefix}btn-close-focus-opacity); + } + + &:disabled, + &.disabled { + pointer-events: none; + user-select: none; + opacity: var(--#{$prefix}btn-close-disabled-opacity); + } +} + +@mixin btn-close-white() { + filter: var(--#{$prefix}btn-close-white-filter); +} + +.btn-close-white { + @include btn-close-white(); +} + +@if $enable-dark-mode { + @include color-mode(dark) { + .btn-close { + @include btn-close-white(); + } + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_containers.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_containers.scss new file mode 100644 index 0000000..83b3138 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_containers.scss @@ -0,0 +1,41 @@ +// Container widths +// +// Set the container width, and override it for fixed navbars in media queries. + +@if $enable-container-classes { + // Single container class with breakpoint max-widths + .container, + // 100% wide container at all breakpoints + .container-fluid { + @include make-container(); + } + + // Responsive containers that are 100% wide until a breakpoint + @each $breakpoint, $container-max-width in $container-max-widths { + .container-#{$breakpoint} { + @extend .container-fluid; + } + + @include media-breakpoint-up($breakpoint, $grid-breakpoints) { + %responsive-container-#{$breakpoint} { + max-width: $container-max-width; + } + + // Extend each breakpoint which is smaller or equal to the current breakpoint + $extend-breakpoint: true; + + @each $name, $width in $grid-breakpoints { + @if ($extend-breakpoint) { + .container#{breakpoint-infix($name, $grid-breakpoints)} { + @extend %responsive-container-#{$breakpoint}; + } + + // Once the current breakpoint is reached, stop extending + @if ($breakpoint == $name) { + $extend-breakpoint: false; + } + } + } + } + } +} diff --git a/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_dropdown.scss b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_dropdown.scss new file mode 100644 index 0000000..587ebb4 --- /dev/null +++ b/extensions/pagetop-bootsier/static/bootstrap-5.3.3/_dropdown.scss @@ -0,0 +1,250 @@ +// The dropdown wrapper (`
`) +.dropup, +.dropend, +.dropdown, +.dropstart, +.dropup-center, +.dropdown-center { + position: relative; +} + +.dropdown-toggle { + white-space: nowrap; + + // Generate the caret automatically + @include caret(); +} + +// The dropdown menu +.dropdown-menu { + // scss-docs-start dropdown-css-vars + --#{$prefix}dropdown-zindex: #{$zindex-dropdown}; + --#{$prefix}dropdown-min-width: #{$dropdown-min-width}; + --#{$prefix}dropdown-padding-x: #{$dropdown-padding-x}; + --#{$prefix}dropdown-padding-y: #{$dropdown-padding-y}; + --#{$prefix}dropdown-spacer: #{$dropdown-spacer}; + @include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size); + --#{$prefix}dropdown-color: #{$dropdown-color}; + --#{$prefix}dropdown-bg: #{$dropdown-bg}; + --#{$prefix}dropdown-border-color: #{$dropdown-border-color}; + --#{$prefix}dropdown-border-radius: #{$dropdown-border-radius}; + --#{$prefix}dropdown-border-width: #{$dropdown-border-width}; + --#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius}; + --#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg}; + --#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y}; + --#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow}; + --#{$prefix}dropdown-link-color: #{$dropdown-link-color}; + --#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color}; + --#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg}; + --#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color}; + --#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg}; + --#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color}; + --#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x}; + --#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y}; + --#{$prefix}dropdown-header-color: #{$dropdown-header-color}; + --#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x}; + --#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y}; + // scss-docs-end dropdown-css-vars + + position: absolute; + z-index: var(--#{$prefix}dropdown-zindex); + display: none; // none by default, but block on "open" of the menu + min-width: var(--#{$prefix}dropdown-min-width); + padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x); + margin: 0; // Override default margin of ul + @include font-size(var(--#{$prefix}dropdown-font-size)); + color: var(--#{$prefix}dropdown-color); + text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) + list-style: none; + background-color: var(--#{$prefix}dropdown-bg); + background-clip: padding-box; + border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color); + @include border-radius(var(--#{$prefix}dropdown-border-radius)); + @include box-shadow(var(--#{$prefix}dropdown-box-shadow)); + + &[data-bs-popper] { + top: 100%; + left: 0; + margin-top: var(--#{$prefix}dropdown-spacer); + } + + @if $dropdown-padding-y == 0 { + > .dropdown-item:first-child, + > li:first-child .dropdown-item { + @include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius)); + } + > .dropdown-item:last-child, + > li:last-child .dropdown-item { + @include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius)); + } + + } +} + +// scss-docs-start responsive-breakpoints +// We deliberately hardcode the `bs-` prefix because we check +// this custom property in JS to determine Popper's positioning + +@each $breakpoint in map-keys($grid-breakpoints) { + @include media-breakpoint-up($breakpoint) { + $infix: breakpoint-infix($breakpoint, $grid-breakpoints); + + .dropdown-menu#{$infix}-start { + --bs-position: start; + + &[data-bs-popper] { + right: auto; + left: 0; + } + } + + .dropdown-menu#{$infix}-end { + --bs-position: end; + + &[data-bs-popper] { + right: 0; + left: auto; + } + } + } +} +// scss-docs-end responsive-breakpoints + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// Just add .dropup after the standard .dropdown class and you're set. +.dropup { + .dropdown-menu[data-bs-popper] { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: var(--#{$prefix}dropdown-spacer); + } + + .dropdown-toggle { + @include caret(up); + } +} + +.dropend { + .dropdown-menu[data-bs-popper] { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: var(--#{$prefix}dropdown-spacer); + } + + .dropdown-toggle { + @include caret(end); + &::after { + vertical-align: 0; + } + } +} + +.dropstart { + .dropdown-menu[data-bs-popper] { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: var(--#{$prefix}dropdown-spacer); + } + + .dropdown-toggle { + @include caret(start); + &::before { + vertical-align: 0; + } + } +} + + +// Dividers (basically an `
`) within the dropdown +.dropdown-divider { + height: 0; + margin: var(--#{$prefix}dropdown-divider-margin-y) 0; + overflow: hidden; + border-top: 1px solid var(--#{$prefix}dropdown-divider-bg); + opacity: 1; // Revisit in v6 to de-dupe styles that conflict with
element +} + +// Links, buttons, and more within the dropdown menu +// +// `