diff --git a/.cargo/cliff.toml b/.cargo/cliff.toml deleted file mode 100644 index cbbb57b6..00000000 --- a/.cargo/cliff.toml +++ /dev/null @@ -1,69 +0,0 @@ -# cliff.toml - -[changelog] -header = """ -# CHANGELOG - -Este archivo documenta los cambios más relevantes realizados en cada versión. El formato está basado -en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/), y las versiones se numeran siguiendo -las reglas del [Versionado Semántico](https://semver.org/lang/es/). - -Resume la evolución del proyecto para usuarios y colaboradores, destacando nuevas funcionalidades, -correcciones, mejoras durante el desarrollo o cambios en la documentación. Cambios menores o -internos pueden omitirse si no afectan al uso del proyecto. -""" -trim = true -render_always = true - -body = """ -{% if version %} -## {{ version | trim_start_matches(pat="v") }} ({{ timestamp | date(format="%Y-%m-%d") }}) -{% else %} -## Pendiente de publicación -{% endif %}\ -{% for group, commits in commits | group_by(attribute="group") %} -### {{ group | upper_first }} - -{% for commit in commits %} -{%- set msg = commit.message - | split(pat="\n") - | first - | replace(from="✨ ", to="") - | replace(from="🐛 ", to="") - | replace(from="🚑 ", to="") - | replace(from="⬆️ ", to="") - | replace(from="🚧 ", to="") - | replace(from="♻️ ", to="") - | replace(from="✏️ ", to="") - | replace(from="🏷️ ", to="") - | replace(from="🧑‍💻 ", to="") - | replace(from="🍱 ", to="") - | replace(from="📝 ", to="") - | replace(from="💡 ", to="") --%} - -- {{ msg | trim }} {% if commit.author.name != "Manuel Cillero" %} - {{ commit.author.name }}{% endif %} -{% endfor %}{% endfor %} -""" - -[git] -conventional_commits = false -filter_unconventional = false -topo_order_commits = true -sort_commits = "oldest" - -commit_parsers = [ - { message = "^✨", group = "Añadido" }, - { message = "^🐛", group = "Corregido" }, - { message = "^🚑", group = "Corregido" }, - { message = "^🚧", group = "Cambiado" }, - { message = "^♻️", group = "Cambiado" }, - { message = "^✏️", group = "Cambiado" }, - { message = "^🏷️", group = "Cambiado" }, - { message = "^🧑‍💻", group = "Cambiado" }, - { message = "^🍱", group = "Cambiado" }, - { message = "^⬆️", group = "Dependencias" }, - { message = "^📝", group = "Documentado" }, - { message = "^💡", group = "Documentado" }, - { message = "^.*", group = "Otros cambios" }, -] diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index d29b0de3..00000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[alias] -ts = ["test", "--features", "testing"] # cargo ts -tw = ["test", "--workspace", "--features", "testing"] # cargo tw diff --git a/.cargo/release.toml b/.cargo/release.toml deleted file mode 100644 index 68f7a9cc..00000000 --- a/.cargo/release.toml +++ /dev/null @@ -1,25 +0,0 @@ -# release.toml - -# Etiqueta por crate: `pagetop-macros-v0.2.0` -tag-prefix = "{{crate_name}}-" - -# Confirmaciones firmadas (no requeridas) -sign-commit = false -sign-tag = false - -# Empuja etiquetas y commits -push = true - -# Publica en crates.io (puedes desactivarlo para pruebas) -publish = true - -# Solo permite publicar estos crates (los que forman parte del workspace) -allow-branch = ["main"] -consolidate-commits = false - -# Mensaje personalizado para el commit de versión -pre-release-commit-message = "🔖 Prepara publicación de {{crate_name}} {{version}}" - -pre-release-hook = [ - "sh", "-c", "ROOT=$(git rev-parse --show-toplevel) && \"$ROOT/tools/changelog.sh\" {{crate_name}} {{version}} --stage" -] diff --git a/.gitignore b/.gitignore index 65db440e..e0e82dec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ -# Ignora directorios de compilación **/target - -# Archivos de log **/log/*.log* - -# Archivos de configuración locales **/local.*.toml **/local.toml -.env +workdir diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index bfc9067a..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,86 +0,0 @@ -# CHANGELOG - -Este archivo documenta los cambios más relevantes realizados en cada versión. El formato está basado -en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/), y las versiones se numeran siguiendo -las reglas del [Versionado Semántico](https://semver.org/lang/es/). - -Resume la evolución del proyecto para usuarios y colaboradores, destacando nuevas funcionalidades, -correcciones, mejoras durante el desarrollo o cambios en la documentación. Cambios menores o -internos pueden omitirse si no afectan al uso del proyecto. - -## 0.4.0 (2025-09-20) - -### Añadido - -- [app] Añade manejo de rutas no encontradas -- [context] Añade métodos auxiliares de parámetros -- [util] Añade `indoc` para indentar código bien -- Añade componente `PoweredBy` para copyright - -### Cambiado - -- [html] Cambia tipos `Option...` por `Attr...` -- [html] Implementa `Default` en `Context` -- [welcome] Crea página de bienvenida desde intro -- [context] Generaliza los parámetros de contexto -- [context] Define un `trait` común de contexto -- Modifica tipos para atributos HTML a minúsculas -- Renombra `with_component` por `add_child` - -### Corregido - -- [welcome] Corrige giro botón con ancho estrecho -- [welcome] Corrige centrado del pie de página -- Corrige nombre de función en prueba de `Html` -- Corrige doc y código por cambios en Page - -### Dependencias - -- Actualiza dependencias para 0.4.0 - -### Documentado - -- [component] Amplía documentación de preparación -- Normaliza referencias al nombre PageTop -- Simplifica documentación de obsoletos -- Mejora la documentación de recursos y contexto - -### Otros cambios - -- 🎨 [theme] Mejora gestión de regiones en páginas -- ✅ [tests] Amplía pruebas para `PrepareMarkup' -- 🎨 [locale] Mejora el uso de `lookup` / `using` -- 🔨 [tools] Fuerza pulsar intro para confirmar input -- 💄 Aplica BEM a estilos de bienvenida y componente -- 🎨 Unifica conversiones a String con `to_string()` -- 🔥 Elimina `Render` para usar siempre el contexto - -## 0.3.0 (2025-08-16) - -### Cambiado - -- Redefine función para directorios absolutos -- Mejora la integración de archivos estáticos - -### Documentado - -- Cambia el formato para la documentación - -## 0.2.0 (2025-08-09) - -### Añadido - -- Añade librería para gestionar recursos estáticos -- Añade soporte a changelog de `pagetop-statics` - -### Documentado - -- Corrige enlace del botón de licencia en la documentación - -### Otros cambios - -- Afina Cargo.toml para buscar la mejor categoría - -## 0.1.0 (2025-08-06) - -- Versión inicial diff --git a/CREDITS.md b/CREDITS.md index c5a7bd2e..c10822d5 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,33 +1,46 @@ -# 🔃 Dependencias +# 🔃 Dependencies -PageTop está basado en [Rust](https://www.rust-lang.org/) y crece a hombros de gigantes aprovechando -algunas de las librerías más robustas y populares del [ecosistema Rust](https://lib.rs) como son: +PageTop is developed in the [Rust programming language](https://www.rust-lang.org/) and stands on +the shoulders of giants by leveraging some of the most robust and renowned libraries (*crates*) from +the [Rust ecosystem](https://lib.rs), including: - * [Actix Web](https://actix.rs/) para los servicios web. - * [Config](https://docs.rs/config) para cargar y procesar las opciones de configuración. - * [Tracing](https://github.com/tokio-rs/tracing) para la gestión de trazas y registro de eventos - de la aplicación. - * [Fluent templates](https://github.com/XAMPPRocky/fluent-templates), que integra - [Fluent](https://projectfluent.org/) para internacionalizar las aplicaciones. - * Además de otros *crates* adicionales que se pueden explorar en los archivos `Cargo.toml` de - PageTop y sus extensiones. +* [Actix Web](https://actix.rs/) for web services and server management. +* [Tracing](https://github.com/tokio-rs/tracing) for diagnostics and structured logging. +* [Fluent templates](https://github.com/XAMPPRocky/fluent-templates), which integrate + [Fluent](https://projectfluent.org/) for internationalization. +* Additional crates, which you can explore in the `Cargo.toml` files of PageTop and its packages. + + +# ⌨️ Code + +PageTop includes code from [config-rs](https://crates.io/crates/config) (version +[0.11.0](https://github.com/mehcode/config-rs/tree/0.11.0)) by +[Ryan Leckey](https://crates.io/users/mehcode), chosen for its advantages in reading configuration +settings and delegating assignment to safe types, tailored to the specific needs of each package, +theme, or application. # 🗚 FIGfonts -PageTop usa el *crate* [figlet-rs](https://crates.io/crates/figlet-rs) desarrollado por *yuanbohan* -para mostrar un banner de presentación en el terminal con el nombre de la aplicación en caracteres -[FIGlet](http://www.figlet.org). Las fuentes incluidas en `pagetop/src/app` son: +PageTop uses the [figlet-rs](https://crates.io/crates/figlet-rs) package by *yuanbohan* to display a +presentation banner in the terminal featuring the application's name in +[FIGlet](http://www.figlet.org) characters. The fonts included in `pagetop/src/app` are: - * [slant.flf](http://www.figlet.org/fontdb_example.cgi?font=slant.flf) de *Glenn Chappell* - * [small.flf](http://www.figlet.org/fontdb_example.cgi?font=small.flf) de *Glenn Chappell* - (predeterminada) - * [speed.flf](http://www.figlet.org/fontdb_example.cgi?font=speed.flf) de *Claude Martins* - * [starwars.flf](http://www.figlet.org/fontdb_example.cgi?font=starwars.flf) de *Ryan Youck* +* [slant.flf](http://www.figlet.org/fontdb_example.cgi?font=slant.flf) by *Glenn Chappell* +* [small.flf](http://www.figlet.org/fontdb_example.cgi?font=small.flf) by *Glenn Chappell* (default) +* [speed.flf](http://www.figlet.org/fontdb_example.cgi?font=speed.flf) by *Claude Martins* +* [starwars.flf](http://www.figlet.org/fontdb_example.cgi?font=starwars.flf) by *Ryan Youck* -# 🎨 Icono +# 📰 Templates -"La Criatura" sonriente es una simpática creación de [Webalys](https://www.iconfinder.com/webalys). -Forma parte de su colección [Nasty Icons](https://www.iconfinder.com/iconsets/nasty), disponible en +The default welcome homepage design is inspired by a tutorial for creating a unique +[Neobrutalism](https://www.codewithfaraz.com/content/109/creating-a-unique-neobrutalism-portfolio-page-with-html-css-and-javascript) +portfolio page by [Faraz](https://www.codewithfaraz.com/). + + +# 🎨 Icon + +"The Creature" smiling is a playful creation by [Webalys](https://www.iconfinder.com/webalys). It is +part of their [Nasty Icons](https://www.iconfinder.com/iconsets/nasty) collection, available on [ICONFINDER](https://www.iconfinder.com). diff --git a/Cargo.lock b/Cargo.lock index 2f9fa42e..27c4a241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "actix-codec" @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "actix-files" -version = "0.6.8" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0d87f10d70e2948ad40e8edea79c8e77c6c66e0250a4c1f09b690465199576" +checksum = "0773d59061dedb49a8aed04c67291b9d8cf2fe0b60130a381aab53c6dd86e9be" dependencies = [ "actix-http", "actix-service", @@ -31,7 +31,7 @@ dependencies = [ "actix-web", "bitflags", "bytes", - "derive_more 2.0.1", + "derive_more 0.99.18", "futures-core", "http-range", "log", @@ -44,23 +44,23 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" +checksum = "d48f96fc3003717aeb9856ca3d02a8c7de502667ad76eeacd830b48d2e91fac4" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", + "ahash", "base64 0.22.1", "bitflags", "brotli", "bytes", "bytestring", - "derive_more 2.0.1", + "derive_more 0.99.18", "encoding_rs", "flate2", - "foldhash", "futures-core", "h2", "http", @@ -72,7 +72,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand 0.9.2", + "rand", "sha1", "smallvec", "tokio", @@ -88,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn", + "syn 2.0.90", ] [[package]] @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "futures-core", "tokio", @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "actix-server" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +checksum = "7ca2549781d8dd6d75c40cf6b6051260a2cc2f3c62343d761a969a0640646894" dependencies = [ "actix-rt", "actix-service", @@ -128,33 +128,34 @@ dependencies = [ "futures-core", "futures-util", "mio", - "socket2 0.5.10", + "socket2", "tokio", "tracing", ] [[package]] name = "actix-service" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" dependencies = [ "futures-core", + "paste", "pin-project-lite", ] [[package]] name = "actix-session" -version = "0.11.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "400c27fd4cdbe0082b7bbd29ac44a3070cbda1b2114138dc106ba39fe2f90dff" +checksum = "efe6976a74f34f1b6d07a6c05aadc0ed0359304a7781c367fa5b4029418db08f" dependencies = [ "actix-service", "actix-utils", "actix-web", "anyhow", - "derive_more 2.0.1", - "rand 0.9.2", + "derive_more 1.0.0", + "rand", "serde", "serde_json", "tracing", @@ -172,9 +173,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.11.0" +version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" dependencies = [ "actix-codec", "actix-http", @@ -185,13 +186,13 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", + "ahash", "bytes", "bytestring", "cfg-if", "cookie", - "derive_more 2.0.1", + "derive_more 0.99.18", "encoding_rs", - "foldhash", "futures-core", "futures-util", "impl-more", @@ -207,9 +208,8 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.10", + "socket2", "time", - "tracing", "url", ] @@ -222,23 +222,35 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn", + "syn 2.0.90", +] + +[[package]] +name = "actix-web-static-files" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf6d1ef6d7a60e084f9e0595e2a5234abda14e76c105ecf8e2d0e8800c41a1f" +dependencies = [ + "actix-web", + "derive_more 0.99.18", + "futures-util", + "static-files", ] [[package]] name = "addr2line" -version = "0.25.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -277,11 +289,12 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.12" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -313,9 +326,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.21" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" @@ -328,9 +347,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -343,56 +362,55 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.76" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", @@ -400,7 +418,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link", + "windows-targets 0.52.6", ] [[package]] @@ -417,9 +435,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -432,9 +450,9 @@ dependencies = [ [[package]] name = "brotli" -version = "8.0.2" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -443,9 +461,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "5.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -453,9 +471,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", "serde", @@ -463,32 +481,37 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bytestring" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ "bytes", ] [[package]] name = "cc" -version = "1.2.41" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ - "find-msvc-tools", "jobserver", "libc", "shlex", @@ -496,9 +519,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "change-detection" @@ -507,20 +530,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "159fa412eae48a1d94d0b9ecdb85c97ce56eb2a347c62394d3fdbf221adabc1a" dependencies = [ "path-matchers", - "path-slash 0.1.5", + "path-slash", ] [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-targets 0.52.6", ] [[package]] @@ -535,18 +559,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -556,9 +580,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "codemap" @@ -568,17 +592,18 @@ checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "3.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "windows-sys 0.59.0", + "lazy_static", + "windows-sys 0.48.0", ] [[package]] @@ -587,18 +612,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7439becb5fafc780b6f4de382b1a7a3e70234afe783854a4702ee8adbb838609" -[[package]] -name = "config" -version = "0.15.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e549344080374f9b32ed41bf3b6b57885ff6a289367b3dbc10eea8acc1918" -dependencies = [ - "pathdiff", - "serde_core", - "toml", - "winnow", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -616,7 +629,7 @@ dependencies = [ "hkdf", "hmac", "percent-encoding", - "rand 0.8.5", + "rand", "sha2", "subtle", "time", @@ -631,36 +644,36 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -677,9 +690,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -688,7 +701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "typenum", ] @@ -703,44 +716,44 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.4" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "derive_more" -version = "0.99.20" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.90", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", "unicode-xid", ] @@ -763,7 +776,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] @@ -777,43 +790,31 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.14" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "figlet-rs" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4742a071cd9694fc86f9fa1a08fa3e53d40cc899d7ee532295da2d085639fbc5" -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - [[package]] name = "flate2" -version = "1.1.4" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -821,16 +822,16 @@ dependencies = [ [[package]] name = "fluent-bundle" -version = "0.16.0" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" dependencies = [ "fluent-langneg", "fluent-syntax", "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -846,33 +847,33 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.12.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ - "memchr", - "thiserror 2.0.17", + "thiserror", ] [[package]] name = "fluent-template-macros" -version = "0.13.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6222b8a208b9f0e7b984da3616651b0cc74e4461571b118cb1c713e0f7617ee" +checksum = "007d176e568a4f73ad4225df02aa29ccfecffd8eda31ce78da0bc8b4b310f20a" dependencies = [ "flume", "ignore", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.90", "unic-langid", ] [[package]] name = "fluent-templates" -version = "0.13.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a893d77c0e48dc3f78421e9cba5d82e02bcb85d4520c506cec2fbebd0f513b" +checksum = "74f22f61b2c8551163ea13c16a381484e5360b089401c6e47c4bfcf6b62bb7ac" dependencies = [ "fluent-bundle", "fluent-langneg", @@ -882,7 +883,8 @@ dependencies = [ "ignore", "intl-memoizer", "log", - "thiserror 1.0.69", + "once_cell", + "thiserror", "unic-langid", ] @@ -901,17 +903,11 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "form_urlencoded" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -959,29 +955,17 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", -] - [[package]] name = "ghash" version = "0.5.1" @@ -994,27 +978,27 @@ dependencies = [ [[package]] name = "gimli" -version = "0.32.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -1024,7 +1008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7a68216437ef68f0738e48d6c7bb9e6e6a92237e001b03d838314b068f33c94" dependencies = [ "clap", - "getrandom 0.2.16", + "getrandom", "grass_compiler", ] @@ -1039,14 +1023,14 @@ dependencies = [ "lasso", "once_cell", "phf", - "rand 0.8.5", + "rand", ] [[package]] name = "h2" -version = "0.3.27" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1073,9 +1057,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hkdf" @@ -1114,9 +1098,9 @@ checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" [[package]] name = "httparse" -version = "1.10.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1126,15 +1110,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", - "log", "wasm-bindgen", "windows-core", ] @@ -1150,22 +1133,21 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.0.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -1175,10 +1157,30 @@ dependencies = [ ] [[package]] -name = "icu_normalizer" -version = "2.0.0" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", @@ -1186,59 +1188,72 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" -version = "2.0.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "potential_utf", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" -version = "2.0.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "zerovec", ] [[package]] -name = "idna" -version = "1.1.0" +name = "icu_provider_macros" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", @@ -1247,9 +1262,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -1265,7 +1280,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -1273,40 +1288,34 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" +checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" [[package]] name = "indexmap" -version = "2.11.4" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.15.2", ] -[[package]] -name = "indoc" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" - [[package]] name = "inout" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array", ] [[package]] name = "intl-memoizer" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" dependencies = [ "type-map", "unic-langid", @@ -1321,17 +1330,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -1340,27 +1338,25 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ - "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "fb15147158e79fd8b8afd0252522769c4f48725460b37338544d8379d94fc8f9" dependencies = [ - "once_cell", "wasm-bindgen", ] @@ -1387,21 +1383,21 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "litemap" -version = "0.8.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "local-channel" @@ -1422,33 +1418,34 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.14" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] name = "memchr" -version = "2.7.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1467,25 +1464,30 @@ dependencies = [ ] [[package]] -name = "miniz_oxide" -version = "0.8.9" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", - "simd-adler32", ] [[package]] name = "mio" -version = "1.0.4" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.52.0", ] [[package]] @@ -1495,12 +1497,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94e1e6445d314f972ff7395df2de295fe51b71821694f0b0e1e79c4f12c8577" [[package]] -name = "nu-ansi-term" -version = "0.50.1" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "windows-sys 0.52.0", + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", ] [[package]] @@ -1520,24 +1533,18 @@ dependencies = [ [[package]] name = "object" -version = "0.37.3" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -1545,33 +1552,35 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "pagetop" -version = "0.4.0" +version = "0.0.57" dependencies = [ "actix-files", "actix-session", "actix-web", + "actix-web-static-files", "chrono", "colored", "concat-string", - "config", "figlet-rs", "fluent-templates", - "indoc", "itoa", - "pagetop-aliner", - "pagetop-bootsier", + "nom", "pagetop-build", "pagetop-macros", - "pagetop-statics", - "parking_lot", - "pastey", + "paste", "serde", - "serde_json", + "static-files", "substring", - "tempfile", "terminal_size", + "toml", "tracing", "tracing-actix-web", "tracing-appender", @@ -1579,58 +1588,30 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "pagetop-aliner" -version = "0.0.9" -dependencies = [ - "pagetop", - "pagetop-build", -] - -[[package]] -name = "pagetop-bootsier" -version = "0.0.18" -dependencies = [ - "pagetop", - "pagetop-build", - "serde", -] - [[package]] name = "pagetop-build" -version = "0.3.1" +version = "0.0.12" dependencies = [ "grass", - "pagetop-statics", + "static-files", ] [[package]] name = "pagetop-macros" -version = "0.2.0" +version = "0.0.14" dependencies = [ + "proc-macro-crate", + "proc-macro-error", "proc-macro2", - "proc-macro2-diagnostics", "quote", - "syn", -] - -[[package]] -name = "pagetop-statics" -version = "0.1.2" -dependencies = [ - "actix-web", - "change-detection", - "derive_more 0.99.20", - "futures-util", - "mime_guess", - "path-slash 0.2.1", + "syn 2.0.90", ] [[package]] name = "parking_lot" -version = "0.12.5" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -1638,22 +1619,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.12" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-link", + "windows-targets 0.52.6", ] [[package]] -name = "pastey" -version = "0.1.1" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "path-matchers" @@ -1670,29 +1651,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696" -[[package]] -name = "path-slash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" - -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "percent-encoding" -version = "2.3.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", "phf_shared", @@ -1700,61 +1669,61 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand", ] [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -1764,9 +1733,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polyval" @@ -1780,15 +1749,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1797,13 +1757,46 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.21" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -1812,40 +1805,22 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", -] - [[package]] name = "quote" -version = "1.0.41" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "rand" version = "0.8.5" @@ -1853,18 +1828,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_chacha", + "rand_core", ] [[package]] @@ -1874,17 +1839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -1893,73 +1848,79 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", + "getrandom", ] [[package]] name = "redox_syscall" -version = "0.5.18" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.12.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" -version = "0.4.12" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" @@ -1972,28 +1933,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -2012,66 +1967,64 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "self_cell" -version = "1.2.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "serde_core", - "serde_derive", + "self_cell 1.0.4", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "self_cell" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", "ryu", "serde", - "serde_core", ] [[package]] name = "serde_spanned" -version = "1.0.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -2099,9 +2052,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -2125,55 +2078,42 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "siphasher" -version = "1.0.1" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "libc", - "windows-sys 0.52.0", + "autocfg", ] [[package]] -name = "socket2" -version = "0.6.0" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2187,9 +2127,20 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static-files" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8590e848e1c53be9258210bcd4a8f4118e08988f03a4e2d63b62e4ad9f7ced" +dependencies = [ + "change-detection", + "mime_guess", + "path-slash", +] [[package]] name = "strsim" @@ -2214,9 +2165,19 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2225,36 +2186,23 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.61.2", + "syn 2.0.90", ] [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2263,16 +2211,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl", ] [[package]] @@ -2283,34 +2222,24 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", + "once_cell", ] [[package]] name = "time" -version = "0.3.44" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -2323,15 +2252,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -2339,9 +2268,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -2349,28 +2278,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", - "windows-sys 0.59.0", + "socket2", + "windows-sys 0.52.0", ] [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -2381,32 +2308,35 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "serde_core", + "serde", "serde_spanned", "toml_datetime", - "toml_parser", - "winnow", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ - "serde_core", + "serde", ] [[package]] -name = "toml_parser" -version = "1.0.4" +name = "toml_edit" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", "winnow", ] @@ -2424,9 +2354,9 @@ dependencies = [ [[package]] name = "tracing-actix-web" -version = "0.7.19" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5360edd490ec8dee9fedfc6a9fd83ac2f01b3e1996e3261b9ad18a61971fe064" +checksum = "54a9f5c1aca50ebebf074ee665b9f99f2e84906dcf6b993a0d0090edb835166d" dependencies = [ "actix-web", "mutually_exclusive_features", @@ -2442,27 +2372,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.69", + "thiserror", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -2491,14 +2421,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex-automata", + "regex", "serde", "serde_json", "sharded-slab", @@ -2512,24 +2442,24 @@ dependencies = [ [[package]] name = "type-map" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ "rustc-hash", ] [[package]] name = "typenum" -version = "1.19.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unic-langid" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" dependencies = [ "unic-langid-impl", "unic-langid-macros", @@ -2537,18 +2467,18 @@ dependencies = [ [[package]] name = "unic-langid-impl" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" dependencies = [ "tinystr", ] [[package]] name = "unic-langid-macros" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5957eb82e346d7add14182a3315a7e298f04e1ba4baac36f7f0dbfedba5fc25" +checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e" dependencies = [ "proc-macro-hack", "tinystr", @@ -2558,27 +2488,27 @@ dependencies = [ [[package]] name = "unic-langid-macros-impl" -version = "0.9.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" +checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn", + "syn 2.0.90", "unic-langid-impl", ] [[package]] name = "unicase" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-xid" @@ -2598,16 +2528,21 @@ dependencies = [ [[package]] name = "url" -version = "2.5.7" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -2622,13 +2557,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ - "getrandom 0.3.3", - "js-sys", - "wasm-bindgen", + "getrandom", ] [[package]] @@ -2639,9 +2572,9 @@ checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" [[package]] name = "valuable" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" @@ -2661,60 +2594,41 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "21d3b25c3ea1126a2ad5f4f9068483c2af1e64168f847abe863a526b8dbfe00b" dependencies = [ "cfg-if", "once_cell", - "rustversion", "wasm-bindgen-macro", - "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.104" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +checksum = "52857d4c32e496dc6537646b5b117081e71fd2ff06de792e3577a150627db283" dependencies = [ "bumpalo", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "920b0ffe069571ebbfc9ddc0b36ba305ef65577c94b06262ed793716a1afd981" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2722,92 +2636,70 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "bf59002391099644be3524e23b781fa43d2be0c5aa0719a18c0731b9d195cab6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "e5047c5392700766601942795a436d7d2599af60dcc3cc1248c9120bfb0827b0" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "unicode-ident", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" -version = "0.62.2" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-targets 0.52.6", ] [[package]] -name = "windows-implement" -version = "0.60.2" +name = "windows-sys" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", + "windows-targets 0.48.5", ] [[package]] @@ -2829,21 +2721,18 @@ dependencies = [ ] [[package]] -name = "windows-sys" -version = "0.60.2" +name = "windows-targets" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2855,7 +2744,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -2863,21 +2752,10 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.53.5" +name = "windows_aarch64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -2886,10 +2764,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" +name = "windows_aarch64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -2898,10 +2776,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" +name = "windows_i686_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -2909,12 +2787,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -2922,10 +2794,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" +name = "windows_i686_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -2934,10 +2806,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_i686_msvc" -version = "0.53.1" +name = "windows_x86_64_gnu" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -2946,10 +2818,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" +name = "windows_x86_64_gnullvm" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -2958,10 +2830,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -2969,38 +2841,32 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" -version = "0.7.13" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen" -version = "0.46.0" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.6.1" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "yoke" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -3010,73 +2876,63 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", "synstructure", ] -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - [[package]] name = "zerovec" -version = "0.11.4" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -3085,38 +2941,38 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.90", ] [[package]] name = "zstd" -version = "0.13.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.4" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index b1bae0a6..fa090e35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,90 +1,57 @@ [package] name = "pagetop" -version = "0.4.0" +version = "0.0.57" edition = "2021" -description = """ - Un entorno de desarrollo para crear soluciones web modulares, extensibles y configurables. +description = """\ + An opinionated web framework to build modular Server-Side Rendering web solutions.\ """ -categories = ["web-programming::http-server"] +categories = ["web-programming", "gui", "development-tools", "asynchronous"] keywords = ["pagetop", "web", "framework", "frontend", "ssr"] -repository.workspace = true -homepage.workspace = true -license.workspace = true -authors.workspace = true - -[dependencies] -chrono = "0.4" -colored = "3.0" -concat-string = "1.0" -config = { version = "0.15", default-features = false, features = ["toml"] } -figlet-rs = "0.1" -indoc = "2.0" -itoa = "1.0" -parking_lot = "0.12" -paste = { package = "pastey", version = "0.1" } -substring = "1.4" -terminal_size = "0.4" - -tracing = "0.1" -tracing-appender = "0.2" -tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] } -tracing-actix-web = "0.7" - -fluent-templates = "0.13" -unic-langid = { version = "0.9", features = ["macros"] } - -actix-web = { workspace = true, default-features = true } -actix-session = { version = "0.11", features = ["cookie-session"] } -actix-web-files = { package = "actix-files", version = "0.6" } - -serde.workspace = true - -pagetop-macros.workspace = true -pagetop-statics.workspace = true - -[features] -default = [] -testing = [] - -[dev-dependencies] -tempfile = "3.23" -serde_json = "1.0" -pagetop-aliner.workspace = true -pagetop-bootsier.workspace = true - -[build-dependencies] -pagetop-build.workspace = true - - -[workspace] -resolver = "2" -members = [ - # Helpers - "helpers/pagetop-build", - "helpers/pagetop-macros", - "helpers/pagetop-statics", - # Extensions - "extensions/pagetop-aliner", - "extensions/pagetop-bootsier", -] - -[workspace.package] -repository = "https://git.cillero.es/manuelcillero/pagetop" +repository = "https://github.com/manuelcillero/pagetop" homepage = "https://pagetop.cillero.es" license = "MIT OR Apache-2.0" authors = ["Manuel Cillero "] -[workspace.dependencies] -actix-web = { version = "4.11", default-features = false } +exclude = ["examples/", "helpers/", "tests/"] +rust-version = "1.80.0" + +[workspace] +resolver = "2" +members = ["helpers/*"] + +[lib] +name = "pagetop" + +[dependencies] +chrono = "0.4.38" +colored = "2.1.0" +concat-string = "1.0.1" +figlet-rs = "0.1.5" +itoa = "1.0.14" +nom = "7.1.3" +paste = "1.0.15" serde = { version = "1.0", features = ["derive"] } -# Helpers -pagetop-build = { version = "0.3", path = "helpers/pagetop-build" } -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 = "." } +substring = "1.4.5" +terminal_size = "0.4.1" +toml = "0.8.19" + +tracing = "0.1.41" +tracing-appender = "0.2.3" +tracing-subscriber = { version = "0.3.19", features = ["json", "env-filter"] } +tracing-actix-web = "0.7.15" + +fluent-templates = "0.11.0" +unic-langid = { version = "0.9.5", features = ["macros"] } + +actix-web = "4.9.0" +actix-session = { version = "0.10.1", features = ["cookie-session"] } +actix-web-files = { package = "actix-files", version = "0.6.6" } +actix-web-static-files = "4.0.1" +static-files = "0.2.4" + +pagetop-macros = { version = "0.0", path = "helpers/pagetop-macros" } + +[build-dependencies] +pagetop-build = { version = "0.0", path = "helpers/pagetop-build" } diff --git a/README.md b/README.md index 9a12c845..117da733 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,38 @@
- +

PageTop

-

Un entorno para el desarrollo de soluciones web modulares, extensibles y configurables.

+

An opinionated web framework to build modular Server-Side Rendering web solutions.

-[![Doc API](https://img.shields.io/docsrs/pagetop?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop) +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?style=for-the-badge)](#-license) +[![API Docs](https://img.shields.io/docsrs/pagetop?label=API%20Docs&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop) [![Crates.io](https://img.shields.io/crates/v/pagetop.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop) -[![Descargas](https://img.shields.io/crates/d/pagetop.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop#licencia) +[![Downloads](https://img.shields.io/crates/d/pagetop.svg?style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop) -
-PageTop reivindica la esencia de la web clásica usando [Rust](https://www.rust-lang.org/es) para la -creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y JavaScript. -Ofrece un conjunto de herramientas que los desarrolladores pueden implementar, extender o adaptar -según las necesidades de cada proyecto, incluyendo: +## Overview - * **Acciones** (*actions*): alteran la lógica interna de una funcionalidad interceptando su flujo - de ejecución. - * **Componentes** (*components*): encapsulan HTML, CSS y JavaScript en unidades funcionales, - configurables y reutilizables. - * **Extensiones** (*extensions*): añaden, extienden o personalizan funcionalidades usando las APIs - de PageTop o de terceros. - * **Temas** (*themes*): son extensiones que permiten modificar la apariencia de páginas y - componentes sin comprometer su funcionalidad. +The PageTop core API provides a comprehensive toolkit for extending its functionalities to specific +requirements and application scenarios through actions, components, packages, and themes: + + * **Actions** serve as a mechanism to customize PageTop's internal behavior by intercepting its + execution flow. + * **Components** encapsulate HTML, CSS, and JavaScript into functional, configurable, and + well-defined units. + * **Packages** extend or customize existing functionality by interacting with PageTop APIs or + third-party package APIs. + * **Themes** enable developers to alter the appearance of pages and components without affecting + their functionality. -# ⚡️ Guía rápida +# ⚡️ Quick start -La aplicación más sencilla de PageTop se ve así: +The simplest PageTop application looks like this: -```rust,no_run +```rust use pagetop::prelude::*; #[pagetop::main] @@ -42,17 +41,15 @@ async fn main() -> std::io::Result<()> { } ``` -Este código arranca el servidor de PageTop. Con la configuración por defecto, muestra una página de -bienvenida accesible desde un navegador local en la dirección `http://localhost:8080`. +This provides a default homepage at `http://localhost:8088` using the default configuration. To +customize the service, you can define a PageTop package like this: -Para personalizar el servicio, se puede crear una extensión de PageTop de la siguiente manera: - -```rust,no_run +```rust use pagetop::prelude::*; struct HelloWorld; -impl Extension for HelloWorld { +impl PackageTrait for HelloWorld { fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { scfg.route("/", service::web::get().to(hello_world)); } @@ -60,7 +57,7 @@ impl Extension for HelloWorld { async fn hello_world(request: HttpRequest) -> ResultPage { Page::new(request) - .add_child(Html::with(|_| html! { h1 { "Hello World!" } })) + .with_component(Html::with(html! { h1 { "Hello World!" } })) .render() } @@ -70,82 +67,42 @@ async fn main() -> std::io::Result<()> { } ``` -Este programa implementa una extensión llamada `HelloWorld` que sirve una página web en la ruta raíz -(`/`) mostrando el texto "Hello world!" dentro de un elemento HTML `

`. +This program defines a custom `HelloWorld` package to serve a page at the root path (`/`) displaying +a "Hello World!" message inside an HTML `

` element. -# 📂 Repositorio +# 📂 Helpers -El código se organiza en un *workspace* donde actualmente se incluyen los siguientes subproyectos: +* [pagetop-macros](https://github.com/manuelcillero/pagetop/tree/latest/helpers/pagetop-macros): + A collection of procedural macros that enhance the development experience within PageTop. - * **[pagetop](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/src)**, con el código - fuente de la librería principal. Reúne algunos de los *crates* más estables y populares del - ecosistema Rust para proporcionar APIs y recursos para la creación avanzada de soluciones web. - -## Auxiliares - - * **[pagetop-statics](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-statics)**, - es la librería que permite incluir archivos estáticos en el ejecutable de las aplicaciones - PageTop para servirlos de forma eficiente, con detección de cambios que optimizan el tiempo de - compilación. - - * **[pagetop-build](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-build)**, - prepara los archivos estáticos o archivos SCSS compilados para incluirlos en el binario de las - aplicaciones PageTop durante la compilación de los ejecutables. - - * **[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. - - * **[pagetop-bootsier](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-bootsier)**, - tema basado en [Bootstrap](https://getbootstrap.com) para integrar su catálogo de estilos y - componentes flexibles. +* [pagetop-build](https://github.com/manuelcillero/pagetop/tree/latest/helpers/pagetop-build): + Simplifies the process of embedding resources directly into binary files for PageTop applications. -# 🧪 Pruebas +# 🚧 Warning -Para simplificar el flujo de trabajo, el repositorio incluye varios **alias de Cargo** declarados en -`.cargo/config.toml`. Basta con ejecutarlos desde la raíz del proyecto: - -| Comando | Descripción | -| ------- | ----------- | -| `cargo ts` | Ejecuta los tests de `pagetop` (*unit + integration*) con la *feature* `testing`. | -| `cargo ts --test util` | Lanza sólo las pruebas de integración del módulo `util`. | -| `cargo ts --doc locale` | Lanza las pruebas de la documentación del módulo `locale`. | -| `cargo tw` | Ejecuta los tests de **todos los paquetes** del *workspace*. | - -> **Nota** -> Estos alias ya compilan con la configuración adecuada. No requieren `--no-default-features`. -> Si quieres **activar** las trazas del registro de eventos entonces usa simplemente `cargo test`. +**PageTop** framework is currently in active development. The API is unstable and subject to +frequent changes. Production use is not recommended until version **0.1.0**. -# 🚧 Advertencia +# 📜 License -**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**. +PageTop is free, open source and permissively licensed! Except where noted (below and/or in +individual files), all code in this project is dual-licensed under either: + + * MIT License + ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) + + * Apache License, Version 2.0, + ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + +at your option. This means you can select the license you prefer! This dual-licensing approach is +the de-facto standard in the Rust ecosystem. -# 📜 Licencia +# ✨ Contributions -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. - - -# ✨ Contribuir - -Cualquier contribución para añadir al proyecto se considerará automáticamente bajo la doble licencia -indicada arriba (MIT o Apache v2.0), sin términos o condiciones adicionales, tal y como permite la -licencia *Apache v2.0*. +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the +work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any +additional terms or conditions. diff --git a/config/common.toml b/config/common.toml new file mode 100644 index 00000000..d6b30e57 --- /dev/null +++ b/config/common.toml @@ -0,0 +1,6 @@ +[app] +name = "Samples" +#language = "es-ES" + +[log] +tracing = "Debug" diff --git a/config/default.toml b/config/default.toml deleted file mode 100644 index 3e254586..00000000 --- a/config/default.toml +++ /dev/null @@ -1,2 +0,0 @@ -[log] -tracing = "Info,pagetop=Debug" diff --git a/config/predefined-settings.toml b/config/predefined-settings.toml new file mode 100644 index 00000000..08571a92 --- /dev/null +++ b/config/predefined-settings.toml @@ -0,0 +1,40 @@ +[app] +name = "My App" +description = "Developed with the amazing PageTop framework." +# Default theme. +theme = "Default" +# Default language (localization). +language = "en-US" +# Default text direction: "ltr", "rtl", or "auto". +direction = "ltr" +# Startup banner: "Off", "Slant", "Small", "Speed", or "Starwars". +startup_banner = "Slant" + +[dev] +# Static files required by the app are integrated by default into the executable +# binary. However, during development, it can be useful to serve these files +# from their own directory to avoid recompiling every time they are modified. In +# this case, just indicate the full path to the project's root directory. +pagetop_project_dir = "" + +[log] +# Execution trace: "Error", "Warn", "Info", "Debug", or "Trace". +# For example: "Error,actix_server::builder=Info,tracing_actix_web=Debug". +tracing = "Info" +# In terminal ("Stdout") or files "Daily", "Hourly", "Minutely", or "Endless". +rolling = "Stdout" +# Directory for trace files (if rolling != "Stdout"). +path = "log" +# Prefix for trace files (if rolling != "Stdout"). +prefix = "tracing.log" +# Traces format: "Full", "Compact", "Pretty", or "Json". +format = "Full" + +[server] +# Web server config. +bind_address = "localhost" +bind_port = 8088 +# Session cookie duration (in seconds), i.e., the time from when the session is +# created until the cookie expires. A value of 0 indicates "until the browser is +# closed". By default, it is one week. +session_lifetime = 604800 diff --git a/examples/hello-name.rs b/examples/hello-name.rs index c6a82aaf..3a03e8b1 100644 --- a/examples/hello-name.rs +++ b/examples/hello-name.rs @@ -2,19 +2,20 @@ use pagetop::prelude::*; struct HelloName; -impl Extension for HelloName { +impl PackageTrait for HelloName { fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - scfg.route("/hello/{name}", service::web::get().to(hello_name)); + scfg.service(hello_name); } } +#[service::get("/hello/{name}")] async fn hello_name( request: HttpRequest, path: service::web::Path, ) -> ResultPage { let name = path.into_inner(); Page::new(request) - .add_child(Html::with(move |_| html! { h1 { "Hello " (name) "!" } })) + .with_component(Html::with(html! { h1 { "Hello " (name) "!" } })) .render() } diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 64817466..17c1e9d3 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -2,7 +2,7 @@ use pagetop::prelude::*; struct HelloWorld; -impl Extension for HelloWorld { +impl PackageTrait for HelloWorld { fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { scfg.route("/", service::web::get().to(hello_world)); } @@ -10,7 +10,7 @@ impl Extension for HelloWorld { async fn hello_world(request: HttpRequest) -> ResultPage { Page::new(request) - .add_child(Html::with(|_| html! { h1 { "Hello World!" } })) + .with_component(Html::with(html! { h1 { "Hello World!" } })) .render() } diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs deleted file mode 100644 index 341d394a..00000000 --- a/examples/navbar-menus.rs +++ /dev/null @@ -1,109 +0,0 @@ -use pagetop::prelude::*; - -use pagetop_bootsier::prelude::*; - -struct SuperMenu; - -impl Extension for SuperMenu { - fn dependencies(&self) -> Vec { - vec![&pagetop_aliner::Aliner, &pagetop_bootsier::Bootsier] - } - - fn initialize(&self) { - let home_path = |cx: &Context| match cx.langid().language.as_str() { - "en" => "/en", - _ => "/", - }; - - let navbar_menu = Navbar::brand_left(navbar::Brand::new().with_path(Some(home_path))) - .with_expand(BreakPoint::LG) - .add_item(navbar::Item::nav( - Nav::new() - .add_item(nav::Item::link( - L10n::l("sample_menus_item_link"), - home_path, - )) - .add_item(nav::Item::link_blank( - L10n::l("sample_menus_item_blank"), - |_| "https://docs.rs/pagetop", - )) - .add_item(nav::Item::dropdown( - Dropdown::new() - .with_title(L10n::l("sample_menus_test_title")) - .add_item(dropdown::Item::header(L10n::l("sample_menus_dev_header"))) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_dev_getting_started"), - |_| "/dev/getting-started", - )) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_dev_guides"), - |_| "/dev/guides", - )) - .add_item(dropdown::Item::link_blank( - L10n::l("sample_menus_dev_forum"), - |_| "https://forum.example.dev", - )) - .add_item(dropdown::Item::divider()) - .add_item(dropdown::Item::header(L10n::l("sample_menus_sdk_header"))) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_sdk_rust"), - |_| "/dev/sdks/rust", - )) - .add_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |_| { - "/dev/sdks/js" - })) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_sdk_python"), - |_| "/dev/sdks/python", - )) - .add_item(dropdown::Item::divider()) - .add_item(dropdown::Item::header(L10n::l( - "sample_menus_plugin_header", - ))) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_plugin_auth"), - |_| "/dev/sdks/rust/plugins/auth", - )) - .add_item(dropdown::Item::link( - L10n::l("sample_menus_plugin_cache"), - |_| "/dev/sdks/rust/plugins/cache", - )) - .add_item(dropdown::Item::divider()) - .add_item(dropdown::Item::label(L10n::l("sample_menus_item_label"))) - .add_item(dropdown::Item::link_disabled( - L10n::l("sample_menus_item_disabled"), - |_| "#", - )), - )) - .add_item(nav::Item::link_disabled( - L10n::l("sample_menus_item_disabled"), - |_| "#", - )), - )) - .add_item(navbar::Item::nav( - Nav::new() - .with_classes( - ClassesOp::Add, - classes::Margin::with(Side::Start, ScaleSize::Auto).to_class(), - ) - .add_item(nav::Item::link( - L10n::l("sample_menus_item_sign_up"), - |_| "/auth/sign-up", - )) - .add_item(nav::Item::link(L10n::l("sample_menus_item_login"), |_| { - "/auth/login" - })), - )); - - InRegion::Named("header").add(Child::with( - Container::new() - .with_width(container::Width::FluidMax(UnitValue::RelRem(75.0))) - .add_child(navbar_menu), - )); - } -} - -#[pagetop::main] -async fn main() -> std::io::Result<()> { - Application::prepare(&SuperMenu).run()?.await -} diff --git a/extensions/pagetop-aliner/Cargo.toml b/extensions/pagetop-aliner/Cargo.toml deleted file mode 100644 index 603a6309..00000000 --- a/extensions/pagetop-aliner/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "pagetop-aliner" -version = "0.0.9" -edition = "2021" - -description = """ - Tema de PageTop que muestra esquemáticamente la composición de las páginas HTML -""" -categories = ["web-programming", "gui"] -keywords = ["pagetop", "theme", "css"] - -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-aliner/LICENSE-APACHE b/extensions/pagetop-aliner/LICENSE-APACHE deleted file mode 100644 index 263ddac1..00000000 --- a/extensions/pagetop-aliner/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Manuel Cillero - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/extensions/pagetop-aliner/LICENSE-MIT b/extensions/pagetop-aliner/LICENSE-MIT deleted file mode 100644 index cd8af3d6..00000000 --- a/extensions/pagetop-aliner/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Manuel Cillero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/extensions/pagetop-aliner/README.md b/extensions/pagetop-aliner/README.md deleted file mode 100644 index f4670aae..00000000 --- a/extensions/pagetop-aliner/README.md +++ /dev/null @@ -1,101 +0,0 @@ -
- -

PageTop Aliner

- -

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

- -[![Doc API](https://img.shields.io/docsrs/pagetop-aliner?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-aliner) -[![Crates.io](https://img.shields.io/crates/v/pagetop-aliner.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-aliner) -[![Descargas](https://img.shields.io/crates/d/pagetop-aliner.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-aliner) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner#licencia) - -
-
- -## 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-aliner = "..." -``` - -**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_aliner::Aliner, - // ... - ] - } -} - -#[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 = "Aliner" -``` - -…o **fuerza el tema por código** en una página concreta: - -```rust,no_run -use pagetop::prelude::*; -use pagetop_aliner::Aliner; - -async fn homepage(request: HttpRequest) -> ResultPage { - Page::new(request) - .with_theme(&Aliner) - .add_child( - Block::new() - .with_title(L10n::l("sample_title")) - .add_child(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-aliner/build.rs b/extensions/pagetop-aliner/build.rs deleted file mode 100644 index 26713f52..00000000 --- a/extensions/pagetop-aliner/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -use pagetop_build::StaticFilesBundle; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_dir("./static", None) - .with_name("aliner") - .build() -} diff --git a/extensions/pagetop-aliner/src/lib.rs b/extensions/pagetop-aliner/src/lib.rs deleted file mode 100644 index 4ae4121e..00000000 --- a/extensions/pagetop-aliner/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -/*! -
- -

PageTop Aliner

- -

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

- -[![Doc API](https://img.shields.io/docsrs/pagetop-aliner?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-aliner) -[![Crates.io](https://img.shields.io/crates/v/pagetop-aliner.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-aliner) -[![Descargas](https://img.shields.io/crates/d/pagetop-aliner.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-aliner) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner#licencia) - -
-
- -## 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-aliner = "..." -``` - -**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_aliner::Aliner, - // ... - ] - } -} - -#[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 = "Aliner" -``` - -…o **fuerza el tema por código** en una página concreta: - -```rust,no_run -use pagetop::prelude::*; -use pagetop_aliner::Aliner; - -async fn homepage(request: HttpRequest) -> ResultPage { - Page::new(request) - .with_theme(&Aliner) - .add_child( - Block::new() - .with_title(L10n::l("sample_title")) - .add_child(Html::with(|cx| html! { - p { (L10n::l("sample_content").using(cx)) } - })), - ) - .render() -} -``` -*/ - -use pagetop::prelude::*; - -/// Implementa el tema para usar en pruebas que muestran el esquema de páginas HTML. -/// -/// Define un tema mínimo útil para: -/// -/// - Comprobar el funcionamiento de temas, plantillas y regiones. -/// - Verificar integración de componentes y composiciones (*layouts*) sin estilos complejos. -/// - Realizar pruebas de renderizado rápido con salida estable y predecible. -/// - Preparar ejemplos y documentación, sin dependencias visuales (CSS/JS) innecesarias. -pub struct Aliner; - -impl Extension for Aliner { - fn theme(&self) -> Option { - Some(&Self) - } - - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - static_files_service!(scfg, [aliner] => "/aliner"); - } -} - -impl Theme for Aliner { - 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") - .with_version(env!("CARGO_PKG_VERSION")) - .with_weight(-90), - )); - } -} diff --git a/extensions/pagetop-aliner/static/css/styles.css b/extensions/pagetop-aliner/static/css/styles.css deleted file mode 100644 index 1cc2f5dc..00000000 --- a/extensions/pagetop-aliner/static/css/styles.css +++ /dev/null @@ -1,356 +0,0 @@ -html { - background-color: white; - padding: 1px 3px; -} -body { - padding: 1px 3px; -} -div { - padding: 1px 3px; - margin: 5px; -} -h1, h2, h3, h4,h5, h6, p { - background-color: snow; -} -* * { - outline: 5px solid rgba(255,0,0,.1); -} -* * * { - outline: 3px dashed rgba(255,0,0,.4); -} -* * * * { - outline: 2px dotted rgba(255,0,0,.6); -} -* * * * * { - outline: 1px dotted rgba(255,0,0,.9); -} -* * * * * * { - outline-color: gray; -} - -*::before, *::after { - background: #faa; - border-radius: 3px; - font: normal normal 400 10px/1.2 monospace; - vertical-align: middle; - padding: 1px 3px; - margin: 0 3px; -} -*::before { - content: "("; -} -*::after { - content: ")"; -} - -a::before { content: ""; } -a::after { content: ""; } -abbr::before { content: ""; } -abbr::after { content: ""; } -acronym::before { content: ""; } -acronym::after { content: ""; } -address::before { content: "
"; } -address::after { content: "
"; } -applet::before { content: ""; } -applet::after { content: ""; } -area::before { content: ""; } -area::after { content: ""; } -article::before { content: "
"; } -article::after { content: "
"; } -aside::before { content: ""; } -audio::before { content: ""; } - -b::before { content: ""; } -b::after { content: ""; } -base::before { content: ""; } -base::after { content: ""; } -basefont::before { content: ""; } -basefont::after { content: ""; } -bdi::before { content: ""; } -bdi::after { content: ""; } -bdo::before { content: ""; } -bdo::after { content: ""; } -bgsound::before { content: ""; } -bgsound::after { content: ""; } -big::before { content: ""; } -big::after { content: ""; } -blink::before { content: ""; } -blink::after { content: ""; } -blockquote::before { content: "
"; } -blockquote::after { content: "
"; } -body::before { content: ""; } -body::after { content: ""; } -br::before { content: "
"; } -br::after { content: "
"; } -button::before { content: ""; } - -caption::before { content: ""; } -caption::after { content: ""; } -canvas::before { content: ""; } -canvas::after { content: ""; } -center::before { content: "
"; } -center::after { content: "
"; } -cite::before { content: ""; } -cite::after { content: ""; } -code::before { content: ""; } -code::after { content: ""; } -col::before { content: ""; } -col::after { content: ""; } -colgroup::before { content: ""; } -colgroup::after { content: ""; } -command::before { content: ""; } -command::after { content: ""; } -content::before { content: ""; } -content::after { content: ""; } - -data::before { content: ""; } -data::after { content: ""; } -datalist::before { content: ""; } -datalist::after { content: ""; } -dd::before { content: "
"; } -dd::after { content: "
"; } -del::before { content: ""; } -del::after { content: ""; } -details::before { content: "
"; } -details::after { content: "
"; } -dfn::before { content: ""; } -dfn::after { content: ""; } -dialog::before { content: ""; } -dialog::after { content: ""; } -dir::before { content: ""; } -dir::after { content: ""; } -div::before { content: "
"; } -div::after { content: "
"; } -dl::before { content: "
"; } -dl::after { content: "
"; } -dt::before { content: "
"; } -dt::after { content: "
"; } - -element::before { content: ""; } -element::after { content: ""; } -em::before { content: ""; } -em::after { content: ""; } -embed::before { content: ""; } -embed::after { content: ""; } - -fieldset::before { content: "
"; } -fieldset::after { content: "
"; } -figcaption::before { content: "
"; } -figcaption::after { content: "
"; } -figure::before { content: "
"; } -figure::after { content: "
"; } -font::before { content: ""; } -font::after { content: ""; } -footer::before { content: "
"; } -footer::after { content: "
"; } -form::before { content: "
"; } -form::after { content: "
"; } -frame::before { content: ""; } -frame::after { content: ""; } -frameset::before { content: ""; } -frameset::after { content: ""; } - -h1::before { content: "

"; } -h1::after { content: "

"; } -h2::before { content: "

"; } -h2::after { content: "

"; } -h3::before { content: "

"; } -h3::after { content: "

"; } -h4::before { content: "

"; } -h4::after { content: "

"; } -h5::before { content: "
"; } -h5::after { content: "
"; } -h6::before { content: "
"; } -h6::after { content: "
"; } -head::before { content: ""; } -head::after { content: ""; } -header::before { content: "
"; } -header::after { content: "
"; } -hgroup::before { content: "
"; } -hgroup::after { content: "
"; } -hr::before { content: "
"; } -hr::after { content: ""; } -html::before { content: ""; } -html::after { content: ""; } - -i::before { content: ""; } -i::after { content: ""; } -iframe::before { content: ""; } -image::before { content: ""; } -image::after { content: ""; } -img::before { content: ""; } -img::after { content: ""; } -input::before { content: ""; } -input::after { content: ""; } -ins::before { content: ""; } -ins::after { content: ""; } -isindex::before { content: ""; } -isindex::after { content: ""; } - -kbd::before { content: ""; } -kbd::after { content: ""; } -keygen::before { content: ""; } -keygen::after { content: ""; } - -label::before { content: ""; } -legend::before { content: ""; } -legend::after { content: ""; } -li::before { content: "
  • "; } -li::after { content: "
  • "; } -link::before { content: ""; } -link::after { content: ""; } -listing::before { content: ""; } -listing::after { content: ""; } - -main::before { content: "
    "; } -main::after { content: "
    "; } -map::before { content: ""; } -map::after { content: ""; } -mark::before { content: ""; } -mark::after { content: ""; } -marquee::before { content: ""; } -marquee::after { content: ""; } -menu::before { content: ""; } -menu::after { content: ""; } -menuitem::before { content: ""; } -menuitem::after { content: ""; } -meta::before { content: ""; } -meta::after { content: ""; } -meter::before { content: ""; } -meter::after { content: ""; } -multicol::before { content: ""; } -multicol::after { content: ""; } - -nav::before { content: ""; } -nextid::before { content: ""; } -nextid::after { content: ""; } -nobr::before { content: ""; } -nobr::after { content: ""; } -noembed::before { content: ""; } -noembed::after { content: ""; } -noframes::before { content: ""; } -noframes::after { content: ""; } -noscript::before { content: ""; } - -object::before { content: ""; } -object::after { content: ""; } -ol::before { content: "
      "; } -ol::after { content: "
    "; } -optgroup::before { content: ""; } -optgroup::after { content: ""; } -option::before { content: ""; } -output::before { content: ""; } -output::after { content: ""; } - -p::before { content: "

    "; } -p::after { content: "

    "; } -param::before { content: ""; } -param::after { content: ""; } -picture::before { content: ""; } -picture::after { content: ""; } -plaintext::before { content: ""; } -plaintext::after { content: "</plaintext>"; } -pre::before { content: "<pre>"; } -pre::after { content: "</pre>"; } -progress::before { content: "<progress>"; } -progress::after { content: "</progress>"; } - -q::before { content: "<q>"; } -q::after { content: "</q>"; } - -rb::before { content: "<rb>"; } -rb::after { content: "</rb>"; } -rp::before { content: "<rp>"; } -rp::after { content: "</rp>"; } -rt::before { content: "<rt>"; } -rt::after { content: "</rt>"; } -rtc::before { content: "<rtc>"; } -rtc::after { content: "</rtc>"; } -ruby::before { content: "<ruby>"; } -ruby::after { content: "</ruby>"; } - -s::before { content: "<s>"; } -s::after { content: "</s>"; } -samp::before { content: "<samp>"; } -samp::after { content: "</samp>"; } -script::before { content: "<script>"; } -script::after { content: "</script>"; } -section::before { content: "<section>"; } -section::after { content: "</section>"; } -select::before { content: "<select>"; } -select::after { content: "</select>"; } -shadow::before { content: "<shadow>"; } -shadow::after { content: "</shadow>"; } -slot::before { content: "<slot>"; } -slot::after { content: "</slot>"; } -small::before { content: "<small>"; } -small::after { content: "</small>"; } -source::before { content: "<source>"; } -source::after { content: "</source>"; } -spacer::before { content: "<spacer>"; } -spacer::after { content: "</spacer>"; } -span::before { content: "<span>"; } -span::after { content: "</span>"; } -strike::before { content: "<strike>"; } -strike::after { content: "</strike>"; } -strong::before { content: "<strong>"; } -strong::after { content: "</strong>"; } -style::before { content: "<style>"; } -style::after { content: "<\/style>"; } -sub::before { content: "<sub>"; } -sub::after { content: "</sub>"; } -summary::before { content: "<summary>"; } -summary::after { content: "</summary>"; } -sup::before { content: "<sup>"; } -sup::after { content: "</sup>"; } - -table::before { content: "<table>"; } -table::after { content: "</table>"; } -tbody::before { content: "<tbody>"; } -tbody::after { content: "</tbody>"; } -td::before { content: "<td>"; } -td::after { content: "</td>"; } -template::before { content: "<template>"; } -template::after { content: "</template>"; } -textarea::before { content: "<textarea>"; } -textarea::after { content: "</textarea>"; } -tfoot::before { content: "<tfoot>"; } -tfoot::after { content: "</tfoot>"; } -th::before { content: "<th>"; } -th::after { content: "</th>"; } -thead::before { content: "<thead>"; } -thead::after { content: "</thead>"; } -time::before { content: "<time>"; } -time::after { content: "</time>"; } -title::before { content: "<title>"; } -title::after { content: "</title>"; } -tr::before { content: "<tr>"; } -tr::after { content: "</tr>"; } -track::before { content: "<track>"; } -track::after { content: "</track>"; } -tt::before { content: "<tt>"; } -tt::after { content: "</tt>"; } - -u::before { content: "<u>"; } -u::after { content: "</u>"; } -ul::before { content: "<ul>"; } -ul::after { content: "</ul>"; } - -var::before { content: "<var>"; } -var::after { content: "</var>"; } -video::before { content: "<video>"; } -video::after { content: "</video>"; } - -wbr::before { content: "<wbr>"; } -wbr::after { content: "</wbr>"; } - -xmp::before { content: "<xmp>"; } -xmp::after { content: "</xmp>"; } diff --git a/extensions/pagetop-bootsier/.gitattributes b/extensions/pagetop-bootsier/.gitattributes deleted file mode 100644 index 940d6a84..00000000 --- a/extensions/pagetop-bootsier/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -static/** linguist-vendored diff --git a/extensions/pagetop-bootsier/Cargo.toml b/extensions/pagetop-bootsier/Cargo.toml deleted file mode 100644 index 6df6cf69..00000000 --- a/extensions/pagetop-bootsier/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "pagetop-bootsier" -version = "0.0.18" -edition = "2021" - -description = """ - Tema de PageTop basado en Bootstrap para aplicar 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 -serde.workspace = true - -[build-dependencies] -pagetop-build.workspace = true diff --git a/extensions/pagetop-bootsier/LICENSE-APACHE b/extensions/pagetop-bootsier/LICENSE-APACHE deleted file mode 100644 index 263ddac1..00000000 --- a/extensions/pagetop-bootsier/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Manuel Cillero - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/extensions/pagetop-bootsier/LICENSE-MIT b/extensions/pagetop-bootsier/LICENSE-MIT deleted file mode 100644 index cd8af3d6..00000000 --- a/extensions/pagetop-bootsier/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Manuel Cillero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/extensions/pagetop-bootsier/README.md b/extensions/pagetop-bootsier/README.md deleted file mode 100644 index d6e1666a..00000000 --- a/extensions/pagetop-bootsier/README.md +++ /dev/null @@ -1,101 +0,0 @@ -<div align="center"> - -<h1>PageTop Bootsier</h1> - -<p>Tema de <strong>PageTop</strong> basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.</p> - -[![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) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-bootsier#licencia) - -<br> -</div> - -## 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<ExtensionRef> { - 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::*; -use pagetop_bootsier::Bootsier; - -async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { - Page::new(request) - .with_theme(&Bootsier) - .add_child( - Block::new() - .with_title(L10n::l("sample_title")) - .add_child(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 deleted file mode 100644 index df7a2750..00000000 --- a/extensions/pagetop-bootsier/build.rs +++ /dev/null @@ -1,20 +0,0 @@ -use pagetop_build::StaticFilesBundle; - -use std::env; -use std::path::Path; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_scss("./static/scss/bootsier.scss", "bootstrap.min.css") - .with_name("bootsier_bs") - .build()?; - StaticFilesBundle::from_dir("./static/js", Some(bootstrap_js_files)) - .with_name("bootsier_js") - .build() -} - -fn bootstrap_js_files(path: &Path) -> bool { - let bootstrap_js = "bootstrap.bundle.min.js"; - // No filtra durante el desarrollo, solo en la compilación "release". - env::var("PROFILE").unwrap_or_else(|_| "release".to_string()) != "release" - || path.file_name().is_some_and(|f| f == bootstrap_js) -} diff --git a/extensions/pagetop-bootsier/src/config.rs b/extensions/pagetop-bootsier/src/config.rs deleted file mode 100644 index 6c2365ba..00000000 --- a/extensions/pagetop-bootsier/src/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Opciones de configuración del tema. -//! -//! Ejemplo: -//! -//! ```toml -//! [bootsier] -//! max_width = "90rem" -//! ``` -//! -//! Uso: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! use pagetop_bootsier::config; -//! -//! assert_eq!(config::SETTINGS.bootsier.max_width, UnitValue::Px(1440)); -//! ``` -//! -//! Consulta [`pagetop::config`] para ver cómo PageTop lee los archivos de configuración y aplica -//! los valores a los ajustes. - -use pagetop::prelude::*; - -use serde::Deserialize; - -include_config!(SETTINGS: Settings => [ - // [bootsier] - "bootsier.max_width" => "1440px", -]); - -#[derive(Debug, Deserialize)] -/// Tipos para la sección [`[bootsier]`](Bootsier) de [`SETTINGS`]. -pub struct Settings { - pub bootsier: Bootsier, -} -#[derive(Debug, Deserialize)] -/// Sección `[bootsier]` de la configuración. Forma parte de [`Settings`]. -pub struct Bootsier { - /// Ancho máximo predeterminado para la página, por ejemplo "100%" o "90rem". - pub max_width: UnitValue, -} diff --git a/extensions/pagetop-bootsier/src/lib.rs b/extensions/pagetop-bootsier/src/lib.rs deleted file mode 100644 index 0bf94f47..00000000 --- a/extensions/pagetop-bootsier/src/lib.rs +++ /dev/null @@ -1,132 +0,0 @@ -/*! -<div align="center"> - -<h1>PageTop Bootsier</h1> - -<p>Tema de <strong>PageTop</strong> basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.</p> - -[![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) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-bootsier#licencia) - -<br> -</div> - -## 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<ExtensionRef> { - 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::*; -use pagetop_bootsier::Bootsier; - -async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { - Page::new(request) - .with_theme(&Bootsier) - .add_child( - Block::new() - .with_title(L10n::l("sample_title")) - .add_child(Html::with(|cx| html! { - p { (L10n::l("sample_content").using(cx)) } - })), - ) - .render() -} -``` -*/ - -#![doc( - html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" -)] - -use pagetop::prelude::*; - -include_locales!(LOCALES_BOOTSIER); - -// Versión de la librería Bootstrap. -const BOOTSTRAP_VERSION: &str = "5.3.8"; - -pub mod config; - -pub mod theme; - -/// *Prelude* del tema. -pub mod prelude { - pub use crate::config::*; - pub use crate::theme::*; -} - -/// Implementa el tema. -pub struct Bootsier; - -impl Extension for Bootsier { - fn theme(&self) -> Option<ThemeRef> { - Some(&Self) - } - - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - static_files_service!(scfg, [bootsier_bs] => "/bootsier/bs"); - 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/bs/bootstrap.min.css") - .with_version(BOOTSTRAP_VERSION) - .with_weight(-90), - )) - .alter_assets(ContextOp::AddJavaScript( - JavaScript::defer("/bootsier/js/bootstrap.bundle.min.js") - .with_version(BOOTSTRAP_VERSION) - .with_weight(-90), - )); - } -} diff --git a/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl b/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl deleted file mode 100644 index 0e8969cd..00000000 --- a/extensions/pagetop-bootsier/src/locale/en-US/bootsier.ftl +++ /dev/null @@ -1,5 +0,0 @@ -e404-description = Oops! Page Not Found -e404-message = The page you are looking for may have been removed, had its name changed, or is temporarily unavailable. -e500-description = Oops! Unexpected Error -e500-message = We're having an issue. Please report this error to an administrator. -back-homepage = Back to homepage diff --git a/extensions/pagetop-bootsier/src/locale/en-US/components.ftl b/extensions/pagetop-bootsier/src/locale/en-US/components.ftl deleted file mode 100644 index e3b0d6e6..00000000 --- a/extensions/pagetop-bootsier/src/locale/en-US/components.ftl +++ /dev/null @@ -1,8 +0,0 @@ -# Dropdown -dropdown_toggle = Toggle Dropdown - -# Offcanvas -offcanvas_close = Close - -# Navbar -toggle = Toggle navigation diff --git a/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl b/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl deleted file mode 100644 index f3b76e22..00000000 --- a/extensions/pagetop-bootsier/src/locale/en-US/regions.ftl +++ /dev/null @@ -1,9 +0,0 @@ -header = Header -nav_branding = Navigation branding region -nav_main = Main navigation region -nav_additional = Additional navigation region (eg search form, social icons, etc) -breadcrumb = Breadcrumb -content = Main content -sidebar_first = Sidebar first -sidebar_second = Sidebar second -footer = Footer diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl deleted file mode 100644 index 998b54f2..00000000 --- a/extensions/pagetop-bootsier/src/locale/es-ES/bootsier.ftl +++ /dev/null @@ -1,5 +0,0 @@ -e404-description = ¡Vaya! Página No Encontrada -e404-message = La página que está buscando puede haber sido eliminada, cambiada de nombre o no está disponible temporalmente. -e500-description = ¡Vaya! Error Inesperado -e500-message = Está ocurriendo una incidencia. Por favor, informe de este error a un administrador. -back-homepage = Volver al inicio diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl deleted file mode 100644 index ab7ff687..00000000 --- a/extensions/pagetop-bootsier/src/locale/es-ES/components.ftl +++ /dev/null @@ -1,8 +0,0 @@ -# Dropdown -dropdown_toggle = Mostrar/ocultar menú - -# Offcanvas -offcanvas_close = Cerrar - -# Navbar -toggle = Mostrar/ocultar navegación diff --git a/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl b/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl deleted file mode 100644 index 674fc4b1..00000000 --- a/extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl +++ /dev/null @@ -1,9 +0,0 @@ -header = Cabecera -nav_branding = Navegación y marca -nav_main = Navegación principal -nav_additional = Navegación adicional (p.e. formulario de búsqueda, iconos sociales, etc.) -breadcrumb = Ruta de posicionamiento -content = Contenido principal -sidebar_first = Barra lateral primera -sidebar_second = Barra lateral segunda -footer = Pie diff --git a/extensions/pagetop-bootsier/src/theme.rs b/extensions/pagetop-bootsier/src/theme.rs deleted file mode 100644 index 2c6b5757..00000000 --- a/extensions/pagetop-bootsier/src/theme.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Definiciones y componentes del tema. -//! -//! En esta página, el apartado **Modules** incluye las definiciones necesarias para los componentes -//! que se muestran en el apartado **Structs**, mientras que en **Enums** se listan los elementos -//! auxiliares del tema utilizados en clases y componentes. - -mod aux; -pub use aux::*; - -pub mod classes; - -// Container. -pub mod container; -#[doc(inline)] -pub use container::Container; - -// Dropdown. -pub mod dropdown; -#[doc(inline)] -pub use dropdown::Dropdown; - -// Image. -pub mod image; -#[doc(inline)] -pub use image::Image; - -// Nav. -pub mod nav; -#[doc(inline)] -pub use nav::Nav; - -// Navbar. -pub mod navbar; -#[doc(inline)] -pub use navbar::Navbar; - -// Offcanvas. -pub mod offcanvas; -#[doc(inline)] -pub use offcanvas::Offcanvas; diff --git a/extensions/pagetop-bootsier/src/theme/aux.rs b/extensions/pagetop-bootsier/src/theme/aux.rs deleted file mode 100644 index 99431fe3..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Colección de elementos auxiliares de Bootstrap para Bootsier. - -mod breakpoint; -pub use breakpoint::BreakPoint; - -mod color; -pub use color::{Color, Opacity}; -pub use color::{ColorBg, ColorText}; - -mod layout; -pub use layout::{ScaleSize, Side}; - -mod border; -pub use border::BorderColor; - -mod rounded; -pub use rounded::RoundedRadius; - -mod button; -pub use button::{ButtonColor, ButtonSize}; diff --git a/extensions/pagetop-bootsier/src/theme/aux/border.rs b/extensions/pagetop-bootsier/src/theme/aux/border.rs deleted file mode 100644 index 43882767..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/border.rs +++ /dev/null @@ -1,87 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::Color; - -/// Colores `border-*` para los bordes ([`classes::Border`](crate::theme::classes::Border)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum BorderColor { - /// No define ninguna clase. - #[default] - Default, - /// Genera internamente clases `border-{color}`. - Theme(Color), - /// Genera internamente clases `border-{color}-subtle` (un tono suavizado del color). - Subtle(Color), - /// Color negro. - Black, - /// Color blanco. - White, -} - -impl BorderColor { - const BORDER: &str = "border"; - const BORDER_PREFIX: &str = "border-"; - - // Devuelve el sufijo de la clase `border-*`, o `None` si no define ninguna clase. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::Default => None, - Self::Theme(_) => Some(""), - Self::Subtle(_) => Some("-subtle"), - Self::Black => Some("-black"), - Self::White => Some("-white"), - } - } - - // Añade la clase `border-*` a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::Theme(c) | Self::Subtle(c) => { - classes.push_str(Self::BORDER_PREFIX); - classes.push_str(c.as_str()); - } - _ => classes.push_str(Self::BORDER), - } - classes.push_str(suffix); - } - } - - /// Devuelve la clase `border-*` correspondiente al color de borde. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(BorderColor::Theme(Color::Primary).to_class(), "border-primary"); - /// assert_eq!(BorderColor::Subtle(Color::Warning).to_class(), "border-warning-subtle"); - /// assert_eq!(BorderColor::Black.to_class(), "border-black"); - /// assert_eq!(BorderColor::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - if let Some(suffix) = self.suffix() { - let base_len = match self { - Self::Theme(c) | Self::Subtle(c) => Self::BORDER_PREFIX.len() + c.as_str().len(), - _ => Self::BORDER.len(), - }; - let mut class = String::with_capacity(base_len + suffix.len()); - match self { - Self::Theme(c) | Self::Subtle(c) => { - class.push_str(Self::BORDER_PREFIX); - class.push_str(c.as_str()); - } - _ => class.push_str(Self::BORDER), - } - class.push_str(suffix); - return class; - } - String::new() - } -} diff --git a/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs b/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs deleted file mode 100644 index 4d9a7626..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs +++ /dev/null @@ -1,114 +0,0 @@ -use pagetop::prelude::*; - -/// Define los puntos de ruptura (*breakpoints*) para aplicar diseño *responsive*. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum BreakPoint { - /// **Menos de 576px**. Dispositivos muy pequeños: teléfonos en modo vertical. - #[default] - None, - /// **576px o más** - Dispositivos pequeños: teléfonos en modo horizontal. - SM, - /// **768px o más** - Dispositivos medianos: tabletas. - MD, - /// **992px o más** - Dispositivos grandes: puestos de escritorio. - LG, - /// **1200px o más** - Dispositivos muy grandes: puestos de escritorio grandes. - XL, - /// **1400px o más** - Dispositivos extragrandes: puestos de escritorio más grandes. - XXL, -} - -impl BreakPoint { - // Devuelve la identificación del punto de ruptura. - #[rustfmt::skip] - #[inline] - pub(crate) const fn as_str(self) -> &'static str { - match self { - Self::None => "", - Self::SM => "sm", - Self::MD => "md", - Self::LG => "lg", - Self::XL => "xl", - Self::XXL => "xxl", - } - } - - // Añade el punto de ruptura con un prefijo y un sufijo (opcional) separados por un guion `-` a - // la cadena de clases. - // - // - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). - // - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. - #[inline] - pub(crate) fn push_class(self, classes: &mut String, prefix: &str, suffix: &str) { - if prefix.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::None => classes.push_str(prefix), - _ => { - classes.push_str(prefix); - classes.push('-'); - classes.push_str(self.as_str()); - } - } - if !suffix.is_empty() { - classes.push('-'); - classes.push_str(suffix); - } - } - - // Devuelve la clase para el punto de ruptura, con un prefijo y un sufijo opcional, separados - // por un guion `-`. - // - // - Para `None` - `prefix` o `prefix-suffix` (si `suffix` no está vacío). - // - Para `SM..XXL` - `prefix-{breakpoint}` o `prefix-{breakpoint}-{suffix}`. - // - Si `prefix` está vacío devuelve `""`. - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // let bp = BreakPoint::MD; - // assert_eq!(bp.class_with("col", ""), "col-md"); - // assert_eq!(bp.class_with("col", "6"), "col-md-6"); - // - // let bp = BreakPoint::None; - // assert_eq!(bp.class_with("offcanvas", ""), "offcanvas"); - // assert_eq!(bp.class_with("col", "12"), "col-12"); - // - // let bp = BreakPoint::LG; - // assert_eq!(bp.class_with("", "3"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str, suffix: &str) -> String { - if prefix.is_empty() { - return String::new(); - } - - let bp = self.as_str(); - let has_bp = !bp.is_empty(); - let has_suffix = !suffix.is_empty(); - - let mut len = prefix.len(); - if has_bp { - len += 1 + bp.len(); - } - if has_suffix { - len += 1 + suffix.len(); - } - let mut class = String::with_capacity(len); - class.push_str(prefix); - if has_bp { - class.push('-'); - class.push_str(bp); - } - if has_suffix { - class.push('-'); - class.push_str(suffix); - } - class - } -} diff --git a/extensions/pagetop-bootsier/src/theme/aux/button.rs b/extensions/pagetop-bootsier/src/theme/aux/button.rs deleted file mode 100644 index 0d1df87d..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/button.rs +++ /dev/null @@ -1,143 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::Color; - -// **< ButtonColor >******************************************************************************** - -/// Variantes de color `btn-*` para botones. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum ButtonColor { - /// No define ninguna clase. - #[default] - Default, - /// Genera internamente clases `btn-{color}` (botón relleno). - Background(Color), - /// Genera `btn-outline-{color}` (fondo transparente y contorno con borde). - Outline(Color), - /// Aplica estilo de los enlaces (`btn-link`), sin caja ni fondo, heredando el color de texto. - Link, -} - -impl ButtonColor { - const BTN_PREFIX: &str = "btn-"; - const BTN_OUTLINE_PREFIX: &str = "btn-outline-"; - const BTN_LINK: &str = "btn-link"; - - // Añade la clase `btn-*` a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if let Self::Default = self { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::Default => unreachable!(), - Self::Background(c) => { - classes.push_str(Self::BTN_PREFIX); - classes.push_str(c.as_str()); - } - Self::Outline(c) => { - classes.push_str(Self::BTN_OUTLINE_PREFIX); - classes.push_str(c.as_str()); - } - Self::Link => { - classes.push_str(Self::BTN_LINK); - } - } - } - - /// Devuelve la clase `btn-*` correspondiente al color del botón. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!( - /// ButtonColor::Background(Color::Primary).to_class(), - /// "btn-primary" - /// ); - /// assert_eq!( - /// ButtonColor::Outline(Color::Danger).to_class(), - /// "btn-outline-danger" - /// ); - /// assert_eq!(ButtonColor::Link.to_class(), "btn-link"); - /// assert_eq!(ButtonColor::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - match self { - Self::Default => String::new(), - Self::Background(c) => { - let color = c.as_str(); - let mut class = String::with_capacity(Self::BTN_PREFIX.len() + color.len()); - class.push_str(Self::BTN_PREFIX); - class.push_str(color); - class - } - Self::Outline(c) => { - let color = c.as_str(); - let mut class = String::with_capacity(Self::BTN_OUTLINE_PREFIX.len() + color.len()); - class.push_str(Self::BTN_OUTLINE_PREFIX); - class.push_str(color); - class - } - Self::Link => Self::BTN_LINK.to_string(), - } - } -} - -// **< ButtonSize >********************************************************************************* - -/// Tamaño visual de un botón. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum ButtonSize { - /// Tamaño por defecto del tema (no añade clase). - #[default] - Default, - /// Botón compacto. - Small, - /// Botón destacado/grande. - Large, -} - -impl ButtonSize { - const BTN_SM: &str = "btn-sm"; - const BTN_LG: &str = "btn-lg"; - - // Añade la clase de tamaño `btn-sm` o `btn-lg` a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if let Self::Default = self { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::Default => unreachable!(), - Self::Small => classes.push_str(Self::BTN_SM), - Self::Large => classes.push_str(Self::BTN_LG), - } - } - - /// Devuelve la clase `btn-sm` o `btn-lg` correspondiente al tamaño del botón. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(ButtonSize::Small.to_class(), "btn-sm"); - /// assert_eq!(ButtonSize::Large.to_class(), "btn-lg"); - /// assert_eq!(ButtonSize::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - match self { - Self::Default => String::new(), - Self::Small => Self::BTN_SM.to_string(), - Self::Large => Self::BTN_LG.to_string(), - } - } -} diff --git a/extensions/pagetop-bootsier/src/theme/aux/color.rs b/extensions/pagetop-bootsier/src/theme/aux/color.rs deleted file mode 100644 index 480ff3d8..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/color.rs +++ /dev/null @@ -1,375 +0,0 @@ -use pagetop::prelude::*; - -// **< Color >************************************************************************************** - -/// Paleta de colores temáticos. -/// -/// Equivalen a los nombres estándar definidos por Bootstrap (`primary`, `secondary`, `success`, -/// etc.). Este tipo enumerado sirve de base para componer las clases de color para fondo -/// ([`classes::Background`](crate::theme::classes::Background)), bordes -/// ([`classes::Border`](crate::theme::classes::Border)) y texto -/// ([`classes::Text`](crate::theme::classes::Text)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Color { - #[default] - Primary, - Secondary, - Success, - Info, - Warning, - Danger, - Light, - Dark, -} - -impl Color { - // Devuelve el nombre del color. - #[rustfmt::skip] - #[inline] - pub(crate) const fn as_str(self) -> &'static str { - match self { - Self::Primary => "primary", - Self::Secondary => "secondary", - Self::Success => "success", - Self::Info => "info", - Self::Warning => "warning", - Self::Danger => "danger", - Self::Light => "light", - Self::Dark => "dark", - } - } - - /* Añade el nombre del color a la cadena de clases (reservado). - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(self.as_str()); - } */ - - /// Devuelve la clase correspondiente al color. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(Color::Primary.to_class(), "primary"); - /// assert_eq!(Color::Danger.to_class(), "danger"); - /// ``` - #[inline] - pub fn to_class(self) -> String { - self.as_str().to_owned() - } -} - -// **< Opacity >************************************************************************************ - -/// Niveles de opacidad (`opacity-*`). -/// -/// Se usa normalmente para graduar la transparencia del color de fondo `bg-opacity-*` -/// ([`classes::Background`](crate::theme::classes::Background)), de los bordes `border-opacity-*` -/// ([`classes::Border`](crate::theme::classes::Border)) o del texto `text-opacity-*` -/// ([`classes::Text`](crate::theme::classes::Text)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Opacity { - /// No define ninguna clase. - #[default] - Default, - /// Permite generar clases `*-opacity-100` (100% de opacidad). - Opaque, - /// Permite generar clases `*-opacity-75` (75%). - SemiOpaque, - /// Permite generar clases `*-opacity-50` (50%). - Half, - /// Permite generar clases `*-opacity-25` (25%). - SemiTransparent, - /// Permite generar clases `*-opacity-10` (10%). - AlmostTransparent, - /// Permite generar clases `*-opacity-0` (0%, totalmente transparente). - Transparent, -} - -impl Opacity { - const OPACITY: &str = "opacity"; - const OPACITY_PREFIX: &str = "-opacity"; - - // Devuelve el sufijo para `*opacity-*`, o `None` si no define ninguna clase. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::Default => None, - Self::Opaque => Some("-100"), - Self::SemiOpaque => Some("-75"), - Self::Half => Some("-50"), - Self::SemiTransparent => Some("-25"), - Self::AlmostTransparent => Some("-10"), - Self::Transparent => Some("-0"), - } - } - - // Añade la opacidad a la cadena de clases usando el prefijo dado (`bg`, `border`, `text`, o - // vacío para `opacity-*`). - #[inline] - pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - if prefix.is_empty() { - classes.push_str(Self::OPACITY); - } else { - classes.push_str(prefix); - classes.push_str(Self::OPACITY_PREFIX); - } - classes.push_str(suffix); - } - } - - // Devuelve la clase de opacidad con el prefijo dado (`bg`, `border`, `text`, o vacío para - // `opacity-*`). - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(Opacity::Opaque.class_with(""), "opacity-100"); - // assert_eq!(Opacity::Half.class_with("bg"), "bg-opacity-50"); - // assert_eq!(Opacity::SemiTransparent.class_with("text"), "text-opacity-25"); - // assert_eq!(Opacity::Default.class_with("bg"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { - if let Some(suffix) = self.suffix() { - let base_len = if prefix.is_empty() { - Self::OPACITY.len() - } else { - prefix.len() + Self::OPACITY_PREFIX.len() - }; - let mut class = String::with_capacity(base_len + suffix.len()); - if prefix.is_empty() { - class.push_str(Self::OPACITY); - } else { - class.push_str(prefix); - class.push_str(Self::OPACITY_PREFIX); - } - class.push_str(suffix); - return class; - } - String::new() - } - - /// Devuelve la clase de opacidad `opacity-*`. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(Opacity::Opaque.to_class(), "opacity-100"); - /// assert_eq!(Opacity::Half.to_class(), "opacity-50"); - /// assert_eq!(Opacity::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - self.class_with("") - } -} - -// **< ColorBg >************************************************************************************ - -/// Colores `bg-*` para el fondo. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum ColorBg { - /// No define ninguna clase. - #[default] - Default, - /// Fondo predefinido del tema (`bg-body`). - Body, - /// Fondo predefinido del tema (`bg-body-secondary`). - BodySecondary, - /// Fondo predefinido del tema (`bg-body-tertiary`). - BodyTertiary, - /// Genera internamente clases `bg-{color}` (p. ej., `bg-primary`). - Theme(Color), - /// Genera internamente clases `bg-{color}-subtle` (un tono suavizado del color). - Subtle(Color), - /// Color negro. - Black, - /// Color blanco. - White, - /// No aplica ningún color de fondo (`bg-transparent`). - Transparent, -} - -impl ColorBg { - const BG: &str = "bg"; - const BG_PREFIX: &str = "bg-"; - - // Devuelve el sufijo de la clase `bg-*`, o `None` si no define ninguna clase. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::Default => None, - Self::Body => Some("-body"), - Self::BodySecondary => Some("-body-secondary"), - Self::BodyTertiary => Some("-body-tertiary"), - Self::Theme(_) => Some(""), - Self::Subtle(_) => Some("-subtle"), - Self::Black => Some("-black"), - Self::White => Some("-white"), - Self::Transparent => Some("-transparent"), - } - } - - // Añade la clase de fondo `bg-*` a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::Theme(c) | Self::Subtle(c) => { - classes.push_str(Self::BG_PREFIX); - classes.push_str(c.as_str()); - } - _ => classes.push_str(Self::BG), - } - classes.push_str(suffix); - } - } - - /// Devuelve la clase `bg-*` correspondiente al fondo. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(ColorBg::Body.to_class(), "bg-body"); - /// assert_eq!(ColorBg::Theme(Color::Primary).to_class(), "bg-primary"); - /// assert_eq!(ColorBg::Subtle(Color::Warning).to_class(), "bg-warning-subtle"); - /// assert_eq!(ColorBg::Transparent.to_class(), "bg-transparent"); - /// assert_eq!(ColorBg::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - if let Some(suffix) = self.suffix() { - let base_len = match self { - Self::Theme(c) | Self::Subtle(c) => Self::BG_PREFIX.len() + c.as_str().len(), - _ => Self::BG.len(), - }; - let mut class = String::with_capacity(base_len + suffix.len()); - match self { - Self::Theme(c) | Self::Subtle(c) => { - class.push_str(Self::BG_PREFIX); - class.push_str(c.as_str()); - } - _ => class.push_str(Self::BG), - } - class.push_str(suffix); - return class; - } - String::new() - } -} - -// **< ColorText >********************************************************************************** - -/// Colores `text-*` para el texto. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum ColorText { - /// No define ninguna clase. - #[default] - Default, - /// Color predefinido del tema (`text-body`). - Body, - /// Color predefinido del tema (`text-body-emphasis`). - BodyEmphasis, - /// Color predefinido del tema (`text-body-secondary`). - BodySecondary, - /// Color predefinido del tema (`text-body-tertiary`). - BodyTertiary, - /// Genera internamente clases `text-{color}`. - Theme(Color), - /// Genera internamente clases `text-{color}-emphasis` (mayor contraste acorde al tema). - Emphasis(Color), - /// Color negro. - Black, - /// Color blanco. - White, -} - -impl ColorText { - const TEXT: &str = "text"; - const TEXT_PREFIX: &str = "text-"; - - // Devuelve el sufijo de la clase `text-*`, o `None` si no define ninguna clase. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::Default => None, - Self::Body => Some("-body"), - Self::BodyEmphasis => Some("-body-emphasis"), - Self::BodySecondary => Some("-body-secondary"), - Self::BodyTertiary => Some("-body-tertiary"), - Self::Theme(_) => Some(""), - Self::Emphasis(_) => Some("-emphasis"), - Self::Black => Some("-black"), - Self::White => Some("-white"), - } - } - - // Añade la clase de texto `text-*` a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - match self { - Self::Theme(c) | Self::Emphasis(c) => { - classes.push_str(Self::TEXT_PREFIX); - classes.push_str(c.as_str()); - } - _ => classes.push_str(Self::TEXT), - } - classes.push_str(suffix); - } - } - - /// Devuelve la clase `text-*` correspondiente al color del texto. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(ColorText::Body.to_class(), "text-body"); - /// assert_eq!(ColorText::Theme(Color::Primary).to_class(), "text-primary"); - /// assert_eq!(ColorText::Emphasis(Color::Danger).to_class(), "text-danger-emphasis"); - /// assert_eq!(ColorText::Black.to_class(), "text-black"); - /// assert_eq!(ColorText::Default.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - if let Some(suffix) = self.suffix() { - let base_len = match self { - Self::Theme(c) | Self::Emphasis(c) => Self::TEXT_PREFIX.len() + c.as_str().len(), - _ => Self::TEXT.len(), - }; - let mut class = String::with_capacity(base_len + suffix.len()); - match self { - Self::Theme(c) | Self::Emphasis(c) => { - class.push_str(Self::TEXT_PREFIX); - class.push_str(c.as_str()); - } - _ => class.push_str(Self::TEXT), - } - class.push_str(suffix); - return class; - } - String::new() - } -} diff --git a/extensions/pagetop-bootsier/src/theme/aux/layout.rs b/extensions/pagetop-bootsier/src/theme/aux/layout.rs deleted file mode 100644 index 1d351582..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/layout.rs +++ /dev/null @@ -1,104 +0,0 @@ -use pagetop::prelude::*; - -// **< ScaleSize >********************************************************************************** - -/// Escala discreta de tamaños para definir clases utilitarias. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum ScaleSize { - /// Sin tamaño (no define ninguna clase). - #[default] - None, - /// Tamaño automático. - Auto, - /// Escala cero. - Zero, - /// Escala uno. - One, - /// Escala dos. - Two, - /// Escala tres. - Three, - /// Escala cuatro. - Four, - /// Escala cinco. - Five, -} - -impl ScaleSize { - // Devuelve el sufijo para el tamaño (`"-0"`, `"-1"`, etc.), o `None` si no define ninguna - // clase, o `""` para el tamaño automático. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::None => None, - Self::Auto => Some(""), - Self::Zero => Some("-0"), - Self::One => Some("-1"), - Self::Two => Some("-2"), - Self::Three => Some("-3"), - Self::Four => Some("-4"), - Self::Five => Some("-5"), - } - } - - // Añade el tamaño a la cadena de clases usando el prefijo dado. - #[inline] - pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { - if !prefix.is_empty() { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(prefix); - classes.push_str(suffix); - } - } - } - - /* Devuelve la clase del tamaño para el prefijo, o una cadena vacía si no aplica (reservado). - // - // # Ejemplo - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(ScaleSize::Auto.class_with("border"), "border"); - // assert_eq!(ScaleSize::Zero.class_with("m"), "m-0"); - // assert_eq!(ScaleSize::Three.class_with("p"), "p-3"); - // assert_eq!(ScaleSize::None.class_with("border"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { - if !prefix.is_empty() { - if let Some(suffix) = self.suffix() { - let mut class = String::with_capacity(prefix.len() + suffix.len()); - class.push_str(prefix); - class.push_str(suffix); - return class; - } - } - String::new() - } */ -} - -// **< Side >*************************************************************************************** - -/// Lados sobre los que aplicar una clase utilitaria (respetando LTR/RTL). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Side { - /// Todos los lados. - #[default] - All, - /// Lado superior. - Top, - /// Lado inferior. - Bottom, - /// Lado lógico de inicio (respetando RTL). - Start, - /// Lado lógico de fin (respetando RTL). - End, - /// Lados lógicos laterales (abreviatura *x*). - LeftAndRight, - /// Lados superior e inferior (abreviatura *y*). - TopAndBottom, -} diff --git a/extensions/pagetop-bootsier/src/theme/aux/rounded.rs b/extensions/pagetop-bootsier/src/theme/aux/rounded.rs deleted file mode 100644 index 20e061d6..00000000 --- a/extensions/pagetop-bootsier/src/theme/aux/rounded.rs +++ /dev/null @@ -1,117 +0,0 @@ -use pagetop::prelude::*; - -/// Radio para el redondeo de esquinas ([`classes::Rounded`](crate::theme::classes::Rounded)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum RoundedRadius { - /// No define ninguna clase. - #[default] - None, - /// Genera `rounded` (radio por defecto del tema). - Default, - /// Genera `rounded-0` (sin redondeo). - Zero, - /// Genera `rounded-1`. - Scale1, - /// Genera `rounded-2`. - Scale2, - /// Genera `rounded-3`. - Scale3, - /// Genera `rounded-4`. - Scale4, - /// Genera `rounded-5`. - Scale5, - /// Genera `rounded-circle`. - Circle, - /// Genera `rounded-pill`. - Pill, -} - -impl RoundedRadius { - const ROUNDED: &str = "rounded"; - - // Devuelve el sufijo para `*rounded-*`, o `None` si no define ninguna clase, o `""` para el - // redondeo por defecto. - #[rustfmt::skip] - #[inline] - const fn suffix(self) -> Option<&'static str> { - match self { - Self::None => None, - Self::Default => Some(""), - Self::Zero => Some("-0"), - Self::Scale1 => Some("-1"), - Self::Scale2 => Some("-2"), - Self::Scale3 => Some("-3"), - Self::Scale4 => Some("-4"), - Self::Scale5 => Some("-5"), - Self::Circle => Some("-circle"), - Self::Pill => Some("-pill"), - } - } - - // Añade el redondeo de esquinas a la cadena de clases usando el prefijo dado (`rounded-top`, - // `rounded-bottom-start`, o vacío para `rounded-*`). - #[inline] - pub(crate) fn push_class(self, classes: &mut String, prefix: &str) { - if let Some(suffix) = self.suffix() { - if !classes.is_empty() { - classes.push(' '); - } - if prefix.is_empty() { - classes.push_str(Self::ROUNDED); - } else { - classes.push_str(prefix); - } - classes.push_str(suffix); - } - } - - // Devuelve la clase para el redondeo de esquinas con el prefijo dado (`rounded-top`, - // `rounded-bottom-start`, o vacío para `rounded-*`). - // - // # Ejemplos - // - // ```rust - // # use pagetop_bootsier::prelude::*; - // assert_eq!(RoundedRadius::Scale2.class_with(""), "rounded-2"); - // assert_eq!(RoundedRadius::Zero.class_with("rounded-top"), "rounded-top-0"); - // assert_eq!(RoundedRadius::Scale3.class_with("rounded-top-end"), "rounded-top-end-3"); - // assert_eq!(RoundedRadius::Circle.class_with(""), "rounded-circle"); - // assert_eq!(RoundedRadius::None.class_with("rounded-bottom-start"), ""); - // ``` - #[inline] - pub(crate) fn class_with(self, prefix: &str) -> String { - if let Some(suffix) = self.suffix() { - let base_len = if prefix.is_empty() { - Self::ROUNDED.len() - } else { - prefix.len() - }; - let mut class = String::with_capacity(base_len + suffix.len()); - if prefix.is_empty() { - class.push_str(Self::ROUNDED); - } else { - class.push_str(prefix); - } - class.push_str(suffix); - return class; - } - String::new() - } - - /// Devuelve la clase `rounded-*` para el redondeo de esquinas. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop_bootsier::prelude::*; - /// assert_eq!(RoundedRadius::Default.to_class(), "rounded"); - /// assert_eq!(RoundedRadius::Zero.to_class(), "rounded-0"); - /// assert_eq!(RoundedRadius::Scale3.to_class(), "rounded-3"); - /// assert_eq!(RoundedRadius::Circle.to_class(), "rounded-circle"); - /// assert_eq!(RoundedRadius::None.to_class(), ""); - /// ``` - #[inline] - pub fn to_class(self) -> String { - self.class_with("") - } -} diff --git a/extensions/pagetop-bootsier/src/theme/classes.rs b/extensions/pagetop-bootsier/src/theme/classes.rs deleted file mode 100644 index 9e6c234d..00000000 --- a/extensions/pagetop-bootsier/src/theme/classes.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Conjunto de clases para aplicar en componentes del tema. - -mod color; -pub use color::{Background, Text}; - -mod border; -pub use border::Border; - -mod rounded; -pub use rounded::Rounded; - -mod layout; -pub use layout::{Margin, Padding}; diff --git a/extensions/pagetop-bootsier/src/theme/classes/border.rs b/extensions/pagetop-bootsier/src/theme/classes/border.rs deleted file mode 100644 index 3095498c..00000000 --- a/extensions/pagetop-bootsier/src/theme/classes/border.rs +++ /dev/null @@ -1,175 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::{BorderColor, Opacity, ScaleSize, Side}; - -/// Clases para crear **bordes**. -/// -/// Permite: -/// -/// - Iniciar un borde sin tamaño inicial (`Border::default()`). -/// - Crear un borde con tamaño por defecto (`Border::new()`). -/// - Ajustar el tamaño de cada **lado lógico** (`side`, respetando LTR/RTL). -/// - Definir un tamaño **global** para todo el borde (`size`). -/// - Aplicar un **color** al borde (`BorderColor`). -/// - Aplicar un nivel de **opacidad** (`Opacity`). -/// -/// # Comportamiento aditivo / sustractivo -/// -/// - **Aditivo**: basta con crear un borde sin tamaño con `classes::Border::default()` para ir -/// añadiendo cada lado lógico con el tamaño deseado usando `ScaleSize::{One..Five}`. -/// -/// - **Sustractivo**: se crea un borde con tamaño predefinido, p. ej. usando -/// `classes::Border::new()` o `classes::Border::with(ScaleSize::Two)` y eliminar los lados -/// deseados con `ScaleSize::Zero`. -/// -/// - **Anchos diferentes por lado**: usando `ScaleSize::{Zero..Five}` en cada lado deseado. -/// -/// # Ejemplos -/// -/// **Borde global:** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let b = classes::Border::with(ScaleSize::Two); -/// assert_eq!(b.to_class(), "border-2"); -/// ``` -/// -/// **Aditivo (solo borde superior):** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let b = classes::Border::default().with_side(Side::Top, ScaleSize::One); -/// assert_eq!(b.to_class(), "border-top-1"); -/// ``` -/// -/// **Sustractivo (borde global menos el superior):** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let b = classes::Border::new().with_side(Side::Top, ScaleSize::Zero); -/// assert_eq!(b.to_class(), "border border-top-0"); -/// ``` -/// -/// **Ancho por lado (lado lógico inicial a 2 y final a 4):** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let b = classes::Border::default() -/// .with_side(Side::Start, ScaleSize::Two) -/// .with_side(Side::End, ScaleSize::Four); -/// assert_eq!(b.to_class(), "border-end-4 border-start-2"); -/// ``` -/// -/// **Combinado (ejemplo completo):** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let b = classes::Border::new() // Borde por defecto. -/// .with_side(Side::Top, ScaleSize::Zero) // Quita borde superior. -/// .with_side(Side::End, ScaleSize::Three) // Ancho 3 para el lado lógico final. -/// .with_color(BorderColor::Theme(Color::Primary)) -/// .with_opacity(Opacity::Half); -/// -/// assert_eq!(b.to_class(), "border border-top-0 border-end-3 border-primary border-opacity-50"); -/// ``` -#[rustfmt::skip] -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Border { - all : ScaleSize, - top : ScaleSize, - end : ScaleSize, - bottom : ScaleSize, - start : ScaleSize, - color : BorderColor, - opacity: Opacity, -} - -impl Border { - /// Prepara un borde del tamaño predefinido. Equivale a `border` (ancho por defecto del tema). - pub fn new() -> Self { - Self::with(ScaleSize::Auto) - } - - /// Crea un borde **con un tamaño global** (`size`). - pub fn with(size: ScaleSize) -> Self { - Self::default().with_side(Side::All, size) - } - - // **< Border BUILDER >************************************************************************* - - pub fn with_side(mut self, side: Side, size: ScaleSize) -> Self { - match side { - Side::All => self.all = size, - Side::Top => self.top = size, - Side::Bottom => self.bottom = size, - Side::Start => self.start = size, - Side::End => self.end = size, - Side::LeftAndRight => { - self.start = size; - self.end = size; - } - Side::TopAndBottom => { - self.top = size; - self.bottom = size; - } - }; - self - } - - /// Establece el color del borde. - pub fn with_color(mut self, color: BorderColor) -> Self { - self.color = color; - self - } - - /// Establece la opacidad del borde. - pub fn with_opacity(mut self, opacity: Opacity) -> Self { - self.opacity = opacity; - self - } - - // **< Border HELPERS >************************************************************************* - - /// Añade las clases de borde a la cadena de clases. - /// - /// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`, - /// *color* y *opacidad*; respetando LTR/RTL y omitiendo las definiciones vacías. - #[rustfmt::skip] - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - self.all .push_class(classes, "border"); - self.top .push_class(classes, "border-top"); - self.end .push_class(classes, "border-end"); - self.bottom .push_class(classes, "border-bottom"); - self.start .push_class(classes, "border-start"); - self.color .push_class(classes); - self.opacity.push_class(classes, "border"); - } - - /// Devuelve las clases de borde como cadena (`"border-2"`, - /// `"border border-top-0 border-end-3 border-primary border-opacity-50"`, etc.). - /// - /// Si no se define ningún tamaño, color ni opacidad, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let mut classes = String::new(); - self.push_class(&mut classes); - classes - } -} - -/// Atajo para crear un [`classes::Border`](crate::theme::classes::Border) a partir de un tamaño -/// [`ScaleSize`] aplicado a todo el borde. -/// -/// # Ejemplos -/// -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// // Convertir explícitamente con `From::from`: -/// let b = classes::Border::from(ScaleSize::Two); -/// assert_eq!(b.to_class(), "border-2"); -/// -/// // Convertir implícitamente con `into()`: -/// let b: classes::Border = ScaleSize::Auto.into(); -/// assert_eq!(b.to_class(), "border"); -/// ``` -impl From<ScaleSize> for Border { - fn from(size: ScaleSize) -> Self { - Self::with(size) - } -} diff --git a/extensions/pagetop-bootsier/src/theme/classes/color.rs b/extensions/pagetop-bootsier/src/theme/classes/color.rs deleted file mode 100644 index 162b7849..00000000 --- a/extensions/pagetop-bootsier/src/theme/classes/color.rs +++ /dev/null @@ -1,230 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::{ColorBg, ColorText, Opacity}; - -// **< Background >********************************************************************************* - -/// Clases para establecer **color/opacidad del fondo**. -/// -/// # Ejemplos -/// -/// ``` -/// # use pagetop_bootsier::prelude::*; -/// // Sin clases. -/// let s = classes::Background::new(); -/// assert_eq!(s.to_class(), ""); -/// -/// // Sólo color de fondo. -/// let s = classes::Background::with(ColorBg::Theme(Color::Primary)); -/// assert_eq!(s.to_class(), "bg-primary"); -/// -/// // Color más opacidad. -/// let s = classes::Background::with(ColorBg::BodySecondary).with_opacity(Opacity::Half); -/// assert_eq!(s.to_class(), "bg-body-secondary bg-opacity-50"); -/// -/// // Usando `From<ColorBg>`. -/// let s: classes::Background = ColorBg::Black.into(); -/// assert_eq!(s.to_class(), "bg-black"); -/// -/// // Usando `From<(ColorBg, Opacity)>`. -/// let s: classes::Background = (ColorBg::White, Opacity::SemiTransparent).into(); -/// assert_eq!(s.to_class(), "bg-white bg-opacity-25"); -/// ``` -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Background { - color: ColorBg, - opacity: Opacity, -} - -impl Background { - /// Prepara un nuevo estilo para aplicar al fondo. - pub fn new() -> Self { - Self::default() - } - - /// Crea un estilo fijando el color de fondo (`bg-*`). - pub fn with(color: ColorBg) -> Self { - Self::default().with_color(color) - } - - // **< Background BUILDER >********************************************************************* - - /// Establece el color de fondo (`bg-*`). - pub fn with_color(mut self, color: ColorBg) -> Self { - self.color = color; - self - } - - /// Establece la opacidad del fondo (`bg-opacity-*`). - pub fn with_opacity(mut self, opacity: Opacity) -> Self { - self.opacity = opacity; - self - } - - // **< Background HELPERS >********************************************************************* - - /// Añade las clases de fondo a la cadena de clases. - /// - /// Concatena, en este orden, color del fondo (`bg-*`) y opacidad (`bg-opacity-*`), - /// omitiendo los fragmentos vacíos. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - self.color.push_class(classes); - self.opacity.push_class(classes, "bg"); - } - - /// Devuelve las clases de fondo como cadena (`"bg-primary"`, `"bg-body-secondary bg-opacity-50"`, etc.). - /// - /// Si no se define ni color ni opacidad, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let mut classes = String::new(); - self.push_class(&mut classes); - classes - } -} - -impl From<(ColorBg, Opacity)> for Background { - /// Atajo para crear un [`classes::Background`](crate::theme::classes::Background) a partir del color de fondo y - /// la opacidad. - /// - /// # Ejemplo - /// - /// ``` - /// # use pagetop_bootsier::prelude::*; - /// let s: classes::Background = (ColorBg::White, Opacity::SemiTransparent).into(); - /// assert_eq!(s.to_class(), "bg-white bg-opacity-25"); - /// ``` - fn from((color, opacity): (ColorBg, Opacity)) -> Self { - Background::with(color).with_opacity(opacity) - } -} - -impl From<ColorBg> for Background { - /// Atajo para crear un [`classes::Background`](crate::theme::classes::Background) a partir del color de fondo. - /// - /// # Ejemplo - /// - /// ``` - /// # use pagetop_bootsier::prelude::*; - /// let s: classes::Background = ColorBg::Black.into(); - /// assert_eq!(s.to_class(), "bg-black"); - /// ``` - fn from(color: ColorBg) -> Self { - Background::with(color) - } -} - -// **< Text >*************************************************************************************** - -/// Clases para establecer **color/opacidad del texto**. -/// -/// # Ejemplos -/// -/// ``` -/// # use pagetop_bootsier::prelude::*; -/// // Sin clases. -/// let s = classes::Text::new(); -/// assert_eq!(s.to_class(), ""); -/// -/// // Sólo color del texto. -/// let s = classes::Text::with(ColorText::Theme(Color::Primary)); -/// assert_eq!(s.to_class(), "text-primary"); -/// -/// // Color del texto y opacidad. -/// let s = classes::Text::new().with_color(ColorText::White).with_opacity(Opacity::SemiTransparent); -/// assert_eq!(s.to_class(), "text-white text-opacity-25"); -/// -/// // Usando `From<ColorText>`. -/// let s: classes::Text = ColorText::Black.into(); -/// assert_eq!(s.to_class(), "text-black"); -/// -/// // Usando `From<(ColorText, Opacity)>`. -/// let s: classes::Text = (ColorText::Theme(Color::Danger), Opacity::Opaque).into(); -/// assert_eq!(s.to_class(), "text-danger text-opacity-100"); -/// ``` -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Text { - color: ColorText, - opacity: Opacity, -} - -impl Text { - /// Prepara un nuevo estilo para aplicar al texto. - pub fn new() -> Self { - Self::default() - } - - /// Crea un estilo fijando el color del texto (`text-*`). - pub fn with(color: ColorText) -> Self { - Self::default().with_color(color) - } - - // **< Text BUILDER >*************************************************************************** - - /// Establece el color del texto (`text-*`). - pub fn with_color(mut self, color: ColorText) -> Self { - self.color = color; - self - } - - /// Establece la opacidad del texto (`text-opacity-*`). - pub fn with_opacity(mut self, opacity: Opacity) -> Self { - self.opacity = opacity; - self - } - - // **< Text HELPERS >*************************************************************************** - - /// Añade las clases de texto a la cadena de clases. - /// - /// Concatena, en este orden, `text-*` y `text-opacity-*`, omitiendo los fragmentos vacíos. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - self.color.push_class(classes); - self.opacity.push_class(classes, "text"); - } - - /// Devuelve las clases de texto como cadena (`"text-primary"`, `"text-white text-opacity-25"`, - /// etc.). - /// - /// Si no se define ni color ni opacidad, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let mut classes = String::new(); - self.push_class(&mut classes); - classes - } -} - -impl From<(ColorText, Opacity)> for Text { - /// Atajo para crear un [`classes::Text`](crate::theme::classes::Text) a partir del color del - /// texto y su opacidad. - /// - /// # Ejemplo - /// - /// ``` - /// # use pagetop_bootsier::prelude::*; - /// let s: classes::Text = (ColorText::Theme(Color::Danger), Opacity::Opaque).into(); - /// assert_eq!(s.to_class(), "text-danger text-opacity-100"); - /// ``` - fn from((color, opacity): (ColorText, Opacity)) -> Self { - Text::with(color).with_opacity(opacity) - } -} - -impl From<ColorText> for Text { - /// Atajo para crear un [`classes::Text`](crate::theme::classes::Text) a partir del color del - /// texto. - /// - /// # Ejemplo - /// - /// ``` - /// # use pagetop_bootsier::prelude::*; - /// let s: classes::Text = ColorText::Black.into(); - /// assert_eq!(s.to_class(), "text-black"); - /// ``` - fn from(color: ColorText) -> Self { - Text::with(color) - } -} diff --git a/extensions/pagetop-bootsier/src/theme/classes/layout.rs b/extensions/pagetop-bootsier/src/theme/classes/layout.rs deleted file mode 100644 index e9d7e248..00000000 --- a/extensions/pagetop-bootsier/src/theme/classes/layout.rs +++ /dev/null @@ -1,205 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::{ScaleSize, Side}; -use crate::theme::BreakPoint; - -// **< Margin >************************************************************************************* - -/// Clases para establecer **margin** por lado, tamaño y punto de ruptura. -/// -/// # Ejemplos -/// -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let m = classes::Margin::with(Side::Top, ScaleSize::Three); -/// assert_eq!(m.to_class(), "mt-3"); -/// -/// let m = classes::Margin::with(Side::Start, ScaleSize::Auto).with_breakpoint(BreakPoint::LG); -/// assert_eq!(m.to_class(), "ms-lg-auto"); -/// -/// let m = classes::Margin::with(Side::All, ScaleSize::None); -/// assert_eq!(m.to_class(), ""); -/// ``` -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Margin { - side: Side, - size: ScaleSize, - breakpoint: BreakPoint, -} - -impl Margin { - /// Crea un **margin** indicando lado(s) y tamaño. Por defecto no se aplica a ningún punto de - /// ruptura. - pub fn with(side: Side, size: ScaleSize) -> Self { - Margin { - side, - size, - breakpoint: BreakPoint::None, - } - } - - // **< Margin BUILDER >************************************************************************* - - /// Establece el punto de ruptura a partir del cual se empieza a aplicar el **margin**. - pub fn with_breakpoint(mut self, breakpoint: BreakPoint) -> Self { - self.breakpoint = breakpoint; - self - } - - // **< Margin HELPERS >************************************************************************* - - // Devuelve el prefijo `m*` según el lado. - #[rustfmt::skip] - #[inline] - const fn side_prefix(&self) -> &'static str { - match self.side { - Side::All => "m", - Side::Top => "mt", - Side::Bottom => "mb", - Side::Start => "ms", - Side::End => "me", - Side::LeftAndRight => "mx", - Side::TopAndBottom => "my", - } - } - - // Devuelve el sufijo del tamaño (`auto`, `0`..`5`), o `None` si no define clase. - #[rustfmt::skip] - #[inline] - const fn size_suffix(&self) -> Option<&'static str> { - match self.size { - ScaleSize::None => None, - ScaleSize::Auto => Some("auto"), - ScaleSize::Zero => Some("0"), - ScaleSize::One => Some("1"), - ScaleSize::Two => Some("2"), - ScaleSize::Three => Some("3"), - ScaleSize::Four => Some("4"), - ScaleSize::Five => Some("5"), - } - } - - /* Añade la clase de **margin** a la cadena de clases (reservado). - // - // No añade nada si `size` es `ScaleSize::None`. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let Some(size) = self.size_suffix() else { - return; - }; - self.breakpoint - .push_class(classes, self.side_prefix(), size); - } */ - - /// Devuelve la clase de **margin** como cadena (`"mt-3"`, `"ms-lg-auto"`, etc.). - /// - /// Si `size` es `ScaleSize::None`, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let Some(size) = self.size_suffix() else { - return String::new(); - }; - self.breakpoint.class_with(self.side_prefix(), size) - } -} - -// **< Padding >************************************************************************************ - -/// Clases para establecer **padding** por lado, tamaño y punto de ruptura. -/// -/// # Ejemplos -/// -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let p = classes::Padding::with(Side::LeftAndRight, ScaleSize::Two); -/// assert_eq!(p.to_class(), "px-2"); -/// -/// let p = classes::Padding::with(Side::End, ScaleSize::Four).with_breakpoint(BreakPoint::SM); -/// assert_eq!(p.to_class(), "pe-sm-4"); -/// -/// let p = classes::Padding::with(Side::All, ScaleSize::Auto); -/// assert_eq!(p.to_class(), ""); // `Auto` no aplica a padding. -/// ``` -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Padding { - side: Side, - size: ScaleSize, - breakpoint: BreakPoint, -} - -impl Padding { - /// Crea un **padding** indicando lado(s) y tamaño. Por defecto no se aplica a ningún punto de - /// ruptura. - pub fn with(side: Side, size: ScaleSize) -> Self { - Padding { - side, - size, - breakpoint: BreakPoint::None, - } - } - - // **< Padding BUILDER >************************************************************************ - - /// Establece el punto de ruptura a partir del cual se empieza a aplicar el **padding**. - pub fn with_breakpoint(mut self, breakpoint: BreakPoint) -> Self { - self.breakpoint = breakpoint; - self - } - - // **< Padding HELPERS >************************************************************************ - - // Devuelve el prefijo `p*` según el lado. - #[rustfmt::skip] - #[inline] - const fn prefix(&self) -> &'static str { - match self.side { - Side::All => "p", - Side::Top => "pt", - Side::Bottom => "pb", - Side::Start => "ps", - Side::End => "pe", - Side::LeftAndRight => "px", - Side::TopAndBottom => "py", - } - } - - // Devuelve el sufijo del tamaño (`0`..`5`), o `None` si no define clase. - // - // Nota: `ScaleSize::Auto` **no aplica** a padding ⇒ devuelve `None`. - #[rustfmt::skip] - #[inline] - const fn suffix(&self) -> Option<&'static str> { - match self.size { - ScaleSize::None => None, - ScaleSize::Auto => None, - ScaleSize::Zero => Some("0"), - ScaleSize::One => Some("1"), - ScaleSize::Two => Some("2"), - ScaleSize::Three => Some("3"), - ScaleSize::Four => Some("4"), - ScaleSize::Five => Some("5"), - } - } - - /* Añade la clase de **padding** a la cadena de clases (reservado). - // - // No añade nada si `size` es `ScaleSize::None` o `ScaleSize::Auto`. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let Some(size) = self.suffix() else { - return; - }; - self.breakpoint.push_class(classes, self.prefix(), size); - } */ - - // Devuelve la clase de **padding** como cadena (`"px-2"`, `"pe-sm-4"`, etc.). - // - // Si `size` es `ScaleSize::None` o `ScaleSize::Auto`, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let Some(size) = self.suffix() else { - return String::new(); - }; - self.breakpoint.class_with(self.prefix(), size) - } -} diff --git a/extensions/pagetop-bootsier/src/theme/classes/rounded.rs b/extensions/pagetop-bootsier/src/theme/classes/rounded.rs deleted file mode 100644 index 58d50b86..00000000 --- a/extensions/pagetop-bootsier/src/theme/classes/rounded.rs +++ /dev/null @@ -1,169 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::RoundedRadius; - -/// Clases para definir **esquinas redondeadas**. -/// -/// Permite: -/// -/// - Definir un radio **global para todas las esquinas** (`radius`). -/// - Ajustar el radio asociado a las **esquinas de cada lado lógico** (`top`, `end`, `bottom`, -/// `start`, **en este orden**, respetando LTR/RTL). -/// - Ajustar el radio de las **esquinas concretas** (`top-start`, `top-end`, `bottom-start`, -/// `bottom-end`, **en este orden**, respetando LTR/RTL). -/// -/// # Ejemplos -/// -/// **Radio global:** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let r = classes::Rounded::with(RoundedRadius::Default); -/// assert_eq!(r.to_class(), "rounded"); -/// ``` -/// -/// **Sin redondeo:** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let r = classes::Rounded::new(); -/// assert_eq!(r.to_class(), ""); -/// ``` -/// -/// **Radio en las esquinas de un lado lógico:** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let r = classes::Rounded::new().with_end(RoundedRadius::Scale2); -/// assert_eq!(r.to_class(), "rounded-end-2"); -/// ``` -/// -/// **Radio en una esquina concreta:** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let r = classes::Rounded::new().with_top_start(RoundedRadius::Scale3); -/// assert_eq!(r.to_class(), "rounded-top-start-3"); -/// ``` -/// -/// **Combinado (ejemplo completo):** -/// ```rust -/// # use pagetop_bootsier::prelude::*; -/// let r = classes::Rounded::new() -/// .with_top(RoundedRadius::Default) // Añade redondeo arriba. -/// .with_bottom_start(RoundedRadius::Scale4) // Añade una esquina redondeada concreta. -/// .with_bottom_end(RoundedRadius::Circle); // Añade redondeo extremo en otra esquina. -/// -/// assert_eq!(r.to_class(), "rounded-top rounded-bottom-start-4 rounded-bottom-end-circle"); -/// ``` -#[rustfmt::skip] -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub struct Rounded { - radius : RoundedRadius, - top : RoundedRadius, - end : RoundedRadius, - bottom : RoundedRadius, - start : RoundedRadius, - top_start : RoundedRadius, - top_end : RoundedRadius, - bottom_start: RoundedRadius, - bottom_end : RoundedRadius, -} - -impl Rounded { - /// Prepara las esquinas **sin redondeo global** de partida. - pub fn new() -> Self { - Self::default() - } - - /// Crea las esquinas **con redondeo global** (`radius`). - pub fn with(radius: RoundedRadius) -> Self { - Self::default().with_radius(radius) - } - - // **< Rounded BUILDER >************************************************************************ - - /// Establece el radio global de las esquinas (`rounded*`). - pub fn with_radius(mut self, radius: RoundedRadius) -> Self { - self.radius = radius; - self - } - - /// Establece el radio en las esquinas del lado superior (`rounded-top-*`). - pub fn with_top(mut self, radius: RoundedRadius) -> Self { - self.top = radius; - self - } - - /// Establece el radio en las esquinas del lado lógico final (`rounded-end-*`). Respeta LTR/RTL. - pub fn with_end(mut self, radius: RoundedRadius) -> Self { - self.end = radius; - self - } - - /// Establece el radio en las esquinas del lado inferior (`rounded-bottom-*`). - pub fn with_bottom(mut self, radius: RoundedRadius) -> Self { - self.bottom = radius; - self - } - - /// Establece el radio en las esquinas del lado lógico inicial (`rounded-start-*`). Respeta - /// LTR/RTL. - pub fn with_start(mut self, radius: RoundedRadius) -> Self { - self.start = radius; - self - } - - /// Establece el radio en la esquina superior-inicial (`rounded-top-start-*`). Respeta LTR/RTL. - pub fn with_top_start(mut self, radius: RoundedRadius) -> Self { - self.top_start = radius; - self - } - - /// Establece el radio en la esquina superior-final (`rounded-top-end-*`). Respeta LTR/RTL. - pub fn with_top_end(mut self, radius: RoundedRadius) -> Self { - self.top_end = radius; - self - } - - /// Establece el radio en la esquina inferior-inicial (`rounded-bottom-start-*`). Respeta - /// LTR/RTL. - pub fn with_bottom_start(mut self, radius: RoundedRadius) -> Self { - self.bottom_start = radius; - self - } - - /// Establece el radio en la esquina inferior-final (`rounded-bottom-end-*`). Respeta LTR/RTL. - pub fn with_bottom_end(mut self, radius: RoundedRadius) -> Self { - self.bottom_end = radius; - self - } - - // **< Rounded HELPERS >************************************************************************ - - /// Añade las clases de redondeo a la cadena de clases. - /// - /// Concatena, en este orden, las clases para *global*, `top`, `end`, `bottom`, `start`, - /// `top-start`, `top-end`, `bottom-start` y `bottom-end`; respetando LTR/RTL y omitiendo las - /// definiciones vacías. - #[rustfmt::skip] - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - self.radius .push_class(classes, ""); - self.top .push_class(classes, "rounded-top"); - self.end .push_class(classes, "rounded-end"); - self.bottom .push_class(classes, "rounded-bottom"); - self.start .push_class(classes, "rounded-start"); - self.top_start .push_class(classes, "rounded-top-start"); - self.top_end .push_class(classes, "rounded-top-end"); - self.bottom_start.push_class(classes, "rounded-bottom-start"); - self.bottom_end .push_class(classes, "rounded-bottom-end"); - } - - /// Devuelve las clases de redondeo como cadena (`"rounded"`, - /// `"rounded-top rounded-bottom-start-4 rounded-bottom-end-circle"`, etc.). - /// - /// Si no se define ningún radio, devuelve `""`. - #[inline] - pub fn to_class(self) -> String { - let mut classes = String::new(); - self.push_class(&mut classes); - classes - } -} diff --git a/extensions/pagetop-bootsier/src/theme/container.rs b/extensions/pagetop-bootsier/src/theme/container.rs deleted file mode 100644 index a860110f..00000000 --- a/extensions/pagetop-bootsier/src/theme/container.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Definiciones para crear contenedores de componentes ([`Container`]). -//! -//! Cada contenedor envuelve contenido usando la etiqueta semántica indicada por -//! [`container::Kind`](crate::theme::container::Kind). -//! -//! Con [`container::Width`](crate::theme::container::Width) se puede definir el ancho y el -//! comportamiento *responsive* del contenedor. También permite aplicar utilidades de estilo para el -//! fondo, texto, borde o esquinas redondeadas. -//! -//! # Ejemplo -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let main = Container::main() -//! .with_id("main-page") -//! .with_width(container::Width::From(BreakPoint::LG)); -//! ``` - -mod props; -pub use props::{Kind, Width}; - -mod component; -pub use component::Container; diff --git a/extensions/pagetop-bootsier/src/theme/container/component.rs b/extensions/pagetop-bootsier/src/theme/container/component.rs deleted file mode 100644 index 068d24a3..00000000 --- a/extensions/pagetop-bootsier/src/theme/container/component.rs +++ /dev/null @@ -1,184 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -/// Componente para crear un **contenedor de componentes**. -/// -/// Envuelve un contenido con la etiqueta HTML indicada por [`container::Kind`]. Sólo se renderiza -/// si existen componentes hijos (*children*). -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Container { - id : AttrId, - classes : AttrClasses, - container_kind : container::Kind, - container_width: container::Width, - children : Children, -} - -impl Component for Container { - fn new() -> Self { - Container::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, self.width().to_class()); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let output = self.children().render(cx); - if output.is_empty() { - return PrepareMarkup::None; - } - let style = match self.width() { - container::Width::FluidMax(w) if w.is_measurable() => { - Some(join!("max-width: ", w.to_string(), ";")) - } - _ => None, - }; - match self.container_kind() { - container::Kind::Default => PrepareMarkup::With(html! { - div id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - container::Kind::Main => PrepareMarkup::With(html! { - main id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - container::Kind::Header => PrepareMarkup::With(html! { - header id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - container::Kind::Footer => PrepareMarkup::With(html! { - footer id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - container::Kind::Section => PrepareMarkup::With(html! { - section id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - container::Kind::Article => PrepareMarkup::With(html! { - article id=[self.id()] class=[self.classes().get()] style=[style] { - (output) - } - }), - } - } -} - -impl Container { - /// Crea un contenedor de tipo `Main` (`<main>`). - pub fn main() -> Self { - Container { - container_kind: container::Kind::Main, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Header` (`<header>`). - pub fn header() -> Self { - Container { - container_kind: container::Kind::Header, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Footer` (`<footer>`). - pub fn footer() -> Self { - Container { - container_kind: container::Kind::Footer, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Section` (`<section>`). - pub fn section() -> Self { - Container { - container_kind: container::Kind::Section, - ..Default::default() - } - } - - /// Crea un contenedor de tipo `Article` (`<article>`). - pub fn article() -> Self { - Container { - container_kind: container::Kind::Article, - ..Default::default() - } - } - - // **< Container BUILDER >********************************************************************** - - /// Establece el identificador único (`id`) del contenedor. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al contenedor. - /// - /// También acepta clases predefinidas para: - /// - /// - Modificar el color de fondo ([`classes::Background`]). - /// - Definir la apariencia del texto ([`classes::Text`]). - /// - Establecer bordes ([`classes::Border`]). - /// - Redondear las esquinas ([`classes::Rounded`]). - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Establece el comportamiento del ancho para el contenedor. - #[builder_fn] - pub fn with_width(mut self, width: container::Width) -> Self { - self.container_width = width; - self - } - - /// Añade un nuevo componente hijo al contenedor. - #[inline] - pub fn add_child(mut self, component: impl Component) -> Self { - self.children.add(Child::with(component)); - self - } - - /// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`]. - #[builder_fn] - pub fn with_child(mut self, op: ChildOp) -> Self { - self.children.alter_child(op); - self - } - - // **< Container GETTERS >********************************************************************** - - /// Devuelve las clases CSS asociadas al contenedor. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el tipo semántico del contenedor. - pub fn container_kind(&self) -> &container::Kind { - &self.container_kind - } - - /// Devuelve el comportamiento para el ancho del contenedor. - pub fn width(&self) -> &container::Width { - &self.container_width - } - - /// Devuelve la lista de componentes (`children`) del contenedor. - pub fn children(&self) -> &Children { - &self.children - } -} diff --git a/extensions/pagetop-bootsier/src/theme/container/props.rs b/extensions/pagetop-bootsier/src/theme/container/props.rs deleted file mode 100644 index 2010ba8e..00000000 --- a/extensions/pagetop-bootsier/src/theme/container/props.rs +++ /dev/null @@ -1,72 +0,0 @@ -use pagetop::prelude::*; - -use crate::theme::aux::BreakPoint; - -// **< Kind >*************************************************************************************** - -/// Tipo de contenedor ([`Container`](crate::theme::Container)). -/// -/// Permite aplicar la etiqueta HTML apropiada (`<main>`, `<header>`, etc.) manteniendo una API -/// común a todos los contenedores. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Kind { - /// Contenedor genérico (`<div>`). - #[default] - Default, - /// Contenido principal de la página (`<main>`). - Main, - /// Encabezado de la página o de sección (`<header>`). - Header, - /// Pie de la página o de sección (`<footer>`). - Footer, - /// Sección de contenido (`<section>`). - Section, - /// Artículo de contenido (`<article>`). - Article, -} - -// **< Width >************************************************************************************** - -/// Define cómo se comporta el ancho de un contenedor ([`Container`](crate::theme::Container)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Width { - /// Comportamiento por defecto, aplica los anchos máximos predefinidos para cada punto de - /// ruptura. Por debajo del menor punto de ruptura ocupa el 100% del ancho disponible. - #[default] - Default, - /// Aplica los anchos máximos predefinidos a partir del punto de ruptura indicado. Por debajo de - /// ese punto de ruptura ocupa el 100% del ancho disponible. - From(BreakPoint), - /// Ocupa el 100% del ancho disponible siempre. - Fluid, - /// Ocupa el 100% del ancho disponible hasta un ancho máximo explícito. - FluidMax(UnitValue), -} - -impl Width { - const CONTAINER: &str = "container"; - - /* Añade el comportamiento del contenedor a la cadena de clases según ancho (reservado). - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - match self { - Self::Default => BreakPoint::None.push_class(classes, Self::CONTAINER, ""), - Self::From(bp) => bp.push_class(classes, Self::CONTAINER, ""), - Self::Fluid | Self::FluidMax(_) => { - BreakPoint::None.push_class(classes, Self::CONTAINER, "fluid") - } - } - } */ - - /// Devuelve la clase asociada al comportamiento del contenedor según el ajuste de su ancho. - #[inline] - pub fn to_class(self) -> String { - match self { - Self::Default => BreakPoint::None.class_with(Self::CONTAINER, ""), - Self::From(bp) => bp.class_with(Self::CONTAINER, ""), - Self::Fluid | Self::FluidMax(_) => { - BreakPoint::None.class_with(Self::CONTAINER, "fluid") - } - } - } -} diff --git a/extensions/pagetop-bootsier/src/theme/dropdown.rs b/extensions/pagetop-bootsier/src/theme/dropdown.rs deleted file mode 100644 index ed4cbec0..00000000 --- a/extensions/pagetop-bootsier/src/theme/dropdown.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Definiciones para crear menús desplegables [`Dropdown`]. -//! -//! Cada [`dropdown::Item`](crate::theme::dropdown::Item) representa un elemento individual del -//! desplegable [`Dropdown`], con distintos comportamientos según su finalidad, como enlaces de -//! navegación, botones de acción, encabezados o divisores visuales. -//! -//! Los ítems pueden estar activos, deshabilitados o abrirse en nueva ventana según su contexto y -//! configuración, y permiten incluir etiquetas localizables usando [`L10n`](pagetop::locale::L10n). -//! -//! # Ejemplo -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let dd = Dropdown::new() -//! .with_title(L10n::n("Menu")) -//! .with_button_color(ButtonColor::Background(Color::Secondary)) -//! .with_auto_close(dropdown::AutoClose::ClickableInside) -//! .with_direction(dropdown::Direction::Dropend) -//! .add_item(dropdown::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://www.google.es")) -//! .add_item(dropdown::Item::divider()) -//! .add_item(dropdown::Item::header(L10n::n("User session"))) -//! .add_item(dropdown::Item::button(L10n::n("Sign out"))); -//! ``` - -mod props; -pub use props::{AutoClose, Direction, MenuAlign, MenuPosition}; - -mod component; -pub use component::Dropdown; - -mod item; -pub use item::{Item, ItemKind}; diff --git a/extensions/pagetop-bootsier/src/theme/dropdown/component.rs b/extensions/pagetop-bootsier/src/theme/dropdown/component.rs deleted file mode 100644 index 5d27daf5..00000000 --- a/extensions/pagetop-bootsier/src/theme/dropdown/component.rs +++ /dev/null @@ -1,302 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; -use crate::LOCALES_BOOTSIER; - -/// Componente para crear un **menú desplegable**. -/// -/// Renderiza un botón (único o desdoblado, ver [`with_button_split()`](Self::with_button_split)) -/// para mostrar un menú desplegable de elementos [`dropdown::Item`], que se muestra/oculta según la -/// interacción del usuario. Admite variaciones de tamaño/color del botón, también dirección de -/// apertura, alineación o política de cierre. -/// -/// Si no tiene título (ver [`with_title()`](Self::with_title)) se muestra únicamente la lista de -/// elementos sin ningún botón para interactuar. -/// -/// Si este componente se usa en un menú [`Nav`] (ver [`nav::Item::dropdown()`]) sólo se tendrán en -/// cuenta **el título** (si no existe le asigna uno por defecto) y **la lista de elementos**; el -/// resto de propiedades no afectarán a su representación en [`Nav`]. -/// -/// Ver ejemplo en el módulo [`dropdown`]. -/// Si no contiene elementos, el componente **no se renderiza**. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Dropdown { - id : AttrId, - classes : AttrClasses, - title : L10n, - button_size : ButtonSize, - button_color : ButtonColor, - button_split : bool, - button_grouped: bool, - auto_close : dropdown::AutoClose, - direction : dropdown::Direction, - menu_align : dropdown::MenuAlign, - menu_position : dropdown::MenuPosition, - items : Children, -} - -impl Component for Dropdown { - fn new() -> Self { - Dropdown::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes( - ClassesOp::Prepend, - self.direction().class_with(self.button_grouped()), - ); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - // Si no hay elementos en el menú, no se prepara. - let items = self.items().render(cx); - if items.is_empty() { - return PrepareMarkup::None; - } - - // Título opcional para el menú desplegable. - let title = self.title().using(cx); - - PrepareMarkup::With(html! { - div id=[self.id()] class=[self.classes().get()] { - @if !title.is_empty() { - @let mut btn_classes = AttrClasses::new({ - let mut classes = "btn".to_string(); - self.button_size().push_class(&mut classes); - self.button_color().push_class(&mut classes); - classes - }); - @let pos = self.menu_position(); - @let offset = pos.data_offset(); - @let reference = pos.data_reference(); - @let auto_close = self.auto_close.as_str(); - @let menu_classes = AttrClasses::new({ - let mut classes = "dropdown-menu".to_string(); - self.menu_align().push_class(&mut classes); - classes - }); - - // Renderizado en modo split (dos botones) o simple (un botón). - @if self.button_split() { - // Botón principal (acción/etiqueta). - @let btn = html! { - button - type="button" - class=[btn_classes.get()] - { - (title) - } - }; - // Botón *toggle* que abre/cierra el menú asociado. - @let btn_toggle = html! { - button - type="button" - class=[btn_classes.alter_value( - ClassesOp::Add, "dropdown-toggle dropdown-toggle-split" - ).get()] - data-bs-toggle="dropdown" - data-bs-offset=[offset] - data-bs-reference=[reference] - data-bs-auto-close=[auto_close] - aria-expanded="false" - { - span class="visually-hidden" { - (L10n::t("dropdown_toggle", &LOCALES_BOOTSIER).using(cx)) - } - } - }; - // Orden según dirección (en `dropstart` el *toggle* se sitúa antes). - @match self.direction() { - dropdown::Direction::Dropstart => { - (btn_toggle) - ul class=[menu_classes.get()] { (items) } - (btn) - } - _ => { - (btn) - (btn_toggle) - ul class=[menu_classes.get()] { (items) } - } - } - } @else { - // Botón único con funcionalidad de *toggle*. - button - type="button" - class=[btn_classes.alter_value( - ClassesOp::Add, "dropdown-toggle" - ).get()] - data-bs-toggle="dropdown" - data-bs-offset=[offset] - data-bs-reference=[reference] - data-bs-auto-close=[auto_close] - aria-expanded="false" - { - (title) - } - ul class=[menu_classes.get()] { (items) } - } - } @else { - // Sin botón: sólo el listado como menú contextual. - ul class="dropdown-menu" { (items) } - } - } - }) - } -} - -impl Dropdown { - // **< Dropdown BUILDER >*********************************************************************** - - /// Establece el identificador único (`id`) del menú desplegable. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al menú desplegable. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Establece el título del menú desplegable. - #[builder_fn] - pub fn with_title(mut self, title: L10n) -> Self { - self.title = title; - self - } - - /// Ajusta el tamaño del botón. - #[builder_fn] - pub fn with_button_size(mut self, size: ButtonSize) -> Self { - self.button_size = size; - self - } - - /// Define el color/estilo del botón. - #[builder_fn] - pub fn with_button_color(mut self, color: ButtonColor) -> Self { - self.button_color = color; - self - } - - /// Activa/desactiva el modo *split* (botón de acción + *toggle*). - #[builder_fn] - pub fn with_button_split(mut self, split: bool) -> Self { - self.button_split = split; - self - } - - /// Indica si el botón del menú está integrado en un grupo de botones. - #[builder_fn] - pub fn with_button_grouped(mut self, grouped: bool) -> Self { - self.button_grouped = grouped; - self - } - - /// Establece la política de cierre automático del menú desplegable. - #[builder_fn] - pub fn with_auto_close(mut self, auto_close: dropdown::AutoClose) -> Self { - self.auto_close = auto_close; - self - } - - /// Establece la dirección de despliegue del menú. - #[builder_fn] - pub fn with_direction(mut self, direction: dropdown::Direction) -> Self { - self.direction = direction; - self - } - - /// Configura la alineación horizontal (con posible comportamiento *responsive* adicional). - #[builder_fn] - pub fn with_menu_align(mut self, align: dropdown::MenuAlign) -> Self { - self.menu_align = align; - self - } - - /// Configura la posición del menú. - #[builder_fn] - pub fn with_menu_position(mut self, position: dropdown::MenuPosition) -> Self { - self.menu_position = position; - self - } - - /// Añade un nuevo elemento hijo al menú. - #[inline] - pub fn add_item(mut self, item: dropdown::Item) -> Self { - self.items.add(Child::with(item)); - self - } - - /// Modifica la lista de elementos (`children`) aplicando una operación [`TypedOp`]. - #[builder_fn] - pub fn with_items(mut self, op: TypedOp<dropdown::Item>) -> Self { - self.items.alter_typed(op); - self - } - - // **< Dropdown GETTERS >*********************************************************************** - - /// Devuelve las clases CSS asociadas al menú desplegable. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el título del menú desplegable. - pub fn title(&self) -> &L10n { - &self.title - } - - /// Devuelve el tamaño configurado del botón. - pub fn button_size(&self) -> &ButtonSize { - &self.button_size - } - - /// Devuelve el color/estilo configurado del botón. - pub fn button_color(&self) -> &ButtonColor { - &self.button_color - } - - /// Devuelve si se debe desdoblar (*split*) el botón (botón de acción + *toggle*). - pub fn button_split(&self) -> bool { - self.button_split - } - - /// Devuelve si el botón del menú está integrado en un grupo de botones. - pub fn button_grouped(&self) -> bool { - self.button_grouped - } - - /// Devuelve la política de cierre automático del menú desplegado. - pub fn auto_close(&self) -> &dropdown::AutoClose { - &self.auto_close - } - - /// Devuelve la dirección de despliegue configurada. - pub fn direction(&self) -> &dropdown::Direction { - &self.direction - } - - /// Devuelve la configuración de alineación horizontal del menú desplegable. - pub fn menu_align(&self) -> &dropdown::MenuAlign { - &self.menu_align - } - - /// Devuelve la posición configurada para el menú desplegable. - pub fn menu_position(&self) -> &dropdown::MenuPosition { - &self.menu_position - } - - /// Devuelve la lista de elementos (`children`) del menú. - pub fn items(&self) -> &Children { - &self.items - } -} diff --git a/extensions/pagetop-bootsier/src/theme/dropdown/item.rs b/extensions/pagetop-bootsier/src/theme/dropdown/item.rs deleted file mode 100644 index 2f62f286..00000000 --- a/extensions/pagetop-bootsier/src/theme/dropdown/item.rs +++ /dev/null @@ -1,281 +0,0 @@ -use pagetop::prelude::*; - -// **< ItemKind >*********************************************************************************** - -/// Tipos de [`dropdown::Item`](crate::theme::dropdown::Item) disponibles en un menú desplegable -/// [`Dropdown`](crate::theme::Dropdown). -/// -/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar -/// con él. -#[derive(AutoDefault)] -pub enum ItemKind { - /// Elemento vacío, no produce salida. - #[default] - Void, - /// Etiqueta sin comportamiento interactivo. - Label(L10n), - /// Elemento de navegación. Opcionalmente puede abrirse en una nueva ventana y estar - /// inicialmente deshabilitado. - Link { - label: L10n, - path: FnPathByContext, - blank: bool, - disabled: bool, - }, - /// Acción ejecutable en la propia página, sin navegación asociada. Inicialmente puede estar - /// deshabilitado. - Button { label: L10n, disabled: bool }, - /// Título o encabezado que separa grupos de opciones. - Header(L10n), - /// Separador visual entre bloques de elementos. - Divider, -} - -// **< Item >*************************************************************************************** - -/// Representa un **elemento individual** de un menú desplegable -/// [`Dropdown`](crate::theme::Dropdown). -/// -/// Cada instancia de [`dropdown::Item`](crate::theme::dropdown::Item) se traduce en un componente -/// visible que puede comportarse como texto, enlace, botón, encabezado o separador, según su -/// [`ItemKind`]. -/// -/// Permite definir identificador, clases de estilo adicionales o tipo de interacción asociada, -/// manteniendo una interfaz común para renderizar todos los elementos del menú. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Item { - id : AttrId, - classes : AttrClasses, - item_kind: ItemKind, -} - -impl Component for Item { - fn new() -> Self { - Item::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - match self.item_kind() { - ItemKind::Void => PrepareMarkup::None, - - ItemKind::Label(label) => PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - span class="dropdown-item-text" { - (label.using(cx)) - } - } - }), - - ItemKind::Link { - label, - path, - blank, - disabled, - } => { - let path = path(cx); - let current_path = cx.request().map(|request| request.path()); - let is_current = !*disabled && (current_path == Some(path)); - - let mut classes = "dropdown-item".to_string(); - if is_current { - classes.push_str(" active"); - } - if *disabled { - classes.push_str(" disabled"); - } - - let href = (!disabled).then_some(path); - let target = (!disabled && *blank).then_some("_blank"); - let rel = (!disabled && *blank).then_some("noopener noreferrer"); - - let aria_current = (href.is_some() && is_current).then_some("page"); - let aria_disabled = disabled.then_some("true"); - let tabindex = disabled.then_some("-1"); - - PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - a - class=(classes) - href=[href] - target=[target] - rel=[rel] - aria-current=[aria_current] - aria-disabled=[aria_disabled] - tabindex=[tabindex] - { - (label.using(cx)) - } - } - }) - } - - ItemKind::Button { label, disabled } => { - let mut classes = "dropdown-item".to_string(); - if *disabled { - classes.push_str(" disabled"); - } - - let aria_disabled = disabled.then_some("true"); - let disabled_attr = disabled.then_some("disabled"); - - PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - button - class=(classes) - type="button" - aria-disabled=[aria_disabled] - disabled=[disabled_attr] - { - (label.using(cx)) - } - } - }) - } - - ItemKind::Header(label) => PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - h6 class="dropdown-header" { - (label.using(cx)) - } - } - }), - - ItemKind::Divider => PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { hr class="dropdown-divider" {} } - }), - } - } -} - -impl Item { - /// Crea un elemento de tipo texto, mostrado sin interacción. - pub fn label(label: L10n) -> Self { - Item { - item_kind: ItemKind::Label(label), - ..Default::default() - } - } - - /// Crea un enlace para la navegación. - pub fn link(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: false, - disabled: false, - }, - ..Default::default() - } - } - - /// Crea un enlace deshabilitado que no permite la interacción. - pub fn link_disabled(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: false, - disabled: true, - }, - ..Default::default() - } - } - - /// Crea un enlace que se abre en una nueva ventana o pestaña. - pub fn link_blank(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: true, - disabled: false, - }, - ..Default::default() - } - } - - /// Crea un enlace inicialmente deshabilitado que se abriría en una nueva ventana. - pub fn link_blank_disabled(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: true, - disabled: true, - }, - ..Default::default() - } - } - - /// Crea un botón de acción local, sin navegación asociada. - pub fn button(label: L10n) -> Self { - Item { - item_kind: ItemKind::Button { - label, - disabled: false, - }, - ..Default::default() - } - } - - /// Crea un botón deshabilitado. - pub fn button_disabled(label: L10n) -> Self { - Item { - item_kind: ItemKind::Button { - label, - disabled: true, - }, - ..Default::default() - } - } - - /// Crea un encabezado para un grupo de elementos dentro del menú. - pub fn header(label: L10n) -> Self { - Item { - item_kind: ItemKind::Header(label), - ..Default::default() - } - } - - /// Crea un separador visual entre bloques de elementos. - pub fn divider() -> Self { - Item { - item_kind: ItemKind::Divider, - ..Default::default() - } - } - - // **< Item BUILDER >*************************************************************************** - - /// Establece el identificador único (`id`) del elemento. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al elemento. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - // **< Item GETTERS >*************************************************************************** - - /// Devuelve las clases CSS asociadas al elemento. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el tipo de elemento representado. - pub fn item_kind(&self) -> &ItemKind { - &self.item_kind - } -} diff --git a/extensions/pagetop-bootsier/src/theme/dropdown/props.rs b/extensions/pagetop-bootsier/src/theme/dropdown/props.rs deleted file mode 100644 index 7571332b..00000000 --- a/extensions/pagetop-bootsier/src/theme/dropdown/props.rs +++ /dev/null @@ -1,226 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -// **< AutoClose >********************************************************************************** - -/// Estrategia para el cierre automático de un menú [`Dropdown`]. -/// -/// Define cuándo se cierra el menú desplegado según la interacción del usuario. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum AutoClose { - /// Comportamiento por defecto, se cierra con clics dentro y fuera del menú, o pulsando `Esc`. - #[default] - Default, - /// Sólo se cierra con clics dentro del menú. - ClickableInside, - /// Sólo se cierra con clics fuera del menú. - ClickableOutside, - /// Cierre manual, no se cierra con clics; sólo al pulsar nuevamente el botón del menú - /// (*toggle*), o pulsando `Esc`. - ManualClose, -} - -impl AutoClose { - // Devuelve el valor para `data-bs-auto-close`, o `None` si es el comportamiento por defecto. - #[rustfmt::skip] - #[inline] - pub(crate) const fn as_str(self) -> Option<&'static str> { - match self { - Self::Default => None, - Self::ClickableInside => Some("inside"), - Self::ClickableOutside => Some("outside"), - Self::ManualClose => Some("false"), - } - } -} - -// **< Direction >********************************************************************************** - -/// Dirección de despliegue de un menú [`Dropdown`]. -/// -/// Controla desde qué posición se muestra el menú respecto al botón. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Direction { - /// Comportamiento por defecto (despliega el menú hacia abajo desde la posición inicial, - /// respetando LTR/RTL). - #[default] - Default, - /// Centra horizontalmente el menú respecto al botón. - Centered, - /// Despliega el menú hacia arriba. - Dropup, - /// Despliega el menú hacia arriba y centrado. - DropupCentered, - /// Despliega el menú desde el lateral final, respetando LTR/RTL. - Dropend, - /// Despliega el menú desde el lateral inicial, respetando LTR/RTL. - Dropstart, -} - -impl Direction { - // Mapea la dirección teniendo en cuenta si se agrupa con otros menús [`Dropdown`]. - #[rustfmt::skip ] - #[inline] - const fn as_str(self, grouped: bool) -> &'static str { - match self { - Self::Default if grouped => "", - Self::Default => "dropdown", - Self::Centered => "dropdown-center", - Self::Dropup => "dropup", - Self::DropupCentered => "dropup-center", - Self::Dropend => "dropend", - Self::Dropstart => "dropstart", - } - } - - // Añade la dirección de despliegue a la cadena de clases teniendo en cuenta si se agrupa con - // otros menús [`Dropdown`]. - #[inline] - pub(crate) fn push_class(self, classes: &mut String, grouped: bool) { - if grouped { - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str("btn-group"); - } - let class = self.as_str(grouped); - if !class.is_empty() { - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - } - - // Devuelve la clase asociada a la dirección teniendo en cuenta si se agrupa con otros menús - // [`Dropdown`], o `""` si no corresponde ninguna. - #[inline] - pub(crate) fn class_with(self, grouped: bool) -> String { - let mut classes = String::new(); - self.push_class(&mut classes, grouped); - classes - } -} - -// **< MenuAlign >********************************************************************************** - -/// Alineación horizontal del menú desplegable [`Dropdown`]. -/// -/// Permite alinear el menú al inicio o al final del botón (respetando LTR/RTL) y añadirle una -/// alineación diferente a partir de un punto de ruptura ([`BreakPoint`]). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum MenuAlign { - /// Alineación al inicio (comportamiento por defecto). - #[default] - Start, - /// Alineación al inicio a partir del punto de ruptura indicado. - StartAt(BreakPoint), - /// Alineación al inicio por defecto, y al final a partir de un punto de ruptura válido. - StartAndEnd(BreakPoint), - /// Alineación al final. - End, - /// Alineación al final a partir del punto de ruptura indicado. - EndAt(BreakPoint), - /// Alineación al final por defecto, y al inicio a partir de un punto de ruptura válido. - EndAndStart(BreakPoint), -} - -impl MenuAlign { - #[inline] - fn push_one(classes: &mut String, class: &str) { - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - - // Añade las clases de alineación a `classes` (sin incluir la base `dropdown-menu`). - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - match self { - // Alineación por defecto (start), no añade clases extra. - Self::Start => {} - - // `dropdown-menu-{bp}-start` - Self::StartAt(bp) => { - let class = bp.class_with("dropdown-menu", "start"); - Self::push_one(classes, &class); - } - - // `dropdown-menu-start` + `dropdown-menu-{bp}-end` - Self::StartAndEnd(bp) => { - Self::push_one(classes, "dropdown-menu-start"); - let bp_class = bp.class_with("dropdown-menu", "end"); - Self::push_one(classes, &bp_class); - } - - // `dropdown-menu-end` - Self::End => { - Self::push_one(classes, "dropdown-menu-end"); - } - - // `dropdown-menu-{bp}-end` - Self::EndAt(bp) => { - let class = bp.class_with("dropdown-menu", "end"); - Self::push_one(classes, &class); - } - - // `dropdown-menu-end` + `dropdown-menu-{bp}-start` - Self::EndAndStart(bp) => { - Self::push_one(classes, "dropdown-menu-end"); - let bp_class = bp.class_with("dropdown-menu", "start"); - Self::push_one(classes, &bp_class); - } - } - } - - /* Devuelve las clases de alineación sin incluir `dropdown-menu` (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - let mut classes = String::new(); - self.push_class(&mut classes); - classes - } */ -} - -// **< MenuPosition >******************************************************************************* - -/// Posición relativa del menú desplegable [`Dropdown`]. -/// -/// Permite indicar un desplazamiento (*offset*) manual o referenciar al elemento padre para el -/// cálculo de la posición. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum MenuPosition { - /// Posicionamiento automático por defecto. - #[default] - Default, - /// Desplazamiento manual en píxeles `(x, y)` aplicado al menú. Se admiten valores negativos. - Offset(i8, i8), - /// Posiciona el menú tomando como referencia el botón padre. Especialmente útil cuando - /// [`button_split()`](crate::theme::Dropdown::button_split) es `true`. - Parent, -} - -impl MenuPosition { - // Devuelve el valor para `data-bs-offset` o `None` si no aplica. - #[inline] - pub(crate) fn data_offset(self) -> Option<String> { - match self { - Self::Offset(x, y) => Some(format!("{x},{y}")), - _ => None, - } - } - - // Devuelve el valor para `data-bs-reference` o `None` si no aplica. - #[inline] - pub(crate) fn data_reference(self) -> Option<&'static str> { - match self { - Self::Parent => Some("parent"), - _ => None, - } - } -} diff --git a/extensions/pagetop-bootsier/src/theme/icon.rs b/extensions/pagetop-bootsier/src/theme/icon.rs deleted file mode 100644 index 73e5ac4e..00000000 --- a/extensions/pagetop-bootsier/src/theme/icon.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::prelude::*; - -const DEFAULT_VIEWBOX: &str = "0 0 16 16"; - -#[derive(AutoDefault)] -pub enum IconKind { - #[default] - None, - Font(FontSize), - Svg { - shapes: Markup, - viewbox: AttrValue, - }, -} - -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Icon { - classes : AttrClasses, - icon_kind : IconKind, - aria_label: AttrL10n, -} - -impl Component for Icon { - fn new() -> Self { - Icon::default() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - if !matches!(self.icon_kind(), IconKind::None) { - self.alter_classes(ClassesOp::Prepend, "icon"); - } - if let IconKind::Font(font_size) = self.icon_kind() { - self.alter_classes(ClassesOp::Add, font_size.as_str()); - } - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - match self.icon_kind() { - IconKind::None => PrepareMarkup::None, - IconKind::Font(_) => { - let aria_label = self.aria_label().lookup(cx); - let has_label = aria_label.is_some(); - PrepareMarkup::With(html! { - i - class=[self.classes().get()] - role=[has_label.then_some("img")] - aria-label=[aria_label] - aria-hidden=[(!has_label).then_some("true")] - {} - }) - } - IconKind::Svg { shapes, viewbox } => { - let aria_label = self.aria_label().lookup(cx); - let has_label = aria_label.is_some(); - let viewbox = viewbox.get().unwrap_or_else(|| DEFAULT_VIEWBOX.to_string()); - PrepareMarkup::With(html! { - svg - xmlns="http://www.w3.org/2000/svg" - viewBox=(viewbox) - fill="currentColor" - focusable="false" - class=[self.classes().get()] - role=[has_label.then_some("img")] - aria-label=[aria_label] - aria-hidden=[(!has_label).then_some("true")] - { - (shapes) - } - }) - } - } - } -} - -impl Icon { - pub fn font() -> Self { - Icon::default().with_icon_kind(IconKind::Font(FontSize::default())) - } - - pub fn font_sized(font_size: FontSize) -> Self { - Icon::default().with_icon_kind(IconKind::Font(font_size)) - } - - pub fn svg(shapes: Markup) -> Self { - Icon::default().with_icon_kind(IconKind::Svg { - shapes, - viewbox: AttrValue::default(), - }) - } - - pub fn svg_with_viewbox(shapes: Markup, viewbox: impl AsRef<str>) -> Self { - Icon::default().with_icon_kind(IconKind::Svg { - shapes, - viewbox: AttrValue::new(viewbox), - }) - } - - // **< Icon BUILDER >*************************************************************************** - - /// Modifica la lista de clases CSS aplicadas al icono. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - #[builder_fn] - pub fn with_icon_kind(mut self, icon_kind: IconKind) -> Self { - self.icon_kind = icon_kind; - self - } - - #[builder_fn] - pub fn with_aria_label(mut self, label: L10n) -> Self { - self.aria_label.alter_value(label); - self - } - - // **< Icon GETTERS >*************************************************************************** - - /// Devuelve las clases CSS asociadas al icono. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - pub fn icon_kind(&self) -> &IconKind { - &self.icon_kind - } - - pub fn aria_label(&self) -> &AttrL10n { - &self.aria_label - } -} diff --git a/extensions/pagetop-bootsier/src/theme/image.rs b/extensions/pagetop-bootsier/src/theme/image.rs deleted file mode 100644 index 837d25a1..00000000 --- a/extensions/pagetop-bootsier/src/theme/image.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Definiciones para renderizar imágenes ([`Image`]). - -mod props; -pub use props::{Size, Source}; - -mod component; -pub use component::Image; diff --git a/extensions/pagetop-bootsier/src/theme/image/component.rs b/extensions/pagetop-bootsier/src/theme/image/component.rs deleted file mode 100644 index bc3f2c9d..00000000 --- a/extensions/pagetop-bootsier/src/theme/image/component.rs +++ /dev/null @@ -1,141 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -/// Componente para renderizar una **imagen**. -/// -/// - Ajusta su disposición según el origen definido en [`image::Source`]. -/// - Permite configurar **dimensiones** ([`with_size()`](Self::with_size)), **borde** -/// ([`classes::Border`](crate::theme::classes::Border)) y **redondeo de esquinas** -/// ([`classes::Rounded`](crate::theme::classes::Rounded)). -/// - Resuelve el texto alternativo `alt` con **localización** mediante [`L10n`]. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Image { - id : AttrId, - classes: AttrClasses, - size : image::Size, - source : image::Source, - alt : AttrL10n, -} - -impl Component for Image { - fn new() -> Self { - Image::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, self.source().to_class()); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let dimensions = self.size().to_style(); - let alt_text = self.alternative().lookup(cx).unwrap_or_default(); - let is_decorative = alt_text.is_empty(); - let source = match self.source() { - image::Source::Logo(logo) => { - return PrepareMarkup::With(html! { - span - id=[self.id()] - class=[self.classes().get()] - style=[dimensions] - role=[(!is_decorative).then_some("img")] - aria-label=[(!is_decorative).then_some(alt_text)] - aria-hidden=[is_decorative.then_some("true")] - { - (logo.render(cx)) - } - }) - } - image::Source::Responsive(source) => Some(source), - image::Source::Thumbnail(source) => Some(source), - image::Source::Plain(source) => Some(source), - }; - PrepareMarkup::With(html! { - img - src=[source] - alt=(alt_text) - id=[self.id()] - class=[self.classes().get()] - style=[dimensions] {} - }) - } -} - -impl Image { - /// Crea rápidamente una imagen especificando su origen. - pub fn with(source: image::Source) -> Self { - Image::default().with_source(source) - } - - // **< Image BUILDER >************************************************************************** - - /// Establece el identificador único (`id`) de la imagen. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas a la imagen. - /// - /// También acepta clases predefinidas para: - /// - /// - Establecer bordes ([`classes::Border`]). - /// - Redondear las esquinas ([`classes::Rounded`]). - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Define las dimensiones de la imagen (auto, ancho/alto, ambos). - #[builder_fn] - pub fn with_size(mut self, size: image::Size) -> Self { - self.size = size; - self - } - - /// Establece el origen de la imagen, influyendo en su disposición en el contenido. - #[builder_fn] - pub fn with_source(mut self, source: image::Source) -> Self { - self.source = source; - self - } - - /// Define el texto alternativo localizado ([`L10n`]) para la imagen. - /// - /// Se recomienda siempre aportar un texto alternativo salvo que la imagen sea puramente - /// decorativa. - #[builder_fn] - pub fn with_alternative(mut self, alt: L10n) -> Self { - self.alt.alter_value(alt); - self - } - - // **< Image GETTERS >************************************************************************** - - /// Devuelve las clases CSS asociadas a la imagen. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve las dimensiones de la imagen. - pub fn size(&self) -> &image::Size { - &self.size - } - - /// Devuelve el origen de la imagen. - pub fn source(&self) -> &image::Source { - &self.source - } - - /// Devuelve el texto alternativo localizado. - pub fn alternative(&self) -> &AttrL10n { - &self.alt - } -} diff --git a/extensions/pagetop-bootsier/src/theme/image/props.rs b/extensions/pagetop-bootsier/src/theme/image/props.rs deleted file mode 100644 index e9b1286a..00000000 --- a/extensions/pagetop-bootsier/src/theme/image/props.rs +++ /dev/null @@ -1,108 +0,0 @@ -use pagetop::prelude::*; - -// **< Size >*************************************************************************************** - -/// Define las **dimensiones** de una imagen ([`Image`](crate::theme::Image)). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Size { - /// Ajuste automático por defecto. - /// - /// La imagen usa su tamaño natural o se ajusta al contenedor donde se publica. - #[default] - Auto, - /// Establece explícitamente el **ancho y alto** de la imagen. - /// - /// Útil cuando se desea fijar ambas dimensiones de forma exacta. Ten en cuenta que la imagen - /// puede distorsionarse si no se mantiene la proporción original. - Dimensions(UnitValue, UnitValue), - /// Establece sólo el **ancho** de la imagen. - /// - /// La altura se ajusta proporcionalmente de manera automática. - Width(UnitValue), - /// Establece sólo la **altura** de la imagen. - /// - /// El ancho se ajusta proporcionalmente de manera automática. - Height(UnitValue), - /// Establece **el mismo valor** para el ancho y el alto de la imagen. - /// - /// Práctico para forzar rápidamente un área cuadrada. Ten en cuenta que la imagen puede - /// distorsionarse si la original no es cuadrada. - Both(UnitValue), -} - -impl Size { - // Devuelve el valor del atributo `style` en función del tamaño, o `None` si no aplica. - #[inline] - pub(crate) fn to_style(self) -> Option<String> { - match self { - Self::Auto => None, - Self::Dimensions(w, h) => Some(format!("width: {w}; height: {h};")), - Self::Width(w) => Some(format!("width: {w};")), - Self::Height(h) => Some(format!("height: {h};")), - Self::Both(v) => Some(format!("width: {v}; height: {v};")), - } - } -} - -// **< Source >************************************************************************************* - -/// Especifica la **fuente** para publicar una imagen ([`Image`](crate::theme::Image)). -#[derive(AutoDefault, Clone, Debug, PartialEq)] -pub enum Source { - /// Imagen con el logotipo de PageTop. - #[default] - Logo(PageTopSvg), - /// Imagen que se adapta automáticamente a su contenedor. - /// - /// El `String` asociado es la URL (o ruta) de la imagen. - Responsive(String), - /// Imagen que aplica el estilo **miniatura** de Bootstrap. - /// - /// El `String` asociado es la URL (o ruta) de la imagen. - Thumbnail(String), - /// Imagen sin clases específicas de Bootstrap, útil para controlar con CSS propio. - /// - /// El `String` asociado es la URL (o ruta) de la imagen. - Plain(String), -} - -impl Source { - const IMG_FLUID: &str = "img-fluid"; - const IMG_THUMBNAIL: &str = "img-thumbnail"; - - // Devuelve la clase base asociada a la imagen según la fuente. - #[inline] - fn as_str(&self) -> &'static str { - match self { - Source::Logo(_) | Source::Responsive(_) => Self::IMG_FLUID, - Source::Thumbnail(_) => Self::IMG_THUMBNAIL, - Source::Plain(_) => "", - } - } - - /* Añade la clase base asociada a la imagen según la fuente a la cadena de clases (reservado). - #[inline] - pub(crate) fn push_class(&self, classes: &mut String) { - let s = self.as_str(); - if s.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(s); - } */ - - // Devuelve la clase asociada a la imagen según la fuente. - #[inline] - pub(crate) fn to_class(&self) -> String { - let s = self.as_str(); - if s.is_empty() { - String::new() - } else { - let mut class = String::with_capacity(s.len()); - class.push_str(s); - class - } - } -} diff --git a/extensions/pagetop-bootsier/src/theme/nav.rs b/extensions/pagetop-bootsier/src/theme/nav.rs deleted file mode 100644 index c74ab3b5..00000000 --- a/extensions/pagetop-bootsier/src/theme/nav.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Definiciones para crear menús [`Nav`] o alguna de sus variantes de presentación. -//! -//! Cada [`nav::Item`](crate::theme::nav::Item) representa un elemento individual del menú [`Nav`], -//! con distintos comportamientos según su finalidad, como enlaces de navegación o menús -//! desplegables [`Dropdown`](crate::theme::Dropdown). -//! -//! Los ítems pueden estar activos, deshabilitados o abrirse en nueva ventana según su contexto y -//! configuración, y permiten incluir etiquetas localizables usando [`L10n`](pagetop::locale::L10n). -//! -//! # Ejemplo -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let nav = Nav::tabs() -//! .with_layout(nav::Layout::End) -//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(nav::Item::link_blank(L10n::n("External"), |_| "https://www.google.es")) -//! .add_item(nav::Item::dropdown( -//! Dropdown::new() -//! .with_title(L10n::n("Options")) -//! .with_items(TypedOp::AddMany(vec![ -//! Typed::with(dropdown::Item::link(L10n::n("Action"), |_| "/action")), -//! Typed::with(dropdown::Item::link(L10n::n("Another action"), |_| "/another")), -//! ])), -//! )) -//! .add_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#")); -//! ``` - -mod props; -pub use props::{Kind, Layout}; - -mod component; -pub use component::Nav; - -mod item; -pub use item::{Item, ItemKind}; diff --git a/extensions/pagetop-bootsier/src/theme/nav/component.rs b/extensions/pagetop-bootsier/src/theme/nav/component.rs deleted file mode 100644 index 2bd43774..00000000 --- a/extensions/pagetop-bootsier/src/theme/nav/component.rs +++ /dev/null @@ -1,135 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -/// Componente para crear un **menú** o alguna de sus variantes ([`nav::Kind`]). -/// -/// Presenta un menú con una lista de elementos usando una vista básica, o alguna de sus variantes -/// como *pestañas* (`Tabs`), *botones* (`Pills`) o *subrayado* (`Underline`). También permite -/// controlar su distribución y orientación ([`nav::Layout`](crate::theme::nav::Layout)). -/// -/// Ver ejemplo en el módulo [`nav`]. -/// Si no contiene elementos, el componente **no se renderiza**. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Nav { - id : AttrId, - classes : AttrClasses, - items : Children, - nav_kind : nav::Kind, - nav_layout: nav::Layout, -} - -impl Component for Nav { - fn new() -> Self { - Nav::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, { - let mut classes = "nav".to_string(); - self.nav_kind().push_class(&mut classes); - self.nav_layout().push_class(&mut classes); - classes - }); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let items = self.items().render(cx); - if items.is_empty() { - return PrepareMarkup::None; - } - - PrepareMarkup::With(html! { - ul id=[self.id()] class=[self.classes().get()] { - (items) - } - }) - } -} - -impl Nav { - /// Crea un `Nav` usando pestañas para los elementos (*Tabs*). - pub fn tabs() -> Self { - Nav::default().with_kind(nav::Kind::Tabs) - } - - /// Crea un `Nav` usando botones para los elementos (*Pills*). - pub fn pills() -> Self { - Nav::default().with_kind(nav::Kind::Pills) - } - - /// Crea un `Nav` usando elementos subrayados (*Underline*). - pub fn underline() -> Self { - Nav::default().with_kind(nav::Kind::Underline) - } - - // **< Nav BUILDER >**************************************************************************** - - /// Establece el identificador único (`id`) del menú. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al menú. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Cambia el estilo del menú (*Tabs*, *Pills*, *Underline* o *Default*). - #[builder_fn] - pub fn with_kind(mut self, kind: nav::Kind) -> Self { - self.nav_kind = kind; - self - } - - /// Selecciona la distribución y orientación del menú. - #[builder_fn] - pub fn with_layout(mut self, layout: nav::Layout) -> Self { - self.nav_layout = layout; - self - } - - /// Añade un nuevo elemento hijo al menú. - pub fn add_item(mut self, item: nav::Item) -> Self { - self.items.add(Child::with(item)); - self - } - - /// Modifica la lista de elementos (`children`) aplicando una operación [`TypedOp`]. - #[builder_fn] - pub fn with_items(mut self, op: TypedOp<nav::Item>) -> Self { - self.items.alter_typed(op); - self - } - - // **< Nav GETTERS >**************************************************************************** - - /// Devuelve las clases CSS asociadas al menú. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el estilo visual seleccionado. - pub fn nav_kind(&self) -> &nav::Kind { - &self.nav_kind - } - - /// Devuelve la distribución y orientación seleccionada. - pub fn nav_layout(&self) -> &nav::Layout { - &self.nav_layout - } - - /// Devuelve la lista de elementos (`children`) del menú. - pub fn items(&self) -> &Children { - &self.items - } -} diff --git a/extensions/pagetop-bootsier/src/theme/nav/item.rs b/extensions/pagetop-bootsier/src/theme/nav/item.rs deleted file mode 100644 index a79947cd..00000000 --- a/extensions/pagetop-bootsier/src/theme/nav/item.rs +++ /dev/null @@ -1,284 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; -use crate::LOCALES_BOOTSIER; - -// **< ItemKind >*********************************************************************************** - -/// Tipos de [`nav::Item`](crate::theme::nav::Item) disponibles en un menú -/// [`Nav`](crate::theme::Nav). -/// -/// Define internamente la naturaleza del elemento y su comportamiento al mostrarse o interactuar -/// con él. -#[derive(AutoDefault)] -pub enum ItemKind { - /// Elemento vacío, no produce salida. - #[default] - Void, - /// Etiqueta sin comportamiento interactivo. - Label(L10n), - /// Elemento de navegación. Opcionalmente puede abrirse en una nueva ventana y estar - /// inicialmente deshabilitado. - Link { - label: L10n, - path: FnPathByContext, - blank: bool, - disabled: bool, - }, - /// Elemento que despliega un menú [`Dropdown`]. - Dropdown(Typed<Dropdown>), -} - -impl ItemKind { - const ITEM: &str = "nav-item"; - const DROPDOWN: &str = "nav-item dropdown"; - - // Devuelve las clases base asociadas al tipo de elemento. - #[inline] - const fn as_str(&self) -> &'static str { - match self { - Self::Void => "", - Self::Dropdown(_) => Self::DROPDOWN, - _ => Self::ITEM, - } - } - - /* Añade las clases asociadas al tipo de elemento a la cadena de clases (reservado). - #[inline] - pub(crate) fn push_class(&self, classes: &mut String) { - let class = self.as_str(); - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } */ - - // Devuelve las clases asociadas al tipo de elemento. - #[inline] - pub(crate) fn to_class(&self) -> String { - self.as_str().to_owned() - } -} - -// **< Item >*************************************************************************************** - -/// Representa un **elemento individual** de un menú [`Nav`](crate::theme::Nav). -/// -/// Cada instancia de [`nav::Item`](crate::theme::nav::Item) se traduce en un componente visible que -/// puede comportarse como texto, enlace, botón o menú desplegable según su [`ItemKind`]. -/// -/// Permite definir identificador, clases de estilo adicionales o tipo de interacción asociada, -/// manteniendo una interfaz común para renderizar todos los elementos del menú. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Item { - id : AttrId, - classes : AttrClasses, - item_kind: ItemKind, -} - -impl Component for Item { - fn new() -> Self { - Item::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, self.item_kind().to_class()); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - match self.item_kind() { - ItemKind::Void => PrepareMarkup::None, - - ItemKind::Label(label) => PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - span class="nav-link disabled" aria-disabled="true" { - (label.using(cx)) - } - } - }), - - ItemKind::Link { - label, - path, - blank, - disabled, - } => { - let path = path(cx); - let current_path = cx.request().map(|request| request.path()); - let is_current = !*disabled && (current_path == Some(path)); - - let mut classes = "nav-link".to_string(); - if is_current { - classes.push_str(" active"); - } - if *disabled { - classes.push_str(" disabled"); - } - - let href = (!*disabled).then_some(path); - let target = (!*disabled && *blank).then_some("_blank"); - let rel = (!*disabled && *blank).then_some("noopener noreferrer"); - - let aria_current = (href.is_some() && is_current).then_some("page"); - let aria_disabled = (*disabled).then_some("true"); - - PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - a - class=(classes) - href=[href] - target=[target] - rel=[rel] - aria-current=[aria_current] - aria-disabled=[aria_disabled] - { - (label.using(cx)) - } - } - }) - } - - ItemKind::Dropdown(menu) => { - if let Some(dd) = menu.borrow() { - let items = dd.items().render(cx); - if items.is_empty() { - return PrepareMarkup::None; - } - let title = dd.title().lookup(cx).unwrap_or_else(|| { - L10n::t("dropdown", &LOCALES_BOOTSIER) - .lookup(cx) - .unwrap_or_else(|| "Dropdown".to_string()) - }); - PrepareMarkup::With(html! { - li id=[self.id()] class=[self.classes().get()] { - a - class="nav-link dropdown-toggle" - data-bs-toggle="dropdown" - href="#" - role="button" - aria-expanded="false" - { - (title) - } - ul class="dropdown-menu" { - (items) - } - } - }) - } else { - PrepareMarkup::None - } - } - } - } -} - -impl Item { - /// Crea un elemento de tipo texto, mostrado sin interacción. - pub fn label(label: L10n) -> Self { - Item { - item_kind: ItemKind::Label(label), - ..Default::default() - } - } - - /// Crea un enlace para la navegación. - pub fn link(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: false, - disabled: false, - }, - ..Default::default() - } - } - - /// Crea un enlace deshabilitado que no permite la interacción. - pub fn link_disabled(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: false, - disabled: true, - }, - ..Default::default() - } - } - - /// Crea un enlace que se abre en una nueva ventana o pestaña. - pub fn link_blank(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: true, - disabled: false, - }, - ..Default::default() - } - } - - /// Crea un enlace inicialmente deshabilitado que se abriría en una nueva ventana. - pub fn link_blank_disabled(label: L10n, path: FnPathByContext) -> Self { - Item { - item_kind: ItemKind::Link { - label, - path, - blank: true, - disabled: true, - }, - ..Default::default() - } - } - - /// Crea un elemento de navegación que contiene un menú desplegable [`Dropdown`]. - /// - /// Sólo se tienen en cuenta **el título** (si no existe le asigna uno por defecto) y **la lista - /// de elementos** del [`Dropdown`]; el resto de propiedades del componente no afectarán a su - /// representación en [`Nav`]. - pub fn dropdown(menu: Dropdown) -> Self { - Item { - item_kind: ItemKind::Dropdown(Typed::with(menu)), - ..Default::default() - } - } - - // **< Item BUILDER >*************************************************************************** - - /// Establece el identificador único (`id`) del elemento. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al elemento. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - // **< Item GETTERS >*************************************************************************** - - /// Devuelve las clases CSS asociadas al elemento. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el tipo de elemento representado. - pub fn item_kind(&self) -> &ItemKind { - &self.item_kind - } -} diff --git a/extensions/pagetop-bootsier/src/theme/nav/props.rs b/extensions/pagetop-bootsier/src/theme/nav/props.rs deleted file mode 100644 index 46a4e2bc..00000000 --- a/extensions/pagetop-bootsier/src/theme/nav/props.rs +++ /dev/null @@ -1,120 +0,0 @@ -use pagetop::prelude::*; - -// **< Kind >*************************************************************************************** - -/// Define la variante de presentación de un menú [`Nav`](crate::theme::Nav). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Kind { - /// Estilo por defecto, lista de enlaces flexible y minimalista. - #[default] - Default, - /// Pestañas con borde para cambiar entre secciones. - Tabs, - /// Botones con fondo que resaltan el elemento activo. - Pills, - /// Variante con subrayado del elemento activo, estética ligera. - Underline, -} - -impl Kind { - const TABS: &str = "nav-tabs"; - const PILLS: &str = "nav-pills"; - const UNDERLINE: &str = "nav-underline"; - - // Devuelve la clase base asociada al tipo de menú, o una cadena vacía si no aplica. - #[rustfmt::skip] - #[inline] - const fn as_str(self) -> &'static str { - match self { - Self::Default => "", - Self::Tabs => Self::TABS, - Self::Pills => Self::PILLS, - Self::Underline => Self::UNDERLINE, - } - } - - // Añade la clase asociada al tipo de menú a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let class = self.as_str(); - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - - /* Devuelve la clase asociada al tipo de menú, o una cadena vacía si no aplica (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - self.as_str().to_owned() - } */ -} - -// **< Layout >************************************************************************************* - -/// Distribución y orientación de un menú [`Nav`](crate::theme::Nav). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Layout { - /// Comportamiento por defecto, ancho definido por el contenido y sin alineación forzada. - #[default] - Default, - /// Alinea los elementos al inicio de la fila. - Start, - /// Centra horizontalmente los elementos. - Center, - /// Alinea los elementos al final de la fila. - End, - /// Apila los elementos en columna. - Vertical, - /// Los elementos se expanden para rellenar la fila. - Fill, - /// Todos los elementos ocupan el mismo ancho rellenando la fila. - Justified, -} - -impl Layout { - const START: &str = "justify-content-start"; - const CENTER: &str = "justify-content-center"; - const END: &str = "justify-content-end"; - const VERTICAL: &str = "flex-column"; - const FILL: &str = "nav-fill"; - const JUSTIFIED: &str = "nav-justified"; - - // Devuelve la clase base asociada a la distribución y orientación del menú. - #[rustfmt::skip] - #[inline] - const fn as_str(self) -> &'static str { - match self { - Self::Default => "", - Self::Start => Self::START, - Self::Center => Self::CENTER, - Self::End => Self::END, - Self::Vertical => Self::VERTICAL, - Self::Fill => Self::FILL, - Self::Justified => Self::JUSTIFIED, - } - } - - // Añade la clase asociada a la distribución y orientación del menú a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let class = self.as_str(); - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - - /* Devuelve la clase asociada a la distribución y orientación del menú, o una cadena vacía si no - // aplica (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - self.as_str().to_owned() - } */ -} diff --git a/extensions/pagetop-bootsier/src/theme/navbar.rs b/extensions/pagetop-bootsier/src/theme/navbar.rs deleted file mode 100644 index 7b958d7f..00000000 --- a/extensions/pagetop-bootsier/src/theme/navbar.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! Definiciones para crear barras de navegación [`Navbar`]. -//! -//! Cada [`navbar::Item`](crate::theme::navbar::Item) representa un elemento individual de la barra -//! de navegación [`Navbar`], con distintos comportamientos según su finalidad, como menús -//! [`Nav`](crate::theme::Nav) o textos localizados usando [`L10n`](pagetop::locale::L10n). -//! -//! También puede mostrar una marca de identidad ([`navbar::Brand`](crate::theme::navbar::Brand)) -//! que identifique la compañía, producto o nombre del proyecto asociado a la solución web. -//! -//! # Ejemplos -//! -//! Barra **simple**, sólo con un menú horizontal: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let navbar = Navbar::simple() -//! .add_item(navbar::Item::nav( -//! Nav::new() -//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(nav::Item::link(L10n::n("About"), |_| "/about")) -//! .add_item(nav::Item::link(L10n::n("Contact"), |_| "/contact")) -//! )); -//! ``` -//! -//! Barra **colapsable**, con botón de despliegue y contenido en el desplegable cuando colapsa: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let navbar = Navbar::simple_toggle() -//! .with_expand(BreakPoint::MD) -//! .add_item(navbar::Item::nav( -//! Nav::new() -//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://docs.example.com")) -//! .add_item(nav::Item::link(L10n::n("Support"), |_| "/support")) -//! )); -//! ``` -//! -//! Barra con **marca de identidad a la izquierda** y menú a la derecha, típica de una cabecera: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let brand = navbar::Brand::new() -//! .with_title(L10n::n("PageTop")) -//! .with_path(Some(|_| "/")); -//! -//! let navbar = Navbar::brand_left(brand) -//! .add_item(navbar::Item::nav( -//! Nav::new() -//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(nav::Item::dropdown( -//! Dropdown::new() -//! .with_title(L10n::n("Tools")) -//! .add_item(dropdown::Item::link(L10n::n("Generator"), |_| "/tools/gen")) -//! .add_item(dropdown::Item::link(L10n::n("Reports"), |_| "/tools/reports")) -//! )) -//! .add_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#")) -//! )); -//! ``` -//! -//! Barra con **botón de despliegue a la izquierda** y **marca de identidad a la derecha**: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let brand = navbar::Brand::new() -//! .with_title(L10n::n("Intranet")) -//! .with_path(Some(|_| "/")); -//! -//! let navbar = Navbar::brand_right(brand) -//! .with_expand(BreakPoint::LG) -//! .add_item(navbar::Item::nav( -//! Nav::pills() -//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard")) -//! .add_item(nav::Item::link(L10n::n("Users"), |_| "/users")) -//! )); -//! ``` -//! -//! Barra con el **contenido en un *offcanvas***, ideal para dispositivos móviles o menús largos: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let oc = Offcanvas::new() -//! .with_id("main_offcanvas") -//! .with_title(L10n::n("Main menu")) -//! .with_placement(offcanvas::Placement::Start) -//! .with_backdrop(offcanvas::Backdrop::Enabled); -//! -//! let navbar = Navbar::offcanvas(oc) -//! .add_item(navbar::Item::nav( -//! Nav::new() -//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/")) -//! .add_item(nav::Item::link(L10n::n("Profile"), |_| "/profile")) -//! .add_item(nav::Item::dropdown( -//! Dropdown::new() -//! .with_title(L10n::n("More")) -//! .add_item(dropdown::Item::link(L10n::n("Settings"), |_| "/settings")) -//! .add_item(dropdown::Item::link(L10n::n("Help"), |_| "/help")) -//! )) -//! )); -//! ``` -//! -//! Barra **fija arriba**: -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let brand = navbar::Brand::new() -//! .with_title(L10n::n("Main App")) -//! .with_path(Some(|_| "/")); -//! -//! let navbar = Navbar::brand_left(brand) -//! .with_position(navbar::Position::FixedTop) -//! .add_item(navbar::Item::nav( -//! Nav::new() -//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/")) -//! .add_item(nav::Item::link(L10n::n("Donors"), |_| "/donors")) -//! .add_item(nav::Item::link(L10n::n("Stock"), |_| "/stock")) -//! )); -//! ``` - -mod props; -pub use props::{Layout, Position}; - -mod brand; -pub use brand::Brand; - -mod component; -pub use component::Navbar; - -mod item; -pub use item::Item; diff --git a/extensions/pagetop-bootsier/src/theme/navbar/brand.rs b/extensions/pagetop-bootsier/src/theme/navbar/brand.rs deleted file mode 100644 index e969b0e4..00000000 --- a/extensions/pagetop-bootsier/src/theme/navbar/brand.rs +++ /dev/null @@ -1,111 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -/// Marca de identidad para mostrar en una barra de navegación [`Navbar`]. -/// -/// Representa la identidad del sitio con una imagen, título y eslogan: -/// -/// - Si hay URL ([`with_path()`](Self::with_path)), el bloque completo actúa como enlace. Por -/// defecto enlaza a la raíz del sitio (`/`). -/// - Si no hay imagen ([`with_image()`](Self::with_image)) ni título -/// ([`with_title()`](Self::with_title)), la marca de identidad no se renderiza. -/// - El eslogan ([`with_slogan()`](Self::with_slogan)) es opcional; por defecto no tiene contenido. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Brand { - id : AttrId, - image : Typed<Image>, - #[default(_code = "L10n::n(&global::SETTINGS.app.name)")] - title : L10n, - slogan: L10n, - #[default(_code = "Some(|_| \"/\")")] - path : Option<FnPathByContext>, -} - -impl Component for Brand { - fn new() -> Self { - Brand::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let image = self.image().render(cx); - let title = self.title().using(cx); - if title.is_empty() && image.is_empty() { - return PrepareMarkup::None; - } - let slogan = self.slogan().using(cx); - PrepareMarkup::With(html! { - @if let Some(path) = self.path() { - a class="navbar-brand" href=(path(cx)) { (image) (title) (slogan) } - } @else { - span class="navbar-brand" { (image) (title) (slogan) } - } - }) - } -} - -impl Brand { - // **< Brand BUILDER >************************************************************************** - - /// Establece el identificador único (`id`) de la marca. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Asigna o quita la imagen de marca. Si se pasa `None`, no se mostrará. - #[builder_fn] - pub fn with_image(mut self, image: Option<Image>) -> Self { - self.image.alter_component(image); - self - } - - /// Establece el título de la identidad de marca. - #[builder_fn] - pub fn with_title(mut self, title: L10n) -> Self { - self.title = title; - self - } - - /// Define el eslogan de la marca. - #[builder_fn] - pub fn with_slogan(mut self, slogan: L10n) -> Self { - self.slogan = slogan; - self - } - - /// Define la URL de destino. Si es `None`, la marca no será un enlace. - #[builder_fn] - pub fn with_path(mut self, path: Option<FnPathByContext>) -> Self { - self.path = path; - self - } - - // **< Brand GETTERS >************************************************************************** - - /// Devuelve la imagen de marca (si la hay). - pub fn image(&self) -> &Typed<Image> { - &self.image - } - - /// Devuelve el título de la identidad de marca. - pub fn title(&self) -> &L10n { - &self.title - } - - /// Devuelve el eslogan de la marca. - pub fn slogan(&self) -> &L10n { - &self.slogan - } - - /// Devuelve la función que resuelve la URL asociada a la marca (si existe). - pub fn path(&self) -> &Option<FnPathByContext> { - &self.path - } -} diff --git a/extensions/pagetop-bootsier/src/theme/navbar/component.rs b/extensions/pagetop-bootsier/src/theme/navbar/component.rs deleted file mode 100644 index b40d06c2..00000000 --- a/extensions/pagetop-bootsier/src/theme/navbar/component.rs +++ /dev/null @@ -1,293 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; -use crate::LOCALES_BOOTSIER; - -const TOGGLE_COLLAPSE: &str = "collapse"; -const TOGGLE_OFFCANVAS: &str = "offcanvas"; - -/// Componente para crear una **barra de navegación**. -/// -/// Permite mostrar enlaces, menús y una marca de identidad en distintas disposiciones (simples, con -/// botón de despliegue o dentro de un [`offcanvas`]), controladas por [`navbar::Layout`]. También -/// puede fijarse en la parte superior o inferior del documento mediante [`navbar::Position`]. -/// -/// Ver ejemplos en el módulo [`navbar`]. -/// Si no contiene elementos, el componente **no se renderiza**. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Navbar { - id : AttrId, - classes : AttrClasses, - expand : BreakPoint, - layout : navbar::Layout, - position : navbar::Position, - items : Children, -} - -impl Component for Navbar { - fn new() -> Self { - Navbar::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, { - let mut classes = "navbar".to_string(); - self.expand().push_class(&mut classes, "navbar-expand", ""); - self.position().push_class(&mut classes); - classes - }); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - // Botón de despliegue (colapso u offcanvas) para la barra. - fn button(cx: &mut Context, data_bs_toggle: &str, id_content: &str) -> Markup { - let id_content_target = join!("#", id_content); - let aria_expanded = if data_bs_toggle == TOGGLE_COLLAPSE { - Some("false") - } else { - None - }; - html! { - button - type="button" - class="navbar-toggler" - data-bs-toggle=(data_bs_toggle) - data-bs-target=(id_content_target) - aria-controls=(id_content) - aria-expanded=[aria_expanded] - aria-label=[L10n::t("toggle", &LOCALES_BOOTSIER).lookup(cx)] - { - span class="navbar-toggler-icon" {} - } - } - } - - // Si no hay contenidos, no tiene sentido mostrar una barra vacía. - let items = self.items().render(cx); - if items.is_empty() { - return PrepareMarkup::None; - } - - // Asegura que la barra tiene un id estable para poder asociarlo al colapso/offcanvas. - let id = cx.required_id::<Self>(self.id()); - - PrepareMarkup::With(html! { - nav id=(id) class=[self.classes().get()] { - div class="container-fluid" { - @match self.layout() { - // Barra más sencilla: sólo contenido. - navbar::Layout::Simple => { - (items) - }, - - // Barra sencilla que se puede contraer/expandir. - navbar::Layout::SimpleToggle => { - @let id_content = join!(id, "-content"); - - (button(cx, TOGGLE_COLLAPSE, &id_content)) - div id=(id_content) class="collapse navbar-collapse" { - (items) - } - }, - - // Barra con marca a la izquierda, siempre visible. - navbar::Layout::SimpleBrandLeft(brand) => { - (brand.render(cx)) - (items) - }, - - // Barra con marca a la izquierda y botón a la derecha. - navbar::Layout::BrandLeft(brand) => { - @let id_content = join!(id, "-content"); - - (brand.render(cx)) - (button(cx, TOGGLE_COLLAPSE, &id_content)) - div id=(id_content) class="collapse navbar-collapse" { - (items) - } - }, - - // Barra con botón a la izquierda y marca a la derecha. - navbar::Layout::BrandRight(brand) => { - @let id_content = join!(id, "-content"); - - (button(cx, TOGGLE_COLLAPSE, &id_content)) - (brand.render(cx)) - div id=(id_content) class="collapse navbar-collapse" { - (items) - } - }, - - // Barra cuyo contenido se muestra en un offcanvas, sin marca. - navbar::Layout::Offcanvas(offcanvas) => { - @let id_content = offcanvas.id().unwrap_or_default(); - - (button(cx, TOGGLE_OFFCANVAS, &id_content)) - @if let Some(oc) = offcanvas.borrow() { - (oc.render_offcanvas(cx, Some(self.items()))) - } - }, - - // Barra con marca a la izquierda y contenido en offcanvas. - navbar::Layout::OffcanvasBrandLeft(brand, offcanvas) => { - @let id_content = offcanvas.id().unwrap_or_default(); - - (brand.render(cx)) - (button(cx, TOGGLE_OFFCANVAS, &id_content)) - @if let Some(oc) = offcanvas.borrow() { - (oc.render_offcanvas(cx, Some(self.items()))) - } - }, - - // Barra con contenido en offcanvas y marca a la derecha. - navbar::Layout::OffcanvasBrandRight(brand, offcanvas) => { - @let id_content = offcanvas.id().unwrap_or_default(); - - (button(cx, TOGGLE_OFFCANVAS, &id_content)) - (brand.render(cx)) - @if let Some(oc) = offcanvas.borrow() { - (oc.render_offcanvas(cx, Some(self.items()))) - } - }, - } - } - } - }) - } -} - -impl Navbar { - /// Crea una barra de navegación **simple**, sin marca y sin botón. - pub fn simple() -> Self { - Navbar::default().with_layout(navbar::Layout::Simple) - } - - /// Crea una barra de navegación **simple pero colapsable**, con botón a la izquierda. - pub fn simple_toggle() -> Self { - Navbar::default().with_layout(navbar::Layout::SimpleToggle) - } - - /// Crea una barra de navegación **con marca a la izquierda**, siempre visible. - pub fn simple_brand_left(brand: navbar::Brand) -> Self { - Navbar::default().with_layout(navbar::Layout::SimpleBrandLeft(Typed::with(brand))) - } - - /// Crea una barra de navegación con **marca a la izquierda** y **botón a la derecha**. - pub fn brand_left(brand: navbar::Brand) -> Self { - Navbar::default().with_layout(navbar::Layout::BrandLeft(Typed::with(brand))) - } - - /// Crea una barra de navegación con **botón a la izquierda** y **marca a la derecha**. - pub fn brand_right(brand: navbar::Brand) -> Self { - Navbar::default().with_layout(navbar::Layout::BrandRight(Typed::with(brand))) - } - - /// Crea una barra de navegación cuyo contenido se muestra en un **offcanvas**. - pub fn offcanvas(oc: Offcanvas) -> Self { - Navbar::default().with_layout(navbar::Layout::Offcanvas(Typed::with(oc))) - } - - /// Crea una barra de navegación con **marca a la izquierda** y contenido en **offcanvas**. - pub fn offcanvas_brand_left(brand: navbar::Brand, oc: Offcanvas) -> Self { - Navbar::default().with_layout(navbar::Layout::OffcanvasBrandLeft( - Typed::with(brand), - Typed::with(oc), - )) - } - - /// Crea una barra de navegación con **marca a la derecha** y contenido en **offcanvas**. - pub fn offcanvas_brand_right(brand: navbar::Brand, oc: Offcanvas) -> Self { - Navbar::default().with_layout(navbar::Layout::OffcanvasBrandRight( - Typed::with(brand), - Typed::with(oc), - )) - } - - // **< Navbar BUILDER >************************************************************************* - - /// Establece el identificador único (`id`) de la barra de navegación. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas a la barra de navegación. - /// - /// También acepta clases predefinidas para: - /// - /// - Modificar el color de fondo ([`classes::Background`]). - /// - Definir la apariencia del texto ([`classes::Text`]). - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Define a partir de qué punto de ruptura la barra de navegación deja de colapsar. - #[builder_fn] - pub fn with_expand(mut self, bp: BreakPoint) -> Self { - self.expand = bp; - self - } - - /// Define el tipo de disposición que tendrá la barra de navegación. - #[builder_fn] - pub fn with_layout(mut self, layout: navbar::Layout) -> Self { - self.layout = layout; - self - } - - /// Define dónde se mostrará la barra de navegación dentro del documento. - #[builder_fn] - pub fn with_position(mut self, position: navbar::Position) -> Self { - self.position = position; - self - } - - /// Añade un nuevo contenido hijo. - #[inline] - pub fn add_item(mut self, item: navbar::Item) -> Self { - self.items.add(Child::with(item)); - self - } - - /// Modifica la lista de contenidos (`children`) aplicando una operación [`TypedOp`]. - #[builder_fn] - pub fn with_items(mut self, op: TypedOp<navbar::Item>) -> Self { - self.items.alter_typed(op); - self - } - - // **< Navbar GETTERS >************************************************************************* - - /// Devuelve las clases CSS asociadas a la barra de navegación. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el punto de ruptura configurado. - pub fn expand(&self) -> &BreakPoint { - &self.expand - } - - /// Devuelve la disposición configurada para la barra de navegación. - pub fn layout(&self) -> &navbar::Layout { - &self.layout - } - - /// Devuelve la posición configurada para la barra de navegación. - pub fn position(&self) -> &navbar::Position { - &self.position - } - - /// Devuelve la lista de contenidos (`children`). - pub fn items(&self) -> &Children { - &self.items - } -} diff --git a/extensions/pagetop-bootsier/src/theme/navbar/item.rs b/extensions/pagetop-bootsier/src/theme/navbar/item.rs deleted file mode 100644 index 7e912a49..00000000 --- a/extensions/pagetop-bootsier/src/theme/navbar/item.rs +++ /dev/null @@ -1,95 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -/// Elementos que puede contener una barra de navegación [`Navbar`](crate::theme::Navbar). -/// -/// Cada variante determina qué se renderiza y cómo. Estos elementos se colocan **dentro del -/// contenido** de la barra (la parte colapsable, el *offcanvas* o el bloque simple), por lo que son -/// independientes de la marca o del botón que ya pueda definir el propio [`navbar::Layout`]. -#[derive(AutoDefault)] -pub enum Item { - /// Sin contenido, no produce salida. - #[default] - Void, - /// Marca de identidad mostrada dentro del contenido de la barra de navegación. - /// - /// Útil cuando el [`navbar::Layout`] no incluye marca, y se quiere incluir dentro del área - /// colapsable/*offcanvas*. Si el *layout* ya muestra una marca, esta variante no la sustituye, - /// sólo añade otra dentro del bloque de contenidos. - Brand(Typed<navbar::Brand>), - /// Representa un menú de navegación [`Nav`](crate::theme::Nav). - Nav(Typed<Nav>), - /// Representa un texto libre localizado. - Text(L10n), -} - -impl Component for Item { - fn new() -> Self { - Item::default() - } - - fn id(&self) -> Option<String> { - match self { - Self::Void => None, - Self::Brand(brand) => brand.id(), - Self::Nav(nav) => nav.id(), - Self::Text(_) => None, - } - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - if let Self::Nav(nav) = self { - if let Some(mut nav) = nav.borrow_mut() { - nav.alter_classes(ClassesOp::Prepend, "navbar-nav"); - } - } - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - match self { - Self::Void => PrepareMarkup::None, - Self::Brand(brand) => PrepareMarkup::With(html! { (brand.render(cx)) }), - Self::Nav(nav) => { - if let Some(nav) = nav.borrow() { - let items = nav.items().render(cx); - if items.is_empty() { - return PrepareMarkup::None; - } - PrepareMarkup::With(html! { - ul id=[nav.id()] class=[nav.classes().get()] { - (items) - } - }) - } else { - PrepareMarkup::None - } - } - Self::Text(text) => PrepareMarkup::With(html! { - span class="navbar-text" { - (text.using(cx)) - } - }), - } - } -} - -impl Item { - /// Crea un elemento de tipo [`navbar::Brand`] para añadir en el contenido de [`Navbar`]. - /// - /// Pensado para barras colapsables u offcanvas donde se quiere que la marca aparezca en la zona - /// desplegable. - pub fn brand(brand: navbar::Brand) -> Self { - Self::Brand(Typed::with(brand)) - } - - /// Crea un elemento de tipo [`Nav`] para añadir al contenido de [`Navbar`]. - pub fn nav(item: Nav) -> Self { - Self::Nav(Typed::with(item)) - } - - /// Crea un elemento de texto localizado, mostrado sin interacción. - pub fn text(item: L10n) -> Self { - Self::Text(item) - } -} diff --git a/extensions/pagetop-bootsier/src/theme/navbar/props.rs b/extensions/pagetop-bootsier/src/theme/navbar/props.rs deleted file mode 100644 index 1aeb6170..00000000 --- a/extensions/pagetop-bootsier/src/theme/navbar/props.rs +++ /dev/null @@ -1,98 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; - -// **< Layout >************************************************************************************* - -/// Representa los diferentes tipos de presentación de una barra de navegación [`Navbar`]. -#[derive(AutoDefault)] -pub enum Layout { - /// Barra simple, sin marca de identidad y sin botón de despliegue. - /// - /// La barra de navegación no se colapsa. - #[default] - Simple, - - /// Barra simple, con botón de despliegue a la izquierda y sin marca de identidad. - SimpleToggle, - - /// Barra simple, con marca de identidad a la izquierda y sin botón de despliegue. - /// - /// La barra de navegación no se colapsa. - SimpleBrandLeft(Typed<navbar::Brand>), - - /// Barra con marca de identidad a la izquierda y botón de despliegue a la derecha. - BrandLeft(Typed<navbar::Brand>), - - /// Barra con botón de despliegue a la izquierda y marca de identidad a la derecha. - BrandRight(Typed<navbar::Brand>), - - /// Contenido en [`Offcanvas`], con botón de despliegue a la izquierda y sin marca de identidad. - Offcanvas(Typed<Offcanvas>), - - /// Contenido en [`Offcanvas`], con marca de identidad a la izquierda y botón de despliegue a la - /// derecha. - OffcanvasBrandLeft(Typed<navbar::Brand>, Typed<Offcanvas>), - - /// Contenido en [`Offcanvas`], con botón de despliegue a la izquierda y marca de identidad a la - /// derecha. - OffcanvasBrandRight(Typed<navbar::Brand>, Typed<Offcanvas>), -} - -// **< Position >*********************************************************************************** - -/// Posición global de una barra de navegación [`Navbar`] en el documento. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Position { - /// Barra normal, fluye con el documento. - #[default] - Static, - /// Barra fijada en la parte superior, siempre visible. - /// - /// Puede ser necesario reservar espacio en la parte superior del contenido que fluye debajo - /// para evitar que quede oculto por la barra. - FixedTop, - /// Barra fijada en la parte inferior, siempre visible. - /// - /// Puede ser necesario reservar espacio en la parte inferior del contenido que fluye debajo - /// para evitar que quede oculto por la barra. - FixedBottom, - /// La barra de navegación se fija en la parte superior al hacer *scroll*. - StickyTop, - /// La barra de navegación se fija en la parte inferior al hacer *scroll*. - StickyBottom, -} - -impl Position { - // Devuelve la clase base asociada a la posición de la barra de navegación. - #[inline] - const fn as_str(self) -> &'static str { - match self { - Self::Static => "", - Self::FixedTop => "fixed-top", - Self::FixedBottom => "fixed-bottom", - Self::StickyTop => "sticky-top", - Self::StickyBottom => "sticky-bottom", - } - } - - // Añade la clase asociada a la posición de la barra de navegación a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let class = self.as_str(); - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - - /* Devuelve la clase asociada a la posición de la barra de navegación, o cadena vacía si no - // aplica (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - self.as_str().to_string() - } */ -} diff --git a/extensions/pagetop-bootsier/src/theme/offcanvas.rs b/extensions/pagetop-bootsier/src/theme/offcanvas.rs deleted file mode 100644 index 18cc253a..00000000 --- a/extensions/pagetop-bootsier/src/theme/offcanvas.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Definiciones para crear paneles laterales deslizantes [`Offcanvas`]. -//! -//! # Ejemplo -//! -//! ```rust -//! # use pagetop::prelude::*; -//! # use pagetop_bootsier::prelude::*; -//! let panel = Offcanvas::new() -//! .with_id("offcanvas_example") -//! .with_title(L10n::n("Offcanvas title")) -//! .with_placement(offcanvas::Placement::End) -//! .with_backdrop(offcanvas::Backdrop::Enabled) -//! .with_body_scroll(offcanvas::BodyScroll::Enabled) -//! .with_visibility(offcanvas::Visibility::Default) -//! .add_child(Dropdown::new() -//! .with_title(L10n::n("Menu")) -//! .add_item(dropdown::Item::label(L10n::n("Label"))) -//! .add_item(dropdown::Item::link_blank(L10n::n("Google"), |_| "https://www.google.es")) -//! .add_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout")) -//! ); -//! ``` - -mod props; -pub use props::{Backdrop, BodyScroll, Placement, Visibility}; - -mod component; -pub use component::Offcanvas; diff --git a/extensions/pagetop-bootsier/src/theme/offcanvas/component.rs b/extensions/pagetop-bootsier/src/theme/offcanvas/component.rs deleted file mode 100644 index f17fe97a..00000000 --- a/extensions/pagetop-bootsier/src/theme/offcanvas/component.rs +++ /dev/null @@ -1,240 +0,0 @@ -use pagetop::prelude::*; - -use crate::prelude::*; -use crate::LOCALES_BOOTSIER; - -/// Componente para crear un **panel lateral deslizante** con contenidos adicionales. -/// -/// Útil para navegación, filtros, formularios o menús contextuales. Incluye las siguientes -/// características principales: -/// -/// - Puede mostrar una capa de fondo para centrar la atención del usuario en el panel -/// ([`with_backdrop()`](Self::with_backdrop)); o puede bloquear el desplazamiento del documento -/// principal ([`with_body_scroll()`](Self::with_body_scroll)). -/// - Se puede configurar el borde de la ventana desde el que se desliza el panel -/// ([`with_placement()`](Self::with_placement)). -/// - Encabezado con título ([`with_title()`](Self::with_title)) y **botón de cierre** integrado. -/// - Puede cambiar su comportamiento a partir de un punto de ruptura -/// ([`with_breakpoint()`](Self::with_breakpoint)). -/// - Asocia título y controles de accesibilidad a un identificador único y expone atributos -/// adecuados para lectores de pantalla y navegación por teclado. -/// -/// Ver ejemplo en el módulo [`offcanvas`]. -/// Si no contiene elementos, el componente **no se renderiza**. -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Offcanvas { - id : AttrId, - classes : AttrClasses, - title : L10n, - breakpoint: BreakPoint, - backdrop : offcanvas::Backdrop, - scrolling : offcanvas::BodyScroll, - placement : offcanvas::Placement, - visibility: offcanvas::Visibility, - children : Children, -} - -impl Component for Offcanvas { - fn new() -> Self { - Offcanvas::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, { - let mut classes = "offcanvas".to_string(); - self.breakpoint().push_class(&mut classes, "offcanvas", ""); - self.placement().push_class(&mut classes); - self.visibility().push_class(&mut classes); - classes - }); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - PrepareMarkup::With(self.render_offcanvas(cx, None)) - } -} - -impl Offcanvas { - // **< Offcanvas BUILDER >********************************************************************** - - /// Establece el identificador único (`id`) del panel. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al panel. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Establece el título del encabezado. - #[builder_fn] - pub fn with_title(mut self, title: L10n) -> Self { - self.title = title; - self - } - - /// Establece el punto de ruptura a partir del cual cambia el comportamiento del panel. - /// - /// - **Por debajo** de ese tamaño de pantalla, el componente actúa como panel deslizante - /// ([`Offcanvas`]). - /// - **Por encima**, el contenido del panel se muestra tal cual, integrado en la página. - /// - /// Por ejemplo, con `BreakPoint::LG`, será *offcanvas* en móviles y tabletas, y visible - /// directamente en pantallas grandes. Por defecto usa `BreakPoint::None` para que sea - /// *offcanvas* siempre. - #[builder_fn] - pub fn with_breakpoint(mut self, bp: BreakPoint) -> Self { - self.breakpoint = bp; - self - } - - /// Ajusta la capa de fondo del panel para definir su comportamiento al hacer clic fuera del - /// panel. - #[builder_fn] - pub fn with_backdrop(mut self, backdrop: offcanvas::Backdrop) -> Self { - self.backdrop = backdrop; - self - } - - /// Permite o bloquea el desplazamiento de la página principal mientras el panel está abierto. - #[builder_fn] - pub fn with_body_scroll(mut self, scrolling: offcanvas::BodyScroll) -> Self { - self.scrolling = scrolling; - self - } - - /// Indica desde qué borde de la ventana entra y se ancla el panel. - #[builder_fn] - pub fn with_placement(mut self, placement: offcanvas::Placement) -> Self { - self.placement = placement; - self - } - - /// Fija el estado inicial del panel (oculto o visible al cargar). - #[builder_fn] - pub fn with_visibility(mut self, visibility: offcanvas::Visibility) -> Self { - self.visibility = visibility; - self - } - - /// Añade un nuevo componente hijo al panel. - #[inline] - pub fn add_child(mut self, child: impl Component) -> Self { - self.children.add(Child::with(child)); - self - } - - /// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`]. - #[builder_fn] - pub fn with_children(mut self, op: ChildOp) -> Self { - self.children.alter_child(op); - self - } - - // **< Offcanvas GETTERS >********************************************************************** - - /// Devuelve las clases CSS asociadas al panel. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el título del panel. - pub fn title(&self) -> &L10n { - &self.title - } - - /// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel. - pub fn breakpoint(&self) -> &BreakPoint { - &self.breakpoint - } - - /// Devuelve el comportamiento configurado para la capa de fondo. - pub fn backdrop(&self) -> &offcanvas::Backdrop { - &self.backdrop - } - - /// Indica si la página principal puede desplazarse mientras el panel está abierto. - pub fn body_scroll(&self) -> &offcanvas::BodyScroll { - &self.scrolling - } - - /// Devuelve la posición de inicio del panel. - pub fn placement(&self) -> &offcanvas::Placement { - &self.placement - } - - /// Devuelve el estado inicial del panel. - pub fn visibility(&self) -> &offcanvas::Visibility { - &self.visibility - } - - /// Devuelve la lista de componentes (`children`) del panel. - pub fn children(&self) -> &Children { - &self.children - } - - // **< Offcanvas HELPERS >********************************************************************** - - pub(crate) fn render_offcanvas(&self, cx: &mut Context, extra: Option<&Children>) -> Markup { - let body = self.children().render(cx); - let body_extra = extra.map(|c| c.render(cx)).unwrap_or_else(|| html! {}); - if body.is_empty() && body_extra.is_empty() { - return html! {}; - } - - let id = cx.required_id::<Self>(self.id()); - let id_label = join!(id, "-label"); - let id_target = join!("#", id); - - let body_scroll = match self.body_scroll() { - offcanvas::BodyScroll::Disabled => None, - offcanvas::BodyScroll::Enabled => Some("true"), - }; - - let backdrop = match self.backdrop() { - offcanvas::Backdrop::Disabled => Some("false"), - offcanvas::Backdrop::Enabled => None, - offcanvas::Backdrop::Static => Some("static"), - }; - - let title = self.title().using(cx); - - html! { - div - id=(id) - class=[self.classes().get()] - tabindex="-1" - data-bs-scroll=[body_scroll] - data-bs-backdrop=[backdrop] - aria-labelledby=(id_label) - { - div class="offcanvas-header" { - @if !title.is_empty() { - h5 class="offcanvas-title" id=(id_label) { (title) } - } - button - type="button" - class="btn-close" - data-bs-dismiss="offcanvas" - data-bs-target=(id_target) - aria-label=[L10n::t("offcanvas_close", &LOCALES_BOOTSIER).lookup(cx)] - {} - } - div class="offcanvas-body" { - (body) - (body_extra) - } - } - } - } -} diff --git a/extensions/pagetop-bootsier/src/theme/offcanvas/props.rs b/extensions/pagetop-bootsier/src/theme/offcanvas/props.rs deleted file mode 100644 index cdfe8623..00000000 --- a/extensions/pagetop-bootsier/src/theme/offcanvas/props.rs +++ /dev/null @@ -1,119 +0,0 @@ -use pagetop::prelude::*; - -// **< Backdrop >*********************************************************************************** - -/// Comportamiento de la capa de fondo (*backdrop*) de un panel -/// [`Offcanvas`](crate::theme::Offcanvas) al deslizarse. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Backdrop { - /// Sin capa de fondo, la página principal permanece visible e interactiva. - Disabled, - /// Opción por defecto, se oscurece el fondo; un clic fuera del panel suele cerrarlo. - #[default] - Enabled, - /// Muestra la capa de fondo pero no se cierra al hacer clic fuera del panel. Útil si se - /// requiere completar una acción antes de salir. - Static, -} - -// **< BodyScroll >********************************************************************************* - -/// Controla si la página principal puede desplazarse al abrir un panel -/// [`Offcanvas`](crate::theme::Offcanvas). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum BodyScroll { - /// Opción por defecto, la página principal se bloquea centrando la interacción en el panel. - #[default] - Disabled, - /// Permite el desplazamiento de la página principal. - Enabled, -} - -// **< Placement >********************************************************************************** - -/// Posición de aparición de un panel [`Offcanvas`](crate::theme::Offcanvas) al deslizarse. -/// -/// Define desde qué borde de la ventana entra y se ancla el panel. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Placement { - /// Opción por defecto, desde el borde inicial según dirección de lectura (respetando LTR/RTL). - #[default] - Start, - /// Desde el borde final según dirección de lectura (respetando LTR/RTL). - End, - /// Desde la parte superior. - Top, - /// Desde la parte inferior. - Bottom, -} - -impl Placement { - // Devuelve la clase base asociada a la posición de aparición del panel. - #[rustfmt::skip] - #[inline] - const fn as_str(self) -> &'static str { - match self { - Placement::Start => "offcanvas-start", - Placement::End => "offcanvas-end", - Placement::Top => "offcanvas-top", - Placement::Bottom => "offcanvas-bottom", - } - } - - // Añade la clase asociada a la posición de aparición del panel a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(self.as_str()); - } - - /* Devuelve la clase asociada a la posición de aparición del panel (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - self.as_str().to_owned() - } */ -} - -// **< Visibility >********************************************************************************* - -/// Estado inicial de un panel [`Offcanvas`](crate::theme::Offcanvas). -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum Visibility { - /// El panel permanece oculto desde el principio. - #[default] - Default, - /// El panel se muestra abierto al cargar. - Show, -} - -impl Visibility { - // Devuelve la clase base asociada al estado inicial del panel. - #[inline] - const fn as_str(self) -> &'static str { - match self { - Visibility::Default => "", - Visibility::Show => "show", - } - } - - // Añade la clase asociada al estado inicial del panel a la cadena de clases. - #[inline] - pub(crate) fn push_class(self, classes: &mut String) { - let class = self.as_str(); - if class.is_empty() { - return; - } - if !classes.is_empty() { - classes.push(' '); - } - classes.push_str(class); - } - - /* Devuelve la clase asociada al estado inicial, o una cadena vacía si no aplica (reservado). - #[inline] - pub(crate) fn to_class(self) -> String { - self.as_str().to_owned() - } */ -} diff --git a/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js b/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js deleted file mode 100644 index 0b873693..00000000 --- a/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v5.3.8 (https://getbootstrap.com/) - * Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e()}(this,function(){"use strict";const t=new Map,e={set(e,i,n){t.has(e)||t.set(e,new Map);const s=t.get(e);s.has(i)||0===s.size?s.set(i,n):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(e,i)=>t.has(e)&&t.get(e).get(i)||null,remove(e,i){if(!t.has(e))return;const n=t.get(e);n.delete(i),0===n.size&&t.delete(e)}},i="transitionend",n=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,(t,e)=>`#${CSS.escape(e)}`)),t),s=t=>null==t?`${t}`:Object.prototype.toString.call(t).match(/\s([a-z]+)/i)[1].toLowerCase(),o=t=>{t.dispatchEvent(new Event(i))},r=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),a=t=>r(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(n(t)):null,l=t=>{if(!r(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},c=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),h=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?h(t.parentNode):null},d=()=>{},u=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,p=[],m=()=>"rtl"===document.documentElement.dir,g=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(p.length||document.addEventListener("DOMContentLoaded",()=>{for(const t of p)t()}),p.push(e)):e()},_=(t,e=[],i=t)=>"function"==typeof t?t.call(...e):i,b=(t,e,n=!0)=>{if(!n)return void _(t);const s=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let r=!1;const a=({target:n})=>{n===e&&(r=!0,e.removeEventListener(i,a),_(t))};e.addEventListener(i,a),setTimeout(()=>{r||o(e)},s)},v=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},y=/[^.]*(?=\..*)\.|.*/,w=/\..*/,A=/::\d+$/,E={};let T=1;const C={mouseenter:"mouseover",mouseleave:"mouseout"},O=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function x(t,e){return e&&`${e}::${T++}`||t.uidEvent||T++}function k(t){const e=x(t);return t.uidEvent=e,E[e]=E[e]||{},E[e]}function L(t,e,i=null){return Object.values(t).find(t=>t.callable===e&&t.delegationSelector===i)}function S(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=N(t);return O.has(o)||(o=t),[n,s,o]}function D(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=S(e,i,n);if(e in C){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=k(t),c=l[a]||(l[a]={}),h=L(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=x(r,e.replace(y,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return j(s,{delegateTarget:r}),n.oneOff&&P.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return j(n,{delegateTarget:t}),i.oneOff&&P.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function $(t,e,i,n,s){const o=L(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function I(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&$(t,e,i,r.callable,r.delegationSelector)}function N(t){return t=t.replace(w,""),C[t]||t}const P={on(t,e,i,n){D(t,e,i,n,!1)},one(t,e,i,n){D(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=S(e,i,n),a=r!==e,l=k(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))I(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(A,"");a&&!e.includes(s)||$(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;$(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=f();let s=null,o=!0,r=!0,a=!1;e!==N(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=j(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function j(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function M(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function F(t){return t.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}const H={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${F(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${F(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter(t=>t.startsWith("bs")&&!t.startsWith("bsConfig"));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1),e[i]=M(t.dataset[n])}return e},getDataAttribute:(t,e)=>M(t.getAttribute(`data-bs-${F(e)}`))};class W{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=r(e)?H.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...r(e)?H.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[i,n]of Object.entries(e)){const e=t[i],o=r(e)?"element":s(e);if(!new RegExp(n).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${i}" provided type "${o}" but expected type "${n}".`)}}}class B extends W{constructor(t,i){super(),(t=a(t))&&(this._element=t,this._config=this._getConfig(i),e.set(this._element,this.constructor.DATA_KEY,this))}dispose(){e.remove(this._element,this.constructor.DATA_KEY),P.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){b(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return e.get(a(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.8"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const z=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map(t=>n(t)).join(","):null},R={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter(t=>t.matches(e)),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(t=>`${t}:not([tabindex^="-"])`).join(",");return this.find(e,t).filter(t=>!c(t)&&l(t))},getSelectorFromElement(t){const e=z(t);return e&&R.findOne(e)?e:null},getElementFromSelector(t){const e=z(t);return e?R.findOne(e):null},getMultipleElementsFromSelector(t){const e=z(t);return e?R.find(e):[]}},q=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;P.on(document,i,`[data-bs-dismiss="${n}"]`,function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),c(this))return;const s=R.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()})},V=".bs.alert",K=`close${V}`,Q=`closed${V}`;class X extends B{static get NAME(){return"alert"}close(){if(P.trigger(this._element,K).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback(()=>this._destroyElement(),this._element,t)}_destroyElement(){this._element.remove(),P.trigger(this._element,Q),this.dispose()}static jQueryInterface(t){return this.each(function(){const e=X.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}})}}q(X,"close"),g(X);const Y='[data-bs-toggle="button"]';class U extends B{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each(function(){const e=U.getOrCreateInstance(this);"toggle"===t&&e[t]()})}}P.on(document,"click.bs.button.data-api",Y,t=>{t.preventDefault();const e=t.target.closest(Y);U.getOrCreateInstance(e).toggle()}),g(U);const G=".bs.swipe",J=`touchstart${G}`,Z=`touchmove${G}`,tt=`touchend${G}`,et=`pointerdown${G}`,it=`pointerup${G}`,nt={endCallback:null,leftCallback:null,rightCallback:null},st={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class ot extends W{constructor(t,e){super(),this._element=t,t&&ot.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return nt}static get DefaultType(){return st}static get NAME(){return"swipe"}dispose(){P.off(this._element,G)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),_(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&_(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(P.on(this._element,et,t=>this._start(t)),P.on(this._element,it,t=>this._end(t)),this._element.classList.add("pointer-event")):(P.on(this._element,J,t=>this._start(t)),P.on(this._element,Z,t=>this._move(t)),P.on(this._element,tt,t=>this._end(t)))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const rt=".bs.carousel",at=".data-api",lt="ArrowLeft",ct="ArrowRight",ht="next",dt="prev",ut="left",ft="right",pt=`slide${rt}`,mt=`slid${rt}`,gt=`keydown${rt}`,_t=`mouseenter${rt}`,bt=`mouseleave${rt}`,vt=`dragstart${rt}`,yt=`load${rt}${at}`,wt=`click${rt}${at}`,At="carousel",Et="active",Tt=".active",Ct=".carousel-item",Ot=Tt+Ct,xt={[lt]:ft,[ct]:ut},kt={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Lt={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class St extends B{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=R.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===At&&this.cycle()}static get Default(){return kt}static get DefaultType(){return Lt}static get NAME(){return"carousel"}next(){this._slide(ht)}nextWhenVisible(){!document.hidden&&l(this._element)&&this.next()}prev(){this._slide(dt)}pause(){this._isSliding&&o(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?P.one(this._element,mt,()=>this.cycle()):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void P.one(this._element,mt,()=>this.to(t));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ht:dt;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&P.on(this._element,gt,t=>this._keydown(t)),"hover"===this._config.pause&&(P.on(this._element,_t,()=>this.pause()),P.on(this._element,bt,()=>this._maybeEnableCycle())),this._config.touch&&ot.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of R.find(".carousel-item img",this._element))P.on(t,vt,t=>t.preventDefault());const t={leftCallback:()=>this._slide(this._directionToOrder(ut)),rightCallback:()=>this._slide(this._directionToOrder(ft)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),500+this._config.interval))}};this._swipeHelper=new ot(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=xt[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=R.findOne(Tt,this._indicatorsElement);e.classList.remove(Et),e.removeAttribute("aria-current");const i=R.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(Et),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ht,s=e||v(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>P.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(pt).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),u(s),i.classList.add(l),s.classList.add(l),this._queueCallback(()=>{s.classList.remove(l,c),s.classList.add(Et),i.classList.remove(Et,c,l),this._isSliding=!1,r(mt)},i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return R.findOne(Ot,this._element)}_getItems(){return R.find(Ct,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return m()?t===ut?dt:ht:t===ut?ht:dt}_orderToDirection(t){return m()?t===dt?ut:ft:t===dt?ft:ut}static jQueryInterface(t){return this.each(function(){const e=St.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)})}}P.on(document,wt,"[data-bs-slide], [data-bs-slide-to]",function(t){const e=R.getElementFromSelector(this);if(!e||!e.classList.contains(At))return;t.preventDefault();const i=St.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===H.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())}),P.on(window,yt,()=>{const t=R.find('[data-bs-ride="carousel"]');for(const e of t)St.getOrCreateInstance(e)}),g(St);const Dt=".bs.collapse",$t=`show${Dt}`,It=`shown${Dt}`,Nt=`hide${Dt}`,Pt=`hidden${Dt}`,jt=`click${Dt}.data-api`,Mt="show",Ft="collapse",Ht="collapsing",Wt=`:scope .${Ft} .${Ft}`,Bt='[data-bs-toggle="collapse"]',zt={parent:null,toggle:!0},Rt={parent:"(null|element)",toggle:"boolean"};class qt extends B{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=R.find(Bt);for(const t of i){const e=R.getSelectorFromElement(t),i=R.find(e).filter(t=>t===this._element);null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return zt}static get DefaultType(){return Rt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter(t=>t!==this._element).map(t=>qt.getOrCreateInstance(t,{toggle:!1}))),t.length&&t[0]._isTransitioning)return;if(P.trigger(this._element,$t).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Ft),this._element.classList.add(Ht),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(Ht),this._element.classList.add(Ft,Mt),this._element.style[e]="",P.trigger(this._element,It)},this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(P.trigger(this._element,Nt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,u(this._element),this._element.classList.add(Ht),this._element.classList.remove(Ft,Mt);for(const t of this._triggerArray){const e=R.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback(()=>{this._isTransitioning=!1,this._element.classList.remove(Ht),this._element.classList.add(Ft),P.trigger(this._element,Pt)},this._element,!0)}_isShown(t=this._element){return t.classList.contains(Mt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=a(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Bt);for(const e of t){const t=R.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=R.find(Wt,this._config.parent);return R.find(t,this._config.parent).filter(t=>!e.includes(t))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each(function(){const i=qt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}})}}P.on(document,jt,Bt,function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of R.getMultipleElementsFromSelector(this))qt.getOrCreateInstance(t,{toggle:!1}).toggle()}),g(qt);var Vt="top",Kt="bottom",Qt="right",Xt="left",Yt="auto",Ut=[Vt,Kt,Qt,Xt],Gt="start",Jt="end",Zt="clippingParents",te="viewport",ee="popper",ie="reference",ne=Ut.reduce(function(t,e){return t.concat([e+"-"+Gt,e+"-"+Jt])},[]),se=[].concat(Ut,[Yt]).reduce(function(t,e){return t.concat([e,e+"-"+Gt,e+"-"+Jt])},[]),oe="beforeRead",re="read",ae="afterRead",le="beforeMain",ce="main",he="afterMain",de="beforeWrite",ue="write",fe="afterWrite",pe=[oe,re,ae,le,ce,he,de,ue,fe];function me(t){return t?(t.nodeName||"").toLowerCase():null}function ge(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function _e(t){return t instanceof ge(t).Element||t instanceof Element}function be(t){return t instanceof ge(t).HTMLElement||t instanceof HTMLElement}function ve(t){return"undefined"!=typeof ShadowRoot&&(t instanceof ge(t).ShadowRoot||t instanceof ShadowRoot)}const ye={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach(function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];be(s)&&me(s)&&(Object.assign(s.style,i),Object.keys(n).forEach(function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)}))})},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach(function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce(function(t,e){return t[e]="",t},{});be(n)&&me(n)&&(Object.assign(n.style,o),Object.keys(s).forEach(function(t){n.removeAttribute(t)}))})}},requires:["computeStyles"]};function we(t){return t.split("-")[0]}var Ae=Math.max,Ee=Math.min,Te=Math.round;function Ce(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map(function(t){return t.brand+"/"+t.version}).join(" "):navigator.userAgent}function Oe(){return!/^((?!chrome|android).)*safari/i.test(Ce())}function xe(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&be(t)&&(s=t.offsetWidth>0&&Te(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Te(n.height)/t.offsetHeight||1);var r=(_e(t)?ge(t):window).visualViewport,a=!Oe()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function ke(t){var e=xe(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function Le(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&ve(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Se(t){return ge(t).getComputedStyle(t)}function De(t){return["table","td","th"].indexOf(me(t))>=0}function $e(t){return((_e(t)?t.ownerDocument:t.document)||window.document).documentElement}function Ie(t){return"html"===me(t)?t:t.assignedSlot||t.parentNode||(ve(t)?t.host:null)||$e(t)}function Ne(t){return be(t)&&"fixed"!==Se(t).position?t.offsetParent:null}function Pe(t){for(var e=ge(t),i=Ne(t);i&&De(i)&&"static"===Se(i).position;)i=Ne(i);return i&&("html"===me(i)||"body"===me(i)&&"static"===Se(i).position)?e:i||function(t){var e=/firefox/i.test(Ce());if(/Trident/i.test(Ce())&&be(t)&&"fixed"===Se(t).position)return null;var i=Ie(t);for(ve(i)&&(i=i.host);be(i)&&["html","body"].indexOf(me(i))<0;){var n=Se(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function je(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Me(t,e,i){return Ae(t,Ee(e,i))}function Fe(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function He(t,e){return e.reduce(function(e,i){return e[i]=t,e},{})}const We={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,n=t.name,s=t.options,o=i.elements.arrow,r=i.modifiersData.popperOffsets,a=we(i.placement),l=je(a),c=[Xt,Qt].indexOf(a)>=0?"height":"width";if(o&&r){var h=function(t,e){return Fe("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:He(t,Ut))}(s.padding,i),d=ke(o),u="y"===l?Vt:Xt,f="y"===l?Kt:Qt,p=i.rects.reference[c]+i.rects.reference[l]-r[l]-i.rects.popper[c],m=r[l]-i.rects.reference[l],g=Pe(o),_=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,b=p/2-m/2,v=h[u],y=_-d[c]-h[f],w=_/2-d[c]/2+b,A=Me(v,w,y),E=l;i.modifiersData[n]=((e={})[E]=A,e.centerOffset=A-w,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&Le(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Be(t){return t.split("-")[1]}var ze={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Re(t){var e,i=t.popper,n=t.popperRect,s=t.placement,o=t.variation,r=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,h=t.roundOffsets,d=t.isFixed,u=r.x,f=void 0===u?0:u,p=r.y,m=void 0===p?0:p,g="function"==typeof h?h({x:f,y:m}):{x:f,y:m};f=g.x,m=g.y;var _=r.hasOwnProperty("x"),b=r.hasOwnProperty("y"),v=Xt,y=Vt,w=window;if(c){var A=Pe(i),E="clientHeight",T="clientWidth";A===ge(i)&&"static"!==Se(A=$e(i)).position&&"absolute"===a&&(E="scrollHeight",T="scrollWidth"),(s===Vt||(s===Xt||s===Qt)&&o===Jt)&&(y=Kt,m-=(d&&A===w&&w.visualViewport?w.visualViewport.height:A[E])-n.height,m*=l?1:-1),s!==Xt&&(s!==Vt&&s!==Kt||o!==Jt)||(v=Qt,f-=(d&&A===w&&w.visualViewport?w.visualViewport.width:A[T])-n.width,f*=l?1:-1)}var C,O=Object.assign({position:a},c&&ze),x=!0===h?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:Te(i*s)/s||0,y:Te(n*s)/s||0}}({x:f,y:m},ge(i)):{x:f,y:m};return f=x.x,m=x.y,l?Object.assign({},O,((C={})[y]=b?"0":"",C[v]=_?"0":"",C.transform=(w.devicePixelRatio||1)<=1?"translate("+f+"px, "+m+"px)":"translate3d("+f+"px, "+m+"px, 0)",C)):Object.assign({},O,((e={})[y]=b?m+"px":"",e[v]=_?f+"px":"",e.transform="",e))}const qe={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:we(e.placement),variation:Be(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,Re(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,Re(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var Ve={passive:!0};const Ke={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=ge(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach(function(t){t.addEventListener("scroll",i.update,Ve)}),a&&l.addEventListener("resize",i.update,Ve),function(){o&&c.forEach(function(t){t.removeEventListener("scroll",i.update,Ve)}),a&&l.removeEventListener("resize",i.update,Ve)}},data:{}};var Qe={left:"right",right:"left",bottom:"top",top:"bottom"};function Xe(t){return t.replace(/left|right|bottom|top/g,function(t){return Qe[t]})}var Ye={start:"end",end:"start"};function Ue(t){return t.replace(/start|end/g,function(t){return Ye[t]})}function Ge(t){var e=ge(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Je(t){return xe($e(t)).left+Ge(t).scrollLeft}function Ze(t){var e=Se(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function ti(t){return["html","body","#document"].indexOf(me(t))>=0?t.ownerDocument.body:be(t)&&Ze(t)?t:ti(Ie(t))}function ei(t,e){var i;void 0===e&&(e=[]);var n=ti(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=ge(n),r=s?[o].concat(o.visualViewport||[],Ze(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ei(Ie(r)))}function ii(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function ni(t,e,i){return e===te?ii(function(t,e){var i=ge(t),n=$e(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=Oe();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+Je(t),y:l}}(t,i)):_e(e)?function(t,e){var i=xe(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):ii(function(t){var e,i=$e(t),n=Ge(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=Ae(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=Ae(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+Je(t),l=-n.scrollTop;return"rtl"===Se(s||i).direction&&(a+=Ae(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}($e(t)))}function si(t){var e,i=t.reference,n=t.element,s=t.placement,o=s?we(s):null,r=s?Be(s):null,a=i.x+i.width/2-n.width/2,l=i.y+i.height/2-n.height/2;switch(o){case Vt:e={x:a,y:i.y-n.height};break;case Kt:e={x:a,y:i.y+i.height};break;case Qt:e={x:i.x+i.width,y:l};break;case Xt:e={x:i.x-n.width,y:l};break;default:e={x:i.x,y:i.y}}var c=o?je(o):null;if(null!=c){var h="y"===c?"height":"width";switch(r){case Gt:e[c]=e[c]-(i[h]/2-n[h]/2);break;case Jt:e[c]=e[c]+(i[h]/2-n[h]/2)}}return e}function oi(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=void 0===n?t.placement:n,o=i.strategy,r=void 0===o?t.strategy:o,a=i.boundary,l=void 0===a?Zt:a,c=i.rootBoundary,h=void 0===c?te:c,d=i.elementContext,u=void 0===d?ee:d,f=i.altBoundary,p=void 0!==f&&f,m=i.padding,g=void 0===m?0:m,_=Fe("number"!=typeof g?g:He(g,Ut)),b=u===ee?ie:ee,v=t.rects.popper,y=t.elements[p?b:u],w=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ei(Ie(t)),i=["absolute","fixed"].indexOf(Se(t).position)>=0&&be(t)?Pe(t):t;return _e(i)?e.filter(function(t){return _e(t)&&Le(t,i)&&"body"!==me(t)}):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce(function(e,i){var s=ni(t,i,n);return e.top=Ae(s.top,e.top),e.right=Ee(s.right,e.right),e.bottom=Ee(s.bottom,e.bottom),e.left=Ae(s.left,e.left),e},ni(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(_e(y)?y:y.contextElement||$e(t.elements.popper),l,h,r),A=xe(t.elements.reference),E=si({reference:A,element:v,placement:s}),T=ii(Object.assign({},v,E)),C=u===ee?T:A,O={top:w.top-C.top+_.top,bottom:C.bottom-w.bottom+_.bottom,left:w.left-C.left+_.left,right:C.right-w.right+_.right},x=t.modifiersData.offset;if(u===ee&&x){var k=x[s];Object.keys(O).forEach(function(t){var e=[Qt,Kt].indexOf(t)>=0?1:-1,i=[Vt,Kt].indexOf(t)>=0?"y":"x";O[t]+=k[i]*e})}return O}function ri(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,l=i.allowedAutoPlacements,c=void 0===l?se:l,h=Be(n),d=h?a?ne:ne.filter(function(t){return Be(t)===h}):Ut,u=d.filter(function(t){return c.indexOf(t)>=0});0===u.length&&(u=d);var f=u.reduce(function(e,i){return e[i]=oi(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[we(i)],e},{});return Object.keys(f).sort(function(t,e){return f[t]-f[e]})}const ai={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0===r||r,l=i.fallbackPlacements,c=i.padding,h=i.boundary,d=i.rootBoundary,u=i.altBoundary,f=i.flipVariations,p=void 0===f||f,m=i.allowedAutoPlacements,g=e.options.placement,_=we(g),b=l||(_!==g&&p?function(t){if(we(t)===Yt)return[];var e=Xe(t);return[Ue(t),e,Ue(e)]}(g):[Xe(g)]),v=[g].concat(b).reduce(function(t,i){return t.concat(we(i)===Yt?ri(e,{placement:i,boundary:h,rootBoundary:d,padding:c,flipVariations:p,allowedAutoPlacements:m}):i)},[]),y=e.rects.reference,w=e.rects.popper,A=new Map,E=!0,T=v[0],C=0;C<v.length;C++){var O=v[C],x=we(O),k=Be(O)===Gt,L=[Vt,Kt].indexOf(x)>=0,S=L?"width":"height",D=oi(e,{placement:O,boundary:h,rootBoundary:d,altBoundary:u,padding:c}),$=L?k?Qt:Xt:k?Kt:Vt;y[S]>w[S]&&($=Xe($));var I=Xe($),N=[];if(o&&N.push(D[x]<=0),a&&N.push(D[$]<=0,D[I]<=0),N.every(function(t){return t})){T=O,E=!1;break}A.set(O,N)}if(E)for(var P=function(t){var e=v.find(function(e){var i=A.get(e);if(i)return i.slice(0,t).every(function(t){return t})});if(e)return T=e,"break"},j=p?3:1;j>0&&"break"!==P(j);j--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function li(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function ci(t){return[Vt,Qt,Kt,Xt].some(function(e){return t[e]>=0})}const hi={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=oi(e,{elementContext:"reference"}),a=oi(e,{altBoundary:!0}),l=li(r,n),c=li(a,s,o),h=ci(l),d=ci(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},di={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.offset,o=void 0===s?[0,0]:s,r=se.reduce(function(t,i){return t[i]=function(t,e,i){var n=we(t),s=[Xt,Vt].indexOf(n)>=0?-1:1,o="function"==typeof i?i(Object.assign({},e,{placement:t})):i,r=o[0],a=o[1];return r=r||0,a=(a||0)*s,[Xt,Qt].indexOf(n)>=0?{x:a,y:r}:{x:r,y:a}}(i,e.rects,o),t},{}),a=r[e.placement],l=a.x,c=a.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=r}},ui={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=si({reference:e.rects.reference,element:e.rects.popper,placement:e.placement})},data:{}},fi={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,n=t.name,s=i.mainAxis,o=void 0===s||s,r=i.altAxis,a=void 0!==r&&r,l=i.boundary,c=i.rootBoundary,h=i.altBoundary,d=i.padding,u=i.tether,f=void 0===u||u,p=i.tetherOffset,m=void 0===p?0:p,g=oi(e,{boundary:l,rootBoundary:c,padding:d,altBoundary:h}),_=we(e.placement),b=Be(e.placement),v=!b,y=je(_),w="x"===y?"y":"x",A=e.modifiersData.popperOffsets,E=e.rects.reference,T=e.rects.popper,C="function"==typeof m?m(Object.assign({},e.rects,{placement:e.placement})):m,O="number"==typeof C?{mainAxis:C,altAxis:C}:Object.assign({mainAxis:0,altAxis:0},C),x=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,k={x:0,y:0};if(A){if(o){var L,S="y"===y?Vt:Xt,D="y"===y?Kt:Qt,$="y"===y?"height":"width",I=A[y],N=I+g[S],P=I-g[D],j=f?-T[$]/2:0,M=b===Gt?E[$]:T[$],F=b===Gt?-T[$]:-E[$],H=e.elements.arrow,W=f&&H?ke(H):{width:0,height:0},B=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},z=B[S],R=B[D],q=Me(0,E[$],W[$]),V=v?E[$]/2-j-q-z-O.mainAxis:M-q-z-O.mainAxis,K=v?-E[$]/2+j+q+R+O.mainAxis:F+q+R+O.mainAxis,Q=e.elements.arrow&&Pe(e.elements.arrow),X=Q?"y"===y?Q.clientTop||0:Q.clientLeft||0:0,Y=null!=(L=null==x?void 0:x[y])?L:0,U=I+K-Y,G=Me(f?Ee(N,I+V-Y-X):N,I,f?Ae(P,U):P);A[y]=G,k[y]=G-I}if(a){var J,Z="x"===y?Vt:Xt,tt="x"===y?Kt:Qt,et=A[w],it="y"===w?"height":"width",nt=et+g[Z],st=et-g[tt],ot=-1!==[Vt,Xt].indexOf(_),rt=null!=(J=null==x?void 0:x[w])?J:0,at=ot?nt:et-E[it]-T[it]-rt+O.altAxis,lt=ot?et+E[it]+T[it]-rt-O.altAxis:st,ct=f&&ot?function(t,e,i){var n=Me(t,e,i);return n>i?i:n}(at,et,lt):Me(f?at:nt,et,f?lt:st);A[w]=ct,k[w]=ct-et}e.modifiersData[n]=k}},requiresIfExists:["offset"]};function pi(t,e,i){void 0===i&&(i=!1);var n,s,o=be(e),r=be(e)&&function(t){var e=t.getBoundingClientRect(),i=Te(e.width)/t.offsetWidth||1,n=Te(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=$e(e),l=xe(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==me(e)||Ze(a))&&(c=(n=e)!==ge(n)&&be(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:Ge(n)),be(e)?((h=xe(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=Je(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function mi(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach(function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}}),n.push(t)}return t.forEach(function(t){e.set(t.name,t)}),t.forEach(function(t){i.has(t.name)||s(t)}),n}var gi={placement:"bottom",modifiers:[],strategy:"absolute"};function _i(){for(var t=arguments.length,e=new Array(t),i=0;i<t;i++)e[i]=arguments[i];return!e.some(function(t){return!(t&&"function"==typeof t.getBoundingClientRect)})}function bi(t){void 0===t&&(t={});var e=t,i=e.defaultModifiers,n=void 0===i?[]:i,s=e.defaultOptions,o=void 0===s?gi:s;return function(t,e,i){void 0===i&&(i=o);var s,r,a={placement:"bottom",orderedModifiers:[],options:Object.assign({},gi,o),modifiersData:{},elements:{reference:t,popper:e},attributes:{},styles:{}},l=[],c=!1,h={state:a,setOptions:function(i){var s="function"==typeof i?i(a.options):i;d(),a.options=Object.assign({},o,a.options,s),a.scrollParents={reference:_e(t)?ei(t):t.contextElement?ei(t.contextElement):[],popper:ei(e)};var r,c,u=function(t){var e=mi(t);return pe.reduce(function(t,i){return t.concat(e.filter(function(t){return t.phase===i}))},[])}((r=[].concat(n,a.options.modifiers),c=r.reduce(function(t,e){var i=t[e.name];return t[e.name]=i?Object.assign({},i,e,{options:Object.assign({},i.options,e.options),data:Object.assign({},i.data,e.data)}):e,t},{}),Object.keys(c).map(function(t){return c[t]})));return a.orderedModifiers=u.filter(function(t){return t.enabled}),a.orderedModifiers.forEach(function(t){var e=t.name,i=t.options,n=void 0===i?{}:i,s=t.effect;if("function"==typeof s){var o=s({state:a,name:e,instance:h,options:n});l.push(o||function(){})}}),h.update()},forceUpdate:function(){if(!c){var t=a.elements,e=t.reference,i=t.popper;if(_i(e,i)){a.rects={reference:pi(e,Pe(i),"fixed"===a.options.strategy),popper:ke(i)},a.reset=!1,a.placement=a.options.placement,a.orderedModifiers.forEach(function(t){return a.modifiersData[t.name]=Object.assign({},t.data)});for(var n=0;n<a.orderedModifiers.length;n++)if(!0!==a.reset){var s=a.orderedModifiers[n],o=s.fn,r=s.options,l=void 0===r?{}:r,d=s.name;"function"==typeof o&&(a=o({state:a,options:l,name:d,instance:h})||a)}else a.reset=!1,n=-1}}},update:(s=function(){return new Promise(function(t){h.forceUpdate(),t(a)})},function(){return r||(r=new Promise(function(t){Promise.resolve().then(function(){r=void 0,t(s())})})),r}),destroy:function(){d(),c=!0}};if(!_i(t,e))return h;function d(){l.forEach(function(t){return t()}),l=[]}return h.setOptions(i).then(function(t){!c&&i.onFirstUpdate&&i.onFirstUpdate(t)}),h}}var vi=bi(),yi=bi({defaultModifiers:[Ke,ui,qe,ye]}),wi=bi({defaultModifiers:[Ke,ui,qe,ye,di,ai,fi,We,hi]});const Ai=Object.freeze(Object.defineProperty({__proto__:null,afterMain:he,afterRead:ae,afterWrite:fe,applyStyles:ye,arrow:We,auto:Yt,basePlacements:Ut,beforeMain:le,beforeRead:oe,beforeWrite:de,bottom:Kt,clippingParents:Zt,computeStyles:qe,createPopper:wi,createPopperBase:vi,createPopperLite:yi,detectOverflow:oi,end:Jt,eventListeners:Ke,flip:ai,hide:hi,left:Xt,main:ce,modifierPhases:pe,offset:di,placements:se,popper:ee,popperGenerator:bi,popperOffsets:ui,preventOverflow:fi,read:re,reference:ie,right:Qt,start:Gt,top:Vt,variationPlacements:ne,viewport:te,write:ue},Symbol.toStringTag,{value:"Module"})),Ei="dropdown",Ti=".bs.dropdown",Ci=".data-api",Oi="ArrowUp",xi="ArrowDown",ki=`hide${Ti}`,Li=`hidden${Ti}`,Si=`show${Ti}`,Di=`shown${Ti}`,$i=`click${Ti}${Ci}`,Ii=`keydown${Ti}${Ci}`,Ni=`keyup${Ti}${Ci}`,Pi="show",ji='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Mi=`${ji}.${Pi}`,Fi=".dropdown-menu",Hi=m()?"top-end":"top-start",Wi=m()?"top-start":"top-end",Bi=m()?"bottom-end":"bottom-start",zi=m()?"bottom-start":"bottom-end",Ri=m()?"left-start":"right-start",qi=m()?"right-start":"left-start",Vi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Ki={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Qi extends B{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=R.next(this._element,Fi)[0]||R.prev(this._element,Fi)[0]||R.findOne(Fi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Vi}static get DefaultType(){return Ki}static get NAME(){return Ei}toggle(){return this._isShown()?this.hide():this.show()}show(){if(c(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!P.trigger(this._element,Si,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))P.on(t,"mouseover",d);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Pi),this._element.classList.add(Pi),P.trigger(this._element,Di,t)}}hide(){if(c(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!P.trigger(this._element,ki,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))P.off(t,"mouseover",d);this._popper&&this._popper.destroy(),this._menu.classList.remove(Pi),this._element.classList.remove(Pi),this._element.setAttribute("aria-expanded","false"),H.removeDataAttribute(this._menu,"popper"),P.trigger(this._element,Li,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!r(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ei.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===Ai)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org/docs/v2/)");let t=this._element;"parent"===this._config.reference?t=this._parent:r(this._config.reference)?t=a(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const e=this._getPopperConfig();this._popper=wi(t,this._menu,e)}_isShown(){return this._menu.classList.contains(Pi)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Ri;if(t.classList.contains("dropstart"))return qi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?Wi:Hi:e?zi:Bi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map(t=>Number.parseInt(t,10)):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(H.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..._(this._config.popperConfig,[void 0,t])}}_selectMenuItem({key:t,target:e}){const i=R.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter(t=>l(t));i.length&&v(i,e,t===xi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each(function(){const e=Qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}})}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=R.find(Mi);for(const i of e){const e=Qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Oi,xi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(ji)?this:R.prev(this,ji)[0]||R.next(this,ji)[0]||R.findOne(ji,t.delegateTarget.parentNode),o=Qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}P.on(document,Ii,ji,Qi.dataApiKeydownHandler),P.on(document,Ii,Fi,Qi.dataApiKeydownHandler),P.on(document,$i,Qi.clearMenus),P.on(document,Ni,Qi.clearMenus),P.on(document,$i,ji,function(t){t.preventDefault(),Qi.getOrCreateInstance(this).toggle()}),g(Qi);const Xi="backdrop",Yi="show",Ui=`mousedown.bs.${Xi}`,Gi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ji={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Zi extends W{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Gi}static get DefaultType(){return Ji}static get NAME(){return Xi}show(t){if(!this._config.isVisible)return void _(t);this._append();const e=this._getElement();this._config.isAnimated&&u(e),e.classList.add(Yi),this._emulateAnimation(()=>{_(t)})}hide(t){this._config.isVisible?(this._getElement().classList.remove(Yi),this._emulateAnimation(()=>{this.dispose(),_(t)})):_(t)}dispose(){this._isAppended&&(P.off(this._element,Ui),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=a(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),P.on(t,Ui,()=>{_(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(t){b(t,this._getElement(),this._config.isAnimated)}}const tn=".bs.focustrap",en=`focusin${tn}`,nn=`keydown.tab${tn}`,sn="backward",on={autofocus:!0,trapElement:null},rn={autofocus:"boolean",trapElement:"element"};class an extends W{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return on}static get DefaultType(){return rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),P.off(document,tn),P.on(document,en,t=>this._handleFocusin(t)),P.on(document,nn,t=>this._handleKeydown(t)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,P.off(document,tn))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=R.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===sn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?sn:"forward")}}const ln=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",cn=".sticky-top",hn="padding-right",dn="margin-right";class un{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,hn,e=>e+t),this._setElementAttributes(ln,hn,e=>e+t),this._setElementAttributes(cn,dn,e=>e-t)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,hn),this._resetElementAttributes(ln,hn),this._resetElementAttributes(cn,dn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)})}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&H.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,t=>{const i=H.getDataAttribute(t,e);null!==i?(H.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)})}_applyManipulationCallback(t,e){if(r(t))e(t);else for(const i of R.find(t,this._element))e(i)}}const fn=".bs.modal",pn=`hide${fn}`,mn=`hidePrevented${fn}`,gn=`hidden${fn}`,_n=`show${fn}`,bn=`shown${fn}`,vn=`resize${fn}`,yn=`click.dismiss${fn}`,wn=`mousedown.dismiss${fn}`,An=`keydown.dismiss${fn}`,En=`click${fn}.data-api`,Tn="modal-open",Cn="show",On="modal-static",xn={backdrop:!0,focus:!0,keyboard:!0},kn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ln extends B{constructor(t,e){super(t,e),this._dialog=R.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new un,this._addEventListeners()}static get Default(){return xn}static get DefaultType(){return kn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||P.trigger(this._element,_n,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Tn),this._adjustDialog(),this._backdrop.show(()=>this._showElement(t)))}hide(){this._isShown&&!this._isTransitioning&&(P.trigger(this._element,pn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Cn),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated())))}dispose(){P.off(window,fn),P.off(this._dialog,fn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new an({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=R.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),u(this._element),this._element.classList.add(Cn),this._queueCallback(()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,P.trigger(this._element,bn,{relatedTarget:t})},this._dialog,this._isAnimated())}_addEventListeners(){P.on(this._element,An,t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())}),P.on(window,vn,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),P.on(this._element,wn,t=>{P.one(this._element,yn,e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Tn),this._resetAdjustments(),this._scrollBar.reset(),P.trigger(this._element,gn)})}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(P.trigger(this._element,mn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(On)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(On),this._queueCallback(()=>{this._element.classList.remove(On),this._queueCallback(()=>{this._element.style.overflowY=e},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=m()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=m()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each(function(){const i=Ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}})}}P.on(document,En,'[data-bs-toggle="modal"]',function(t){const e=R.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),P.one(e,_n,t=>{t.defaultPrevented||P.one(e,gn,()=>{l(this)&&this.focus()})});const i=R.findOne(".modal.show");i&&Ln.getInstance(i).hide(),Ln.getOrCreateInstance(e).toggle(this)}),q(Ln),g(Ln);const Sn=".bs.offcanvas",Dn=".data-api",$n=`load${Sn}${Dn}`,In="show",Nn="showing",Pn="hiding",jn=".offcanvas.show",Mn=`show${Sn}`,Fn=`shown${Sn}`,Hn=`hide${Sn}`,Wn=`hidePrevented${Sn}`,Bn=`hidden${Sn}`,zn=`resize${Sn}`,Rn=`click${Sn}${Dn}`,qn=`keydown.dismiss${Sn}`,Vn={backdrop:!0,keyboard:!0,scroll:!1},Kn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Qn extends B{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Vn}static get DefaultType(){return Kn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||P.trigger(this._element,Mn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new un).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Nn),this._queueCallback(()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(In),this._element.classList.remove(Nn),P.trigger(this._element,Fn,{relatedTarget:t})},this._element,!0))}hide(){this._isShown&&(P.trigger(this._element,Hn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Pn),this._backdrop.hide(),this._queueCallback(()=>{this._element.classList.remove(In,Pn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new un).reset(),P.trigger(this._element,Bn)},this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Zi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():P.trigger(this._element,Wn)}:null})}_initializeFocusTrap(){return new an({trapElement:this._element})}_addEventListeners(){P.on(this._element,qn,t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():P.trigger(this._element,Wn))})}static jQueryInterface(t){return this.each(function(){const e=Qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}})}}P.on(document,Rn,'[data-bs-toggle="offcanvas"]',function(t){const e=R.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this))return;P.one(e,Bn,()=>{l(this)&&this.focus()});const i=R.findOne(jn);i&&i!==e&&Qn.getInstance(i).hide(),Qn.getOrCreateInstance(e).toggle(this)}),P.on(window,$n,()=>{for(const t of R.find(jn))Qn.getOrCreateInstance(t).show()}),P.on(window,zn,()=>{for(const t of R.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Qn.getOrCreateInstance(t).hide()}),q(Qn),g(Qn);const Xn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Yn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Un=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Gn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Yn.has(i)||Boolean(Un.test(t.nodeValue)):e.filter(t=>t instanceof RegExp).some(t=>t.test(i))},Jn={allowList:Xn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"<div></div>"},Zn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ts={entry:"(string|element|function|null)",selector:"(string|element)"};class es extends W{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Jn}static get DefaultType(){return Zn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map(t=>this._resolvePossibleFunction(t)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},ts)}_setContent(t,e,i){const n=R.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?r(e)?this._putElementInTemplate(a(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Gn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return _(t,[void 0,this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const is=new Set(["sanitize","allowList","sanitizeFn"]),ns="fade",ss="show",os=".tooltip-inner",rs=".modal",as="hide.bs.modal",ls="hover",cs="focus",hs="click",ds={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},us={allowList:Xn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',title:"",trigger:"hover focus"},fs={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class ps extends B{constructor(t,e){if(void 0===Ai)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org/docs/v2/)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return us}static get DefaultType(){return fs}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),P.off(this._element.closest(rs),as,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=P.trigger(this._element,this.constructor.eventName("show")),e=(h(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),P.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))P.on(t,"mouseover",d);this._queueCallback(()=>{P.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1},this.tip,this._isAnimated())}hide(){if(this._isShown()&&!P.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))P.off(t,"mouseover",d);this._activeTrigger[hs]=!1,this._activeTrigger[cs]=!1,this._activeTrigger[ls]=!1,this._isHovered=null,this._queueCallback(()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),P.trigger(this._element,this.constructor.eventName("hidden")))},this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ns,ss),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ns),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new es({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[os]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ns)}_isShown(){return this.tip&&this.tip.classList.contains(ss)}_createPopper(t){const e=_(this._config.placement,[this,t,this._element]),i=ds[e.toUpperCase()];return wi(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map(t=>Number.parseInt(t,10)):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return _(t,[this._element,this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,..._(this._config.popperConfig,[void 0,e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)P.on(this._element,this.constructor.eventName("click"),this._config.selector,t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger[hs]=!(e._isShown()&&e._activeTrigger[hs]),e.toggle()});else if("manual"!==e){const t=e===ls?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ls?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");P.on(this._element,t,this._config.selector,t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?cs:ls]=!0,e._enter()}),P.on(this._element,i,this._config.selector,t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?cs:ls]=e._element.contains(t.relatedTarget),e._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},P.on(this._element.closest(rs),as,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=H.getDataAttributes(this._element);for(const t of Object.keys(e))is.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:a(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each(function(){const e=ps.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}})}}g(ps);const ms=".popover-header",gs=".popover-body",_s={...ps.Default,content:"",offset:[0,8],placement:"right",template:'<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>',trigger:"click"},bs={...ps.DefaultType,content:"(null|string|element|function)"};class vs extends ps{static get Default(){return _s}static get DefaultType(){return bs}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[ms]:this._getTitle(),[gs]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each(function(){const e=vs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}})}}g(vs);const ys=".bs.scrollspy",ws=`activate${ys}`,As=`click${ys}`,Es=`load${ys}.data-api`,Ts="active",Cs="[href]",Os=".nav-link",xs=`${Os}, .nav-item > ${Os}, .list-group-item`,ks={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},Ls={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ss extends B{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ks}static get DefaultType(){return Ls}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=a(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map(t=>Number.parseFloat(t))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(P.off(this._config.target,As),P.on(this._config.target,As,Cs,t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}}))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver(t=>this._observerCallback(t),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=R.find(Cs,this._config.target);for(const e of t){if(!e.hash||c(e))continue;const t=R.findOne(decodeURI(e.hash),this._element);l(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(Ts),this._activateParents(t),P.trigger(this._element,ws,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))R.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(Ts);else for(const e of R.parents(t,".nav, .list-group"))for(const t of R.prev(e,xs))t.classList.add(Ts)}_clearActiveClass(t){t.classList.remove(Ts);const e=R.find(`${Cs}.${Ts}`,t);for(const t of e)t.classList.remove(Ts)}static jQueryInterface(t){return this.each(function(){const e=Ss.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}})}}P.on(window,Es,()=>{for(const t of R.find('[data-bs-spy="scroll"]'))Ss.getOrCreateInstance(t)}),g(Ss);const Ds=".bs.tab",$s=`hide${Ds}`,Is=`hidden${Ds}`,Ns=`show${Ds}`,Ps=`shown${Ds}`,js=`click${Ds}`,Ms=`keydown${Ds}`,Fs=`load${Ds}`,Hs="ArrowLeft",Ws="ArrowRight",Bs="ArrowUp",zs="ArrowDown",Rs="Home",qs="End",Vs="active",Ks="fade",Qs="show",Xs=".dropdown-toggle",Ys=`:not(${Xs})`,Us='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Gs=`.nav-link${Ys}, .list-group-item${Ys}, [role="tab"]${Ys}, ${Us}`,Js=`.${Vs}[data-bs-toggle="tab"], .${Vs}[data-bs-toggle="pill"], .${Vs}[data-bs-toggle="list"]`;class Zs extends B{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),P.on(this._element,Ms,t=>this._keydown(t)))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?P.trigger(e,$s,{relatedTarget:t}):null;P.trigger(t,Ns,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Vs),this._activate(R.getElementFromSelector(t)),this._queueCallback(()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),P.trigger(t,Ps,{relatedTarget:e})):t.classList.add(Qs)},t,t.classList.contains(Ks)))}_deactivate(t,e){t&&(t.classList.remove(Vs),t.blur(),this._deactivate(R.getElementFromSelector(t)),this._queueCallback(()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),P.trigger(t,Is,{relatedTarget:e})):t.classList.remove(Qs)},t,t.classList.contains(Ks)))}_keydown(t){if(![Hs,Ws,Bs,zs,Rs,qs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter(t=>!c(t));let i;if([Rs,qs].includes(t.key))i=e[t.key===Rs?0:e.length-1];else{const n=[Ws,zs].includes(t.key);i=v(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Zs.getOrCreateInstance(i).show())}_getChildren(){return R.find(Gs,this._parent)}_getActiveElem(){return this._getChildren().find(t=>this._elemIsActive(t))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=R.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=R.findOne(t,i);s&&s.classList.toggle(n,e)};n(Xs,Vs),n(".dropdown-menu",Qs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Vs)}_getInnerElement(t){return t.matches(Gs)?t:R.findOne(Gs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each(function(){const e=Zs.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}})}}P.on(document,js,Us,function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),c(this)||Zs.getOrCreateInstance(this).show()}),P.on(window,Fs,()=>{for(const t of R.find(Js))Zs.getOrCreateInstance(t)}),g(Zs);const to=".bs.toast",eo=`mouseover${to}`,io=`mouseout${to}`,no=`focusin${to}`,so=`focusout${to}`,oo=`hide${to}`,ro=`hidden${to}`,ao=`show${to}`,lo=`shown${to}`,co="hide",ho="show",uo="showing",fo={animation:"boolean",autohide:"boolean",delay:"number"},po={animation:!0,autohide:!0,delay:5e3};class mo extends B{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return po}static get DefaultType(){return fo}static get NAME(){return"toast"}show(){P.trigger(this._element,ao).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(co),u(this._element),this._element.classList.add(ho,uo),this._queueCallback(()=>{this._element.classList.remove(uo),P.trigger(this._element,lo),this._maybeScheduleHide()},this._element,this._config.animation))}hide(){this.isShown()&&(P.trigger(this._element,oo).defaultPrevented||(this._element.classList.add(uo),this._queueCallback(()=>{this._element.classList.add(co),this._element.classList.remove(uo,ho),P.trigger(this._element,ro)},this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(ho),super.dispose()}isShown(){return this._element.classList.contains(ho)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout(()=>{this.hide()},this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){P.on(this._element,eo,t=>this._onInteraction(t,!0)),P.on(this._element,io,t=>this._onInteraction(t,!1)),P.on(this._element,no,t=>this._onInteraction(t,!0)),P.on(this._element,so,t=>this._onInteraction(t,!1))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each(function(){const e=mo.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}})}}return q(mo),g(mo),{Alert:X,Button:U,Carousel:St,Collapse:qt,Dropdown:Qi,Modal:Ln,Offcanvas:Qn,Popover:vs,ScrollSpy:Ss,Tab:Zs,Toast:mo,Tooltip:ps}}); -//# sourceMappingURL=bootstrap.bundle.min.js.map \ No newline at end of file diff --git a/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js.map b/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js.map deleted file mode 100644 index 3e678d4c..00000000 --- a/extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"names":["elementMap","Map","Data","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete","TRANSITION_END","parseSelector","selector","window","CSS","escape","replace","match","id","toType","object","Object","prototype","toString","call","toLowerCase","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","getElement","length","document","querySelector","isVisible","getClientRects","elementIsVisible","getComputedStyle","getPropertyValue","closedDetails","closest","summary","parentNode","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","noop","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","isRTL","dir","defineJQueryPlugin","plugin","callback","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","readyState","addEventListener","push","execute","possibleCallback","args","defaultValue","executeAfterTransition","transitionElement","waitForTransition","emulatedDuration","transitionDuration","transitionDelay","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","getTransitionDurationFromElement","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","listLength","index","indexOf","Math","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","nativeEvents","Set","makeEventUid","uid","getElementEvents","findHandler","events","callable","delegationSelector","values","find","event","normalizeParameters","originalTypeEvent","delegationFunction","isDelegated","typeEvent","getTypeEvent","addHandler","oneOff","wrapFunction","relatedTarget","delegateTarget","this","handlers","previousFunction","domElements","querySelectorAll","domElement","hydrateObj","EventHandler","off","type","apply","bootstrapDelegationHandler","bootstrapHandler","removeHandler","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","handlerKey","entries","includes","on","one","inNamespace","isNamespace","startsWith","elementEvent","slice","keyHandlers","trigger","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","evt","cancelable","preventDefault","obj","meta","value","_unused","defineProperty","configurable","normalizeData","JSON","parse","decodeURIComponent","normalizeDataKey","chr","Manipulator","setDataAttribute","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","dataset","filter","pureKey","charAt","getDataAttribute","Config","Default","DefaultType","Error","_getConfig","config","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","jsonConfig","constructor","configTypes","property","expectedTypes","valueType","RegExp","test","TypeError","toUpperCase","BaseComponent","super","_element","_config","DATA_KEY","dispose","EVENT_KEY","propertyName","getOwnPropertyNames","_queueCallback","isAnimated","getInstance","getOrCreateInstance","VERSION","eventName","getSelector","hrefAttribute","trim","map","sel","join","SelectorEngine","concat","Element","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","el","getSelectorFromElement","getElementFromSelector","getMultipleElementsFromSelector","enableDismissTrigger","component","method","clickEvent","tagName","EVENT_CLOSE","EVENT_CLOSED","Alert","close","_destroyElement","each","data","undefined","SELECTOR_DATA_TOGGLE","Button","toggle","button","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","endCallback","leftCallback","rightCallback","Swipe","isSupported","_deltaX","_supportPointerEvents","PointerEvent","_initEvents","_start","_eventIsPointerPenTouch","clientX","touches","_end","_handleSwipe","_move","absDeltaX","abs","direction","add","pointerType","navigator","maxTouchPoints","DATA_API_KEY","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_DRAG_START","EVENT_LOAD_DATA_API","EVENT_CLICK_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_ACTIVE","SELECTOR_ACTIVE","SELECTOR_ITEM","SELECTOR_ACTIVE_ITEM","KEY_TO_DIRECTION","ARROW_LEFT_KEY$1","ARROW_RIGHT_KEY$1","interval","keyboard","pause","ride","touch","wrap","Carousel","_interval","_activeElement","_isSliding","touchTimeout","_swipeHelper","_indicatorsElement","_addEventListeners","cycle","_slide","nextWhenVisible","hidden","_clearInterval","_updateInterval","setInterval","_maybeEnableCycle","to","items","_getItems","activeIndex","_getItemIndex","_getActive","order","defaultInterval","_keydown","_addTouchEventListeners","img","swipeConfig","_directionToOrder","endCallBack","clearTimeout","_setActiveIndicatorElement","activeIndicator","newActiveIndicator","elementInterval","parseInt","isNext","nextElement","nextElementIndex","triggerEvent","_orderToDirection","isCycling","directionalClassName","orderClassName","completeCallBack","_isAnimated","clearInterval","carousel","slideIndex","carousels","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_DEEPER_CHILDREN","parent","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElement","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activeChildren","_getFirstLevelChildren","activeInstance","dimension","_getDimension","style","scrollSize","complete","getBoundingClientRect","selected","triggerArray","isOpen","top","bottom","right","left","auto","basePlacements","start","end","clippingParents","viewport","popper","reference","variationPlacements","reduce","acc","placement","placements","beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite","modifierPhases","getNodeName","nodeName","getWindow","node","ownerDocument","defaultView","isHTMLElement","HTMLElement","isShadowRoot","applyStyles$1","enabled","phase","_ref","state","elements","forEach","styles","assign","effect","_ref2","initialStyles","position","options","strategy","margin","arrow","hasOwnProperty","attribute","requires","getBasePlacement","round","getUAString","uaData","userAgentData","brands","isArray","item","brand","version","userAgent","isLayoutViewport","includeScale","isFixedStrategy","clientRect","scaleX","scaleY","offsetWidth","width","height","visualViewport","addVisualOffsets","x","offsetLeft","y","offsetTop","getLayoutRect","rootNode","isSameNode","host","isTableElement","getDocumentElement","getParentNode","assignedSlot","getTrueOffsetParent","offsetParent","getOffsetParent","isFirefox","currentNode","css","transform","perspective","contain","willChange","getContainingBlock","getMainAxisFromPlacement","within","mathMax","mathMin","mergePaddingObject","paddingObject","expandToHashMap","hashMap","arrow$1","_state$modifiersData$","arrowElement","popperOffsets","modifiersData","basePlacement","axis","len","padding","rects","toPaddingObject","arrowRect","minProp","maxProp","endDiff","startDiff","arrowOffsetParent","clientSize","clientHeight","clientWidth","centerToReference","center","offset","axisProp","centerOffset","_options$element","requiresIfExists","getVariation","unsetSides","mapToStyles","_Object$assign2","popperRect","variation","offsets","gpuAcceleration","adaptive","roundOffsets","isFixed","_offsets$x","_offsets$y","_ref3","hasX","hasY","sideX","sideY","win","heightProp","widthProp","_Object$assign","commonStyles","_ref4","dpr","devicePixelRatio","roundOffsetsByDPR","computeStyles$1","_ref5","_options$gpuAccelerat","_options$adaptive","_options$roundOffsets","passive","eventListeners","_options$scroll","scroll","_options$resize","resize","scrollParents","scrollParent","update","hash","getOppositePlacement","matched","getOppositeVariationPlacement","getWindowScroll","scrollLeft","pageXOffset","scrollTop","pageYOffset","getWindowScrollBarX","isScrollParent","_getComputedStyle","overflow","overflowX","overflowY","getScrollParent","listScrollParents","_element$ownerDocumen","isBody","updatedList","rectToClientRect","rect","getClientRectFromMixedType","clippingParent","html","layoutViewport","getViewportRect","clientTop","clientLeft","getInnerBoundingClientRect","winScroll","scrollWidth","scrollHeight","getDocumentRect","computeOffsets","commonX","commonY","mainAxis","detectOverflow","_options","_options$placement","_options$strategy","_options$boundary","boundary","_options$rootBoundary","rootBoundary","_options$elementConte","elementContext","_options$altBoundary","altBoundary","_options$padding","altContext","clippingClientRect","mainClippingParents","clipperElement","getClippingParents","firstClippingParent","clippingRect","accRect","getClippingRect","contextElement","referenceClientRect","popperClientRect","elementClientRect","overflowOffsets","offsetData","multiply","computeAutoPlacement","flipVariations","_options$allowedAutoP","allowedAutoPlacements","allPlacements","allowedPlacements","overflows","sort","a","b","flip$1","_skip","_options$mainAxis","checkMainAxis","_options$altAxis","altAxis","checkAltAxis","specifiedFallbackPlacements","fallbackPlacements","_options$flipVariatio","preferredPlacement","oppositePlacement","getExpandedFallbackPlacements","referenceRect","checksMap","makeFallbackChecks","firstFittingPlacement","i","_basePlacement","isStartVariation","isVertical","mainVariationSide","altVariationSide","checks","every","check","_loop","_i","fittingPlacement","reset","getSideOffsets","preventedOffsets","isAnySideFullyClipped","some","side","hide$1","preventOverflow","referenceOverflow","popperAltOverflow","referenceClippingOffsets","popperEscapeOffsets","isReferenceHidden","hasPopperEscaped","offset$1","_options$offset","invertDistance","skidding","distance","distanceAndSkiddingToXY","_data$state$placement","popperOffsets$1","preventOverflow$1","_options$tether","tether","_options$tetherOffset","tetherOffset","isBasePlacement","tetherOffsetValue","normalizedTetherOffsetValue","offsetModifierState","_offsetModifierState$","mainSide","altSide","additive","minLen","maxLen","arrowPaddingObject","arrowPaddingMin","arrowPaddingMax","arrowLen","minOffset","maxOffset","clientOffset","offsetModifierValue","tetherMax","preventedOffset","_offsetModifierState$2","_mainSide","_altSide","_offset","_len","_min","_max","isOriginSide","_offsetModifierValue","_tetherMin","_tetherMax","_preventedOffset","v","withinMaxClamp","getCompositeRect","elementOrVirtualElement","isOffsetParentAnElement","offsetParentIsScaled","isElementScaled","modifiers","visited","result","modifier","dep","depModifier","DEFAULT_OPTIONS","areValidElements","arguments","_key","popperGenerator","generatorOptions","_generatorOptions","_generatorOptions$def","defaultModifiers","_generatorOptions$def2","defaultOptions","pending","orderedModifiers","effectCleanupFns","isDestroyed","setOptions","setOptionsAction","cleanupModifierEffects","merged","orderModifiers","current","existing","m","_ref$options","cleanupFn","forceUpdate","_state$elements","_state$orderedModifie","_state$orderedModifie2","Promise","resolve","then","destroy","onFirstUpdate","createPopper","computeStyles","applyStyles","flip","ARROW_UP_KEY","ARROW_DOWN_KEY","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","SELECTOR_DATA_TOGGLE_SHOWN","SELECTOR_MENU","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","autoClose","display","popperConfig","Dropdown","_popper","_parent","_menu","_inNavbar","_detectNavbar","_createPopper","focus","_completeHide","Popper","referenceElement","_getPopperConfig","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","_selectMenuItem","clearMenus","openToggles","context","composedPath","isMenuTarget","dataApiKeydownHandler","isInput","isEscapeEvent","isUpOrDownEvent","getToggleButton","stopPropagation","EVENT_MOUSEDOWN","className","clickCallback","rootElement","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_NAV_BACKWARD","autofocus","trapElement","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","shiftKey","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","getWidth","documentWidth","innerWidth","_disableOverFlow","_setElementAttributes","calculatedValue","_resetElementAttributes","isOverflowing","_saveInitialAttribute","styleProperty","scrollbarWidth","_applyManipulationCallback","setProperty","actualValue","removeProperty","callBack","EVENT_HIDE_PREVENTED","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","CLASS_NAME_OPEN","CLASS_NAME_STATIC","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_scrollBar","_adjustDialog","_showElement","_hideModal","handleUpdate","modalBody","transitionComplete","_triggerBackdropTransition","event2","_resetAdjustments","isModalOverflowing","initialOverflowY","isBodyOverflowing","paddingLeft","paddingRight","showEvent","alreadyOpen","CLASS_NAME_SHOWING","CLASS_NAME_HIDING","OPEN_SELECTOR","Offcanvas","blur","completeCallback","DefaultAllowlist","area","br","col","code","dd","div","dl","dt","em","hr","h1","h2","h3","h4","h5","h6","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","uriAttributes","SAFE_URL_PATTERN","allowedAttribute","allowedAttributeList","attributeName","nodeValue","attributeRegex","regex","allowList","content","extraClass","sanitize","sanitizeFn","template","DefaultContentType","entry","TemplateFactory","getContent","_resolvePossibleFunction","hasContent","changeContent","_checkContent","toHtml","templateWrapper","innerHTML","_maybeSanitize","text","_setContent","arg","templateElement","_putElementInTemplate","textContent","unsafeHtml","sanitizeFunction","createdDocument","DOMParser","parseFromString","elementName","attributeList","allowedAttributes","sanitizeHtml","DISALLOWED_ATTRIBUTES","CLASS_NAME_FADE","SELECTOR_TOOLTIP_INNER","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","TRIGGER_CLICK","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","animation","container","customClass","delay","title","Tooltip","_isEnabled","_timeout","_isHovered","_activeTrigger","_templateFactory","_newContent","tip","_setListeners","_fixTitle","enable","disable","toggleEnabled","_leave","_enter","_hideModalHandler","_disposePopper","_isWithContent","isInTheDom","_getTipElement","_isWithActiveTrigger","_getTitle","_createTipElement","_getContentForTemplate","_getTemplateFactory","tipId","prefix","floor","random","getElementById","getUID","setContent","_initializeOnDelegatedTarget","_getDelegateConfig","attachment","triggers","eventIn","eventOut","_setTimeout","timeout","dataAttributes","dataAttribute","SELECTOR_TITLE","SELECTOR_CONTENT","Popover","_getContent","EVENT_ACTIVATE","EVENT_CLICK","SELECTOR_TARGET_LINKS","SELECTOR_NAV_LINKS","SELECTOR_LINK_ITEMS","rootMargin","smoothScroll","threshold","ScrollSpy","_targetLinks","_observableSections","_rootElement","_activeTarget","_observer","_previousScrollData","visibleEntryTop","parentScrollTop","refresh","_initializeTargetsAndObservables","_maybeEnableSmoothScroll","disconnect","_getNewObserver","section","observe","observableSection","scrollTo","behavior","IntersectionObserver","_observerCallback","targetElement","_process","userScrollsDown","isIntersecting","_clearActiveClass","entryIsLowerThanPrevious","targetLinks","anchor","decodeURI","_activateParents","listGroup","activeNodes","spy","HOME_KEY","END_KEY","SELECTOR_DROPDOWN_TOGGLE","NOT_SELECTOR_DROPDOWN_TOGGLE","SELECTOR_INNER_ELEM","SELECTOR_DATA_TOGGLE_ACTIVE","Tab","_setInitialAttributes","_getChildren","innerElem","_elemIsActive","active","_getActiveElem","hideEvent","_deactivate","_activate","relatedElem","_toggleDropDown","nextActiveElement","preventScroll","_setAttributeIfNotExists","_setInitialAttributesOnChild","_getInnerElement","isActive","outerElem","_getOuterElement","_setInitialAttributesOnTargetPanel","open","EVENT_MOUSEOVER","EVENT_MOUSEOUT","EVENT_FOCUSOUT","CLASS_NAME_HIDE","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","isShown","_onInteraction","isInteracting"],"sources":["../../js/src/dom/data.js","../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/manipulator.js","../../js/src/util/config.js","../../js/src/base-component.js","../../js/src/dom/selector-engine.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/util/swipe.js","../../js/src/carousel.js","../../js/src/collapse.js","../../node_modules/@popperjs/core/lib/enums.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js","../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","../../node_modules/@popperjs/core/lib/modifiers/applyStyles.js","../../node_modules/@popperjs/core/lib/utils/getBasePlacement.js","../../node_modules/@popperjs/core/lib/utils/math.js","../../node_modules/@popperjs/core/lib/utils/userAgent.js","../../node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","../../node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","../../node_modules/@popperjs/core/lib/dom-utils/contains.js","../../node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","../../node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","../../node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","../../node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","../../node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","../../node_modules/@popperjs/core/lib/utils/within.js","../../node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","../../node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","../../node_modules/@popperjs/core/lib/utils/expandToHashMap.js","../../node_modules/@popperjs/core/lib/modifiers/arrow.js","../../node_modules/@popperjs/core/lib/utils/getVariation.js","../../node_modules/@popperjs/core/lib/modifiers/computeStyles.js","../../node_modules/@popperjs/core/lib/modifiers/eventListeners.js","../../node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","../../node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","../../node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","../../node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","../../node_modules/@popperjs/core/lib/utils/rectToClientRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","../../node_modules/@popperjs/core/lib/utils/computeOffsets.js","../../node_modules/@popperjs/core/lib/utils/detectOverflow.js","../../node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","../../node_modules/@popperjs/core/lib/modifiers/flip.js","../../node_modules/@popperjs/core/lib/modifiers/hide.js","../../node_modules/@popperjs/core/lib/modifiers/offset.js","../../node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","../../node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","../../node_modules/@popperjs/core/lib/utils/getAltAxis.js","../../node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","../../node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","../../node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","../../node_modules/@popperjs/core/lib/utils/orderModifiers.js","../../node_modules/@popperjs/core/lib/createPopper.js","../../node_modules/@popperjs/core/lib/utils/debounce.js","../../node_modules/@popperjs/core/lib/utils/mergeByName.js","../../node_modules/@popperjs/core/lib/popper-lite.js","../../node_modules/@popperjs/core/lib/popper.js","../../js/src/dropdown.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/util/scrollbar.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.js","../../js/src/util/template-factory.js","../../js/src/tooltip.js","../../js/src/popover.js","../../js/src/scrollspy.js","../../js/src/tab.js","../../js/src/toast.js","../../js/index.umd.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1_000_000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`)\n }\n\n return selector\n}\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`\n }\n\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false\n }\n\n if (typeof object.jquery !== 'undefined') {\n object = object[0]\n }\n\n return typeof object.nodeType !== 'undefined'\n}\n\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object\n }\n\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object))\n }\n\n return null\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])')\n\n if (!closedDetails) {\n return elementIsVisible\n }\n\n if (closedDetails !== element) {\n const summary = element.closest('summary')\n if (summary && summary.parentNode !== closedDetails) {\n return false\n }\n\n if (summary === null) {\n return false\n }\n }\n\n return elementIsVisible\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight // eslint-disable-line no-unused-expressions\n}\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback()\n }\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]\n }\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n defineJQueryPlugin,\n execute,\n executeAfterTransition,\n findShadowRoot,\n getElement,\n getjQuery,\n getNextActiveElement,\n getTransitionDurationFromElement,\n getUID,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n onDOMContentLoaded,\n parseSelector,\n reflow,\n triggerTransitionEnd,\n toType\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index.js'\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\n\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getElementEvents(element) {\n const uid = makeEventUid(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, { delegateTarget: element })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue\n }\n\n hydrateObj(event, { delegateTarget: target })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events)\n .find(event => event.callable === callable && event.delegationSelector === delegationSelector)\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string'\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : (handler || delegationFunction)\n let typeEvent = getTypeEvent(originalTypeEvent)\n\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent\n }\n\n return [isDelegated, callable, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n callable = wrapFunction(callable)\n }\n\n const events = getElementEvents(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)\n\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff\n\n return\n }\n\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = isDelegated ?\n bootstrapDelegationHandler(element, handler, callable) :\n bootstrapHandler(element, callable)\n\n fn.delegationSelector = isDelegated ? handler : null\n fn.callable = callable\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, isDelegated)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false)\n },\n\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getElementEvents(element)\n const storeElementEvent = events[typeEvent] || {}\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return\n }\n\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)\n return\n }\n\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n }\n }\n\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n\n let jQueryEvent = null\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value\n } catch {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value\n }\n })\n }\n }\n\n return obj\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport { isElement, toType } from './index.js'\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {}\n }\n\n static get DefaultType() {\n return {}\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n return config\n }\n\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n }\n }\n\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property]\n const valueType = isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n }\n }\n}\n\nexport default Config\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data.js'\nimport EventHandler from './dom/event-handler.js'\nimport Config from './util/config.js'\nimport { executeAfterTransition, getElement } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.8'\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super()\n\n element = getElement(element)\n if (!element) {\n return\n }\n\n this._element = element\n this._config = this._getConfig(config)\n\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null\n }\n }\n\n // Private\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`\n }\n}\n\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible, parseSelector } from '../util/index.js'\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`\n }\n\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null\n }\n\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null\n}\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n let ancestor = element.parentNode.closest(selector)\n\n while (ancestor) {\n parents.push(ancestor)\n ancestor = ancestor.parentNode.closest(selector)\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n },\n\n getSelectorFromElement(element) {\n const selector = getSelector(element)\n\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null\n }\n\n return null\n },\n\n getElementFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.findOne(selector) : null\n },\n\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.find(selector) : []\n }\n}\n\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isDisabled } from './index.js'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'swipe'\nconst EVENT_KEY = '.bs.swipe'\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n}\n\nconst DefaultType = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n}\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super()\n this._element = element\n\n if (!element || !Swipe.isSupported()) {\n return\n }\n\n this._config = this._getConfig(config)\n this._deltaX = 0\n this._supportPointerEvents = Boolean(window.PointerEvent)\n this._initEvents()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY)\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX\n\n return\n }\n\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX\n }\n }\n\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX\n }\n\n this._handleSwipe()\n execute(this._config.endCallback)\n }\n\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this._deltaX\n }\n\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX)\n\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltaX / this._deltaX\n\n this._deltaX = 0\n\n if (!direction) {\n return\n }\n\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)\n }\n\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))\n }\n }\n\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n }\n}\n\nexport default Swipe\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getNextActiveElement,\n isRTL,\n isVisible,\n reflow,\n triggerTransitionEnd\n} from './util/index.js'\nimport Swipe from './util/swipe.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)', // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._interval = null\n this._activeElement = null\n this._isSliding = false\n this.touchTimeout = null\n this._swipeHelper = null\n\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._addEventListeners()\n\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element)\n }\n\n this._clearInterval()\n }\n\n cycle() {\n this._clearInterval()\n this._updateInterval()\n\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)\n }\n\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle())\n return\n }\n\n this.cycle()\n }\n\n to(index) {\n const items = this._getItems()\n if (index > items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n const activeIndex = this._getItemIndex(this._getActive())\n if (activeIndex === index) {\n return\n }\n\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV\n\n this._slide(order, items[index])\n }\n\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose()\n }\n\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())\n EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())\n }\n\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())\n }\n\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n }\n\n this._swipeHelper = new Swipe(this._element, swipeConfig)\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(this._directionToOrder(direction))\n }\n }\n\n _getItemIndex(element) {\n return this._getItems().indexOf(element)\n }\n\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return\n }\n\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement)\n\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)\n newActiveIndicator.setAttribute('aria-current', 'true')\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || this._getActive()\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n this._config.interval = elementInterval || this._config.defaultInterval\n }\n\n _slide(order, element = null) {\n if (this._isSliding) {\n return\n }\n\n const activeElement = this._getActive()\n const isNext = order === ORDER_NEXT\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)\n\n if (nextElement === activeElement) {\n return\n }\n\n const nextElementIndex = this._getItemIndex(nextElement)\n\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n })\n }\n\n const slideEvent = triggerEvent(EVENT_SLIDE)\n\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return\n }\n\n const isCycling = Boolean(this._interval)\n this.pause()\n\n this._isSliding = true\n\n this._setActiveIndicatorElement(nextElementIndex)\n this._activeElement = nextElement\n\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n triggerEvent(EVENT_SLID)\n }\n\n this._queueCallback(completeCallBack, activeElement, this._isAnimated())\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE)\n }\n\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n }\n\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element)\n }\n\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n }\n\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config)\n\n if (typeof config === 'number') {\n data.to(config)\n return\n }\n\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n event.preventDefault()\n\n const carousel = Carousel.getOrCreateInstance(target)\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n carousel.to(slideIndex)\n carousel._maybeEnableCycle()\n return\n }\n\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next()\n carousel._maybeEnableCycle()\n return\n }\n\n carousel.prev()\n carousel._maybeEnableCycle()\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getElement,\n reflow\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\nconst Default = {\n parent: null,\n toggle: true\n}\n\nconst DefaultType = {\n parent: '(null|element)',\n toggle: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isTransitioning = false\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElement => foundElement === this._element)\n\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let activeChildren = []\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)\n .filter(element => element !== this._element)\n .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))\n }\n\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n for (const activeInstance of activeChildren) {\n activeInstance.hide()\n }\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger)\n\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n // Private\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)\n\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n }\n }\n\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)\n element.setAttribute('aria-expanded', isOpen)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If <html> has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on <html>\n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `<html>` and `<body>` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n execute,\n getElement,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center'\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)'\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR = '.navbar'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\nconst PLACEMENT_TOPCENTER = 'top'\nconst PLACEMENT_BOTTOMCENTER = 'bottom'\n\nconst Default = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n}\n\nconst DefaultType = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n}\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._popper = null\n this._parent = this._element.parentNode // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.findOne(SELECTOR_MENU, this._parent)\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._createPopper()\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = super._getConfig(config)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = this._parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n }\n\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getPlacement() {\n const parentDropdown = this._parent\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {\n return\n }\n\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)\n\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle)\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n const relatedTarget = { relatedTarget: context._element }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName)\n const isEscapeEvent = event.key === ESCAPE_KEY\n const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)\n\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return\n }\n\n if (isInput && !isEscapeEvent) {\n return\n }\n\n event.preventDefault()\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?\n this :\n (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))\n\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (isUpOrDownEvent) {\n event.stopPropagation()\n instance.show()\n instance._selectMenuItem(event)\n return\n }\n\n if (instance._isShown()) { // else is escape and we check if it is shown\n event.stopPropagation()\n instance.hide()\n getToggleButton.focus()\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport {\n execute, executeAfterTransition, getElement, reflow\n} from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nconst Default = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n}\n\nconst DefaultType = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n}\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n const element = this._getElement()\n if (this._config.isAnimated) {\n reflow(element)\n }\n\n element.classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n const element = this._getElement()\n this._config.rootElement.append(element)\n\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin, isRTL, isVisible, reflow\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin,\n isDisabled,\n isVisible\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\nconst CLASS_NAME_HIDING = 'hiding'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate()\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.add(CLASS_NAME_HIDING)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n return\n }\n\n this.hide()\n }\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop)\n\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n })\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show()\n }\n})\n\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide()\n }\n }\n})\n\nenableDismissTrigger(Offcanvas)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n// js-docs-end allow-list\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n .some(regex => regex.test(attributeName))\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\nimport { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'\nimport { execute, getElement, isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'TemplateFactory'\n\nconst Default = {\n allowList: DefaultAllowlist,\n content: {}, // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '<div></div>'\n}\n\nconst DefaultType = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n}\n\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n}\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content)\n .map(config => this._resolvePossibleFunction(config))\n .filter(Boolean)\n }\n\n hasContent() {\n return this.getContent().length > 0\n }\n\n changeContent(content) {\n this._checkContent(content)\n this._config.content = { ...this._config.content, ...content }\n return this\n }\n\n toHtml() {\n const templateWrapper = document.createElement('div')\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template)\n\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector)\n }\n\n const template = templateWrapper.children[0]\n const extraClass = this._resolvePossibleFunction(this._config.extraClass)\n\n if (extraClass) {\n template.classList.add(...extraClass.split(' '))\n }\n\n return template\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config)\n this._checkContent(config.content)\n }\n\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({ selector, entry: content }, DefaultContentType)\n }\n }\n\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!templateElement) {\n return\n }\n\n content = this._resolvePossibleFunction(content)\n\n if (!content) {\n templateElement.remove()\n return\n }\n\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement)\n return\n }\n\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content)\n return\n }\n\n templateElement.textContent = content\n }\n\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [undefined, this])\n }\n\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = ''\n templateElement.append(element)\n return\n }\n\n templateElement.textContent = element.textContent\n }\n}\n\nexport default TemplateFactory\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport {\n defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop\n} from './util/index.js'\nimport { DefaultAllowlist } from './util/sanitizer.js'\nimport TemplateFactory from './util/template-factory.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\nconst EVENT_HIDE = 'hide'\nconst EVENT_HIDDEN = 'hidden'\nconst EVENT_SHOW = 'show'\nconst EVENT_SHOWN = 'shown'\nconst EVENT_INSERTED = 'inserted'\nconst EVENT_CLICK = 'click'\nconst EVENT_FOCUSIN = 'focusin'\nconst EVENT_FOCUSOUT = 'focusout'\nconst EVENT_MOUSEENTER = 'mouseenter'\nconst EVENT_MOUSELEAVE = 'mouseleave'\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '<div class=\"tooltip\" role=\"tooltip\">' +\n '<div class=\"tooltip-arrow\"></div>' +\n '<div class=\"tooltip-inner\"></div>' +\n '</div>',\n title: '',\n trigger: 'hover focus'\n}\n\nconst DefaultType = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n}\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org/docs/v2/)')\n }\n\n super(element, config)\n\n // Private\n this._isEnabled = true\n this._timeout = 0\n this._isHovered = null\n this._activeTrigger = {}\n this._popper = null\n this._templateFactory = null\n this._newContent = null\n\n // Protected\n this.tip = null\n\n this._setListeners()\n\n if (!this._config.selector) {\n this._fixTitle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle() {\n if (!this._isEnabled) {\n return\n }\n\n if (this._isShown()) {\n this._leave()\n return\n }\n\n this._enter()\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this._isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper()\n\n const tip = this._getTipElement()\n\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'))\n\n const { container } = this._config\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))\n }\n\n this._popper = this._createPopper(tip)\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))\n\n if (this._isHovered === false) {\n this._leave()\n }\n\n this._isHovered = false\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n hide() {\n if (!this._isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const tip = this._getTipElement()\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n this._isHovered = null // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (!this._isHovered) {\n this._disposePopper()\n }\n\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n update() {\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle())\n }\n\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())\n }\n\n return this.tip\n }\n\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml()\n\n // TODO: remove this check in v6\n if (!tip) {\n return null\n }\n\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`)\n\n const tipId = getUID(this.constructor.NAME).toString()\n\n tip.setAttribute('id', tipId)\n\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n return tip\n }\n\n setContent(content) {\n this._newContent = content\n if (this._isShown()) {\n this._disposePopper()\n this.show()\n }\n }\n\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content)\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n })\n }\n\n return this._templateFactory\n }\n\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n }\n }\n\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _isAnimated() {\n return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))\n }\n\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)\n }\n\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element])\n const attachment = AttachmentMap[placement.toUpperCase()]\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element, this._element])\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement)\n }\n }\n ]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])\n }\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[TRIGGER_CLICK] = !(context._isShown() && context._activeTrigger[TRIGGER_CLICK])\n context.toggle()\n })\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSEENTER) :\n this.constructor.eventName(EVENT_FOCUSIN)\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSELEAVE) :\n this.constructor.eventName(EVENT_FOCUSOUT)\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true\n context._enter()\n })\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =\n context._element.contains(event.relatedTarget)\n\n context._leave()\n })\n }\n }\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n\n if (!title) {\n return\n }\n\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title')\n }\n\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true\n return\n }\n\n this._isHovered = true\n\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show()\n }\n }, this._config.delay.show)\n }\n\n _leave() {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n this._isHovered = false\n\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide()\n }\n }, this._config.delay.hide)\n }\n\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout)\n this._timeout = setTimeout(handler, timeout)\n }\n\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true)\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute]\n }\n }\n\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value\n }\n }\n\n config.selector = false\n config.trigger = 'manual'\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n\n if (this.tip) {\n this.tip.remove()\n this.tip = null\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Tooltip from './tooltip.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'popover'\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\nconst Default = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '<div class=\"popover\" role=\"tooltip\">' +\n '<div class=\"popover-arrow\"></div>' +\n '<h3 class=\"popover-header\"></h3>' +\n '<div class=\"popover-body\"></div>' +\n '</div>',\n trigger: 'click'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n}\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent()\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n }\n }\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin, getElement, isDisabled, isVisible\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_CLICK = `click${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_TARGET_LINKS = '[href]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst Default = {\n offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n}\n\nconst DefaultType = {\n offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n}\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map()\n this._observableSections = new Map()\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element\n this._activeTarget = null\n this._observer = null\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n }\n this.refresh() // initialize\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables()\n this._maybeEnableSmoothScroll()\n\n if (this._observer) {\n this._observer.disconnect()\n } else {\n this._observer = this._getNewObserver()\n }\n\n for (const section of this._observableSections.values()) {\n this._observer.observe(section)\n }\n }\n\n dispose() {\n this._observer.disconnect()\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin\n\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))\n }\n\n return config\n }\n\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK)\n\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash)\n if (observableSection) {\n event.preventDefault()\n const root = this._rootElement || window\n const height = observableSection.offsetTop - this._element.offsetTop\n if (root.scrollTo) {\n root.scrollTo({ top: height, behavior: 'smooth' })\n return\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height\n }\n })\n }\n\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n }\n\n return new IntersectionObserver(entries => this._observerCallback(entries), options)\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop\n this._process(targetElement(entry))\n }\n\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop\n this._previousScrollData.parentScrollTop = parentScrollTop\n\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null\n this._clearActiveClass(targetElement(entry))\n\n continue\n }\n\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry)\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return\n }\n\n continue\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry)\n }\n }\n }\n\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map()\n this._observableSections = new Map()\n\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)\n\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue\n }\n\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor)\n this._observableSections.set(anchor.hash, observableSection)\n }\n }\n }\n\n _process(target) {\n if (this._activeTarget === target) {\n return\n }\n\n this._clearActiveClass(this._config.target)\n this._activeTarget = target\n target.classList.add(CLASS_NAME_ACTIVE)\n this._activateParents(target)\n\n EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })\n }\n\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n return\n }\n\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n item.classList.add(CLASS_NAME_ACTIVE)\n }\n }\n }\n\n _clearActiveClass(parent) {\n parent.classList.remove(CLASS_NAME_ACTIVE)\n\n const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)\n for (const node of activeNodes) {\n node.classList.remove(CLASS_NAME_ACTIVE)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n ScrollSpy.getOrCreateInstance(spy)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tab.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}`\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst HOME_KEY = 'Home'\nconst END_KEY = 'End'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_DROPDOWN = 'dropdown'\n\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu'\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`\n\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]'\nconst SELECTOR_OUTER = '.nav-item, .list-group-item'\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]' // TODO: could only be `tab` in v6\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`\n\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`\n\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n constructor(element) {\n super(element)\n this._parent = this._element.closest(SELECTOR_TAB_PANEL)\n\n if (!this._parent) {\n return\n // TODO: should throw exception in v6\n // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n }\n\n // Set up initial aria attributes\n this._setInitialAttributes(this._parent, this._getChildren())\n\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n show() { // Shows this elem and deactivate the active sibling if exists\n const innerElem = this._element\n if (this._elemIsActive(innerElem)) {\n return\n }\n\n // Search for active tab on same parent to deactivate it\n const active = this._getActiveElem()\n\n const hideEvent = active ?\n EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :\n null\n\n const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })\n\n if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {\n return\n }\n\n this._deactivate(active, innerElem)\n this._activate(innerElem, active)\n }\n\n // Private\n _activate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n\n this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.add(CLASS_NAME_SHOW)\n return\n }\n\n element.removeAttribute('tabindex')\n element.setAttribute('aria-selected', true)\n this._toggleDropDown(element, true)\n EventHandler.trigger(element, EVENT_SHOWN, {\n relatedTarget: relatedElem\n })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _deactivate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.remove(CLASS_NAME_ACTIVE)\n element.blur()\n\n this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.remove(CLASS_NAME_SHOW)\n return\n }\n\n element.setAttribute('aria-selected', false)\n element.setAttribute('tabindex', '-1')\n this._toggleDropDown(element, false)\n EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _keydown(event) {\n if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {\n return\n }\n\n event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n event.preventDefault()\n\n const children = this._getChildren().filter(element => !isDisabled(element))\n let nextActiveElement\n\n if ([HOME_KEY, END_KEY].includes(event.key)) {\n nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]\n } else {\n const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)\n nextActiveElement = getNextActiveElement(children, event.target, isNext, true)\n }\n\n if (nextActiveElement) {\n nextActiveElement.focus({ preventScroll: true })\n Tab.getOrCreateInstance(nextActiveElement).show()\n }\n }\n\n _getChildren() { // collection of inner elements\n return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)\n }\n\n _getActiveElem() {\n return this._getChildren().find(child => this._elemIsActive(child)) || null\n }\n\n _setInitialAttributes(parent, children) {\n this._setAttributeIfNotExists(parent, 'role', 'tablist')\n\n for (const child of children) {\n this._setInitialAttributesOnChild(child)\n }\n }\n\n _setInitialAttributesOnChild(child) {\n child = this._getInnerElement(child)\n const isActive = this._elemIsActive(child)\n const outerElem = this._getOuterElement(child)\n child.setAttribute('aria-selected', isActive)\n\n if (outerElem !== child) {\n this._setAttributeIfNotExists(outerElem, 'role', 'presentation')\n }\n\n if (!isActive) {\n child.setAttribute('tabindex', '-1')\n }\n\n this._setAttributeIfNotExists(child, 'role', 'tab')\n\n // set attributes to the related panel too\n this._setInitialAttributesOnTargetPanel(child)\n }\n\n _setInitialAttributesOnTargetPanel(child) {\n const target = SelectorEngine.getElementFromSelector(child)\n\n if (!target) {\n return\n }\n\n this._setAttributeIfNotExists(target, 'role', 'tabpanel')\n\n if (child.id) {\n this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)\n }\n }\n\n _toggleDropDown(element, open) {\n const outerElem = this._getOuterElement(element)\n if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n return\n }\n\n const toggle = (selector, className) => {\n const element = SelectorEngine.findOne(selector, outerElem)\n if (element) {\n element.classList.toggle(className, open)\n }\n }\n\n toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)\n toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)\n outerElem.setAttribute('aria-expanded', open)\n }\n\n _setAttributeIfNotExists(element, attribute, value) {\n if (!element.hasAttribute(attribute)) {\n element.setAttribute(attribute, value)\n }\n }\n\n _elemIsActive(elem) {\n return elem.classList.contains(CLASS_NAME_ACTIVE)\n }\n\n // Try to get the inner element (usually the .nav-link)\n _getInnerElement(elem) {\n return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)\n }\n\n // Try to get the outer element (usually the .nav-item)\n _getOuterElement(elem) {\n return elem.closest(SELECTOR_OUTER) || elem\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n Tab.getOrCreateInstance(this).show()\n})\n\n/**\n * Initialize on focus\n */\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n Tab.getOrCreateInstance(element)\n }\n})\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap toast.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin, reflow } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n hide() {\n if (!this.isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this.isShown()) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n isShown() {\n return this._element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout': {\n this._hasMouseInteraction = isInteracting\n break\n }\n\n case 'focusin':\n case 'focusout': {\n this._hasKeyboardInteraction = isInteracting\n break\n }\n\n default: {\n break\n }\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Toast)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap index.umd.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Alert from './src/alert.js'\nimport Button from './src/button.js'\nimport Carousel from './src/carousel.js'\nimport Collapse from './src/collapse.js'\nimport Dropdown from './src/dropdown.js'\nimport Modal from './src/modal.js'\nimport Offcanvas from './src/offcanvas.js'\nimport Popover from './src/popover.js'\nimport ScrollSpy from './src/scrollspy.js'\nimport Tab from './src/tab.js'\nimport Toast from './src/toast.js'\nimport Tooltip from './src/tooltip.js'\n\nexport default {\n Alert,\n Button,\n Carousel,\n Collapse,\n Dropdown,\n Modal,\n Offcanvas,\n Popover,\n ScrollSpy,\n Tab,\n Toast,\n Tooltip\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/extensions/pagetop-bootsier/static/js/bootstrap.js b/extensions/pagetop-bootsier/static/js/bootstrap.js deleted file mode 100644 index e1f6e58f..00000000 --- a/extensions/pagetop-bootsier/static/js/bootstrap.js +++ /dev/null @@ -1,4494 +0,0 @@ -/*! - * Bootstrap v5.3.8 (https://getbootstrap.com/) - * Copyright 2011-2025 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) : - typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.Popper)); -})(this, (function (Popper) { 'use strict'; - - function _interopNamespaceDefault(e) { - const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } }); - if (e) { - for (const k in e) { - if (k !== 'default') { - const d = Object.getOwnPropertyDescriptor(e, k); - Object.defineProperty(n, k, d.get ? d : { - enumerable: true, - get: () => e[k] - }); - } - } - } - n.default = e; - return Object.freeze(n); - } - - const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper); - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/data.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - /** - * Constants - */ - - const elementMap = new Map(); - const Data = { - set(element, key, instance) { - if (!elementMap.has(element)) { - elementMap.set(element, new Map()); - } - const instanceMap = elementMap.get(element); - - // make it clear we only want one instance per element - // can be removed later when multiple key/instances are fine to be used - if (!instanceMap.has(key) && instanceMap.size !== 0) { - // eslint-disable-next-line no-console - console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); - return; - } - instanceMap.set(key, instance); - }, - get(element, key) { - if (elementMap.has(element)) { - return elementMap.get(element).get(key) || null; - } - return null; - }, - remove(element, key) { - if (!elementMap.has(element)) { - return; - } - const instanceMap = elementMap.get(element); - instanceMap.delete(key); - - // free up element references if there are no instances left for an element - if (instanceMap.size === 0) { - elementMap.delete(element); - } - } - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/index.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const MAX_UID = 1000000; - const MILLISECONDS_MULTIPLIER = 1000; - const TRANSITION_END = 'transitionend'; - - /** - * Properly escape IDs selectors to handle weird IDs - * @param {string} selector - * @returns {string} - */ - const parseSelector = selector => { - if (selector && window.CSS && window.CSS.escape) { - // document.querySelector needs escaping to handle IDs (html5+) containing for instance / - selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); - } - return selector; - }; - - // Shout-out Angus Croll (https://goo.gl/pxwQGp) - const toType = object => { - if (object === null || object === undefined) { - return `${object}`; - } - return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); - }; - - /** - * Public Util API - */ - - const getUID = prefix => { - do { - prefix += Math.floor(Math.random() * MAX_UID); - } while (document.getElementById(prefix)); - return prefix; - }; - const getTransitionDurationFromElement = element => { - if (!element) { - return 0; - } - - // Get transition-duration of the element - let { - transitionDuration, - transitionDelay - } = window.getComputedStyle(element); - const floatTransitionDuration = Number.parseFloat(transitionDuration); - const floatTransitionDelay = Number.parseFloat(transitionDelay); - - // Return 0 if element or transition duration is not found - if (!floatTransitionDuration && !floatTransitionDelay) { - return 0; - } - - // If multiple durations are defined, take the first - transitionDuration = transitionDuration.split(',')[0]; - transitionDelay = transitionDelay.split(',')[0]; - return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; - }; - const triggerTransitionEnd = element => { - element.dispatchEvent(new Event(TRANSITION_END)); - }; - const isElement = object => { - if (!object || typeof object !== 'object') { - return false; - } - if (typeof object.jquery !== 'undefined') { - object = object[0]; - } - return typeof object.nodeType !== 'undefined'; - }; - const getElement = object => { - // it's a jQuery object or a node element - if (isElement(object)) { - return object.jquery ? object[0] : object; - } - if (typeof object === 'string' && object.length > 0) { - return document.querySelector(parseSelector(object)); - } - return null; - }; - const isVisible = element => { - if (!isElement(element) || element.getClientRects().length === 0) { - return false; - } - const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; - // Handle `details` element as its content may falsie appear visible when it is closed - const closedDetails = element.closest('details:not([open])'); - if (!closedDetails) { - return elementIsVisible; - } - if (closedDetails !== element) { - const summary = element.closest('summary'); - if (summary && summary.parentNode !== closedDetails) { - return false; - } - if (summary === null) { - return false; - } - } - return elementIsVisible; - }; - const isDisabled = element => { - if (!element || element.nodeType !== Node.ELEMENT_NODE) { - return true; - } - if (element.classList.contains('disabled')) { - return true; - } - if (typeof element.disabled !== 'undefined') { - return element.disabled; - } - return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; - }; - const findShadowRoot = element => { - if (!document.documentElement.attachShadow) { - return null; - } - - // Can find the shadow root otherwise it'll return the document - if (typeof element.getRootNode === 'function') { - const root = element.getRootNode(); - return root instanceof ShadowRoot ? root : null; - } - if (element instanceof ShadowRoot) { - return element; - } - - // when we don't find a shadow root - if (!element.parentNode) { - return null; - } - return findShadowRoot(element.parentNode); - }; - const noop = () => {}; - - /** - * Trick to restart an element's animation - * - * @param {HTMLElement} element - * @return void - * - * @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation - */ - const reflow = element => { - element.offsetHeight; // eslint-disable-line no-unused-expressions - }; - const getjQuery = () => { - if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { - return window.jQuery; - } - return null; - }; - const DOMContentLoadedCallbacks = []; - const onDOMContentLoaded = callback => { - if (document.readyState === 'loading') { - // add listener on the first call when the document is in loading state - if (!DOMContentLoadedCallbacks.length) { - document.addEventListener('DOMContentLoaded', () => { - for (const callback of DOMContentLoadedCallbacks) { - callback(); - } - }); - } - DOMContentLoadedCallbacks.push(callback); - } else { - callback(); - } - }; - const isRTL = () => document.documentElement.dir === 'rtl'; - const defineJQueryPlugin = plugin => { - onDOMContentLoaded(() => { - const $ = getjQuery(); - /* istanbul ignore if */ - if ($) { - const name = plugin.NAME; - const JQUERY_NO_CONFLICT = $.fn[name]; - $.fn[name] = plugin.jQueryInterface; - $.fn[name].Constructor = plugin; - $.fn[name].noConflict = () => { - $.fn[name] = JQUERY_NO_CONFLICT; - return plugin.jQueryInterface; - }; - } - }); - }; - const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { - return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue; - }; - const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { - if (!waitForTransition) { - execute(callback); - return; - } - const durationPadding = 5; - const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; - let called = false; - const handler = ({ - target - }) => { - if (target !== transitionElement) { - return; - } - called = true; - transitionElement.removeEventListener(TRANSITION_END, handler); - execute(callback); - }; - transitionElement.addEventListener(TRANSITION_END, handler); - setTimeout(() => { - if (!called) { - triggerTransitionEnd(transitionElement); - } - }, emulatedDuration); - }; - - /** - * Return the previous/next element of a list. - * - * @param {array} list The list of elements - * @param activeElement The active element - * @param shouldGetNext Choose to get next or previous element - * @param isCycleAllowed - * @return {Element|elem} The proper element - */ - const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { - const listLength = list.length; - let index = list.indexOf(activeElement); - - // if the element does not exist in the list return an element - // depending on the direction and if cycle is allowed - if (index === -1) { - return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; - } - index += shouldGetNext ? 1 : -1; - if (isCycleAllowed) { - index = (index + listLength) % listLength; - } - return list[Math.max(0, Math.min(index, listLength - 1))]; - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/event-handler.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const namespaceRegex = /[^.]*(?=\..*)\.|.*/; - const stripNameRegex = /\..*/; - const stripUidRegex = /::\d+$/; - const eventRegistry = {}; // Events storage - let uidEvent = 1; - const customEvents = { - mouseenter: 'mouseover', - mouseleave: 'mouseout' - }; - const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); - - /** - * Private methods - */ - - function makeEventUid(element, uid) { - return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; - } - function getElementEvents(element) { - const uid = makeEventUid(element); - element.uidEvent = uid; - eventRegistry[uid] = eventRegistry[uid] || {}; - return eventRegistry[uid]; - } - function bootstrapHandler(element, fn) { - return function handler(event) { - hydrateObj(event, { - delegateTarget: element - }); - if (handler.oneOff) { - EventHandler.off(element, event.type, fn); - } - return fn.apply(element, [event]); - }; - } - function bootstrapDelegationHandler(element, selector, fn) { - return function handler(event) { - const domElements = element.querySelectorAll(selector); - for (let { - target - } = event; target && target !== this; target = target.parentNode) { - for (const domElement of domElements) { - if (domElement !== target) { - continue; - } - hydrateObj(event, { - delegateTarget: target - }); - if (handler.oneOff) { - EventHandler.off(element, event.type, selector, fn); - } - return fn.apply(target, [event]); - } - } - }; - } - function findHandler(events, callable, delegationSelector = null) { - return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); - } - function normalizeParameters(originalTypeEvent, handler, delegationFunction) { - const isDelegated = typeof handler === 'string'; - // TODO: tooltip passes `false` instead of selector, so we need to check - const callable = isDelegated ? delegationFunction : handler || delegationFunction; - let typeEvent = getTypeEvent(originalTypeEvent); - if (!nativeEvents.has(typeEvent)) { - typeEvent = originalTypeEvent; - } - return [isDelegated, callable, typeEvent]; - } - function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { - if (typeof originalTypeEvent !== 'string' || !element) { - return; - } - let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); - - // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position - // this prevents the handler from being dispatched the same way as mouseover or mouseout does - if (originalTypeEvent in customEvents) { - const wrapFunction = fn => { - return function (event) { - if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { - return fn.call(this, event); - } - }; - }; - callable = wrapFunction(callable); - } - const events = getElementEvents(element); - const handlers = events[typeEvent] || (events[typeEvent] = {}); - const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); - if (previousFunction) { - previousFunction.oneOff = previousFunction.oneOff && oneOff; - return; - } - const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); - const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); - fn.delegationSelector = isDelegated ? handler : null; - fn.callable = callable; - fn.oneOff = oneOff; - fn.uidEvent = uid; - handlers[uid] = fn; - element.addEventListener(typeEvent, fn, isDelegated); - } - function removeHandler(element, events, typeEvent, handler, delegationSelector) { - const fn = findHandler(events[typeEvent], handler, delegationSelector); - if (!fn) { - return; - } - element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); - delete events[typeEvent][fn.uidEvent]; - } - function removeNamespacedHandlers(element, events, typeEvent, namespace) { - const storeElementEvent = events[typeEvent] || {}; - for (const [handlerKey, event] of Object.entries(storeElementEvent)) { - if (handlerKey.includes(namespace)) { - removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); - } - } - } - function getTypeEvent(event) { - // allow to get the native events from namespaced events ('click.bs.button' --> 'click') - event = event.replace(stripNameRegex, ''); - return customEvents[event] || event; - } - const EventHandler = { - on(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, false); - }, - one(element, event, handler, delegationFunction) { - addHandler(element, event, handler, delegationFunction, true); - }, - off(element, originalTypeEvent, handler, delegationFunction) { - if (typeof originalTypeEvent !== 'string' || !element) { - return; - } - const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); - const inNamespace = typeEvent !== originalTypeEvent; - const events = getElementEvents(element); - const storeElementEvent = events[typeEvent] || {}; - const isNamespace = originalTypeEvent.startsWith('.'); - if (typeof callable !== 'undefined') { - // Simplest case: handler is passed, remove that listener ONLY. - if (!Object.keys(storeElementEvent).length) { - return; - } - removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); - return; - } - if (isNamespace) { - for (const elementEvent of Object.keys(events)) { - removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); - } - } - for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { - const handlerKey = keyHandlers.replace(stripUidRegex, ''); - if (!inNamespace || originalTypeEvent.includes(handlerKey)) { - removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); - } - } - }, - trigger(element, event, args) { - if (typeof event !== 'string' || !element) { - return null; - } - const $ = getjQuery(); - const typeEvent = getTypeEvent(event); - const inNamespace = event !== typeEvent; - let jQueryEvent = null; - let bubbles = true; - let nativeDispatch = true; - let defaultPrevented = false; - if (inNamespace && $) { - jQueryEvent = $.Event(event, args); - $(element).trigger(jQueryEvent); - bubbles = !jQueryEvent.isPropagationStopped(); - nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); - defaultPrevented = jQueryEvent.isDefaultPrevented(); - } - const evt = hydrateObj(new Event(event, { - bubbles, - cancelable: true - }), args); - if (defaultPrevented) { - evt.preventDefault(); - } - if (nativeDispatch) { - element.dispatchEvent(evt); - } - if (evt.defaultPrevented && jQueryEvent) { - jQueryEvent.preventDefault(); - } - return evt; - } - }; - function hydrateObj(obj, meta = {}) { - for (const [key, value] of Object.entries(meta)) { - try { - obj[key] = value; - } catch (_unused) { - Object.defineProperty(obj, key, { - configurable: true, - get() { - return value; - } - }); - } - } - return obj; - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/manipulator.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - function normalizeData(value) { - if (value === 'true') { - return true; - } - if (value === 'false') { - return false; - } - if (value === Number(value).toString()) { - return Number(value); - } - if (value === '' || value === 'null') { - return null; - } - if (typeof value !== 'string') { - return value; - } - try { - return JSON.parse(decodeURIComponent(value)); - } catch (_unused) { - return value; - } - } - function normalizeDataKey(key) { - return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); - } - const Manipulator = { - setDataAttribute(element, key, value) { - element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); - }, - removeDataAttribute(element, key) { - element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); - }, - getDataAttributes(element) { - if (!element) { - return {}; - } - const attributes = {}; - const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); - for (const key of bsKeys) { - let pureKey = key.replace(/^bs/, ''); - pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1); - attributes[pureKey] = normalizeData(element.dataset[key]); - } - return attributes; - }, - getDataAttribute(element, key) { - return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); - } - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/config.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Class definition - */ - - class Config { - // Getters - static get Default() { - return {}; - } - static get DefaultType() { - return {}; - } - static get NAME() { - throw new Error('You have to implement the static method "NAME", for each component!'); - } - _getConfig(config) { - config = this._mergeConfigObj(config); - config = this._configAfterMerge(config); - this._typeCheckConfig(config); - return config; - } - _configAfterMerge(config) { - return config; - } - _mergeConfigObj(config, element) { - const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse - - return { - ...this.constructor.Default, - ...(typeof jsonConfig === 'object' ? jsonConfig : {}), - ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}), - ...(typeof config === 'object' ? config : {}) - }; - } - _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { - for (const [property, expectedTypes] of Object.entries(configTypes)) { - const value = config[property]; - const valueType = isElement(value) ? 'element' : toType(value); - if (!new RegExp(expectedTypes).test(valueType)) { - throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); - } - } - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap base-component.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const VERSION = '5.3.8'; - - /** - * Class definition - */ - - class BaseComponent extends Config { - constructor(element, config) { - super(); - element = getElement(element); - if (!element) { - return; - } - this._element = element; - this._config = this._getConfig(config); - Data.set(this._element, this.constructor.DATA_KEY, this); - } - - // Public - dispose() { - Data.remove(this._element, this.constructor.DATA_KEY); - EventHandler.off(this._element, this.constructor.EVENT_KEY); - for (const propertyName of Object.getOwnPropertyNames(this)) { - this[propertyName] = null; - } - } - - // Private - _queueCallback(callback, element, isAnimated = true) { - executeAfterTransition(callback, element, isAnimated); - } - _getConfig(config) { - config = this._mergeConfigObj(config, this._element); - config = this._configAfterMerge(config); - this._typeCheckConfig(config); - return config; - } - - // Static - static getInstance(element) { - return Data.get(getElement(element), this.DATA_KEY); - } - static getOrCreateInstance(element, config = {}) { - return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); - } - static get VERSION() { - return VERSION; - } - static get DATA_KEY() { - return `bs.${this.NAME}`; - } - static get EVENT_KEY() { - return `.${this.DATA_KEY}`; - } - static eventName(name) { - return `${name}${this.EVENT_KEY}`; - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap dom/selector-engine.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const getSelector = element => { - let selector = element.getAttribute('data-bs-target'); - if (!selector || selector === '#') { - let hrefAttribute = element.getAttribute('href'); - - // The only valid content that could double as a selector are IDs or classes, - // so everything starting with `#` or `.`. If a "real" URL is used as the selector, - // `document.querySelector` will rightfully complain it is invalid. - // See https://github.com/twbs/bootstrap/issues/32273 - if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { - return null; - } - - // Just in case some CMS puts out a full URL with the anchor appended - if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { - hrefAttribute = `#${hrefAttribute.split('#')[1]}`; - } - selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; - } - return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null; - }; - const SelectorEngine = { - find(selector, element = document.documentElement) { - return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); - }, - findOne(selector, element = document.documentElement) { - return Element.prototype.querySelector.call(element, selector); - }, - children(element, selector) { - return [].concat(...element.children).filter(child => child.matches(selector)); - }, - parents(element, selector) { - const parents = []; - let ancestor = element.parentNode.closest(selector); - while (ancestor) { - parents.push(ancestor); - ancestor = ancestor.parentNode.closest(selector); - } - return parents; - }, - prev(element, selector) { - let previous = element.previousElementSibling; - while (previous) { - if (previous.matches(selector)) { - return [previous]; - } - previous = previous.previousElementSibling; - } - return []; - }, - // TODO: this is now unused; remove later along with prev() - next(element, selector) { - let next = element.nextElementSibling; - while (next) { - if (next.matches(selector)) { - return [next]; - } - next = next.nextElementSibling; - } - return []; - }, - focusableChildren(element) { - const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); - return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); - }, - getSelectorFromElement(element) { - const selector = getSelector(element); - if (selector) { - return SelectorEngine.findOne(selector) ? selector : null; - } - return null; - }, - getElementFromSelector(element) { - const selector = getSelector(element); - return selector ? SelectorEngine.findOne(selector) : null; - }, - getMultipleElementsFromSelector(element) { - const selector = getSelector(element); - return selector ? SelectorEngine.find(selector) : []; - } - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/component-functions.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const enableDismissTrigger = (component, method = 'hide') => { - const clickEvent = `click.dismiss${component.EVENT_KEY}`; - const name = component.NAME; - EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - if (isDisabled(this)) { - return; - } - const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); - const instance = component.getOrCreateInstance(target); - - // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method - instance[method](); - }); - }; - - /** - * -------------------------------------------------------------------------- - * Bootstrap alert.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$f = 'alert'; - const DATA_KEY$a = 'bs.alert'; - const EVENT_KEY$b = `.${DATA_KEY$a}`; - const EVENT_CLOSE = `close${EVENT_KEY$b}`; - const EVENT_CLOSED = `closed${EVENT_KEY$b}`; - const CLASS_NAME_FADE$5 = 'fade'; - const CLASS_NAME_SHOW$8 = 'show'; - - /** - * Class definition - */ - - class Alert extends BaseComponent { - // Getters - static get NAME() { - return NAME$f; - } - - // Public - close() { - const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); - if (closeEvent.defaultPrevented) { - return; - } - this._element.classList.remove(CLASS_NAME_SHOW$8); - const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); - this._queueCallback(() => this._destroyElement(), this._element, isAnimated); - } - - // Private - _destroyElement() { - this._element.remove(); - EventHandler.trigger(this._element, EVENT_CLOSED); - this.dispose(); - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Alert.getOrCreateInstance(this); - if (typeof config !== 'string') { - return; - } - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](this); - }); - } - } - - /** - * Data API implementation - */ - - enableDismissTrigger(Alert, 'close'); - - /** - * jQuery - */ - - defineJQueryPlugin(Alert); - - /** - * -------------------------------------------------------------------------- - * Bootstrap button.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$e = 'button'; - const DATA_KEY$9 = 'bs.button'; - const EVENT_KEY$a = `.${DATA_KEY$9}`; - const DATA_API_KEY$6 = '.data-api'; - const CLASS_NAME_ACTIVE$3 = 'active'; - const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; - const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; - - /** - * Class definition - */ - - class Button extends BaseComponent { - // Getters - static get NAME() { - return NAME$e; - } - - // Public - toggle() { - // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method - this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Button.getOrCreateInstance(this); - if (config === 'toggle') { - data[config](); - } - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { - event.preventDefault(); - const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); - const data = Button.getOrCreateInstance(button); - data.toggle(); - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Button); - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/swipe.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$d = 'swipe'; - const EVENT_KEY$9 = '.bs.swipe'; - const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; - const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; - const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; - const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; - const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; - const POINTER_TYPE_TOUCH = 'touch'; - const POINTER_TYPE_PEN = 'pen'; - const CLASS_NAME_POINTER_EVENT = 'pointer-event'; - const SWIPE_THRESHOLD = 40; - const Default$c = { - endCallback: null, - leftCallback: null, - rightCallback: null - }; - const DefaultType$c = { - endCallback: '(function|null)', - leftCallback: '(function|null)', - rightCallback: '(function|null)' - }; - - /** - * Class definition - */ - - class Swipe extends Config { - constructor(element, config) { - super(); - this._element = element; - if (!element || !Swipe.isSupported()) { - return; - } - this._config = this._getConfig(config); - this._deltaX = 0; - this._supportPointerEvents = Boolean(window.PointerEvent); - this._initEvents(); - } - - // Getters - static get Default() { - return Default$c; - } - static get DefaultType() { - return DefaultType$c; - } - static get NAME() { - return NAME$d; - } - - // Public - dispose() { - EventHandler.off(this._element, EVENT_KEY$9); - } - - // Private - _start(event) { - if (!this._supportPointerEvents) { - this._deltaX = event.touches[0].clientX; - return; - } - if (this._eventIsPointerPenTouch(event)) { - this._deltaX = event.clientX; - } - } - _end(event) { - if (this._eventIsPointerPenTouch(event)) { - this._deltaX = event.clientX - this._deltaX; - } - this._handleSwipe(); - execute(this._config.endCallback); - } - _move(event) { - this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; - } - _handleSwipe() { - const absDeltaX = Math.abs(this._deltaX); - if (absDeltaX <= SWIPE_THRESHOLD) { - return; - } - const direction = absDeltaX / this._deltaX; - this._deltaX = 0; - if (!direction) { - return; - } - execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); - } - _initEvents() { - if (this._supportPointerEvents) { - EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); - EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); - this._element.classList.add(CLASS_NAME_POINTER_EVENT); - } else { - EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); - EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); - EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); - } - } - _eventIsPointerPenTouch(event) { - return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); - } - - // Static - static isSupported() { - return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap carousel.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$c = 'carousel'; - const DATA_KEY$8 = 'bs.carousel'; - const EVENT_KEY$8 = `.${DATA_KEY$8}`; - const DATA_API_KEY$5 = '.data-api'; - const ARROW_LEFT_KEY$1 = 'ArrowLeft'; - const ARROW_RIGHT_KEY$1 = 'ArrowRight'; - const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch - - const ORDER_NEXT = 'next'; - const ORDER_PREV = 'prev'; - const DIRECTION_LEFT = 'left'; - const DIRECTION_RIGHT = 'right'; - const EVENT_SLIDE = `slide${EVENT_KEY$8}`; - const EVENT_SLID = `slid${EVENT_KEY$8}`; - const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; - const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; - const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; - const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; - const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; - const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; - const CLASS_NAME_CAROUSEL = 'carousel'; - const CLASS_NAME_ACTIVE$2 = 'active'; - const CLASS_NAME_SLIDE = 'slide'; - const CLASS_NAME_END = 'carousel-item-end'; - const CLASS_NAME_START = 'carousel-item-start'; - const CLASS_NAME_NEXT = 'carousel-item-next'; - const CLASS_NAME_PREV = 'carousel-item-prev'; - const SELECTOR_ACTIVE = '.active'; - const SELECTOR_ITEM = '.carousel-item'; - const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; - const SELECTOR_ITEM_IMG = '.carousel-item img'; - const SELECTOR_INDICATORS = '.carousel-indicators'; - const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; - const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; - const KEY_TO_DIRECTION = { - [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, - [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT - }; - const Default$b = { - interval: 5000, - keyboard: true, - pause: 'hover', - ride: false, - touch: true, - wrap: true - }; - const DefaultType$b = { - interval: '(number|boolean)', - // TODO:v6 remove boolean support - keyboard: 'boolean', - pause: '(string|boolean)', - ride: '(boolean|string)', - touch: 'boolean', - wrap: 'boolean' - }; - - /** - * Class definition - */ - - class Carousel extends BaseComponent { - constructor(element, config) { - super(element, config); - this._interval = null; - this._activeElement = null; - this._isSliding = false; - this.touchTimeout = null; - this._swipeHelper = null; - this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); - this._addEventListeners(); - if (this._config.ride === CLASS_NAME_CAROUSEL) { - this.cycle(); - } - } - - // Getters - static get Default() { - return Default$b; - } - static get DefaultType() { - return DefaultType$b; - } - static get NAME() { - return NAME$c; - } - - // Public - next() { - this._slide(ORDER_NEXT); - } - nextWhenVisible() { - // FIXME TODO use `document.visibilityState` - // Don't call next when the page isn't visible - // or the carousel or its parent isn't visible - if (!document.hidden && isVisible(this._element)) { - this.next(); - } - } - prev() { - this._slide(ORDER_PREV); - } - pause() { - if (this._isSliding) { - triggerTransitionEnd(this._element); - } - this._clearInterval(); - } - cycle() { - this._clearInterval(); - this._updateInterval(); - this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); - } - _maybeEnableCycle() { - if (!this._config.ride) { - return; - } - if (this._isSliding) { - EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); - return; - } - this.cycle(); - } - to(index) { - const items = this._getItems(); - if (index > items.length - 1 || index < 0) { - return; - } - if (this._isSliding) { - EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); - return; - } - const activeIndex = this._getItemIndex(this._getActive()); - if (activeIndex === index) { - return; - } - const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; - this._slide(order, items[index]); - } - dispose() { - if (this._swipeHelper) { - this._swipeHelper.dispose(); - } - super.dispose(); - } - - // Private - _configAfterMerge(config) { - config.defaultInterval = config.interval; - return config; - } - _addEventListeners() { - if (this._config.keyboard) { - EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); - } - if (this._config.pause === 'hover') { - EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); - EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); - } - if (this._config.touch && Swipe.isSupported()) { - this._addTouchEventListeners(); - } - } - _addTouchEventListeners() { - for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { - EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); - } - const endCallBack = () => { - if (this._config.pause !== 'hover') { - return; - } - - // If it's a touch-enabled device, mouseenter/leave are fired as - // part of the mouse compatibility events on first tap - the carousel - // would stop cycling until user tapped out of it; - // here, we listen for touchend, explicitly pause the carousel - // (as if it's the second time we tap on it, mouseenter compat event - // is NOT fired) and after a timeout (to allow for mouse compatibility - // events to fire) we explicitly restart cycling - - this.pause(); - if (this.touchTimeout) { - clearTimeout(this.touchTimeout); - } - this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); - }; - const swipeConfig = { - leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), - rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), - endCallback: endCallBack - }; - this._swipeHelper = new Swipe(this._element, swipeConfig); - } - _keydown(event) { - if (/input|textarea/i.test(event.target.tagName)) { - return; - } - const direction = KEY_TO_DIRECTION[event.key]; - if (direction) { - event.preventDefault(); - this._slide(this._directionToOrder(direction)); - } - } - _getItemIndex(element) { - return this._getItems().indexOf(element); - } - _setActiveIndicatorElement(index) { - if (!this._indicatorsElement) { - return; - } - const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); - activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); - activeIndicator.removeAttribute('aria-current'); - const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); - if (newActiveIndicator) { - newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); - newActiveIndicator.setAttribute('aria-current', 'true'); - } - } - _updateInterval() { - const element = this._activeElement || this._getActive(); - if (!element) { - return; - } - const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); - this._config.interval = elementInterval || this._config.defaultInterval; - } - _slide(order, element = null) { - if (this._isSliding) { - return; - } - const activeElement = this._getActive(); - const isNext = order === ORDER_NEXT; - const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); - if (nextElement === activeElement) { - return; - } - const nextElementIndex = this._getItemIndex(nextElement); - const triggerEvent = eventName => { - return EventHandler.trigger(this._element, eventName, { - relatedTarget: nextElement, - direction: this._orderToDirection(order), - from: this._getItemIndex(activeElement), - to: nextElementIndex - }); - }; - const slideEvent = triggerEvent(EVENT_SLIDE); - if (slideEvent.defaultPrevented) { - return; - } - if (!activeElement || !nextElement) { - // Some weirdness is happening, so we bail - // TODO: change tests that use empty divs to avoid this check - return; - } - const isCycling = Boolean(this._interval); - this.pause(); - this._isSliding = true; - this._setActiveIndicatorElement(nextElementIndex); - this._activeElement = nextElement; - const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; - const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; - nextElement.classList.add(orderClassName); - reflow(nextElement); - activeElement.classList.add(directionalClassName); - nextElement.classList.add(directionalClassName); - const completeCallBack = () => { - nextElement.classList.remove(directionalClassName, orderClassName); - nextElement.classList.add(CLASS_NAME_ACTIVE$2); - activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); - this._isSliding = false; - triggerEvent(EVENT_SLID); - }; - this._queueCallback(completeCallBack, activeElement, this._isAnimated()); - if (isCycling) { - this.cycle(); - } - } - _isAnimated() { - return this._element.classList.contains(CLASS_NAME_SLIDE); - } - _getActive() { - return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); - } - _getItems() { - return SelectorEngine.find(SELECTOR_ITEM, this._element); - } - _clearInterval() { - if (this._interval) { - clearInterval(this._interval); - this._interval = null; - } - } - _directionToOrder(direction) { - if (isRTL()) { - return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; - } - return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; - } - _orderToDirection(order) { - if (isRTL()) { - return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Carousel.getOrCreateInstance(this, config); - if (typeof config === 'number') { - data.to(config); - return; - } - if (typeof config === 'string') { - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - } - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { - const target = SelectorEngine.getElementFromSelector(this); - if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { - return; - } - event.preventDefault(); - const carousel = Carousel.getOrCreateInstance(target); - const slideIndex = this.getAttribute('data-bs-slide-to'); - if (slideIndex) { - carousel.to(slideIndex); - carousel._maybeEnableCycle(); - return; - } - if (Manipulator.getDataAttribute(this, 'slide') === 'next') { - carousel.next(); - carousel._maybeEnableCycle(); - return; - } - carousel.prev(); - carousel._maybeEnableCycle(); - }); - EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { - const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); - for (const carousel of carousels) { - Carousel.getOrCreateInstance(carousel); - } - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Carousel); - - /** - * -------------------------------------------------------------------------- - * Bootstrap collapse.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$b = 'collapse'; - const DATA_KEY$7 = 'bs.collapse'; - const EVENT_KEY$7 = `.${DATA_KEY$7}`; - const DATA_API_KEY$4 = '.data-api'; - const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; - const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; - const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; - const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; - const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; - const CLASS_NAME_SHOW$7 = 'show'; - const CLASS_NAME_COLLAPSE = 'collapse'; - const CLASS_NAME_COLLAPSING = 'collapsing'; - const CLASS_NAME_COLLAPSED = 'collapsed'; - const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; - const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; - const WIDTH = 'width'; - const HEIGHT = 'height'; - const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; - const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; - const Default$a = { - parent: null, - toggle: true - }; - const DefaultType$a = { - parent: '(null|element)', - toggle: 'boolean' - }; - - /** - * Class definition - */ - - class Collapse extends BaseComponent { - constructor(element, config) { - super(element, config); - this._isTransitioning = false; - this._triggerArray = []; - const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); - for (const elem of toggleList) { - const selector = SelectorEngine.getSelectorFromElement(elem); - const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); - if (selector !== null && filterElement.length) { - this._triggerArray.push(elem); - } - } - this._initializeChildren(); - if (!this._config.parent) { - this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); - } - if (this._config.toggle) { - this.toggle(); - } - } - - // Getters - static get Default() { - return Default$a; - } - static get DefaultType() { - return DefaultType$a; - } - static get NAME() { - return NAME$b; - } - - // Public - toggle() { - if (this._isShown()) { - this.hide(); - } else { - this.show(); - } - } - show() { - if (this._isTransitioning || this._isShown()) { - return; - } - let activeChildren = []; - - // find active children - if (this._config.parent) { - activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { - toggle: false - })); - } - if (activeChildren.length && activeChildren[0]._isTransitioning) { - return; - } - const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); - if (startEvent.defaultPrevented) { - return; - } - for (const activeInstance of activeChildren) { - activeInstance.hide(); - } - const dimension = this._getDimension(); - this._element.classList.remove(CLASS_NAME_COLLAPSE); - this._element.classList.add(CLASS_NAME_COLLAPSING); - this._element.style[dimension] = 0; - this._addAriaAndCollapsedClass(this._triggerArray, true); - this._isTransitioning = true; - const complete = () => { - this._isTransitioning = false; - this._element.classList.remove(CLASS_NAME_COLLAPSING); - this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); - this._element.style[dimension] = ''; - EventHandler.trigger(this._element, EVENT_SHOWN$6); - }; - const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); - const scrollSize = `scroll${capitalizedDimension}`; - this._queueCallback(complete, this._element, true); - this._element.style[dimension] = `${this._element[scrollSize]}px`; - } - hide() { - if (this._isTransitioning || !this._isShown()) { - return; - } - const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); - if (startEvent.defaultPrevented) { - return; - } - const dimension = this._getDimension(); - this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; - reflow(this._element); - this._element.classList.add(CLASS_NAME_COLLAPSING); - this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); - for (const trigger of this._triggerArray) { - const element = SelectorEngine.getElementFromSelector(trigger); - if (element && !this._isShown(element)) { - this._addAriaAndCollapsedClass([trigger], false); - } - } - this._isTransitioning = true; - const complete = () => { - this._isTransitioning = false; - this._element.classList.remove(CLASS_NAME_COLLAPSING); - this._element.classList.add(CLASS_NAME_COLLAPSE); - EventHandler.trigger(this._element, EVENT_HIDDEN$6); - }; - this._element.style[dimension] = ''; - this._queueCallback(complete, this._element, true); - } - - // Private - _isShown(element = this._element) { - return element.classList.contains(CLASS_NAME_SHOW$7); - } - _configAfterMerge(config) { - config.toggle = Boolean(config.toggle); // Coerce string values - config.parent = getElement(config.parent); - return config; - } - _getDimension() { - return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; - } - _initializeChildren() { - if (!this._config.parent) { - return; - } - const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); - for (const element of children) { - const selected = SelectorEngine.getElementFromSelector(element); - if (selected) { - this._addAriaAndCollapsedClass([element], this._isShown(selected)); - } - } - } - _getFirstLevelChildren(selector) { - const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); - // remove children if greater depth - return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); - } - _addAriaAndCollapsedClass(triggerArray, isOpen) { - if (!triggerArray.length) { - return; - } - for (const element of triggerArray) { - element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); - element.setAttribute('aria-expanded', isOpen); - } - } - - // Static - static jQueryInterface(config) { - const _config = {}; - if (typeof config === 'string' && /show|hide/.test(config)) { - _config.toggle = false; - } - return this.each(function () { - const data = Collapse.getOrCreateInstance(this, _config); - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - } - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { - // preventDefault only for <a> elements (which change the URL) not inside the collapsible element - if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { - event.preventDefault(); - } - for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { - Collapse.getOrCreateInstance(element, { - toggle: false - }).toggle(); - } - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Collapse); - - /** - * -------------------------------------------------------------------------- - * Bootstrap dropdown.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$a = 'dropdown'; - const DATA_KEY$6 = 'bs.dropdown'; - const EVENT_KEY$6 = `.${DATA_KEY$6}`; - const DATA_API_KEY$3 = '.data-api'; - const ESCAPE_KEY$2 = 'Escape'; - const TAB_KEY$1 = 'Tab'; - const ARROW_UP_KEY$1 = 'ArrowUp'; - const ARROW_DOWN_KEY$1 = 'ArrowDown'; - const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button - - const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; - const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; - const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; - const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; - const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; - const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; - const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; - const CLASS_NAME_SHOW$6 = 'show'; - const CLASS_NAME_DROPUP = 'dropup'; - const CLASS_NAME_DROPEND = 'dropend'; - const CLASS_NAME_DROPSTART = 'dropstart'; - const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; - const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; - const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; - const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; - const SELECTOR_MENU = '.dropdown-menu'; - const SELECTOR_NAVBAR = '.navbar'; - const SELECTOR_NAVBAR_NAV = '.navbar-nav'; - const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; - const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; - const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; - const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; - const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; - const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; - const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; - const PLACEMENT_TOPCENTER = 'top'; - const PLACEMENT_BOTTOMCENTER = 'bottom'; - const Default$9 = { - autoClose: true, - boundary: 'clippingParents', - display: 'dynamic', - offset: [0, 2], - popperConfig: null, - reference: 'toggle' - }; - const DefaultType$9 = { - autoClose: '(boolean|string)', - boundary: '(string|element)', - display: 'string', - offset: '(array|string|function)', - popperConfig: '(null|object|function)', - reference: '(string|element|object)' - }; - - /** - * Class definition - */ - - class Dropdown extends BaseComponent { - constructor(element, config) { - super(element, config); - this._popper = null; - this._parent = this._element.parentNode; // dropdown wrapper - // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ - this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); - this._inNavbar = this._detectNavbar(); - } - - // Getters - static get Default() { - return Default$9; - } - static get DefaultType() { - return DefaultType$9; - } - static get NAME() { - return NAME$a; - } - - // Public - toggle() { - return this._isShown() ? this.hide() : this.show(); - } - show() { - if (isDisabled(this._element) || this._isShown()) { - return; - } - const relatedTarget = { - relatedTarget: this._element - }; - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); - if (showEvent.defaultPrevented) { - return; - } - this._createPopper(); - - // If this is a touch-enabled device we add extra - // empty mouseover listeners to the body's immediate children; - // only needed because of broken event delegation on iOS - // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { - for (const element of [].concat(...document.body.children)) { - EventHandler.on(element, 'mouseover', noop); - } - } - this._element.focus(); - this._element.setAttribute('aria-expanded', true); - this._menu.classList.add(CLASS_NAME_SHOW$6); - this._element.classList.add(CLASS_NAME_SHOW$6); - EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); - } - hide() { - if (isDisabled(this._element) || !this._isShown()) { - return; - } - const relatedTarget = { - relatedTarget: this._element - }; - this._completeHide(relatedTarget); - } - dispose() { - if (this._popper) { - this._popper.destroy(); - } - super.dispose(); - } - update() { - this._inNavbar = this._detectNavbar(); - if (this._popper) { - this._popper.update(); - } - } - - // Private - _completeHide(relatedTarget) { - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget); - if (hideEvent.defaultPrevented) { - return; - } - - // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - if ('ontouchstart' in document.documentElement) { - for (const element of [].concat(...document.body.children)) { - EventHandler.off(element, 'mouseover', noop); - } - } - if (this._popper) { - this._popper.destroy(); - } - this._menu.classList.remove(CLASS_NAME_SHOW$6); - this._element.classList.remove(CLASS_NAME_SHOW$6); - this._element.setAttribute('aria-expanded', 'false'); - Manipulator.removeDataAttribute(this._menu, 'popper'); - EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); - } - _getConfig(config) { - config = super._getConfig(config); - if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { - // Popper virtual elements require a getBoundingClientRect method - throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); - } - return config; - } - _createPopper() { - if (typeof Popper__namespace === 'undefined') { - throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org/docs/v2/)'); - } - let referenceElement = this._element; - if (this._config.reference === 'parent') { - referenceElement = this._parent; - } else if (isElement(this._config.reference)) { - referenceElement = getElement(this._config.reference); - } else if (typeof this._config.reference === 'object') { - referenceElement = this._config.reference; - } - const popperConfig = this._getPopperConfig(); - this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig); - } - _isShown() { - return this._menu.classList.contains(CLASS_NAME_SHOW$6); - } - _getPlacement() { - const parentDropdown = this._parent; - if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { - return PLACEMENT_RIGHT; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { - return PLACEMENT_LEFT; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { - return PLACEMENT_TOPCENTER; - } - if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { - return PLACEMENT_BOTTOMCENTER; - } - - // We need to trim the value because custom properties can also include spaces - const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; - if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { - return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; - } - return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; - } - _detectNavbar() { - return this._element.closest(SELECTOR_NAVBAR) !== null; - } - _getOffset() { - const { - offset - } = this._config; - if (typeof offset === 'string') { - return offset.split(',').map(value => Number.parseInt(value, 10)); - } - if (typeof offset === 'function') { - return popperData => offset(popperData, this._element); - } - return offset; - } - _getPopperConfig() { - const defaultBsPopperConfig = { - placement: this._getPlacement(), - modifiers: [{ - name: 'preventOverflow', - options: { - boundary: this._config.boundary - } - }, { - name: 'offset', - options: { - offset: this._getOffset() - } - }] - }; - - // Disable Popper if we have a static display or Dropdown is in Navbar - if (this._inNavbar || this._config.display === 'static') { - Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove - defaultBsPopperConfig.modifiers = [{ - name: 'applyStyles', - enabled: false - }]; - } - return { - ...defaultBsPopperConfig, - ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig]) - }; - } - _selectMenuItem({ - key, - target - }) { - const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); - if (!items.length) { - return; - } - - // if target isn't included in items (e.g. when expanding the dropdown) - // allow cycling to get the last item in case key equals ARROW_UP_KEY - getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Dropdown.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - static clearMenus(event) { - if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { - return; - } - const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); - for (const toggle of openToggles) { - const context = Dropdown.getInstance(toggle); - if (!context || context._config.autoClose === false) { - continue; - } - const composedPath = event.composedPath(); - const isMenuTarget = composedPath.includes(context._menu); - if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { - continue; - } - - // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu - if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { - continue; - } - const relatedTarget = { - relatedTarget: context._element - }; - if (event.type === 'click') { - relatedTarget.clickEvent = event; - } - context._completeHide(relatedTarget); - } - } - static dataApiKeydownHandler(event) { - // If not an UP | DOWN | ESCAPE key => not a dropdown command - // If input/textarea && if key is other than ESCAPE => not a dropdown command - - const isInput = /input|textarea/i.test(event.target.tagName); - const isEscapeEvent = event.key === ESCAPE_KEY$2; - const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); - if (!isUpOrDownEvent && !isEscapeEvent) { - return; - } - if (isInput && !isEscapeEvent) { - return; - } - event.preventDefault(); - - // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ - const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); - const instance = Dropdown.getOrCreateInstance(getToggleButton); - if (isUpOrDownEvent) { - event.stopPropagation(); - instance.show(); - instance._selectMenuItem(event); - return; - } - if (instance._isShown()) { - // else is escape and we check if it is shown - event.stopPropagation(); - instance.hide(); - getToggleButton.focus(); - } - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); - EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); - EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); - EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); - EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { - event.preventDefault(); - Dropdown.getOrCreateInstance(this).toggle(); - }); - - /** - * jQuery - */ - - defineJQueryPlugin(Dropdown); - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/backdrop.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$9 = 'backdrop'; - const CLASS_NAME_FADE$4 = 'fade'; - const CLASS_NAME_SHOW$5 = 'show'; - const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; - const Default$8 = { - className: 'modal-backdrop', - clickCallback: null, - isAnimated: false, - isVisible: true, - // if false, we use the backdrop helper without adding any element to the dom - rootElement: 'body' // give the choice to place backdrop under different elements - }; - const DefaultType$8 = { - className: 'string', - clickCallback: '(function|null)', - isAnimated: 'boolean', - isVisible: 'boolean', - rootElement: '(element|string)' - }; - - /** - * Class definition - */ - - class Backdrop extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - this._isAppended = false; - this._element = null; - } - - // Getters - static get Default() { - return Default$8; - } - static get DefaultType() { - return DefaultType$8; - } - static get NAME() { - return NAME$9; - } - - // Public - show(callback) { - if (!this._config.isVisible) { - execute(callback); - return; - } - this._append(); - const element = this._getElement(); - if (this._config.isAnimated) { - reflow(element); - } - element.classList.add(CLASS_NAME_SHOW$5); - this._emulateAnimation(() => { - execute(callback); - }); - } - hide(callback) { - if (!this._config.isVisible) { - execute(callback); - return; - } - this._getElement().classList.remove(CLASS_NAME_SHOW$5); - this._emulateAnimation(() => { - this.dispose(); - execute(callback); - }); - } - dispose() { - if (!this._isAppended) { - return; - } - EventHandler.off(this._element, EVENT_MOUSEDOWN); - this._element.remove(); - this._isAppended = false; - } - - // Private - _getElement() { - if (!this._element) { - const backdrop = document.createElement('div'); - backdrop.className = this._config.className; - if (this._config.isAnimated) { - backdrop.classList.add(CLASS_NAME_FADE$4); - } - this._element = backdrop; - } - return this._element; - } - _configAfterMerge(config) { - // use getElement() with the default "body" to get a fresh Element on each instantiation - config.rootElement = getElement(config.rootElement); - return config; - } - _append() { - if (this._isAppended) { - return; - } - const element = this._getElement(); - this._config.rootElement.append(element); - EventHandler.on(element, EVENT_MOUSEDOWN, () => { - execute(this._config.clickCallback); - }); - this._isAppended = true; - } - _emulateAnimation(callback) { - executeAfterTransition(callback, this._getElement(), this._config.isAnimated); - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/focustrap.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$8 = 'focustrap'; - const DATA_KEY$5 = 'bs.focustrap'; - const EVENT_KEY$5 = `.${DATA_KEY$5}`; - const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; - const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; - const TAB_KEY = 'Tab'; - const TAB_NAV_FORWARD = 'forward'; - const TAB_NAV_BACKWARD = 'backward'; - const Default$7 = { - autofocus: true, - trapElement: null // The element to trap focus inside of - }; - const DefaultType$7 = { - autofocus: 'boolean', - trapElement: 'element' - }; - - /** - * Class definition - */ - - class FocusTrap extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - this._isActive = false; - this._lastTabNavDirection = null; - } - - // Getters - static get Default() { - return Default$7; - } - static get DefaultType() { - return DefaultType$7; - } - static get NAME() { - return NAME$8; - } - - // Public - activate() { - if (this._isActive) { - return; - } - if (this._config.autofocus) { - this._config.trapElement.focus(); - } - EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop - EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); - EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); - this._isActive = true; - } - deactivate() { - if (!this._isActive) { - return; - } - this._isActive = false; - EventHandler.off(document, EVENT_KEY$5); - } - - // Private - _handleFocusin(event) { - const { - trapElement - } = this._config; - if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { - return; - } - const elements = SelectorEngine.focusableChildren(trapElement); - if (elements.length === 0) { - trapElement.focus(); - } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { - elements[elements.length - 1].focus(); - } else { - elements[0].focus(); - } - } - _handleKeydown(event) { - if (event.key !== TAB_KEY) { - return; - } - this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/scrollBar.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; - const SELECTOR_STICKY_CONTENT = '.sticky-top'; - const PROPERTY_PADDING = 'padding-right'; - const PROPERTY_MARGIN = 'margin-right'; - - /** - * Class definition - */ - - class ScrollBarHelper { - constructor() { - this._element = document.body; - } - - // Public - getWidth() { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth; - return Math.abs(window.innerWidth - documentWidth); - } - hide() { - const width = this.getWidth(); - this._disableOverFlow(); - // give padding to element to balance the hidden scrollbar width - this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth - this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); - } - reset() { - this._resetElementAttributes(this._element, 'overflow'); - this._resetElementAttributes(this._element, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); - } - isOverflowing() { - return this.getWidth() > 0; - } - - // Private - _disableOverFlow() { - this._saveInitialAttribute(this._element, 'overflow'); - this._element.style.overflow = 'hidden'; - } - _setElementAttributes(selector, styleProperty, callback) { - const scrollbarWidth = this.getWidth(); - const manipulationCallBack = element => { - if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { - return; - } - this._saveInitialAttribute(element, styleProperty); - const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); - element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _saveInitialAttribute(element, styleProperty) { - const actualValue = element.style.getPropertyValue(styleProperty); - if (actualValue) { - Manipulator.setDataAttribute(element, styleProperty, actualValue); - } - } - _resetElementAttributes(selector, styleProperty) { - const manipulationCallBack = element => { - const value = Manipulator.getDataAttribute(element, styleProperty); - // We only want to remove the property if the value is `null`; the value can also be zero - if (value === null) { - element.style.removeProperty(styleProperty); - return; - } - Manipulator.removeDataAttribute(element, styleProperty); - element.style.setProperty(styleProperty, value); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _applyManipulationCallback(selector, callBack) { - if (isElement(selector)) { - callBack(selector); - return; - } - for (const sel of SelectorEngine.find(selector, this._element)) { - callBack(sel); - } - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap modal.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$7 = 'modal'; - const DATA_KEY$4 = 'bs.modal'; - const EVENT_KEY$4 = `.${DATA_KEY$4}`; - const DATA_API_KEY$2 = '.data-api'; - const ESCAPE_KEY$1 = 'Escape'; - const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; - const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; - const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; - const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; - const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; - const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; - const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; - const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; - const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; - const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; - const CLASS_NAME_OPEN = 'modal-open'; - const CLASS_NAME_FADE$3 = 'fade'; - const CLASS_NAME_SHOW$4 = 'show'; - const CLASS_NAME_STATIC = 'modal-static'; - const OPEN_SELECTOR$1 = '.modal.show'; - const SELECTOR_DIALOG = '.modal-dialog'; - const SELECTOR_MODAL_BODY = '.modal-body'; - const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; - const Default$6 = { - backdrop: true, - focus: true, - keyboard: true - }; - const DefaultType$6 = { - backdrop: '(boolean|string)', - focus: 'boolean', - keyboard: 'boolean' - }; - - /** - * Class definition - */ - - class Modal extends BaseComponent { - constructor(element, config) { - super(element, config); - this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); - this._backdrop = this._initializeBackDrop(); - this._focustrap = this._initializeFocusTrap(); - this._isShown = false; - this._isTransitioning = false; - this._scrollBar = new ScrollBarHelper(); - this._addEventListeners(); - } - - // Getters - static get Default() { - return Default$6; - } - static get DefaultType() { - return DefaultType$6; - } - static get NAME() { - return NAME$7; - } - - // Public - toggle(relatedTarget) { - return this._isShown ? this.hide() : this.show(relatedTarget); - } - show(relatedTarget) { - if (this._isShown || this._isTransitioning) { - return; - } - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, { - relatedTarget - }); - if (showEvent.defaultPrevented) { - return; - } - this._isShown = true; - this._isTransitioning = true; - this._scrollBar.hide(); - document.body.classList.add(CLASS_NAME_OPEN); - this._adjustDialog(); - this._backdrop.show(() => this._showElement(relatedTarget)); - } - hide() { - if (!this._isShown || this._isTransitioning) { - return; - } - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4); - if (hideEvent.defaultPrevented) { - return; - } - this._isShown = false; - this._isTransitioning = true; - this._focustrap.deactivate(); - this._element.classList.remove(CLASS_NAME_SHOW$4); - this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); - } - dispose() { - EventHandler.off(window, EVENT_KEY$4); - EventHandler.off(this._dialog, EVENT_KEY$4); - this._backdrop.dispose(); - this._focustrap.deactivate(); - super.dispose(); - } - handleUpdate() { - this._adjustDialog(); - } - - // Private - _initializeBackDrop() { - return new Backdrop({ - isVisible: Boolean(this._config.backdrop), - // 'static' option will be translated to true, and booleans will keep their value, - isAnimated: this._isAnimated() - }); - } - _initializeFocusTrap() { - return new FocusTrap({ - trapElement: this._element - }); - } - _showElement(relatedTarget) { - // try to append dynamic modal - if (!document.body.contains(this._element)) { - document.body.append(this._element); - } - this._element.style.display = 'block'; - this._element.removeAttribute('aria-hidden'); - this._element.setAttribute('aria-modal', true); - this._element.setAttribute('role', 'dialog'); - this._element.scrollTop = 0; - const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); - if (modalBody) { - modalBody.scrollTop = 0; - } - reflow(this._element); - this._element.classList.add(CLASS_NAME_SHOW$4); - const transitionComplete = () => { - if (this._config.focus) { - this._focustrap.activate(); - } - this._isTransitioning = false; - EventHandler.trigger(this._element, EVENT_SHOWN$4, { - relatedTarget - }); - }; - this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); - } - _addEventListeners() { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { - if (event.key !== ESCAPE_KEY$1) { - return; - } - if (this._config.keyboard) { - this.hide(); - return; - } - this._triggerBackdropTransition(); - }); - EventHandler.on(window, EVENT_RESIZE$1, () => { - if (this._isShown && !this._isTransitioning) { - this._adjustDialog(); - } - }); - EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { - // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks - EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { - if (this._element !== event.target || this._element !== event2.target) { - return; - } - if (this._config.backdrop === 'static') { - this._triggerBackdropTransition(); - return; - } - if (this._config.backdrop) { - this.hide(); - } - }); - }); - } - _hideModal() { - this._element.style.display = 'none'; - this._element.setAttribute('aria-hidden', true); - this._element.removeAttribute('aria-modal'); - this._element.removeAttribute('role'); - this._isTransitioning = false; - this._backdrop.hide(() => { - document.body.classList.remove(CLASS_NAME_OPEN); - this._resetAdjustments(); - this._scrollBar.reset(); - EventHandler.trigger(this._element, EVENT_HIDDEN$4); - }); - } - _isAnimated() { - return this._element.classList.contains(CLASS_NAME_FADE$3); - } - _triggerBackdropTransition() { - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1); - if (hideEvent.defaultPrevented) { - return; - } - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; - const initialOverflowY = this._element.style.overflowY; - // return if the following background transition hasn't yet completed - if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { - return; - } - if (!isModalOverflowing) { - this._element.style.overflowY = 'hidden'; - } - this._element.classList.add(CLASS_NAME_STATIC); - this._queueCallback(() => { - this._element.classList.remove(CLASS_NAME_STATIC); - this._queueCallback(() => { - this._element.style.overflowY = initialOverflowY; - }, this._dialog); - }, this._dialog); - this._element.focus(); - } - - /** - * The following methods are used to handle overflowing modals - */ - - _adjustDialog() { - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; - const scrollbarWidth = this._scrollBar.getWidth(); - const isBodyOverflowing = scrollbarWidth > 0; - if (isBodyOverflowing && !isModalOverflowing) { - const property = isRTL() ? 'paddingLeft' : 'paddingRight'; - this._element.style[property] = `${scrollbarWidth}px`; - } - if (!isBodyOverflowing && isModalOverflowing) { - const property = isRTL() ? 'paddingRight' : 'paddingLeft'; - this._element.style[property] = `${scrollbarWidth}px`; - } - } - _resetAdjustments() { - this._element.style.paddingLeft = ''; - this._element.style.paddingRight = ''; - } - - // Static - static jQueryInterface(config, relatedTarget) { - return this.each(function () { - const data = Modal.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](relatedTarget); - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { - const target = SelectorEngine.getElementFromSelector(this); - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - EventHandler.one(target, EVENT_SHOW$4, showEvent => { - if (showEvent.defaultPrevented) { - // only register focus restorer if modal will actually get shown - return; - } - EventHandler.one(target, EVENT_HIDDEN$4, () => { - if (isVisible(this)) { - this.focus(); - } - }); - }); - - // avoid conflict when clicking modal toggler while another one is open - const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1); - if (alreadyOpen) { - Modal.getInstance(alreadyOpen).hide(); - } - const data = Modal.getOrCreateInstance(target); - data.toggle(this); - }); - enableDismissTrigger(Modal); - - /** - * jQuery - */ - - defineJQueryPlugin(Modal); - - /** - * -------------------------------------------------------------------------- - * Bootstrap offcanvas.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$6 = 'offcanvas'; - const DATA_KEY$3 = 'bs.offcanvas'; - const EVENT_KEY$3 = `.${DATA_KEY$3}`; - const DATA_API_KEY$1 = '.data-api'; - const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`; - const ESCAPE_KEY = 'Escape'; - const CLASS_NAME_SHOW$3 = 'show'; - const CLASS_NAME_SHOWING$1 = 'showing'; - const CLASS_NAME_HIDING = 'hiding'; - const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'; - const OPEN_SELECTOR = '.offcanvas.show'; - const EVENT_SHOW$3 = `show${EVENT_KEY$3}`; - const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`; - const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`; - const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`; - const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`; - const EVENT_RESIZE = `resize${EVENT_KEY$3}`; - const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`; - const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`; - const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; - const Default$5 = { - backdrop: true, - keyboard: true, - scroll: false - }; - const DefaultType$5 = { - backdrop: '(boolean|string)', - keyboard: 'boolean', - scroll: 'boolean' - }; - - /** - * Class definition - */ - - class Offcanvas extends BaseComponent { - constructor(element, config) { - super(element, config); - this._isShown = false; - this._backdrop = this._initializeBackDrop(); - this._focustrap = this._initializeFocusTrap(); - this._addEventListeners(); - } - - // Getters - static get Default() { - return Default$5; - } - static get DefaultType() { - return DefaultType$5; - } - static get NAME() { - return NAME$6; - } - - // Public - toggle(relatedTarget) { - return this._isShown ? this.hide() : this.show(relatedTarget); - } - show(relatedTarget) { - if (this._isShown) { - return; - } - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, { - relatedTarget - }); - if (showEvent.defaultPrevented) { - return; - } - this._isShown = true; - this._backdrop.show(); - if (!this._config.scroll) { - new ScrollBarHelper().hide(); - } - this._element.setAttribute('aria-modal', true); - this._element.setAttribute('role', 'dialog'); - this._element.classList.add(CLASS_NAME_SHOWING$1); - const completeCallBack = () => { - if (!this._config.scroll || this._config.backdrop) { - this._focustrap.activate(); - } - this._element.classList.add(CLASS_NAME_SHOW$3); - this._element.classList.remove(CLASS_NAME_SHOWING$1); - EventHandler.trigger(this._element, EVENT_SHOWN$3, { - relatedTarget - }); - }; - this._queueCallback(completeCallBack, this._element, true); - } - hide() { - if (!this._isShown) { - return; - } - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3); - if (hideEvent.defaultPrevented) { - return; - } - this._focustrap.deactivate(); - this._element.blur(); - this._isShown = false; - this._element.classList.add(CLASS_NAME_HIDING); - this._backdrop.hide(); - const completeCallback = () => { - this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING); - this._element.removeAttribute('aria-modal'); - this._element.removeAttribute('role'); - if (!this._config.scroll) { - new ScrollBarHelper().reset(); - } - EventHandler.trigger(this._element, EVENT_HIDDEN$3); - }; - this._queueCallback(completeCallback, this._element, true); - } - dispose() { - this._backdrop.dispose(); - this._focustrap.deactivate(); - super.dispose(); - } - - // Private - _initializeBackDrop() { - const clickCallback = () => { - if (this._config.backdrop === 'static') { - EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); - return; - } - this.hide(); - }; - - // 'static' option will be translated to true, and booleans will keep their value - const isVisible = Boolean(this._config.backdrop); - return new Backdrop({ - className: CLASS_NAME_BACKDROP, - isVisible, - isAnimated: true, - rootElement: this._element.parentNode, - clickCallback: isVisible ? clickCallback : null - }); - } - _initializeFocusTrap() { - return new FocusTrap({ - trapElement: this._element - }); - } - _addEventListeners() { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (event.key !== ESCAPE_KEY) { - return; - } - if (this._config.keyboard) { - this.hide(); - return; - } - EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); - }); - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Offcanvas.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](this); - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) { - const target = SelectorEngine.getElementFromSelector(this); - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - if (isDisabled(this)) { - return; - } - EventHandler.one(target, EVENT_HIDDEN$3, () => { - // focus on trigger when it is closed - if (isVisible(this)) { - this.focus(); - } - }); - - // avoid conflict when clicking a toggler of an offcanvas, while another is open - const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); - if (alreadyOpen && alreadyOpen !== target) { - Offcanvas.getInstance(alreadyOpen).hide(); - } - const data = Offcanvas.getOrCreateInstance(target); - data.toggle(this); - }); - EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { - for (const selector of SelectorEngine.find(OPEN_SELECTOR)) { - Offcanvas.getOrCreateInstance(selector).show(); - } - }); - EventHandler.on(window, EVENT_RESIZE, () => { - for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { - if (getComputedStyle(element).position !== 'fixed') { - Offcanvas.getOrCreateInstance(element).hide(); - } - } - }); - enableDismissTrigger(Offcanvas); - - /** - * jQuery - */ - - defineJQueryPlugin(Offcanvas); - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/sanitizer.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - // js-docs-start allow-list - const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; - const DefaultAllowlist = { - // Global attributes allowed on any supplied element below. - '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], - a: ['target', 'href', 'title', 'rel'], - area: [], - b: [], - br: [], - col: [], - code: [], - dd: [], - div: [], - dl: [], - dt: [], - em: [], - hr: [], - h1: [], - h2: [], - h3: [], - h4: [], - h5: [], - h6: [], - i: [], - img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], - li: [], - ol: [], - p: [], - pre: [], - s: [], - small: [], - span: [], - sub: [], - sup: [], - strong: [], - u: [], - ul: [] - }; - // js-docs-end allow-list - - const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); - - /** - * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation - * contexts. - * - * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38 - */ - const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i; - const allowedAttribute = (attribute, allowedAttributeList) => { - const attributeName = attribute.nodeName.toLowerCase(); - if (allowedAttributeList.includes(attributeName)) { - if (uriAttributes.has(attributeName)) { - return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue)); - } - return true; - } - - // Check if a regular expression validates the attribute. - return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName)); - }; - function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) { - if (!unsafeHtml.length) { - return unsafeHtml; - } - if (sanitizeFunction && typeof sanitizeFunction === 'function') { - return sanitizeFunction(unsafeHtml); - } - const domParser = new window.DOMParser(); - const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); - const elements = [].concat(...createdDocument.body.querySelectorAll('*')); - for (const element of elements) { - const elementName = element.nodeName.toLowerCase(); - if (!Object.keys(allowList).includes(elementName)) { - element.remove(); - continue; - } - const attributeList = [].concat(...element.attributes); - const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []); - for (const attribute of attributeList) { - if (!allowedAttribute(attribute, allowedAttributes)) { - element.removeAttribute(attribute.nodeName); - } - } - } - return createdDocument.body.innerHTML; - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap util/template-factory.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$5 = 'TemplateFactory'; - const Default$4 = { - allowList: DefaultAllowlist, - content: {}, - // { selector : text , selector2 : text2 , } - extraClass: '', - html: false, - sanitize: true, - sanitizeFn: null, - template: '<div></div>' - }; - const DefaultType$4 = { - allowList: 'object', - content: 'object', - extraClass: '(string|function)', - html: 'boolean', - sanitize: 'boolean', - sanitizeFn: '(null|function)', - template: 'string' - }; - const DefaultContentType = { - entry: '(string|element|function|null)', - selector: '(string|element)' - }; - - /** - * Class definition - */ - - class TemplateFactory extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - } - - // Getters - static get Default() { - return Default$4; - } - static get DefaultType() { - return DefaultType$4; - } - static get NAME() { - return NAME$5; - } - - // Public - getContent() { - return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean); - } - hasContent() { - return this.getContent().length > 0; - } - changeContent(content) { - this._checkContent(content); - this._config.content = { - ...this._config.content, - ...content - }; - return this; - } - toHtml() { - const templateWrapper = document.createElement('div'); - templateWrapper.innerHTML = this._maybeSanitize(this._config.template); - for (const [selector, text] of Object.entries(this._config.content)) { - this._setContent(templateWrapper, text, selector); - } - const template = templateWrapper.children[0]; - const extraClass = this._resolvePossibleFunction(this._config.extraClass); - if (extraClass) { - template.classList.add(...extraClass.split(' ')); - } - return template; - } - - // Private - _typeCheckConfig(config) { - super._typeCheckConfig(config); - this._checkContent(config.content); - } - _checkContent(arg) { - for (const [selector, content] of Object.entries(arg)) { - super._typeCheckConfig({ - selector, - entry: content - }, DefaultContentType); - } - } - _setContent(template, content, selector) { - const templateElement = SelectorEngine.findOne(selector, template); - if (!templateElement) { - return; - } - content = this._resolvePossibleFunction(content); - if (!content) { - templateElement.remove(); - return; - } - if (isElement(content)) { - this._putElementInTemplate(getElement(content), templateElement); - return; - } - if (this._config.html) { - templateElement.innerHTML = this._maybeSanitize(content); - return; - } - templateElement.textContent = content; - } - _maybeSanitize(arg) { - return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg; - } - _resolvePossibleFunction(arg) { - return execute(arg, [undefined, this]); - } - _putElementInTemplate(element, templateElement) { - if (this._config.html) { - templateElement.innerHTML = ''; - templateElement.append(element); - return; - } - templateElement.textContent = element.textContent; - } - } - - /** - * -------------------------------------------------------------------------- - * Bootstrap tooltip.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$4 = 'tooltip'; - const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); - const CLASS_NAME_FADE$2 = 'fade'; - const CLASS_NAME_MODAL = 'modal'; - const CLASS_NAME_SHOW$2 = 'show'; - const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; - const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`; - const EVENT_MODAL_HIDE = 'hide.bs.modal'; - const TRIGGER_HOVER = 'hover'; - const TRIGGER_FOCUS = 'focus'; - const TRIGGER_CLICK = 'click'; - const TRIGGER_MANUAL = 'manual'; - const EVENT_HIDE$2 = 'hide'; - const EVENT_HIDDEN$2 = 'hidden'; - const EVENT_SHOW$2 = 'show'; - const EVENT_SHOWN$2 = 'shown'; - const EVENT_INSERTED = 'inserted'; - const EVENT_CLICK$1 = 'click'; - const EVENT_FOCUSIN$1 = 'focusin'; - const EVENT_FOCUSOUT$1 = 'focusout'; - const EVENT_MOUSEENTER = 'mouseenter'; - const EVENT_MOUSELEAVE = 'mouseleave'; - const AttachmentMap = { - AUTO: 'auto', - TOP: 'top', - RIGHT: isRTL() ? 'left' : 'right', - BOTTOM: 'bottom', - LEFT: isRTL() ? 'right' : 'left' - }; - const Default$3 = { - allowList: DefaultAllowlist, - animation: true, - boundary: 'clippingParents', - container: false, - customClass: '', - delay: 0, - fallbackPlacements: ['top', 'right', 'bottom', 'left'], - html: false, - offset: [0, 6], - placement: 'top', - popperConfig: null, - sanitize: true, - sanitizeFn: null, - selector: false, - template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>', - title: '', - trigger: 'hover focus' - }; - const DefaultType$3 = { - allowList: 'object', - animation: 'boolean', - boundary: '(string|element)', - container: '(string|element|boolean)', - customClass: '(string|function)', - delay: '(number|object)', - fallbackPlacements: 'array', - html: 'boolean', - offset: '(array|string|function)', - placement: '(string|function)', - popperConfig: '(null|object|function)', - sanitize: 'boolean', - sanitizeFn: '(null|function)', - selector: '(string|boolean)', - template: 'string', - title: '(string|element|function)', - trigger: 'string' - }; - - /** - * Class definition - */ - - class Tooltip extends BaseComponent { - constructor(element, config) { - if (typeof Popper__namespace === 'undefined') { - throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org/docs/v2/)'); - } - super(element, config); - - // Private - this._isEnabled = true; - this._timeout = 0; - this._isHovered = null; - this._activeTrigger = {}; - this._popper = null; - this._templateFactory = null; - this._newContent = null; - - // Protected - this.tip = null; - this._setListeners(); - if (!this._config.selector) { - this._fixTitle(); - } - } - - // Getters - static get Default() { - return Default$3; - } - static get DefaultType() { - return DefaultType$3; - } - static get NAME() { - return NAME$4; - } - - // Public - enable() { - this._isEnabled = true; - } - disable() { - this._isEnabled = false; - } - toggleEnabled() { - this._isEnabled = !this._isEnabled; - } - toggle() { - if (!this._isEnabled) { - return; - } - if (this._isShown()) { - this._leave(); - return; - } - this._enter(); - } - dispose() { - clearTimeout(this._timeout); - EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); - if (this._element.getAttribute('data-bs-original-title')) { - this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title')); - } - this._disposePopper(); - super.dispose(); - } - show() { - if (this._element.style.display === 'none') { - throw new Error('Please use show on visible elements'); - } - if (!(this._isWithContent() && this._isEnabled)) { - return; - } - const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2)); - const shadowRoot = findShadowRoot(this._element); - const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element); - if (showEvent.defaultPrevented || !isInTheDom) { - return; - } - - // TODO: v6 remove this or make it optional - this._disposePopper(); - const tip = this._getTipElement(); - this._element.setAttribute('aria-describedby', tip.getAttribute('id')); - const { - container - } = this._config; - if (!this._element.ownerDocument.documentElement.contains(this.tip)) { - container.append(tip); - EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED)); - } - this._popper = this._createPopper(tip); - tip.classList.add(CLASS_NAME_SHOW$2); - - // If this is a touch-enabled device we add extra - // empty mouseover listeners to the body's immediate children; - // only needed because of broken event delegation on iOS - // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - if ('ontouchstart' in document.documentElement) { - for (const element of [].concat(...document.body.children)) { - EventHandler.on(element, 'mouseover', noop); - } - } - const complete = () => { - EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2)); - if (this._isHovered === false) { - this._leave(); - } - this._isHovered = false; - }; - this._queueCallback(complete, this.tip, this._isAnimated()); - } - hide() { - if (!this._isShown()) { - return; - } - const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2)); - if (hideEvent.defaultPrevented) { - return; - } - const tip = this._getTipElement(); - tip.classList.remove(CLASS_NAME_SHOW$2); - - // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - if ('ontouchstart' in document.documentElement) { - for (const element of [].concat(...document.body.children)) { - EventHandler.off(element, 'mouseover', noop); - } - } - this._activeTrigger[TRIGGER_CLICK] = false; - this._activeTrigger[TRIGGER_FOCUS] = false; - this._activeTrigger[TRIGGER_HOVER] = false; - this._isHovered = null; // it is a trick to support manual triggering - - const complete = () => { - if (this._isWithActiveTrigger()) { - return; - } - if (!this._isHovered) { - this._disposePopper(); - } - this._element.removeAttribute('aria-describedby'); - EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2)); - }; - this._queueCallback(complete, this.tip, this._isAnimated()); - } - update() { - if (this._popper) { - this._popper.update(); - } - } - - // Protected - _isWithContent() { - return Boolean(this._getTitle()); - } - _getTipElement() { - if (!this.tip) { - this.tip = this._createTipElement(this._newContent || this._getContentForTemplate()); - } - return this.tip; - } - _createTipElement(content) { - const tip = this._getTemplateFactory(content).toHtml(); - - // TODO: remove this check in v6 - if (!tip) { - return null; - } - tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); - // TODO: v6 the following can be achieved with CSS only - tip.classList.add(`bs-${this.constructor.NAME}-auto`); - const tipId = getUID(this.constructor.NAME).toString(); - tip.setAttribute('id', tipId); - if (this._isAnimated()) { - tip.classList.add(CLASS_NAME_FADE$2); - } - return tip; - } - setContent(content) { - this._newContent = content; - if (this._isShown()) { - this._disposePopper(); - this.show(); - } - } - _getTemplateFactory(content) { - if (this._templateFactory) { - this._templateFactory.changeContent(content); - } else { - this._templateFactory = new TemplateFactory({ - ...this._config, - // the `content` var has to be after `this._config` - // to override config.content in case of popover - content, - extraClass: this._resolvePossibleFunction(this._config.customClass) - }); - } - return this._templateFactory; - } - _getContentForTemplate() { - return { - [SELECTOR_TOOLTIP_INNER]: this._getTitle() - }; - } - _getTitle() { - return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title'); - } - - // Private - _initializeOnDelegatedTarget(event) { - return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()); - } - _isAnimated() { - return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2); - } - _isShown() { - return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2); - } - _createPopper(tip) { - const placement = execute(this._config.placement, [this, tip, this._element]); - const attachment = AttachmentMap[placement.toUpperCase()]; - return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment)); - } - _getOffset() { - const { - offset - } = this._config; - if (typeof offset === 'string') { - return offset.split(',').map(value => Number.parseInt(value, 10)); - } - if (typeof offset === 'function') { - return popperData => offset(popperData, this._element); - } - return offset; - } - _resolvePossibleFunction(arg) { - return execute(arg, [this._element, this._element]); - } - _getPopperConfig(attachment) { - const defaultBsPopperConfig = { - placement: attachment, - modifiers: [{ - name: 'flip', - options: { - fallbackPlacements: this._config.fallbackPlacements - } - }, { - name: 'offset', - options: { - offset: this._getOffset() - } - }, { - name: 'preventOverflow', - options: { - boundary: this._config.boundary - } - }, { - name: 'arrow', - options: { - element: `.${this.constructor.NAME}-arrow` - } - }, { - name: 'preSetPlacement', - enabled: true, - phase: 'beforeMain', - fn: data => { - // Pre-set Popper's placement attribute in order to read the arrow sizes properly. - // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement - this._getTipElement().setAttribute('data-popper-placement', data.state.placement); - } - }] - }; - return { - ...defaultBsPopperConfig, - ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig]) - }; - } - _setListeners() { - const triggers = this._config.trigger.split(' '); - for (const trigger of triggers) { - if (trigger === 'click') { - EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => { - const context = this._initializeOnDelegatedTarget(event); - context._activeTrigger[TRIGGER_CLICK] = !(context._isShown() && context._activeTrigger[TRIGGER_CLICK]); - context.toggle(); - }); - } else if (trigger !== TRIGGER_MANUAL) { - const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1); - const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1); - EventHandler.on(this._element, eventIn, this._config.selector, event => { - const context = this._initializeOnDelegatedTarget(event); - context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; - context._enter(); - }); - EventHandler.on(this._element, eventOut, this._config.selector, event => { - const context = this._initializeOnDelegatedTarget(event); - context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget); - context._leave(); - }); - } - } - this._hideModalHandler = () => { - if (this._element) { - this.hide(); - } - }; - EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); - } - _fixTitle() { - const title = this._element.getAttribute('title'); - if (!title) { - return; - } - if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) { - this._element.setAttribute('aria-label', title); - } - this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility - this._element.removeAttribute('title'); - } - _enter() { - if (this._isShown() || this._isHovered) { - this._isHovered = true; - return; - } - this._isHovered = true; - this._setTimeout(() => { - if (this._isHovered) { - this.show(); - } - }, this._config.delay.show); - } - _leave() { - if (this._isWithActiveTrigger()) { - return; - } - this._isHovered = false; - this._setTimeout(() => { - if (!this._isHovered) { - this.hide(); - } - }, this._config.delay.hide); - } - _setTimeout(handler, timeout) { - clearTimeout(this._timeout); - this._timeout = setTimeout(handler, timeout); - } - _isWithActiveTrigger() { - return Object.values(this._activeTrigger).includes(true); - } - _getConfig(config) { - const dataAttributes = Manipulator.getDataAttributes(this._element); - for (const dataAttribute of Object.keys(dataAttributes)) { - if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { - delete dataAttributes[dataAttribute]; - } - } - config = { - ...dataAttributes, - ...(typeof config === 'object' && config ? config : {}) - }; - config = this._mergeConfigObj(config); - config = this._configAfterMerge(config); - this._typeCheckConfig(config); - return config; - } - _configAfterMerge(config) { - config.container = config.container === false ? document.body : getElement(config.container); - if (typeof config.delay === 'number') { - config.delay = { - show: config.delay, - hide: config.delay - }; - } - if (typeof config.title === 'number') { - config.title = config.title.toString(); - } - if (typeof config.content === 'number') { - config.content = config.content.toString(); - } - return config; - } - _getDelegateConfig() { - const config = {}; - for (const [key, value] of Object.entries(this._config)) { - if (this.constructor.Default[key] !== value) { - config[key] = value; - } - } - config.selector = false; - config.trigger = 'manual'; - - // In the future can be replaced with: - // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) - // `Object.fromEntries(keysWithDifferentValues)` - return config; - } - _disposePopper() { - if (this._popper) { - this._popper.destroy(); - this._popper = null; - } - if (this.tip) { - this.tip.remove(); - this.tip = null; - } - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Tooltip.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - } - - /** - * jQuery - */ - - defineJQueryPlugin(Tooltip); - - /** - * -------------------------------------------------------------------------- - * Bootstrap popover.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$3 = 'popover'; - const SELECTOR_TITLE = '.popover-header'; - const SELECTOR_CONTENT = '.popover-body'; - const Default$2 = { - ...Tooltip.Default, - content: '', - offset: [0, 8], - placement: 'right', - template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div>' + '</div>', - trigger: 'click' - }; - const DefaultType$2 = { - ...Tooltip.DefaultType, - content: '(null|string|element|function)' - }; - - /** - * Class definition - */ - - class Popover extends Tooltip { - // Getters - static get Default() { - return Default$2; - } - static get DefaultType() { - return DefaultType$2; - } - static get NAME() { - return NAME$3; - } - - // Overrides - _isWithContent() { - return this._getTitle() || this._getContent(); - } - - // Private - _getContentForTemplate() { - return { - [SELECTOR_TITLE]: this._getTitle(), - [SELECTOR_CONTENT]: this._getContent() - }; - } - _getContent() { - return this._resolvePossibleFunction(this._config.content); - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Popover.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - } - - /** - * jQuery - */ - - defineJQueryPlugin(Popover); - - /** - * -------------------------------------------------------------------------- - * Bootstrap scrollspy.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$2 = 'scrollspy'; - const DATA_KEY$2 = 'bs.scrollspy'; - const EVENT_KEY$2 = `.${DATA_KEY$2}`; - const DATA_API_KEY = '.data-api'; - const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`; - const EVENT_CLICK = `click${EVENT_KEY$2}`; - const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`; - const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; - const CLASS_NAME_ACTIVE$1 = 'active'; - const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; - const SELECTOR_TARGET_LINKS = '[href]'; - const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; - const SELECTOR_NAV_LINKS = '.nav-link'; - const SELECTOR_NAV_ITEMS = '.nav-item'; - const SELECTOR_LIST_ITEMS = '.list-group-item'; - const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; - const SELECTOR_DROPDOWN = '.dropdown'; - const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle'; - const Default$1 = { - offset: null, - // TODO: v6 @deprecated, keep it for backwards compatibility reasons - rootMargin: '0px 0px -25%', - smoothScroll: false, - target: null, - threshold: [0.1, 0.5, 1] - }; - const DefaultType$1 = { - offset: '(number|null)', - // TODO v6 @deprecated, keep it for backwards compatibility reasons - rootMargin: 'string', - smoothScroll: 'boolean', - target: 'element', - threshold: 'array' - }; - - /** - * Class definition - */ - - class ScrollSpy extends BaseComponent { - constructor(element, config) { - super(element, config); - - // this._element is the observablesContainer and config.target the menu links wrapper - this._targetLinks = new Map(); - this._observableSections = new Map(); - this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; - this._activeTarget = null; - this._observer = null; - this._previousScrollData = { - visibleEntryTop: 0, - parentScrollTop: 0 - }; - this.refresh(); // initialize - } - - // Getters - static get Default() { - return Default$1; - } - static get DefaultType() { - return DefaultType$1; - } - static get NAME() { - return NAME$2; - } - - // Public - refresh() { - this._initializeTargetsAndObservables(); - this._maybeEnableSmoothScroll(); - if (this._observer) { - this._observer.disconnect(); - } else { - this._observer = this._getNewObserver(); - } - for (const section of this._observableSections.values()) { - this._observer.observe(section); - } - } - dispose() { - this._observer.disconnect(); - super.dispose(); - } - - // Private - _configAfterMerge(config) { - // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case - config.target = getElement(config.target) || document.body; - - // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only - config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; - if (typeof config.threshold === 'string') { - config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); - } - return config; - } - _maybeEnableSmoothScroll() { - if (!this._config.smoothScroll) { - return; - } - - // unregister any previous listeners - EventHandler.off(this._config.target, EVENT_CLICK); - EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { - const observableSection = this._observableSections.get(event.target.hash); - if (observableSection) { - event.preventDefault(); - const root = this._rootElement || window; - const height = observableSection.offsetTop - this._element.offsetTop; - if (root.scrollTo) { - root.scrollTo({ - top: height, - behavior: 'smooth' - }); - return; - } - - // Chrome 60 doesn't support `scrollTo` - root.scrollTop = height; - } - }); - } - _getNewObserver() { - const options = { - root: this._rootElement, - threshold: this._config.threshold, - rootMargin: this._config.rootMargin - }; - return new IntersectionObserver(entries => this._observerCallback(entries), options); - } - - // The logic of selection - _observerCallback(entries) { - const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); - const activate = entry => { - this._previousScrollData.visibleEntryTop = entry.target.offsetTop; - this._process(targetElement(entry)); - }; - const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; - const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; - this._previousScrollData.parentScrollTop = parentScrollTop; - for (const entry of entries) { - if (!entry.isIntersecting) { - this._activeTarget = null; - this._clearActiveClass(targetElement(entry)); - continue; - } - const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; - // if we are scrolling down, pick the bigger offsetTop - if (userScrollsDown && entryIsLowerThanPrevious) { - activate(entry); - // if parent isn't scrolled, let's keep the first visible item, breaking the iteration - if (!parentScrollTop) { - return; - } - continue; - } - - // if we are scrolling up, pick the smallest offsetTop - if (!userScrollsDown && !entryIsLowerThanPrevious) { - activate(entry); - } - } - } - _initializeTargetsAndObservables() { - this._targetLinks = new Map(); - this._observableSections = new Map(); - const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target); - for (const anchor of targetLinks) { - // ensure that the anchor has an id and is not disabled - if (!anchor.hash || isDisabled(anchor)) { - continue; - } - const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element); - - // ensure that the observableSection exists & is visible - if (isVisible(observableSection)) { - this._targetLinks.set(decodeURI(anchor.hash), anchor); - this._observableSections.set(anchor.hash, observableSection); - } - } - } - _process(target) { - if (this._activeTarget === target) { - return; - } - this._clearActiveClass(this._config.target); - this._activeTarget = target; - target.classList.add(CLASS_NAME_ACTIVE$1); - this._activateParents(target); - EventHandler.trigger(this._element, EVENT_ACTIVATE, { - relatedTarget: target - }); - } - _activateParents(target) { - // Activate dropdown parents - if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { - SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1); - return; - } - for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) { - // Set triggered links parents as active - // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor - for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) { - item.classList.add(CLASS_NAME_ACTIVE$1); - } - } - } - _clearActiveClass(parent) { - parent.classList.remove(CLASS_NAME_ACTIVE$1); - const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent); - for (const node of activeNodes) { - node.classList.remove(CLASS_NAME_ACTIVE$1); - } - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = ScrollSpy.getOrCreateInstance(this, config); - if (typeof config !== 'string') { - return; - } - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(window, EVENT_LOAD_DATA_API$1, () => { - for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) { - ScrollSpy.getOrCreateInstance(spy); - } - }); - - /** - * jQuery - */ - - defineJQueryPlugin(ScrollSpy); - - /** - * -------------------------------------------------------------------------- - * Bootstrap tab.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME$1 = 'tab'; - const DATA_KEY$1 = 'bs.tab'; - const EVENT_KEY$1 = `.${DATA_KEY$1}`; - const EVENT_HIDE$1 = `hide${EVENT_KEY$1}`; - const EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`; - const EVENT_SHOW$1 = `show${EVENT_KEY$1}`; - const EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`; - const EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`; - const EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`; - const EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`; - const ARROW_LEFT_KEY = 'ArrowLeft'; - const ARROW_RIGHT_KEY = 'ArrowRight'; - const ARROW_UP_KEY = 'ArrowUp'; - const ARROW_DOWN_KEY = 'ArrowDown'; - const HOME_KEY = 'Home'; - const END_KEY = 'End'; - const CLASS_NAME_ACTIVE = 'active'; - const CLASS_NAME_FADE$1 = 'fade'; - const CLASS_NAME_SHOW$1 = 'show'; - const CLASS_DROPDOWN = 'dropdown'; - const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; - const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'; - const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`; - const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'; - const SELECTOR_OUTER = '.nav-item, .list-group-item'; - const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`; - const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6 - const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`; - const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`; - - /** - * Class definition - */ - - class Tab extends BaseComponent { - constructor(element) { - super(element); - this._parent = this._element.closest(SELECTOR_TAB_PANEL); - if (!this._parent) { - return; - // TODO: should throw exception in v6 - // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`) - } - - // Set up initial aria attributes - this._setInitialAttributes(this._parent, this._getChildren()); - EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)); - } - - // Getters - static get NAME() { - return NAME$1; - } - - // Public - show() { - // Shows this elem and deactivate the active sibling if exists - const innerElem = this._element; - if (this._elemIsActive(innerElem)) { - return; - } - - // Search for active tab on same parent to deactivate it - const active = this._getActiveElem(); - const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, { - relatedTarget: innerElem - }) : null; - const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, { - relatedTarget: active - }); - if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) { - return; - } - this._deactivate(active, innerElem); - this._activate(innerElem, active); - } - - // Private - _activate(element, relatedElem) { - if (!element) { - return; - } - element.classList.add(CLASS_NAME_ACTIVE); - this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section - - const complete = () => { - if (element.getAttribute('role') !== 'tab') { - element.classList.add(CLASS_NAME_SHOW$1); - return; - } - element.removeAttribute('tabindex'); - element.setAttribute('aria-selected', true); - this._toggleDropDown(element, true); - EventHandler.trigger(element, EVENT_SHOWN$1, { - relatedTarget: relatedElem - }); - }; - this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); - } - _deactivate(element, relatedElem) { - if (!element) { - return; - } - element.classList.remove(CLASS_NAME_ACTIVE); - element.blur(); - this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too - - const complete = () => { - if (element.getAttribute('role') !== 'tab') { - element.classList.remove(CLASS_NAME_SHOW$1); - return; - } - element.setAttribute('aria-selected', false); - element.setAttribute('tabindex', '-1'); - this._toggleDropDown(element, false); - EventHandler.trigger(element, EVENT_HIDDEN$1, { - relatedTarget: relatedElem - }); - }; - this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); - } - _keydown(event) { - if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) { - return; - } - event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page - event.preventDefault(); - const children = this._getChildren().filter(element => !isDisabled(element)); - let nextActiveElement; - if ([HOME_KEY, END_KEY].includes(event.key)) { - nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]; - } else { - const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key); - nextActiveElement = getNextActiveElement(children, event.target, isNext, true); - } - if (nextActiveElement) { - nextActiveElement.focus({ - preventScroll: true - }); - Tab.getOrCreateInstance(nextActiveElement).show(); - } - } - _getChildren() { - // collection of inner elements - return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent); - } - _getActiveElem() { - return this._getChildren().find(child => this._elemIsActive(child)) || null; - } - _setInitialAttributes(parent, children) { - this._setAttributeIfNotExists(parent, 'role', 'tablist'); - for (const child of children) { - this._setInitialAttributesOnChild(child); - } - } - _setInitialAttributesOnChild(child) { - child = this._getInnerElement(child); - const isActive = this._elemIsActive(child); - const outerElem = this._getOuterElement(child); - child.setAttribute('aria-selected', isActive); - if (outerElem !== child) { - this._setAttributeIfNotExists(outerElem, 'role', 'presentation'); - } - if (!isActive) { - child.setAttribute('tabindex', '-1'); - } - this._setAttributeIfNotExists(child, 'role', 'tab'); - - // set attributes to the related panel too - this._setInitialAttributesOnTargetPanel(child); - } - _setInitialAttributesOnTargetPanel(child) { - const target = SelectorEngine.getElementFromSelector(child); - if (!target) { - return; - } - this._setAttributeIfNotExists(target, 'role', 'tabpanel'); - if (child.id) { - this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`); - } - } - _toggleDropDown(element, open) { - const outerElem = this._getOuterElement(element); - if (!outerElem.classList.contains(CLASS_DROPDOWN)) { - return; - } - const toggle = (selector, className) => { - const element = SelectorEngine.findOne(selector, outerElem); - if (element) { - element.classList.toggle(className, open); - } - }; - toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE); - toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1); - outerElem.setAttribute('aria-expanded', open); - } - _setAttributeIfNotExists(element, attribute, value) { - if (!element.hasAttribute(attribute)) { - element.setAttribute(attribute, value); - } - } - _elemIsActive(elem) { - return elem.classList.contains(CLASS_NAME_ACTIVE); - } - - // Try to get the inner element (usually the .nav-link) - _getInnerElement(elem) { - return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem); - } - - // Try to get the outer element (usually the .nav-item) - _getOuterElement(elem) { - return elem.closest(SELECTOR_OUTER) || elem; - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Tab.getOrCreateInstance(this); - if (typeof config !== 'string') { - return; - } - if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { - throw new TypeError(`No method named "${config}"`); - } - data[config](); - }); - } - } - - /** - * Data API implementation - */ - - EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - if (['A', 'AREA'].includes(this.tagName)) { - event.preventDefault(); - } - if (isDisabled(this)) { - return; - } - Tab.getOrCreateInstance(this).show(); - }); - - /** - * Initialize on focus - */ - EventHandler.on(window, EVENT_LOAD_DATA_API, () => { - for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) { - Tab.getOrCreateInstance(element); - } - }); - /** - * jQuery - */ - - defineJQueryPlugin(Tab); - - /** - * -------------------------------------------------------------------------- - * Bootstrap toast.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - - /** - * Constants - */ - - const NAME = 'toast'; - const DATA_KEY = 'bs.toast'; - const EVENT_KEY = `.${DATA_KEY}`; - const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`; - const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`; - const EVENT_FOCUSIN = `focusin${EVENT_KEY}`; - const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`; - const EVENT_HIDE = `hide${EVENT_KEY}`; - const EVENT_HIDDEN = `hidden${EVENT_KEY}`; - const EVENT_SHOW = `show${EVENT_KEY}`; - const EVENT_SHOWN = `shown${EVENT_KEY}`; - const CLASS_NAME_FADE = 'fade'; - const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility - const CLASS_NAME_SHOW = 'show'; - const CLASS_NAME_SHOWING = 'showing'; - const DefaultType = { - animation: 'boolean', - autohide: 'boolean', - delay: 'number' - }; - const Default = { - animation: true, - autohide: true, - delay: 5000 - }; - - /** - * Class definition - */ - - class Toast extends BaseComponent { - constructor(element, config) { - super(element, config); - this._timeout = null; - this._hasMouseInteraction = false; - this._hasKeyboardInteraction = false; - this._setListeners(); - } - - // Getters - static get Default() { - return Default; - } - static get DefaultType() { - return DefaultType; - } - static get NAME() { - return NAME; - } - - // Public - show() { - const showEvent = EventHandler.trigger(this._element, EVENT_SHOW); - if (showEvent.defaultPrevented) { - return; - } - this._clearTimeout(); - if (this._config.animation) { - this._element.classList.add(CLASS_NAME_FADE); - } - const complete = () => { - this._element.classList.remove(CLASS_NAME_SHOWING); - EventHandler.trigger(this._element, EVENT_SHOWN); - this._maybeScheduleHide(); - }; - this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated - reflow(this._element); - this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING); - this._queueCallback(complete, this._element, this._config.animation); - } - hide() { - if (!this.isShown()) { - return; - } - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE); - if (hideEvent.defaultPrevented) { - return; - } - const complete = () => { - this._element.classList.add(CLASS_NAME_HIDE); // @deprecated - this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW); - EventHandler.trigger(this._element, EVENT_HIDDEN); - }; - this._element.classList.add(CLASS_NAME_SHOWING); - this._queueCallback(complete, this._element, this._config.animation); - } - dispose() { - this._clearTimeout(); - if (this.isShown()) { - this._element.classList.remove(CLASS_NAME_SHOW); - } - super.dispose(); - } - isShown() { - return this._element.classList.contains(CLASS_NAME_SHOW); - } - - // Private - _maybeScheduleHide() { - if (!this._config.autohide) { - return; - } - if (this._hasMouseInteraction || this._hasKeyboardInteraction) { - return; - } - this._timeout = setTimeout(() => { - this.hide(); - }, this._config.delay); - } - _onInteraction(event, isInteracting) { - switch (event.type) { - case 'mouseover': - case 'mouseout': - { - this._hasMouseInteraction = isInteracting; - break; - } - case 'focusin': - case 'focusout': - { - this._hasKeyboardInteraction = isInteracting; - break; - } - } - if (isInteracting) { - this._clearTimeout(); - return; - } - const nextElement = event.relatedTarget; - if (this._element === nextElement || this._element.contains(nextElement)) { - return; - } - this._maybeScheduleHide(); - } - _setListeners() { - EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true)); - EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false)); - EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true)); - EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false)); - } - _clearTimeout() { - clearTimeout(this._timeout); - this._timeout = null; - } - - // Static - static jQueryInterface(config) { - return this.each(function () { - const data = Toast.getOrCreateInstance(this, config); - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - data[config](this); - } - }); - } - } - - /** - * Data API implementation - */ - - enableDismissTrigger(Toast); - - /** - * jQuery - */ - - defineJQueryPlugin(Toast); - - /** - * -------------------------------------------------------------------------- - * Bootstrap index.umd.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - const index_umd = { - Alert, - Button, - Carousel, - Collapse, - Dropdown, - Modal, - Offcanvas, - Popover, - ScrollSpy, - Tab, - Toast, - Tooltip - }; - - return index_umd; - -})); -//# sourceMappingURL=bootstrap.js.map diff --git a/extensions/pagetop-bootsier/static/js/bootstrap.js.map b/extensions/pagetop-bootsier/static/js/bootstrap.js.map deleted file mode 100644 index 062cdd5e..00000000 --- a/extensions/pagetop-bootsier/static/js/bootstrap.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"bootstrap.js","sources":["../../js/src/dom/data.js","../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/manipulator.js","../../js/src/util/config.js","../../js/src/base-component.js","../../js/src/dom/selector-engine.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/util/swipe.js","../../js/src/carousel.js","../../js/src/collapse.js","../../js/src/dropdown.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/util/scrollbar.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.js","../../js/src/util/template-factory.js","../../js/src/tooltip.js","../../js/src/popover.js","../../js/src/scrollspy.js","../../js/src/tab.js","../../js/src/toast.js","../../js/index.umd.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1_000_000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`)\n }\n\n return selector\n}\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`\n }\n\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false\n }\n\n if (typeof object.jquery !== 'undefined') {\n object = object[0]\n }\n\n return typeof object.nodeType !== 'undefined'\n}\n\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object\n }\n\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object))\n }\n\n return null\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])')\n\n if (!closedDetails) {\n return elementIsVisible\n }\n\n if (closedDetails !== element) {\n const summary = element.closest('summary')\n if (summary && summary.parentNode !== closedDetails) {\n return false\n }\n\n if (summary === null) {\n return false\n }\n }\n\n return elementIsVisible\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight // eslint-disable-line no-unused-expressions\n}\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback()\n }\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]\n }\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n defineJQueryPlugin,\n execute,\n executeAfterTransition,\n findShadowRoot,\n getElement,\n getjQuery,\n getNextActiveElement,\n getTransitionDurationFromElement,\n getUID,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n onDOMContentLoaded,\n parseSelector,\n reflow,\n triggerTransitionEnd,\n toType\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index.js'\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\n\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getElementEvents(element) {\n const uid = makeEventUid(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, { delegateTarget: element })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue\n }\n\n hydrateObj(event, { delegateTarget: target })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events)\n .find(event => event.callable === callable && event.delegationSelector === delegationSelector)\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string'\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : (handler || delegationFunction)\n let typeEvent = getTypeEvent(originalTypeEvent)\n\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent\n }\n\n return [isDelegated, callable, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n callable = wrapFunction(callable)\n }\n\n const events = getElementEvents(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)\n\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff\n\n return\n }\n\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = isDelegated ?\n bootstrapDelegationHandler(element, handler, callable) :\n bootstrapHandler(element, callable)\n\n fn.delegationSelector = isDelegated ? handler : null\n fn.callable = callable\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, isDelegated)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false)\n },\n\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getElementEvents(element)\n const storeElementEvent = events[typeEvent] || {}\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return\n }\n\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)\n return\n }\n\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n }\n }\n\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n\n let jQueryEvent = null\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value\n } catch {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value\n }\n })\n }\n }\n\n return obj\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport { isElement, toType } from './index.js'\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {}\n }\n\n static get DefaultType() {\n return {}\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n return config\n }\n\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n }\n }\n\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property]\n const valueType = isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n }\n }\n}\n\nexport default Config\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data.js'\nimport EventHandler from './dom/event-handler.js'\nimport Config from './util/config.js'\nimport { executeAfterTransition, getElement } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.8'\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super()\n\n element = getElement(element)\n if (!element) {\n return\n }\n\n this._element = element\n this._config = this._getConfig(config)\n\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null\n }\n }\n\n // Private\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`\n }\n}\n\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible, parseSelector } from '../util/index.js'\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`\n }\n\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null\n }\n\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null\n}\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n let ancestor = element.parentNode.closest(selector)\n\n while (ancestor) {\n parents.push(ancestor)\n ancestor = ancestor.parentNode.closest(selector)\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n },\n\n getSelectorFromElement(element) {\n const selector = getSelector(element)\n\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null\n }\n\n return null\n },\n\n getElementFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.findOne(selector) : null\n },\n\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.find(selector) : []\n }\n}\n\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isDisabled } from './index.js'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'swipe'\nconst EVENT_KEY = '.bs.swipe'\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n}\n\nconst DefaultType = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n}\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super()\n this._element = element\n\n if (!element || !Swipe.isSupported()) {\n return\n }\n\n this._config = this._getConfig(config)\n this._deltaX = 0\n this._supportPointerEvents = Boolean(window.PointerEvent)\n this._initEvents()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY)\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX\n\n return\n }\n\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX\n }\n }\n\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX\n }\n\n this._handleSwipe()\n execute(this._config.endCallback)\n }\n\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this._deltaX\n }\n\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX)\n\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltaX / this._deltaX\n\n this._deltaX = 0\n\n if (!direction) {\n return\n }\n\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)\n }\n\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))\n }\n }\n\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n }\n}\n\nexport default Swipe\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getNextActiveElement,\n isRTL,\n isVisible,\n reflow,\n triggerTransitionEnd\n} from './util/index.js'\nimport Swipe from './util/swipe.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)', // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._interval = null\n this._activeElement = null\n this._isSliding = false\n this.touchTimeout = null\n this._swipeHelper = null\n\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._addEventListeners()\n\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element)\n }\n\n this._clearInterval()\n }\n\n cycle() {\n this._clearInterval()\n this._updateInterval()\n\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)\n }\n\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle())\n return\n }\n\n this.cycle()\n }\n\n to(index) {\n const items = this._getItems()\n if (index > items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n const activeIndex = this._getItemIndex(this._getActive())\n if (activeIndex === index) {\n return\n }\n\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV\n\n this._slide(order, items[index])\n }\n\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose()\n }\n\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())\n EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())\n }\n\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())\n }\n\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n }\n\n this._swipeHelper = new Swipe(this._element, swipeConfig)\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(this._directionToOrder(direction))\n }\n }\n\n _getItemIndex(element) {\n return this._getItems().indexOf(element)\n }\n\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return\n }\n\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement)\n\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)\n newActiveIndicator.setAttribute('aria-current', 'true')\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || this._getActive()\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n this._config.interval = elementInterval || this._config.defaultInterval\n }\n\n _slide(order, element = null) {\n if (this._isSliding) {\n return\n }\n\n const activeElement = this._getActive()\n const isNext = order === ORDER_NEXT\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)\n\n if (nextElement === activeElement) {\n return\n }\n\n const nextElementIndex = this._getItemIndex(nextElement)\n\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n })\n }\n\n const slideEvent = triggerEvent(EVENT_SLIDE)\n\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return\n }\n\n const isCycling = Boolean(this._interval)\n this.pause()\n\n this._isSliding = true\n\n this._setActiveIndicatorElement(nextElementIndex)\n this._activeElement = nextElement\n\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n triggerEvent(EVENT_SLID)\n }\n\n this._queueCallback(completeCallBack, activeElement, this._isAnimated())\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE)\n }\n\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n }\n\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element)\n }\n\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n }\n\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config)\n\n if (typeof config === 'number') {\n data.to(config)\n return\n }\n\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n event.preventDefault()\n\n const carousel = Carousel.getOrCreateInstance(target)\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n carousel.to(slideIndex)\n carousel._maybeEnableCycle()\n return\n }\n\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next()\n carousel._maybeEnableCycle()\n return\n }\n\n carousel.prev()\n carousel._maybeEnableCycle()\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getElement,\n reflow\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\nconst Default = {\n parent: null,\n toggle: true\n}\n\nconst DefaultType = {\n parent: '(null|element)',\n toggle: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isTransitioning = false\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElement => foundElement === this._element)\n\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let activeChildren = []\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)\n .filter(element => element !== this._element)\n .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))\n }\n\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n for (const activeInstance of activeChildren) {\n activeInstance.hide()\n }\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger)\n\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n // Private\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)\n\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n }\n }\n\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)\n element.setAttribute('aria-expanded', isOpen)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n execute,\n getElement,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center'\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)'\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR = '.navbar'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\nconst PLACEMENT_TOPCENTER = 'top'\nconst PLACEMENT_BOTTOMCENTER = 'bottom'\n\nconst Default = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n}\n\nconst DefaultType = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n}\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._popper = null\n this._parent = this._element.parentNode // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.findOne(SELECTOR_MENU, this._parent)\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._createPopper()\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = super._getConfig(config)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = this._parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n }\n\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getPlacement() {\n const parentDropdown = this._parent\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {\n return\n }\n\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)\n\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle)\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n const relatedTarget = { relatedTarget: context._element }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName)\n const isEscapeEvent = event.key === ESCAPE_KEY\n const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)\n\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return\n }\n\n if (isInput && !isEscapeEvent) {\n return\n }\n\n event.preventDefault()\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?\n this :\n (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))\n\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (isUpOrDownEvent) {\n event.stopPropagation()\n instance.show()\n instance._selectMenuItem(event)\n return\n }\n\n if (instance._isShown()) { // else is escape and we check if it is shown\n event.stopPropagation()\n instance.hide()\n getToggleButton.focus()\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport {\n execute, executeAfterTransition, getElement, reflow\n} from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nconst Default = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n}\n\nconst DefaultType = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n}\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n const element = this._getElement()\n if (this._config.isAnimated) {\n reflow(element)\n }\n\n element.classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n const element = this._getElement()\n this._config.rootElement.append(element)\n\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin, isRTL, isVisible, reflow\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin,\n isDisabled,\n isVisible\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\nconst CLASS_NAME_HIDING = 'hiding'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate()\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.add(CLASS_NAME_HIDING)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n return\n }\n\n this.hide()\n }\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop)\n\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n })\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show()\n }\n})\n\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide()\n }\n }\n})\n\nenableDismissTrigger(Offcanvas)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n// js-docs-end allow-list\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n .some(regex => regex.test(attributeName))\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\nimport { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'\nimport { execute, getElement, isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'TemplateFactory'\n\nconst Default = {\n allowList: DefaultAllowlist,\n content: {}, // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '<div></div>'\n}\n\nconst DefaultType = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n}\n\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n}\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content)\n .map(config => this._resolvePossibleFunction(config))\n .filter(Boolean)\n }\n\n hasContent() {\n return this.getContent().length > 0\n }\n\n changeContent(content) {\n this._checkContent(content)\n this._config.content = { ...this._config.content, ...content }\n return this\n }\n\n toHtml() {\n const templateWrapper = document.createElement('div')\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template)\n\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector)\n }\n\n const template = templateWrapper.children[0]\n const extraClass = this._resolvePossibleFunction(this._config.extraClass)\n\n if (extraClass) {\n template.classList.add(...extraClass.split(' '))\n }\n\n return template\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config)\n this._checkContent(config.content)\n }\n\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({ selector, entry: content }, DefaultContentType)\n }\n }\n\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!templateElement) {\n return\n }\n\n content = this._resolvePossibleFunction(content)\n\n if (!content) {\n templateElement.remove()\n return\n }\n\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement)\n return\n }\n\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content)\n return\n }\n\n templateElement.textContent = content\n }\n\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [undefined, this])\n }\n\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = ''\n templateElement.append(element)\n return\n }\n\n templateElement.textContent = element.textContent\n }\n}\n\nexport default TemplateFactory\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport {\n defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop\n} from './util/index.js'\nimport { DefaultAllowlist } from './util/sanitizer.js'\nimport TemplateFactory from './util/template-factory.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\nconst EVENT_HIDE = 'hide'\nconst EVENT_HIDDEN = 'hidden'\nconst EVENT_SHOW = 'show'\nconst EVENT_SHOWN = 'shown'\nconst EVENT_INSERTED = 'inserted'\nconst EVENT_CLICK = 'click'\nconst EVENT_FOCUSIN = 'focusin'\nconst EVENT_FOCUSOUT = 'focusout'\nconst EVENT_MOUSEENTER = 'mouseenter'\nconst EVENT_MOUSELEAVE = 'mouseleave'\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '<div class=\"tooltip\" role=\"tooltip\">' +\n '<div class=\"tooltip-arrow\"></div>' +\n '<div class=\"tooltip-inner\"></div>' +\n '</div>',\n title: '',\n trigger: 'hover focus'\n}\n\nconst DefaultType = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n}\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org/docs/v2/)')\n }\n\n super(element, config)\n\n // Private\n this._isEnabled = true\n this._timeout = 0\n this._isHovered = null\n this._activeTrigger = {}\n this._popper = null\n this._templateFactory = null\n this._newContent = null\n\n // Protected\n this.tip = null\n\n this._setListeners()\n\n if (!this._config.selector) {\n this._fixTitle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle() {\n if (!this._isEnabled) {\n return\n }\n\n if (this._isShown()) {\n this._leave()\n return\n }\n\n this._enter()\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this._isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper()\n\n const tip = this._getTipElement()\n\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'))\n\n const { container } = this._config\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))\n }\n\n this._popper = this._createPopper(tip)\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))\n\n if (this._isHovered === false) {\n this._leave()\n }\n\n this._isHovered = false\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n hide() {\n if (!this._isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const tip = this._getTipElement()\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n this._isHovered = null // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (!this._isHovered) {\n this._disposePopper()\n }\n\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n update() {\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle())\n }\n\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())\n }\n\n return this.tip\n }\n\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml()\n\n // TODO: remove this check in v6\n if (!tip) {\n return null\n }\n\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`)\n\n const tipId = getUID(this.constructor.NAME).toString()\n\n tip.setAttribute('id', tipId)\n\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n return tip\n }\n\n setContent(content) {\n this._newContent = content\n if (this._isShown()) {\n this._disposePopper()\n this.show()\n }\n }\n\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content)\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n })\n }\n\n return this._templateFactory\n }\n\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n }\n }\n\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _isAnimated() {\n return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))\n }\n\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)\n }\n\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element])\n const attachment = AttachmentMap[placement.toUpperCase()]\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element, this._element])\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement)\n }\n }\n ]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])\n }\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[TRIGGER_CLICK] = !(context._isShown() && context._activeTrigger[TRIGGER_CLICK])\n context.toggle()\n })\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSEENTER) :\n this.constructor.eventName(EVENT_FOCUSIN)\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSELEAVE) :\n this.constructor.eventName(EVENT_FOCUSOUT)\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true\n context._enter()\n })\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =\n context._element.contains(event.relatedTarget)\n\n context._leave()\n })\n }\n }\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n\n if (!title) {\n return\n }\n\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title')\n }\n\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true\n return\n }\n\n this._isHovered = true\n\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show()\n }\n }, this._config.delay.show)\n }\n\n _leave() {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n this._isHovered = false\n\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide()\n }\n }, this._config.delay.hide)\n }\n\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout)\n this._timeout = setTimeout(handler, timeout)\n }\n\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true)\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute]\n }\n }\n\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value\n }\n }\n\n config.selector = false\n config.trigger = 'manual'\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n\n if (this.tip) {\n this.tip.remove()\n this.tip = null\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Tooltip from './tooltip.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'popover'\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\nconst Default = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '<div class=\"popover\" role=\"tooltip\">' +\n '<div class=\"popover-arrow\"></div>' +\n '<h3 class=\"popover-header\"></h3>' +\n '<div class=\"popover-body\"></div>' +\n '</div>',\n trigger: 'click'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n}\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent()\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n }\n }\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin, getElement, isDisabled, isVisible\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_CLICK = `click${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_TARGET_LINKS = '[href]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst Default = {\n offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n}\n\nconst DefaultType = {\n offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n}\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map()\n this._observableSections = new Map()\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element\n this._activeTarget = null\n this._observer = null\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n }\n this.refresh() // initialize\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables()\n this._maybeEnableSmoothScroll()\n\n if (this._observer) {\n this._observer.disconnect()\n } else {\n this._observer = this._getNewObserver()\n }\n\n for (const section of this._observableSections.values()) {\n this._observer.observe(section)\n }\n }\n\n dispose() {\n this._observer.disconnect()\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin\n\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))\n }\n\n return config\n }\n\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK)\n\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash)\n if (observableSection) {\n event.preventDefault()\n const root = this._rootElement || window\n const height = observableSection.offsetTop - this._element.offsetTop\n if (root.scrollTo) {\n root.scrollTo({ top: height, behavior: 'smooth' })\n return\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height\n }\n })\n }\n\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n }\n\n return new IntersectionObserver(entries => this._observerCallback(entries), options)\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop\n this._process(targetElement(entry))\n }\n\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop\n this._previousScrollData.parentScrollTop = parentScrollTop\n\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null\n this._clearActiveClass(targetElement(entry))\n\n continue\n }\n\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry)\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return\n }\n\n continue\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry)\n }\n }\n }\n\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map()\n this._observableSections = new Map()\n\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)\n\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue\n }\n\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor)\n this._observableSections.set(anchor.hash, observableSection)\n }\n }\n }\n\n _process(target) {\n if (this._activeTarget === target) {\n return\n }\n\n this._clearActiveClass(this._config.target)\n this._activeTarget = target\n target.classList.add(CLASS_NAME_ACTIVE)\n this._activateParents(target)\n\n EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })\n }\n\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n return\n }\n\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n item.classList.add(CLASS_NAME_ACTIVE)\n }\n }\n }\n\n _clearActiveClass(parent) {\n parent.classList.remove(CLASS_NAME_ACTIVE)\n\n const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)\n for (const node of activeNodes) {\n node.classList.remove(CLASS_NAME_ACTIVE)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n ScrollSpy.getOrCreateInstance(spy)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tab.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}`\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst HOME_KEY = 'Home'\nconst END_KEY = 'End'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_DROPDOWN = 'dropdown'\n\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu'\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`\n\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]'\nconst SELECTOR_OUTER = '.nav-item, .list-group-item'\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]' // TODO: could only be `tab` in v6\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`\n\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`\n\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n constructor(element) {\n super(element)\n this._parent = this._element.closest(SELECTOR_TAB_PANEL)\n\n if (!this._parent) {\n return\n // TODO: should throw exception in v6\n // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n }\n\n // Set up initial aria attributes\n this._setInitialAttributes(this._parent, this._getChildren())\n\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n show() { // Shows this elem and deactivate the active sibling if exists\n const innerElem = this._element\n if (this._elemIsActive(innerElem)) {\n return\n }\n\n // Search for active tab on same parent to deactivate it\n const active = this._getActiveElem()\n\n const hideEvent = active ?\n EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :\n null\n\n const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })\n\n if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {\n return\n }\n\n this._deactivate(active, innerElem)\n this._activate(innerElem, active)\n }\n\n // Private\n _activate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n\n this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.add(CLASS_NAME_SHOW)\n return\n }\n\n element.removeAttribute('tabindex')\n element.setAttribute('aria-selected', true)\n this._toggleDropDown(element, true)\n EventHandler.trigger(element, EVENT_SHOWN, {\n relatedTarget: relatedElem\n })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _deactivate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.remove(CLASS_NAME_ACTIVE)\n element.blur()\n\n this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.remove(CLASS_NAME_SHOW)\n return\n }\n\n element.setAttribute('aria-selected', false)\n element.setAttribute('tabindex', '-1')\n this._toggleDropDown(element, false)\n EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _keydown(event) {\n if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {\n return\n }\n\n event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n event.preventDefault()\n\n const children = this._getChildren().filter(element => !isDisabled(element))\n let nextActiveElement\n\n if ([HOME_KEY, END_KEY].includes(event.key)) {\n nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]\n } else {\n const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)\n nextActiveElement = getNextActiveElement(children, event.target, isNext, true)\n }\n\n if (nextActiveElement) {\n nextActiveElement.focus({ preventScroll: true })\n Tab.getOrCreateInstance(nextActiveElement).show()\n }\n }\n\n _getChildren() { // collection of inner elements\n return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)\n }\n\n _getActiveElem() {\n return this._getChildren().find(child => this._elemIsActive(child)) || null\n }\n\n _setInitialAttributes(parent, children) {\n this._setAttributeIfNotExists(parent, 'role', 'tablist')\n\n for (const child of children) {\n this._setInitialAttributesOnChild(child)\n }\n }\n\n _setInitialAttributesOnChild(child) {\n child = this._getInnerElement(child)\n const isActive = this._elemIsActive(child)\n const outerElem = this._getOuterElement(child)\n child.setAttribute('aria-selected', isActive)\n\n if (outerElem !== child) {\n this._setAttributeIfNotExists(outerElem, 'role', 'presentation')\n }\n\n if (!isActive) {\n child.setAttribute('tabindex', '-1')\n }\n\n this._setAttributeIfNotExists(child, 'role', 'tab')\n\n // set attributes to the related panel too\n this._setInitialAttributesOnTargetPanel(child)\n }\n\n _setInitialAttributesOnTargetPanel(child) {\n const target = SelectorEngine.getElementFromSelector(child)\n\n if (!target) {\n return\n }\n\n this._setAttributeIfNotExists(target, 'role', 'tabpanel')\n\n if (child.id) {\n this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)\n }\n }\n\n _toggleDropDown(element, open) {\n const outerElem = this._getOuterElement(element)\n if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n return\n }\n\n const toggle = (selector, className) => {\n const element = SelectorEngine.findOne(selector, outerElem)\n if (element) {\n element.classList.toggle(className, open)\n }\n }\n\n toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)\n toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)\n outerElem.setAttribute('aria-expanded', open)\n }\n\n _setAttributeIfNotExists(element, attribute, value) {\n if (!element.hasAttribute(attribute)) {\n element.setAttribute(attribute, value)\n }\n }\n\n _elemIsActive(elem) {\n return elem.classList.contains(CLASS_NAME_ACTIVE)\n }\n\n // Try to get the inner element (usually the .nav-link)\n _getInnerElement(elem) {\n return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)\n }\n\n // Try to get the outer element (usually the .nav-item)\n _getOuterElement(elem) {\n return elem.closest(SELECTOR_OUTER) || elem\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n Tab.getOrCreateInstance(this).show()\n})\n\n/**\n * Initialize on focus\n */\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n Tab.getOrCreateInstance(element)\n }\n})\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap toast.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin, reflow } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n hide() {\n if (!this.isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this.isShown()) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n isShown() {\n return this._element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout': {\n this._hasMouseInteraction = isInteracting\n break\n }\n\n case 'focusin':\n case 'focusout': {\n this._hasKeyboardInteraction = isInteracting\n break\n }\n\n default: {\n break\n }\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Toast)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap index.umd.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Alert from './src/alert.js'\nimport Button from './src/button.js'\nimport Carousel from './src/carousel.js'\nimport Collapse from './src/collapse.js'\nimport Dropdown from './src/dropdown.js'\nimport Modal from './src/modal.js'\nimport Offcanvas from './src/offcanvas.js'\nimport Popover from './src/popover.js'\nimport ScrollSpy from './src/scrollspy.js'\nimport Tab from './src/tab.js'\nimport Toast from './src/toast.js'\nimport Tooltip from './src/tooltip.js'\n\nexport default {\n Alert,\n Button,\n Carousel,\n Collapse,\n Dropdown,\n Modal,\n Offcanvas,\n Popover,\n ScrollSpy,\n Tab,\n Toast,\n Tooltip\n}\n"],"names":["elementMap","Map","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete","MAX_UID","MILLISECONDS_MULTIPLIER","TRANSITION_END","parseSelector","selector","window","CSS","escape","replace","match","id","toType","object","undefined","Object","prototype","toString","call","toLowerCase","getUID","prefix","Math","floor","random","document","getElementById","getTransitionDurationFromElement","transitionDuration","transitionDelay","getComputedStyle","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","getElement","length","querySelector","isVisible","getClientRects","elementIsVisible","getPropertyValue","closedDetails","closest","summary","parentNode","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","noop","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","execute","possibleCallback","args","defaultValue","executeAfterTransition","transitionElement","waitForTransition","durationPadding","emulatedDuration","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","listLength","index","indexOf","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","nativeEvents","Set","makeEventUid","uid","getElementEvents","bootstrapHandler","event","hydrateObj","delegateTarget","oneOff","EventHandler","off","type","apply","bootstrapDelegationHandler","domElements","querySelectorAll","domElement","findHandler","events","callable","delegationSelector","values","find","normalizeParameters","originalTypeEvent","delegationFunction","isDelegated","typeEvent","getTypeEvent","addHandler","wrapFunction","relatedTarget","handlers","previousFunction","removeHandler","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","handlerKey","entries","includes","on","one","inNamespace","isNamespace","startsWith","elementEvent","slice","keyHandlers","trigger","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","evt","cancelable","preventDefault","obj","meta","value","_unused","defineProperty","configurable","normalizeData","JSON","parse","decodeURIComponent","normalizeDataKey","chr","Manipulator","setDataAttribute","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","dataset","filter","pureKey","charAt","getDataAttribute","Config","Default","DefaultType","Error","_getConfig","config","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","jsonConfig","constructor","configTypes","property","expectedTypes","valueType","RegExp","test","TypeError","toUpperCase","VERSION","BaseComponent","_element","_config","Data","DATA_KEY","dispose","EVENT_KEY","propertyName","getOwnPropertyNames","_queueCallback","isAnimated","getInstance","getOrCreateInstance","eventName","getSelector","hrefAttribute","trim","map","sel","join","SelectorEngine","concat","Element","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","el","getSelectorFromElement","getElementFromSelector","getMultipleElementsFromSelector","enableDismissTrigger","component","method","clickEvent","tagName","EVENT_CLOSE","EVENT_CLOSED","CLASS_NAME_FADE","CLASS_NAME_SHOW","Alert","close","closeEvent","_destroyElement","each","data","DATA_API_KEY","CLASS_NAME_ACTIVE","SELECTOR_DATA_TOGGLE","EVENT_CLICK_DATA_API","Button","toggle","button","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","POINTER_TYPE_TOUCH","POINTER_TYPE_PEN","CLASS_NAME_POINTER_EVENT","SWIPE_THRESHOLD","endCallback","leftCallback","rightCallback","Swipe","isSupported","_deltaX","_supportPointerEvents","PointerEvent","_initEvents","_start","touches","clientX","_eventIsPointerPenTouch","_end","_handleSwipe","_move","absDeltaX","abs","direction","add","pointerType","navigator","maxTouchPoints","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","TOUCHEVENT_COMPAT_WAIT","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_DRAG_START","EVENT_LOAD_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_SLIDE","CLASS_NAME_END","CLASS_NAME_START","CLASS_NAME_NEXT","CLASS_NAME_PREV","SELECTOR_ACTIVE","SELECTOR_ITEM","SELECTOR_ACTIVE_ITEM","SELECTOR_ITEM_IMG","SELECTOR_INDICATORS","SELECTOR_DATA_SLIDE","SELECTOR_DATA_RIDE","KEY_TO_DIRECTION","interval","keyboard","pause","ride","touch","wrap","Carousel","_interval","_activeElement","_isSliding","touchTimeout","_swipeHelper","_indicatorsElement","_addEventListeners","cycle","_slide","nextWhenVisible","hidden","_clearInterval","_updateInterval","setInterval","_maybeEnableCycle","to","items","_getItems","activeIndex","_getItemIndex","_getActive","order","defaultInterval","_keydown","_addTouchEventListeners","img","endCallBack","clearTimeout","swipeConfig","_directionToOrder","_setActiveIndicatorElement","activeIndicator","newActiveIndicator","elementInterval","parseInt","isNext","nextElement","nextElementIndex","triggerEvent","_orderToDirection","slideEvent","isCycling","directionalClassName","orderClassName","completeCallBack","_isAnimated","clearInterval","carousel","slideIndex","carousels","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_COLLAPSED","CLASS_NAME_DEEPER_CHILDREN","CLASS_NAME_HORIZONTAL","WIDTH","HEIGHT","SELECTOR_ACTIVES","parent","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElement","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activeChildren","_getFirstLevelChildren","startEvent","activeInstance","dimension","_getDimension","style","complete","capitalizedDimension","scrollSize","getBoundingClientRect","selected","triggerArray","isOpen","ESCAPE_KEY","TAB_KEY","ARROW_UP_KEY","ARROW_DOWN_KEY","RIGHT_MOUSE_BUTTON","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","CLASS_NAME_DROPUP","CLASS_NAME_DROPEND","CLASS_NAME_DROPSTART","CLASS_NAME_DROPUP_CENTER","CLASS_NAME_DROPDOWN_CENTER","SELECTOR_DATA_TOGGLE_SHOWN","SELECTOR_MENU","SELECTOR_NAVBAR","SELECTOR_NAVBAR_NAV","SELECTOR_VISIBLE_ITEMS","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","PLACEMENT_TOPCENTER","PLACEMENT_BOTTOMCENTER","autoClose","boundary","display","offset","popperConfig","reference","Dropdown","_popper","_parent","_menu","_inNavbar","_detectNavbar","showEvent","_createPopper","focus","_completeHide","destroy","update","hideEvent","Popper","referenceElement","_getPopperConfig","createPopper","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","placement","modifiers","options","enabled","_selectMenuItem","clearMenus","openToggles","context","composedPath","isMenuTarget","dataApiKeydownHandler","isInput","isEscapeEvent","isUpOrDownEvent","getToggleButton","stopPropagation","EVENT_MOUSEDOWN","className","clickCallback","rootElement","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_NAV_FORWARD","TAB_NAV_BACKWARD","autofocus","trapElement","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","elements","shiftKey","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","getWidth","documentWidth","clientWidth","innerWidth","width","_disableOverFlow","_setElementAttributes","calculatedValue","reset","_resetElementAttributes","isOverflowing","_saveInitialAttribute","overflow","styleProperty","scrollbarWidth","manipulationCallBack","setProperty","_applyManipulationCallback","actualValue","removeProperty","callBack","EVENT_HIDE_PREVENTED","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","CLASS_NAME_OPEN","CLASS_NAME_STATIC","OPEN_SELECTOR","SELECTOR_DIALOG","SELECTOR_MODAL_BODY","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_scrollBar","_adjustDialog","_showElement","_hideModal","handleUpdate","scrollTop","modalBody","transitionComplete","_triggerBackdropTransition","event2","_resetAdjustments","isModalOverflowing","scrollHeight","clientHeight","initialOverflowY","overflowY","isBodyOverflowing","paddingLeft","paddingRight","alreadyOpen","CLASS_NAME_SHOWING","CLASS_NAME_HIDING","CLASS_NAME_BACKDROP","scroll","Offcanvas","blur","completeCallback","position","ARIA_ATTRIBUTE_PATTERN","DefaultAllowlist","a","area","b","br","col","code","dd","div","dl","dt","em","hr","h1","h2","h3","h4","h5","h6","i","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","uriAttributes","SAFE_URL_PATTERN","allowedAttribute","attribute","allowedAttributeList","attributeName","nodeName","nodeValue","attributeRegex","some","regex","sanitizeHtml","unsafeHtml","allowList","sanitizeFunction","domParser","DOMParser","createdDocument","parseFromString","elementName","attributeList","allowedAttributes","innerHTML","content","extraClass","html","sanitize","sanitizeFn","template","DefaultContentType","entry","TemplateFactory","getContent","_resolvePossibleFunction","hasContent","changeContent","_checkContent","toHtml","templateWrapper","_maybeSanitize","text","_setContent","arg","templateElement","_putElementInTemplate","textContent","DISALLOWED_ATTRIBUTES","CLASS_NAME_MODAL","SELECTOR_TOOLTIP_INNER","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","TRIGGER_CLICK","TRIGGER_MANUAL","EVENT_INSERTED","EVENT_CLICK","EVENT_FOCUSOUT","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","animation","container","customClass","delay","fallbackPlacements","title","Tooltip","_isEnabled","_timeout","_isHovered","_activeTrigger","_templateFactory","_newContent","tip","_setListeners","_fixTitle","enable","disable","toggleEnabled","_leave","_enter","_hideModalHandler","_disposePopper","_isWithContent","shadowRoot","isInTheDom","ownerDocument","_getTipElement","_isWithActiveTrigger","_getTitle","_createTipElement","_getContentForTemplate","_getTemplateFactory","tipId","setContent","_initializeOnDelegatedTarget","_getDelegateConfig","attachment","phase","state","triggers","eventIn","eventOut","_setTimeout","timeout","dataAttributes","dataAttribute","SELECTOR_TITLE","SELECTOR_CONTENT","Popover","_getContent","EVENT_ACTIVATE","CLASS_NAME_DROPDOWN_ITEM","SELECTOR_DATA_SPY","SELECTOR_TARGET_LINKS","SELECTOR_NAV_LIST_GROUP","SELECTOR_NAV_LINKS","SELECTOR_NAV_ITEMS","SELECTOR_LIST_ITEMS","SELECTOR_LINK_ITEMS","SELECTOR_DROPDOWN","SELECTOR_DROPDOWN_TOGGLE","rootMargin","smoothScroll","threshold","ScrollSpy","_targetLinks","_observableSections","_rootElement","_activeTarget","_observer","_previousScrollData","visibleEntryTop","parentScrollTop","refresh","_initializeTargetsAndObservables","_maybeEnableSmoothScroll","disconnect","_getNewObserver","section","observe","observableSection","hash","height","offsetTop","scrollTo","top","behavior","IntersectionObserver","_observerCallback","targetElement","_process","userScrollsDown","isIntersecting","_clearActiveClass","entryIsLowerThanPrevious","targetLinks","anchor","decodeURI","_activateParents","listGroup","item","activeNodes","node","spy","HOME_KEY","END_KEY","CLASS_DROPDOWN","SELECTOR_DROPDOWN_MENU","NOT_SELECTOR_DROPDOWN_TOGGLE","SELECTOR_TAB_PANEL","SELECTOR_OUTER","SELECTOR_INNER","SELECTOR_INNER_ELEM","SELECTOR_DATA_TOGGLE_ACTIVE","Tab","_setInitialAttributes","_getChildren","innerElem","_elemIsActive","active","_getActiveElem","_deactivate","_activate","relatedElem","_toggleDropDown","nextActiveElement","preventScroll","_setAttributeIfNotExists","_setInitialAttributesOnChild","_getInnerElement","isActive","outerElem","_getOuterElement","_setInitialAttributesOnTargetPanel","open","EVENT_MOUSEOVER","EVENT_MOUSEOUT","CLASS_NAME_HIDE","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","isShown","_onInteraction","isInteracting"],"mappings":"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"} \ No newline at end of file diff --git a/extensions/pagetop-bootsier/static/scss/_customs.scss b/extensions/pagetop-bootsier/static/scss/_customs.scss deleted file mode 100644 index 988d7055..00000000 --- a/extensions/pagetop-bootsier/static/scss/_customs.scss +++ /dev/null @@ -1,108 +0,0 @@ -// Enable CSS Grid -$enable-grid-classes: false; -$enable-cssgrid: true; - -// Opacity -.bg-opacity-0 { - --bs-bg-opacity: 0; -} - -.border-opacity-0 { - --bs-border-opacity: 0; -} - -.text-opacity-0 { - --bs-text-opacity: 0; -} -.text-opacity-10 { - --bs-text-opacity: 0.1; -} - -// Extending utilities -$utilities: map-merge( - $utilities, - ( - // Individual border widths - "border-top": ( - property: border-top-width, - class: border-top, - values: $border-widths - ), - "border-end": ( - property: border-right-width, - class: border-end, - values: $border-widths - ), - "border-bottom": ( - property: border-bottom-width, - class: border-bottom, - values: $border-widths - ), - "border-start": ( - property: border-left-width, - class: border-start, - values: $border-widths - ), - // Individual rounded values - "rounded-top-start": ( - property: border-top-left-radius, - class: rounded-top-start, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-top-end": ( - property: border-top-right-radius, - class: rounded-top-end, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-bottom-start": ( - property: border-bottom-left-radius, - class: rounded-bottom-start, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-bottom-end": ( - property: border-bottom-right-radius, - class: rounded-bottom-end, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - ) -); diff --git a/extensions/pagetop-bootsier/static/scss/bootsier.scss b/extensions/pagetop-bootsier/static/scss/bootsier.scss deleted file mode 100644 index 0d52046a..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootsier.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import "bootstrap-5.3.8/mixins/banner"; -@include bsBanner(""); - - -// scss-docs-start import-stack -// Configuration -@import "bootstrap-5.3.8/functions"; -@import "bootstrap-5.3.8/variables"; -@import "bootstrap-5.3.8/variables-dark"; -@import "bootstrap-5.3.8/maps"; -@import "bootstrap-5.3.8/mixins"; -@import "bootstrap-5.3.8/utilities"; - -// Layout & components -@import "bootstrap-5.3.8/root"; -@import "bootstrap-5.3.8/reboot"; -@import "bootstrap-5.3.8/type"; -@import "bootstrap-5.3.8/images"; -@import "bootstrap-5.3.8/containers"; -@import "bootstrap-5.3.8/grid"; -@import "bootstrap-5.3.8/tables"; -@import "bootstrap-5.3.8/forms"; -@import "bootstrap-5.3.8/buttons"; -@import "bootstrap-5.3.8/transitions"; -@import "bootstrap-5.3.8/dropdown"; -@import "bootstrap-5.3.8/button-group"; -@import "bootstrap-5.3.8/nav"; -@import "bootstrap-5.3.8/navbar"; -@import "bootstrap-5.3.8/card"; -@import "bootstrap-5.3.8/accordion"; -@import "bootstrap-5.3.8/breadcrumb"; -@import "bootstrap-5.3.8/pagination"; -@import "bootstrap-5.3.8/badge"; -@import "bootstrap-5.3.8/alert"; -@import "bootstrap-5.3.8/progress"; -@import "bootstrap-5.3.8/list-group"; -@import "bootstrap-5.3.8/close"; -@import "bootstrap-5.3.8/toasts"; -@import "bootstrap-5.3.8/modal"; -@import "bootstrap-5.3.8/tooltip"; -@import "bootstrap-5.3.8/popover"; -@import "bootstrap-5.3.8/carousel"; -@import "bootstrap-5.3.8/spinners"; -@import "bootstrap-5.3.8/offcanvas"; -@import "bootstrap-5.3.8/placeholders"; - -// Helpers -@import "bootstrap-5.3.8/helpers"; - -// Custom definitions -@import "customs"; - -// Utilities -@import "bootstrap-5.3.8/utilities/api"; -// scss-docs-end import-stack diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_accordion.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_accordion.scss deleted file mode 100644 index e9f267fb..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_accordion.scss +++ /dev/null @@ -1,153 +0,0 @@ -// -// 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-collapse, - > .accordion-header .accordion-button, - > .accordion-header .accordion-button.collapsed { - @include border-radius(0); - } - // stylelint-enable selector-max-class - } -} - -@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/scss/bootstrap-5.3.8/_alert.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_alert.scss deleted file mode 100644 index b8cff9b7..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_alert.scss +++ /dev/null @@ -1,68 +0,0 @@ -// -// 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/scss/bootstrap-5.3.8/_badge.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_badge.scss deleted file mode 100644 index cc3d2695..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_badge.scss +++ /dev/null @@ -1,38 +0,0 @@ -// 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/scss/bootstrap-5.3.8/_breadcrumb.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_breadcrumb.scss deleted file mode 100644 index b8252ff2..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_breadcrumb.scss +++ /dev/null @@ -1,40 +0,0 @@ -.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/scss/bootstrap-5.3.8/_button-group.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_button-group.scss deleted file mode 100644 index 78e12522..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_button-group.scss +++ /dev/null @@ -1,147 +0,0 @@ -// 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(-1 * #{$btn-border-width}); // 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(-1 * #{$btn-border-width}); // 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); - } - - // The top 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-top-radius(0); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_buttons.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_buttons.scss deleted file mode 100644 index caa4518a..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_buttons.scss +++ /dev/null @@ -1,216 +0,0 @@ -// -// 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/scss/bootstrap-5.3.8/_card.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_card.scss deleted file mode 100644 index dcebe6ac..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_card.scss +++ /dev/null @@ -1,238 +0,0 @@ -// -// 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 <hN> - 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 { - 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/scss/bootstrap-5.3.8/_carousel.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_carousel.scss deleted file mode 100644 index 5ebf6b15..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_carousel.scss +++ /dev/null @@ -1,226 +0,0 @@ -// 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; - filter: var(--#{$prefix}carousel-control-icon-filter); - 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: var(--#{$prefix}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: var(--#{$prefix}carousel-caption-color); - text-align: center; -} - -// Dark mode carousel - -@mixin carousel-dark() { - --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark}; - --#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark}; - --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark}; -} - -.carousel-dark { - @include carousel-dark(); -} - -:root, -[data-bs-theme="light"] { - --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg}; - --#{$prefix}carousel-caption-color: #{$carousel-caption-color}; - --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter}; -} - -@if $enable-dark-mode { - @include color-mode(dark, true) { - @include carousel-dark(); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_close.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_close.scss deleted file mode 100644 index d53c96fb..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_close.scss +++ /dev/null @@ -1,66 +0,0 @@ -// 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}; - // 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 - filter: var(--#{$prefix}btn-close-filter); - border: 0; // for button elements - @include border-radius(); - opacity: var(--#{$prefix}btn-close-opacity); - - // Override <a>'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() { - --#{$prefix}btn-close-filter: #{$btn-close-filter-dark}; -} - -.btn-close-white { - @include btn-close-white(); -} - -:root, -[data-bs-theme="light"] { - --#{$prefix}btn-close-filter: #{$btn-close-filter}; -} - -@if $enable-dark-mode { - @include color-mode(dark, true) { - @include btn-close-white(); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_containers.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_containers.scss deleted file mode 100644 index 83b31381..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_containers.scss +++ /dev/null @@ -1,41 +0,0 @@ -// 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/scss/bootstrap-5.3.8/_dropdown.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_dropdown.scss deleted file mode 100644 index 587ebb48..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_dropdown.scss +++ /dev/null @@ -1,250 +0,0 @@ -// The dropdown wrapper (`<div>`) -.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 `<hr>`) 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 <hr> element -} - -// Links, buttons, and more within the dropdown menu -// -// `<button>`-specific styles are denoted with `// For <button>s` -.dropdown-item { - display: block; - width: 100%; // For `<button>`s - padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x); - clear: both; - font-weight: $font-weight-normal; - color: var(--#{$prefix}dropdown-link-color); - text-align: inherit; // For `<button>`s - text-decoration: if($link-decoration == none, null, none); - white-space: nowrap; // prevent links from randomly breaking onto new lines - background-color: transparent; // For `<button>`s - border: 0; // For `<button>`s - @include border-radius(var(--#{$prefix}dropdown-item-border-radius, 0)); - - &:hover, - &:focus { - color: var(--#{$prefix}dropdown-link-hover-color); - text-decoration: if($link-hover-decoration == underline, none, null); - @include gradient-bg(var(--#{$prefix}dropdown-link-hover-bg)); - } - - &.active, - &:active { - color: var(--#{$prefix}dropdown-link-active-color); - text-decoration: none; - @include gradient-bg(var(--#{$prefix}dropdown-link-active-bg)); - } - - &.disabled, - &:disabled { - color: var(--#{$prefix}dropdown-link-disabled-color); - pointer-events: none; - background-color: transparent; - // Remove CSS gradients if they're enabled - background-image: if($enable-gradients, none, null); - } -} - -.dropdown-menu.show { - display: block; -} - -// Dropdown section headers -.dropdown-header { - display: block; - padding: var(--#{$prefix}dropdown-header-padding-y) var(--#{$prefix}dropdown-header-padding-x); - margin-bottom: 0; // for use with heading elements - @include font-size($font-size-sm); - color: var(--#{$prefix}dropdown-header-color); - white-space: nowrap; // as with > li > a -} - -// Dropdown text -.dropdown-item-text { - display: block; - padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x); - color: var(--#{$prefix}dropdown-link-color); -} - -// Dark dropdowns -.dropdown-menu-dark { - // scss-docs-start dropdown-dark-css-vars - --#{$prefix}dropdown-color: #{$dropdown-dark-color}; - --#{$prefix}dropdown-bg: #{$dropdown-dark-bg}; - --#{$prefix}dropdown-border-color: #{$dropdown-dark-border-color}; - --#{$prefix}dropdown-box-shadow: #{$dropdown-dark-box-shadow}; - --#{$prefix}dropdown-link-color: #{$dropdown-dark-link-color}; - --#{$prefix}dropdown-link-hover-color: #{$dropdown-dark-link-hover-color}; - --#{$prefix}dropdown-divider-bg: #{$dropdown-dark-divider-bg}; - --#{$prefix}dropdown-link-hover-bg: #{$dropdown-dark-link-hover-bg}; - --#{$prefix}dropdown-link-active-color: #{$dropdown-dark-link-active-color}; - --#{$prefix}dropdown-link-active-bg: #{$dropdown-dark-link-active-bg}; - --#{$prefix}dropdown-link-disabled-color: #{$dropdown-dark-link-disabled-color}; - --#{$prefix}dropdown-header-color: #{$dropdown-dark-header-color}; - // scss-docs-end dropdown-dark-css-vars -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_forms.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_forms.scss deleted file mode 100644 index 7b17d849..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_forms.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import "forms/labels"; -@import "forms/form-text"; -@import "forms/form-control"; -@import "forms/form-select"; -@import "forms/form-check"; -@import "forms/form-range"; -@import "forms/floating-labels"; -@import "forms/input-group"; -@import "forms/validation"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_functions.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_functions.scss deleted file mode 100644 index 59d431a1..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_functions.scss +++ /dev/null @@ -1,302 +0,0 @@ -// Bootstrap functions -// -// Utility mixins and functions for evaluating source code across our variables, maps, and mixins. - -// Ascending -// Used to evaluate Sass maps like our grid breakpoints. -@mixin _assert-ascending($map, $map-name) { - $prev-key: null; - $prev-num: null; - @each $key, $num in $map { - @if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" { - // Do nothing - } @else if not comparable($prev-num, $num) { - @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !"; - } @else if $prev-num >= $num { - @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !"; - } - $prev-key: $key; - $prev-num: $num; - } -} - -// Starts at zero -// Used to ensure the min-width of the lowest breakpoint starts at 0. -@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") { - @if length($map) > 0 { - $values: map-values($map); - $first-value: nth($values, 1); - @if $first-value != 0 { - @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}."; - } - } -} - -// Colors -@function to-rgb($value) { - @return red($value), green($value), blue($value); -} - -// stylelint-disable scss/dollar-variable-pattern -@function rgba-css-var($identifier, $target) { - @if $identifier == "body" and $target == "bg" { - @return rgba(var(--#{$prefix}#{$identifier}-bg-rgb), var(--#{$prefix}#{$target}-opacity)); - } @if $identifier == "body" and $target == "text" { - @return rgba(var(--#{$prefix}#{$identifier}-color-rgb), var(--#{$prefix}#{$target}-opacity)); - } @else { - @return rgba(var(--#{$prefix}#{$identifier}-rgb), var(--#{$prefix}#{$target}-opacity)); - } -} - -@function map-loop($map, $func, $args...) { - $_map: (); - - @each $key, $value in $map { - // allow to pass the $key and $value of the map as an function argument - $_args: (); - @each $arg in $args { - $_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg))); - } - - $_map: map-merge($_map, ($key: call(get-function($func), $_args...))); - } - - @return $_map; -} -// stylelint-enable scss/dollar-variable-pattern - -@function varify($list) { - $result: null; - @each $entry in $list { - $result: append($result, var(--#{$prefix}#{$entry}), space); - } - @return $result; -} - -// Internal Bootstrap function to turn maps into its negative variant. -// It prefixes the keys with `n` and makes the value negative. -@function negativify-map($map) { - $result: (); - @each $key, $value in $map { - @if $key != 0 { - $result: map-merge($result, ("n" + $key: (-$value))); - } - } - @return $result; -} - -// Get multiple keys from a sass map -@function map-get-multiple($map, $values) { - $result: (); - @each $key, $value in $map { - @if (index($values, $key) != null) { - $result: map-merge($result, ($key: $value)); - } - } - @return $result; -} - -// Merge multiple maps -@function map-merge-multiple($maps...) { - $merged-maps: (); - - @each $map in $maps { - $merged-maps: map-merge($merged-maps, $map); - } - @return $merged-maps; -} - -// Replace `$search` with `$replace` in `$string` -// Used on our SVG icon backgrounds for custom forms. -// -// @author Kitty Giraudel -// @param {String} $string - Initial string -// @param {String} $search - Substring to replace -// @param {String} $replace ('') - New value -// @return {String} - Updated string -@function str-replace($string, $search, $replace: "") { - $index: str-index($string, $search); - - @if $index { - @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); - } - - @return $string; -} - -// See https://codepen.io/kevinweber/pen/dXWoRw -// -// Requires the use of quotes around data URIs. - -@function escape-svg($string) { - @if str-index($string, "data:image/svg+xml") { - @each $char, $encoded in $escaped-characters { - // Do not escape the url brackets - @if str-index($string, "url(") == 1 { - $string: url("#{str-replace(str-slice($string, 6, -3), $char, $encoded)}"); - } @else { - $string: str-replace($string, $char, $encoded); - } - } - } - - @return $string; -} - -// Color contrast -// See https://github.com/twbs/bootstrap/pull/30168 - -// A list of pre-calculated numbers of pow(divide((divide($value, 255) + .055), 1.055), 2.4). (from 0 to 255) -// stylelint-disable-next-line scss/dollar-variable-default, scss/dollar-variable-pattern -$_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 .0033 .0037 .004 .0044 .0048 .0052 .0056 .006 .0065 .007 .0075 .008 .0086 .0091 .0097 .0103 .011 .0116 .0123 .013 .0137 .0144 .0152 .016 .0168 .0176 .0185 .0194 .0203 .0212 .0222 .0232 .0242 .0252 .0262 .0273 .0284 .0296 .0307 .0319 .0331 .0343 .0356 .0369 .0382 .0395 .0409 .0423 .0437 .0452 .0467 .0482 .0497 .0513 .0529 .0545 .0561 .0578 .0595 .0612 .063 .0648 .0666 .0685 .0704 .0723 .0742 .0762 .0782 .0802 .0823 .0844 .0865 .0887 .0908 .0931 .0953 .0976 .0999 .1022 .1046 .107 .1095 .1119 .1144 .117 .1195 .1221 .1248 .1274 .1301 .1329 .1356 .1384 .1413 .1441 .147 .15 .1529 .1559 .159 .162 .1651 .1683 .1714 .1746 .1779 .1812 .1845 .1878 .1912 .1946 .1981 .2016 .2051 .2086 .2122 .2159 .2195 .2232 .227 .2307 .2346 .2384 .2423 .2462 .2502 .2542 .2582 .2623 .2664 .2705 .2747 .2789 .2831 .2874 .2918 .2961 .3005 .305 .3095 .314 .3185 .3231 .3278 .3325 .3372 .3419 .3467 .3515 .3564 .3613 .3663 .3712 .3763 .3813 .3864 .3916 .3968 .402 .4072 .4125 .4179 .4233 .4287 .4342 .4397 .4452 .4508 .4564 .4621 .4678 .4735 .4793 .4851 .491 .4969 .5029 .5089 .5149 .521 .5271 .5333 .5395 .5457 .552 .5583 .5647 .5711 .5776 .5841 .5906 .5972 .6038 .6105 .6172 .624 .6308 .6376 .6445 .6514 .6584 .6654 .6724 .6795 .6867 .6939 .7011 .7084 .7157 .7231 .7305 .7379 .7454 .7529 .7605 .7682 .7758 .7835 .7913 .7991 .807 .8148 .8228 .8308 .8388 .8469 .855 .8632 .8714 .8796 .8879 .8963 .9047 .9131 .9216 .9301 .9387 .9473 .956 .9647 .9734 .9823 .9911 1; - -@function color-contrast($background, $color-contrast-dark: $color-contrast-dark, $color-contrast-light: $color-contrast-light, $min-contrast-ratio: $min-contrast-ratio) { - $foregrounds: $color-contrast-light, $color-contrast-dark, $white, $black; - $max-ratio: 0; - $max-ratio-color: null; - - @each $color in $foregrounds { - $contrast-ratio: contrast-ratio($background, $color); - @if $contrast-ratio >= $min-contrast-ratio { - @return $color; - } @else if $contrast-ratio > $max-ratio { - $max-ratio: $contrast-ratio; - $max-ratio-color: $color; - } - } - - @warn "Found no color leading to #{$min-contrast-ratio}:1 contrast ratio against #{$background}..."; - - @return $max-ratio-color; -} - -@function contrast-ratio($background, $foreground: $color-contrast-light) { - $l1: luminance($background); - $l2: luminance(opaque($background, $foreground)); - - @return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05)); -} - -// Return WCAG2.2 relative luminance -// See https://www.w3.org/TR/WCAG/#dfn-relative-luminance -// See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio -@function luminance($color) { - $rgb: ( - "r": red($color), - "g": green($color), - "b": blue($color) - ); - - @each $name, $value in $rgb { - $value: if(divide($value, 255) < .04045, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1)); - $rgb: map-merge($rgb, ($name: $value)); - } - - @return (map-get($rgb, "r") * .2126) + (map-get($rgb, "g") * .7152) + (map-get($rgb, "b") * .0722); -} - -// Return opaque color -// opaque(#fff, rgba(0, 0, 0, .5)) => #808080 -@function opaque($background, $foreground) { - @return mix(rgba($foreground, 1), $background, opacity($foreground) * 100%); -} - -// scss-docs-start color-functions -// Tint a color: mix a color with white -@function tint-color($color, $weight) { - @return mix(white, $color, $weight); -} - -// Shade a color: mix a color with black -@function shade-color($color, $weight) { - @return mix(black, $color, $weight); -} - -// Shade the color if the weight is positive, else tint it -@function shift-color($color, $weight) { - @return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight)); -} -// scss-docs-end color-functions - -// Return valid calc -@function add($value1, $value2, $return-calc: true) { - @if $value1 == null { - @return $value2; - } - - @if $value2 == null { - @return $value1; - } - - @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) { - @return $value1 + $value2; - } - - @return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2); -} - -@function subtract($value1, $value2, $return-calc: true) { - @if $value1 == null and $value2 == null { - @return null; - } - - @if $value1 == null { - @return -$value2; - } - - @if $value2 == null { - @return $value1; - } - - @if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) { - @return $value1 - $value2; - } - - @if type-of($value2) != number { - $value2: unquote("(") + $value2 + unquote(")"); - } - - @return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(" - ") + $value2); -} - -@function divide($dividend, $divisor, $precision: 10) { - $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1); - $dividend: abs($dividend); - $divisor: abs($divisor); - @if $dividend == 0 { - @return 0; - } - @if $divisor == 0 { - @error "Cannot divide by 0"; - } - $remainder: $dividend; - $result: 0; - $factor: 10; - @while ($remainder > 0 and $precision >= 0) { - $quotient: 0; - @while ($remainder >= $divisor) { - $remainder: $remainder - $divisor; - $quotient: $quotient + 1; - } - $result: $result * 10 + $quotient; - $factor: $factor * .1; - $remainder: $remainder * 10; - $precision: $precision - 1; - @if ($precision < 0 and $remainder >= $divisor * 5) { - $result: $result + 1; - } - } - $result: $result * $factor * $sign; - $dividend-unit: unit($dividend); - $divisor-unit: unit($divisor); - $unit-map: ( - "px": 1px, - "rem": 1rem, - "em": 1em, - "%": 1% - ); - @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) { - $result: $result * map-get($unit-map, $dividend-unit); - } - @return $result; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_grid.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_grid.scss deleted file mode 100644 index 048f8009..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_grid.scss +++ /dev/null @@ -1,39 +0,0 @@ -// Row -// -// Rows contain your columns. - -:root { - @each $name, $value in $grid-breakpoints { - --#{$prefix}breakpoint-#{$name}: #{$value}; - } -} - -@if $enable-grid-classes { - .row { - @include make-row(); - - > * { - @include make-col-ready(); - } - } -} - -@if $enable-cssgrid { - .grid { - display: grid; - grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr); - grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr); - gap: var(--#{$prefix}gap, #{$grid-gutter-width}); - - @include make-cssgrid(); - } -} - - -// Columns -// -// Common styles for small and large grid columns - -@if $enable-grid-classes { - @include make-grid-columns(); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_helpers.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_helpers.scss deleted file mode 100644 index 13f2752c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_helpers.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import "helpers/clearfix"; -@import "helpers/color-bg"; -@import "helpers/colored-links"; -@import "helpers/focus-ring"; -@import "helpers/icon-link"; -@import "helpers/ratio"; -@import "helpers/position"; -@import "helpers/stacks"; -@import "helpers/visually-hidden"; -@import "helpers/stretched-link"; -@import "helpers/text-truncation"; -@import "helpers/vr"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_images.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_images.scss deleted file mode 100644 index 3d6a1014..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_images.scss +++ /dev/null @@ -1,42 +0,0 @@ -// Responsive images (ensure images don't scale beyond their parents) -// -// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s. -// We previously tried the "images are responsive by default" approach in Bootstrap v2, -// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps) -// which weren't expecting the images within themselves to be involuntarily resized. -// See also https://github.com/twbs/bootstrap/issues/18178 -.img-fluid { - @include img-fluid(); -} - - -// Image thumbnails -.img-thumbnail { - padding: $thumbnail-padding; - background-color: $thumbnail-bg; - border: $thumbnail-border-width solid $thumbnail-border-color; - @include border-radius($thumbnail-border-radius); - @include box-shadow($thumbnail-box-shadow); - - // Keep them at most 100% wide - @include img-fluid(); -} - -// -// Figures -// - -.figure { - // Ensures the caption's text aligns with the image. - display: inline-block; -} - -.figure-img { - margin-bottom: $spacer * .5; - line-height: 1; -} - -.figure-caption { - @include font-size($figure-caption-font-size); - color: $figure-caption-color; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_list-group.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_list-group.scss deleted file mode 100644 index 3bdff679..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_list-group.scss +++ /dev/null @@ -1,199 +0,0 @@ -// Base class -// -// Easily usable on <ul>, <ol>, or <div>. - -.list-group { - // scss-docs-start list-group-css-vars - --#{$prefix}list-group-color: #{$list-group-color}; - --#{$prefix}list-group-bg: #{$list-group-bg}; - --#{$prefix}list-group-border-color: #{$list-group-border-color}; - --#{$prefix}list-group-border-width: #{$list-group-border-width}; - --#{$prefix}list-group-border-radius: #{$list-group-border-radius}; - --#{$prefix}list-group-item-padding-x: #{$list-group-item-padding-x}; - --#{$prefix}list-group-item-padding-y: #{$list-group-item-padding-y}; - --#{$prefix}list-group-action-color: #{$list-group-action-color}; - --#{$prefix}list-group-action-hover-color: #{$list-group-action-hover-color}; - --#{$prefix}list-group-action-hover-bg: #{$list-group-hover-bg}; - --#{$prefix}list-group-action-active-color: #{$list-group-action-active-color}; - --#{$prefix}list-group-action-active-bg: #{$list-group-action-active-bg}; - --#{$prefix}list-group-disabled-color: #{$list-group-disabled-color}; - --#{$prefix}list-group-disabled-bg: #{$list-group-disabled-bg}; - --#{$prefix}list-group-active-color: #{$list-group-active-color}; - --#{$prefix}list-group-active-bg: #{$list-group-active-bg}; - --#{$prefix}list-group-active-border-color: #{$list-group-active-border-color}; - // scss-docs-end list-group-css-vars - - display: flex; - flex-direction: column; - - // No need to set list-style: none; since .list-group-item is block level - padding-left: 0; // reset padding because ul and ol - margin-bottom: 0; - @include border-radius(var(--#{$prefix}list-group-border-radius)); -} - -.list-group-numbered { - list-style-type: none; - counter-reset: section; - - > .list-group-item::before { - // Increments only this instance of the section counter - content: counters(section, ".") ". "; - counter-increment: section; - } -} - -// Individual list items -// -// Use on `li`s or `div`s within the `.list-group` parent. - -.list-group-item { - position: relative; - display: block; - padding: var(--#{$prefix}list-group-item-padding-y) var(--#{$prefix}list-group-item-padding-x); - color: var(--#{$prefix}list-group-color); - text-decoration: if($link-decoration == none, null, none); - background-color: var(--#{$prefix}list-group-bg); - border: var(--#{$prefix}list-group-border-width) solid var(--#{$prefix}list-group-border-color); - - &:first-child { - @include border-top-radius(inherit); - } - - &:last-child { - @include border-bottom-radius(inherit); - } - - &.disabled, - &:disabled { - color: var(--#{$prefix}list-group-disabled-color); - pointer-events: none; - background-color: var(--#{$prefix}list-group-disabled-bg); - } - - // Include both here for `<a>`s and `<button>`s - &.active { - z-index: 2; // Place active items above their siblings for proper border styling - color: var(--#{$prefix}list-group-active-color); - background-color: var(--#{$prefix}list-group-active-bg); - border-color: var(--#{$prefix}list-group-active-border-color); - } - - // stylelint-disable-next-line scss/selector-no-redundant-nesting-selector - & + .list-group-item { - border-top-width: 0; - - &.active { - margin-top: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list - border-top-width: var(--#{$prefix}list-group-border-width); - } - } -} - -// Interactive list items -// -// Use anchor or button elements instead of `li`s or `div`s to create interactive -// list items. Includes an extra `.active` modifier class for selected items. - -.list-group-item-action { - width: 100%; // For `<button>`s (anchors become 100% by default though) - color: var(--#{$prefix}list-group-action-color); - text-align: inherit; // For `<button>`s (anchors inherit) - - &:not(.active) { - // Hover state - &:hover, - &:focus { - z-index: 1; // Place hover/focus items above their siblings for proper border styling - color: var(--#{$prefix}list-group-action-hover-color); - text-decoration: none; - background-color: var(--#{$prefix}list-group-action-hover-bg); - } - - &:active { - color: var(--#{$prefix}list-group-action-active-color); - background-color: var(--#{$prefix}list-group-action-active-bg); - } - } -} - -// Horizontal -// -// Change the layout of list group items from vertical (default) to horizontal. - -@each $breakpoint in map-keys($grid-breakpoints) { - @include media-breakpoint-up($breakpoint) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - - .list-group-horizontal#{$infix} { - flex-direction: row; - - > .list-group-item { - &:first-child:not(:last-child) { - @include border-bottom-start-radius(var(--#{$prefix}list-group-border-radius)); - @include border-top-end-radius(0); - } - - &:last-child:not(:first-child) { - @include border-top-end-radius(var(--#{$prefix}list-group-border-radius)); - @include border-bottom-start-radius(0); - } - - &.active { - margin-top: 0; - } - - + .list-group-item { - border-top-width: var(--#{$prefix}list-group-border-width); - border-left-width: 0; - - &.active { - margin-left: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list - border-left-width: var(--#{$prefix}list-group-border-width); - } - } - } - } - } -} - - -// Flush list items -// -// Remove borders and border-radius to keep list group items edge-to-edge. Most -// useful within other components (e.g., cards). - -.list-group-flush { - @include border-radius(0); - - > .list-group-item { - border-width: 0 0 var(--#{$prefix}list-group-border-width); - - &:last-child { - border-bottom-width: 0; - } - } -} - - -// scss-docs-start list-group-modifiers -// List group contextual variants -// -// Add modifier classes to change text and background color on individual items. -// Organizationally, this must come after the `:hover` states. - -@each $state in map-keys($theme-colors) { - .list-group-item-#{$state} { - --#{$prefix}list-group-color: var(--#{$prefix}#{$state}-text-emphasis); - --#{$prefix}list-group-bg: var(--#{$prefix}#{$state}-bg-subtle); - --#{$prefix}list-group-border-color: var(--#{$prefix}#{$state}-border-subtle); - --#{$prefix}list-group-action-hover-color: var(--#{$prefix}emphasis-color); - --#{$prefix}list-group-action-hover-bg: var(--#{$prefix}#{$state}-border-subtle); - --#{$prefix}list-group-action-active-color: var(--#{$prefix}emphasis-color); - --#{$prefix}list-group-action-active-bg: var(--#{$prefix}#{$state}-border-subtle); - --#{$prefix}list-group-active-color: var(--#{$prefix}#{$state}-bg-subtle); - --#{$prefix}list-group-active-bg: var(--#{$prefix}#{$state}-text-emphasis); - --#{$prefix}list-group-active-border-color: var(--#{$prefix}#{$state}-text-emphasis); - } -} -// scss-docs-end list-group-modifiers diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_maps.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_maps.scss deleted file mode 100644 index 68ee421c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_maps.scss +++ /dev/null @@ -1,174 +0,0 @@ -// Re-assigned maps -// -// Placed here so that others can override the default Sass maps and see automatic updates to utilities and more. - -// scss-docs-start theme-colors-rgb -$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default; -// scss-docs-end theme-colors-rgb - -// scss-docs-start theme-text-map -$theme-colors-text: ( - "primary": $primary-text-emphasis, - "secondary": $secondary-text-emphasis, - "success": $success-text-emphasis, - "info": $info-text-emphasis, - "warning": $warning-text-emphasis, - "danger": $danger-text-emphasis, - "light": $light-text-emphasis, - "dark": $dark-text-emphasis, -) !default; -// scss-docs-end theme-text-map - -// scss-docs-start theme-bg-subtle-map -$theme-colors-bg-subtle: ( - "primary": $primary-bg-subtle, - "secondary": $secondary-bg-subtle, - "success": $success-bg-subtle, - "info": $info-bg-subtle, - "warning": $warning-bg-subtle, - "danger": $danger-bg-subtle, - "light": $light-bg-subtle, - "dark": $dark-bg-subtle, -) !default; -// scss-docs-end theme-bg-subtle-map - -// scss-docs-start theme-border-subtle-map -$theme-colors-border-subtle: ( - "primary": $primary-border-subtle, - "secondary": $secondary-border-subtle, - "success": $success-border-subtle, - "info": $info-border-subtle, - "warning": $warning-border-subtle, - "danger": $danger-border-subtle, - "light": $light-border-subtle, - "dark": $dark-border-subtle, -) !default; -// scss-docs-end theme-border-subtle-map - -$theme-colors-text-dark: null !default; -$theme-colors-bg-subtle-dark: null !default; -$theme-colors-border-subtle-dark: null !default; - -@if $enable-dark-mode { - // scss-docs-start theme-text-dark-map - $theme-colors-text-dark: ( - "primary": $primary-text-emphasis-dark, - "secondary": $secondary-text-emphasis-dark, - "success": $success-text-emphasis-dark, - "info": $info-text-emphasis-dark, - "warning": $warning-text-emphasis-dark, - "danger": $danger-text-emphasis-dark, - "light": $light-text-emphasis-dark, - "dark": $dark-text-emphasis-dark, - ) !default; - // scss-docs-end theme-text-dark-map - - // scss-docs-start theme-bg-subtle-dark-map - $theme-colors-bg-subtle-dark: ( - "primary": $primary-bg-subtle-dark, - "secondary": $secondary-bg-subtle-dark, - "success": $success-bg-subtle-dark, - "info": $info-bg-subtle-dark, - "warning": $warning-bg-subtle-dark, - "danger": $danger-bg-subtle-dark, - "light": $light-bg-subtle-dark, - "dark": $dark-bg-subtle-dark, - ) !default; - // scss-docs-end theme-bg-subtle-dark-map - - // scss-docs-start theme-border-subtle-dark-map - $theme-colors-border-subtle-dark: ( - "primary": $primary-border-subtle-dark, - "secondary": $secondary-border-subtle-dark, - "success": $success-border-subtle-dark, - "info": $info-border-subtle-dark, - "warning": $warning-border-subtle-dark, - "danger": $danger-border-subtle-dark, - "light": $light-border-subtle-dark, - "dark": $dark-border-subtle-dark, - ) !default; - // scss-docs-end theme-border-subtle-dark-map -} - -// Utilities maps -// -// Extends the default `$theme-colors` maps to help create our utilities. - -// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign. -// scss-docs-start utilities-colors -$utilities-colors: $theme-colors-rgb !default; -// scss-docs-end utilities-colors - -// scss-docs-start utilities-text-colors -$utilities-text: map-merge( - $utilities-colors, - ( - "black": to-rgb($black), - "white": to-rgb($white), - "body": to-rgb($body-color) - ) -) !default; -$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default; - -$utilities-text-emphasis-colors: ( - "primary-emphasis": var(--#{$prefix}primary-text-emphasis), - "secondary-emphasis": var(--#{$prefix}secondary-text-emphasis), - "success-emphasis": var(--#{$prefix}success-text-emphasis), - "info-emphasis": var(--#{$prefix}info-text-emphasis), - "warning-emphasis": var(--#{$prefix}warning-text-emphasis), - "danger-emphasis": var(--#{$prefix}danger-text-emphasis), - "light-emphasis": var(--#{$prefix}light-text-emphasis), - "dark-emphasis": var(--#{$prefix}dark-text-emphasis) -) !default; -// scss-docs-end utilities-text-colors - -// scss-docs-start utilities-bg-colors -$utilities-bg: map-merge( - $utilities-colors, - ( - "black": to-rgb($black), - "white": to-rgb($white), - "body": to-rgb($body-bg) - ) -) !default; -$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default; - -$utilities-bg-subtle: ( - "primary-subtle": var(--#{$prefix}primary-bg-subtle), - "secondary-subtle": var(--#{$prefix}secondary-bg-subtle), - "success-subtle": var(--#{$prefix}success-bg-subtle), - "info-subtle": var(--#{$prefix}info-bg-subtle), - "warning-subtle": var(--#{$prefix}warning-bg-subtle), - "danger-subtle": var(--#{$prefix}danger-bg-subtle), - "light-subtle": var(--#{$prefix}light-bg-subtle), - "dark-subtle": var(--#{$prefix}dark-bg-subtle) -) !default; -// scss-docs-end utilities-bg-colors - -// scss-docs-start utilities-border-colors -$utilities-border: map-merge( - $utilities-colors, - ( - "black": to-rgb($black), - "white": to-rgb($white) - ) -) !default; -$utilities-border-colors: map-loop($utilities-border, rgba-css-var, "$key", "border") !default; - -$utilities-border-subtle: ( - "primary-subtle": var(--#{$prefix}primary-border-subtle), - "secondary-subtle": var(--#{$prefix}secondary-border-subtle), - "success-subtle": var(--#{$prefix}success-border-subtle), - "info-subtle": var(--#{$prefix}info-border-subtle), - "warning-subtle": var(--#{$prefix}warning-border-subtle), - "danger-subtle": var(--#{$prefix}danger-border-subtle), - "light-subtle": var(--#{$prefix}light-border-subtle), - "dark-subtle": var(--#{$prefix}dark-border-subtle) -) !default; -// scss-docs-end utilities-border-colors - -$utilities-links-underline: map-loop($utilities-colors, rgba-css-var, "$key", "link-underline") !default; - -$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default; - -$gutters: $spacers !default; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_mixins.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_mixins.scss deleted file mode 100644 index e1e130b1..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_mixins.scss +++ /dev/null @@ -1,42 +0,0 @@ -// Toggles -// -// Used in conjunction with global variables to enable certain theme features. - -// Vendor -@import "vendor/rfs"; - -// Deprecate -@import "mixins/deprecate"; - -// Helpers -@import "mixins/breakpoints"; -@import "mixins/color-mode"; -@import "mixins/color-scheme"; -@import "mixins/image"; -@import "mixins/resize"; -@import "mixins/visually-hidden"; -@import "mixins/reset-text"; -@import "mixins/text-truncate"; - -// Utilities -@import "mixins/utilities"; - -// Components -@import "mixins/backdrop"; -@import "mixins/buttons"; -@import "mixins/caret"; -@import "mixins/pagination"; -@import "mixins/lists"; -@import "mixins/forms"; -@import "mixins/table-variants"; - -// Skins -@import "mixins/border-radius"; -@import "mixins/box-shadow"; -@import "mixins/gradients"; -@import "mixins/transition"; - -// Layout -@import "mixins/clearfix"; -@import "mixins/container"; -@import "mixins/grid"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_modal.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_modal.scss deleted file mode 100644 index a3492c17..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_modal.scss +++ /dev/null @@ -1,240 +0,0 @@ -// stylelint-disable function-disallowed-list - -// .modal-open - body class for killing the scroll -// .modal - container to scroll within -// .modal-dialog - positioning shell for the actual modal -// .modal-content - actual modal w/ bg and corners and stuff - - -// Container that the modal scrolls within -.modal { - // scss-docs-start modal-css-vars - --#{$prefix}modal-zindex: #{$zindex-modal}; - --#{$prefix}modal-width: #{$modal-md}; - --#{$prefix}modal-padding: #{$modal-inner-padding}; - --#{$prefix}modal-margin: #{$modal-dialog-margin}; - --#{$prefix}modal-color: #{$modal-content-color}; - --#{$prefix}modal-bg: #{$modal-content-bg}; - --#{$prefix}modal-border-color: #{$modal-content-border-color}; - --#{$prefix}modal-border-width: #{$modal-content-border-width}; - --#{$prefix}modal-border-radius: #{$modal-content-border-radius}; - --#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-xs}; - --#{$prefix}modal-inner-border-radius: #{$modal-content-inner-border-radius}; - --#{$prefix}modal-header-padding-x: #{$modal-header-padding-x}; - --#{$prefix}modal-header-padding-y: #{$modal-header-padding-y}; - --#{$prefix}modal-header-padding: #{$modal-header-padding}; // Todo in v6: Split this padding into x and y - --#{$prefix}modal-header-border-color: #{$modal-header-border-color}; - --#{$prefix}modal-header-border-width: #{$modal-header-border-width}; - --#{$prefix}modal-title-line-height: #{$modal-title-line-height}; - --#{$prefix}modal-footer-gap: #{$modal-footer-margin-between}; - --#{$prefix}modal-footer-bg: #{$modal-footer-bg}; - --#{$prefix}modal-footer-border-color: #{$modal-footer-border-color}; - --#{$prefix}modal-footer-border-width: #{$modal-footer-border-width}; - // scss-docs-end modal-css-vars - - position: fixed; - top: 0; - left: 0; - z-index: var(--#{$prefix}modal-zindex); - display: none; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - // Prevent Chrome on Windows from adding a focus outline. For details, see - // https://github.com/twbs/bootstrap/pull/10951. - outline: 0; - // We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a - // gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342 - // See also https://github.com/twbs/bootstrap/issues/17695 -} - -// Shell div to position the modal with bottom padding -.modal-dialog { - position: relative; - width: auto; - margin: var(--#{$prefix}modal-margin); - // allow clicks to pass through for custom click handling to close modal - pointer-events: none; - - // When fading in the modal, animate it to slide down - .modal.fade & { - transform: $modal-fade-transform; - @include transition($modal-transition); - } - .modal.show & { - transform: $modal-show-transform; - } - - // When trying to close, animate focus to scale - .modal.modal-static & { - transform: $modal-scale-transform; - } -} - -.modal-dialog-scrollable { - height: calc(100% - var(--#{$prefix}modal-margin) * 2); - - .modal-content { - max-height: 100%; - overflow: hidden; - } - - .modal-body { - overflow-y: auto; - } -} - -.modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - var(--#{$prefix}modal-margin) * 2); -} - -// Actual modal -.modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog` - // counteract the pointer-events: none; in the .modal-dialog - color: var(--#{$prefix}modal-color); - pointer-events: auto; - background-color: var(--#{$prefix}modal-bg); - background-clip: padding-box; - border: var(--#{$prefix}modal-border-width) solid var(--#{$prefix}modal-border-color); - @include border-radius(var(--#{$prefix}modal-border-radius)); - @include box-shadow(var(--#{$prefix}modal-box-shadow)); - // Remove focus outline from opened modal - outline: 0; -} - -// Modal background -.modal-backdrop { - // scss-docs-start modal-backdrop-css-vars - --#{$prefix}backdrop-zindex: #{$zindex-modal-backdrop}; - --#{$prefix}backdrop-bg: #{$modal-backdrop-bg}; - --#{$prefix}backdrop-opacity: #{$modal-backdrop-opacity}; - // scss-docs-end modal-backdrop-css-vars - - @include overlay-backdrop(var(--#{$prefix}backdrop-zindex), var(--#{$prefix}backdrop-bg), var(--#{$prefix}backdrop-opacity)); -} - -// Modal header -// Top section of the modal w/ title and dismiss -.modal-header { - display: flex; - flex-shrink: 0; - align-items: center; - padding: var(--#{$prefix}modal-header-padding); - border-bottom: var(--#{$prefix}modal-header-border-width) solid var(--#{$prefix}modal-header-border-color); - @include border-top-radius(var(--#{$prefix}modal-inner-border-radius)); - - .btn-close { - padding: calc(var(--#{$prefix}modal-header-padding-y) * .5) calc(var(--#{$prefix}modal-header-padding-x) * .5); - // Split properties to avoid invalid calc() function if value is 0 - margin-top: calc(-.5 * var(--#{$prefix}modal-header-padding-y)); - margin-right: calc(-.5 * var(--#{$prefix}modal-header-padding-x)); - margin-bottom: calc(-.5 * var(--#{$prefix}modal-header-padding-y)); - margin-left: auto; - } -} - -// Title text within header -.modal-title { - margin-bottom: 0; - line-height: var(--#{$prefix}modal-title-line-height); -} - -// Modal body -// Where all modal content resides (sibling of .modal-header and .modal-footer) -.modal-body { - position: relative; - // Enable `flex-grow: 1` so that the body take up as much space as possible - // when there should be a fixed height on `.modal-dialog`. - flex: 1 1 auto; - padding: var(--#{$prefix}modal-padding); -} - -// Footer (for actions) -.modal-footer { - display: flex; - flex-shrink: 0; - flex-wrap: wrap; - align-items: center; // vertically center - justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items - padding: calc(var(--#{$prefix}modal-padding) - var(--#{$prefix}modal-footer-gap) * .5); - background-color: var(--#{$prefix}modal-footer-bg); - border-top: var(--#{$prefix}modal-footer-border-width) solid var(--#{$prefix}modal-footer-border-color); - @include border-bottom-radius(var(--#{$prefix}modal-inner-border-radius)); - - // Place margin between footer elements - // This solution is far from ideal because of the universal selector usage, - // but is needed to fix https://github.com/twbs/bootstrap/issues/24800 - > * { - margin: calc(var(--#{$prefix}modal-footer-gap) * .5); // Todo in v6: replace with gap on parent class - } -} - -// Scale up the modal -@include media-breakpoint-up(sm) { - .modal { - --#{$prefix}modal-margin: #{$modal-dialog-margin-y-sm-up}; - --#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-sm-up}; - } - - // Automatically set modal's width for larger viewports - .modal-dialog { - max-width: var(--#{$prefix}modal-width); - margin-right: auto; - margin-left: auto; - } - - .modal-sm { - --#{$prefix}modal-width: #{$modal-sm}; - } -} - -@include media-breakpoint-up(lg) { - .modal-lg, - .modal-xl { - --#{$prefix}modal-width: #{$modal-lg}; - } -} - -@include media-breakpoint-up(xl) { - .modal-xl { - --#{$prefix}modal-width: #{$modal-xl}; - } -} - -// scss-docs-start modal-fullscreen-loop -@each $breakpoint in map-keys($grid-breakpoints) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - $postfix: if($infix != "", $infix + "-down", ""); - - @include media-breakpoint-down($breakpoint) { - .modal-fullscreen#{$postfix} { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - - .modal-content { - height: 100%; - border: 0; - @include border-radius(0); - } - - .modal-header, - .modal-footer { - @include border-radius(0); - } - - .modal-body { - overflow-y: auto; - } - } - } -} -// scss-docs-end modal-fullscreen-loop diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_nav.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_nav.scss deleted file mode 100644 index 96fa5289..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_nav.scss +++ /dev/null @@ -1,197 +0,0 @@ -// Base class -// -// Kickstart any navigation component with a set of style resets. Works with -// `<nav>`s, `<ul>`s or `<ol>`s. - -.nav { - // scss-docs-start nav-css-vars - --#{$prefix}nav-link-padding-x: #{$nav-link-padding-x}; - --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y}; - @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size); - --#{$prefix}nav-link-font-weight: #{$nav-link-font-weight}; - --#{$prefix}nav-link-color: #{$nav-link-color}; - --#{$prefix}nav-link-hover-color: #{$nav-link-hover-color}; - --#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color}; - // scss-docs-end nav-css-vars - - display: flex; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav-link { - display: block; - padding: var(--#{$prefix}nav-link-padding-y) var(--#{$prefix}nav-link-padding-x); - @include font-size(var(--#{$prefix}nav-link-font-size)); - font-weight: var(--#{$prefix}nav-link-font-weight); - color: var(--#{$prefix}nav-link-color); - text-decoration: if($link-decoration == none, null, none); - background: none; - border: 0; - @include transition($nav-link-transition); - - &:hover, - &:focus { - color: var(--#{$prefix}nav-link-hover-color); - text-decoration: if($link-hover-decoration == underline, none, null); - } - - &:focus-visible { - outline: 0; - box-shadow: $nav-link-focus-box-shadow; - } - - // Disabled state lightens text - &.disabled, - &:disabled { - color: var(--#{$prefix}nav-link-disabled-color); - pointer-events: none; - cursor: default; - } -} - -// -// Tabs -// - -.nav-tabs { - // scss-docs-start nav-tabs-css-vars - --#{$prefix}nav-tabs-border-width: #{$nav-tabs-border-width}; - --#{$prefix}nav-tabs-border-color: #{$nav-tabs-border-color}; - --#{$prefix}nav-tabs-border-radius: #{$nav-tabs-border-radius}; - --#{$prefix}nav-tabs-link-hover-border-color: #{$nav-tabs-link-hover-border-color}; - --#{$prefix}nav-tabs-link-active-color: #{$nav-tabs-link-active-color}; - --#{$prefix}nav-tabs-link-active-bg: #{$nav-tabs-link-active-bg}; - --#{$prefix}nav-tabs-link-active-border-color: #{$nav-tabs-link-active-border-color}; - // scss-docs-end nav-tabs-css-vars - - border-bottom: var(--#{$prefix}nav-tabs-border-width) solid var(--#{$prefix}nav-tabs-border-color); - - .nav-link { - margin-bottom: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list - border: var(--#{$prefix}nav-tabs-border-width) solid transparent; - @include border-top-radius(var(--#{$prefix}nav-tabs-border-radius)); - - &:hover, - &:focus { - // Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link - isolation: isolate; - border-color: var(--#{$prefix}nav-tabs-link-hover-border-color); - } - } - - .nav-link.active, - .nav-item.show .nav-link { - color: var(--#{$prefix}nav-tabs-link-active-color); - background-color: var(--#{$prefix}nav-tabs-link-active-bg); - border-color: var(--#{$prefix}nav-tabs-link-active-border-color); - } - - .dropdown-menu { - // Make dropdown border overlap tab border - margin-top: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list - // Remove the top rounded corners here since there is a hard edge above the menu - @include border-top-radius(0); - } -} - - -// -// Pills -// - -.nav-pills { - // scss-docs-start nav-pills-css-vars - --#{$prefix}nav-pills-border-radius: #{$nav-pills-border-radius}; - --#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color}; - --#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg}; - // scss-docs-end nav-pills-css-vars - - .nav-link { - @include border-radius(var(--#{$prefix}nav-pills-border-radius)); - } - - .nav-link.active, - .show > .nav-link { - color: var(--#{$prefix}nav-pills-link-active-color); - @include gradient-bg(var(--#{$prefix}nav-pills-link-active-bg)); - } -} - - -// -// Underline -// - -.nav-underline { - // scss-docs-start nav-underline-css-vars - --#{$prefix}nav-underline-gap: #{$nav-underline-gap}; - --#{$prefix}nav-underline-border-width: #{$nav-underline-border-width}; - --#{$prefix}nav-underline-link-active-color: #{$nav-underline-link-active-color}; - // scss-docs-end nav-underline-css-vars - - gap: var(--#{$prefix}nav-underline-gap); - - .nav-link { - padding-right: 0; - padding-left: 0; - border-bottom: var(--#{$prefix}nav-underline-border-width) solid transparent; - - &:hover, - &:focus { - border-bottom-color: currentcolor; - } - } - - .nav-link.active, - .show > .nav-link { - font-weight: $font-weight-bold; - color: var(--#{$prefix}nav-underline-link-active-color); - border-bottom-color: currentcolor; - } -} - - -// -// Justified variants -// - -.nav-fill { - > .nav-link, - .nav-item { - flex: 1 1 auto; - text-align: center; - } -} - -.nav-justified { - > .nav-link, - .nav-item { - flex-grow: 1; - flex-basis: 0; - text-align: center; - } -} - -.nav-fill, -.nav-justified { - .nav-item .nav-link { - width: 100%; // Make sure button will grow - } -} - - -// Tabbable tabs -// -// Hide tabbable panes to start, show them when `.active` - -.tab-content { - > .tab-pane { - display: none; - } - > .active { - display: block; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_navbar.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_navbar.scss deleted file mode 100644 index 86aa441e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_navbar.scss +++ /dev/null @@ -1,289 +0,0 @@ -// Navbar -// -// Provide a static navbar from which we expand to create full-width, fixed, and -// other navbar variations. - -.navbar { - // scss-docs-start navbar-css-vars - --#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)}; - --#{$prefix}navbar-padding-y: #{$navbar-padding-y}; - --#{$prefix}navbar-color: #{$navbar-light-color}; - --#{$prefix}navbar-hover-color: #{$navbar-light-hover-color}; - --#{$prefix}navbar-disabled-color: #{$navbar-light-disabled-color}; - --#{$prefix}navbar-active-color: #{$navbar-light-active-color}; - --#{$prefix}navbar-brand-padding-y: #{$navbar-brand-padding-y}; - --#{$prefix}navbar-brand-margin-end: #{$navbar-brand-margin-end}; - --#{$prefix}navbar-brand-font-size: #{$navbar-brand-font-size}; - --#{$prefix}navbar-brand-color: #{$navbar-light-brand-color}; - --#{$prefix}navbar-brand-hover-color: #{$navbar-light-brand-hover-color}; - --#{$prefix}navbar-nav-link-padding-x: #{$navbar-nav-link-padding-x}; - --#{$prefix}navbar-toggler-padding-y: #{$navbar-toggler-padding-y}; - --#{$prefix}navbar-toggler-padding-x: #{$navbar-toggler-padding-x}; - --#{$prefix}navbar-toggler-font-size: #{$navbar-toggler-font-size}; - --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)}; - --#{$prefix}navbar-toggler-border-color: #{$navbar-light-toggler-border-color}; - --#{$prefix}navbar-toggler-border-radius: #{$navbar-toggler-border-radius}; - --#{$prefix}navbar-toggler-focus-width: #{$navbar-toggler-focus-width}; - --#{$prefix}navbar-toggler-transition: #{$navbar-toggler-transition}; - // scss-docs-end navbar-css-vars - - position: relative; - display: flex; - flex-wrap: wrap; // allow us to do the line break for collapsing content - align-items: center; - justify-content: space-between; // space out brand from logo - padding: var(--#{$prefix}navbar-padding-y) var(--#{$prefix}navbar-padding-x); - @include gradient-bg(); - - // Because flex properties aren't inherited, we need to redeclare these first - // few properties so that content nested within behave properly. - // The `flex-wrap` property is inherited to simplify the expanded navbars - %container-flex-properties { - display: flex; - flex-wrap: inherit; - align-items: center; - justify-content: space-between; - } - - > .container, - > .container-fluid { - @extend %container-flex-properties; - } - - @each $breakpoint, $container-max-width in $container-max-widths { - > .container#{breakpoint-infix($breakpoint, $container-max-widths)} { - @extend %container-flex-properties; - } - } -} - - -// Navbar brand -// -// Used for brand, project, or site names. - -.navbar-brand { - padding-top: var(--#{$prefix}navbar-brand-padding-y); - padding-bottom: var(--#{$prefix}navbar-brand-padding-y); - margin-right: var(--#{$prefix}navbar-brand-margin-end); - @include font-size(var(--#{$prefix}navbar-brand-font-size)); - color: var(--#{$prefix}navbar-brand-color); - text-decoration: if($link-decoration == none, null, none); - white-space: nowrap; - - &:hover, - &:focus { - color: var(--#{$prefix}navbar-brand-hover-color); - text-decoration: if($link-hover-decoration == underline, none, null); - } -} - - -// Navbar nav -// -// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`). - -.navbar-nav { - // scss-docs-start navbar-nav-css-vars - --#{$prefix}nav-link-padding-x: 0; - --#{$prefix}nav-link-padding-y: #{$nav-link-padding-y}; - @include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size); - --#{$prefix}nav-link-font-weight: #{$nav-link-font-weight}; - --#{$prefix}nav-link-color: var(--#{$prefix}navbar-color); - --#{$prefix}nav-link-hover-color: var(--#{$prefix}navbar-hover-color); - --#{$prefix}nav-link-disabled-color: var(--#{$prefix}navbar-disabled-color); - // scss-docs-end navbar-nav-css-vars - - display: flex; - flex-direction: column; // cannot use `inherit` to get the `.navbar`s value - padding-left: 0; - margin-bottom: 0; - list-style: none; - - .nav-link { - &.active, - &.show { - color: var(--#{$prefix}navbar-active-color); - } - } - - .dropdown-menu { - position: static; - } -} - - -// Navbar text -// -// - -.navbar-text { - padding-top: $nav-link-padding-y; - padding-bottom: $nav-link-padding-y; - color: var(--#{$prefix}navbar-color); - - a, - a:hover, - a:focus { - color: var(--#{$prefix}navbar-active-color); - } -} - - -// Responsive navbar -// -// Custom styles for responsive collapsing and toggling of navbar contents. -// Powered by the collapse Bootstrap JavaScript plugin. - -// When collapsed, prevent the toggleable navbar contents from appearing in -// the default flexbox row orientation. Requires the use of `flex-wrap: wrap` -// on the `.navbar` parent. -.navbar-collapse { - flex-grow: 1; - flex-basis: 100%; - // For always expanded or extra full navbars, ensure content aligns itself - // properly vertically. Can be easily overridden with flex utilities. - align-items: center; -} - -// Button for toggling the navbar when in its collapsed state -.navbar-toggler { - padding: var(--#{$prefix}navbar-toggler-padding-y) var(--#{$prefix}navbar-toggler-padding-x); - @include font-size(var(--#{$prefix}navbar-toggler-font-size)); - line-height: 1; - color: var(--#{$prefix}navbar-color); - background-color: transparent; // remove default button style - border: var(--#{$prefix}border-width) solid var(--#{$prefix}navbar-toggler-border-color); // remove default button style - @include border-radius(var(--#{$prefix}navbar-toggler-border-radius)); - @include transition(var(--#{$prefix}navbar-toggler-transition)); - - &:hover { - text-decoration: none; - } - - &:focus { - text-decoration: none; - outline: 0; - box-shadow: 0 0 0 var(--#{$prefix}navbar-toggler-focus-width); - } -} - -// Keep as a separate element so folks can easily override it with another icon -// or image file as needed. -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - background-image: var(--#{$prefix}navbar-toggler-icon-bg); - background-repeat: no-repeat; - background-position: center; - background-size: 100%; -} - -.navbar-nav-scroll { - max-height: var(--#{$prefix}scroll-height, 75vh); - overflow-y: auto; -} - -// scss-docs-start navbar-expand-loop -// Generate series of `.navbar-expand-*` responsive classes for configuring -// where your navbar collapses. -.navbar-expand { - @each $breakpoint in map-keys($grid-breakpoints) { - $next: breakpoint-next($breakpoint, $grid-breakpoints); - $infix: breakpoint-infix($next, $grid-breakpoints); - - // stylelint-disable-next-line scss/selector-no-union-class-name - &#{$infix} { - @include media-breakpoint-up($next) { - flex-wrap: nowrap; - justify-content: flex-start; - - .navbar-nav { - flex-direction: row; - - .dropdown-menu { - position: absolute; - } - - .nav-link { - padding-right: var(--#{$prefix}navbar-nav-link-padding-x); - padding-left: var(--#{$prefix}navbar-nav-link-padding-x); - } - } - - .navbar-nav-scroll { - overflow: visible; - } - - .navbar-collapse { - display: flex !important; // stylelint-disable-line declaration-no-important - flex-basis: auto; - } - - .navbar-toggler { - display: none; - } - - .offcanvas { - // stylelint-disable declaration-no-important - position: static; - z-index: auto; - flex-grow: 1; - width: auto !important; - height: auto !important; - visibility: visible !important; - background-color: transparent !important; - border: 0 !important; - transform: none !important; - @include box-shadow(none); - @include transition(none); - // stylelint-enable declaration-no-important - - .offcanvas-header { - display: none; - } - - .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - } - } - } - } - } -} -// scss-docs-end navbar-expand-loop - -// Navbar themes -// -// Styles for switching between navbars with light or dark background. - -.navbar-light { - @include deprecate("`.navbar-light`", "v5.2.0", "v6.0.0", true); -} - -.navbar-dark, -.navbar[data-bs-theme="dark"] { - // scss-docs-start navbar-dark-css-vars - --#{$prefix}navbar-color: #{$navbar-dark-color}; - --#{$prefix}navbar-hover-color: #{$navbar-dark-hover-color}; - --#{$prefix}navbar-disabled-color: #{$navbar-dark-disabled-color}; - --#{$prefix}navbar-active-color: #{$navbar-dark-active-color}; - --#{$prefix}navbar-brand-color: #{$navbar-dark-brand-color}; - --#{$prefix}navbar-brand-hover-color: #{$navbar-dark-brand-hover-color}; - --#{$prefix}navbar-toggler-border-color: #{$navbar-dark-toggler-border-color}; - --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)}; - // scss-docs-end navbar-dark-css-vars -} - -@if $enable-dark-mode { - @include color-mode(dark) { - .navbar-toggler-icon { - --#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)}; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_offcanvas.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_offcanvas.scss deleted file mode 100644 index b40b2cd9..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_offcanvas.scss +++ /dev/null @@ -1,147 +0,0 @@ -// stylelint-disable function-disallowed-list - -%offcanvas-css-vars { - // scss-docs-start offcanvas-css-vars - --#{$prefix}offcanvas-zindex: #{$zindex-offcanvas}; - --#{$prefix}offcanvas-width: #{$offcanvas-horizontal-width}; - --#{$prefix}offcanvas-height: #{$offcanvas-vertical-height}; - --#{$prefix}offcanvas-padding-x: #{$offcanvas-padding-x}; - --#{$prefix}offcanvas-padding-y: #{$offcanvas-padding-y}; - --#{$prefix}offcanvas-color: #{$offcanvas-color}; - --#{$prefix}offcanvas-bg: #{$offcanvas-bg-color}; - --#{$prefix}offcanvas-border-width: #{$offcanvas-border-width}; - --#{$prefix}offcanvas-border-color: #{$offcanvas-border-color}; - --#{$prefix}offcanvas-box-shadow: #{$offcanvas-box-shadow}; - --#{$prefix}offcanvas-transition: #{transform $offcanvas-transition-duration ease-in-out}; - --#{$prefix}offcanvas-title-line-height: #{$offcanvas-title-line-height}; - // scss-docs-end offcanvas-css-vars -} - -@each $breakpoint in map-keys($grid-breakpoints) { - $next: breakpoint-next($breakpoint, $grid-breakpoints); - $infix: breakpoint-infix($next, $grid-breakpoints); - - .offcanvas#{$infix} { - @extend %offcanvas-css-vars; - } -} - -@each $breakpoint in map-keys($grid-breakpoints) { - $next: breakpoint-next($breakpoint, $grid-breakpoints); - $infix: breakpoint-infix($next, $grid-breakpoints); - - .offcanvas#{$infix} { - @include media-breakpoint-down($next) { - position: fixed; - bottom: 0; - z-index: var(--#{$prefix}offcanvas-zindex); - display: flex; - flex-direction: column; - max-width: 100%; - color: var(--#{$prefix}offcanvas-color); - visibility: hidden; - background-color: var(--#{$prefix}offcanvas-bg); - background-clip: padding-box; - outline: 0; - @include box-shadow(var(--#{$prefix}offcanvas-box-shadow)); - @include transition(var(--#{$prefix}offcanvas-transition)); - - &.offcanvas-start { - top: 0; - left: 0; - width: var(--#{$prefix}offcanvas-width); - border-right: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); - transform: translateX(-100%); - } - - &.offcanvas-end { - top: 0; - right: 0; - width: var(--#{$prefix}offcanvas-width); - border-left: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); - transform: translateX(100%); - } - - &.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: var(--#{$prefix}offcanvas-height); - max-height: 100%; - border-bottom: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); - transform: translateY(-100%); - } - - &.offcanvas-bottom { - right: 0; - left: 0; - height: var(--#{$prefix}offcanvas-height); - max-height: 100%; - border-top: var(--#{$prefix}offcanvas-border-width) solid var(--#{$prefix}offcanvas-border-color); - transform: translateY(100%); - } - - &.showing, - &.show:not(.hiding) { - transform: none; - } - - &.showing, - &.hiding, - &.show { - visibility: visible; - } - } - - @if not ($infix == "") { - @include media-breakpoint-up($next) { - --#{$prefix}offcanvas-height: auto; - --#{$prefix}offcanvas-border-width: 0; - background-color: transparent !important; // stylelint-disable-line declaration-no-important - - .offcanvas-header { - display: none; - } - - .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; - // Reset `background-color` in case `.bg-*` classes are used in offcanvas - background-color: transparent !important; // stylelint-disable-line declaration-no-important - } - } - } - } -} - -.offcanvas-backdrop { - @include overlay-backdrop($zindex-offcanvas-backdrop, $offcanvas-backdrop-bg, $offcanvas-backdrop-opacity); -} - -.offcanvas-header { - display: flex; - align-items: center; - padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x); - - .btn-close { - padding: calc(var(--#{$prefix}offcanvas-padding-y) * .5) calc(var(--#{$prefix}offcanvas-padding-x) * .5); - // Split properties to avoid invalid calc() function if value is 0 - margin-top: calc(-.5 * var(--#{$prefix}offcanvas-padding-y)); - margin-right: calc(-.5 * var(--#{$prefix}offcanvas-padding-x)); - margin-bottom: calc(-.5 * var(--#{$prefix}offcanvas-padding-y)); - margin-left: auto; - } -} - -.offcanvas-title { - margin-bottom: 0; - line-height: var(--#{$prefix}offcanvas-title-line-height); -} - -.offcanvas-body { - flex-grow: 1; - padding: var(--#{$prefix}offcanvas-padding-y) var(--#{$prefix}offcanvas-padding-x); - overflow-y: auto; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_pagination.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_pagination.scss deleted file mode 100644 index 9f09694c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_pagination.scss +++ /dev/null @@ -1,109 +0,0 @@ -.pagination { - // scss-docs-start pagination-css-vars - --#{$prefix}pagination-padding-x: #{$pagination-padding-x}; - --#{$prefix}pagination-padding-y: #{$pagination-padding-y}; - @include rfs($pagination-font-size, --#{$prefix}pagination-font-size); - --#{$prefix}pagination-color: #{$pagination-color}; - --#{$prefix}pagination-bg: #{$pagination-bg}; - --#{$prefix}pagination-border-width: #{$pagination-border-width}; - --#{$prefix}pagination-border-color: #{$pagination-border-color}; - --#{$prefix}pagination-border-radius: #{$pagination-border-radius}; - --#{$prefix}pagination-hover-color: #{$pagination-hover-color}; - --#{$prefix}pagination-hover-bg: #{$pagination-hover-bg}; - --#{$prefix}pagination-hover-border-color: #{$pagination-hover-border-color}; - --#{$prefix}pagination-focus-color: #{$pagination-focus-color}; - --#{$prefix}pagination-focus-bg: #{$pagination-focus-bg}; - --#{$prefix}pagination-focus-box-shadow: #{$pagination-focus-box-shadow}; - --#{$prefix}pagination-active-color: #{$pagination-active-color}; - --#{$prefix}pagination-active-bg: #{$pagination-active-bg}; - --#{$prefix}pagination-active-border-color: #{$pagination-active-border-color}; - --#{$prefix}pagination-disabled-color: #{$pagination-disabled-color}; - --#{$prefix}pagination-disabled-bg: #{$pagination-disabled-bg}; - --#{$prefix}pagination-disabled-border-color: #{$pagination-disabled-border-color}; - // scss-docs-end pagination-css-vars - - display: flex; - @include list-unstyled(); -} - -.page-link { - position: relative; - display: block; - padding: var(--#{$prefix}pagination-padding-y) var(--#{$prefix}pagination-padding-x); - @include font-size(var(--#{$prefix}pagination-font-size)); - color: var(--#{$prefix}pagination-color); - text-decoration: if($link-decoration == none, null, none); - background-color: var(--#{$prefix}pagination-bg); - border: var(--#{$prefix}pagination-border-width) solid var(--#{$prefix}pagination-border-color); - @include transition($pagination-transition); - - &:hover { - z-index: 2; - color: var(--#{$prefix}pagination-hover-color); - text-decoration: if($link-hover-decoration == underline, none, null); - background-color: var(--#{$prefix}pagination-hover-bg); - border-color: var(--#{$prefix}pagination-hover-border-color); - } - - &:focus { - z-index: 3; - color: var(--#{$prefix}pagination-focus-color); - background-color: var(--#{$prefix}pagination-focus-bg); - outline: $pagination-focus-outline; - box-shadow: var(--#{$prefix}pagination-focus-box-shadow); - } - - &.active, - .active > & { - z-index: 3; - color: var(--#{$prefix}pagination-active-color); - @include gradient-bg(var(--#{$prefix}pagination-active-bg)); - border-color: var(--#{$prefix}pagination-active-border-color); - } - - &.disabled, - .disabled > & { - color: var(--#{$prefix}pagination-disabled-color); - pointer-events: none; - background-color: var(--#{$prefix}pagination-disabled-bg); - border-color: var(--#{$prefix}pagination-disabled-border-color); - } -} - -.page-item { - &:not(:first-child) .page-link { - margin-left: $pagination-margin-start; - } - - @if $pagination-margin-start == calc(-1 * #{$pagination-border-width}) { - &:first-child { - .page-link { - @include border-start-radius(var(--#{$prefix}pagination-border-radius)); - } - } - - &:last-child { - .page-link { - @include border-end-radius(var(--#{$prefix}pagination-border-radius)); - } - } - } @else { - // Add border-radius to all pageLinks in case they have left margin - .page-link { - @include border-radius(var(--#{$prefix}pagination-border-radius)); - } - } -} - - -// -// Sizing -// - -.pagination-lg { - @include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $pagination-border-radius-lg); -} - -.pagination-sm { - @include pagination-size($pagination-padding-y-sm, $pagination-padding-x-sm, $font-size-sm, $pagination-border-radius-sm); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_placeholders.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_placeholders.scss deleted file mode 100644 index 6e32e1cd..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_placeholders.scss +++ /dev/null @@ -1,51 +0,0 @@ -.placeholder { - display: inline-block; - min-height: 1em; - vertical-align: middle; - cursor: wait; - background-color: currentcolor; - opacity: $placeholder-opacity-max; - - &.btn::before { - display: inline-block; - content: ""; - } -} - -// Sizing -.placeholder-xs { - min-height: .6em; -} - -.placeholder-sm { - min-height: .8em; -} - -.placeholder-lg { - min-height: 1.2em; -} - -// Animation -.placeholder-glow { - .placeholder { - animation: placeholder-glow 2s ease-in-out infinite; - } -} - -@keyframes placeholder-glow { - 50% { - opacity: $placeholder-opacity-min; - } -} - -.placeholder-wave { - mask-image: linear-gradient(130deg, $black 55%, rgba(0, 0, 0, (1 - $placeholder-opacity-min)) 75%, $black 95%); - mask-size: 200% 100%; - animation: placeholder-wave 2s linear infinite; -} - -@keyframes placeholder-wave { - 100% { - mask-position: -200% 0%; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_popover.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_popover.scss deleted file mode 100644 index 7b69f623..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_popover.scss +++ /dev/null @@ -1,196 +0,0 @@ -.popover { - // scss-docs-start popover-css-vars - --#{$prefix}popover-zindex: #{$zindex-popover}; - --#{$prefix}popover-max-width: #{$popover-max-width}; - @include rfs($popover-font-size, --#{$prefix}popover-font-size); - --#{$prefix}popover-bg: #{$popover-bg}; - --#{$prefix}popover-border-width: #{$popover-border-width}; - --#{$prefix}popover-border-color: #{$popover-border-color}; - --#{$prefix}popover-border-radius: #{$popover-border-radius}; - --#{$prefix}popover-inner-border-radius: #{$popover-inner-border-radius}; - --#{$prefix}popover-box-shadow: #{$popover-box-shadow}; - --#{$prefix}popover-header-padding-x: #{$popover-header-padding-x}; - --#{$prefix}popover-header-padding-y: #{$popover-header-padding-y}; - @include rfs($popover-header-font-size, --#{$prefix}popover-header-font-size); - --#{$prefix}popover-header-color: #{$popover-header-color}; - --#{$prefix}popover-header-bg: #{$popover-header-bg}; - --#{$prefix}popover-body-padding-x: #{$popover-body-padding-x}; - --#{$prefix}popover-body-padding-y: #{$popover-body-padding-y}; - --#{$prefix}popover-body-color: #{$popover-body-color}; - --#{$prefix}popover-arrow-width: #{$popover-arrow-width}; - --#{$prefix}popover-arrow-height: #{$popover-arrow-height}; - --#{$prefix}popover-arrow-border: var(--#{$prefix}popover-border-color); - // scss-docs-end popover-css-vars - - z-index: var(--#{$prefix}popover-zindex); - display: block; - max-width: var(--#{$prefix}popover-max-width); - // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. - // So reset our font and text properties to avoid inheriting weird values. - @include reset-text(); - @include font-size(var(--#{$prefix}popover-font-size)); - // Allow breaking very long words so they don't overflow the popover's bounds - word-wrap: break-word; - background-color: var(--#{$prefix}popover-bg); - background-clip: padding-box; - border: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-border-color); - @include border-radius(var(--#{$prefix}popover-border-radius)); - @include box-shadow(var(--#{$prefix}popover-box-shadow)); - - .popover-arrow { - display: block; - width: var(--#{$prefix}popover-arrow-width); - height: var(--#{$prefix}popover-arrow-height); - - &::before, - &::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; - border-width: 0; - } - } -} - -.bs-popover-top { - > .popover-arrow { - bottom: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list - - &::before, - &::after { - border-width: var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list - } - - &::before { - bottom: 0; - border-top-color: var(--#{$prefix}popover-arrow-border); - } - - &::after { - bottom: var(--#{$prefix}popover-border-width); - border-top-color: var(--#{$prefix}popover-bg); - } - } -} - -/* rtl:begin:ignore */ -.bs-popover-end { - > .popover-arrow { - left: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list - width: var(--#{$prefix}popover-arrow-height); - height: var(--#{$prefix}popover-arrow-width); - - &::before, - &::after { - border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list - } - - &::before { - left: 0; - border-right-color: var(--#{$prefix}popover-arrow-border); - } - - &::after { - left: var(--#{$prefix}popover-border-width); - border-right-color: var(--#{$prefix}popover-bg); - } - } -} - -/* rtl:end:ignore */ - -.bs-popover-bottom { - > .popover-arrow { - top: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list - - &::before, - &::after { - border-width: 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list - } - - &::before { - top: 0; - border-bottom-color: var(--#{$prefix}popover-arrow-border); - } - - &::after { - top: var(--#{$prefix}popover-border-width); - border-bottom-color: var(--#{$prefix}popover-bg); - } - } - - // This will remove the popover-header's border just below the arrow - .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: var(--#{$prefix}popover-arrow-width); - margin-left: calc(-.5 * var(--#{$prefix}popover-arrow-width)); // stylelint-disable-line function-disallowed-list - content: ""; - border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-header-bg); - } -} - -/* rtl:begin:ignore */ -.bs-popover-start { - > .popover-arrow { - right: calc(-1 * (var(--#{$prefix}popover-arrow-height)) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list - width: var(--#{$prefix}popover-arrow-height); - height: var(--#{$prefix}popover-arrow-width); - - &::before, - &::after { - border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list - } - - &::before { - right: 0; - border-left-color: var(--#{$prefix}popover-arrow-border); - } - - &::after { - right: var(--#{$prefix}popover-border-width); - border-left-color: var(--#{$prefix}popover-bg); - } - } -} - -/* rtl:end:ignore */ - -.bs-popover-auto { - &[data-popper-placement^="top"] { - @extend .bs-popover-top; - } - &[data-popper-placement^="right"] { - @extend .bs-popover-end; - } - &[data-popper-placement^="bottom"] { - @extend .bs-popover-bottom; - } - &[data-popper-placement^="left"] { - @extend .bs-popover-start; - } -} - -// Offset the popover to account for the popover arrow -.popover-header { - padding: var(--#{$prefix}popover-header-padding-y) var(--#{$prefix}popover-header-padding-x); - margin-bottom: 0; // Reset the default from Reboot - @include font-size(var(--#{$prefix}popover-header-font-size)); - color: var(--#{$prefix}popover-header-color); - background-color: var(--#{$prefix}popover-header-bg); - border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-border-color); - @include border-top-radius(var(--#{$prefix}popover-inner-border-radius)); - - &:empty { - display: none; - } -} - -.popover-body { - padding: var(--#{$prefix}popover-body-padding-y) var(--#{$prefix}popover-body-padding-x); - color: var(--#{$prefix}popover-body-color); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_progress.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_progress.scss deleted file mode 100644 index 732365c5..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_progress.scss +++ /dev/null @@ -1,68 +0,0 @@ -// Disable animation if transitions are disabled - -// scss-docs-start progress-keyframes -@if $enable-transitions { - @keyframes progress-bar-stripes { - 0% { background-position-x: var(--#{$prefix}progress-height); } - } -} -// scss-docs-end progress-keyframes - -.progress, -.progress-stacked { - // scss-docs-start progress-css-vars - --#{$prefix}progress-height: #{$progress-height}; - @include rfs($progress-font-size, --#{$prefix}progress-font-size); - --#{$prefix}progress-bg: #{$progress-bg}; - --#{$prefix}progress-border-radius: #{$progress-border-radius}; - --#{$prefix}progress-box-shadow: #{$progress-box-shadow}; - --#{$prefix}progress-bar-color: #{$progress-bar-color}; - --#{$prefix}progress-bar-bg: #{$progress-bar-bg}; - --#{$prefix}progress-bar-transition: #{$progress-bar-transition}; - // scss-docs-end progress-css-vars - - display: flex; - height: var(--#{$prefix}progress-height); - overflow: hidden; // force rounded corners by cropping it - @include font-size(var(--#{$prefix}progress-font-size)); - background-color: var(--#{$prefix}progress-bg); - @include border-radius(var(--#{$prefix}progress-border-radius)); - @include box-shadow(var(--#{$prefix}progress-box-shadow)); -} - -.progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - color: var(--#{$prefix}progress-bar-color); - text-align: center; - white-space: nowrap; - background-color: var(--#{$prefix}progress-bar-bg); - @include transition(var(--#{$prefix}progress-bar-transition)); -} - -.progress-bar-striped { - @include gradient-striped(); - background-size: var(--#{$prefix}progress-height) var(--#{$prefix}progress-height); -} - -.progress-stacked > .progress { - overflow: visible; -} - -.progress-stacked > .progress > .progress-bar { - width: 100%; -} - -@if $enable-transitions { - .progress-bar-animated { - animation: $progress-bar-animation-timing progress-bar-stripes; - - @if $enable-reduced-motion { - @media (prefers-reduced-motion: reduce) { - animation: none; - } - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_reboot.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_reboot.scss deleted file mode 100644 index 524645fb..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_reboot.scss +++ /dev/null @@ -1,617 +0,0 @@ -// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix - - -// Reboot -// -// Normalization of HTML elements, manually forked from Normalize.css to remove -// styles targeting irrelevant browsers while applying new styles. -// -// Normalize is licensed MIT. https://github.com/necolas/normalize.css - - -// Document -// -// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`. - -*, -*::before, -*::after { - box-sizing: border-box; -} - - -// Root -// -// Ability to the value of the root font sizes, affecting the value of `rem`. -// null by default, thus nothing is generated. - -:root { - @if $font-size-root != null { - @include font-size(var(--#{$prefix}root-font-size)); - } - - @if $enable-smooth-scroll { - @media (prefers-reduced-motion: no-preference) { - scroll-behavior: smooth; - } - } -} - - -// Body -// -// 1. Remove the margin in all browsers. -// 2. As a best practice, apply a default `background-color`. -// 3. Prevent adjustments of font size after orientation changes in iOS. -// 4. Change the default tap highlight to be completely transparent in iOS. - -// scss-docs-start reboot-body-rules -body { - margin: 0; // 1 - font-family: var(--#{$prefix}body-font-family); - @include font-size(var(--#{$prefix}body-font-size)); - font-weight: var(--#{$prefix}body-font-weight); - line-height: var(--#{$prefix}body-line-height); - color: var(--#{$prefix}body-color); - text-align: var(--#{$prefix}body-text-align); - background-color: var(--#{$prefix}body-bg); // 2 - -webkit-text-size-adjust: 100%; // 3 - -webkit-tap-highlight-color: rgba($black, 0); // 4 -} -// scss-docs-end reboot-body-rules - - -// Content grouping -// -// 1. Reset Firefox's gray color - -hr { - margin: $hr-margin-y 0; - color: $hr-color; // 1 - border: 0; - border-top: $hr-border-width solid $hr-border-color; - opacity: $hr-opacity; -} - - -// Typography -// -// 1. Remove top margins from headings -// By default, `<h1>`-`<h6>` all receive top and bottom margins. We nuke the top -// margin for easier control within type scales as it avoids margin collapsing. - -%heading { - margin-top: 0; // 1 - margin-bottom: $headings-margin-bottom; - font-family: $headings-font-family; - font-style: $headings-font-style; - font-weight: $headings-font-weight; - line-height: $headings-line-height; - color: var(--#{$prefix}heading-color); -} - -h1 { - @extend %heading; - @include font-size($h1-font-size); -} - -h2 { - @extend %heading; - @include font-size($h2-font-size); -} - -h3 { - @extend %heading; - @include font-size($h3-font-size); -} - -h4 { - @extend %heading; - @include font-size($h4-font-size); -} - -h5 { - @extend %heading; - @include font-size($h5-font-size); -} - -h6 { - @extend %heading; - @include font-size($h6-font-size); -} - - -// Reset margins on paragraphs -// -// Similarly, the top margin on `<p>`s get reset. However, we also reset the -// bottom margin to use `rem` units instead of `em`. - -p { - margin-top: 0; - margin-bottom: $paragraph-margin-bottom; -} - - -// Abbreviations -// -// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari. -// 2. Add explicit cursor to indicate changed behavior. -// 3. Prevent the text-decoration to be skipped. - -abbr[title] { - text-decoration: underline dotted; // 1 - cursor: help; // 2 - text-decoration-skip-ink: none; // 3 -} - - -// Address - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - - -// Lists - -ol, -ul { - padding-left: 2rem; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: $dt-font-weight; -} - -// 1. Undo browser default - -dd { - margin-bottom: .5rem; - margin-left: 0; // 1 -} - - -// Blockquote - -blockquote { - margin: 0 0 1rem; -} - - -// Strong -// -// Add the correct font weight in Chrome, Edge, and Safari - -b, -strong { - font-weight: $font-weight-bolder; -} - - -// Small -// -// Add the correct font size in all browsers - -small { - @include font-size($small-font-size); -} - - -// Mark - -mark { - padding: $mark-padding; - color: var(--#{$prefix}highlight-color); - background-color: var(--#{$prefix}highlight-bg); -} - - -// Sub and Sup -// -// Prevent `sub` and `sup` elements from affecting the line height in -// all browsers. - -sub, -sup { - position: relative; - @include font-size($sub-sup-font-size); - line-height: 0; - vertical-align: baseline; -} - -sub { bottom: -.25em; } -sup { top: -.5em; } - - -// Links - -a { - color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1)); - text-decoration: $link-decoration; - - &:hover { - --#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb); - text-decoration: $link-hover-decoration; - } -} - -// And undo these styles for placeholder links/named anchors (without href). -// It would be more straightforward to just use a[href] in previous block, but that -// causes specificity issues in many other styles that are too complex to fix. -// See https://github.com/twbs/bootstrap/issues/19402 - -a:not([href]):not([class]) { - &, - &:hover { - color: inherit; - text-decoration: none; - } -} - - -// Code - -pre, -code, -kbd, -samp { - font-family: $font-family-code; - @include font-size(1em); // Correct the odd `em` font sizing in all browsers. -} - -// 1. Remove browser default top margin -// 2. Reset browser default of `1em` to use `rem`s -// 3. Don't allow content to break outside - -pre { - display: block; - margin-top: 0; // 1 - margin-bottom: 1rem; // 2 - overflow: auto; // 3 - @include font-size($code-font-size); - color: $pre-color; - - // Account for some code outputs that place code tags in pre tags - code { - @include font-size(inherit); - color: inherit; - word-break: normal; - } -} - -code { - @include font-size($code-font-size); - color: var(--#{$prefix}code-color); - word-wrap: break-word; - - // Streamline the style when inside anchors to avoid broken underline and more - a > & { - color: inherit; - } -} - -kbd { - padding: $kbd-padding-y $kbd-padding-x; - @include font-size($kbd-font-size); - color: $kbd-color; - background-color: $kbd-bg; - @include border-radius($border-radius-sm); - - kbd { - padding: 0; - @include font-size(1em); - font-weight: $nested-kbd-font-weight; - } -} - - -// Figures -// -// Apply a consistent margin strategy (matches our type styles). - -figure { - margin: 0 0 1rem; -} - - -// Images and content - -img, -svg { - vertical-align: middle; -} - - -// Tables -// -// Prevent double borders - -table { - caption-side: bottom; - border-collapse: collapse; -} - -caption { - padding-top: $table-cell-padding-y; - padding-bottom: $table-cell-padding-y; - color: $table-caption-color; - text-align: left; -} - -// 1. Removes font-weight bold by inheriting -// 2. Matches default `<td>` alignment by inheriting `text-align`. -// 3. Fix alignment for Safari - -th { - font-weight: $table-th-font-weight; // 1 - text-align: inherit; // 2 - text-align: -webkit-match-parent; // 3 -} - -thead, -tbody, -tfoot, -tr, -td, -th { - border-color: inherit; - border-style: solid; - border-width: 0; -} - - -// Forms -// -// 1. Allow labels to use `margin` for spacing. - -label { - display: inline-block; // 1 -} - -// Remove the default `border-radius` that macOS Chrome adds. -// See https://github.com/twbs/bootstrap/issues/24093 - -button { - // stylelint-disable-next-line property-disallowed-list - border-radius: 0; -} - -// Explicitly remove focus outline in Chromium when it shouldn't be -// visible (e.g. as result of mouse click or touch tap). It already -// should be doing this automatically, but seems to currently be -// confused and applies its very visible two-tone outline anyway. - -button:focus:not(:focus-visible) { - outline: 0; -} - -// 1. Remove the margin in Firefox and Safari - -input, -button, -select, -optgroup, -textarea { - margin: 0; // 1 - font-family: inherit; - @include font-size(inherit); - line-height: inherit; -} - -// Remove the inheritance of text transform in Firefox -button, -select { - text-transform: none; -} -// Set the cursor for non-`<button>` buttons -// -// Details at https://github.com/twbs/bootstrap/pull/30562 -[role="button"] { - cursor: pointer; -} - -select { - // Remove the inheritance of word-wrap in Safari. - // See https://github.com/twbs/bootstrap/issues/24990 - word-wrap: normal; - - // Undo the opacity change from Chrome - &:disabled { - opacity: 1; - } -} - -// Remove the dropdown arrow only from text type inputs built with datalists in Chrome. -// See https://stackoverflow.com/a/54997118 - -[list]:not([type="date"]):not([type="datetime-local"]):not([type="month"]):not([type="week"]):not([type="time"])::-webkit-calendar-picker-indicator { - display: none !important; -} - -// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` -// controls in Android 4. -// 2. Correct the inability to style clickable types in iOS and Safari. -// 3. Opinionated: add "hand" cursor to non-disabled button elements. - -button, -[type="button"], // 1 -[type="reset"], -[type="submit"] { - -webkit-appearance: button; // 2 - - @if $enable-button-pointers { - &:not(:disabled) { - cursor: pointer; // 3 - } - } -} - -// Remove inner border and padding from Firefox, but don't restore the outline like Normalize. - -::-moz-focus-inner { - padding: 0; - border-style: none; -} - -// 1. Textareas should really only resize vertically so they don't break their (horizontal) containers. - -textarea { - resize: vertical; // 1 -} - -// 1. Browsers set a default `min-width: min-content;` on fieldsets, -// unlike e.g. `<div>`s, which have `min-width: 0;` by default. -// So we reset that to ensure fieldsets behave more like a standard block element. -// See https://github.com/twbs/bootstrap/issues/12359 -// and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements -// 2. Reset the default outline behavior of fieldsets so they don't affect page layout. - -fieldset { - min-width: 0; // 1 - padding: 0; // 2 - margin: 0; // 2 - border: 0; // 2 -} - -// 1. By using `float: left`, the legend will behave like a block element. -// This way the border of a fieldset wraps around the legend if present. -// 2. Fix wrapping bug. -// See https://github.com/twbs/bootstrap/issues/29712 - -legend { - float: left; // 1 - width: 100%; - padding: 0; - margin-bottom: $legend-margin-bottom; - font-weight: $legend-font-weight; - line-height: inherit; - @include font-size($legend-font-size); - - + * { - clear: left; // 2 - } -} - -// Fix height of inputs with a type of datetime-local, date, month, week, or time -// See https://github.com/twbs/bootstrap/issues/18842 - -::-webkit-datetime-edit-fields-wrapper, -::-webkit-datetime-edit-text, -::-webkit-datetime-edit-minute, -::-webkit-datetime-edit-hour-field, -::-webkit-datetime-edit-day-field, -::-webkit-datetime-edit-month-field, -::-webkit-datetime-edit-year-field { - padding: 0; -} - -::-webkit-inner-spin-button { - height: auto; -} - -// 1. This overrides the extra rounded corners on search inputs in iOS so that our -// `.form-control` class can properly style them. Note that this cannot simply -// be added to `.form-control` as it's not specific enough. For details, see -// https://github.com/twbs/bootstrap/issues/11586. -// 2. Correct the outline style in Safari. - -[type="search"] { - -webkit-appearance: textfield; // 1 - outline-offset: -2px; // 2 - - // 3. Better affordance and consistent appearance for search cancel button - &::-webkit-search-cancel-button { - cursor: pointer; - filter: grayscale(1); - } -} - -// 1. A few input types should stay LTR -// See https://rtlstyling.com/posts/rtl-styling#form-inputs -// 2. RTL only output -// See https://rtlcss.com/learn/usage-guide/control-directives/#raw - -/* rtl:raw: -[type="tel"], -[type="url"], -[type="email"], -[type="number"] { - direction: ltr; -} -*/ - -// Remove the inner padding in Chrome and Safari on macOS. - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -// Remove padding around color pickers in webkit browsers - -::-webkit-color-swatch-wrapper { - padding: 0; -} - - -// 1. Inherit font family and line height for file input buttons -// 2. Correct the inability to style clickable types in iOS and Safari. - -::file-selector-button { - font: inherit; // 1 - -webkit-appearance: button; // 2 -} - -// Correct element displays - -output { - display: inline-block; -} - -// Remove border from iframe - -iframe { - border: 0; -} - -// Summary -// -// 1. Add the correct display in all browsers - -summary { - display: list-item; // 1 - cursor: pointer; -} - - -// Progress -// -// Add the correct vertical alignment in Chrome, Firefox, and Opera. - -progress { - vertical-align: baseline; -} - - -// Hidden attribute -// -// Always hide an element with the `hidden` HTML attribute. - -[hidden] { - display: none !important; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_root.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_root.scss deleted file mode 100644 index becddf14..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_root.scss +++ /dev/null @@ -1,187 +0,0 @@ -:root, -[data-bs-theme="light"] { - // Note: Custom variable values only support SassScript inside `#{}`. - - // Colors - // - // Generate palettes for full colors, grays, and theme colors. - - @each $color, $value in $colors { - --#{$prefix}#{$color}: #{$value}; - } - - @each $color, $value in $grays { - --#{$prefix}gray-#{$color}: #{$value}; - } - - @each $color, $value in $theme-colors { - --#{$prefix}#{$color}: #{$value}; - } - - @each $color, $value in $theme-colors-rgb { - --#{$prefix}#{$color}-rgb: #{$value}; - } - - @each $color, $value in $theme-colors-text { - --#{$prefix}#{$color}-text-emphasis: #{$value}; - } - - @each $color, $value in $theme-colors-bg-subtle { - --#{$prefix}#{$color}-bg-subtle: #{$value}; - } - - @each $color, $value in $theme-colors-border-subtle { - --#{$prefix}#{$color}-border-subtle: #{$value}; - } - - --#{$prefix}white-rgb: #{to-rgb($white)}; - --#{$prefix}black-rgb: #{to-rgb($black)}; - - // Fonts - - // Note: Use `inspect` for lists so that quoted items keep the quotes. - // See https://github.com/sass/sass/issues/2383#issuecomment-336349172 - --#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)}; - --#{$prefix}font-monospace: #{inspect($font-family-monospace)}; - --#{$prefix}gradient: #{$gradient}; - - // Root and body - // scss-docs-start root-body-variables - @if $font-size-root != null { - --#{$prefix}root-font-size: #{$font-size-root}; - } - --#{$prefix}body-font-family: #{inspect($font-family-base)}; - @include rfs($font-size-base, --#{$prefix}body-font-size); - --#{$prefix}body-font-weight: #{$font-weight-base}; - --#{$prefix}body-line-height: #{$line-height-base}; - @if $body-text-align != null { - --#{$prefix}body-text-align: #{$body-text-align}; - } - - --#{$prefix}body-color: #{$body-color}; - --#{$prefix}body-color-rgb: #{to-rgb($body-color)}; - --#{$prefix}body-bg: #{$body-bg}; - --#{$prefix}body-bg-rgb: #{to-rgb($body-bg)}; - - --#{$prefix}emphasis-color: #{$body-emphasis-color}; - --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color)}; - - --#{$prefix}secondary-color: #{$body-secondary-color}; - --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color)}; - --#{$prefix}secondary-bg: #{$body-secondary-bg}; - --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg)}; - - --#{$prefix}tertiary-color: #{$body-tertiary-color}; - --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color)}; - --#{$prefix}tertiary-bg: #{$body-tertiary-bg}; - --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg)}; - // scss-docs-end root-body-variables - - --#{$prefix}heading-color: #{$headings-color}; - - --#{$prefix}link-color: #{$link-color}; - --#{$prefix}link-color-rgb: #{to-rgb($link-color)}; - --#{$prefix}link-decoration: #{$link-decoration}; - - --#{$prefix}link-hover-color: #{$link-hover-color}; - --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color)}; - - @if $link-hover-decoration != null { - --#{$prefix}link-hover-decoration: #{$link-hover-decoration}; - } - - --#{$prefix}code-color: #{$code-color}; - --#{$prefix}highlight-color: #{$mark-color}; - --#{$prefix}highlight-bg: #{$mark-bg}; - - // scss-docs-start root-border-var - --#{$prefix}border-width: #{$border-width}; - --#{$prefix}border-style: #{$border-style}; - --#{$prefix}border-color: #{$border-color}; - --#{$prefix}border-color-translucent: #{$border-color-translucent}; - - --#{$prefix}border-radius: #{$border-radius}; - --#{$prefix}border-radius-sm: #{$border-radius-sm}; - --#{$prefix}border-radius-lg: #{$border-radius-lg}; - --#{$prefix}border-radius-xl: #{$border-radius-xl}; - --#{$prefix}border-radius-xxl: #{$border-radius-xxl}; - --#{$prefix}border-radius-2xl: var(--#{$prefix}border-radius-xxl); // Deprecated in v5.3.0 for consistency - --#{$prefix}border-radius-pill: #{$border-radius-pill}; - // scss-docs-end root-border-var - - --#{$prefix}box-shadow: #{$box-shadow}; - --#{$prefix}box-shadow-sm: #{$box-shadow-sm}; - --#{$prefix}box-shadow-lg: #{$box-shadow-lg}; - --#{$prefix}box-shadow-inset: #{$box-shadow-inset}; - - // Focus styles - // scss-docs-start root-focus-variables - --#{$prefix}focus-ring-width: #{$focus-ring-width}; - --#{$prefix}focus-ring-opacity: #{$focus-ring-opacity}; - --#{$prefix}focus-ring-color: #{$focus-ring-color}; - // scss-docs-end root-focus-variables - - // scss-docs-start root-form-validation-variables - --#{$prefix}form-valid-color: #{$form-valid-color}; - --#{$prefix}form-valid-border-color: #{$form-valid-border-color}; - --#{$prefix}form-invalid-color: #{$form-invalid-color}; - --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color}; - // scss-docs-end root-form-validation-variables -} - -@if $enable-dark-mode { - @include color-mode(dark, true) { - color-scheme: dark; - - // scss-docs-start root-dark-mode-vars - --#{$prefix}body-color: #{$body-color-dark}; - --#{$prefix}body-color-rgb: #{to-rgb($body-color-dark)}; - --#{$prefix}body-bg: #{$body-bg-dark}; - --#{$prefix}body-bg-rgb: #{to-rgb($body-bg-dark)}; - - --#{$prefix}emphasis-color: #{$body-emphasis-color-dark}; - --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color-dark)}; - - --#{$prefix}secondary-color: #{$body-secondary-color-dark}; - --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color-dark)}; - --#{$prefix}secondary-bg: #{$body-secondary-bg-dark}; - --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg-dark)}; - - --#{$prefix}tertiary-color: #{$body-tertiary-color-dark}; - --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color-dark)}; - --#{$prefix}tertiary-bg: #{$body-tertiary-bg-dark}; - --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg-dark)}; - - @each $color, $value in $theme-colors-text-dark { - --#{$prefix}#{$color}-text-emphasis: #{$value}; - } - - @each $color, $value in $theme-colors-bg-subtle-dark { - --#{$prefix}#{$color}-bg-subtle: #{$value}; - } - - @each $color, $value in $theme-colors-border-subtle-dark { - --#{$prefix}#{$color}-border-subtle: #{$value}; - } - - --#{$prefix}heading-color: #{$headings-color-dark}; - - --#{$prefix}link-color: #{$link-color-dark}; - --#{$prefix}link-hover-color: #{$link-hover-color-dark}; - --#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)}; - --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)}; - - --#{$prefix}code-color: #{$code-color-dark}; - --#{$prefix}highlight-color: #{$mark-color-dark}; - --#{$prefix}highlight-bg: #{$mark-bg-dark}; - - --#{$prefix}border-color: #{$border-color-dark}; - --#{$prefix}border-color-translucent: #{$border-color-translucent-dark}; - - --#{$prefix}form-valid-color: #{$form-valid-color-dark}; - --#{$prefix}form-valid-border-color: #{$form-valid-border-color-dark}; - --#{$prefix}form-invalid-color: #{$form-invalid-color-dark}; - --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color-dark}; - // scss-docs-end root-dark-mode-vars - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_spinners.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_spinners.scss deleted file mode 100644 index 9dff2892..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_spinners.scss +++ /dev/null @@ -1,86 +0,0 @@ -// -// Rotating border -// - -.spinner-grow, -.spinner-border { - display: inline-block; - flex-shrink: 0; - width: var(--#{$prefix}spinner-width); - height: var(--#{$prefix}spinner-height); - vertical-align: var(--#{$prefix}spinner-vertical-align); - // stylelint-disable-next-line property-disallowed-list - border-radius: 50%; - animation: var(--#{$prefix}spinner-animation-speed) linear infinite var(--#{$prefix}spinner-animation-name); -} - -// scss-docs-start spinner-border-keyframes -@keyframes spinner-border { - to { transform: rotate(360deg) #{"/* rtl:ignore */"}; } -} -// scss-docs-end spinner-border-keyframes - -.spinner-border { - // scss-docs-start spinner-border-css-vars - --#{$prefix}spinner-width: #{$spinner-width}; - --#{$prefix}spinner-height: #{$spinner-height}; - --#{$prefix}spinner-vertical-align: #{$spinner-vertical-align}; - --#{$prefix}spinner-border-width: #{$spinner-border-width}; - --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed}; - --#{$prefix}spinner-animation-name: spinner-border; - // scss-docs-end spinner-border-css-vars - - border: var(--#{$prefix}spinner-border-width) solid currentcolor; - border-right-color: transparent; -} - -.spinner-border-sm { - // scss-docs-start spinner-border-sm-css-vars - --#{$prefix}spinner-width: #{$spinner-width-sm}; - --#{$prefix}spinner-height: #{$spinner-height-sm}; - --#{$prefix}spinner-border-width: #{$spinner-border-width-sm}; - // scss-docs-end spinner-border-sm-css-vars -} - -// -// Growing circle -// - -// scss-docs-start spinner-grow-keyframes -@keyframes spinner-grow { - 0% { - transform: scale(0); - } - 50% { - opacity: 1; - transform: none; - } -} -// scss-docs-end spinner-grow-keyframes - -.spinner-grow { - // scss-docs-start spinner-grow-css-vars - --#{$prefix}spinner-width: #{$spinner-width}; - --#{$prefix}spinner-height: #{$spinner-height}; - --#{$prefix}spinner-vertical-align: #{$spinner-vertical-align}; - --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed}; - --#{$prefix}spinner-animation-name: spinner-grow; - // scss-docs-end spinner-grow-css-vars - - background-color: currentcolor; - opacity: 0; -} - -.spinner-grow-sm { - --#{$prefix}spinner-width: #{$spinner-width-sm}; - --#{$prefix}spinner-height: #{$spinner-height-sm}; -} - -@if $enable-reduced-motion { - @media (prefers-reduced-motion: reduce) { - .spinner-border, - .spinner-grow { - --#{$prefix}spinner-animation-speed: #{$spinner-animation-speed * 2}; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tables.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tables.scss deleted file mode 100644 index 276521a3..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tables.scss +++ /dev/null @@ -1,171 +0,0 @@ -// -// Basic Bootstrap table -// - -.table { - // Reset needed for nesting tables - --#{$prefix}table-color-type: initial; - --#{$prefix}table-bg-type: initial; - --#{$prefix}table-color-state: initial; - --#{$prefix}table-bg-state: initial; - // End of reset - --#{$prefix}table-color: #{$table-color}; - --#{$prefix}table-bg: #{$table-bg}; - --#{$prefix}table-border-color: #{$table-border-color}; - --#{$prefix}table-accent-bg: #{$table-accent-bg}; - --#{$prefix}table-striped-color: #{$table-striped-color}; - --#{$prefix}table-striped-bg: #{$table-striped-bg}; - --#{$prefix}table-active-color: #{$table-active-color}; - --#{$prefix}table-active-bg: #{$table-active-bg}; - --#{$prefix}table-hover-color: #{$table-hover-color}; - --#{$prefix}table-hover-bg: #{$table-hover-bg}; - - width: 100%; - margin-bottom: $spacer; - vertical-align: $table-cell-vertical-align; - border-color: var(--#{$prefix}table-border-color); - - // Target th & td - // We need the child combinator to prevent styles leaking to nested tables which doesn't have a `.table` class. - // We use the universal selectors here to simplify the selector (else we would need 6 different selectors). - // Another advantage is that this generates less code and makes the selector less specific making it easier to override. - // stylelint-disable-next-line selector-max-universal - > :not(caption) > * > * { - padding: $table-cell-padding-y $table-cell-padding-x; - // Following the precept of cascades: https://codepen.io/miriamsuzanne/full/vYNgodb - color: var(--#{$prefix}table-color-state, var(--#{$prefix}table-color-type, var(--#{$prefix}table-color))); - background-color: var(--#{$prefix}table-bg); - border-bottom-width: $table-border-width; - box-shadow: inset 0 0 0 9999px var(--#{$prefix}table-bg-state, var(--#{$prefix}table-bg-type, var(--#{$prefix}table-accent-bg))); - } - - > tbody { - vertical-align: inherit; - } - - > thead { - vertical-align: bottom; - } -} - -.table-group-divider { - border-top: calc(#{$table-border-width} * 2) solid $table-group-separator-color; // stylelint-disable-line function-disallowed-list -} - -// -// Change placement of captions with a class -// - -.caption-top { - caption-side: top; -} - - -// -// Condensed table w/ half padding -// - -.table-sm { - // stylelint-disable-next-line selector-max-universal - > :not(caption) > * > * { - padding: $table-cell-padding-y-sm $table-cell-padding-x-sm; - } -} - - -// Border versions -// -// Add or remove borders all around the table and between all the columns. -// -// When borders are added on all sides of the cells, the corners can render odd when -// these borders do not have the same color or if they are semi-transparent. -// Therefore we add top and border bottoms to the `tr`s and left and right borders -// to the `td`s or `th`s - -.table-bordered { - > :not(caption) > * { - border-width: $table-border-width 0; - - // stylelint-disable-next-line selector-max-universal - > * { - border-width: 0 $table-border-width; - } - } -} - -.table-borderless { - // stylelint-disable-next-line selector-max-universal - > :not(caption) > * > * { - border-bottom-width: 0; - } - - > :not(:first-child) { - border-top-width: 0; - } -} - -// Zebra-striping -// -// Default zebra-stripe styles (alternating gray and transparent backgrounds) - -// For rows -.table-striped { - > tbody > tr:nth-of-type(#{$table-striped-order}) > * { - --#{$prefix}table-color-type: var(--#{$prefix}table-striped-color); - --#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg); - } -} - -// For columns -.table-striped-columns { - > :not(caption) > tr > :nth-child(#{$table-striped-columns-order}) { - --#{$prefix}table-color-type: var(--#{$prefix}table-striped-color); - --#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg); - } -} - -// Active table -// -// The `.table-active` class can be added to highlight rows or cells - -.table-active { - --#{$prefix}table-color-state: var(--#{$prefix}table-active-color); - --#{$prefix}table-bg-state: var(--#{$prefix}table-active-bg); -} - -// Hover effect -// -// Placed here since it has to come after the potential zebra striping - -.table-hover { - > tbody > tr:hover > * { - --#{$prefix}table-color-state: var(--#{$prefix}table-hover-color); - --#{$prefix}table-bg-state: var(--#{$prefix}table-hover-bg); - } -} - - -// Table variants -// -// Table variants set the table cell backgrounds, border colors -// and the colors of the striped, hovered & active tables - -@each $color, $value in $table-variants { - @include table-variant($color, $value); -} - -// Responsive tables -// -// Generate series of `.table-responsive-*` classes for configuring the screen -// size of where your table will overflow. - -@each $breakpoint in map-keys($grid-breakpoints) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - - @include media-breakpoint-down($breakpoint) { - .table-responsive#{$infix} { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_toasts.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_toasts.scss deleted file mode 100644 index 2ce378d5..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_toasts.scss +++ /dev/null @@ -1,73 +0,0 @@ -.toast { - // scss-docs-start toast-css-vars - --#{$prefix}toast-zindex: #{$zindex-toast}; - --#{$prefix}toast-padding-x: #{$toast-padding-x}; - --#{$prefix}toast-padding-y: #{$toast-padding-y}; - --#{$prefix}toast-spacing: #{$toast-spacing}; - --#{$prefix}toast-max-width: #{$toast-max-width}; - @include rfs($toast-font-size, --#{$prefix}toast-font-size); - --#{$prefix}toast-color: #{$toast-color}; - --#{$prefix}toast-bg: #{$toast-background-color}; - --#{$prefix}toast-border-width: #{$toast-border-width}; - --#{$prefix}toast-border-color: #{$toast-border-color}; - --#{$prefix}toast-border-radius: #{$toast-border-radius}; - --#{$prefix}toast-box-shadow: #{$toast-box-shadow}; - --#{$prefix}toast-header-color: #{$toast-header-color}; - --#{$prefix}toast-header-bg: #{$toast-header-background-color}; - --#{$prefix}toast-header-border-color: #{$toast-header-border-color}; - // scss-docs-end toast-css-vars - - width: var(--#{$prefix}toast-max-width); - max-width: 100%; - @include font-size(var(--#{$prefix}toast-font-size)); - color: var(--#{$prefix}toast-color); - pointer-events: auto; - background-color: var(--#{$prefix}toast-bg); - background-clip: padding-box; - border: var(--#{$prefix}toast-border-width) solid var(--#{$prefix}toast-border-color); - box-shadow: var(--#{$prefix}toast-box-shadow); - @include border-radius(var(--#{$prefix}toast-border-radius)); - - &.showing { - opacity: 0; - } - - &:not(.show) { - display: none; - } -} - -.toast-container { - --#{$prefix}toast-zindex: #{$zindex-toast}; - - position: absolute; - z-index: var(--#{$prefix}toast-zindex); - width: max-content; - max-width: 100%; - pointer-events: none; - - > :not(:last-child) { - margin-bottom: var(--#{$prefix}toast-spacing); - } -} - -.toast-header { - display: flex; - align-items: center; - padding: var(--#{$prefix}toast-padding-y) var(--#{$prefix}toast-padding-x); - color: var(--#{$prefix}toast-header-color); - background-color: var(--#{$prefix}toast-header-bg); - background-clip: padding-box; - border-bottom: var(--#{$prefix}toast-border-width) solid var(--#{$prefix}toast-header-border-color); - @include border-top-radius(calc(var(--#{$prefix}toast-border-radius) - var(--#{$prefix}toast-border-width))); - - .btn-close { - margin-right: calc(-.5 * var(--#{$prefix}toast-padding-x)); // stylelint-disable-line function-disallowed-list - margin-left: var(--#{$prefix}toast-padding-x); - } -} - -.toast-body { - padding: var(--#{$prefix}toast-padding-x); - word-wrap: break-word; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tooltip.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tooltip.scss deleted file mode 100644 index 85de90f5..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tooltip.scss +++ /dev/null @@ -1,119 +0,0 @@ -// Base class -.tooltip { - // scss-docs-start tooltip-css-vars - --#{$prefix}tooltip-zindex: #{$zindex-tooltip}; - --#{$prefix}tooltip-max-width: #{$tooltip-max-width}; - --#{$prefix}tooltip-padding-x: #{$tooltip-padding-x}; - --#{$prefix}tooltip-padding-y: #{$tooltip-padding-y}; - --#{$prefix}tooltip-margin: #{$tooltip-margin}; - @include rfs($tooltip-font-size, --#{$prefix}tooltip-font-size); - --#{$prefix}tooltip-color: #{$tooltip-color}; - --#{$prefix}tooltip-bg: #{$tooltip-bg}; - --#{$prefix}tooltip-border-radius: #{$tooltip-border-radius}; - --#{$prefix}tooltip-opacity: #{$tooltip-opacity}; - --#{$prefix}tooltip-arrow-width: #{$tooltip-arrow-width}; - --#{$prefix}tooltip-arrow-height: #{$tooltip-arrow-height}; - // scss-docs-end tooltip-css-vars - - z-index: var(--#{$prefix}tooltip-zindex); - display: block; - margin: var(--#{$prefix}tooltip-margin); - @include deprecate("`$tooltip-margin`", "v5", "v5.x", true); - // Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element. - // So reset our font and text properties to avoid inheriting weird values. - @include reset-text(); - @include font-size(var(--#{$prefix}tooltip-font-size)); - // Allow breaking very long words so they don't overflow the tooltip's bounds - word-wrap: break-word; - opacity: 0; - - &.show { opacity: var(--#{$prefix}tooltip-opacity); } - - .tooltip-arrow { - display: block; - width: var(--#{$prefix}tooltip-arrow-width); - height: var(--#{$prefix}tooltip-arrow-height); - - &::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; - } - } -} - -.bs-tooltip-top .tooltip-arrow { - bottom: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list - - &::before { - top: -1px; - border-width: var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list - border-top-color: var(--#{$prefix}tooltip-bg); - } -} - -/* rtl:begin:ignore */ -.bs-tooltip-end .tooltip-arrow { - left: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list - width: var(--#{$prefix}tooltip-arrow-height); - height: var(--#{$prefix}tooltip-arrow-width); - - &::before { - right: -1px; - border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list - border-right-color: var(--#{$prefix}tooltip-bg); - } -} - -/* rtl:end:ignore */ - -.bs-tooltip-bottom .tooltip-arrow { - top: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list - - &::before { - bottom: -1px; - border-width: 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list - border-bottom-color: var(--#{$prefix}tooltip-bg); - } -} - -/* rtl:begin:ignore */ -.bs-tooltip-start .tooltip-arrow { - right: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list - width: var(--#{$prefix}tooltip-arrow-height); - height: var(--#{$prefix}tooltip-arrow-width); - - &::before { - left: -1px; - border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list - border-left-color: var(--#{$prefix}tooltip-bg); - } -} - -/* rtl:end:ignore */ - -.bs-tooltip-auto { - &[data-popper-placement^="top"] { - @extend .bs-tooltip-top; - } - &[data-popper-placement^="right"] { - @extend .bs-tooltip-end; - } - &[data-popper-placement^="bottom"] { - @extend .bs-tooltip-bottom; - } - &[data-popper-placement^="left"] { - @extend .bs-tooltip-start; - } -} - -// Wrapper for the tooltip content -.tooltip-inner { - max-width: var(--#{$prefix}tooltip-max-width); - padding: var(--#{$prefix}tooltip-padding-y) var(--#{$prefix}tooltip-padding-x); - color: var(--#{$prefix}tooltip-color); - text-align: center; - background-color: var(--#{$prefix}tooltip-bg); - @include border-radius(var(--#{$prefix}tooltip-border-radius)); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_transitions.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_transitions.scss deleted file mode 100644 index bfb26aa8..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_transitions.scss +++ /dev/null @@ -1,27 +0,0 @@ -.fade { - @include transition($transition-fade); - - &:not(.show) { - opacity: 0; - } -} - -// scss-docs-start collapse-classes -.collapse { - &:not(.show) { - display: none; - } -} - -.collapsing { - height: 0; - overflow: hidden; - @include transition($transition-collapse); - - &.collapse-horizontal { - width: 0; - height: auto; - @include transition($transition-collapse-width); - } -} -// scss-docs-end collapse-classes diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_type.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_type.scss deleted file mode 100644 index 6961390f..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_type.scss +++ /dev/null @@ -1,106 +0,0 @@ -// -// Headings -// -.h1 { - @extend h1; -} - -.h2 { - @extend h2; -} - -.h3 { - @extend h3; -} - -.h4 { - @extend h4; -} - -.h5 { - @extend h5; -} - -.h6 { - @extend h6; -} - - -.lead { - @include font-size($lead-font-size); - font-weight: $lead-font-weight; -} - -// Type display classes -@each $display, $font-size in $display-font-sizes { - .display-#{$display} { - font-family: $display-font-family; - font-style: $display-font-style; - font-weight: $display-font-weight; - line-height: $display-line-height; - @include font-size($font-size); - } -} - -// -// Emphasis -// -.small { - @extend small; -} - -.mark { - @extend mark; -} - -// -// Lists -// - -.list-unstyled { - @include list-unstyled(); -} - -// Inline turns list items into inline-block -.list-inline { - @include list-unstyled(); -} -.list-inline-item { - display: inline-block; - - &:not(:last-child) { - margin-right: $list-inline-padding; - } -} - - -// -// Misc -// - -// Builds on `abbr` -.initialism { - @include font-size($initialism-font-size); - text-transform: uppercase; -} - -// Blockquotes -.blockquote { - margin-bottom: $blockquote-margin-y; - @include font-size($blockquote-font-size); - - > :last-child { - margin-bottom: 0; - } -} - -.blockquote-footer { - margin-top: -$blockquote-margin-y; - margin-bottom: $blockquote-margin-y; - @include font-size($blockquote-footer-font-size); - color: $blockquote-footer-color; - - &::before { - content: "\2014\00A0"; // em dash, nbsp - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_utilities.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_utilities.scss deleted file mode 100644 index 696f906e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_utilities.scss +++ /dev/null @@ -1,806 +0,0 @@ -// Utilities - -$utilities: () !default; -// stylelint-disable-next-line scss/dollar-variable-default -$utilities: map-merge( - ( - // scss-docs-start utils-vertical-align - "align": ( - property: vertical-align, - class: align, - values: baseline top middle bottom text-bottom text-top - ), - // scss-docs-end utils-vertical-align - // scss-docs-start utils-float - "float": ( - responsive: true, - property: float, - values: ( - start: left, - end: right, - none: none, - ) - ), - // scss-docs-end utils-float - // Object Fit utilities - // scss-docs-start utils-object-fit - "object-fit": ( - responsive: true, - property: object-fit, - values: ( - contain: contain, - cover: cover, - fill: fill, - scale: scale-down, - none: none, - ) - ), - // scss-docs-end utils-object-fit - // Opacity utilities - // scss-docs-start utils-opacity - "opacity": ( - property: opacity, - values: ( - 0: 0, - 25: .25, - 50: .5, - 75: .75, - 100: 1, - ) - ), - // scss-docs-end utils-opacity - // scss-docs-start utils-overflow - "overflow": ( - property: overflow, - values: auto hidden visible scroll, - ), - "overflow-x": ( - property: overflow-x, - values: auto hidden visible scroll, - ), - "overflow-y": ( - property: overflow-y, - values: auto hidden visible scroll, - ), - // scss-docs-end utils-overflow - // scss-docs-start utils-display - "display": ( - responsive: true, - print: true, - property: display, - class: d, - values: inline inline-block block grid inline-grid table table-row table-cell flex inline-flex none - ), - // scss-docs-end utils-display - // scss-docs-start utils-shadow - "shadow": ( - property: box-shadow, - class: shadow, - values: ( - null: var(--#{$prefix}box-shadow), - sm: var(--#{$prefix}box-shadow-sm), - lg: var(--#{$prefix}box-shadow-lg), - none: none, - ) - ), - // scss-docs-end utils-shadow - // scss-docs-start utils-focus-ring - "focus-ring": ( - css-var: true, - css-variable-name: focus-ring-color, - class: focus-ring, - values: map-loop($theme-colors-rgb, rgba-css-var, "$key", "focus-ring") - ), - // scss-docs-end utils-focus-ring - // scss-docs-start utils-position - "position": ( - property: position, - values: static relative absolute fixed sticky - ), - "top": ( - property: top, - values: $position-values - ), - "bottom": ( - property: bottom, - values: $position-values - ), - "start": ( - property: left, - class: start, - values: $position-values - ), - "end": ( - property: right, - class: end, - values: $position-values - ), - "translate-middle": ( - property: transform, - class: translate-middle, - values: ( - null: translate(-50%, -50%), - x: translateX(-50%), - y: translateY(-50%), - ) - ), - // scss-docs-end utils-position - // scss-docs-start utils-borders - "border": ( - property: border, - values: ( - null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color), - 0: 0, - ) - ), - "border-top": ( - property: border-top, - values: ( - null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color), - 0: 0, - ) - ), - "border-end": ( - property: border-right, - class: border-end, - values: ( - null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color), - 0: 0, - ) - ), - "border-bottom": ( - property: border-bottom, - values: ( - null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color), - 0: 0, - ) - ), - "border-start": ( - property: border-left, - class: border-start, - values: ( - null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color), - 0: 0, - ) - ), - "border-color": ( - property: border-color, - class: border, - local-vars: ( - "border-opacity": 1 - ), - values: $utilities-border-colors - ), - "subtle-border-color": ( - property: border-color, - class: border, - values: $utilities-border-subtle - ), - "border-width": ( - property: border-width, - class: border, - values: $border-widths - ), - "border-opacity": ( - css-var: true, - class: border-opacity, - values: ( - 10: .1, - 25: .25, - 50: .5, - 75: .75, - 100: 1 - ) - ), - // scss-docs-end utils-borders - // Sizing utilities - // scss-docs-start utils-sizing - "width": ( - property: width, - class: w, - values: ( - 25: 25%, - 50: 50%, - 75: 75%, - 100: 100%, - auto: auto - ) - ), - "max-width": ( - property: max-width, - class: mw, - values: (100: 100%) - ), - "viewport-width": ( - property: width, - class: vw, - values: (100: 100vw) - ), - "min-viewport-width": ( - property: min-width, - class: min-vw, - values: (100: 100vw) - ), - "height": ( - property: height, - class: h, - values: ( - 25: 25%, - 50: 50%, - 75: 75%, - 100: 100%, - auto: auto - ) - ), - "max-height": ( - property: max-height, - class: mh, - values: (100: 100%) - ), - "viewport-height": ( - property: height, - class: vh, - values: (100: 100vh) - ), - "min-viewport-height": ( - property: min-height, - class: min-vh, - values: (100: 100vh) - ), - // scss-docs-end utils-sizing - // Flex utilities - // scss-docs-start utils-flex - "flex": ( - responsive: true, - property: flex, - values: (fill: 1 1 auto) - ), - "flex-direction": ( - responsive: true, - property: flex-direction, - class: flex, - values: row column row-reverse column-reverse - ), - "flex-grow": ( - responsive: true, - property: flex-grow, - class: flex, - values: ( - grow-0: 0, - grow-1: 1, - ) - ), - "flex-shrink": ( - responsive: true, - property: flex-shrink, - class: flex, - values: ( - shrink-0: 0, - shrink-1: 1, - ) - ), - "flex-wrap": ( - responsive: true, - property: flex-wrap, - class: flex, - values: wrap nowrap wrap-reverse - ), - "justify-content": ( - responsive: true, - property: justify-content, - values: ( - start: flex-start, - end: flex-end, - center: center, - between: space-between, - around: space-around, - evenly: space-evenly, - ) - ), - "align-items": ( - responsive: true, - property: align-items, - values: ( - start: flex-start, - end: flex-end, - center: center, - baseline: baseline, - stretch: stretch, - ) - ), - "align-content": ( - responsive: true, - property: align-content, - values: ( - start: flex-start, - end: flex-end, - center: center, - between: space-between, - around: space-around, - stretch: stretch, - ) - ), - "align-self": ( - responsive: true, - property: align-self, - values: ( - auto: auto, - start: flex-start, - end: flex-end, - center: center, - baseline: baseline, - stretch: stretch, - ) - ), - "order": ( - responsive: true, - property: order, - values: ( - first: -1, - 0: 0, - 1: 1, - 2: 2, - 3: 3, - 4: 4, - 5: 5, - last: 6, - ), - ), - // scss-docs-end utils-flex - // Margin utilities - // scss-docs-start utils-spacing - "margin": ( - responsive: true, - property: margin, - class: m, - values: map-merge($spacers, (auto: auto)) - ), - "margin-x": ( - responsive: true, - property: margin-right margin-left, - class: mx, - values: map-merge($spacers, (auto: auto)) - ), - "margin-y": ( - responsive: true, - property: margin-top margin-bottom, - class: my, - values: map-merge($spacers, (auto: auto)) - ), - "margin-top": ( - responsive: true, - property: margin-top, - class: mt, - values: map-merge($spacers, (auto: auto)) - ), - "margin-end": ( - responsive: true, - property: margin-right, - class: me, - values: map-merge($spacers, (auto: auto)) - ), - "margin-bottom": ( - responsive: true, - property: margin-bottom, - class: mb, - values: map-merge($spacers, (auto: auto)) - ), - "margin-start": ( - responsive: true, - property: margin-left, - class: ms, - values: map-merge($spacers, (auto: auto)) - ), - // Negative margin utilities - "negative-margin": ( - responsive: true, - property: margin, - class: m, - values: $negative-spacers - ), - "negative-margin-x": ( - responsive: true, - property: margin-right margin-left, - class: mx, - values: $negative-spacers - ), - "negative-margin-y": ( - responsive: true, - property: margin-top margin-bottom, - class: my, - values: $negative-spacers - ), - "negative-margin-top": ( - responsive: true, - property: margin-top, - class: mt, - values: $negative-spacers - ), - "negative-margin-end": ( - responsive: true, - property: margin-right, - class: me, - values: $negative-spacers - ), - "negative-margin-bottom": ( - responsive: true, - property: margin-bottom, - class: mb, - values: $negative-spacers - ), - "negative-margin-start": ( - responsive: true, - property: margin-left, - class: ms, - values: $negative-spacers - ), - // Padding utilities - "padding": ( - responsive: true, - property: padding, - class: p, - values: $spacers - ), - "padding-x": ( - responsive: true, - property: padding-right padding-left, - class: px, - values: $spacers - ), - "padding-y": ( - responsive: true, - property: padding-top padding-bottom, - class: py, - values: $spacers - ), - "padding-top": ( - responsive: true, - property: padding-top, - class: pt, - values: $spacers - ), - "padding-end": ( - responsive: true, - property: padding-right, - class: pe, - values: $spacers - ), - "padding-bottom": ( - responsive: true, - property: padding-bottom, - class: pb, - values: $spacers - ), - "padding-start": ( - responsive: true, - property: padding-left, - class: ps, - values: $spacers - ), - // Gap utility - "gap": ( - responsive: true, - property: gap, - class: gap, - values: $spacers - ), - "row-gap": ( - responsive: true, - property: row-gap, - class: row-gap, - values: $spacers - ), - "column-gap": ( - responsive: true, - property: column-gap, - class: column-gap, - values: $spacers - ), - // scss-docs-end utils-spacing - // Text - // scss-docs-start utils-text - "font-family": ( - property: font-family, - class: font, - values: (monospace: var(--#{$prefix}font-monospace)) - ), - "font-size": ( - rfs: true, - property: font-size, - class: fs, - values: $font-sizes - ), - "font-style": ( - property: font-style, - class: fst, - values: italic normal - ), - "font-weight": ( - property: font-weight, - class: fw, - values: ( - lighter: $font-weight-lighter, - light: $font-weight-light, - normal: $font-weight-normal, - medium: $font-weight-medium, - semibold: $font-weight-semibold, - bold: $font-weight-bold, - bolder: $font-weight-bolder - ) - ), - "line-height": ( - property: line-height, - class: lh, - values: ( - 1: 1, - sm: $line-height-sm, - base: $line-height-base, - lg: $line-height-lg, - ) - ), - "text-align": ( - responsive: true, - property: text-align, - class: text, - values: ( - start: left, - end: right, - center: center, - ) - ), - "text-decoration": ( - property: text-decoration, - values: none underline line-through - ), - "text-transform": ( - property: text-transform, - class: text, - values: lowercase uppercase capitalize - ), - "white-space": ( - property: white-space, - class: text, - values: ( - wrap: normal, - nowrap: nowrap, - ) - ), - "word-wrap": ( - property: word-wrap word-break, - class: text, - values: (break: break-word), - rtl: false - ), - // scss-docs-end utils-text - // scss-docs-start utils-color - "color": ( - property: color, - class: text, - local-vars: ( - "text-opacity": 1 - ), - values: map-merge( - $utilities-text-colors, - ( - "muted": var(--#{$prefix}secondary-color), // deprecated - "black-50": rgba($black, .5), // deprecated - "white-50": rgba($white, .5), // deprecated - "body-secondary": var(--#{$prefix}secondary-color), - "body-tertiary": var(--#{$prefix}tertiary-color), - "body-emphasis": var(--#{$prefix}emphasis-color), - "reset": inherit, - ) - ) - ), - "text-opacity": ( - css-var: true, - class: text-opacity, - values: ( - 25: .25, - 50: .5, - 75: .75, - 100: 1 - ) - ), - "text-color": ( - property: color, - class: text, - values: $utilities-text-emphasis-colors - ), - // scss-docs-end utils-color - // scss-docs-start utils-links - "link-opacity": ( - css-var: true, - class: link-opacity, - state: hover, - values: ( - 10: .1, - 25: .25, - 50: .5, - 75: .75, - 100: 1 - ) - ), - "link-offset": ( - property: text-underline-offset, - class: link-offset, - state: hover, - values: ( - 1: .125em, - 2: .25em, - 3: .375em, - ) - ), - "link-underline": ( - property: text-decoration-color, - class: link-underline, - local-vars: ( - "link-underline-opacity": 1 - ), - values: map-merge( - $utilities-links-underline, - ( - null: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-underline-opacity, 1)), - ) - ) - ), - "link-underline-opacity": ( - css-var: true, - class: link-underline-opacity, - state: hover, - values: ( - 0: 0, - 10: .1, - 25: .25, - 50: .5, - 75: .75, - 100: 1 - ), - ), - // scss-docs-end utils-links - // scss-docs-start utils-bg-color - "background-color": ( - property: background-color, - class: bg, - local-vars: ( - "bg-opacity": 1 - ), - values: map-merge( - $utilities-bg-colors, - ( - "transparent": transparent, - "body-secondary": rgba(var(--#{$prefix}secondary-bg-rgb), var(--#{$prefix}bg-opacity)), - "body-tertiary": rgba(var(--#{$prefix}tertiary-bg-rgb), var(--#{$prefix}bg-opacity)), - ) - ) - ), - "bg-opacity": ( - css-var: true, - class: bg-opacity, - values: ( - 10: .1, - 25: .25, - 50: .5, - 75: .75, - 100: 1 - ) - ), - "subtle-background-color": ( - property: background-color, - class: bg, - values: $utilities-bg-subtle - ), - // scss-docs-end utils-bg-color - "gradient": ( - property: background-image, - class: bg, - values: (gradient: var(--#{$prefix}gradient)) - ), - // scss-docs-start utils-interaction - "user-select": ( - property: user-select, - values: all auto none - ), - "pointer-events": ( - property: pointer-events, - class: pe, - values: none auto, - ), - // scss-docs-end utils-interaction - // scss-docs-start utils-border-radius - "rounded": ( - property: border-radius, - class: rounded, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-top": ( - property: border-top-left-radius border-top-right-radius, - class: rounded-top, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-end": ( - property: border-top-right-radius border-bottom-right-radius, - class: rounded-end, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-bottom": ( - property: border-bottom-right-radius border-bottom-left-radius, - class: rounded-bottom, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - "rounded-start": ( - property: border-bottom-left-radius border-top-left-radius, - class: rounded-start, - values: ( - null: var(--#{$prefix}border-radius), - 0: 0, - 1: var(--#{$prefix}border-radius-sm), - 2: var(--#{$prefix}border-radius), - 3: var(--#{$prefix}border-radius-lg), - 4: var(--#{$prefix}border-radius-xl), - 5: var(--#{$prefix}border-radius-xxl), - circle: 50%, - pill: var(--#{$prefix}border-radius-pill) - ) - ), - // scss-docs-end utils-border-radius - // scss-docs-start utils-visibility - "visibility": ( - property: visibility, - class: null, - values: ( - visible: visible, - invisible: hidden, - ) - ), - // scss-docs-end utils-visibility - // scss-docs-start utils-zindex - "z-index": ( - property: z-index, - class: z, - values: $zindex-levels, - ) - // scss-docs-end utils-zindex - ), - $utilities -); diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables-dark.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables-dark.scss deleted file mode 100644 index 260f6dcc..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables-dark.scss +++ /dev/null @@ -1,102 +0,0 @@ -// Dark color mode variables -// -// Custom variables for the `[data-bs-theme="dark"]` theme. Use this as a starting point for your own custom color modes by creating a new theme-specific file like `_variables-dark.scss` and adding the variables you need. - -// -// Global colors -// - -// scss-docs-start sass-dark-mode-vars -// scss-docs-start theme-text-dark-variables -$primary-text-emphasis-dark: tint-color($primary, 40%) !default; -$secondary-text-emphasis-dark: tint-color($secondary, 40%) !default; -$success-text-emphasis-dark: tint-color($success, 40%) !default; -$info-text-emphasis-dark: tint-color($info, 40%) !default; -$warning-text-emphasis-dark: tint-color($warning, 40%) !default; -$danger-text-emphasis-dark: tint-color($danger, 40%) !default; -$light-text-emphasis-dark: $gray-100 !default; -$dark-text-emphasis-dark: $gray-300 !default; -// scss-docs-end theme-text-dark-variables - -// scss-docs-start theme-bg-subtle-dark-variables -$primary-bg-subtle-dark: shade-color($primary, 80%) !default; -$secondary-bg-subtle-dark: shade-color($secondary, 80%) !default; -$success-bg-subtle-dark: shade-color($success, 80%) !default; -$info-bg-subtle-dark: shade-color($info, 80%) !default; -$warning-bg-subtle-dark: shade-color($warning, 80%) !default; -$danger-bg-subtle-dark: shade-color($danger, 80%) !default; -$light-bg-subtle-dark: $gray-800 !default; -$dark-bg-subtle-dark: mix($gray-800, $black) !default; -// scss-docs-end theme-bg-subtle-dark-variables - -// scss-docs-start theme-border-subtle-dark-variables -$primary-border-subtle-dark: shade-color($primary, 40%) !default; -$secondary-border-subtle-dark: shade-color($secondary, 40%) !default; -$success-border-subtle-dark: shade-color($success, 40%) !default; -$info-border-subtle-dark: shade-color($info, 40%) !default; -$warning-border-subtle-dark: shade-color($warning, 40%) !default; -$danger-border-subtle-dark: shade-color($danger, 40%) !default; -$light-border-subtle-dark: $gray-700 !default; -$dark-border-subtle-dark: $gray-800 !default; -// scss-docs-end theme-border-subtle-dark-variables - -$body-color-dark: $gray-300 !default; -$body-bg-dark: $gray-900 !default; -$body-secondary-color-dark: rgba($body-color-dark, .75) !default; -$body-secondary-bg-dark: $gray-800 !default; -$body-tertiary-color-dark: rgba($body-color-dark, .5) !default; -$body-tertiary-bg-dark: mix($gray-800, $gray-900, 50%) !default; -$body-emphasis-color-dark: $white !default; -$border-color-dark: $gray-700 !default; -$border-color-translucent-dark: rgba($white, .15) !default; -$headings-color-dark: inherit !default; -$link-color-dark: tint-color($primary, 40%) !default; -$link-hover-color-dark: shift-color($link-color-dark, -$link-shade-percentage) !default; -$code-color-dark: tint-color($code-color, 40%) !default; -$mark-color-dark: $body-color-dark !default; -$mark-bg-dark: $yellow-800 !default; - - -// -// Forms -// - -$form-select-indicator-color-dark: $body-color-dark !default; -$form-select-indicator-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color-dark}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default; - -$form-switch-color-dark: rgba($white, .25) !default; -$form-switch-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color-dark}'/></svg>") !default; - -// scss-docs-start form-validation-colors-dark -$form-valid-color-dark: $green-300 !default; -$form-valid-border-color-dark: $green-300 !default; -$form-invalid-color-dark: $red-300 !default; -$form-invalid-border-color-dark: $red-300 !default; -// scss-docs-end form-validation-colors-dark - - -// -// Accordion -// - -$accordion-icon-color-dark: $primary-text-emphasis-dark !default; -$accordion-icon-active-color-dark: $primary-text-emphasis-dark !default; - -$accordion-button-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default; -$accordion-button-active-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default; -// scss-docs-end sass-dark-mode-vars - - -// -// Carousel -// - -$carousel-indicator-active-bg-dark: $carousel-dark-indicator-active-bg !default; -$carousel-caption-color-dark: $carousel-dark-caption-color !default; -$carousel-control-icon-filter-dark: $carousel-dark-control-icon-filter !default; - -// -// Close button -// - -$btn-close-filter-dark: $btn-close-white-filter !default; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables.scss deleted file mode 100644 index 1ffa7e74..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_variables.scss +++ /dev/null @@ -1,1753 +0,0 @@ -// Variables -// -// Variables should follow the `$component-state-property-size` formula for -// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs. - -// Color system - -// scss-docs-start gray-color-variables -$white: #fff !default; -$gray-100: #f8f9fa !default; -$gray-200: #e9ecef !default; -$gray-300: #dee2e6 !default; -$gray-400: #ced4da !default; -$gray-500: #adb5bd !default; -$gray-600: #6c757d !default; -$gray-700: #495057 !default; -$gray-800: #343a40 !default; -$gray-900: #212529 !default; -$black: #000 !default; -// scss-docs-end gray-color-variables - -// fusv-disable -// scss-docs-start gray-colors-map -$grays: ( - "100": $gray-100, - "200": $gray-200, - "300": $gray-300, - "400": $gray-400, - "500": $gray-500, - "600": $gray-600, - "700": $gray-700, - "800": $gray-800, - "900": $gray-900 -) !default; -// scss-docs-end gray-colors-map -// fusv-enable - -// scss-docs-start color-variables -$blue: #0d6efd !default; -$indigo: #6610f2 !default; -$purple: #6f42c1 !default; -$pink: #d63384 !default; -$red: #dc3545 !default; -$orange: #fd7e14 !default; -$yellow: #ffc107 !default; -$green: #198754 !default; -$teal: #20c997 !default; -$cyan: #0dcaf0 !default; -// scss-docs-end color-variables - -// scss-docs-start colors-map -$colors: ( - "blue": $blue, - "indigo": $indigo, - "purple": $purple, - "pink": $pink, - "red": $red, - "orange": $orange, - "yellow": $yellow, - "green": $green, - "teal": $teal, - "cyan": $cyan, - "black": $black, - "white": $white, - "gray": $gray-600, - "gray-dark": $gray-800 -) !default; -// scss-docs-end colors-map - -// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.2 are 3, 4.5 and 7. -// See https://www.w3.org/TR/WCAG/#contrast-minimum -$min-contrast-ratio: 4.5 !default; - -// Customize the light and dark text colors for use in our color contrast function. -$color-contrast-dark: $black !default; -$color-contrast-light: $white !default; - -// fusv-disable -$blue-100: tint-color($blue, 80%) !default; -$blue-200: tint-color($blue, 60%) !default; -$blue-300: tint-color($blue, 40%) !default; -$blue-400: tint-color($blue, 20%) !default; -$blue-500: $blue !default; -$blue-600: shade-color($blue, 20%) !default; -$blue-700: shade-color($blue, 40%) !default; -$blue-800: shade-color($blue, 60%) !default; -$blue-900: shade-color($blue, 80%) !default; - -$indigo-100: tint-color($indigo, 80%) !default; -$indigo-200: tint-color($indigo, 60%) !default; -$indigo-300: tint-color($indigo, 40%) !default; -$indigo-400: tint-color($indigo, 20%) !default; -$indigo-500: $indigo !default; -$indigo-600: shade-color($indigo, 20%) !default; -$indigo-700: shade-color($indigo, 40%) !default; -$indigo-800: shade-color($indigo, 60%) !default; -$indigo-900: shade-color($indigo, 80%) !default; - -$purple-100: tint-color($purple, 80%) !default; -$purple-200: tint-color($purple, 60%) !default; -$purple-300: tint-color($purple, 40%) !default; -$purple-400: tint-color($purple, 20%) !default; -$purple-500: $purple !default; -$purple-600: shade-color($purple, 20%) !default; -$purple-700: shade-color($purple, 40%) !default; -$purple-800: shade-color($purple, 60%) !default; -$purple-900: shade-color($purple, 80%) !default; - -$pink-100: tint-color($pink, 80%) !default; -$pink-200: tint-color($pink, 60%) !default; -$pink-300: tint-color($pink, 40%) !default; -$pink-400: tint-color($pink, 20%) !default; -$pink-500: $pink !default; -$pink-600: shade-color($pink, 20%) !default; -$pink-700: shade-color($pink, 40%) !default; -$pink-800: shade-color($pink, 60%) !default; -$pink-900: shade-color($pink, 80%) !default; - -$red-100: tint-color($red, 80%) !default; -$red-200: tint-color($red, 60%) !default; -$red-300: tint-color($red, 40%) !default; -$red-400: tint-color($red, 20%) !default; -$red-500: $red !default; -$red-600: shade-color($red, 20%) !default; -$red-700: shade-color($red, 40%) !default; -$red-800: shade-color($red, 60%) !default; -$red-900: shade-color($red, 80%) !default; - -$orange-100: tint-color($orange, 80%) !default; -$orange-200: tint-color($orange, 60%) !default; -$orange-300: tint-color($orange, 40%) !default; -$orange-400: tint-color($orange, 20%) !default; -$orange-500: $orange !default; -$orange-600: shade-color($orange, 20%) !default; -$orange-700: shade-color($orange, 40%) !default; -$orange-800: shade-color($orange, 60%) !default; -$orange-900: shade-color($orange, 80%) !default; - -$yellow-100: tint-color($yellow, 80%) !default; -$yellow-200: tint-color($yellow, 60%) !default; -$yellow-300: tint-color($yellow, 40%) !default; -$yellow-400: tint-color($yellow, 20%) !default; -$yellow-500: $yellow !default; -$yellow-600: shade-color($yellow, 20%) !default; -$yellow-700: shade-color($yellow, 40%) !default; -$yellow-800: shade-color($yellow, 60%) !default; -$yellow-900: shade-color($yellow, 80%) !default; - -$green-100: tint-color($green, 80%) !default; -$green-200: tint-color($green, 60%) !default; -$green-300: tint-color($green, 40%) !default; -$green-400: tint-color($green, 20%) !default; -$green-500: $green !default; -$green-600: shade-color($green, 20%) !default; -$green-700: shade-color($green, 40%) !default; -$green-800: shade-color($green, 60%) !default; -$green-900: shade-color($green, 80%) !default; - -$teal-100: tint-color($teal, 80%) !default; -$teal-200: tint-color($teal, 60%) !default; -$teal-300: tint-color($teal, 40%) !default; -$teal-400: tint-color($teal, 20%) !default; -$teal-500: $teal !default; -$teal-600: shade-color($teal, 20%) !default; -$teal-700: shade-color($teal, 40%) !default; -$teal-800: shade-color($teal, 60%) !default; -$teal-900: shade-color($teal, 80%) !default; - -$cyan-100: tint-color($cyan, 80%) !default; -$cyan-200: tint-color($cyan, 60%) !default; -$cyan-300: tint-color($cyan, 40%) !default; -$cyan-400: tint-color($cyan, 20%) !default; -$cyan-500: $cyan !default; -$cyan-600: shade-color($cyan, 20%) !default; -$cyan-700: shade-color($cyan, 40%) !default; -$cyan-800: shade-color($cyan, 60%) !default; -$cyan-900: shade-color($cyan, 80%) !default; - -$blues: ( - "blue-100": $blue-100, - "blue-200": $blue-200, - "blue-300": $blue-300, - "blue-400": $blue-400, - "blue-500": $blue-500, - "blue-600": $blue-600, - "blue-700": $blue-700, - "blue-800": $blue-800, - "blue-900": $blue-900 -) !default; - -$indigos: ( - "indigo-100": $indigo-100, - "indigo-200": $indigo-200, - "indigo-300": $indigo-300, - "indigo-400": $indigo-400, - "indigo-500": $indigo-500, - "indigo-600": $indigo-600, - "indigo-700": $indigo-700, - "indigo-800": $indigo-800, - "indigo-900": $indigo-900 -) !default; - -$purples: ( - "purple-100": $purple-100, - "purple-200": $purple-200, - "purple-300": $purple-300, - "purple-400": $purple-400, - "purple-500": $purple-500, - "purple-600": $purple-600, - "purple-700": $purple-700, - "purple-800": $purple-800, - "purple-900": $purple-900 -) !default; - -$pinks: ( - "pink-100": $pink-100, - "pink-200": $pink-200, - "pink-300": $pink-300, - "pink-400": $pink-400, - "pink-500": $pink-500, - "pink-600": $pink-600, - "pink-700": $pink-700, - "pink-800": $pink-800, - "pink-900": $pink-900 -) !default; - -$reds: ( - "red-100": $red-100, - "red-200": $red-200, - "red-300": $red-300, - "red-400": $red-400, - "red-500": $red-500, - "red-600": $red-600, - "red-700": $red-700, - "red-800": $red-800, - "red-900": $red-900 -) !default; - -$oranges: ( - "orange-100": $orange-100, - "orange-200": $orange-200, - "orange-300": $orange-300, - "orange-400": $orange-400, - "orange-500": $orange-500, - "orange-600": $orange-600, - "orange-700": $orange-700, - "orange-800": $orange-800, - "orange-900": $orange-900 -) !default; - -$yellows: ( - "yellow-100": $yellow-100, - "yellow-200": $yellow-200, - "yellow-300": $yellow-300, - "yellow-400": $yellow-400, - "yellow-500": $yellow-500, - "yellow-600": $yellow-600, - "yellow-700": $yellow-700, - "yellow-800": $yellow-800, - "yellow-900": $yellow-900 -) !default; - -$greens: ( - "green-100": $green-100, - "green-200": $green-200, - "green-300": $green-300, - "green-400": $green-400, - "green-500": $green-500, - "green-600": $green-600, - "green-700": $green-700, - "green-800": $green-800, - "green-900": $green-900 -) !default; - -$teals: ( - "teal-100": $teal-100, - "teal-200": $teal-200, - "teal-300": $teal-300, - "teal-400": $teal-400, - "teal-500": $teal-500, - "teal-600": $teal-600, - "teal-700": $teal-700, - "teal-800": $teal-800, - "teal-900": $teal-900 -) !default; - -$cyans: ( - "cyan-100": $cyan-100, - "cyan-200": $cyan-200, - "cyan-300": $cyan-300, - "cyan-400": $cyan-400, - "cyan-500": $cyan-500, - "cyan-600": $cyan-600, - "cyan-700": $cyan-700, - "cyan-800": $cyan-800, - "cyan-900": $cyan-900 -) !default; -// fusv-enable - -// scss-docs-start theme-color-variables -$primary: $blue !default; -$secondary: $gray-600 !default; -$success: $green !default; -$info: $cyan !default; -$warning: $yellow !default; -$danger: $red !default; -$light: $gray-100 !default; -$dark: $gray-900 !default; -// scss-docs-end theme-color-variables - -// scss-docs-start theme-colors-map -$theme-colors: ( - "primary": $primary, - "secondary": $secondary, - "success": $success, - "info": $info, - "warning": $warning, - "danger": $danger, - "light": $light, - "dark": $dark -) !default; -// scss-docs-end theme-colors-map - -// scss-docs-start theme-text-variables -$primary-text-emphasis: shade-color($primary, 60%) !default; -$secondary-text-emphasis: shade-color($secondary, 60%) !default; -$success-text-emphasis: shade-color($success, 60%) !default; -$info-text-emphasis: shade-color($info, 60%) !default; -$warning-text-emphasis: shade-color($warning, 60%) !default; -$danger-text-emphasis: shade-color($danger, 60%) !default; -$light-text-emphasis: $gray-700 !default; -$dark-text-emphasis: $gray-700 !default; -// scss-docs-end theme-text-variables - -// scss-docs-start theme-bg-subtle-variables -$primary-bg-subtle: tint-color($primary, 80%) !default; -$secondary-bg-subtle: tint-color($secondary, 80%) !default; -$success-bg-subtle: tint-color($success, 80%) !default; -$info-bg-subtle: tint-color($info, 80%) !default; -$warning-bg-subtle: tint-color($warning, 80%) !default; -$danger-bg-subtle: tint-color($danger, 80%) !default; -$light-bg-subtle: mix($gray-100, $white) !default; -$dark-bg-subtle: $gray-400 !default; -// scss-docs-end theme-bg-subtle-variables - -// scss-docs-start theme-border-subtle-variables -$primary-border-subtle: tint-color($primary, 60%) !default; -$secondary-border-subtle: tint-color($secondary, 60%) !default; -$success-border-subtle: tint-color($success, 60%) !default; -$info-border-subtle: tint-color($info, 60%) !default; -$warning-border-subtle: tint-color($warning, 60%) !default; -$danger-border-subtle: tint-color($danger, 60%) !default; -$light-border-subtle: $gray-200 !default; -$dark-border-subtle: $gray-500 !default; -// scss-docs-end theme-border-subtle-variables - -// Characters which are escaped by the escape-svg function -$escaped-characters: ( - ("<", "%3c"), - (">", "%3e"), - ("#", "%23"), - ("(", "%28"), - (")", "%29"), -) !default; - -// Options -// -// Quickly modify global styling by enabling or disabling optional features. - -$enable-caret: true !default; -$enable-rounded: true !default; -$enable-shadows: false !default; -$enable-gradients: false !default; -$enable-transitions: true !default; -$enable-reduced-motion: true !default; -$enable-smooth-scroll: true !default; -$enable-grid-classes: true !default; -$enable-container-classes: true !default; -$enable-cssgrid: false !default; -$enable-button-pointers: true !default; -$enable-rfs: true !default; -$enable-validation-icons: true !default; -$enable-negative-margins: false !default; -$enable-deprecation-messages: true !default; -$enable-important-utilities: true !default; - -$enable-dark-mode: true !default; -$color-mode-type: data !default; // `data` or `media-query` - -// Prefix for :root CSS variables - -$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix` -$prefix: $variable-prefix !default; - -// Gradient -// -// The gradient which is added to components if `$enable-gradients` is `true` -// This gradient is also added to elements with `.bg-gradient` -// scss-docs-start variable-gradient -$gradient: linear-gradient(180deg, rgba($white, .15), rgba($white, 0)) !default; -// scss-docs-end variable-gradient - -// Spacing -// -// Control the default styling of most Bootstrap elements by modifying these -// variables. Mostly focused on spacing. -// You can add more entries to the $spacers map, should you need more variation. - -// scss-docs-start spacer-variables-maps -$spacer: 1rem !default; -$spacers: ( - 0: 0, - 1: $spacer * .25, - 2: $spacer * .5, - 3: $spacer, - 4: $spacer * 1.5, - 5: $spacer * 3, -) !default; -// scss-docs-end spacer-variables-maps - -// Position -// -// Define the edge positioning anchors of the position utilities. - -// scss-docs-start position-map -$position-values: ( - 0: 0, - 50: 50%, - 100: 100% -) !default; -// scss-docs-end position-map - -// Body -// -// Settings for the `<body>` element. - -$body-text-align: null !default; -$body-color: $gray-900 !default; -$body-bg: $white !default; - -$body-secondary-color: rgba($body-color, .75) !default; -$body-secondary-bg: $gray-200 !default; - -$body-tertiary-color: rgba($body-color, .5) !default; -$body-tertiary-bg: $gray-100 !default; - -$body-emphasis-color: $black !default; - -// Links -// -// Style anchor elements. - -$link-color: $primary !default; -$link-decoration: underline !default; -$link-shade-percentage: 20% !default; -$link-hover-color: shift-color($link-color, $link-shade-percentage) !default; -$link-hover-decoration: null !default; - -$stretched-link-pseudo-element: after !default; -$stretched-link-z-index: 1 !default; - -// Icon links -// scss-docs-start icon-link-variables -$icon-link-gap: .375rem !default; -$icon-link-underline-offset: .25em !default; -$icon-link-icon-size: 1em !default; -$icon-link-icon-transition: .2s ease-in-out transform !default; -$icon-link-icon-transform: translate3d(.25em, 0, 0) !default; -// scss-docs-end icon-link-variables - -// Paragraphs -// -// Style p element. - -$paragraph-margin-bottom: 1rem !default; - - -// Grid breakpoints -// -// Define the minimum dimensions at which your layout will change, -// adapting to different screen sizes, for use in media queries. - -// scss-docs-start grid-breakpoints -$grid-breakpoints: ( - xs: 0, - sm: 576px, - md: 768px, - lg: 992px, - xl: 1200px, - xxl: 1400px -) !default; -// scss-docs-end grid-breakpoints - -@include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); -@include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints"); - - -// Grid containers -// -// Define the maximum width of `.container` for different screen sizes. - -// scss-docs-start container-max-widths -$container-max-widths: ( - sm: 540px, - md: 720px, - lg: 960px, - xl: 1140px, - xxl: 1320px -) !default; -// scss-docs-end container-max-widths - -@include _assert-ascending($container-max-widths, "$container-max-widths"); - - -// Grid columns -// -// Set the number of columns and specify the width of the gutters. - -$grid-columns: 12 !default; -$grid-gutter-width: 1.5rem !default; -$grid-row-columns: 6 !default; - -// Container padding - -$container-padding-x: $grid-gutter-width !default; - - -// Components -// -// Define common padding and border radius sizes and more. - -// scss-docs-start border-variables -$border-width: 1px !default; -$border-widths: ( - 1: 1px, - 2: 2px, - 3: 3px, - 4: 4px, - 5: 5px -) !default; -$border-style: solid !default; -$border-color: $gray-300 !default; -$border-color-translucent: rgba($black, .175) !default; -// scss-docs-end border-variables - -// scss-docs-start border-radius-variables -$border-radius: .375rem !default; -$border-radius-sm: .25rem !default; -$border-radius-lg: .5rem !default; -$border-radius-xl: 1rem !default; -$border-radius-xxl: 2rem !default; -$border-radius-pill: 50rem !default; -// scss-docs-end border-radius-variables -// fusv-disable -$border-radius-2xl: $border-radius-xxl !default; // Deprecated in v5.3.0 -// fusv-enable - -// scss-docs-start box-shadow-variables -$box-shadow: 0 .5rem 1rem rgba($black, .15) !default; -$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default; -$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default; -$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default; -// scss-docs-end box-shadow-variables - -$component-active-color: $white !default; -$component-active-bg: $primary !default; - -// scss-docs-start focus-ring-variables -$focus-ring-width: .25rem !default; -$focus-ring-opacity: .25 !default; -$focus-ring-color: rgba($primary, $focus-ring-opacity) !default; -$focus-ring-blur: 0 !default; -$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default; -// scss-docs-end focus-ring-variables - -// scss-docs-start caret-variables -$caret-width: .3em !default; -$caret-vertical-align: $caret-width * .85 !default; -$caret-spacing: $caret-width * .85 !default; -// scss-docs-end caret-variables - -$transition-base: all .2s ease-in-out !default; -$transition-fade: opacity .15s linear !default; -// scss-docs-start collapse-transition -$transition-collapse: height .35s ease !default; -$transition-collapse-width: width .35s ease !default; -// scss-docs-end collapse-transition - -// stylelint-disable function-disallowed-list -// scss-docs-start aspect-ratios -$aspect-ratios: ( - "1x1": 100%, - "4x3": calc(3 / 4 * 100%), - "16x9": calc(9 / 16 * 100%), - "21x9": calc(9 / 21 * 100%) -) !default; -// scss-docs-end aspect-ratios -// stylelint-enable function-disallowed-list - -// Typography -// -// Font, line-height, and color for body text, headings, and more. - -// scss-docs-start font-variables -// stylelint-disable value-keyword-case -$font-family-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default; -$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default; -// stylelint-enable value-keyword-case -$font-family-base: var(--#{$prefix}font-sans-serif) !default; -$font-family-code: var(--#{$prefix}font-monospace) !default; - -// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins -// $font-size-base affects the font size of the body text -$font-size-root: null !default; -$font-size-base: 1rem !default; // Assumes the browser default, typically `16px` -$font-size-sm: $font-size-base * .875 !default; -$font-size-lg: $font-size-base * 1.25 !default; - -$font-weight-lighter: lighter !default; -$font-weight-light: 300 !default; -$font-weight-normal: 400 !default; -$font-weight-medium: 500 !default; -$font-weight-semibold: 600 !default; -$font-weight-bold: 700 !default; -$font-weight-bolder: bolder !default; - -$font-weight-base: $font-weight-normal !default; - -$line-height-base: 1.5 !default; -$line-height-sm: 1.25 !default; -$line-height-lg: 2 !default; - -$h1-font-size: $font-size-base * 2.5 !default; -$h2-font-size: $font-size-base * 2 !default; -$h3-font-size: $font-size-base * 1.75 !default; -$h4-font-size: $font-size-base * 1.5 !default; -$h5-font-size: $font-size-base * 1.25 !default; -$h6-font-size: $font-size-base !default; -// scss-docs-end font-variables - -// scss-docs-start font-sizes -$font-sizes: ( - 1: $h1-font-size, - 2: $h2-font-size, - 3: $h3-font-size, - 4: $h4-font-size, - 5: $h5-font-size, - 6: $h6-font-size -) !default; -// scss-docs-end font-sizes - -// scss-docs-start headings-variables -$headings-margin-bottom: $spacer * .5 !default; -$headings-font-family: null !default; -$headings-font-style: null !default; -$headings-font-weight: 500 !default; -$headings-line-height: 1.2 !default; -$headings-color: inherit !default; -// scss-docs-end headings-variables - -// scss-docs-start display-headings -$display-font-sizes: ( - 1: 5rem, - 2: 4.5rem, - 3: 4rem, - 4: 3.5rem, - 5: 3rem, - 6: 2.5rem -) !default; - -$display-font-family: null !default; -$display-font-style: null !default; -$display-font-weight: 300 !default; -$display-line-height: $headings-line-height !default; -// scss-docs-end display-headings - -// scss-docs-start type-variables -$lead-font-size: $font-size-base * 1.25 !default; -$lead-font-weight: 300 !default; - -$small-font-size: .875em !default; - -$sub-sup-font-size: .75em !default; - -// fusv-disable -$text-muted: var(--#{$prefix}secondary-color) !default; // Deprecated in 5.3.0 -// fusv-enable - -$initialism-font-size: $small-font-size !default; - -$blockquote-margin-y: $spacer !default; -$blockquote-font-size: $font-size-base * 1.25 !default; -$blockquote-footer-color: $gray-600 !default; -$blockquote-footer-font-size: $small-font-size !default; - -$hr-margin-y: $spacer !default; -$hr-color: inherit !default; - -// fusv-disable -$hr-bg-color: null !default; // Deprecated in v5.2.0 -$hr-height: null !default; // Deprecated in v5.2.0 -// fusv-enable - -$hr-border-color: null !default; // Allows for inherited colors -$hr-border-width: var(--#{$prefix}border-width) !default; -$hr-opacity: .25 !default; - -// scss-docs-start vr-variables -$vr-border-width: var(--#{$prefix}border-width) !default; -// scss-docs-end vr-variables - -$legend-margin-bottom: .5rem !default; -$legend-font-size: 1.5rem !default; -$legend-font-weight: null !default; - -$dt-font-weight: $font-weight-bold !default; - -$list-inline-padding: .5rem !default; - -$mark-padding: .1875em !default; -$mark-color: $body-color !default; -$mark-bg: $yellow-100 !default; -// scss-docs-end type-variables - - -// Tables -// -// Customizes the `.table` component with basic values, each used across all table variations. - -// scss-docs-start table-variables -$table-cell-padding-y: .5rem !default; -$table-cell-padding-x: .5rem !default; -$table-cell-padding-y-sm: .25rem !default; -$table-cell-padding-x-sm: .25rem !default; - -$table-cell-vertical-align: top !default; - -$table-color: var(--#{$prefix}emphasis-color) !default; -$table-bg: var(--#{$prefix}body-bg) !default; -$table-accent-bg: transparent !default; - -$table-th-font-weight: null !default; - -$table-striped-color: $table-color !default; -$table-striped-bg-factor: .05 !default; -$table-striped-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-striped-bg-factor) !default; - -$table-active-color: $table-color !default; -$table-active-bg-factor: .1 !default; -$table-active-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-active-bg-factor) !default; - -$table-hover-color: $table-color !default; -$table-hover-bg-factor: .075 !default; -$table-hover-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-hover-bg-factor) !default; - -$table-border-factor: .2 !default; -$table-border-width: var(--#{$prefix}border-width) !default; -$table-border-color: var(--#{$prefix}border-color) !default; - -$table-striped-order: odd !default; -$table-striped-columns-order: even !default; - -$table-group-separator-color: currentcolor !default; - -$table-caption-color: var(--#{$prefix}secondary-color) !default; - -$table-bg-scale: -80% !default; -// scss-docs-end table-variables - -// scss-docs-start table-loop -$table-variants: ( - "primary": shift-color($primary, $table-bg-scale), - "secondary": shift-color($secondary, $table-bg-scale), - "success": shift-color($success, $table-bg-scale), - "info": shift-color($info, $table-bg-scale), - "warning": shift-color($warning, $table-bg-scale), - "danger": shift-color($danger, $table-bg-scale), - "light": $light, - "dark": $dark, -) !default; -// scss-docs-end table-loop - - -// Buttons + Forms -// -// Shared variables that are reassigned to `$input-` and `$btn-` specific variables. - -// scss-docs-start input-btn-variables -$input-btn-padding-y: .375rem !default; -$input-btn-padding-x: .75rem !default; -$input-btn-font-family: null !default; -$input-btn-font-size: $font-size-base !default; -$input-btn-line-height: $line-height-base !default; - -$input-btn-focus-width: $focus-ring-width !default; -$input-btn-focus-color-opacity: $focus-ring-opacity !default; -$input-btn-focus-color: $focus-ring-color !default; -$input-btn-focus-blur: $focus-ring-blur !default; -$input-btn-focus-box-shadow: $focus-ring-box-shadow !default; - -$input-btn-padding-y-sm: .25rem !default; -$input-btn-padding-x-sm: .5rem !default; -$input-btn-font-size-sm: $font-size-sm !default; - -$input-btn-padding-y-lg: .5rem !default; -$input-btn-padding-x-lg: 1rem !default; -$input-btn-font-size-lg: $font-size-lg !default; - -$input-btn-border-width: var(--#{$prefix}border-width) !default; -// scss-docs-end input-btn-variables - - -// Buttons -// -// For each of Bootstrap's buttons, define text, background, and border color. - -// scss-docs-start btn-variables -$btn-color: var(--#{$prefix}body-color) !default; -$btn-padding-y: $input-btn-padding-y !default; -$btn-padding-x: $input-btn-padding-x !default; -$btn-font-family: $input-btn-font-family !default; -$btn-font-size: $input-btn-font-size !default; -$btn-line-height: $input-btn-line-height !default; -$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping - -$btn-padding-y-sm: $input-btn-padding-y-sm !default; -$btn-padding-x-sm: $input-btn-padding-x-sm !default; -$btn-font-size-sm: $input-btn-font-size-sm !default; - -$btn-padding-y-lg: $input-btn-padding-y-lg !default; -$btn-padding-x-lg: $input-btn-padding-x-lg !default; -$btn-font-size-lg: $input-btn-font-size-lg !default; - -$btn-border-width: $input-btn-border-width !default; - -$btn-font-weight: $font-weight-normal !default; -$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default; -$btn-focus-width: $input-btn-focus-width !default; -$btn-focus-box-shadow: $input-btn-focus-box-shadow !default; -$btn-disabled-opacity: .65 !default; -$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default; - -$btn-link-color: var(--#{$prefix}link-color) !default; -$btn-link-hover-color: var(--#{$prefix}link-hover-color) !default; -$btn-link-disabled-color: $gray-600 !default; -$btn-link-focus-shadow-rgb: to-rgb(mix(color-contrast($link-color), $link-color, 15%)) !default; - -// Allows for customizing button radius independently from global border radius -$btn-border-radius: var(--#{$prefix}border-radius) !default; -$btn-border-radius-sm: var(--#{$prefix}border-radius-sm) !default; -$btn-border-radius-lg: var(--#{$prefix}border-radius-lg) !default; - -$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; - -$btn-hover-bg-shade-amount: 15% !default; -$btn-hover-bg-tint-amount: 15% !default; -$btn-hover-border-shade-amount: 20% !default; -$btn-hover-border-tint-amount: 10% !default; -$btn-active-bg-shade-amount: 20% !default; -$btn-active-bg-tint-amount: 20% !default; -$btn-active-border-shade-amount: 25% !default; -$btn-active-border-tint-amount: 10% !default; -// scss-docs-end btn-variables - - -// Forms - -// scss-docs-start form-text-variables -$form-text-margin-top: .25rem !default; -$form-text-font-size: $small-font-size !default; -$form-text-font-style: null !default; -$form-text-font-weight: null !default; -$form-text-color: var(--#{$prefix}secondary-color) !default; -// scss-docs-end form-text-variables - -// scss-docs-start form-label-variables -$form-label-margin-bottom: .5rem !default; -$form-label-font-size: null !default; -$form-label-font-style: null !default; -$form-label-font-weight: null !default; -$form-label-color: null !default; -// scss-docs-end form-label-variables - -// scss-docs-start form-input-variables -$input-padding-y: $input-btn-padding-y !default; -$input-padding-x: $input-btn-padding-x !default; -$input-font-family: $input-btn-font-family !default; -$input-font-size: $input-btn-font-size !default; -$input-font-weight: $font-weight-base !default; -$input-line-height: $input-btn-line-height !default; - -$input-padding-y-sm: $input-btn-padding-y-sm !default; -$input-padding-x-sm: $input-btn-padding-x-sm !default; -$input-font-size-sm: $input-btn-font-size-sm !default; - -$input-padding-y-lg: $input-btn-padding-y-lg !default; -$input-padding-x-lg: $input-btn-padding-x-lg !default; -$input-font-size-lg: $input-btn-font-size-lg !default; - -$input-bg: var(--#{$prefix}body-bg) !default; -$input-disabled-color: null !default; -$input-disabled-bg: var(--#{$prefix}secondary-bg) !default; -$input-disabled-border-color: null !default; - -$input-color: var(--#{$prefix}body-color) !default; -$input-border-color: var(--#{$prefix}border-color) !default; -$input-border-width: $input-btn-border-width !default; -$input-box-shadow: var(--#{$prefix}box-shadow-inset) !default; - -$input-border-radius: var(--#{$prefix}border-radius) !default; -$input-border-radius-sm: var(--#{$prefix}border-radius-sm) !default; -$input-border-radius-lg: var(--#{$prefix}border-radius-lg) !default; - -$input-focus-bg: $input-bg !default; -$input-focus-border-color: tint-color($component-active-bg, 50%) !default; -$input-focus-color: $input-color !default; -$input-focus-width: $input-btn-focus-width !default; -$input-focus-box-shadow: $input-btn-focus-box-shadow !default; - -$input-placeholder-color: var(--#{$prefix}secondary-color) !default; -$input-plaintext-color: var(--#{$prefix}body-color) !default; - -$input-height-border: calc(#{$input-border-width} * 2) !default; // stylelint-disable-line function-disallowed-list - -$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default; -$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default; -$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default; - -$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default; -$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default; -$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default; - -$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; - -$form-color-width: 3rem !default; -// scss-docs-end form-input-variables - -// scss-docs-start form-check-variables -$form-check-input-width: 1em !default; -$form-check-min-height: $font-size-base * $line-height-base !default; -$form-check-padding-start: $form-check-input-width + .5em !default; -$form-check-margin-bottom: .125rem !default; -$form-check-label-color: null !default; -$form-check-label-cursor: null !default; -$form-check-transition: null !default; - -$form-check-input-active-filter: brightness(90%) !default; - -$form-check-input-bg: $input-bg !default; -$form-check-input-border: var(--#{$prefix}border-width) solid var(--#{$prefix}border-color) !default; -$form-check-input-border-radius: .25em !default; -$form-check-radio-border-radius: 50% !default; -$form-check-input-focus-border: $input-focus-border-color !default; -$form-check-input-focus-box-shadow: $focus-ring-box-shadow !default; - -$form-check-input-checked-color: $component-active-color !default; -$form-check-input-checked-bg-color: $component-active-bg !default; -$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default; -$form-check-input-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-checked-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/></svg>") !default; -$form-check-radio-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='2' fill='#{$form-check-input-checked-color}'/></svg>") !default; - -$form-check-input-indeterminate-color: $component-active-color !default; -$form-check-input-indeterminate-bg-color: $component-active-bg !default; -$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default; -$form-check-input-indeterminate-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$form-check-input-indeterminate-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/></svg>") !default; - -$form-check-input-disabled-opacity: .5 !default; -$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default; -$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default; - -$form-check-inline-margin-end: 1rem !default; -// scss-docs-end form-check-variables - -// scss-docs-start form-switch-variables -$form-switch-color: rgba($black, .25) !default; -$form-switch-width: 2em !default; -$form-switch-padding-start: $form-switch-width + .5em !default; -$form-switch-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color}'/></svg>") !default; -$form-switch-border-radius: $form-switch-width !default; -$form-switch-transition: background-position .15s ease-in-out !default; - -$form-switch-focus-color: $input-focus-border-color !default; -$form-switch-focus-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-focus-color}'/></svg>") !default; - -$form-switch-checked-color: $component-active-color !default; -$form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !default; -$form-switch-checked-bg-position: right center !default; -// scss-docs-end form-switch-variables - -// scss-docs-start input-group-variables -$input-group-addon-padding-y: $input-padding-y !default; -$input-group-addon-padding-x: $input-padding-x !default; -$input-group-addon-font-weight: $input-font-weight !default; -$input-group-addon-color: $input-color !default; -$input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default; -$input-group-addon-border-color: $input-border-color !default; -// scss-docs-end input-group-variables - -// scss-docs-start form-select-variables -$form-select-padding-y: $input-padding-y !default; -$form-select-padding-x: $input-padding-x !default; -$form-select-font-family: $input-font-family !default; -$form-select-font-size: $input-font-size !default; -$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image -$form-select-font-weight: $input-font-weight !default; -$form-select-line-height: $input-line-height !default; -$form-select-color: $input-color !default; -$form-select-bg: $input-bg !default; -$form-select-disabled-color: null !default; -$form-select-disabled-bg: $input-disabled-bg !default; -$form-select-disabled-border-color: $input-disabled-border-color !default; -$form-select-bg-position: right $form-select-padding-x center !default; -$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions -$form-select-indicator-color: $gray-800 !default; -$form-select-indicator: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default; - -$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default; -$form-select-feedback-icon-position: center right $form-select-indicator-padding !default; -$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default; - -$form-select-border-width: $input-border-width !default; -$form-select-border-color: $input-border-color !default; -$form-select-border-radius: $input-border-radius !default; -$form-select-box-shadow: var(--#{$prefix}box-shadow-inset) !default; - -$form-select-focus-border-color: $input-focus-border-color !default; -$form-select-focus-width: $input-focus-width !default; -$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default; - -$form-select-padding-y-sm: $input-padding-y-sm !default; -$form-select-padding-x-sm: $input-padding-x-sm !default; -$form-select-font-size-sm: $input-font-size-sm !default; -$form-select-border-radius-sm: $input-border-radius-sm !default; - -$form-select-padding-y-lg: $input-padding-y-lg !default; -$form-select-padding-x-lg: $input-padding-x-lg !default; -$form-select-font-size-lg: $input-font-size-lg !default; -$form-select-border-radius-lg: $input-border-radius-lg !default; - -$form-select-transition: $input-transition !default; -// scss-docs-end form-select-variables - -// scss-docs-start form-range-variables -$form-range-track-width: 100% !default; -$form-range-track-height: .5rem !default; -$form-range-track-cursor: pointer !default; -$form-range-track-bg: var(--#{$prefix}secondary-bg) !default; -$form-range-track-border-radius: 1rem !default; -$form-range-track-box-shadow: var(--#{$prefix}box-shadow-inset) !default; - -$form-range-thumb-width: 1rem !default; -$form-range-thumb-height: $form-range-thumb-width !default; -$form-range-thumb-bg: $component-active-bg !default; -$form-range-thumb-border: 0 !default; -$form-range-thumb-border-radius: 1rem !default; -$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default; -$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default; -$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge -$form-range-thumb-active-bg: tint-color($component-active-bg, 70%) !default; -$form-range-thumb-disabled-bg: var(--#{$prefix}secondary-color) !default; -$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; -// scss-docs-end form-range-variables - -// scss-docs-start form-file-variables -$form-file-button-color: $input-color !default; -$form-file-button-bg: var(--#{$prefix}tertiary-bg) !default; -$form-file-button-hover-bg: var(--#{$prefix}secondary-bg) !default; -// scss-docs-end form-file-variables - -// scss-docs-start form-floating-variables -$form-floating-height: add(3.5rem, $input-height-border) !default; -$form-floating-line-height: 1.25 !default; -$form-floating-padding-x: $input-padding-x !default; -$form-floating-padding-y: 1rem !default; -$form-floating-input-padding-t: 1.625rem !default; -$form-floating-input-padding-b: .625rem !default; -$form-floating-label-height: 1.5em !default; -$form-floating-label-opacity: .65 !default; -$form-floating-label-transform: scale(.85) translateY(-.5rem) translateX(.15rem) !default; -$form-floating-label-disabled-color: $gray-600 !default; -$form-floating-transition: opacity .1s ease-in-out, transform .1s ease-in-out !default; -// scss-docs-end form-floating-variables - -// Form validation - -// scss-docs-start form-feedback-variables -$form-feedback-margin-top: $form-text-margin-top !default; -$form-feedback-font-size: $form-text-font-size !default; -$form-feedback-font-style: $form-text-font-style !default; -$form-feedback-valid-color: $success !default; -$form-feedback-invalid-color: $danger !default; - -$form-feedback-icon-valid-color: $form-feedback-valid-color !default; -$form-feedback-icon-valid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path fill='#{$form-feedback-icon-valid-color}' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/></svg>") !default; -$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default; -$form-feedback-icon-invalid: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='#{$form-feedback-icon-invalid-color}'><circle cx='6' cy='6' r='4.5'/><path stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/><circle cx='6' cy='8.2' r='.6' fill='#{$form-feedback-icon-invalid-color}' stroke='none'/></svg>") !default; -// scss-docs-end form-feedback-variables - -// scss-docs-start form-validation-colors -$form-valid-color: $form-feedback-valid-color !default; -$form-valid-border-color: $form-feedback-valid-color !default; -$form-invalid-color: $form-feedback-invalid-color !default; -$form-invalid-border-color: $form-feedback-invalid-color !default; -// scss-docs-end form-validation-colors - -// scss-docs-start form-validation-states -$form-validation-states: ( - "valid": ( - "color": var(--#{$prefix}form-valid-color), - "icon": $form-feedback-icon-valid, - "tooltip-color": #fff, - "tooltip-bg-color": var(--#{$prefix}success), - "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}success-rgb), $input-btn-focus-color-opacity), - "border-color": var(--#{$prefix}form-valid-border-color), - ), - "invalid": ( - "color": var(--#{$prefix}form-invalid-color), - "icon": $form-feedback-icon-invalid, - "tooltip-color": #fff, - "tooltip-bg-color": var(--#{$prefix}danger), - "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}danger-rgb), $input-btn-focus-color-opacity), - "border-color": var(--#{$prefix}form-invalid-border-color), - ) -) !default; -// scss-docs-end form-validation-states - -// Z-index master list -// -// Warning: Avoid customizing these values. They're used for a bird's eye view -// of components dependent on the z-axis and are designed to all work together. - -// scss-docs-start zindex-stack -$zindex-dropdown: 1000 !default; -$zindex-sticky: 1020 !default; -$zindex-fixed: 1030 !default; -$zindex-offcanvas-backdrop: 1040 !default; -$zindex-offcanvas: 1045 !default; -$zindex-modal-backdrop: 1050 !default; -$zindex-modal: 1055 !default; -$zindex-popover: 1070 !default; -$zindex-tooltip: 1080 !default; -$zindex-toast: 1090 !default; -// scss-docs-end zindex-stack - -// scss-docs-start zindex-levels-map -$zindex-levels: ( - n1: -1, - 0: 0, - 1: 1, - 2: 2, - 3: 3 -) !default; -// scss-docs-end zindex-levels-map - - -// Navs - -// scss-docs-start nav-variables -$nav-link-padding-y: .5rem !default; -$nav-link-padding-x: 1rem !default; -$nav-link-font-size: null !default; -$nav-link-font-weight: null !default; -$nav-link-color: var(--#{$prefix}link-color) !default; -$nav-link-hover-color: var(--#{$prefix}link-hover-color) !default; -$nav-link-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default; -$nav-link-disabled-color: var(--#{$prefix}secondary-color) !default; -$nav-link-focus-box-shadow: $focus-ring-box-shadow !default; - -$nav-tabs-border-color: var(--#{$prefix}border-color) !default; -$nav-tabs-border-width: var(--#{$prefix}border-width) !default; -$nav-tabs-border-radius: var(--#{$prefix}border-radius) !default; -$nav-tabs-link-hover-border-color: var(--#{$prefix}secondary-bg) var(--#{$prefix}secondary-bg) $nav-tabs-border-color !default; -$nav-tabs-link-active-color: var(--#{$prefix}emphasis-color) !default; -$nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default; -$nav-tabs-link-active-border-color: var(--#{$prefix}border-color) var(--#{$prefix}border-color) $nav-tabs-link-active-bg !default; - -$nav-pills-border-radius: var(--#{$prefix}border-radius) !default; -$nav-pills-link-active-color: $component-active-color !default; -$nav-pills-link-active-bg: $component-active-bg !default; - -$nav-underline-gap: 1rem !default; -$nav-underline-border-width: .125rem !default; -$nav-underline-link-active-color: var(--#{$prefix}emphasis-color) !default; -// scss-docs-end nav-variables - - -// Navbar - -// scss-docs-start navbar-variables -$navbar-padding-y: $spacer * .5 !default; -$navbar-padding-x: null !default; - -$navbar-nav-link-padding-x: .5rem !default; - -$navbar-brand-font-size: $font-size-lg !default; -// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link -$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default; -$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default; -$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * .5 !default; -$navbar-brand-margin-end: 1rem !default; - -$navbar-toggler-padding-y: .25rem !default; -$navbar-toggler-padding-x: .75rem !default; -$navbar-toggler-font-size: $font-size-lg !default; -$navbar-toggler-border-radius: $btn-border-radius !default; -$navbar-toggler-focus-width: $btn-focus-width !default; -$navbar-toggler-transition: box-shadow .15s ease-in-out !default; - -$navbar-light-color: rgba(var(--#{$prefix}emphasis-color-rgb), .65) !default; -$navbar-light-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), .8) !default; -$navbar-light-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 1) !default; -$navbar-light-disabled-color: rgba(var(--#{$prefix}emphasis-color-rgb), .3) !default; -$navbar-light-icon-color: rgba($body-color, .75) !default; -$navbar-light-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-light-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default; -$navbar-light-toggler-border-color: rgba(var(--#{$prefix}emphasis-color-rgb), .15) !default; -$navbar-light-brand-color: $navbar-light-active-color !default; -$navbar-light-brand-hover-color: $navbar-light-active-color !default; -// scss-docs-end navbar-variables - -// scss-docs-start navbar-dark-variables -$navbar-dark-color: rgba($white, .55) !default; -$navbar-dark-hover-color: rgba($white, .75) !default; -$navbar-dark-active-color: $white !default; -$navbar-dark-disabled-color: rgba($white, .25) !default; -$navbar-dark-icon-color: $navbar-dark-color !default; -$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default; -$navbar-dark-toggler-border-color: rgba($white, .1) !default; -$navbar-dark-brand-color: $navbar-dark-active-color !default; -$navbar-dark-brand-hover-color: $navbar-dark-active-color !default; -// scss-docs-end navbar-dark-variables - - -// Dropdowns -// -// Dropdown menu container and contents. - -// scss-docs-start dropdown-variables -$dropdown-min-width: 10rem !default; -$dropdown-padding-x: 0 !default; -$dropdown-padding-y: .5rem !default; -$dropdown-spacer: .125rem !default; -$dropdown-font-size: $font-size-base !default; -$dropdown-color: var(--#{$prefix}body-color) !default; -$dropdown-bg: var(--#{$prefix}body-bg) !default; -$dropdown-border-color: var(--#{$prefix}border-color-translucent) !default; -$dropdown-border-radius: var(--#{$prefix}border-radius) !default; -$dropdown-border-width: var(--#{$prefix}border-width) !default; -$dropdown-inner-border-radius: calc(#{$dropdown-border-radius} - #{$dropdown-border-width}) !default; // stylelint-disable-line function-disallowed-list -$dropdown-divider-bg: $dropdown-border-color !default; -$dropdown-divider-margin-y: $spacer * .5 !default; -$dropdown-box-shadow: var(--#{$prefix}box-shadow) !default; - -$dropdown-link-color: var(--#{$prefix}body-color) !default; -$dropdown-link-hover-color: $dropdown-link-color !default; -$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg) !default; - -$dropdown-link-active-color: $component-active-color !default; -$dropdown-link-active-bg: $component-active-bg !default; - -$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color) !default; - -$dropdown-item-padding-y: $spacer * .25 !default; -$dropdown-item-padding-x: $spacer !default; - -$dropdown-header-color: $gray-600 !default; -$dropdown-header-padding-x: $dropdown-item-padding-x !default; -$dropdown-header-padding-y: $dropdown-padding-y !default; -// fusv-disable -$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x !default; // Deprecated in v5.2.0 -// fusv-enable -// scss-docs-end dropdown-variables - -// scss-docs-start dropdown-dark-variables -$dropdown-dark-color: $gray-300 !default; -$dropdown-dark-bg: $gray-800 !default; -$dropdown-dark-border-color: $dropdown-border-color !default; -$dropdown-dark-divider-bg: $dropdown-divider-bg !default; -$dropdown-dark-box-shadow: null !default; -$dropdown-dark-link-color: $dropdown-dark-color !default; -$dropdown-dark-link-hover-color: $white !default; -$dropdown-dark-link-hover-bg: rgba($white, .15) !default; -$dropdown-dark-link-active-color: $dropdown-link-active-color !default; -$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default; -$dropdown-dark-link-disabled-color: $gray-500 !default; -$dropdown-dark-header-color: $gray-500 !default; -// scss-docs-end dropdown-dark-variables - - -// Pagination - -// scss-docs-start pagination-variables -$pagination-padding-y: .375rem !default; -$pagination-padding-x: .75rem !default; -$pagination-padding-y-sm: .25rem !default; -$pagination-padding-x-sm: .5rem !default; -$pagination-padding-y-lg: .75rem !default; -$pagination-padding-x-lg: 1.5rem !default; - -$pagination-font-size: $font-size-base !default; - -$pagination-color: var(--#{$prefix}link-color) !default; -$pagination-bg: var(--#{$prefix}body-bg) !default; -$pagination-border-radius: var(--#{$prefix}border-radius) !default; -$pagination-border-width: var(--#{$prefix}border-width) !default; -$pagination-margin-start: calc(-1 * #{$pagination-border-width}) !default; // stylelint-disable-line function-disallowed-list -$pagination-border-color: var(--#{$prefix}border-color) !default; - -$pagination-focus-color: var(--#{$prefix}link-hover-color) !default; -$pagination-focus-bg: var(--#{$prefix}secondary-bg) !default; -$pagination-focus-box-shadow: $focus-ring-box-shadow !default; -$pagination-focus-outline: 0 !default; - -$pagination-hover-color: var(--#{$prefix}link-hover-color) !default; -$pagination-hover-bg: var(--#{$prefix}tertiary-bg) !default; -$pagination-hover-border-color: var(--#{$prefix}border-color) !default; // Todo in v6: remove this? - -$pagination-active-color: $component-active-color !default; -$pagination-active-bg: $component-active-bg !default; -$pagination-active-border-color: $component-active-bg !default; - -$pagination-disabled-color: var(--#{$prefix}secondary-color) !default; -$pagination-disabled-bg: var(--#{$prefix}secondary-bg) !default; -$pagination-disabled-border-color: var(--#{$prefix}border-color) !default; - -$pagination-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; - -$pagination-border-radius-sm: var(--#{$prefix}border-radius-sm) !default; -$pagination-border-radius-lg: var(--#{$prefix}border-radius-lg) !default; -// scss-docs-end pagination-variables - - -// Placeholders - -// scss-docs-start placeholders -$placeholder-opacity-max: .5 !default; -$placeholder-opacity-min: .2 !default; -// scss-docs-end placeholders - -// Cards - -// scss-docs-start card-variables -$card-spacer-y: $spacer !default; -$card-spacer-x: $spacer !default; -$card-title-spacer-y: $spacer * .5 !default; -$card-title-color: null !default; -$card-subtitle-color: null !default; -$card-border-width: var(--#{$prefix}border-width) !default; -$card-border-color: var(--#{$prefix}border-color-translucent) !default; -$card-border-radius: var(--#{$prefix}border-radius) !default; -$card-box-shadow: null !default; -$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default; -$card-cap-padding-y: $card-spacer-y * .5 !default; -$card-cap-padding-x: $card-spacer-x !default; -$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), .03) !default; -$card-cap-color: null !default; -$card-height: null !default; -$card-color: null !default; -$card-bg: var(--#{$prefix}body-bg) !default; -$card-img-overlay-padding: $spacer !default; -$card-group-margin: $grid-gutter-width * .5 !default; -// scss-docs-end card-variables - -// Accordion - -// scss-docs-start accordion-variables -$accordion-padding-y: 1rem !default; -$accordion-padding-x: 1.25rem !default; -$accordion-color: var(--#{$prefix}body-color) !default; -$accordion-bg: var(--#{$prefix}body-bg) !default; -$accordion-border-width: var(--#{$prefix}border-width) !default; -$accordion-border-color: var(--#{$prefix}border-color) !default; -$accordion-border-radius: var(--#{$prefix}border-radius) !default; -$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default; - -$accordion-body-padding-y: $accordion-padding-y !default; -$accordion-body-padding-x: $accordion-padding-x !default; - -$accordion-button-padding-y: $accordion-padding-y !default; -$accordion-button-padding-x: $accordion-padding-x !default; -$accordion-button-color: var(--#{$prefix}body-color) !default; -$accordion-button-bg: var(--#{$prefix}accordion-bg) !default; -$accordion-transition: $btn-transition, border-radius .15s ease !default; -$accordion-button-active-bg: var(--#{$prefix}primary-bg-subtle) !default; -$accordion-button-active-color: var(--#{$prefix}primary-text-emphasis) !default; - -// fusv-disable -$accordion-button-focus-border-color: $input-focus-border-color !default; // Deprecated in v5.3.3 -// fusv-enable -$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default; - -$accordion-icon-width: 1.25rem !default; -$accordion-icon-color: $body-color !default; -$accordion-icon-active-color: $primary-text-emphasis !default; -$accordion-icon-transition: transform .2s ease-in-out !default; -$accordion-icon-transform: rotate(-180deg) !default; - -$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default; -$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-active-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default; -// scss-docs-end accordion-variables - -// Tooltips - -// scss-docs-start tooltip-variables -$tooltip-font-size: $font-size-sm !default; -$tooltip-max-width: 200px !default; -$tooltip-color: var(--#{$prefix}body-bg) !default; -$tooltip-bg: var(--#{$prefix}emphasis-color) !default; -$tooltip-border-radius: var(--#{$prefix}border-radius) !default; -$tooltip-opacity: .9 !default; -$tooltip-padding-y: $spacer * .25 !default; -$tooltip-padding-x: $spacer * .5 !default; -$tooltip-margin: null !default; // TODO: remove this in v6 - -$tooltip-arrow-width: .8rem !default; -$tooltip-arrow-height: .4rem !default; -// fusv-disable -$tooltip-arrow-color: null !default; // Deprecated in Bootstrap 5.2.0 for CSS variables -// fusv-enable -// scss-docs-end tooltip-variables - -// Form tooltips must come after regular tooltips -// scss-docs-start tooltip-feedback-variables -$form-feedback-tooltip-padding-y: $tooltip-padding-y !default; -$form-feedback-tooltip-padding-x: $tooltip-padding-x !default; -$form-feedback-tooltip-font-size: $tooltip-font-size !default; -$form-feedback-tooltip-line-height: null !default; -$form-feedback-tooltip-opacity: $tooltip-opacity !default; -$form-feedback-tooltip-border-radius: $tooltip-border-radius !default; -// scss-docs-end tooltip-feedback-variables - - -// Popovers - -// scss-docs-start popover-variables -$popover-font-size: $font-size-sm !default; -$popover-bg: var(--#{$prefix}body-bg) !default; -$popover-max-width: 276px !default; -$popover-border-width: var(--#{$prefix}border-width) !default; -$popover-border-color: var(--#{$prefix}border-color-translucent) !default; -$popover-border-radius: var(--#{$prefix}border-radius-lg) !default; -$popover-inner-border-radius: calc(#{$popover-border-radius} - #{$popover-border-width}) !default; // stylelint-disable-line function-disallowed-list -$popover-box-shadow: var(--#{$prefix}box-shadow) !default; - -$popover-header-font-size: $font-size-base !default; -$popover-header-bg: var(--#{$prefix}secondary-bg) !default; -$popover-header-color: $headings-color !default; -$popover-header-padding-y: .5rem !default; -$popover-header-padding-x: $spacer !default; - -$popover-body-color: var(--#{$prefix}body-color) !default; -$popover-body-padding-y: $spacer !default; -$popover-body-padding-x: $spacer !default; - -$popover-arrow-width: 1rem !default; -$popover-arrow-height: .5rem !default; -// scss-docs-end popover-variables - -// fusv-disable -// Deprecated in Bootstrap 5.2.0 for CSS variables -$popover-arrow-color: $popover-bg !default; -$popover-arrow-outer-color: var(--#{$prefix}border-color-translucent) !default; -// fusv-enable - - -// Toasts - -// scss-docs-start toast-variables -$toast-max-width: 350px !default; -$toast-padding-x: .75rem !default; -$toast-padding-y: .5rem !default; -$toast-font-size: .875rem !default; -$toast-color: null !default; -$toast-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default; -$toast-border-width: var(--#{$prefix}border-width) !default; -$toast-border-color: var(--#{$prefix}border-color-translucent) !default; -$toast-border-radius: var(--#{$prefix}border-radius) !default; -$toast-box-shadow: var(--#{$prefix}box-shadow) !default; -$toast-spacing: $container-padding-x !default; - -$toast-header-color: var(--#{$prefix}secondary-color) !default; -$toast-header-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default; -$toast-header-border-color: $toast-border-color !default; -// scss-docs-end toast-variables - - -// Badges - -// scss-docs-start badge-variables -$badge-font-size: .75em !default; -$badge-font-weight: $font-weight-bold !default; -$badge-color: $white !default; -$badge-padding-y: .35em !default; -$badge-padding-x: .65em !default; -$badge-border-radius: var(--#{$prefix}border-radius) !default; -// scss-docs-end badge-variables - - -// Modals - -// scss-docs-start modal-variables -$modal-inner-padding: $spacer !default; - -$modal-footer-margin-between: .5rem !default; - -$modal-dialog-margin: .5rem !default; -$modal-dialog-margin-y-sm-up: 1.75rem !default; - -$modal-title-line-height: $line-height-base !default; - -$modal-content-color: var(--#{$prefix}body-color) !default; -$modal-content-bg: var(--#{$prefix}body-bg) !default; -$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default; -$modal-content-border-width: var(--#{$prefix}border-width) !default; -$modal-content-border-radius: var(--#{$prefix}border-radius-lg) !default; -$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default; -$modal-content-box-shadow-xs: var(--#{$prefix}box-shadow-sm) !default; -$modal-content-box-shadow-sm-up: var(--#{$prefix}box-shadow) !default; - -$modal-backdrop-bg: $black !default; -$modal-backdrop-opacity: .5 !default; - -$modal-header-border-color: var(--#{$prefix}border-color) !default; -$modal-header-border-width: $modal-content-border-width !default; -$modal-header-padding-y: $modal-inner-padding !default; -$modal-header-padding-x: $modal-inner-padding !default; -$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility - -$modal-footer-bg: null !default; -$modal-footer-border-color: $modal-header-border-color !default; -$modal-footer-border-width: $modal-header-border-width !default; - -$modal-sm: 300px !default; -$modal-md: 500px !default; -$modal-lg: 800px !default; -$modal-xl: 1140px !default; - -$modal-fade-transform: translate(0, -50px) !default; -$modal-show-transform: none !default; -$modal-transition: transform .3s ease-out !default; -$modal-scale-transform: scale(1.02) !default; -// scss-docs-end modal-variables - - -// Alerts -// -// Define alert colors, border radius, and padding. - -// scss-docs-start alert-variables -$alert-padding-y: $spacer !default; -$alert-padding-x: $spacer !default; -$alert-margin-bottom: 1rem !default; -$alert-border-radius: var(--#{$prefix}border-radius) !default; -$alert-link-font-weight: $font-weight-bold !default; -$alert-border-width: var(--#{$prefix}border-width) !default; -$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side -// scss-docs-end alert-variables - -// fusv-disable -$alert-bg-scale: -80% !default; // Deprecated in v5.2.0, to be removed in v6 -$alert-border-scale: -70% !default; // Deprecated in v5.2.0, to be removed in v6 -$alert-color-scale: 40% !default; // Deprecated in v5.2.0, to be removed in v6 -// fusv-enable - -// Progress bars - -// scss-docs-start progress-variables -$progress-height: 1rem !default; -$progress-font-size: $font-size-base * .75 !default; -$progress-bg: var(--#{$prefix}secondary-bg) !default; -$progress-border-radius: var(--#{$prefix}border-radius) !default; -$progress-box-shadow: var(--#{$prefix}box-shadow-inset) !default; -$progress-bar-color: $white !default; -$progress-bar-bg: $primary !default; -$progress-bar-animation-timing: 1s linear infinite !default; -$progress-bar-transition: width .6s ease !default; -// scss-docs-end progress-variables - - -// List group - -// scss-docs-start list-group-variables -$list-group-color: var(--#{$prefix}body-color) !default; -$list-group-bg: var(--#{$prefix}body-bg) !default; -$list-group-border-color: var(--#{$prefix}border-color) !default; -$list-group-border-width: var(--#{$prefix}border-width) !default; -$list-group-border-radius: var(--#{$prefix}border-radius) !default; - -$list-group-item-padding-y: $spacer * .5 !default; -$list-group-item-padding-x: $spacer !default; -// fusv-disable -$list-group-item-bg-scale: -80% !default; // Deprecated in v5.3.0 -$list-group-item-color-scale: 40% !default; // Deprecated in v5.3.0 -// fusv-enable - -$list-group-hover-bg: var(--#{$prefix}tertiary-bg) !default; -$list-group-active-color: $component-active-color !default; -$list-group-active-bg: $component-active-bg !default; -$list-group-active-border-color: $list-group-active-bg !default; - -$list-group-disabled-color: var(--#{$prefix}secondary-color) !default; -$list-group-disabled-bg: $list-group-bg !default; - -$list-group-action-color: var(--#{$prefix}secondary-color) !default; -$list-group-action-hover-color: var(--#{$prefix}emphasis-color) !default; - -$list-group-action-active-color: var(--#{$prefix}body-color) !default; -$list-group-action-active-bg: var(--#{$prefix}secondary-bg) !default; -// scss-docs-end list-group-variables - - -// Image thumbnails - -// scss-docs-start thumbnail-variables -$thumbnail-padding: .25rem !default; -$thumbnail-bg: var(--#{$prefix}body-bg) !default; -$thumbnail-border-width: var(--#{$prefix}border-width) !default; -$thumbnail-border-color: var(--#{$prefix}border-color) !default; -$thumbnail-border-radius: var(--#{$prefix}border-radius) !default; -$thumbnail-box-shadow: var(--#{$prefix}box-shadow-sm) !default; -// scss-docs-end thumbnail-variables - - -// Figures - -// scss-docs-start figure-variables -$figure-caption-font-size: $small-font-size !default; -$figure-caption-color: var(--#{$prefix}secondary-color) !default; -// scss-docs-end figure-variables - - -// Breadcrumbs - -// scss-docs-start breadcrumb-variables -$breadcrumb-font-size: null !default; -$breadcrumb-padding-y: 0 !default; -$breadcrumb-padding-x: 0 !default; -$breadcrumb-item-padding-x: .5rem !default; -$breadcrumb-margin-bottom: 1rem !default; -$breadcrumb-bg: null !default; -$breadcrumb-divider-color: var(--#{$prefix}secondary-color) !default; -$breadcrumb-active-color: var(--#{$prefix}secondary-color) !default; -$breadcrumb-divider: quote("/") !default; -$breadcrumb-divider-flipped: $breadcrumb-divider !default; -$breadcrumb-border-radius: null !default; -// scss-docs-end breadcrumb-variables - -// Carousel - -// scss-docs-start carousel-variables -$carousel-control-color: $white !default; -$carousel-control-width: 15% !default; -$carousel-control-opacity: .5 !default; -$carousel-control-hover-opacity: .9 !default; -$carousel-control-transition: opacity .15s ease !default; -$carousel-control-icon-filter: null !default; - -$carousel-indicator-width: 30px !default; -$carousel-indicator-height: 3px !default; -$carousel-indicator-hit-area-height: 10px !default; -$carousel-indicator-spacer: 3px !default; -$carousel-indicator-opacity: .5 !default; -$carousel-indicator-active-bg: $white !default; -$carousel-indicator-active-opacity: 1 !default; -$carousel-indicator-transition: opacity .6s ease !default; - -$carousel-caption-width: 70% !default; -$carousel-caption-color: $white !default; -$carousel-caption-padding-y: 1.25rem !default; -$carousel-caption-spacer: 1.25rem !default; - -$carousel-control-icon-width: 2rem !default; - -$carousel-control-prev-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/></svg>") !default; -$carousel-control-next-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$carousel-control-color}'><path d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/></svg>") !default; - -$carousel-transition-duration: .6s !default; -$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`) -// scss-docs-end carousel-variables - -// scss-docs-start carousel-dark-variables -$carousel-dark-indicator-active-bg: $black !default; // Deprecated in v5.3.4 -$carousel-dark-caption-color: $black !default; // Deprecated in v5.3.4 -$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default; // Deprecated in v5.3.4 -// scss-docs-end carousel-dark-variables - - -// Spinners - -// scss-docs-start spinner-variables -$spinner-width: 2rem !default; -$spinner-height: $spinner-width !default; -$spinner-vertical-align: -.125em !default; -$spinner-border-width: .25em !default; -$spinner-animation-speed: .75s !default; - -$spinner-width-sm: 1rem !default; -$spinner-height-sm: $spinner-width-sm !default; -$spinner-border-width-sm: .2em !default; -// scss-docs-end spinner-variables - - -// Close - -// scss-docs-start close-variables -$btn-close-width: 1em !default; -$btn-close-height: $btn-close-width !default; -$btn-close-padding-x: .25em !default; -$btn-close-padding-y: $btn-close-padding-x !default; -$btn-close-color: $black !default; -$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/></svg>") !default; -$btn-close-focus-shadow: $focus-ring-box-shadow !default; -$btn-close-opacity: .5 !default; -$btn-close-hover-opacity: .75 !default; -$btn-close-focus-opacity: 1 !default; -$btn-close-disabled-opacity: .25 !default; -$btn-close-filter: null !default; -$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default; // Deprecated in v5.3.4 -// scss-docs-end close-variables - - -// Offcanvas - -// scss-docs-start offcanvas-variables -$offcanvas-padding-y: $modal-inner-padding !default; -$offcanvas-padding-x: $modal-inner-padding !default; -$offcanvas-horizontal-width: 400px !default; -$offcanvas-vertical-height: 30vh !default; -$offcanvas-transition-duration: .3s !default; -$offcanvas-border-color: $modal-content-border-color !default; -$offcanvas-border-width: $modal-content-border-width !default; -$offcanvas-title-line-height: $modal-title-line-height !default; -$offcanvas-bg-color: var(--#{$prefix}body-bg) !default; -$offcanvas-color: var(--#{$prefix}body-color) !default; -$offcanvas-box-shadow: $modal-content-box-shadow-xs !default; -$offcanvas-backdrop-bg: $modal-backdrop-bg !default; -$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default; -// scss-docs-end offcanvas-variables - -// Code - -$code-font-size: $small-font-size !default; -$code-color: $pink !default; - -$kbd-padding-y: .1875rem !default; -$kbd-padding-x: .375rem !default; -$kbd-font-size: $code-font-size !default; -$kbd-color: var(--#{$prefix}body-bg) !default; -$kbd-bg: var(--#{$prefix}body-color) !default; -$nested-kbd-font-weight: null !default; // Deprecated in v5.2.0, removing in v6 - -$pre-color: null !default; - -@import "variables-dark"; // TODO: can be removed safely in v6, only here to avoid breaking changes in v5.3 diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-grid.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-grid.scss deleted file mode 100644 index 52bd577e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-grid.scss +++ /dev/null @@ -1,62 +0,0 @@ -@import "mixins/banner"; -@include bsBanner(Grid); - -$include-column-box-sizing: true !default; - -@import "functions"; -@import "variables"; -@import "variables-dark"; -@import "maps"; - -@import "mixins/breakpoints"; -@import "mixins/container"; -@import "mixins/grid"; -@import "mixins/utilities"; - -@import "vendor/rfs"; - -@import "containers"; -@import "grid"; - -@import "utilities"; -// Only use the utilities we need -// stylelint-disable-next-line scss/dollar-variable-default -$utilities: map-get-multiple( - $utilities, - ( - "display", - "order", - "flex", - "flex-direction", - "flex-grow", - "flex-shrink", - "flex-wrap", - "justify-content", - "align-items", - "align-content", - "align-self", - "margin", - "margin-x", - "margin-y", - "margin-top", - "margin-end", - "margin-bottom", - "margin-start", - "negative-margin", - "negative-margin-x", - "negative-margin-y", - "negative-margin-top", - "negative-margin-end", - "negative-margin-bottom", - "negative-margin-start", - "padding", - "padding-x", - "padding-y", - "padding-top", - "padding-end", - "padding-bottom", - "padding-start", - ) -); - -@import "utilities/api"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-reboot.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-reboot.scss deleted file mode 100644 index 5b69b955..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-reboot.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import "mixins/banner"; -@include bsBanner(Reboot); - -@import "functions"; -@import "variables"; -@import "variables-dark"; -@import "maps"; -@import "mixins"; -@import "root"; -@import "reboot"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-utilities.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-utilities.scss deleted file mode 100644 index 99c4a359..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap-utilities.scss +++ /dev/null @@ -1,19 +0,0 @@ -@import "mixins/banner"; -@include bsBanner(Utilities); - -// Configuration -@import "functions"; -@import "variables"; -@import "variables-dark"; -@import "maps"; -@import "mixins"; -@import "utilities"; - -// Layout & components -@import "root"; - -// Helpers -@import "helpers"; - -// Utilities -@import "utilities/api"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap.scss deleted file mode 100644 index 449d7048..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/bootstrap.scss +++ /dev/null @@ -1,52 +0,0 @@ -@import "mixins/banner"; -@include bsBanner(""); - - -// scss-docs-start import-stack -// Configuration -@import "functions"; -@import "variables"; -@import "variables-dark"; -@import "maps"; -@import "mixins"; -@import "utilities"; - -// Layout & components -@import "root"; -@import "reboot"; -@import "type"; -@import "images"; -@import "containers"; -@import "grid"; -@import "tables"; -@import "forms"; -@import "buttons"; -@import "transitions"; -@import "dropdown"; -@import "button-group"; -@import "nav"; -@import "navbar"; -@import "card"; -@import "accordion"; -@import "breadcrumb"; -@import "pagination"; -@import "badge"; -@import "alert"; -@import "progress"; -@import "list-group"; -@import "close"; -@import "toasts"; -@import "modal"; -@import "tooltip"; -@import "popover"; -@import "carousel"; -@import "spinners"; -@import "offcanvas"; -@import "placeholders"; - -// Helpers -@import "helpers"; - -// Utilities -@import "utilities/api"; -// scss-docs-end import-stack diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_floating-labels.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_floating-labels.scss deleted file mode 100644 index 38df1155..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_floating-labels.scss +++ /dev/null @@ -1,97 +0,0 @@ -.form-floating { - position: relative; - - > .form-control, - > .form-control-plaintext, - > .form-select { - height: $form-floating-height; - min-height: $form-floating-height; - line-height: $form-floating-line-height; - } - - > label { - position: absolute; - top: 0; - left: 0; - z-index: 2; - max-width: 100%; - height: 100%; // allow textareas - padding: $form-floating-padding-y $form-floating-padding-x; - overflow: hidden; - color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity}); - text-align: start; - text-overflow: ellipsis; - white-space: nowrap; - pointer-events: none; - border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model - transform-origin: 0 0; - @include transition($form-floating-transition); - } - - > .form-control, - > .form-control-plaintext { - padding: $form-floating-padding-y $form-floating-padding-x; - - &::placeholder { - color: transparent; - } - - &:focus, - &:not(:placeholder-shown) { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; - } - // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped - &:-webkit-autofill { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; - } - } - - > .form-select { - padding-top: $form-floating-input-padding-t; - padding-bottom: $form-floating-input-padding-b; - padding-left: $form-floating-padding-x; - } - - > .form-control:focus, - > .form-control:not(:placeholder-shown), - > .form-control-plaintext, - > .form-select { - ~ label { - transform: $form-floating-label-transform; - } - } - // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped - > .form-control:-webkit-autofill { - ~ label { - transform: $form-floating-label-transform; - } - } - > textarea:focus, - > textarea:not(:placeholder-shown) { - ~ label::after { - position: absolute; - inset: $form-floating-padding-y ($form-floating-padding-x * .5); - z-index: -1; - height: $form-floating-label-height; - content: ""; - background-color: $input-bg; - @include border-radius($input-border-radius); - } - } - > textarea:disabled ~ label::after { - background-color: $input-disabled-bg; - } - - > .form-control-plaintext { - ~ label { - border-width: $input-border-width 0; // Required to properly position label text - as explained above - } - } - - > :disabled ~ label, - > .form-control:disabled ~ label { // Required for `.form-control`s because of specificity - color: $form-floating-label-disabled-color; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-check.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-check.scss deleted file mode 100644 index 8a1b639d..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-check.scss +++ /dev/null @@ -1,189 +0,0 @@ -// -// Check/radio -// - -.form-check { - display: block; - min-height: $form-check-min-height; - padding-left: $form-check-padding-start; - margin-bottom: $form-check-margin-bottom; - - .form-check-input { - float: left; - margin-left: $form-check-padding-start * -1; - } -} - -.form-check-reverse { - padding-right: $form-check-padding-start; - padding-left: 0; - text-align: right; - - .form-check-input { - float: right; - margin-right: $form-check-padding-start * -1; - margin-left: 0; - } -} - -.form-check-input { - --#{$prefix}form-check-bg: #{$form-check-input-bg}; - - flex-shrink: 0; - width: $form-check-input-width; - height: $form-check-input-width; - margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height - vertical-align: top; - appearance: none; - background-color: var(--#{$prefix}form-check-bg); - background-image: var(--#{$prefix}form-check-bg-image); - background-repeat: no-repeat; - background-position: center; - background-size: contain; - border: $form-check-input-border; - print-color-adjust: exact; // Keep themed appearance for print - @include transition($form-check-transition); - - &[type="checkbox"] { - @include border-radius($form-check-input-border-radius); - } - - &[type="radio"] { - // stylelint-disable-next-line property-disallowed-list - border-radius: $form-check-radio-border-radius; - } - - &:active { - filter: $form-check-input-active-filter; - } - - &:focus { - border-color: $form-check-input-focus-border; - outline: 0; - box-shadow: $form-check-input-focus-box-shadow; - } - - &:checked { - background-color: $form-check-input-checked-bg-color; - border-color: $form-check-input-checked-border-color; - - &[type="checkbox"] { - @if $enable-gradients { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}, var(--#{$prefix}gradient); - } @else { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}; - } - } - - &[type="radio"] { - @if $enable-gradients { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}, var(--#{$prefix}gradient); - } @else { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}; - } - } - } - - &[type="checkbox"]:indeterminate { - background-color: $form-check-input-indeterminate-bg-color; - border-color: $form-check-input-indeterminate-border-color; - - @if $enable-gradients { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}, var(--#{$prefix}gradient); - } @else { - --#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}; - } - } - - &:disabled { - pointer-events: none; - filter: none; - opacity: $form-check-input-disabled-opacity; - } - - // Use disabled attribute in addition of :disabled pseudo-class - // See: https://github.com/twbs/bootstrap/issues/28247 - &[disabled], - &:disabled { - ~ .form-check-label { - cursor: default; - opacity: $form-check-label-disabled-opacity; - } - } -} - -.form-check-label { - color: $form-check-label-color; - cursor: $form-check-label-cursor; -} - -// -// Switch -// - -.form-switch { - padding-left: $form-switch-padding-start; - - .form-check-input { - --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)}; - - width: $form-switch-width; - margin-left: $form-switch-padding-start * -1; - background-image: var(--#{$prefix}form-switch-bg); - background-position: left center; - @include border-radius($form-switch-border-radius, 0); - @include transition($form-switch-transition); - - &:focus { - --#{$prefix}form-switch-bg: #{escape-svg($form-switch-focus-bg-image)}; - } - - &:checked { - background-position: $form-switch-checked-bg-position; - - @if $enable-gradients { - --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--#{$prefix}gradient); - } @else { - --#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}; - } - } - } - - &.form-check-reverse { - padding-right: $form-switch-padding-start; - padding-left: 0; - - .form-check-input { - margin-right: $form-switch-padding-start * -1; - margin-left: 0; - } - } -} - -.form-check-inline { - display: inline-block; - margin-right: $form-check-inline-margin-end; -} - -.btn-check { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; - - &[disabled], - &:disabled { - + .btn { - pointer-events: none; - filter: none; - opacity: $form-check-btn-check-disabled-opacity; - } - } -} - -@if $enable-dark-mode { - @include color-mode(dark) { - .form-switch .form-check-input:not(:checked):not(:focus) { - --#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)}; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-control.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-control.scss deleted file mode 100644 index 67ae5f4f..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-control.scss +++ /dev/null @@ -1,214 +0,0 @@ -// -// General form controls (plus a few specific high-level interventions) -// - -.form-control { - display: block; - width: 100%; - padding: $input-padding-y $input-padding-x; - font-family: $input-font-family; - @include font-size($input-font-size); - font-weight: $input-font-weight; - line-height: $input-line-height; - color: $input-color; - appearance: none; // Fix appearance for date inputs in Safari - background-color: $input-bg; - background-clip: padding-box; - border: $input-border-width solid $input-border-color; - - // Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS. - @include border-radius($input-border-radius, 0); - - @include box-shadow($input-box-shadow); - @include transition($input-transition); - - &[type="file"] { - overflow: hidden; // prevent pseudo element button overlap - - &:not(:disabled):not([readonly]) { - cursor: pointer; - } - } - - // Customize the `:focus` state to imitate native WebKit styles. - &:focus { - color: $input-focus-color; - background-color: $input-focus-bg; - border-color: $input-focus-border-color; - outline: 0; - @if $enable-shadows { - @include box-shadow($input-box-shadow, $input-focus-box-shadow); - } @else { - // Avoid using mixin so we can pass custom focus shadow properly - box-shadow: $input-focus-box-shadow; - } - } - - &::-webkit-date-and-time-value { - // On Android Chrome, form-control's "width: 100%" makes the input width too small - // Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109 - // - // On iOS Safari, form-control's "appearance: none" + "width: 100%" makes the input width too small - // Tested under iOS 16.2 / Safari 16.2 - min-width: 85px; // Seems to be a good minimum safe width - - // Add some height to date inputs on iOS - // https://github.com/twbs/bootstrap/issues/23307 - // TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved - // Multiply line-height by 1em if it has no unit - height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height); - - // Android Chrome type="date" is taller than the other inputs - // because of "margin: 1px 24px 1px 4px" inside the shadow DOM - // Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109 - margin: 0; - } - - // Prevent excessive date input height in Webkit - // https://github.com/twbs/bootstrap/issues/34433 - &::-webkit-datetime-edit { - display: block; - padding: 0; - } - - // Placeholder - &::placeholder { - color: $input-placeholder-color; - // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526. - opacity: 1; - } - - // Disabled inputs - // - // HTML5 says that controls under a fieldset > legend:first-child won't be - // disabled if the fieldset is disabled. Due to implementation difficulty, we - // don't honor that edge case; we style them as disabled anyway. - &:disabled { - color: $input-disabled-color; - background-color: $input-disabled-bg; - border-color: $input-disabled-border-color; - // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655. - opacity: 1; - } - - // File input buttons theming - &::file-selector-button { - padding: $input-padding-y $input-padding-x; - margin: (-$input-padding-y) (-$input-padding-x); - margin-inline-end: $input-padding-x; - color: $form-file-button-color; - @include gradient-bg($form-file-button-bg); - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: $input-border-width; - border-radius: 0; // stylelint-disable-line property-disallowed-list - @include transition($btn-transition); - } - - &:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: $form-file-button-hover-bg; - } -} - -// Readonly controls as plain text -// -// Apply class to a readonly input to make it appear like regular plain -// text (without any border, background color, focus indicator) - -.form-control-plaintext { - display: block; - width: 100%; - padding: $input-padding-y 0; - margin-bottom: 0; // match inputs if this class comes on inputs with default margins - line-height: $input-line-height; - color: $input-plaintext-color; - background-color: transparent; - border: solid transparent; - border-width: $input-border-width 0; - - &:focus { - outline: 0; - } - - &.form-control-sm, - &.form-control-lg { - padding-right: 0; - padding-left: 0; - } -} - -// Form control sizing -// -// Build on `.form-control` with modifier classes to decrease or increase the -// height and font-size of form controls. -// -// Repeated in `_input_group.scss` to avoid Sass extend issues. - -.form-control-sm { - min-height: $input-height-sm; - padding: $input-padding-y-sm $input-padding-x-sm; - @include font-size($input-font-size-sm); - @include border-radius($input-border-radius-sm); - - &::file-selector-button { - padding: $input-padding-y-sm $input-padding-x-sm; - margin: (-$input-padding-y-sm) (-$input-padding-x-sm); - margin-inline-end: $input-padding-x-sm; - } -} - -.form-control-lg { - min-height: $input-height-lg; - padding: $input-padding-y-lg $input-padding-x-lg; - @include font-size($input-font-size-lg); - @include border-radius($input-border-radius-lg); - - &::file-selector-button { - padding: $input-padding-y-lg $input-padding-x-lg; - margin: (-$input-padding-y-lg) (-$input-padding-x-lg); - margin-inline-end: $input-padding-x-lg; - } -} - -// Make sure textareas don't shrink too much when resized -// https://github.com/twbs/bootstrap/pull/29124 -// stylelint-disable selector-no-qualifying-type -textarea { - &.form-control { - min-height: $input-height; - } - - &.form-control-sm { - min-height: $input-height-sm; - } - - &.form-control-lg { - min-height: $input-height-lg; - } -} -// stylelint-enable selector-no-qualifying-type - -.form-control-color { - width: $form-color-width; - height: $input-height; - padding: $input-padding-y; - - &:not(:disabled):not([readonly]) { - cursor: pointer; - } - - &::-moz-color-swatch { - border: 0 !important; // stylelint-disable-line declaration-no-important - @include border-radius($input-border-radius); - } - - &::-webkit-color-swatch { - border: 0 !important; // stylelint-disable-line declaration-no-important - @include border-radius($input-border-radius); - } - - &.form-control-sm { height: $input-height-sm; } - &.form-control-lg { height: $input-height-lg; } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-range.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-range.scss deleted file mode 100644 index 4732213e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-range.scss +++ /dev/null @@ -1,91 +0,0 @@ -// Range -// -// Style range inputs the same across browsers. Vendor-specific rules for pseudo -// elements cannot be mixed. As such, there are no shared styles for focus or -// active states on prefixed selectors. - -.form-range { - width: 100%; - height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2); - padding: 0; // Need to reset padding - appearance: none; - background-color: transparent; - - &:focus { - outline: 0; - - // Pseudo-elements must be split across multiple rulesets to have an effect. - // No box-shadow() mixin for focus accessibility. - &::-webkit-slider-thumb { box-shadow: $form-range-thumb-focus-box-shadow; } - &::-moz-range-thumb { box-shadow: $form-range-thumb-focus-box-shadow; } - } - - &::-moz-focus-outer { - border: 0; - } - - &::-webkit-slider-thumb { - width: $form-range-thumb-width; - height: $form-range-thumb-height; - margin-top: ($form-range-track-height - $form-range-thumb-height) * .5; // Webkit specific - appearance: none; - @include gradient-bg($form-range-thumb-bg); - border: $form-range-thumb-border; - @include border-radius($form-range-thumb-border-radius); - @include box-shadow($form-range-thumb-box-shadow); - @include transition($form-range-thumb-transition); - - &:active { - @include gradient-bg($form-range-thumb-active-bg); - } - } - - &::-webkit-slider-runnable-track { - width: $form-range-track-width; - height: $form-range-track-height; - color: transparent; // Why? - cursor: $form-range-track-cursor; - background-color: $form-range-track-bg; - border-color: transparent; - @include border-radius($form-range-track-border-radius); - @include box-shadow($form-range-track-box-shadow); - } - - &::-moz-range-thumb { - width: $form-range-thumb-width; - height: $form-range-thumb-height; - appearance: none; - @include gradient-bg($form-range-thumb-bg); - border: $form-range-thumb-border; - @include border-radius($form-range-thumb-border-radius); - @include box-shadow($form-range-thumb-box-shadow); - @include transition($form-range-thumb-transition); - - &:active { - @include gradient-bg($form-range-thumb-active-bg); - } - } - - &::-moz-range-track { - width: $form-range-track-width; - height: $form-range-track-height; - color: transparent; - cursor: $form-range-track-cursor; - background-color: $form-range-track-bg; - border-color: transparent; // Firefox specific? - @include border-radius($form-range-track-border-radius); - @include box-shadow($form-range-track-box-shadow); - } - - &:disabled { - pointer-events: none; - - &::-webkit-slider-thumb { - background-color: $form-range-thumb-disabled-bg; - } - - &::-moz-range-thumb { - background-color: $form-range-thumb-disabled-bg; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-select.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-select.scss deleted file mode 100644 index 69ace529..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-select.scss +++ /dev/null @@ -1,80 +0,0 @@ -// Select -// -// Replaces the browser default select with a custom one, mostly pulled from -// https://primer.github.io/. - -.form-select { - --#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator)}; - - display: block; - width: 100%; - padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x; - font-family: $form-select-font-family; - @include font-size($form-select-font-size); - font-weight: $form-select-font-weight; - line-height: $form-select-line-height; - color: $form-select-color; - appearance: none; - background-color: $form-select-bg; - background-image: var(--#{$prefix}form-select-bg-img), var(--#{$prefix}form-select-bg-icon, none); - background-repeat: no-repeat; - background-position: $form-select-bg-position; - background-size: $form-select-bg-size; - border: $form-select-border-width solid $form-select-border-color; - @include border-radius($form-select-border-radius, 0); - @include box-shadow($form-select-box-shadow); - @include transition($form-select-transition); - - &:focus { - border-color: $form-select-focus-border-color; - outline: 0; - @if $enable-shadows { - @include box-shadow($form-select-box-shadow, $form-select-focus-box-shadow); - } @else { - // Avoid using mixin so we can pass custom focus shadow properly - box-shadow: $form-select-focus-box-shadow; - } - } - - &[multiple], - &[size]:not([size="1"]) { - padding-right: $form-select-padding-x; - background-image: none; - } - - &:disabled { - color: $form-select-disabled-color; - background-color: $form-select-disabled-bg; - border-color: $form-select-disabled-border-color; - } - - // Remove outline from select box in FF - &:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 $form-select-color; - } -} - -.form-select-sm { - padding-top: $form-select-padding-y-sm; - padding-bottom: $form-select-padding-y-sm; - padding-left: $form-select-padding-x-sm; - @include font-size($form-select-font-size-sm); - @include border-radius($form-select-border-radius-sm); -} - -.form-select-lg { - padding-top: $form-select-padding-y-lg; - padding-bottom: $form-select-padding-y-lg; - padding-left: $form-select-padding-x-lg; - @include font-size($form-select-font-size-lg); - @include border-radius($form-select-border-radius-lg); -} - -@if $enable-dark-mode { - @include color-mode(dark) { - .form-select { - --#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator-dark)}; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-text.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-text.scss deleted file mode 100644 index f080d1a2..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_form-text.scss +++ /dev/null @@ -1,11 +0,0 @@ -// -// Form text -// - -.form-text { - margin-top: $form-text-margin-top; - @include font-size($form-text-font-size); - font-style: $form-text-font-style; - font-weight: $form-text-font-weight; - color: $form-text-color; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_input-group.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_input-group.scss deleted file mode 100644 index 8078ebb1..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_input-group.scss +++ /dev/null @@ -1,132 +0,0 @@ -// -// Base styles -// - -.input-group { - position: relative; - display: flex; - flex-wrap: wrap; // For form validation feedback - align-items: stretch; - width: 100%; - - > .form-control, - > .form-select, - > .form-floating { - position: relative; // For focus state's z-index - flex: 1 1 auto; - width: 1%; - min-width: 0; // https://stackoverflow.com/questions/36247140/why-dont-flex-items-shrink-past-content-size - } - - // Bring the "active" form control to the top of surrounding elements - > .form-control:focus, - > .form-select:focus, - > .form-floating:focus-within { - z-index: 5; - } - - // Ensure buttons are always above inputs for more visually pleasing borders. - // This isn't needed for `.input-group-text` since it shares the same border-color - // as our inputs. - .btn { - position: relative; - z-index: 2; - - &:focus { - z-index: 5; - } - } -} - - -// Textual addons -// -// Serves as a catch-all element for any text or radio/checkbox input you wish -// to prepend or append to an input. - -.input-group-text { - display: flex; - align-items: center; - padding: $input-group-addon-padding-y $input-group-addon-padding-x; - @include font-size($input-font-size); // Match inputs - font-weight: $input-group-addon-font-weight; - line-height: $input-line-height; - color: $input-group-addon-color; - text-align: center; - white-space: nowrap; - background-color: $input-group-addon-bg; - border: $input-border-width solid $input-group-addon-border-color; - @include border-radius($input-border-radius); -} - - -// Sizing -// -// Remix the default form control sizing classes into new ones for easier -// manipulation. - -.input-group-lg > .form-control, -.input-group-lg > .form-select, -.input-group-lg > .input-group-text, -.input-group-lg > .btn { - padding: $input-padding-y-lg $input-padding-x-lg; - @include font-size($input-font-size-lg); - @include border-radius($input-border-radius-lg); -} - -.input-group-sm > .form-control, -.input-group-sm > .form-select, -.input-group-sm > .input-group-text, -.input-group-sm > .btn { - padding: $input-padding-y-sm $input-padding-x-sm; - @include font-size($input-font-size-sm); - @include border-radius($input-border-radius-sm); -} - -.input-group-lg > .form-select, -.input-group-sm > .form-select { - padding-right: $form-select-padding-x + $form-select-indicator-padding; -} - - -// Rounded corners -// -// These rulesets must come after the sizing ones to properly override sm and lg -// border-radius values when extending. They're more specific than we'd like -// with the `.input-group >` part, but without it, we cannot override the sizing. - -// stylelint-disable-next-line no-duplicate-selectors -.input-group { - &:not(.has-validation) { - > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), - > .dropdown-toggle:nth-last-child(n + 3), - > .form-floating:not(:last-child) > .form-control, - > .form-floating:not(:last-child) > .form-select { - @include border-end-radius(0); - } - } - - &.has-validation { - > :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), - > .dropdown-toggle:nth-last-child(n + 4), - > .form-floating:nth-last-child(n + 3) > .form-control, - > .form-floating:nth-last-child(n + 3) > .form-select { - @include border-end-radius(0); - } - } - - $validation-messages: ""; - @each $state in map-keys($form-validation-states) { - $validation-messages: $validation-messages + ":not(." + unquote($state) + "-tooltip)" + ":not(." + unquote($state) + "-feedback)"; - } - - > :not(:first-child):not(.dropdown-menu)#{$validation-messages} { - margin-left: calc(-1 * #{$input-border-width}); // stylelint-disable-line function-disallowed-list - @include border-start-radius(0); - } - - > .form-floating:not(:first-child) > .form-control, - > .form-floating:not(:first-child) > .form-select { - @include border-start-radius(0); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_labels.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_labels.scss deleted file mode 100644 index 39ecafcd..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_labels.scss +++ /dev/null @@ -1,36 +0,0 @@ -// -// Labels -// - -.form-label { - margin-bottom: $form-label-margin-bottom; - @include font-size($form-label-font-size); - font-style: $form-label-font-style; - font-weight: $form-label-font-weight; - color: $form-label-color; -} - -// For use with horizontal and inline forms, when you need the label (or legend) -// text to align with the form controls. -.col-form-label { - padding-top: add($input-padding-y, $input-border-width); - padding-bottom: add($input-padding-y, $input-border-width); - margin-bottom: 0; // Override the `<legend>` default - @include font-size(inherit); // Override the `<legend>` default - font-style: $form-label-font-style; - font-weight: $form-label-font-weight; - line-height: $input-line-height; - color: $form-label-color; -} - -.col-form-label-lg { - padding-top: add($input-padding-y-lg, $input-border-width); - padding-bottom: add($input-padding-y-lg, $input-border-width); - @include font-size($input-font-size-lg); -} - -.col-form-label-sm { - padding-top: add($input-padding-y-sm, $input-border-width); - padding-bottom: add($input-padding-y-sm, $input-border-width); - @include font-size($input-font-size-sm); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_validation.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_validation.scss deleted file mode 100644 index c48123a7..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/forms/_validation.scss +++ /dev/null @@ -1,12 +0,0 @@ -// Form validation -// -// Provide feedback to users when form field values are valid or invalid. Works -// primarily for client-side validation via scoped `:invalid` and `:valid` -// pseudo-classes but also includes `.is-invalid` and `.is-valid` classes for -// server-side validation. - -// scss-docs-start form-validation-states-loop -@each $state, $data in $form-validation-states { - @include form-validation-state($state, $data...); -} -// scss-docs-end form-validation-states-loop diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_clearfix.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_clearfix.scss deleted file mode 100644 index e92522a9..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_clearfix.scss +++ /dev/null @@ -1,3 +0,0 @@ -.clearfix { - @include clearfix(); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_color-bg.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_color-bg.scss deleted file mode 100644 index 1a3a4cff..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_color-bg.scss +++ /dev/null @@ -1,7 +0,0 @@ -// All-caps `RGBA()` function used because of this Sass bug: https://github.com/sass/node-sass/issues/2251 -@each $color, $value in $theme-colors { - .text-bg-#{$color} { - color: color-contrast($value) if($enable-important-utilities, !important, null); - background-color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}bg-opacity, 1)) if($enable-important-utilities, !important, null); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_colored-links.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_colored-links.scss deleted file mode 100644 index 5f868578..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_colored-links.scss +++ /dev/null @@ -1,30 +0,0 @@ -// All-caps `RGBA()` function used because of this Sass bug: https://github.com/sass/node-sass/issues/2251 -@each $color, $value in $theme-colors { - .link-#{$color} { - color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null); - text-decoration-color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null); - - @if $link-shade-percentage != 0 { - &:hover, - &:focus { - $hover-color: if(color-contrast($value) == $color-contrast-light, shade-color($value, $link-shade-percentage), tint-color($value, $link-shade-percentage)); - color: RGBA(#{to-rgb($hover-color)}, var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null); - text-decoration-color: RGBA(to-rgb($hover-color), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null); - } - } - } -} - -// One-off special link helper as a bridge until v6 -.link-body-emphasis { - color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-opacity, 1)) if($enable-important-utilities, !important, null); - text-decoration-color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-underline-opacity, 1)) if($enable-important-utilities, !important, null); - - @if $link-shade-percentage != 0 { - &:hover, - &:focus { - color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-opacity, .75)) if($enable-important-utilities, !important, null); - text-decoration-color: RGBA(var(--#{$prefix}emphasis-color-rgb), var(--#{$prefix}link-underline-opacity, .75)) if($enable-important-utilities, !important, null); - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_focus-ring.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_focus-ring.scss deleted file mode 100644 index 26508a8d..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_focus-ring.scss +++ /dev/null @@ -1,5 +0,0 @@ -.focus-ring:focus { - outline: 0; - // By default, there is no `--bs-focus-ring-x`, `--bs-focus-ring-y`, or `--bs-focus-ring-blur`, but we provide CSS variables with fallbacks to initial `0` values - box-shadow: var(--#{$prefix}focus-ring-x, 0) var(--#{$prefix}focus-ring-y, 0) var(--#{$prefix}focus-ring-blur, 0) var(--#{$prefix}focus-ring-width) var(--#{$prefix}focus-ring-color); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_icon-link.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_icon-link.scss deleted file mode 100644 index 3f8bcb33..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_icon-link.scss +++ /dev/null @@ -1,25 +0,0 @@ -.icon-link { - display: inline-flex; - gap: $icon-link-gap; - align-items: center; - text-decoration-color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, .5)); - text-underline-offset: $icon-link-underline-offset; - backface-visibility: hidden; - - > .bi { - flex-shrink: 0; - width: $icon-link-icon-size; - height: $icon-link-icon-size; - fill: currentcolor; - @include transition($icon-link-icon-transition); - } -} - -.icon-link-hover { - &:hover, - &:focus-visible { - > .bi { - transform: var(--#{$prefix}icon-link-transform, $icon-link-icon-transform); - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_position.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_position.scss deleted file mode 100644 index 59103d94..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_position.scss +++ /dev/null @@ -1,36 +0,0 @@ -// Shorthand - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: $zindex-fixed; -} - -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: $zindex-fixed; -} - -// Responsive sticky top and bottom -@each $breakpoint in map-keys($grid-breakpoints) { - @include media-breakpoint-up($breakpoint) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - - .sticky#{$infix}-top { - position: sticky; - top: 0; - z-index: $zindex-sticky; - } - - .sticky#{$infix}-bottom { - position: sticky; - bottom: 0; - z-index: $zindex-sticky; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_ratio.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_ratio.scss deleted file mode 100644 index b6a7654c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_ratio.scss +++ /dev/null @@ -1,26 +0,0 @@ -// Credit: Nicolas Gallagher and SUIT CSS. - -.ratio { - position: relative; - width: 100%; - - &::before { - display: block; - padding-top: var(--#{$prefix}aspect-ratio); - content: ""; - } - - > * { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - } -} - -@each $key, $ratio in $aspect-ratios { - .ratio-#{$key} { - --#{$prefix}aspect-ratio: #{$ratio}; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stacks.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stacks.scss deleted file mode 100644 index 6cd237ae..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stacks.scss +++ /dev/null @@ -1,15 +0,0 @@ -// scss-docs-start stacks -.hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; -} - -.vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; -} -// scss-docs-end stacks diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stretched-link.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stretched-link.scss deleted file mode 100644 index 71a1c755..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_stretched-link.scss +++ /dev/null @@ -1,15 +0,0 @@ -// -// Stretched link -// - -.stretched-link { - &::#{$stretched-link-pseudo-element} { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: $stretched-link-z-index; - content: ""; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_text-truncation.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_text-truncation.scss deleted file mode 100644 index 6421dac9..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_text-truncation.scss +++ /dev/null @@ -1,7 +0,0 @@ -// -// Text truncation -// - -.text-truncate { - @include text-truncate(); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_visually-hidden.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_visually-hidden.scss deleted file mode 100644 index 4760ff03..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_visually-hidden.scss +++ /dev/null @@ -1,8 +0,0 @@ -// -// Visually hidden -// - -.visually-hidden, -.visually-hidden-focusable:not(:focus):not(:focus-within) { - @include visually-hidden(); -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_vr.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_vr.scss deleted file mode 100644 index b6f9d42c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/helpers/_vr.scss +++ /dev/null @@ -1,8 +0,0 @@ -.vr { - display: inline-block; - align-self: stretch; - width: $vr-border-width; - min-height: 1em; - background-color: currentcolor; - opacity: $hr-opacity; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_alert.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_alert.scss deleted file mode 100644 index fb524af1..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_alert.scss +++ /dev/null @@ -1,18 +0,0 @@ -@include deprecate("`alert-variant()`", "v5.3.0", "v6.0.0"); - -// scss-docs-start alert-variant-mixin -@mixin alert-variant($background, $border, $color) { - --#{$prefix}alert-color: #{$color}; - --#{$prefix}alert-bg: #{$background}; - --#{$prefix}alert-border-color: #{$border}; - --#{$prefix}alert-link-color: #{shade-color($color, 20%)}; - - @if $enable-gradients { - background-image: var(--#{$prefix}gradient); - } - - .alert-link { - color: var(--#{$prefix}alert-link-color); - } -} -// scss-docs-end alert-variant-mixin diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_backdrop.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_backdrop.scss deleted file mode 100644 index 9705ae9e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_backdrop.scss +++ /dev/null @@ -1,14 +0,0 @@ -// Shared between modals and offcanvases -@mixin overlay-backdrop($zindex, $backdrop-bg, $backdrop-opacity) { - position: fixed; - top: 0; - left: 0; - z-index: $zindex; - width: 100vw; - height: 100vh; - background-color: $backdrop-bg; - - // Fade for backdrop - &.fade { opacity: 0; } - &.show { opacity: $backdrop-opacity; } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_banner.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_banner.scss deleted file mode 100644 index dd8a5103..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_banner.scss +++ /dev/null @@ -1,7 +0,0 @@ -@mixin bsBanner($file) { - /*! - * Bootstrap #{$file} v5.3.8 (https://getbootstrap.com/) - * Copyright 2011-2025 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_border-radius.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_border-radius.scss deleted file mode 100644 index 616decbc..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_border-radius.scss +++ /dev/null @@ -1,78 +0,0 @@ -// stylelint-disable property-disallowed-list -// Single side border-radius - -// Helper function to replace negative values with 0 -@function valid-radius($radius) { - $return: (); - @each $value in $radius { - @if type-of($value) == number { - $return: append($return, max($value, 0)); - } @else { - $return: append($return, $value); - } - } - @return $return; -} - -// scss-docs-start border-radius-mixins -@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) { - @if $enable-rounded { - border-radius: valid-radius($radius); - } - @else if $fallback-border-radius != false { - border-radius: $fallback-border-radius; - } -} - -@mixin border-top-radius($radius: $border-radius) { - @if $enable-rounded { - border-top-left-radius: valid-radius($radius); - border-top-right-radius: valid-radius($radius); - } -} - -@mixin border-end-radius($radius: $border-radius) { - @if $enable-rounded { - border-top-right-radius: valid-radius($radius); - border-bottom-right-radius: valid-radius($radius); - } -} - -@mixin border-bottom-radius($radius: $border-radius) { - @if $enable-rounded { - border-bottom-right-radius: valid-radius($radius); - border-bottom-left-radius: valid-radius($radius); - } -} - -@mixin border-start-radius($radius: $border-radius) { - @if $enable-rounded { - border-top-left-radius: valid-radius($radius); - border-bottom-left-radius: valid-radius($radius); - } -} - -@mixin border-top-start-radius($radius: $border-radius) { - @if $enable-rounded { - border-top-left-radius: valid-radius($radius); - } -} - -@mixin border-top-end-radius($radius: $border-radius) { - @if $enable-rounded { - border-top-right-radius: valid-radius($radius); - } -} - -@mixin border-bottom-end-radius($radius: $border-radius) { - @if $enable-rounded { - border-bottom-right-radius: valid-radius($radius); - } -} - -@mixin border-bottom-start-radius($radius: $border-radius) { - @if $enable-rounded { - border-bottom-left-radius: valid-radius($radius); - } -} -// scss-docs-end border-radius-mixins diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_box-shadow.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_box-shadow.scss deleted file mode 100644 index 0bb6bf7e..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_box-shadow.scss +++ /dev/null @@ -1,24 +0,0 @@ -@mixin box-shadow($shadow...) { - @if $enable-shadows { - $result: (); - $has-single-value: false; - $single-value: null; - - @each $value in $shadow { - @if $value != null { - @if $value == none or $value == initial or $value == inherit or $value == unset { - $has-single-value: true; - $single-value: $value; - } @else { - $result: append($result, $value, "comma"); - } - } - } - - @if $has-single-value { - box-shadow: $single-value; - } @else if (length($result) > 0) { - box-shadow: $result; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_breakpoints.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_breakpoints.scss deleted file mode 100644 index 286be893..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_breakpoints.scss +++ /dev/null @@ -1,127 +0,0 @@ -// Breakpoint viewport sizes and media queries. -// -// Breakpoints are defined as a map of (name: minimum width), order from small to large: -// -// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px) -// -// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default. - -// Name of the next breakpoint, or null for the last breakpoint. -// -// >> breakpoint-next(sm) -// md -// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) -// md -// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl)) -// md -@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) { - $n: index($breakpoint-names, $name); - @if not $n { - @error "breakpoint `#{$name}` not found in `#{$breakpoints}`"; - } - @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); -} - -// Minimum breakpoint width. Null for the smallest (first) breakpoint. -// -// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) -// 576px -@function breakpoint-min($name, $breakpoints: $grid-breakpoints) { - $min: map-get($breakpoints, $name); - @return if($min != 0, $min, null); -} - -// Maximum breakpoint width. -// The maximum value is reduced by 0.02px to work around the limitations of -// `min-` and `max-` prefixes and viewports with fractional widths. -// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max -// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. -// See https://bugs.webkit.org/show_bug.cgi?id=178261 -// -// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) -// 767.98px -@function breakpoint-max($name, $breakpoints: $grid-breakpoints) { - $max: map-get($breakpoints, $name); - @return if($max and $max > 0, $max - .02, null); -} - -// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front. -// Useful for making responsive utilities. -// -// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) -// "" (Returns a blank string) -// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) -// "-sm" -@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) { - @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); -} - -// Media of at least the minimum breakpoint width. No query for the smallest breakpoint. -// Makes the @content apply to the given breakpoint and wider. -@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { - $min: breakpoint-min($name, $breakpoints); - @if $min { - @media (min-width: $min) { - @content; - } - } @else { - @content; - } -} - -// Media of at most the maximum breakpoint width. No query for the largest breakpoint. -// Makes the @content apply to the given breakpoint and narrower. -@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) { - $max: breakpoint-max($name, $breakpoints); - @if $max { - @media (max-width: $max) { - @content; - } - } @else { - @content; - } -} - -// Media that spans multiple breakpoint widths. -// Makes the @content apply between the min and max breakpoints -@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) { - $min: breakpoint-min($lower, $breakpoints); - $max: breakpoint-max($upper, $breakpoints); - - @if $min != null and $max != null { - @media (min-width: $min) and (max-width: $max) { - @content; - } - } @else if $max == null { - @include media-breakpoint-up($lower, $breakpoints) { - @content; - } - } @else if $min == null { - @include media-breakpoint-down($upper, $breakpoints) { - @content; - } - } -} - -// Media between the breakpoint's minimum and maximum widths. -// No minimum for the smallest breakpoint, and no maximum for the largest one. -// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower. -@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) { - $min: breakpoint-min($name, $breakpoints); - $next: breakpoint-next($name, $breakpoints); - $max: breakpoint-max($next, $breakpoints); - - @if $min != null and $max != null { - @media (min-width: $min) and (max-width: $max) { - @content; - } - } @else if $max == null { - @include media-breakpoint-up($name, $breakpoints) { - @content; - } - } @else if $min == null { - @include media-breakpoint-down($next, $breakpoints) { - @content; - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_buttons.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_buttons.scss deleted file mode 100644 index cf087fda..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_buttons.scss +++ /dev/null @@ -1,70 +0,0 @@ -// Button variants -// -// Easily pump out default styles, as well as :hover, :focus, :active, -// and disabled options for all buttons - -// scss-docs-start btn-variant-mixin -@mixin button-variant( - $background, - $border, - $color: color-contrast($background), - $hover-background: if($color == $color-contrast-light, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)), - $hover-border: if($color == $color-contrast-light, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)), - $hover-color: color-contrast($hover-background), - $active-background: if($color == $color-contrast-light, shade-color($background, $btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)), - $active-border: if($color == $color-contrast-light, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)), - $active-color: color-contrast($active-background), - $disabled-background: $background, - $disabled-border: $border, - $disabled-color: color-contrast($disabled-background) -) { - --#{$prefix}btn-color: #{$color}; - --#{$prefix}btn-bg: #{$background}; - --#{$prefix}btn-border-color: #{$border}; - --#{$prefix}btn-hover-color: #{$hover-color}; - --#{$prefix}btn-hover-bg: #{$hover-background}; - --#{$prefix}btn-hover-border-color: #{$hover-border}; - --#{$prefix}btn-focus-shadow-rgb: #{to-rgb(mix($color, $border, 15%))}; - --#{$prefix}btn-active-color: #{$active-color}; - --#{$prefix}btn-active-bg: #{$active-background}; - --#{$prefix}btn-active-border-color: #{$active-border}; - --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow}; - --#{$prefix}btn-disabled-color: #{$disabled-color}; - --#{$prefix}btn-disabled-bg: #{$disabled-background}; - --#{$prefix}btn-disabled-border-color: #{$disabled-border}; -} -// scss-docs-end btn-variant-mixin - -// scss-docs-start btn-outline-variant-mixin -@mixin button-outline-variant( - $color, - $color-hover: color-contrast($color), - $active-background: $color, - $active-border: $color, - $active-color: color-contrast($active-background) -) { - --#{$prefix}btn-color: #{$color}; - --#{$prefix}btn-border-color: #{$color}; - --#{$prefix}btn-hover-color: #{$color-hover}; - --#{$prefix}btn-hover-bg: #{$active-background}; - --#{$prefix}btn-hover-border-color: #{$active-border}; - --#{$prefix}btn-focus-shadow-rgb: #{to-rgb($color)}; - --#{$prefix}btn-active-color: #{$active-color}; - --#{$prefix}btn-active-bg: #{$active-background}; - --#{$prefix}btn-active-border-color: #{$active-border}; - --#{$prefix}btn-active-shadow: #{$btn-active-box-shadow}; - --#{$prefix}btn-disabled-color: #{$color}; - --#{$prefix}btn-disabled-bg: transparent; - --#{$prefix}btn-disabled-border-color: #{$color}; - --#{$prefix}gradient: none; -} -// scss-docs-end btn-outline-variant-mixin - -// scss-docs-start btn-size-mixin -@mixin button-size($padding-y, $padding-x, $font-size, $border-radius) { - --#{$prefix}btn-padding-y: #{$padding-y}; - --#{$prefix}btn-padding-x: #{$padding-x}; - @include rfs($font-size, --#{$prefix}btn-font-size); - --#{$prefix}btn-border-radius: #{$border-radius}; -} -// scss-docs-end btn-size-mixin diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_caret.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_caret.scss deleted file mode 100644 index be731165..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_caret.scss +++ /dev/null @@ -1,69 +0,0 @@ -// scss-docs-start caret-mixins -@mixin caret-down($width: $caret-width) { - border-top: $width solid; - border-right: $width solid transparent; - border-bottom: 0; - border-left: $width solid transparent; -} - -@mixin caret-up($width: $caret-width) { - border-top: 0; - border-right: $width solid transparent; - border-bottom: $width solid; - border-left: $width solid transparent; -} - -@mixin caret-end($width: $caret-width) { - border-top: $width solid transparent; - border-right: 0; - border-bottom: $width solid transparent; - border-left: $width solid; -} - -@mixin caret-start($width: $caret-width) { - border-top: $width solid transparent; - border-right: $width solid; - border-bottom: $width solid transparent; -} - -@mixin caret( - $direction: down, - $width: $caret-width, - $spacing: $caret-spacing, - $vertical-align: $caret-vertical-align -) { - @if $enable-caret { - &::after { - display: inline-block; - margin-left: $spacing; - vertical-align: $vertical-align; - content: ""; - @if $direction == down { - @include caret-down($width); - } @else if $direction == up { - @include caret-up($width); - } @else if $direction == end { - @include caret-end($width); - } - } - - @if $direction == start { - &::after { - display: none; - } - - &::before { - display: inline-block; - margin-right: $spacing; - vertical-align: $vertical-align; - content: ""; - @include caret-start($width); - } - } - - &:empty::after { - margin-left: 0; - } - } -} -// scss-docs-end caret-mixins diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_clearfix.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_clearfix.scss deleted file mode 100644 index ffc62bb2..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_clearfix.scss +++ /dev/null @@ -1,9 +0,0 @@ -// scss-docs-start clearfix -@mixin clearfix() { - &::after { - display: block; - clear: both; - content: ""; - } -} -// scss-docs-end clearfix diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-mode.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-mode.scss deleted file mode 100644 index 03338b02..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-mode.scss +++ /dev/null @@ -1,21 +0,0 @@ -// scss-docs-start color-mode-mixin -@mixin color-mode($mode: light, $root: false) { - @if $color-mode-type == "media-query" { - @if $root == true { - @media (prefers-color-scheme: $mode) { - :root { - @content; - } - } - } @else { - @media (prefers-color-scheme: $mode) { - @content; - } - } - } @else { - [data-bs-theme="#{$mode}"] { - @content; - } - } -} -// scss-docs-end color-mode-mixin diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-scheme.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-scheme.scss deleted file mode 100644 index 90497aa0..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_color-scheme.scss +++ /dev/null @@ -1,7 +0,0 @@ -// scss-docs-start mixin-color-scheme -@mixin color-scheme($name) { - @media (prefers-color-scheme: #{$name}) { - @content; - } -} -// scss-docs-end mixin-color-scheme diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_container.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_container.scss deleted file mode 100644 index b9f33519..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_container.scss +++ /dev/null @@ -1,11 +0,0 @@ -// Container mixins - -@mixin make-container($gutter: $container-padding-x) { - --#{$prefix}gutter-x: #{$gutter}; - --#{$prefix}gutter-y: 0; - width: 100%; - padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list - padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list - margin-right: auto; - margin-left: auto; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_deprecate.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_deprecate.scss deleted file mode 100644 index df070bc5..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_deprecate.scss +++ /dev/null @@ -1,10 +0,0 @@ -// Deprecate mixin -// -// This mixin can be used to deprecate mixins or functions. -// `$enable-deprecation-messages` is a global variable, `$ignore-warning` is a variable that can be passed to -// some deprecated mixins to suppress the warning (for example if the mixin is still be used in the current version of Bootstrap) -@mixin deprecate($name, $deprecate-version, $remove-version, $ignore-warning: false) { - @if ($enable-deprecation-messages != false and $ignore-warning != true) { - @warn "#{$name} has been deprecated as of #{$deprecate-version}. It will be removed entirely in #{$remove-version}."; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_forms.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_forms.scss deleted file mode 100644 index 00b47641..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_forms.scss +++ /dev/null @@ -1,163 +0,0 @@ -// This mixin uses an `if()` technique to be compatible with Dart Sass -// See https://github.com/sass/sass/issues/1873#issuecomment-152293725 for more details - -// scss-docs-start form-validation-mixins -@mixin form-validation-state-selector($state) { - @if ($state == "valid" or $state == "invalid") { - .was-validated #{if(&, "&", "")}:#{$state}, - #{if(&, "&", "")}.is-#{$state} { - @content; - } - } @else { - #{if(&, "&", "")}.is-#{$state} { - @content; - } - } -} - -@mixin form-validation-state( - $state, - $color, - $icon, - $tooltip-color: color-contrast($color), - $tooltip-bg-color: rgba($color, $form-feedback-tooltip-opacity), - $focus-box-shadow: 0 0 $input-btn-focus-blur $input-focus-width rgba($color, $input-btn-focus-color-opacity), - $border-color: $color -) { - .#{$state}-feedback { - display: none; - width: 100%; - margin-top: $form-feedback-margin-top; - @include font-size($form-feedback-font-size); - font-style: $form-feedback-font-style; - color: $color; - } - - .#{$state}-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; // Contain to parent when possible - padding: $form-feedback-tooltip-padding-y $form-feedback-tooltip-padding-x; - margin-top: .1rem; - @include font-size($form-feedback-tooltip-font-size); - line-height: $form-feedback-tooltip-line-height; - color: $tooltip-color; - background-color: $tooltip-bg-color; - @include border-radius($form-feedback-tooltip-border-radius); - } - - @include form-validation-state-selector($state) { - ~ .#{$state}-feedback, - ~ .#{$state}-tooltip { - display: block; - } - } - - .form-control { - @include form-validation-state-selector($state) { - border-color: $border-color; - - @if $enable-validation-icons { - padding-right: $input-height-inner; - background-image: escape-svg($icon); - background-repeat: no-repeat; - background-position: right $input-height-inner-quarter center; - background-size: $input-height-inner-half $input-height-inner-half; - } - - &:focus { - border-color: $border-color; - @if $enable-shadows { - @include box-shadow($input-box-shadow, $focus-box-shadow); - } @else { - // Avoid using mixin so we can pass custom focus shadow properly - box-shadow: $focus-box-shadow; - } - } - } - } - - // stylelint-disable-next-line selector-no-qualifying-type - textarea.form-control { - @include form-validation-state-selector($state) { - @if $enable-validation-icons { - padding-right: $input-height-inner; - background-position: top $input-height-inner-quarter right $input-height-inner-quarter; - } - } - } - - .form-select { - @include form-validation-state-selector($state) { - border-color: $border-color; - - @if $enable-validation-icons { - &:not([multiple]):not([size]), - &:not([multiple])[size="1"] { - --#{$prefix}form-select-bg-icon: #{escape-svg($icon)}; - padding-right: $form-select-feedback-icon-padding-end; - background-position: $form-select-bg-position, $form-select-feedback-icon-position; - background-size: $form-select-bg-size, $form-select-feedback-icon-size; - } - } - - &:focus { - border-color: $border-color; - @if $enable-shadows { - @include box-shadow($form-select-box-shadow, $focus-box-shadow); - } @else { - // Avoid using mixin so we can pass custom focus shadow properly - box-shadow: $focus-box-shadow; - } - } - } - } - - .form-control-color { - @include form-validation-state-selector($state) { - @if $enable-validation-icons { - width: add($form-color-width, $input-height-inner); - } - } - } - - .form-check-input { - @include form-validation-state-selector($state) { - border-color: $border-color; - - &:checked { - background-color: $color; - } - - &:focus { - box-shadow: $focus-box-shadow; - } - - ~ .form-check-label { - color: $color; - } - } - } - .form-check-inline .form-check-input { - ~ .#{$state}-feedback { - margin-left: .5em; - } - } - - .input-group { - > .form-control:not(:focus), - > .form-select:not(:focus), - > .form-floating:not(:focus-within) { - @include form-validation-state-selector($state) { - @if $state == "valid" { - z-index: 3; - } @else if $state == "invalid" { - z-index: 4; - } - } - } - } -} -// scss-docs-end form-validation-mixins diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_gradients.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_gradients.scss deleted file mode 100644 index 608e18df..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_gradients.scss +++ /dev/null @@ -1,47 +0,0 @@ -// Gradients - -// scss-docs-start gradient-bg-mixin -@mixin gradient-bg($color: null) { - background-color: $color; - - @if $enable-gradients { - background-image: var(--#{$prefix}gradient); - } -} -// scss-docs-end gradient-bg-mixin - -// scss-docs-start gradient-mixins -// Horizontal gradient, from left to right -// -// Creates two color stops, start and end, by specifying a color and position for each color stop. -@mixin gradient-x($start-color: $gray-700, $end-color: $gray-800, $start-percent: 0%, $end-percent: 100%) { - background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent); -} - -// Vertical gradient, from top to bottom -// -// Creates two color stops, start and end, by specifying a color and position for each color stop. -@mixin gradient-y($start-color: $gray-700, $end-color: $gray-800, $start-percent: null, $end-percent: null) { - background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent); -} - -@mixin gradient-directional($start-color: $gray-700, $end-color: $gray-800, $deg: 45deg) { - background-image: linear-gradient($deg, $start-color, $end-color); -} - -@mixin gradient-x-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { - background-image: linear-gradient(to right, $start-color, $mid-color $color-stop, $end-color); -} - -@mixin gradient-y-three-colors($start-color: $blue, $mid-color: $purple, $color-stop: 50%, $end-color: $red) { - background-image: linear-gradient($start-color, $mid-color $color-stop, $end-color); -} - -@mixin gradient-radial($inner-color: $gray-700, $outer-color: $gray-800) { - background-image: radial-gradient(circle, $inner-color, $outer-color); -} - -@mixin gradient-striped($color: rgba($white, .15), $angle: 45deg) { - background-image: linear-gradient($angle, $color 25%, transparent 25%, transparent 50%, $color 50%, $color 75%, transparent 75%, transparent); -} -// scss-docs-end gradient-mixins diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_grid.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_grid.scss deleted file mode 100644 index db77e07f..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_grid.scss +++ /dev/null @@ -1,151 +0,0 @@ -// Grid system -// -// Generate semantic grid columns with these mixins. - -@mixin make-row($gutter: $grid-gutter-width) { - --#{$prefix}gutter-x: #{$gutter}; - --#{$prefix}gutter-y: 0; - display: flex; - flex-wrap: wrap; - // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed - margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list - margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list - margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list -} - -@mixin make-col-ready() { - // Add box sizing if only the grid is loaded - box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null); - // Prevent columns from becoming too narrow when at smaller grid tiers by - // always setting `width: 100%;`. This works because we set the width - // later on to override this initial width. - flex-shrink: 0; - width: 100%; - max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid - padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list - padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list - margin-top: var(--#{$prefix}gutter-y); -} - -@mixin make-col($size: false, $columns: $grid-columns) { - @if $size { - flex: 0 0 auto; - width: percentage(divide($size, $columns)); - - } @else { - flex: 1 1 0; - max-width: 100%; - } -} - -@mixin make-col-auto() { - flex: 0 0 auto; - width: auto; -} - -@mixin make-col-offset($size, $columns: $grid-columns) { - $num: divide($size, $columns); - margin-left: if($num == 0, 0, percentage($num)); -} - -// Row columns -// -// Specify on a parent element(e.g., .row) to force immediate children into NN -// number of columns. Supports wrapping to new lines, but does not do a Masonry -// style grid. -@mixin row-cols($count) { - > * { - flex: 0 0 auto; - width: percentage(divide(1, $count)); - } -} - -// Framework grid generation -// -// Used only by Bootstrap to generate the correct number of grid classes given -// any value of `$grid-columns`. - -@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) { - @each $breakpoint in map-keys($breakpoints) { - $infix: breakpoint-infix($breakpoint, $breakpoints); - - @include media-breakpoint-up($breakpoint, $breakpoints) { - // Provide basic `.col-{bp}` classes for equal-width flexbox columns - .col#{$infix} { - flex: 1 0 0; - } - - .row-cols#{$infix}-auto > * { - @include make-col-auto(); - } - - @if $grid-row-columns > 0 { - @for $i from 1 through $grid-row-columns { - .row-cols#{$infix}-#{$i} { - @include row-cols($i); - } - } - } - - .col#{$infix}-auto { - @include make-col-auto(); - } - - @if $columns > 0 { - @for $i from 1 through $columns { - .col#{$infix}-#{$i} { - @include make-col($i, $columns); - } - } - - // `$columns - 1` because offsetting by the width of an entire row isn't possible - @for $i from 0 through ($columns - 1) { - @if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0 - .offset#{$infix}-#{$i} { - @include make-col-offset($i, $columns); - } - } - } - } - - // Gutters - // - // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns. - @each $key, $value in $gutters { - .g#{$infix}-#{$key}, - .gx#{$infix}-#{$key} { - --#{$prefix}gutter-x: #{$value}; - } - - .g#{$infix}-#{$key}, - .gy#{$infix}-#{$key} { - --#{$prefix}gutter-y: #{$value}; - } - } - } - } -} - -@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) { - @each $breakpoint in map-keys($breakpoints) { - $infix: breakpoint-infix($breakpoint, $breakpoints); - - @include media-breakpoint-up($breakpoint, $breakpoints) { - @if $columns > 0 { - @for $i from 1 through $columns { - .g-col#{$infix}-#{$i} { - grid-column: auto / span $i; - } - } - - // Start with `1` because `0` is an invalid value. - // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible. - @for $i from 1 through ($columns - 1) { - .g-start#{$infix}-#{$i} { - grid-column-start: $i; - } - } - } - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_image.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_image.scss deleted file mode 100644 index e1df779f..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_image.scss +++ /dev/null @@ -1,16 +0,0 @@ -// Image Mixins -// - Responsive image -// - Retina image - - -// Responsive image -// -// Keep images from scaling beyond the width of their parents. - -@mixin img-fluid { - // Part 1: Set a maximum relative to the parent - max-width: 100%; - // Part 2: Override the height to auto, otherwise images will be stretched - // when setting a width and height attribute on the img element. - height: auto; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_list-group.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_list-group.scss deleted file mode 100644 index 6274f343..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_list-group.scss +++ /dev/null @@ -1,26 +0,0 @@ -@include deprecate("`list-group-item-variant()`", "v5.3.0", "v6.0.0"); - -// List Groups - -// scss-docs-start list-group-mixin -@mixin list-group-item-variant($state, $background, $color) { - .list-group-item-#{$state} { - color: $color; - background-color: $background; - - &.list-group-item-action { - &:hover, - &:focus { - color: $color; - background-color: shade-color($background, 10%); - } - - &.active { - color: $white; - background-color: $color; - border-color: $color; - } - } - } -} -// scss-docs-end list-group-mixin diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_lists.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_lists.scss deleted file mode 100644 index 25185626..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_lists.scss +++ /dev/null @@ -1,7 +0,0 @@ -// Lists - -// Unstyled keeps list items block level, just removes default browser padding and list-style -@mixin list-unstyled { - padding-left: 0; - list-style: none; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_pagination.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_pagination.scss deleted file mode 100644 index 0d657964..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_pagination.scss +++ /dev/null @@ -1,10 +0,0 @@ -// Pagination - -// scss-docs-start pagination-mixin -@mixin pagination-size($padding-y, $padding-x, $font-size, $border-radius) { - --#{$prefix}pagination-padding-x: #{$padding-x}; - --#{$prefix}pagination-padding-y: #{$padding-y}; - @include rfs($font-size, --#{$prefix}pagination-font-size); - --#{$prefix}pagination-border-radius: #{$border-radius}; -} -// scss-docs-end pagination-mixin diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_reset-text.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_reset-text.scss deleted file mode 100644 index f5bd1afe..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_reset-text.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin reset-text { - font-family: $font-family-base; - // We deliberately do NOT reset font-size or overflow-wrap / word-wrap. - font-style: normal; - font-weight: $font-weight-normal; - line-height: $line-height-base; - text-align: left; // Fallback for where `start` is not supported - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - white-space: normal; - word-spacing: normal; - line-break: auto; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_resize.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_resize.scss deleted file mode 100644 index 66f233a6..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_resize.scss +++ /dev/null @@ -1,6 +0,0 @@ -// Resize anything - -@mixin resizable($direction) { - overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` - resize: $direction; // Options: horizontal, vertical, both -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_table-variants.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_table-variants.scss deleted file mode 100644 index 5fe1b9b2..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_table-variants.scss +++ /dev/null @@ -1,24 +0,0 @@ -// scss-docs-start table-variant -@mixin table-variant($state, $background) { - .table-#{$state} { - $color: color-contrast(opaque($body-bg, $background)); - $hover-bg: mix($color, $background, percentage($table-hover-bg-factor)); - $striped-bg: mix($color, $background, percentage($table-striped-bg-factor)); - $active-bg: mix($color, $background, percentage($table-active-bg-factor)); - $table-border-color: mix($color, $background, percentage($table-border-factor)); - - --#{$prefix}table-color: #{$color}; - --#{$prefix}table-bg: #{$background}; - --#{$prefix}table-border-color: #{$table-border-color}; - --#{$prefix}table-striped-bg: #{$striped-bg}; - --#{$prefix}table-striped-color: #{color-contrast($striped-bg)}; - --#{$prefix}table-active-bg: #{$active-bg}; - --#{$prefix}table-active-color: #{color-contrast($active-bg)}; - --#{$prefix}table-hover-bg: #{$hover-bg}; - --#{$prefix}table-hover-color: #{color-contrast($hover-bg)}; - - color: var(--#{$prefix}table-color); - border-color: var(--#{$prefix}table-border-color); - } -} -// scss-docs-end table-variant diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_text-truncate.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_text-truncate.scss deleted file mode 100644 index 3504bb1a..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_text-truncate.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Text truncate -// Requires inline-block or block for proper styling - -@mixin text-truncate() { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_transition.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_transition.scss deleted file mode 100644 index d437f6d8..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_transition.scss +++ /dev/null @@ -1,26 +0,0 @@ -// stylelint-disable property-disallowed-list -@mixin transition($transition...) { - @if length($transition) == 0 { - $transition: $transition-base; - } - - @if length($transition) > 1 { - @each $value in $transition { - @if $value == null or $value == none { - @warn "The keyword 'none' or 'null' must be used as a single argument."; - } - } - } - - @if $enable-transitions { - @if nth($transition, 1) != null { - transition: $transition; - } - - @if $enable-reduced-motion and nth($transition, 1) != null and nth($transition, 1) != none { - @media (prefers-reduced-motion: reduce) { - transition: none; - } - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_utilities.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_utilities.scss deleted file mode 100644 index 4795e894..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_utilities.scss +++ /dev/null @@ -1,97 +0,0 @@ -// Utility generator -// Used to generate utilities & print utilities -@mixin generate-utility($utility, $infix: "", $is-rfs-media-query: false) { - $values: map-get($utility, values); - - // If the values are a list or string, convert it into a map - @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" { - $values: zip($values, $values); - } - - @each $key, $value in $values { - $properties: map-get($utility, property); - - // Multiple properties are possible, for example with vertical or horizontal margins or paddings - @if type-of($properties) == "string" { - $properties: append((), $properties); - } - - // Use custom class if present - $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1)); - $property-class: if($property-class == null, "", $property-class); - - // Use custom CSS variable name if present, otherwise default to `class` - $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class)); - - // State params to generate pseudo-classes - $state: if(map-has-key($utility, state), map-get($utility, state), ()); - - $infix: if($property-class == "" and str-slice($infix, 1, 1) == "-", str-slice($infix, 2), $infix); - - // Don't prefix if value key is null (e.g. with shadow class) - $property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, ""); - - @if map-get($utility, rfs) { - // Inside the media query - @if $is-rfs-media-query { - $val: rfs-value($value); - - // Do not render anything if fluid and non fluid values are the same - $value: if($val == rfs-fluid-value($value), null, $val); - } - @else { - $value: rfs-fluid-value($value); - } - } - - $is-css-var: map-get($utility, css-var); - $is-local-vars: map-get($utility, local-vars); - $is-rtl: map-get($utility, rtl); - - @if $value != null { - @if $is-rtl == false { - /* rtl:begin:remove */ - } - - @if $is-css-var { - .#{$property-class + $infix + $property-class-modifier} { - --#{$prefix}#{$css-variable-name}: #{$value}; - } - - @each $pseudo in $state { - .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} { - --#{$prefix}#{$css-variable-name}: #{$value}; - } - } - } @else { - .#{$property-class + $infix + $property-class-modifier} { - @each $property in $properties { - @if $is-local-vars { - @each $local-var, $variable in $is-local-vars { - --#{$prefix}#{$local-var}: #{$variable}; - } - } - #{$property}: $value if($enable-important-utilities, !important, null); - } - } - - @each $pseudo in $state { - .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} { - @each $property in $properties { - @if $is-local-vars { - @each $local-var, $variable in $is-local-vars { - --#{$prefix}#{$local-var}: #{$variable}; - } - } - #{$property}: $value if($enable-important-utilities, !important, null); - } - } - } - } - - @if $is-rtl == false { - /* rtl:end:remove */ - } - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_visually-hidden.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_visually-hidden.scss deleted file mode 100644 index 9dd0ad33..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/mixins/_visually-hidden.scss +++ /dev/null @@ -1,38 +0,0 @@ -// stylelint-disable declaration-no-important - -// Hide content visually while keeping it accessible to assistive technologies -// -// See: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/ -// See: https://kittygiraudel.com/2016/10/13/css-hide-and-seek/ - -@mixin visually-hidden() { - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; // Fix for https://github.com/twbs/bootstrap/issues/25686 - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; - - // Fix for positioned table caption that could become anonymous cells - &:not(caption) { - position: absolute !important; - } - - // Fix to prevent overflowing children to become focusable - * { - overflow: hidden !important; - } -} - -// Use to only display content when it's focused, or one of its child elements is focused -// (i.e. when focus is within the element/container that the class was applied to) -// -// Useful for "Skip to main content" links; see https://www.w3.org/WAI/WCAG22/Techniques/general/G1.html - -@mixin visually-hidden-focusable() { - &:not(:focus):not(:focus-within) { - @include visually-hidden(); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/jasmine.js b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/jasmine.js deleted file mode 100644 index 25d838c9..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/jasmine.js +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-disable camelcase */ - -'use strict' - -const path = require('node:path') - -module.exports = { - spec_dir: 'scss', - // Make Jasmine look for `.test.scss` files - spec_files: ['**/*.{test,spec}.scss'], - // Compile them into JS scripts running `sass-true` - requires: [path.join(__dirname, 'sass-true/register')], - // Ensure we use `require` so that the require.extensions works - // as `import` completely bypasses it - jsLoader: 'require' -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_auto-import-of-variables-dark.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_auto-import-of-variables-dark.test.scss deleted file mode 100644 index f08ae587..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_auto-import-of-variables-dark.test.scss +++ /dev/null @@ -1,7 +0,0 @@ -// TODO: this file can be removed safely in v6 when `@import "variables-dark"` will be removed at the end of _variables.scss - -@import "../../functions"; -@import "../../variables"; -// Voluntarily not importing _variables-dark.scss -@import "../../maps"; -@import "../../mixins"; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_box-shadow.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_box-shadow.test.scss deleted file mode 100644 index f5a07484..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_box-shadow.test.scss +++ /dev/null @@ -1,188 +0,0 @@ -@import "../../functions"; -@import "../../variables"; -@import "../../mixins"; - -// Store original value -$original-enable-shadows: $enable-shadows; - -// Enable shadows for all tests -$enable-shadows: true !global; - -@include describe("box-shadow mixin") { - @include it("handles single none value") { - @include assert() { - @include output() { - .test { - @include box-shadow(none); - } - } - - @include expect() { - .test { - box-shadow: none; - } - } - } - } - - @include it("handles multiple none values by consolidating them") { - @include assert() { - @include output() { - .test { - @include box-shadow(none, none, none); - } - } - - @include expect() { - .test { - box-shadow: none; - } - } - } - } - - @include it("handles other single-value keywords (initial, inherit, unset)") { - @include assert() { - @include output() { - .test-initial { - @include box-shadow(initial); - } - .test-inherit { - @include box-shadow(inherit); - } - .test-unset { - @include box-shadow(unset); - } - } - - @include expect() { - .test-initial { - box-shadow: initial; - } - .test-inherit { - box-shadow: inherit; - } - .test-unset { - box-shadow: unset; - } - } - } - } - - @include it("handles multiple single-value keywords by using the last one") { - @include assert() { - @include output() { - .test { - @include box-shadow(initial, inherit, unset); - } - } - - @include expect() { - .test { - box-shadow: unset; - } - } - } - } - - @include it("handles regular box-shadow values") { - @include assert() { - @include output() { - .test { - @include box-shadow(0 0 10px rgba(0, 0, 0, .5)); - } - } - - @include expect() { - .test { - box-shadow: 0 0 10px rgba(0, 0, 0, .5); - } - } - } - } - - @include it("handles multiple regular box-shadow values") { - @include assert() { - @include output() { - .test { - @include box-shadow(0 0 10px rgba(0, 0, 0, .5), 0 0 20px rgba(0, 0, 0, .3)); - } - } - - @include expect() { - .test { - box-shadow: 0 0 10px rgba(0, 0, 0, .5), 0 0 20px rgba(0, 0, 0, .3); - } - } - } - } - - @include it("handles null values by ignoring them") { - @include assert() { - @include output() { - .test { - @include box-shadow(null, 0 0 10px rgba(0, 0, 0, .5), null); - } - } - - @include expect() { - .test { - box-shadow: 0 0 10px rgba(0, 0, 0, .5); - } - } - } - } - - @include it("handles mixed values with keywords and regular shadows") { - @include assert() { - @include output() { - .test { - @include box-shadow(none, 0 0 10px rgba(0, 0, 0, .5)); - } - } - - @include expect() { - .test { - box-shadow: none; - } - } - } - } - - @include it("handles empty input") { - @include assert() { - @include output() { - .test { - @include box-shadow(); - } - } - - @include expect() { - .test { // stylelint-disable-line block-no-empty - } - } - } - } - - @include it("respects $enable-shadows variable") { - $enable-shadows: false !global; - - @include assert() { - @include output() { - .test { - @include box-shadow(0 0 10px rgba(0, 0, 0, .5)); - } - } - - @include expect() { - .test { // stylelint-disable-line block-no-empty - } - } - } - - $enable-shadows: true !global; - } -} - -// Restore original value -$enable-shadows: $original-enable-shadows !global; diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-contrast.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-contrast.test.scss deleted file mode 100644 index 5b94ac57..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-contrast.test.scss +++ /dev/null @@ -1,139 +0,0 @@ -@import "../../functions"; -@import "../../variables"; -@import "../../variables-dark"; -@import "../../maps"; -@import "../../mixins"; - -@include describe("color-contrast function") { - @include it("should return a color when contrast ratio equals minimum requirement (WCAG 2.1 compliance)") { - // Test case: Background color that produces contrast ratio close to 4.5:1 - // This tests the WCAG 2.1 requirement that contrast should be "at least 4.5:1" (>= 4.5) - // rather than "strictly greater than 4.5:1" (> 4.5) - - // #777777 produces 4.4776:1 contrast ratio with white text - // Since this is below the 4.5:1 threshold, it should return the highest available contrast color - $test-background: #777; - $result: color-contrast($test-background); - - @include assert-equal($result, $black, "Should return black (highest available contrast) for background with 4.4776:1 contrast ratio (below threshold)"); - } - - @include it("should return a color when contrast ratio is above minimum requirement") { - // Test case: Background color that produces contrast ratio above 4.5:1 - // #767676 produces 4.5415:1 contrast ratio with white text - $test-background: #767676; - $result: color-contrast($test-background); - - @include assert-equal($result, $white, "Should return white for background with 4.5415:1 contrast ratio (above threshold)"); - } - - @include it("should return a color when contrast ratio is below minimum requirement") { - // Test case: Background color that produces contrast ratio below 4.5:1 - // #787878 produces 4.4155:1 contrast ratio with white text - $test-background: #787878; - $result: color-contrast($test-background); - - // Should return the color with the highest available contrast ratio - @include assert-equal($result, $black, "Should return black (highest available contrast) for background with 4.4155:1 contrast ratio (below threshold)"); - } - - @include it("should handle edge case with very light background") { - // Test case: Very light background that should return dark text - $test-background: #f8f9fa; // Very light gray - $result: color-contrast($test-background); - - @include assert-equal($result, $color-contrast-dark, "Should return dark text for very light background"); - } - - @include it("should handle edge case with very dark background") { - // Test case: Very dark background that should return light text - $test-background: #212529; // Very dark gray - $result: color-contrast($test-background); - - @include assert-equal($result, $color-contrast-light, "Should return light text for very dark background"); - } - - @include it("should work with custom minimum contrast ratio") { - // Test case: Using a custom minimum contrast ratio - $test-background: #666; - $result: color-contrast($test-background, $color-contrast-dark, $color-contrast-light, 3); - - @include assert-equal($result, $white, "Should return white when using custom minimum contrast ratio of 3.0"); - } - - @include it("should test contrast ratio calculation accuracy") { - // Test case: Verify that contrast-ratio function works correctly - $background: #767676; - $foreground: $white; - $ratio: contrast-ratio($background, $foreground); - // Bootstrap's implementation calculates this as ~4.5415, not exactly 4.5, due to its luminance math. - // We use 4.54 as the threshold for this test to match the actual implementation. - @include assert-true($ratio >= 4.54 and $ratio <= 4.55, "Contrast ratio should be approximately 4.54:1 (Bootstrap's math)"); - } - - @include it("should test luminance calculation") { - // Test case: Verify luminance function works correctly - $white-luminance: luminance($white); - $black-luminance: luminance($black); - - @include assert-equal($white-luminance, 1, "White should have luminance of 1"); - @include assert-equal($black-luminance, 0, "Black should have luminance of 0"); - } - - @include it("should handle rgba colors correctly") { - // Test case: Test with rgba colors - $test-background: rgba(118, 118, 118, 1); // Same as #767676 - $result: color-contrast($test-background); - - @include assert-equal($result, $white, "Should handle rgba colors correctly"); - } - - @include it("should test the WCAG 2.1 boundary condition with color below threshold") { - // Test case: Background color that produces contrast ratio below 4.5:1 - // #787878 produces 4.4155:1 contrast ratio with white - $test-background: #787878; // Produces 4.4155:1 contrast ratio - $contrast-ratio: contrast-ratio($test-background, $white); - - // Verify the contrast ratio is below 4.5:1 - @include assert-true($contrast-ratio < 4.5, "Contrast ratio should be below 4.5:1 threshold"); - - // The color-contrast function should return the color with highest available contrast - $result: color-contrast($test-background); - @include assert-equal($result, $black, "color-contrast should return black (highest available contrast) for below-threshold ratio"); - } - - @include it("should test the WCAG 2.1 boundary condition with color at threshold") { - // Test case: Background color that produces contrast ratio close to 4.5:1 - // #777777 produces 4.4776:1 contrast ratio with white - $test-background: #777; // Produces 4.4776:1 contrast ratio - $contrast-ratio: contrast-ratio($test-background, $white); - - // Verify the contrast ratio is below 4.5:1 threshold - @include assert-true($contrast-ratio < 4.5, "Contrast ratio is below threshold, function should handle gracefully"); - } - - @include it("should demonstrate the difference between > and >= operators") { - // Test case: Demonstrates the difference between > and >= operators - // Uses #767676 with a custom minimum contrast ratio that matches its actual ratio (4.5415) - // With > 4.5415: should return black (fallback to highest available) - // With >= 4.5415: should return white (meets threshold) - - $test-background: #767676; // Produces 4.5415:1 contrast ratio - $actual-ratio: contrast-ratio($test-background, $white); - - // Test with a custom minimum that matches the actual ratio - $result: color-contrast($test-background, $color-contrast-dark, $color-contrast-light, $actual-ratio); - - // Should return white when using >= implementation - @include assert-equal($result, $white, "color-contrast should return white when using exact ratio as threshold (>= implementation)"); - } - - @include it("should test additional working colors above threshold") { - // Test case: Background color that produces contrast ratio well above 4.5:1 - // #757575 produces 4.6047:1 contrast ratio with white text - $test-background: #757575; // Produces 4.6047:1 contrast ratio - $result: color-contrast($test-background); - - @include assert-equal($result, $white, "Should return white for background with 4.6047:1 contrast ratio (well above threshold)"); - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-modes.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-modes.test.scss deleted file mode 100644 index 9ecc628d..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_color-modes.test.scss +++ /dev/null @@ -1,69 +0,0 @@ -// stylelint-disable selector-attribute-quotes - -@import "../../functions"; -@import "../../variables"; -@import "../../variables-dark"; -@import "../../maps"; -@import "../../mixins"; - -@include describe("global $color-mode-type: data") { - @include it("generates data attribute selectors for dark mode") { - @include assert() { - @include output() { - @include color-mode(dark) { - .element { - color: var(--bs-primary-text-emphasis); - background-color: var(--bs-primary-bg-subtle); - } - } - @include color-mode(dark, true) { - --custom-color: #{mix($indigo, $blue, 50%)}; - } - } - @include expect() { - [data-bs-theme=dark] .element { - color: var(--bs-primary-text-emphasis); - background-color: var(--bs-primary-bg-subtle); - } - [data-bs-theme=dark] { - --custom-color: #3a3ff8; - } - } - } - } -} - -@include describe("global $color-mode-type: media-query") { - @include it("generates media queries for dark mode") { - $color-mode-type: media-query !global; - - @include assert() { - @include output() { - @include color-mode(dark) { - .element { - color: var(--bs-primary-text-emphasis); - background-color: var(--bs-primary-bg-subtle); - } - } - @include color-mode(dark, true) { - --custom-color: #{mix($indigo, $blue, 50%)}; - } - } - @include expect() { - @media (prefers-color-scheme: dark) { - .element { - color: var(--bs-primary-text-emphasis); - background-color: var(--bs-primary-bg-subtle); - } - } - @media (prefers-color-scheme: dark) { - :root { - --custom-color: #3a3ff8; - } - } - } - } - - $color-mode-type: data !global; - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_media-query-color-mode-full.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_media-query-color-mode-full.test.scss deleted file mode 100644 index 00ed82d6..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_media-query-color-mode-full.test.scss +++ /dev/null @@ -1,8 +0,0 @@ -$color-mode-type: media-query; - -@import "../../bootstrap"; - -@include describe("global $color-mode-type: media-query") { - @include it("compiles entirely Bootstrap CSS with media-query color mode") { // stylelint-disable-line block-no-empty - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_utilities.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_utilities.test.scss deleted file mode 100644 index 8140ac47..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/mixins/_utilities.test.scss +++ /dev/null @@ -1,393 +0,0 @@ -$prefix: bs-; -$enable-important-utilities: false; - -// Important: Do not import rfs to check that the mixin just calls the appropriate functions from it -@import "../../mixins/utilities"; - -@mixin test-generate-utility($params...) { - @include assert() { - @include output() { - @include generate-utility($params...); - } - - @include expect() { - @content; - } - } -} - -@include describe(generate-utility) { - @include it("generates a utility class for each value") { - @include test-generate-utility( - ( - property: "padding", - values: (small: .5rem, large: 2rem) - ) - ) { - .padding-small { - padding: .5rem; - } - - .padding-large { - padding: 2rem; - } - } - } - - @include describe("global $enable-important-utilities: true") { - @include it("sets !important") { - $enable-important-utilities: true !global; - - @include test-generate-utility( - ( - property: "padding", - values: (small: .5rem, large: 2rem) - ) - ) { - .padding-small { - padding: .5rem !important; - } - - .padding-large { - padding: 2rem !important; - } - } - - $enable-important-utilities: false !global; - } - } - - @include describe("$utility") { - @include describe("values") { - @include it("supports null keys") { - @include test-generate-utility( - ( - property: "padding", - values: (null: 1rem, small: .5rem, large: 2rem) - ), - ) { - .padding { - padding: 1rem; - } - - .padding-small { - padding: .5rem; - } - - .padding-large { - padding: 2rem; - } - } - } - - @include it("ignores null values") { - @include test-generate-utility( - ( - property: "padding", - values: (small: null, large: 2rem) - ) - ) { - .padding-large { - padding: 2rem; - } - } - } - - @include it("supports lists") { - @include test-generate-utility( - ( - property: "padding", - values: 1rem 2rem - ) - ) { - .padding-1rem { - padding: 1rem; - } - - .padding-2rem { - padding: 2rem; - } - } - } - - @include it("supports single values") { - @include test-generate-utility( - ( - property: padding, - values: 1rem - ) - ) { - .padding-1rem { - padding: 1rem; - } - } - } - } - - @include describe("class & property") { - @include it("adds each property to the declaration") { - @include test-generate-utility( - ( - class: padding-x, - property: padding-inline-start padding-inline-end, - values: 1rem - ) - ) { - .padding-x-1rem { - padding-inline-start: 1rem; - padding-inline-end: 1rem; - } - } - } - - @include it("uses the first property as class name") { - @include test-generate-utility( - ( - property: padding-inline-start padding-inline-end, - values: 1rem - ) - ) { - .padding-inline-start-1rem { - padding-inline-start: 1rem; - padding-inline-end: 1rem; - } - } - } - - @include it("supports a null class to create classes straight from the values") { - @include test-generate-utility( - ( - property: visibility, - class: null, - values: ( - visible: visible, - invisible: hidden, - ) - ) - ) { - .visible { - visibility: visible; - } - - .invisible { - visibility: hidden; - } - } - } - } - - @include describe("state") { - @include it("Generates selectors for each states") { - @include test-generate-utility( - ( - property: padding, - values: 1rem, - state: hover focus, - ) - ) { - .padding-1rem { - padding: 1rem; - } - - .padding-1rem-hover:hover { - padding: 1rem; - } - - .padding-1rem-focus:focus { - padding: 1rem; - } - } - } - } - - @include describe("css-var"){ - @include it("sets a CSS variable instead of the property") { - @include test-generate-utility( - ( - property: padding, - css-variable-name: padding, - css-var: true, - values: 1rem 2rem - ) - ) { - .padding-1rem { - --bs-padding: 1rem; - } - - .padding-2rem { - --bs-padding: 2rem; - } - } - } - - @include it("defaults to class") { - @include test-generate-utility( - ( - property: padding, - class: padding, - css-var: true, - values: 1rem 2rem - ) - ) { - .padding-1rem { - --bs-padding: 1rem; - } - - .padding-2rem { - --bs-padding: 2rem; - } - } - } - } - - @include describe("local-vars") { - @include it("generates the listed variables") { - @include test-generate-utility( - ( - property: color, - class: desaturated-color, - local-vars: ( - color-opacity: 1, - color-saturation: .25 - ), - values: ( - blue: hsla(192deg, var(--bs-color-saturation), 0, var(--bs-color-opacity)) - ) - ) - ) { - .desaturated-color-blue { - --bs-color-opacity: 1; - // Sass compilation will put a leading zero so we want to keep that one - // stylelint-disable-next-line @stylistic/number-leading-zero - --bs-color-saturation: 0.25; - color: hsla(192deg, var(--bs-color-saturation), 0, var(--bs-color-opacity)); - } - } - } - } - - @include describe("css-var & state") { - @include it("Generates a rule with for each state with a CSS variable") { - @include test-generate-utility( - ( - property: padding, - css-var: true, - css-variable-name: padding, - values: 1rem, - state: hover focus, - ) - ) { - .padding-1rem { - --bs-padding: 1rem; - } - - .padding-1rem-hover:hover { - --bs-padding: 1rem; - } - - .padding-1rem-focus:focus { - --bs-padding: 1rem; - } - } - } - } - - @include describe("rtl") { - @include it("sets up RTLCSS for removal when false") { - @include test-generate-utility( - ( - property: padding, - values: 1rem, - rtl: false - ) - ) { - /* rtl:begin:remove */ - - .padding-1rem { - padding: 1rem; - } - - /* rtl:end:remove */ - - } - } - } - - @include describe("rfs") { - @include it("sets the fluid value when not inside media query") { - @include test-generate-utility( - ( - property: padding, - values: 1rem, - rfs: true - ) - ) { - .padding-1rem { - padding: rfs-fluid-value(1rem); - } - } - } - - @include it("sets the value when inside the media query") { - @include test-generate-utility( - ( - property: padding, - values: 1rem, - rfs: true - ), - $is-rfs-media-query: true - ) { - .padding-1rem { - padding: rfs-value(1rem); - } - } - } - } - } - - @include describe("$infix") { - @include it("inserts the given infix") { - @include test-generate-utility( - ( - property: "padding", - values: (null: 1rem, small: .5rem, large: 2rem) - ), - $infix: -sm - ) { - .padding-sm { - padding: 1rem; - } - - .padding-sm-small { - padding: .5rem; - } - - .padding-sm-large { - padding: 2rem; - } - } - } - - @include it("strips leading - if class is null") { - @include test-generate-utility( - ( - property: visibility, - class: null, - values: ( - visible: visible, - invisible: hidden, - ) - ), - -sm - ) { - .sm-visible { - visibility: visible; - } - - .sm-invisible { - visibility: hidden; - } - } - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/register.js b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/register.js deleted file mode 100644 index d93e414c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/register.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict' - -const path = require('node:path') - -const runnerPath = path.join(__dirname, 'runner').replace(/\\/g, '/') - -require.extensions['.scss'] = (module, filename) => { - const normalizedFilename = filename.replace(/\\/g, '/') - - return module._compile(` - const runner = require('${runnerPath}') - runner('${normalizedFilename}', { describe, it }) - `, filename) -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/runner.js b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/runner.js deleted file mode 100644 index bef870ac..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/sass-true/runner.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict' - -const fs = require('node:fs') -const path = require('node:path') -const { runSass } = require('sass-true') - -module.exports = (filename, { describe, it }) => { - const data = fs.readFileSync(filename, 'utf8') - const TRUE_SETUP = '$true-terminal-output: false; @import "true";' - const sassString = TRUE_SETUP + data - - runSass( - { describe, it, sourceType: 'string' }, - sassString, - { loadPaths: [path.dirname(filename)] } - ) -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/utilities/_api.test.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/utilities/_api.test.scss deleted file mode 100644 index 304d8d1c..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/tests/utilities/_api.test.scss +++ /dev/null @@ -1,75 +0,0 @@ -@import "../../functions"; -@import "../../variables"; -@import "../../variables-dark"; -@import "../../maps"; -@import "../../mixins"; - -$utilities: (); - -@include describe("utilities/api") { - @include it("generates utilities for each breakpoints") { - $utilities: ( - margin: ( - property: margin, - values: auto - ), - padding: ( - property: padding, - responsive: true, - values: 1rem - ), - font-size: ( - property: font-size, - values: (large: 1.25rem), - print: true - ) - ) !global; - - $grid-breakpoints: ( - xs: 0, - sm: 333px, - md: 666px - ) !global; - - @include assert() { - @include output() { - @import "../../utilities/api"; - } - - @include expect() { - // margin is not set to responsive - .margin-auto { - margin: auto !important; - } - - // padding is, though - .padding-1rem { - padding: 1rem !important; - } - - .font-size-large { - font-size: 1.25rem !important; - } - - @media (min-width: 333px) { - .padding-sm-1rem { - padding: 1rem !important; - } - } - - @media (min-width: 666px) { - .padding-md-1rem { - padding: 1rem !important; - } - } - - @media print { - .font-size-print-large { - font-size: 1.25rem !important; - } - } - } - - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/utilities/_api.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/utilities/_api.scss deleted file mode 100644 index 62e1d398..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/utilities/_api.scss +++ /dev/null @@ -1,47 +0,0 @@ -// Loop over each breakpoint -@each $breakpoint in map-keys($grid-breakpoints) { - - // Generate media query if needed - @include media-breakpoint-up($breakpoint) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - - // Loop over each utility property - @each $key, $utility in $utilities { - // The utility can be disabled with `false`, thus check if the utility is a map first - // Only proceed if responsive media queries are enabled or if it's the base media query - @if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") { - @include generate-utility($utility, $infix); - } - } - } -} - -// RFS rescaling -@media (min-width: $rfs-mq-value) { - @each $breakpoint in map-keys($grid-breakpoints) { - $infix: breakpoint-infix($breakpoint, $grid-breakpoints); - - @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) { - // Loop over each utility property - @each $key, $utility in $utilities { - // The utility can be disabled with `false`, thus check if the utility is a map first - // Only proceed if responsive media queries are enabled or if it's the base media query - @if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") { - @include generate-utility($utility, $infix, true); - } - } - } - } -} - - -// Print utilities -@media print { - @each $key, $utility in $utilities { - // The utility can be disabled with `false`, thus check if the utility is a map first - // Then check if the utility needs print styles - @if type-of($utility) == "map" and map-get($utility, print) == true { - @include generate-utility($utility, "-print"); - } - } -} diff --git a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/vendor/_rfs.scss b/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/vendor/_rfs.scss deleted file mode 100644 index aa1f82b9..00000000 --- a/extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/vendor/_rfs.scss +++ /dev/null @@ -1,348 +0,0 @@ -// stylelint-disable scss/dimension-no-non-numeric-values - -// SCSS RFS mixin -// -// Automated responsive values for font sizes, paddings, margins and much more -// -// Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE) - -// Configuration - -// Base value -$rfs-base-value: 1.25rem !default; -$rfs-unit: rem !default; - -@if $rfs-unit != rem and $rfs-unit != px { - @error "`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`."; -} - -// Breakpoint at where values start decreasing if screen width is smaller -$rfs-breakpoint: 1200px !default; -$rfs-breakpoint-unit: px !default; - -@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem { - @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`."; -} - -// Resize values based on screen height and width -$rfs-two-dimensional: false !default; - -// Factor of decrease -$rfs-factor: 10 !default; - -@if type-of($rfs-factor) != number or $rfs-factor <= 1 { - @error "`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1."; -} - -// Mode. Possibilities: "min-media-query", "max-media-query" -$rfs-mode: min-media-query !default; - -// Generate enable or disable classes. Possibilities: false, "enable" or "disable" -$rfs-class: false !default; - -// 1 rem = $rfs-rem-value px -$rfs-rem-value: 16 !default; - -// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14 -$rfs-safari-iframe-resize-bug-fix: false !default; - -// Disable RFS by setting $enable-rfs to false -$enable-rfs: true !default; - -// Cache $rfs-base-value unit -$rfs-base-value-unit: unit($rfs-base-value); - -@function divide($dividend, $divisor, $precision: 10) { - $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1); - $dividend: abs($dividend); - $divisor: abs($divisor); - @if $dividend == 0 { - @return 0; - } - @if $divisor == 0 { - @error "Cannot divide by 0"; - } - $remainder: $dividend; - $result: 0; - $factor: 10; - @while ($remainder > 0 and $precision >= 0) { - $quotient: 0; - @while ($remainder >= $divisor) { - $remainder: $remainder - $divisor; - $quotient: $quotient + 1; - } - $result: $result * 10 + $quotient; - $factor: $factor * .1; - $remainder: $remainder * 10; - $precision: $precision - 1; - @if ($precision < 0 and $remainder >= $divisor * 5) { - $result: $result + 1; - } - } - $result: $result * $factor * $sign; - $dividend-unit: unit($dividend); - $divisor-unit: unit($divisor); - $unit-map: ( - "px": 1px, - "rem": 1rem, - "em": 1em, - "%": 1% - ); - @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) { - $result: $result * map-get($unit-map, $dividend-unit); - } - @return $result; -} - -// Remove px-unit from $rfs-base-value for calculations -@if $rfs-base-value-unit == px { - $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1); -} -@else if $rfs-base-value-unit == rem { - $rfs-base-value: divide($rfs-base-value, divide($rfs-base-value * 0 + 1, $rfs-rem-value)); -} - -// Cache $rfs-breakpoint unit to prevent multiple calls -$rfs-breakpoint-unit-cache: unit($rfs-breakpoint); - -// Remove unit from $rfs-breakpoint for calculations -@if $rfs-breakpoint-unit-cache == px { - $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1); -} -@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == "em" { - $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value)); -} - -// Calculate the media query value -$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit}); -$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width); -$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height); - -// Internal mixin used to determine which media query needs to be used -@mixin _rfs-media-query { - @if $rfs-two-dimensional { - @if $rfs-mode == max-media-query { - @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) { - @content; - } - } - @else { - @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) { - @content; - } - } - } - @else { - @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) { - @content; - } - } -} - -// Internal mixin that adds disable classes to the selector if needed. -@mixin _rfs-rule { - @if $rfs-class == disable and $rfs-mode == max-media-query { - // Adding an extra class increases specificity, which prevents the media query to override the property - &, - .disable-rfs &, - &.disable-rfs { - @content; - } - } - @else if $rfs-class == enable and $rfs-mode == min-media-query { - .enable-rfs &, - &.enable-rfs { - @content; - } - } @else { - @content; - } -} - -// Internal mixin that adds enable classes to the selector if needed. -@mixin _rfs-media-query-rule { - - @if $rfs-class == enable { - @if $rfs-mode == min-media-query { - @content; - } - - @include _rfs-media-query () { - .enable-rfs &, - &.enable-rfs { - @content; - } - } - } - @else { - @if $rfs-class == disable and $rfs-mode == min-media-query { - .disable-rfs &, - &.disable-rfs { - @content; - } - } - @include _rfs-media-query () { - @content; - } - } -} - -// Helper function to get the formatted non-responsive value -@function rfs-value($values) { - // Convert to list - $values: if(type-of($values) != list, ($values,), $values); - - $val: ""; - - // Loop over each value and calculate value - @each $value in $values { - @if $value == 0 { - $val: $val + " 0"; - } - @else { - // Cache $value unit - $unit: if(type-of($value) == "number", unit($value), false); - - @if $unit == px { - // Convert to rem if needed - $val: $val + " " + if($rfs-unit == rem, #{divide($value, $value * 0 + $rfs-rem-value)}rem, $value); - } - @else if $unit == rem { - // Convert to px if needed - $val: $val + " " + if($rfs-unit == px, #{divide($value, $value * 0 + 1) * $rfs-rem-value}px, $value); - } @else { - // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value - $val: $val + " " + $value; - } - } - } - - // Remove first space - @return unquote(str-slice($val, 2)); -} - -// Helper function to get the responsive value calculated by RFS -@function rfs-fluid-value($values) { - // Convert to list - $values: if(type-of($values) != list, ($values,), $values); - - $val: ""; - - // Loop over each value and calculate value - @each $value in $values { - @if $value == 0 { - $val: $val + " 0"; - } @else { - // Cache $value unit - $unit: if(type-of($value) == "number", unit($value), false); - - // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value - @if not $unit or $unit != px and $unit != rem { - $val: $val + " " + $value; - } @else { - // Remove unit from $value for calculations - $value: divide($value, $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value))); - - // Only add the media query if the value is greater than the minimum value - @if abs($value) <= $rfs-base-value or not $enable-rfs { - $val: $val + " " + if($rfs-unit == rem, #{divide($value, $rfs-rem-value)}rem, #{$value}px); - } - @else { - // Calculate the minimum value - $value-min: $rfs-base-value + divide(abs($value) - $rfs-base-value, $rfs-factor); - - // Calculate difference between $value and the minimum value - $value-diff: abs($value) - $value-min; - - // Base value formatting - $min-width: if($rfs-unit == rem, #{divide($value-min, $rfs-rem-value)}rem, #{$value-min}px); - - // Use negative value if needed - $min-width: if($value < 0, -$min-width, $min-width); - - // Use `vmin` if two-dimensional is enabled - $variable-unit: if($rfs-two-dimensional, vmin, vw); - - // Calculate the variable width between 0 and $rfs-breakpoint - $variable-width: #{divide($value-diff * 100, $rfs-breakpoint)}#{$variable-unit}; - - // Return the calculated value - $val: $val + " calc(" + $min-width + if($value < 0, " - ", " + ") + $variable-width + ")"; - } - } - } - } - - // Remove first space - @return unquote(str-slice($val, 2)); -} - -// RFS mixin -@mixin rfs($values, $property: font-size) { - @if $values != null { - $val: rfs-value($values); - $fluid-val: rfs-fluid-value($values); - - // Do not print the media query if responsive & non-responsive values are the same - @if $val == $fluid-val { - #{$property}: $val; - } - @else { - @include _rfs-rule () { - #{$property}: if($rfs-mode == max-media-query, $val, $fluid-val); - - // Include safari iframe resize fix if needed - min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null); - } - - @include _rfs-media-query-rule () { - #{$property}: if($rfs-mode == max-media-query, $fluid-val, $val); - } - } - } -} - -// Shorthand helper mixins -@mixin font-size($value) { - @include rfs($value); -} - -@mixin padding($value) { - @include rfs($value, padding); -} - -@mixin padding-top($value) { - @include rfs($value, padding-top); -} - -@mixin padding-right($value) { - @include rfs($value, padding-right); -} - -@mixin padding-bottom($value) { - @include rfs($value, padding-bottom); -} - -@mixin padding-left($value) { - @include rfs($value, padding-left); -} - -@mixin margin($value) { - @include rfs($value, margin); -} - -@mixin margin-top($value) { - @include rfs($value, margin-top); -} - -@mixin margin-right($value) { - @include rfs($value, margin-right); -} - -@mixin margin-bottom($value) { - @include rfs($value, margin-bottom); -} - -@mixin margin-left($value) { - @include rfs($value, margin-left); -} diff --git a/helpers/pagetop-build/CHANGELOG.md b/helpers/pagetop-build/CHANGELOG.md deleted file mode 100644 index 5bd9caf3..00000000 --- a/helpers/pagetop-build/CHANGELOG.md +++ /dev/null @@ -1,49 +0,0 @@ -# CHANGELOG - -Este archivo documenta los cambios más relevantes realizados en cada versión. El formato está basado -en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/), y las versiones se numeran siguiendo -las reglas del [Versionado Semántico](https://semver.org/lang/es/). - -Resume la evolución del proyecto para usuarios y colaboradores, destacando nuevas funcionalidades, -correcciones, mejoras durante el desarrollo o cambios en la documentación. Cambios menores o -internos pueden omitirse si no afectan al uso del proyecto. - -## 0.3.1 (2025-09-20) - -### Dependencias - -- Actualiza dependencias para 0.4.0 - -### Documentado - -- Normaliza referencias al nombre PageTop - -## 0.3.0 (2025-08-16) - -### Cambiado - -- Mejora función `from_dir` por compatibilidad -- Mejora la integración de archivos estáticos - -### Documentado - -- Cambia el formato para la documentación - -## 0.2.0 (2025-08-09) - -### Añadido - -- Añade librería propia para gestionar recursos estáticos - -### Otros cambios - -- 🩹 Corrige enlace del botón de licencia en la documentación -- 🚩 Afina Cargo.toml para buscar la mejor categoría - -## 0.1.1 (2025-08-05) - -- Depura la edición de CHANGELOGs y publicación de nuevas versiones - -## 0.1.0 (2025-08-05) - -- Versión inicial diff --git a/helpers/pagetop-build/Cargo.toml b/helpers/pagetop-build/Cargo.toml index cea1de3e..94324cec 100644 --- a/helpers/pagetop-build/Cargo.toml +++ b/helpers/pagetop-build/Cargo.toml @@ -1,20 +1,19 @@ [package] name = "pagetop-build" -version = "0.3.1" +version = "0.0.12" edition = "2021" -description = """ - Prepara un conjunto de archivos estáticos o archivos SCSS compilados para ser incluidos en el - binario de un proyecto PageTop. +description = """\ + Simplifies the process of embedding resources in PageTop app binaries.\ """ -categories = ["development-tools::build-utils"] +categories = ["development-tools::build-utils", "web-programming"] keywords = ["pagetop", "build", "assets", "resources", "static"] -repository.workspace = true -homepage.workspace = true -license.workspace = true -authors.workspace = true +repository = "https://github.com/manuelcillero/pagetop" +homepage = "https://pagetop.cillero.es" +license = "MIT OR Apache-2.0" +authors = ["Manuel Cillero <manuel@cillero.es>"] [dependencies] -grass = "0.13" -pagetop-statics.workspace = true +grass = "0.13.4" +static-files = "0.2.4" diff --git a/helpers/pagetop-build/LICENSE-APACHE b/helpers/pagetop-build/LICENSE-APACHE deleted file mode 100644 index 263ddac1..00000000 --- a/helpers/pagetop-build/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Manuel Cillero - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/helpers/pagetop-build/LICENSE-MIT b/helpers/pagetop-build/LICENSE-MIT deleted file mode 100644 index cd8af3d6..00000000 --- a/helpers/pagetop-build/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Manuel Cillero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/helpers/pagetop-build/README.md b/helpers/pagetop-build/README.md index 875acbd9..0e80b363 100644 --- a/helpers/pagetop-build/README.md +++ b/helpers/pagetop-build/README.md @@ -2,131 +2,36 @@ <h1>PageTop Build</h1> -<p>Prepara un conjunto de archivos estáticos o archivos SCSS compilados para ser incluidos en el binario de un proyecto <strong>PageTop</strong>.</p> +<p>Simplifies the process of embedding resources in PageTop app binaries.</p> -[![Doc API](https://img.shields.io/docsrs/pagetop-build?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-build) +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?style=for-the-badge)](#-license) +[![API Docs](https://img.shields.io/docsrs/pagetop-build?label=API%20Docs&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-build) [![Crates.io](https://img.shields.io/crates/v/pagetop-build.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-build) -[![Descargas](https://img.shields.io/crates/d/pagetop-build.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-build) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-build#licencia) +[![Downloads](https://img.shields.io/crates/d/pagetop-build.svg?style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-build) </div> -## Sobre PageTop +# 📦 About 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. +[PageTop](https://docs.rs/pagetop) is an opinionated web framework to build modular *Server-Side +Rendering* web solutions. -# ⚡️ Guía rápida +# 🚧 Warning -Añadir en el archivo `Cargo.toml` del proyecto: - -```toml -[build-dependencies] -pagetop-build = { ... } -``` - -Y crear un archivo `build.rs` a la altura de `Cargo.toml` para indicar cómo se van a incluir los -archivos estáticos o cómo se van a compilar los archivos SCSS para el proyecto. Casos de uso: - -## Incluir archivos estáticos desde un directorio - -Hay que preparar una carpeta en el proyecto con todos los archivos que se quieren incluir, por -ejemplo `static`, y añadir el siguiente código en `build.rs` para crear el conjunto de recursos: - -```rust,no_run -use pagetop_build::StaticFilesBundle; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_dir("./static", None) - .with_name("guides") - .build() -} -``` - -Si es necesario, se puede añadir un filtro para seleccionar archivos específicos de la carpeta, por -ejemplo: - -```rust,no_run -use pagetop_build::StaticFilesBundle; -use std::path::Path; - -fn main() -> std::io::Result<()> { - fn only_pdf_files(path: &Path) -> bool { - // Selecciona únicamente los archivos con extensión `.pdf`. - path.extension().map_or(false, |ext| ext == "pdf") - } - - StaticFilesBundle::from_dir("./static", Some(only_pdf_files)) - .with_name("guides") - .build() -} -``` - -## Compilar archivos SCSS a CSS - -Se puede compilar un archivo SCSS, que podría importar otros a su vez, para preparar un recurso con -el archivo CSS minificado obtenido. Por ejemplo: - -```rust,no_run -use pagetop_build::StaticFilesBundle; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_scss("./styles/main.scss", "styles.min.css") - .with_name("main_styles") - .build() -} -``` - -Este código compila el archivo `main.scss` de la carpeta `static` del proyecto, y prepara un recurso -llamado `main_styles` que contiene el archivo `styles.min.css` obtenido. +**PageTop** framework is currently in active development. The API is unstable and subject to +frequent changes. Production use is not recommended until version **0.1.0**. -# 📦 Archivos generados +# 📜 License -Cada conjunto de recursos [`StaticFilesBundle`] genera un archivo en el directorio estándar -[OUT_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts) -donde se incluye el código necesario para compilar el proyecto. Por ejemplo, para -`with_name("guides")` se genera un archivo llamado `guides.rs`. +All code in this crate is dual-licensed under either: -No hay ningún problema en generar más de un conjunto de recursos para cada proyecto siempre que se -usen nombres diferentes. + * MIT License + ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) -Normalmente no habrá que acceder a estos módulos; sólo declarar el nombre del conjunto de recursos -en [`static_files_service!`](https://docs.rs/pagetop/latest/pagetop/macro.static_files_service.html) -para configurar un servicio web que sirva los archivos desde la ruta indicada. Por ejemplo: + * Apache License, Version 2.0, + ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) -```rust,ignore -use pagetop::prelude::*; - -pub struct MyExtension; - -impl Extension for MyExtension { - // Servicio web que publica los recursos de `guides` en `/ruta/a/guides`. - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - static_files_service!(scfg, guides => "/ruta/a/guides"); - } -} -``` - - -# 🚧 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. +at your option. This means you can select the license you prefer! This dual-licensing approach is +the de-facto standard in the Rust ecosystem. diff --git a/helpers/pagetop-build/src/lib.rs b/helpers/pagetop-build/src/lib.rs index 9088ec99..1a6d67a1 100644 --- a/helpers/pagetop-build/src/lib.rs +++ b/helpers/pagetop-build/src/lib.rs @@ -1,212 +1,149 @@ -/*! -<div align="center"> - -<h1>PageTop Build</h1> - -<p>Prepara un conjunto de archivos estáticos o archivos SCSS compilados para ser incluidos en el binario de un proyecto <strong>PageTop</strong>.</p> - -[![Doc API](https://img.shields.io/docsrs/pagetop-build?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-build) -[![Crates.io](https://img.shields.io/crates/v/pagetop-build.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-build) -[![Descargas](https://img.shields.io/crates/d/pagetop-build.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-build) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-build#licencia) - -</div> - -## 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 - -Añadir en el archivo `Cargo.toml` del proyecto: - -```toml -[build-dependencies] -pagetop-build = { ... } -``` - -Y crear un archivo `build.rs` a la altura de `Cargo.toml` para indicar cómo se van a incluir los -archivos estáticos o cómo se van a compilar los archivos SCSS para el proyecto. Casos de uso: - -## Incluir archivos estáticos desde un directorio - -Hay que preparar una carpeta en el proyecto con todos los archivos que se quieren incluir, por -ejemplo `static`, y añadir el siguiente código en `build.rs` para crear el conjunto de recursos: - -```rust,no_run -use pagetop_build::StaticFilesBundle; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_dir("./static", None) - .with_name("guides") - .build() -} -``` - -Si es necesario, se puede añadir un filtro para seleccionar archivos específicos de la carpeta, por -ejemplo: - -```rust,no_run -use pagetop_build::StaticFilesBundle; -use std::path::Path; - -fn main() -> std::io::Result<()> { - fn only_pdf_files(path: &Path) -> bool { - // Selecciona únicamente los archivos con extensión `.pdf`. - path.extension().map_or(false, |ext| ext == "pdf") - } - - StaticFilesBundle::from_dir("./static", Some(only_pdf_files)) - .with_name("guides") - .build() -} -``` - -## Compilar archivos SCSS a CSS - -Se puede compilar un archivo SCSS, que podría importar otros a su vez, para preparar un recurso con -el archivo CSS minificado obtenido. Por ejemplo: - -```rust,no_run -use pagetop_build::StaticFilesBundle; - -fn main() -> std::io::Result<()> { - StaticFilesBundle::from_scss("./styles/main.scss", "styles.min.css") - .with_name("main_styles") - .build() -} -``` - -Este código compila el archivo `main.scss` de la carpeta `static` del proyecto, y prepara un recurso -llamado `main_styles` que contiene el archivo `styles.min.css` obtenido. - - -# 📦 Archivos generados - -Cada conjunto de recursos [`StaticFilesBundle`] genera un archivo en el directorio estándar -[OUT_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts) -donde se incluye el código necesario para compilar el proyecto. Por ejemplo, para -`with_name("guides")` se genera un archivo llamado `guides.rs`. - -No hay ningún problema en generar más de un conjunto de recursos para cada proyecto siempre que se -usen nombres diferentes. - -Normalmente no habrá que acceder a estos módulos; sólo declarar el nombre del conjunto de recursos -en [`static_files_service!`](https://docs.rs/pagetop/latest/pagetop/macro.static_files_service.html) -para configurar un servicio web que sirva los archivos desde la ruta indicada. Por ejemplo: - -```rust,ignore -use pagetop::prelude::*; - -pub struct MyExtension; - -impl Extension for MyExtension { - // Servicio web que publica los recursos de `guides` en `/ruta/a/guides`. - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - static_files_service!(scfg, guides => "/ruta/a/guides"); - } -} -``` -*/ - -#![doc( - html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" -)] +//! Easily embed static or compiled SCSS files into your binary at compile time. +//! +//! ## Adding to your project +//! +//! Add the following to your `Cargo.toml`: +//! +//! ```toml +//! [build-dependencies] +//! pagetop-build = { ... } +//! ``` +//! +//! Next, create a `build.rs` file to configure how your static resources or SCSS files will be +//! bundled in your PageTop application, package, or theme. +//! +//! ## Usage examples +//! +//! ### 1. Embedding static files from a directory +//! +//! Include all files from a directory: +//! +//! ```rust#ignore +//! use pagetop_build::StaticFilesBundle; +//! +//! fn main() -> std::io::Result<()> { +//! StaticFilesBundle::from_dir("./static", None) +//! .with_name("guides") +//! .build() +//! } +//! ``` +//! +//! Apply a filter to include only specific files: +//! +//! ```rust#ignore +//! use pagetop_build::StaticFilesBundle; +//! use std::path::Path; +//! +//! fn main() -> std::io::Result<()> { +//! fn only_css_files(path: &Path) -> bool { +//! // Include only files with `.css` extension. +//! path.extension().map_or(false, |ext| ext == "css") +//! } +//! +//! StaticFilesBundle::from_dir("./static", Some(only_css_files)) +//! .with_name("guides") +//! .build() +//! } +//! ``` +//! +//! ### 2. Compiling SCSS files to CSS +//! +//! Compile a SCSS file into CSS and embed it: +//! +//! ```rust#ignore +//! use pagetop_build::StaticFilesBundle; +//! +//! fn main() -> std::io::Result<()> { +//! StaticFilesBundle::from_scss("./styles/main.scss", "main.css") +//! .with_name("main_styles") +//! .build() +//! } +//! ``` +//! +//! This compiles the `main.scss` file, including all imported SCSS files, into `main.css`. All +//! imports are resolved automatically, and the result is accessible within the binary file. +//! +//! ## Generated module +//! +//! [`StaticFilesBundle`] generates a file in the standard directory +//! [OUT_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) where all +//! intermediate and output artifacts are placed during compilation. For example, if you use +//! `with_name("guides")`, it generates a file named `guides.rs`: +//! +//! You don't need to access this file, just include it in your project using the builder name as an +//! identifier: +//! +//! ```rust#ignore +//! use pagetop::prelude::*; +//! +//! include_files!(guides); +//! ``` +//! +//! Or, access the entire bundle as a global static `HashMap`: +//! +//! ```rust#ignore +//! use pagetop::prelude::*; +//! +//! include_files!(guides => BUNDLE_GUIDES); +//! ``` +//! +//! You can build more than one resources file to compile with your project. use grass::{from_path, Options, OutputStyle}; -use pagetop_statics::{resource_dir, ResourceDir}; +use static_files::{resource_dir, ResourceDir}; use std::fs::{create_dir_all, remove_dir_all, File}; use std::io::Write; use std::path::Path; -/// Prepara un conjunto de recursos para ser incluidos en el binario del proyecto. +/// Generates the resources to embed at compile time using +/// [static_files](https://docs.rs/static-files/latest/static_files/). pub struct StaticFilesBundle { resource_dir: ResourceDir, } impl StaticFilesBundle { - /// Prepara el conjunto de recursos con los archivos de un directorio. Opcionalmente se puede - /// aplicar un filtro para seleccionar un subconjunto de los archivos. + /// Creates a bundle from a directory of static files, with an optional filter. /// - /// # Argumentos + /// # Arguments /// - /// * `dir` - Directorio que contiene los archivos. - /// * `filter` - Una función opcional para aceptar o no un archivo según su ruta. - /// - /// # Ejemplo - /// - /// ```rust,no_run - /// use pagetop_build::StaticFilesBundle; - /// use std::path::Path; - /// - /// fn main() -> std::io::Result<()> { - /// fn only_images(path: &Path) -> bool { - /// matches!( - /// path.extension().and_then(|ext| ext.to_str()), - /// Some("jpg" | "png" | "gif") - /// ) - /// } - /// - /// StaticFilesBundle::from_dir("./static", Some(only_images)) - /// .with_name("images") - /// .build() - /// } - /// ``` - pub fn from_dir<P>(dir: P, filter: Option<fn(&Path) -> bool>) -> Self - where - P: AsRef<Path>, - { - let dir_path = dir.as_ref(); - let dir_str = dir_path.to_str().unwrap_or_else(|| { - panic!( - "Resource directory path is not valid UTF-8: {}", - dir_path.display() - ); - }); + /// * `dir` - The directory containing the static files. + /// * `filter` - An optional function to filter files or directories to include. + pub fn from_dir(dir: &'static str, filter: Option<fn(p: &Path) -> bool>) -> Self { + let mut resource_dir = resource_dir(dir); - let mut resource_dir = resource_dir(dir_str); - - // Aplica el filtro si está definido. + // Apply the filter if provided. if let Some(f) = filter { resource_dir.with_filter(f); } - // Identifica el directorio temporal de recursos. StaticFilesBundle { resource_dir } } - /// Prepara un recurso CSS minimizado a partir de la compilación de un archivo SCSS (que puede a - /// su vez importar otros archivos SCSS). + /// Creates a bundle starting from a SCSS file. /// - /// # Argumentos + /// # Arguments /// - /// * `path` - Archivo SCSS a compilar. - /// * `target_name` - Nombre para el archivo CSS. + /// * `path` - The SCSS file to compile. + /// * `target_name` - The name for the CSS file in the bundle. /// - /// # Ejemplo + /// This function will panic: /// - /// ```rust,no_run - /// use pagetop_build::StaticFilesBundle; - /// - /// fn main() -> std::io::Result<()> { - /// StaticFilesBundle::from_scss("./bootstrap/scss/main.scss", "bootstrap.min.css") - /// .with_name("bootstrap_css") - /// .build() - /// } - /// ``` + /// * If the environment variable `OUT_DIR` is not set. + /// * If it is unable to create a temporary directory in the `OUT_DIR`. + /// * If the SCSS file cannot be compiled due to syntax errors in the SCSS file or missing + /// dependencies or import paths required for compilation. + /// * If it is unable to create the output CSS file in the temporary directory due to an invalid + /// `target_name` or insufficient permissions to create files in the temporary directory. + /// * If the function fails to write the compiled CSS content to the file. pub fn from_scss<P>(path: P, target_name: &str) -> Self where P: AsRef<Path>, { - // Crea un directorio temporal para el archivo CSS. + // Create a temporary directory for the CSS file. let out_dir = std::env::var("OUT_DIR").unwrap(); let temp_dir = Path::new(&out_dir).join("from_scss_files"); - - // Limpia el directorio temporal de ejecuciones previas, si existe. + // Clean up the temporary directory from previous runs, if it exists. if temp_dir.exists() { remove_dir_all(&temp_dir).unwrap_or_else(|e| { panic!( @@ -222,7 +159,7 @@ impl StaticFilesBundle { ); }); - // Compila SCSS a CSS. + // Compile SCSS to CSS. let css_content = from_path( path.as_ref(), &Options::default().style(OutputStyle::Compressed), @@ -234,22 +171,31 @@ impl StaticFilesBundle { ) }); - // Guarda el archivo CSS compilado en el directorio temporal. + // Write the compiled CSS to the temporary directory. let css_path = temp_dir.join(target_name); File::create(&css_path) - .unwrap_or_else(|_| panic!("Failed to create CSS file `{}`", css_path.display())) + .expect(&format!( + "Failed to create CSS file `{}`", + css_path.display() + )) .write_all(css_content.as_bytes()) - .unwrap_or_else(|_| panic!("Failed to write CSS content to `{}`", css_path.display())); + .expect(&format!( + "Failed to write CSS content to `{}`", + css_path.display() + )); - // Identifica el directorio temporal de recursos. + // Initialize ResourceDir with the temporary directory. StaticFilesBundle { resource_dir: resource_dir(temp_dir.to_str().unwrap()), } } - /// Asigna un nombre al conjunto de recursos. - pub fn with_name(mut self, name: impl AsRef<str>) -> Self { - let name = name.as_ref(); + /// Configures the name for the bundle of static files. + /// + /// # Panics + /// + /// This function will panic if the standard `OUT_DIR` environment variable is not set. + pub fn with_name(mut self, name: &'static str) -> Self { let out_dir = std::env::var("OUT_DIR").unwrap(); let filename = Path::new(&out_dir).join(format!("{name}.rs")); self.resource_dir.with_generated_filename(filename); @@ -258,7 +204,12 @@ impl StaticFilesBundle { self } - /// Contruye finalmente el conjunto de recursos para incluir en el binario de la aplicación. + /// Builds the bundle. + /// + /// # Errors + /// + /// This function will return an error if there is an issue with I/O operations, such as failing + /// to read or write to a file. pub fn build(self) -> std::io::Result<()> { self.resource_dir.build() } diff --git a/helpers/pagetop-macros/CHANGELOG.md b/helpers/pagetop-macros/CHANGELOG.md deleted file mode 100644 index 66a5f8d4..00000000 --- a/helpers/pagetop-macros/CHANGELOG.md +++ /dev/null @@ -1,43 +0,0 @@ -# CHANGELOG - -Este archivo documenta los cambios más relevantes realizados en cada versión. El formato está basado -en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/), y las versiones se numeran siguiendo -las reglas del [Versionado Semántico](https://semver.org/lang/es/). - -Resume la evolución del proyecto para usuarios y colaboradores, destacando nuevas funcionalidades, -correcciones, mejoras durante el desarrollo o cambios en la documentación. Cambios menores o -internos pueden omitirse si no afectan al uso del proyecto. - -## 0.2.0 (2025-09-20) - -### Cambiado - -- Retoques en el código -- Majora la validación de `builder_fn` - -### Dependencias - -- Actualiza dependencias para 0.4.0 - -### Documentado - -- Normaliza referencias al nombre PageTop - -### Otros cambios - -- 🚨 Ajustes menores sugeridos por clippy - -## 0.1.1 (2025-08-16) - -### Documentado - -- Cambia el formato para la documentación (#4) -- Corrige enlaces de licencia en la documentación - -### Otros cambios - -- Afina Cargo.toml para buscar la mejor categoría - -## 0.1.0 (2025-08-06) - -- Versión inicial diff --git a/helpers/pagetop-macros/Cargo.toml b/helpers/pagetop-macros/Cargo.toml index 601c551d..978c8316 100644 --- a/helpers/pagetop-macros/Cargo.toml +++ b/helpers/pagetop-macros/Cargo.toml @@ -1,24 +1,25 @@ [package] name = "pagetop-macros" -version = "0.2.0" +version = "0.0.14" edition = "2021" -description = """ - Una colección de macros que mejoran la experiencia de desarrollo con PageTop. +description = """\ + A collection of macros that boost PageTop development.\ """ -categories = ["development-tools::procedural-macro-helpers"] +categories = ["development-tools::procedural-macro-helpers", "web-programming"] keywords = ["pagetop", "macros", "proc-macros", "codegen"] -repository.workspace = true -homepage.workspace = true -license.workspace = true -authors.workspace = true +repository = "https://github.com/manuelcillero/pagetop" +homepage = "https://pagetop.cillero.es" +license = "MIT OR Apache-2.0" +authors = ["Manuel Cillero <manuel@cillero.es>"] [lib] proc-macro = true [dependencies] -proc-macro2 = "1.0" -proc-macro2-diagnostics = { version = "0.10", default-features = false } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } +proc-macro2 = "1.0.92" +proc-macro-crate = "3.2.0" +proc-macro-error = "1.0.4" +quote = "1.0.37" +syn = { version = "2.0.90", features = ["full"] } diff --git a/helpers/pagetop-macros/LICENSE-APACHE b/helpers/pagetop-macros/LICENSE-APACHE deleted file mode 100644 index 263ddac1..00000000 --- a/helpers/pagetop-macros/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Manuel Cillero - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/helpers/pagetop-macros/LICENSE-MIT b/helpers/pagetop-macros/LICENSE-MIT deleted file mode 100644 index cd8af3d6..00000000 --- a/helpers/pagetop-macros/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Manuel Cillero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/helpers/pagetop-macros/README.md b/helpers/pagetop-macros/README.md index 66fdc1fa..a3cf40ab 100644 --- a/helpers/pagetop-macros/README.md +++ b/helpers/pagetop-macros/README.md @@ -2,49 +2,50 @@ <h1>PageTop Macros</h1> -<p>Una colección de macros que mejoran la experiencia de desarrollo con <strong>PageTop</strong>.</p> +<p>A collection of macros that boost PageTop development.</p> -[![Doc API](https://img.shields.io/docsrs/pagetop-macros?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-macros) +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?style=for-the-badge)](#-license) +[![API Docs](https://img.shields.io/docsrs/pagetop-macros?label=API%20Docs&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-macros) [![Crates.io](https://img.shields.io/crates/v/pagetop-macros.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-macros) -[![Descargas](https://img.shields.io/crates/d/pagetop-macros.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-macros) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-macros#licencia) +[![Downloads](https://img.shields.io/crates/d/pagetop-macros.svg?style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-macros) </div> -## Sobre PageTop +# 📦 About 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. - -## Créditos - -Esta librería incluye entre sus macros una adaptación de -[maud-macros](https://crates.io/crates/maud_macros) -([0.27.0](https://github.com/lambda-fairy/maud/tree/v0.27.0/maud_macros)) de -[Chris Wong](https://crates.io/users/lambda-fairy) y una versión renombrada de -[SmartDefault](https://crates.io/crates/smart_default) (0.7.1) de -[Jane Doe](https://crates.io/users/jane-doe), llamada `AutoDefault`. Estas macros eliminan la -necesidad de referenciar `maud` o `smart_default` en las dependencias del archivo `Cargo.toml` de -cada proyecto PageTop. +[PageTop](https://docs.rs/pagetop) is an opinionated web framework to build modular *Server-Side +Rendering* web solutions. -# 🚧 Advertencia +# 🚧 Warning -**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**. +**PageTop** framework is currently in active development. The API is unstable and subject to +frequent changes. Production use is not recommended until version **0.1.0**. -# 📜 Licencia +# 🔖 Credits -El código está disponible bajo una doble licencia: +This crate includes an adapted version of [maud-macros](https://crates.io/crates/maud_macros) +(version [0.25.0](https://github.com/lambda-fairy/maud/tree/v0.25.0/maud_macros)) by +[Chris Wong](https://crates.io/users/lambda-fairy). - * **Licencia MIT** - ([LICENSE-MIT](LICENSE-MIT) o también https://opensource.org/licenses/MIT) +Additionally, the [SmartDefault](https://crates.io/crates/smart_default) crate (version 0.7.1) by +[Jane Doe](https://crates.io/users/jane-doe) has been embedded as `AutoDefault`, simplifying +`Default` implementations. - * **Licencia Apache, Versión 2.0** - ([LICENSE-APACHE](LICENSE-APACHE) o también https://www.apache.org/licenses/LICENSE-2.0) +Both eliminate the need to explicitly reference `maud` or `smart_default` in the `Cargo.toml` file +of each project. -Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el estándar de facto en -el ecosistema Rust. + +# 📜 License + +All code in this crate is dual-licensed under either: + + * MIT License + ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) + + * Apache License, Version 2.0, + ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) + +at your option. This means you can select the license you prefer! This dual-licensing approach is +the de-facto standard in the Rust ecosystem. diff --git a/helpers/pagetop-macros/src/lib.rs b/helpers/pagetop-macros/src/lib.rs index 194cd378..abc8ce69 100644 --- a/helpers/pagetop-macros/src/lib.rs +++ b/helpers/pagetop-macros/src/lib.rs @@ -1,109 +1,119 @@ -/*! -<div align="center"> - -<h1>PageTop Macros</h1> - -<p>Una colección de macros que mejoran la experiencia de desarrollo con <strong>PageTop</strong>.</p> - -[![Doc API](https://img.shields.io/docsrs/pagetop-macros?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-macros) -[![Crates.io](https://img.shields.io/crates/v/pagetop-macros.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-macros) -[![Descargas](https://img.shields.io/crates/d/pagetop-macros.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-macros) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-macros#licencia) - -</div> - -## 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. - -## Créditos - -Esta librería incluye entre sus macros una adaptación de -[maud-macros](https://crates.io/crates/maud_macros) -([0.27.0](https://github.com/lambda-fairy/maud/tree/v0.27.0/maud_macros)) de -[Chris Wong](https://crates.io/users/lambda-fairy) y una versión renombrada de -[SmartDefault](https://crates.io/crates/smart_default) (0.7.1) de -[Jane Doe](https://crates.io/users/jane-doe), llamada `AutoDefault`. Estas macros eliminan la -necesidad de referenciar `maud` o `smart_default` en las dependencias del archivo `Cargo.toml` de -cada proyecto PageTop. -*/ - -#![doc( - html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" -)] - mod maud; mod smart_default; use proc_macro::TokenStream; -use quote::{quote, quote_spanned}; -use syn::{parse_macro_input, spanned::Spanned, DeriveInput}; +use proc_macro_error::proc_macro_error; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{parse_macro_input, parse_str, DeriveInput, ItemFn}; + +/// Macro attribute to generate builder methods from `set_` methods. +/// +/// This macro takes a method with the `set_` prefix and generates a corresponding method with the +/// `with_` prefix to use in the builder pattern. +/// +/// # Panics +/// +/// This function will panic if a parameter identifier is not found in the argument list. +/// +/// # Examples +/// +/// ``` +/// #[fn_builder] +/// pub fn set_example(&mut self) -> &mut Self { +/// // implementation +/// } +/// ``` +/// +/// Will generate: +/// +/// ``` +/// pub fn with_example(mut self) -> Self { +/// self.set_example(); +/// self +/// } +/// ``` +#[proc_macro_attribute] +pub fn fn_builder(_: TokenStream, item: TokenStream) -> TokenStream { + let fn_set = parse_macro_input!(item as ItemFn); + let fn_set_name = fn_set.sig.ident.to_string(); + + if !fn_set_name.starts_with("set_") { + let expanded = quote_spanned! { + fn_set.sig.ident.span() => + compile_error!("expected a \"pub fn set_...() -> &mut Self\" method"); + }; + return expanded.into(); + } + + let fn_with_name = fn_set_name.replace("set_", "with_"); + let fn_with_generics = if fn_set.sig.generics.params.is_empty() { + fn_with_name.clone() + } else { + let g = &fn_set.sig.generics; + format!("{fn_with_name}{}", quote! { #g }.to_string()) + }; + + let where_clause = fn_set + .sig + .generics + .where_clause + .as_ref() + .map_or(String::new(), |where_clause| { + format!("{} ", quote! { #where_clause }.to_string()) + }); + + let args: Vec<String> = fn_set + .sig + .inputs + .iter() + .skip(1) + .map(|arg| arg.to_token_stream().to_string()) + .collect(); + + let params: Vec<String> = args + .iter() + .map(|arg| { + arg.split_whitespace() + .next() + .unwrap() + .trim_end_matches(':') + .to_string() + }) + .collect(); + + #[rustfmt::skip] + let fn_with = parse_str::<ItemFn>(format!(r##" + pub fn {fn_with_generics}(mut self, {}) -> Self {where_clause} {{ + self.{fn_set_name}({}); + self + }} + "##, args.join(", "), params.join(", ") + ).as_str()).unwrap(); + + #[rustfmt::skip] + let fn_set_doc = format!(r##" + <p id="method.{fn_with_name}" style="margin-bottom: 12px;">Use + <code class="code-header">pub fn <span class="fn" href="#method.{fn_with_name}">{fn_with_name}</span>(self, …) -> Self</code> + for the <a href="#method.new">builder pattern</a>. + </p> + "##); + + let expanded = quote! { + #[doc(hidden)] + #fn_with + #[inline] + #[doc = #fn_set_doc] + #fn_set + }; + expanded.into() +} -/// Macro para escribir plantillas HTML (basada en [Maud](https://docs.rs/maud)). #[proc_macro] +#[proc_macro_error] pub fn html(input: TokenStream) -> TokenStream { maud::expand(input.into()).into() } -/// Deriva [`Default`] con atributos personalizados (basada en -/// [SmartDefault](https://docs.rs/smart-default)). -/// -/// Al derivar una estructura con *AutoDefault* se genera automáticamente la implementación de -/// [`Default`]. Aunque, a diferencia de un simple `#[derive(Default)]`, el atributo -/// `#[derive(AutoDefault)]` permite usar anotaciones en los campos como `#[default = "..."]`, -/// funcionando incluso en estructuras con campos que no implementan [`Default`] o en *enums*. -/// -/// # Ejemplos -/// -/// ```rust -/// # use pagetop_macros::AutoDefault; -/// # fn main() { -/// #[derive(AutoDefault)] -/// # #[derive(PartialEq)] -/// # #[allow(dead_code)] -/// enum Foo { -/// Bar, -/// #[default] -/// Baz { -/// #[default = 12] -/// a: i32, -/// b: i32, -/// #[default(Some(Default::default()))] -/// c: Option<i32>, -/// #[default(_code = "vec![1, 2, 3]")] -/// d: Vec<u32>, -/// #[default = "four"] -/// e: String, -/// }, -/// Qux(i32), -/// } -/// -/// assert!(Foo::default() == Foo::Baz { -/// a: 12, -/// b: 0, -/// c: Some(0), -/// d: vec![1, 2, 3], -/// e: "four".to_owned(), -/// }); -/// # } -/// ``` -/// -/// * `Baz` tiene el atributo `#[default]`. Esto significa que el valor por defecto de `Foo` es -/// `Foo::Baz`. Solo una variante puede tener el atributo `#[default]`, y dicho atributo no debe -/// tener ningún valor asociado. -/// * `a` tiene el atributo `#[default = 12]`. Esto significa que su valor por defecto es `12`. -/// * `b` no tiene ningún atributo `#[default = ...]`. Su valor por defecto será, por tanto, el -/// valor por defecto de `i32`, es decir, `0`. -/// * `c` es un `Option<i32>`, y su valor por defecto es `Some(Default::default())`. Rust no puede -/// (actualmente) analizar `#[default = Some(Default::default())]`, pero podemos escribir -/// `#[default(Some(Default::default))]`. -/// * `d` contiene el token `!`, que (actualmente) no puede ser analizado ni siquiera usando -/// `#[default(...)]`, así que debemos codificarlo como una cadena y marcarlo con `_code =`. -/// * `e` es un `String`, por lo que el literal de cadena `"four"` se convierte automáticamente en -/// él. Esta conversión automática **solo** ocurre con literales de cadena (o de bytes), y solo si -/// no se usa `_code`. #[proc_macro_derive(AutoDefault, attributes(default))] pub fn derive_auto_default(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -113,285 +123,44 @@ pub fn derive_auto_default(input: TokenStream) -> TokenStream { } } -/// Macro (*attribute*) que asocia un método *builder* `with_` con un método `alter_`. -/// -/// La macro añade automáticamente un método `alter_` que permite modificar la instancia actual -/// usando `&mut self`; y redefine el método *builder* `with_`, que consume `mut self`, para delegar -/// la lógica al nuevo método `alter_`, reutilizando así la misma implementación. -/// -/// Esta macro emitirá un error en tiempo de compilación si la función anotada no cumple con la -/// firma esperada para el método *builder*: `pub fn with_...(mut self, ...) -> Self`. -/// -/// # Ejemplo -/// -/// Si defines un método `with_` como este: -/// -/// ```rust -/// # use pagetop_macros::builder_fn; -/// # struct Example {value: Option<String>}; -/// # impl Example { -/// #[builder_fn] -/// pub fn with_example(mut self, value: impl Into<String>) -> Self { -/// self.value = Some(value.into()); -/// self -/// } -/// # } -/// ``` -/// -/// la macro rescribirá el método `with_` y generará un nuevo método `alter_`: -/// -/// ```rust -/// # struct Example {value: Option<String>}; -/// # impl Example { -/// #[inline] -/// pub fn with_example(mut self, value: impl Into<String>) -> Self { -/// self.alter_example(value); -/// self -/// } -/// -/// pub fn alter_example(&mut self, value: impl Into<String>) -> &mut Self { -/// self.value = Some(value.into()); -/// self -/// } -/// # } -/// ``` -/// -/// De esta forma, cada método *builder* `with_...()` generará automáticamente su correspondiente -/// método `alter_...()` para dejar modificar instancias existentes. -#[proc_macro_attribute] -pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream { - use syn::{parse2, FnArg, Ident, ImplItemFn, Pat, ReturnType, TraitItemFn, Type}; +#[proc_macro_derive(ComponentClasses)] +pub fn derive_component_classes(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; - let ts: proc_macro2::TokenStream = item.clone().into(); + #[rustfmt::skip] + let fn_set_doc = format!(r##" + <p id="method.with_classes">Use + <code class="code-header"><span class="fn" href="#method.with_classes">with_classes</span>(self, …) -> Self</code> + to apply the <a href="#method.new">builder pattern</a>. + </p> + "##); - enum Kind { - Impl(ImplItemFn), - Trait(TraitItemFn), - } - - // Detecta si estamos en `impl` o `trait`. - let kind = if let Ok(it) = parse2::<ImplItemFn>(ts.clone()) { - Kind::Impl(it) - } else if let Ok(tt) = parse2::<TraitItemFn>(ts.clone()) { - Kind::Trait(tt) - } else { - return quote! { - compile_error!("#[builder_fn] only supports methods in `impl` blocks or `trait` items"); - } - .into(); - }; - - // Extrae piezas comunes (sig, attrs, vis, bloque?, es_trait?). - let (sig, attrs, vis, body_opt, is_trait) = match &kind { - Kind::Impl(m) => (&m.sig, &m.attrs, Some(&m.vis), Some(&m.block), false), - Kind::Trait(t) => (&t.sig, &t.attrs, None, t.default.as_ref(), true), - }; - - let with_name = sig.ident.clone(); - let with_name_str = sig.ident.to_string(); - - // Valida el nombre del método. - if !with_name_str.starts_with("with_") { - return quote_spanned! { - sig.ident.span() => compile_error!("expected a named `with_...()` method"); - } - .into(); - } - - // Sólo se exige `pub` en `impl` (en `trait` no aplica). - let vis_pub = match (is_trait, vis) { - (false, Some(v)) => quote! { #v }, - _ => quote! {}, - }; - - // Validaciones comunes. - if sig.asyncness.is_some() { - return quote_spanned! { - sig.asyncness.span() => compile_error!("`with_...()` cannot be `async`"); - } - .into(); - } - if sig.constness.is_some() { - return quote_spanned! { - sig.constness.span() => compile_error!("`with_...()` cannot be `const`"); - } - .into(); - } - if sig.abi.is_some() { - return quote_spanned! { - sig.abi.span() => compile_error!("`with_...()` cannot be `extern`"); - } - .into(); - } - if sig.unsafety.is_some() { - return quote_spanned! { - sig.unsafety.span() => compile_error!("`with_...()` cannot be `unsafe`"); - } - .into(); - } - - // En `impl` se exige exactamente `mut self`; y en `trait` se exige `self` (sin &). - let receiver_ok = match sig.inputs.first() { - Some(FnArg::Receiver(r)) => { - // Rechaza `self: SomeType`. - if r.colon_token.is_some() { - false - } else if is_trait { - // Exactamente `self` (sin &, sin mut). - r.reference.is_none() && r.mutability.is_none() - } else { - // Exactamente `mut self`. - r.reference.is_none() && r.mutability.is_some() + let expanded = quote! { + impl ComponentClasses for #name { + #[inline] + #[doc = #fn_set_doc] + fn set_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self { + self.classes.set_value(op, classes); + self } - } - _ => false, - }; - if !receiver_ok { - let msg = if is_trait { - "expected `self` (not `mut self`, `&self` or `&mut self`) in trait method" - } else { - "expected first argument to be exactly `mut self`" - }; - let err = sig - .inputs - .first() - .map(|a| a.span()) - .unwrap_or(sig.ident.span()); - return quote_spanned! { - err => compile_error!(#msg); - } - .into(); - } - // Valida que el método devuelve exactamente `Self`. - match &sig.output { - ReturnType::Type(_, ty) => match ty.as_ref() { - Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => {} - _ => { - return quote_spanned! { - ty.span() => compile_error!("expected return type to be exactly `Self`"); - } - .into(); - } - }, - _ => { - return quote_spanned! { - sig.output.span() => compile_error!("expected return type to be exactly `Self`"); - } - .into(); - } - } - - // Genera el nombre del método alter_...(). - let stem = with_name_str.strip_prefix("with_").expect("validated"); - let alter_ident = Ident::new(&format!("alter_{stem}"), with_name.span()); - - // Extrae genéricos y cláusulas where. - let generics = &sig.generics; - let where_clause = &sig.generics.where_clause; - - // Extrae identificadores de los argumentos para la llamada (sin `mut` ni patrones complejos). - let args: Vec<_> = sig.inputs.iter().skip(1).collect(); - let call_idents: Vec<Ident> = { - let mut v = Vec::new(); - for arg in sig.inputs.iter().skip(1) { - match arg { - FnArg::Typed(pat) => { - if let Pat::Ident(pat_ident) = pat.pat.as_ref() { - v.push(pat_ident.ident.clone()); - } else { - return quote_spanned! { - pat.pat.span() => compile_error!( - "each parameter must be a simple identifier, e.g. `value: T`" - ); - } - .into(); - } - } - _ => { - return quote_spanned! { - arg.span() => compile_error!("unexpected receiver in parameter list"); - } - .into(); - } - } - } - v - }; - - // Filtra los atributos descartando `#[doc]` y `#[inline]` para el método `alter_...()`. - let non_doc_or_inline_attrs: Vec<_> = attrs - .iter() - .filter(|a| { - let p = a.path(); - !p.is_ident("doc") && !p.is_ident("inline") - }) - .cloned() - .collect(); - - // Documentación del método alter_...(). - let doc = format!("Equivale a [`Self::{with_name_str}()`], pero fuera del patrón *builder*."); - - // Genera el código final. - let expanded = match body_opt { - None => { - quote! { - #(#attrs)* - fn #with_name #generics (self, #(#args),*) -> Self #where_clause; - - #(#non_doc_or_inline_attrs)* - #[doc = #doc] - fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause; - } - } - Some(body) => { - // Si no se indicó ninguna forma de `inline`, fuerza `#[inline]` para `with_...()`. - let force_inline = if attrs.iter().any(|a| a.path().is_ident("inline")) { - quote! {} - } else { - quote! { #[inline] } - }; - let with_fn = if is_trait { - quote! { - #force_inline - #vis_pub fn #with_name #generics (self, #(#args),*) -> Self #where_clause { - let mut s = self; - s.#alter_ident(#(#call_idents),*); - s - } - } - } else { - quote! { - #force_inline - #vis_pub fn #with_name #generics (mut self, #(#args),*) -> Self #where_clause { - self.#alter_ident(#(#call_idents),*); - self - } - } - }; - quote! { - #(#attrs)* - #with_fn - - #(#non_doc_or_inline_attrs)* - #[doc = #doc] - #vis_pub fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause { - #body - } + fn classes(&self) -> &OptionClasses { + &self.classes } } }; - expanded.into() + + TokenStream::from(expanded) } -/// Define una función `main` asíncrona como punto de entrada de PageTop. +/// Marks async main function as the `PageTop` entry-point. /// -/// # Ejemplo -/// -/// ```rust,ignore +/// # Examples +/// ``` /// #[pagetop::main] /// async fn main() { -/// async { println!("Hello world!"); }.await +/// async { println!("Hello world"); }.await /// } /// ``` #[proc_macro_attribute] @@ -405,11 +174,10 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { output } -/// Define funciones de prueba asíncronas para usar con PageTop. +/// Marks async test functions to use the `PageTop` entry-point. /// -/// # Ejemplo -/// -/// ```rust,ignore +/// # Examples +/// ``` /// #[pagetop::test] /// async fn test() { /// assert_eq!(async { "Hello world" }.await, "Hello world"); diff --git a/helpers/pagetop-macros/src/maud.rs b/helpers/pagetop-macros/src/maud.rs index 2c763cd0..a4e7873f 100644 --- a/helpers/pagetop-macros/src/maud.rs +++ b/helpers/pagetop-macros/src/maud.rs @@ -1,4 +1,4 @@ -// #![doc(html_root_url = "https://docs.rs/maud_macros/0.27.0")] +// #![doc(html_root_url = "https://docs.rs/maud_macros/0.25.0")] // TokenStream values are reference counted, and the mental overhead of tracking // lifetimes outweighs the marginal gains from explicit borrowing // #![allow(clippy::needless_pass_by_value)] @@ -6,45 +6,34 @@ mod ast; mod escape; mod generate; +mod parse; -use ast::DiagnosticParse; -use proc_macro2::{Ident, Span, TokenStream}; -use proc_macro2_diagnostics::Diagnostic; +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use proc_macro_crate::{crate_name, FoundCrate}; use quote::quote; -use syn::parse::{ParseStream, Parser}; pub fn expand(input: TokenStream) -> TokenStream { + let output_ident = TokenTree::Ident(Ident::new("__maud_output", Span::mixed_site())); // Heuristic: the size of the resulting markup tends to correlate with the // code size of the template itself let size_hint = input.to_string().len(); - - let mut diagnostics = Vec::new(); - let markups = match Parser::parse2( - |input: ParseStream| ast::Markups::diagnostic_parse(input, &mut diagnostics), - input, - ) { - Ok(data) => data, - Err(err) => { - let err = err.to_compile_error(); - let diag_tokens = diagnostics.into_iter().map(Diagnostic::emit_as_expr_tokens); - - return quote! {{ - #err - #(#diag_tokens)* - }}; - } - }; - - let diag_tokens = diagnostics.into_iter().map(Diagnostic::emit_as_expr_tokens); - - let output_ident = Ident::new("__maud_output", Span::mixed_site()); + let markups = parse::parse(input); let stmts = generate::generate(markups, output_ident.clone()); - quote! {{ + let found_crate = crate_name("pagetop").expect("pagetop is present in `Cargo.toml`"); + let pre_escaped = match found_crate { + FoundCrate::Itself => quote!( + crate::html::PreEscaped(#output_ident) + ), + _ => quote!( + pagetop::html::PreEscaped(#output_ident) + ), + }; + + quote!({ extern crate alloc; let mut #output_ident = alloc::string::String::with_capacity(#size_hint); #stmts - #(#diag_tokens)* - pagetop::html::PreEscaped(#output_ident) - }} + #pre_escaped + }) } diff --git a/helpers/pagetop-macros/src/maud/ast.rs b/helpers/pagetop-macros/src/maud/ast.rs index ebd53318..cd8a2cef 100644 --- a/helpers/pagetop-macros/src/maud/ast.rs +++ b/helpers/pagetop-macros/src/maud/ast.rs @@ -1,1108 +1,221 @@ -use std::fmt::{self, Display, Formatter}; +use proc_macro2::{TokenStream, TokenTree}; +use proc_macro_error::SpanRange; -use proc_macro2::TokenStream; -use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt}; -use quote::ToTokens; -use syn::{ - braced, bracketed, - ext::IdentExt, - parenthesized, - parse::{Lookahead1, Parse, ParseStream}, - punctuated::{Pair, Punctuated}, - spanned::Spanned, - token::{ - At, Brace, Bracket, Colon, Comma, Dot, Else, Eq, FatArrow, For, If, In, Let, Match, Minus, - Paren, Pound, Question, Semi, Slash, While, +#[derive(Debug)] +pub enum Markup { + /// Used as a placeholder value on parse error. + ParseError { + span: SpanRange, + }, + Block(Block), + Literal { + content: String, + span: SpanRange, + }, + Symbol { + symbol: TokenStream, + }, + Splice { + expr: TokenStream, + outer_span: SpanRange, + }, + Element { + name: TokenStream, + attrs: Vec<Attr>, + body: ElementBody, + }, + Let { + at_span: SpanRange, + tokens: TokenStream, + }, + Special { + segments: Vec<Special>, + }, + Match { + at_span: SpanRange, + head: TokenStream, + arms: Vec<MatchArm>, + arms_span: SpanRange, }, - Error, Expr, Ident, Lit, LitBool, LitInt, LitStr, Local, Pat, Stmt, -}; - -#[derive(Debug, Clone)] -pub struct Markups<E> { - pub markups: Vec<Markup<E>>, } -impl<E: MaybeElement> DiagnosticParse for Markups<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let mut markups = Vec::new(); - while !input.is_empty() { - markups.push(Markup::diagnostic_parse_in_block(input, diagnostics)?) - } - Ok(Self { markups }) - } -} - -impl<E: ToTokens> ToTokens for Markups<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - for markup in &self.markups { - markup.to_tokens(tokens); - } - } -} - -#[derive(Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum Markup<E> { - Block(Block<E>), - Lit(HtmlLit), - Splice { paren_token: Paren, expr: Expr }, - Element(E), - ControlFlow(ControlFlow<E>), - Semi(Semi), -} - -impl<E: MaybeElement> Markup<E> { - pub fn diagnostic_parse_in_block( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - if input.peek(Let) - || input.peek(If) - || input.peek(Else) - || input.peek(For) - || input.peek(While) - || input.peek(Match) - { - let kw = input.call(Ident::parse_any)?; - diagnostics.push( - kw.span() - .error(format!("found keyword `{kw}`")) - .help(format!("should this be `@{kw}`?")), - ); - } - - let lookahead = input.lookahead1(); - - if lookahead.peek(Brace) { - input.diagnostic_parse(diagnostics).map(Self::Block) - } else if lookahead.peek(Lit) { - input.diagnostic_parse(diagnostics).map(Self::Lit) - } else if lookahead.peek(Paren) { - let content; - Ok(Self::Splice { - paren_token: parenthesized!(content in input), - expr: content.parse()?, - }) - } else if let Some(parse_element) = E::should_parse(&lookahead) { - parse_element(input, diagnostics).map(Self::Element) - } else if lookahead.peek(At) { - input.diagnostic_parse(diagnostics).map(Self::ControlFlow) - } else if lookahead.peek(Semi) { - input.parse().map(Self::Semi) - } else { - Err(lookahead.error()) - } - } -} - -impl<E: MaybeElement> DiagnosticParse for Markup<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let markup = Self::diagnostic_parse_in_block(input, diagnostics)?; - - if let Self::ControlFlow(ControlFlow { - kind: ControlFlowKind::Let(_), - .. - }) = &markup - { - diagnostics.push( - markup - .span() - .error("`@let` bindings are only allowed inside blocks"), - ) - } - - Ok(markup) - } -} - -impl<E: ToTokens> ToTokens for Markup<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Block(block) => block.to_tokens(tokens), - Self::Lit(lit) => lit.to_tokens(tokens), - Self::Splice { paren_token, expr } => { - paren_token.surround(tokens, |tokens| { - expr.to_tokens(tokens); - }); +impl Markup { + pub fn span(&self) -> SpanRange { + match *self { + Markup::ParseError { span } => span, + Markup::Block(ref block) => block.span(), + Markup::Literal { span, .. } => span, + Markup::Symbol { ref symbol } => span_tokens(symbol.clone()), + Markup::Splice { outer_span, .. } => outer_span, + Markup::Element { + ref name, ref body, .. + } => { + let name_span = span_tokens(name.clone()); + name_span.join_range(body.span()) } - Self::Element(element) => element.to_tokens(tokens), - Self::ControlFlow(control_flow) => control_flow.to_tokens(tokens), - Self::Semi(semi) => semi.to_tokens(tokens), + Markup::Let { + at_span, + ref tokens, + } => at_span.join_range(span_tokens(tokens.clone())), + Markup::Special { ref segments } => join_ranges(segments.iter().map(Special::span)), + Markup::Match { + at_span, arms_span, .. + } => at_span.join_range(arms_span), } } } -/// Represents a context that may or may not allow elements. -/// -/// An attribute accepts almost the same syntax as an element body, except child elements aren't -/// allowed. To enable code reuse, introduce a trait that abstracts over whether an element is -/// allowed or not. -pub trait MaybeElement: Sized + ToTokens { - /// If an element can be parsed here, returns `Some` with a parser for the rest of the element. - fn should_parse(lookahead: &Lookahead1<'_>) -> Option<DiagnosticParseFn<Self>>; -} - -/// An implementation of `DiagnosticParse::diagnostic_parse`. -pub type DiagnosticParseFn<T> = fn(ParseStream, &mut Vec<Diagnostic>) -> syn::Result<T>; - -/// Represents an attribute context, where elements are disallowed. -#[derive(Debug, Clone)] -pub enum NoElement {} - -impl MaybeElement for NoElement { - fn should_parse( - _lookahead: &Lookahead1<'_>, - ) -> Option<fn(ParseStream, &mut Vec<Diagnostic>) -> syn::Result<Self>> { - None - } -} - -impl ToTokens for NoElement { - fn to_tokens(&self, _tokens: &mut TokenStream) { - match *self {} - } -} - -#[derive(Debug, Clone)] -pub struct Element { - pub name: Option<HtmlName>, - pub attrs: Vec<Attribute>, - pub body: ElementBody, -} - -impl From<NoElement> for Element { - fn from(value: NoElement) -> Self { - match value {} - } -} - -impl MaybeElement for Element { - fn should_parse( - lookahead: &Lookahead1<'_>, - ) -> Option<fn(ParseStream, &mut Vec<Diagnostic>) -> syn::Result<Self>> { - if lookahead.peek(Ident::peek_any) || lookahead.peek(Dot) || lookahead.peek(Pound) { - Some(Element::diagnostic_parse) - } else { - None - } - } -} - -impl DiagnosticParse for Element { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - name: if input.peek(Ident::peek_any) { - Some(input.diagnostic_parse(diagnostics)?) - } else { - None - }, - attrs: { - let mut id_pushed = false; - let mut attrs = Vec::new(); - - while input.peek(Ident::peek_any) - || input.peek(Lit) - || input.peek(Dot) - || input.peek(Pound) - { - let attr = input.diagnostic_parse(diagnostics)?; - - if let Attribute::Id { .. } = attr { - if id_pushed { - return Err(Error::new_spanned( - attr, - "duplicate id (`#`) attribute specified", - )); - } - id_pushed = true; - } - - attrs.push(attr); - } - - if !(input.peek(Brace) || input.peek(Semi) || input.peek(Slash)) { - let lookahead = input.lookahead1(); - - lookahead.peek(Ident::peek_any); - lookahead.peek(Lit); - lookahead.peek(Dot); - lookahead.peek(Pound); - - lookahead.peek(Brace); - lookahead.peek(Semi); - - return Err(lookahead.error()); - } - - attrs - }, - body: input.diagnostic_parse(diagnostics)?, - }) - } -} - -impl ToTokens for Element { - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(name) = &self.name { - name.to_tokens(tokens); - } - for attr in &self.attrs { - attr.to_tokens(tokens); - } - self.body.to_tokens(tokens); - } -} - -#[derive(Debug, Clone)] -pub enum ElementBody { - Void(Semi), - Block(Block<Element>), -} - -impl DiagnosticParse for ElementBody { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Semi) { - input.parse().map(Self::Void) - } else if lookahead.peek(Brace) { - input.diagnostic_parse(diagnostics).map(Self::Block) - } else if lookahead.peek(Slash) { - diagnostics.push( - input - .parse::<Slash>()? - .span() - .error("void elements must use `;`, not `/`") - .help("change this to `;`") - .help("see https://github.com/lambda-fairy/maud/pull/315 for details"), - ); - - Ok(Self::Void(<Semi>::default())) - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for ElementBody { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Void(semi) => semi.to_tokens(tokens), - Self::Block(block) => block.to_tokens(tokens), - } - } -} - -#[derive(Debug, Clone)] -pub struct Block<E> { - pub brace_token: Brace, - pub markups: Markups<E>, -} - -impl<E: MaybeElement> DiagnosticParse for Block<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let content; - Ok(Self { - brace_token: braced!(content in input), - markups: content.diagnostic_parse(diagnostics)?, - }) - } -} - -impl<E: ToTokens> ToTokens for Block<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.brace_token.surround(tokens, |tokens| { - self.markups.to_tokens(tokens); - }); - } -} - -#[derive(Debug, Clone)] -pub enum Attribute { +#[derive(Debug)] +pub enum Attr { Class { - dot_token: Dot, - name: HtmlNameOrMarkup, + dot_span: SpanRange, + name: Markup, toggler: Option<Toggler>, }, Id { - pound_token: Pound, - name: HtmlNameOrMarkup, + hash_span: SpanRange, + name: Markup, }, Named { - name: HtmlName, - attr_type: AttributeType, + named_attr: NamedAttr, }, } -impl DiagnosticParse for Attribute { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Dot) { - Ok(Self::Class { - dot_token: input.parse()?, - name: input.diagnostic_parse(diagnostics)?, - toggler: { - let lookahead = input.lookahead1(); - - if lookahead.peek(Bracket) { - Some(input.diagnostic_parse(diagnostics)?) - } else { - None - } - }, - }) - } else if lookahead.peek(Pound) { - Ok(Self::Id { - pound_token: input.parse()?, - name: input.diagnostic_parse(diagnostics)?, - }) - } else { - let name = input.diagnostic_parse::<HtmlName>(diagnostics)?; - - if input.peek(Question) { - input.parse::<Question>()?; - } - - let fork = input.fork(); - - let attr = Self::Named { - name: name.clone(), - attr_type: input.diagnostic_parse(diagnostics)?, - }; - - if fork.peek(Eq) && fork.peek2(LitBool) { - diagnostics.push( - attr.span() - .error("attribute value must be a string") - .help(format!("to declare an empty attribute, omit the equals sign: `{name}`")) - .help(format!("to toggle the attribute, use square brackets: `{name}[some_boolean_flag]`")) - ); - } - - Ok(attr) - } - } -} - -impl ToTokens for Attribute { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Class { - dot_token, - name, - toggler, +impl Attr { + pub fn span(&self) -> SpanRange { + match *self { + Attr::Class { + dot_span, + ref name, + ref toggler, } => { - dot_token.to_tokens(tokens); - name.to_tokens(tokens); + let name_span = name.span(); + let dot_name_span = dot_span.join_range(name_span); if let Some(toggler) = toggler { - toggler.to_tokens(tokens); + dot_name_span.join_range(toggler.cond_span) + } else { + dot_name_span } } - Self::Id { pound_token, name } => { - pound_token.to_tokens(tokens); - name.to_tokens(tokens); - } - Self::Named { name, attr_type } => { - name.to_tokens(tokens); - attr_type.to_tokens(tokens); + Attr::Id { + hash_span, + ref name, + } => { + let name_span = name.span(); + hash_span.join_range(name_span) } + Attr::Named { ref named_attr } => named_attr.span(), } } } -#[derive(Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum HtmlNameOrMarkup { - HtmlName(HtmlName), - Markup(Markup<NoElement>), +#[derive(Debug)] +pub enum ElementBody { + Void { semi_span: SpanRange }, + Block { block: Block }, } -impl DiagnosticParse for HtmlNameOrMarkup { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - if input.peek(Ident::peek_any) || input.peek(Lit) { - input.diagnostic_parse(diagnostics).map(Self::HtmlName) +impl ElementBody { + pub fn span(&self) -> SpanRange { + match *self { + ElementBody::Void { semi_span } => semi_span, + ElementBody::Block { ref block } => block.span(), + } + } +} + +#[derive(Debug)] +pub struct Block { + pub markups: Vec<Markup>, + pub outer_span: SpanRange, +} + +impl Block { + pub fn span(&self) -> SpanRange { + self.outer_span + } +} + +#[derive(Debug)] +pub struct Special { + pub at_span: SpanRange, + pub head: TokenStream, + pub body: Block, +} + +impl Special { + pub fn span(&self) -> SpanRange { + let body_span = self.body.span(); + self.at_span.join_range(body_span) + } +} + +#[derive(Debug)] +pub struct NamedAttr { + pub name: TokenStream, + pub attr_type: AttrType, +} + +impl NamedAttr { + fn span(&self) -> SpanRange { + let name_span = span_tokens(self.name.clone()); + if let Some(attr_type_span) = self.attr_type.span() { + name_span.join_range(attr_type_span) } else { - input.diagnostic_parse(diagnostics).map(Self::Markup) + name_span } } } -impl Parse for HtmlNameOrMarkup { - fn parse(input: ParseStream) -> syn::Result<Self> { - Self::diagnostic_parse(input, &mut Vec::new()) - } +#[derive(Debug)] +pub enum AttrType { + Normal { value: Markup }, + Optional { toggler: Toggler }, + Empty { toggler: Option<Toggler> }, } -impl ToTokens for HtmlNameOrMarkup { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::HtmlName(name) => name.to_tokens(tokens), - Self::Markup(markup) => markup.to_tokens(tokens), +impl AttrType { + fn span(&self) -> Option<SpanRange> { + match *self { + AttrType::Normal { ref value } => Some(value.span()), + AttrType::Optional { ref toggler } => Some(toggler.span()), + AttrType::Empty { ref toggler } => toggler.as_ref().map(Toggler::span), } } } -impl Display for HtmlNameOrMarkup { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::HtmlName(name) => name.fmt(f), - Self::Markup(markup) => markup.to_token_stream().fmt(f), - } - } -} - -#[derive(Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum AttributeType { - Normal { - eq_token: Eq, - value: Markup<NoElement>, - }, - Optional { - eq_token: Eq, - toggler: Toggler, - }, - Empty(Option<Toggler>), -} - -impl DiagnosticParse for AttributeType { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Eq) { - let eq_token = input.parse()?; - - if input.peek(Bracket) { - Ok(Self::Optional { - eq_token, - toggler: input.diagnostic_parse(diagnostics)?, - }) - } else { - Ok(Self::Normal { - eq_token, - value: input.diagnostic_parse(diagnostics)?, - }) - } - } else if lookahead.peek(Bracket) { - Ok(Self::Empty(Some(input.diagnostic_parse(diagnostics)?))) - } else { - Ok(Self::Empty(None)) - } - } -} - -impl ToTokens for AttributeType { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Normal { eq_token, value } => { - eq_token.to_tokens(tokens); - value.to_tokens(tokens); - } - Self::Optional { eq_token, toggler } => { - eq_token.to_tokens(tokens); - toggler.to_tokens(tokens); - } - Self::Empty(toggler) => { - if let Some(toggler) = toggler { - toggler.to_tokens(tokens); - } - } - } - } -} - -#[derive(Debug, Clone)] -pub struct HtmlName { - pub name: Punctuated<HtmlNameFragment, HtmlNamePunct>, -} - -impl DiagnosticParse for HtmlName { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - name: { - let mut punctuated = Punctuated::new(); - - loop { - punctuated.push_value(input.diagnostic_parse(diagnostics)?); - - if !(input.peek(Minus) || input.peek(Colon)) { - break; - } - - let punct = input.diagnostic_parse(diagnostics)?; - punctuated.push_punct(punct); - } - - punctuated - }, - }) - } -} - -impl Parse for HtmlName { - fn parse(input: ParseStream) -> syn::Result<Self> { - Self::diagnostic_parse(input, &mut Vec::new()) - } -} - -impl ToTokens for HtmlName { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.name.to_tokens(tokens); - } -} - -impl Display for HtmlName { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - for pair in self.name.pairs() { - match pair { - Pair::Punctuated(fragment, punct) => { - fragment.fmt(f)?; - punct.fmt(f)?; - } - Pair::End(fragment) => { - fragment.fmt(f)?; - } - } - } - - Ok(()) - } -} - -#[derive(Debug, Clone)] -pub enum HtmlNameFragment { - Ident(Ident), - LitInt(LitInt), - LitStr(LitStr), - Empty, -} - -impl DiagnosticParse for HtmlNameFragment { - fn diagnostic_parse( - input: ParseStream, - _diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Ident::peek_any) { - input.call(Ident::parse_any).map(Self::Ident) - } else if lookahead.peek(LitInt) { - input.parse().map(Self::LitInt) - } else if lookahead.peek(LitStr) { - input.parse().map(Self::LitStr) - } else if lookahead.peek(Minus) || lookahead.peek(Colon) { - Ok(Self::Empty) - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for HtmlNameFragment { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Ident(ident) => ident.to_tokens(tokens), - Self::LitInt(lit) => lit.to_tokens(tokens), - Self::LitStr(lit) => lit.to_tokens(tokens), - Self::Empty => {} - } - } -} - -impl Display for HtmlNameFragment { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Ident(ident) => ident.fmt(f), - Self::LitInt(lit) => lit.fmt(f), - Self::LitStr(lit) => lit.value().fmt(f), - Self::Empty => Ok(()), - } - } -} - -#[derive(Debug, Clone)] -pub struct HtmlLit { - pub lit: LitStr, -} - -impl DiagnosticParse for HtmlLit { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Lit) { - let lit = input.parse()?; - match lit { - Lit::Str(lit) => Ok(Self { lit }), - Lit::Int(lit) => { - diagnostics.push( - lit.span() - .error(format!(r#"literal must be double-quoted: `"{lit}"`"#)), - ); - Ok(Self { - lit: LitStr::new("", lit.span()), - }) - } - Lit::Float(lit) => { - diagnostics.push( - lit.span() - .error(format!(r#"literal must be double-quoted: `"{lit}"`"#)), - ); - Ok(Self { - lit: LitStr::new("", lit.span()), - }) - } - Lit::Char(lit) => { - diagnostics.push(lit.span().error(format!( - r#"literal must be double-quoted: `"{}"`"#, - lit.value() - ))); - Ok(Self { - lit: LitStr::new("", lit.span()), - }) - } - Lit::Bool(_) => { - // diagnostic handled earlier with more information - Ok(Self { - lit: LitStr::new("", lit.span()), - }) - } - _ => { - diagnostics.push(lit.span().error("expected string")); - Ok(Self { - lit: LitStr::new("", lit.span()), - }) - } - } - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for HtmlLit { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.lit.to_tokens(tokens); - } -} - -impl Display for HtmlLit { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.lit.value().fmt(f) - } -} - -#[derive(Debug, Clone)] -pub enum HtmlNamePunct { - Colon(Colon), - Hyphen(Minus), -} - -impl DiagnosticParse for HtmlNamePunct { - fn diagnostic_parse(input: ParseStream, _: &mut Vec<Diagnostic>) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(Colon) { - input.parse().map(Self::Colon) - } else if lookahead.peek(Minus) { - input.parse().map(Self::Hyphen) - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for HtmlNamePunct { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Colon(token) => token.to_tokens(tokens), - Self::Hyphen(token) => token.to_tokens(tokens), - } - } -} - -impl Display for HtmlNamePunct { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Colon(_) => f.write_str(":"), - Self::Hyphen(_) => f.write_str("-"), - } - } -} - -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Toggler { - pub bracket_token: Bracket, - pub cond: Expr, + pub cond: TokenStream, + pub cond_span: SpanRange, } -impl DiagnosticParse for Toggler { - fn diagnostic_parse(input: ParseStream, _: &mut Vec<Diagnostic>) -> syn::Result<Self> { - let content; - Ok(Self { - bracket_token: bracketed!(content in input), - cond: content.parse()?, - }) +impl Toggler { + fn span(&self) -> SpanRange { + self.cond_span } } -impl ToTokens for Toggler { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.bracket_token.surround(tokens, |tokens| { - self.cond.to_tokens(tokens); - }); - } +#[derive(Debug)] +pub struct MatchArm { + pub head: TokenStream, + pub body: Block, } -#[derive(Debug, Clone)] -pub struct ControlFlow<E> { - pub at_token: At, - pub kind: ControlFlowKind<E>, +pub fn span_tokens<I: IntoIterator<Item = TokenTree>>(tokens: I) -> SpanRange { + join_ranges(tokens.into_iter().map(|s| SpanRange::single_span(s.span()))) } -impl<E: MaybeElement> DiagnosticParse for ControlFlow<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - at_token: input.parse()?, - kind: { - let lookahead = input.lookahead1(); - - if lookahead.peek(If) { - ControlFlowKind::If(input.diagnostic_parse(diagnostics)?) - } else if lookahead.peek(For) { - ControlFlowKind::For(input.diagnostic_parse(diagnostics)?) - } else if lookahead.peek(While) { - ControlFlowKind::While(input.diagnostic_parse(diagnostics)?) - } else if lookahead.peek(Match) { - ControlFlowKind::Match(input.diagnostic_parse(diagnostics)?) - } else if lookahead.peek(Let) { - let Stmt::Local(local) = input.parse()? else { - unreachable!() - }; - - ControlFlowKind::Let(local) - } else { - return Err(lookahead.error()); - } - }, - }) - } +pub fn join_ranges<I: IntoIterator<Item = SpanRange>>(ranges: I) -> SpanRange { + let mut iter = ranges.into_iter(); + let first = match iter.next() { + Some(span) => span, + None => return SpanRange::call_site(), + }; + let last = iter.last().unwrap_or(first); + first.join_range(last) } -impl<E: ToTokens> ToTokens for ControlFlow<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.at_token.to_tokens(tokens); - match &self.kind { - ControlFlowKind::Let(local) => local.to_tokens(tokens), - ControlFlowKind::If(if_) => if_.to_tokens(tokens), - ControlFlowKind::For(for_) => for_.to_tokens(tokens), - ControlFlowKind::While(while_) => while_.to_tokens(tokens), - ControlFlowKind::Match(match_) => match_.to_tokens(tokens), - } - } -} - -#[derive(Debug, Clone)] -pub enum ControlFlowKind<E> { - Let(Local), - If(IfExpr<E>), - For(ForExpr<E>), - While(WhileExpr<E>), - Match(MatchExpr<E>), -} - -#[derive(Debug, Clone)] -pub struct IfExpr<E> { - pub if_token: If, - pub cond: Expr, - pub then_branch: Block<E>, - pub else_branch: Option<(At, Else, Box<IfOrBlock<E>>)>, -} - -impl<E: MaybeElement> DiagnosticParse for IfExpr<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - if_token: input.parse()?, - cond: input.call(Expr::parse_without_eager_brace)?, - then_branch: input.diagnostic_parse(diagnostics)?, - else_branch: { - if input.peek(At) && input.peek2(Else) { - Some(( - input.parse()?, - input.parse()?, - input.diagnostic_parse(diagnostics)?, - )) - } else { - None - } - }, - }) - } -} - -impl<E: ToTokens> ToTokens for IfExpr<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.if_token.to_tokens(tokens); - self.cond.to_tokens(tokens); - self.then_branch.to_tokens(tokens); - if let Some((at_token, else_token, else_branch)) = &self.else_branch { - at_token.to_tokens(tokens); - else_token.to_tokens(tokens); - else_branch.to_tokens(tokens); - } - } -} - -#[derive(Debug, Clone)] -pub enum IfOrBlock<E> { - If(IfExpr<E>), - Block(Block<E>), -} - -impl<E: MaybeElement> DiagnosticParse for IfOrBlock<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let lookahead = input.lookahead1(); - - if lookahead.peek(If) { - input.diagnostic_parse(diagnostics).map(Self::If) - } else if lookahead.peek(Brace) { - input.diagnostic_parse(diagnostics).map(Self::Block) - } else { - Err(lookahead.error()) - } - } -} - -impl<E: ToTokens> ToTokens for IfOrBlock<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::If(if_) => if_.to_tokens(tokens), - Self::Block(block) => block.to_tokens(tokens), - } - } -} - -#[derive(Debug, Clone)] -pub struct ForExpr<E> { - pub for_token: For, - pub pat: Pat, - pub in_token: In, - pub expr: Expr, - pub body: Block<E>, -} - -impl<E: MaybeElement> DiagnosticParse for ForExpr<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - for_token: input.parse()?, - pat: input.call(Pat::parse_multi_with_leading_vert)?, - in_token: input.parse()?, - expr: input.call(Expr::parse_without_eager_brace)?, - body: input.diagnostic_parse(diagnostics)?, - }) - } -} - -impl<E: ToTokens> ToTokens for ForExpr<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.for_token.to_tokens(tokens); - self.pat.to_tokens(tokens); - self.in_token.to_tokens(tokens); - self.expr.to_tokens(tokens); - self.body.to_tokens(tokens); - } -} - -#[derive(Debug, Clone)] -pub struct WhileExpr<E> { - pub while_token: While, - pub cond: Expr, - pub body: Block<E>, -} - -impl<E: MaybeElement> DiagnosticParse for WhileExpr<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - while_token: input.parse()?, - cond: input.call(Expr::parse_without_eager_brace)?, - body: input.diagnostic_parse(diagnostics)?, - }) - } -} - -impl<E: ToTokens> ToTokens for WhileExpr<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.while_token.to_tokens(tokens); - self.cond.to_tokens(tokens); - self.body.to_tokens(tokens); - } -} - -#[derive(Debug, Clone)] -pub struct MatchExpr<E> { - pub match_token: Match, - pub expr: Expr, - pub brace_token: Brace, - pub arms: Vec<MatchArm<E>>, -} - -impl<E: MaybeElement> DiagnosticParse for MatchExpr<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - let match_token = input.parse()?; - let expr = input.call(Expr::parse_without_eager_brace)?; - - let content; - let brace_token = braced!(content in input); - - let mut arms = Vec::new(); - while !content.is_empty() { - arms.push(content.diagnostic_parse(diagnostics)?); - } - - Ok(Self { - match_token, - expr, - brace_token, - arms, - }) - } -} - -impl<E: ToTokens> ToTokens for MatchExpr<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.match_token.to_tokens(tokens); - self.expr.to_tokens(tokens); - self.brace_token.surround(tokens, |tokens| { - for arm in &self.arms { - arm.to_tokens(tokens); - } - }); - } -} - -#[derive(Debug, Clone)] -pub struct MatchArm<E> { - pub pat: Pat, - pub guard: Option<(If, Expr)>, - pub fat_arrow_token: FatArrow, - pub body: Markup<E>, - pub comma_token: Option<Comma>, -} - -impl<E: MaybeElement> DiagnosticParse for MatchArm<E> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Self { - pat: Pat::parse_multi_with_leading_vert(input)?, - guard: { - if input.peek(If) { - Some((input.parse()?, input.parse()?)) - } else { - None - } - }, - fat_arrow_token: input.parse()?, - body: Markup::diagnostic_parse_in_block(input, diagnostics)?, - comma_token: if input.peek(Comma) { - Some(input.parse()?) - } else { - None - }, - }) - } -} - -impl<E: ToTokens> ToTokens for MatchArm<E> { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.pat.to_tokens(tokens); - if let Some((if_token, guard)) = &self.guard { - if_token.to_tokens(tokens); - guard.to_tokens(tokens); - } - self.fat_arrow_token.to_tokens(tokens); - self.body.to_tokens(tokens); - if let Some(comma_token) = &self.comma_token { - comma_token.to_tokens(tokens); - } - } -} - -pub trait DiagnosticParse: Sized { - fn diagnostic_parse(input: ParseStream, diagnostics: &mut Vec<Diagnostic>) - -> syn::Result<Self>; -} - -impl<T: DiagnosticParse> DiagnosticParse for Box<T> { - fn diagnostic_parse( - input: ParseStream, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<Self> { - Ok(Box::new(input.diagnostic_parse(diagnostics)?)) - } -} - -trait DiagonsticParseExt: Sized { - fn diagnostic_parse<T: DiagnosticParse>( - self, - diagnostics: &mut Vec<Diagnostic>, - ) -> syn::Result<T>; -} - -impl DiagonsticParseExt for ParseStream<'_> { - fn diagnostic_parse<T>(self, diagnostics: &mut Vec<Diagnostic>) -> syn::Result<T> - where - T: DiagnosticParse, - { - T::diagnostic_parse(self, diagnostics) - } +pub fn name_to_string(name: TokenStream) -> String { + name.into_iter().map(|token| token.to_string()).collect() } diff --git a/helpers/pagetop-macros/src/maud/escape.rs b/helpers/pagetop-macros/src/maud/escape.rs index 786d8c77..49ece776 100644 --- a/helpers/pagetop-macros/src/maud/escape.rs +++ b/helpers/pagetop-macros/src/maud/escape.rs @@ -2,6 +2,10 @@ // !!!!!!!! PLEASE KEEP THIS IN SYNC WITH `maud/src/escape.rs` !!!!!!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +extern crate alloc; + +use alloc::string::String; + pub fn escape_to_string(input: &str, output: &mut String) { for b in input.bytes() { match b { @@ -16,7 +20,10 @@ pub fn escape_to_string(input: &str, output: &mut String) { #[cfg(test)] mod test { + extern crate alloc; + use super::escape_to_string; + use alloc::string::String; #[test] fn it_works() { diff --git a/helpers/pagetop-macros/src/maud/generate.rs b/helpers/pagetop-macros/src/maud/generate.rs index 19ff3d76..be7946d0 100644 --- a/helpers/pagetop-macros/src/maud/generate.rs +++ b/helpers/pagetop-macros/src/maud/generate.rs @@ -1,21 +1,23 @@ -use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, ToTokens}; -use syn::{parse_quote, token::Brace, Expr, Local}; +use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree}; +use proc_macro_error::SpanRange; +use quote::quote; use crate::maud::{ast::*, escape}; -pub fn generate(markups: Markups<Element>, output_ident: Ident) -> TokenStream { +use proc_macro_crate::{crate_name, FoundCrate}; + +pub fn generate(markups: Vec<Markup>, output_ident: TokenTree) -> TokenStream { let mut build = Builder::new(output_ident.clone()); Generator::new(output_ident).markups(markups, &mut build); build.finish() } struct Generator { - output_ident: Ident, + output_ident: TokenTree, } impl Generator { - fn new(output_ident: Ident) -> Generator { + fn new(output_ident: TokenTree) -> Generator { Generator { output_ident } } @@ -23,335 +25,257 @@ impl Generator { Builder::new(self.output_ident.clone()) } - fn markups<E: Into<Element>>(&self, markups: Markups<E>, build: &mut Builder) { - for markup in markups.markups { + fn markups(&self, markups: Vec<Markup>, build: &mut Builder) { + for markup in markups { self.markup(markup, build); } } - fn markup<E: Into<Element>>(&self, markup: Markup<E>, build: &mut Builder) { + fn markup(&self, markup: Markup, build: &mut Builder) { match markup { - Markup::Block(block) => { - if block.markups.markups.iter().any(|markup| { - matches!( - *markup, - Markup::ControlFlow(ControlFlow { - kind: ControlFlowKind::Let(_), - .. - }) - ) - }) { - self.block(block, build); + Markup::ParseError { .. } => {} + Markup::Block(Block { + markups, + outer_span, + }) => { + if markups + .iter() + .any(|markup| matches!(*markup, Markup::Let { .. })) + { + self.block( + Block { + markups, + outer_span, + }, + build, + ); } else { - self.markups(block.markups, build); + self.markups(markups, build); } } - Markup::Lit(lit) => build.push_escaped(&lit.to_string()), + Markup::Literal { content, .. } => build.push_escaped(&content), + Markup::Symbol { symbol } => self.name(symbol, build), Markup::Splice { expr, .. } => self.splice(expr, build), - Markup::Element(element) => self.element(element.into(), build), - Markup::ControlFlow(control_flow) => self.control_flow(control_flow, build), - Markup::Semi(_) => {} + Markup::Element { name, attrs, body } => self.element(name, attrs, body, build), + Markup::Let { tokens, .. } => build.push_tokens(tokens), + Markup::Special { segments } => { + for Special { head, body, .. } in segments { + build.push_tokens(head); + self.block(body, build); + } + } + Markup::Match { + head, + arms, + arms_span, + .. + } => { + let body = { + let mut build = self.builder(); + for MatchArm { head, body } in arms { + build.push_tokens(head); + self.block(body, &mut build); + } + build.finish() + }; + let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body)); + body.set_span(arms_span.collapse()); + build.push_tokens(quote!(#head #body)); + } } } - fn block<E: Into<Element>>(&self, block: Block<E>, build: &mut Builder) { - let markups = { + fn block( + &self, + Block { + markups, + outer_span, + }: Block, + build: &mut Builder, + ) { + let block = { let mut build = self.builder(); - self.markups(block.markups, &mut build); + self.markups(markups, &mut build); build.finish() }; - - build.push_tokens(quote!({ #markups })); + let mut block = TokenTree::Group(Group::new(Delimiter::Brace, block)); + block.set_span(outer_span.collapse()); + build.push_tokens(TokenStream::from(block)); } - fn splice(&self, expr: Expr, build: &mut Builder) { - let output_ident = &self.output_ident; - build.push_tokens( - quote!(pagetop::html::html_private::render_to!(&(#expr), &mut #output_ident);), - ); + fn splice(&self, expr: TokenStream, build: &mut Builder) { + let output_ident = self.output_ident.clone(); + + let found_crate = crate_name("pagetop").expect("pagetop is present in `Cargo.toml`"); + build.push_tokens(match found_crate { + FoundCrate::Itself => quote!( + crate::html::html_private::render_to!(&#expr, &mut #output_ident); + ), + _ => quote!( + pagetop::html::html_private::render_to!(&#expr, &mut #output_ident); + ), + }); } - fn element(&self, element: Element, build: &mut Builder) { - let element_name = element.name.clone().unwrap_or_else(|| parse_quote!(div)); + fn element(&self, name: TokenStream, attrs: Vec<Attr>, body: ElementBody, build: &mut Builder) { build.push_str("<"); - self.name(element_name.clone(), build); - self.attrs(element.attrs, build); + self.name(name.clone(), build); + self.attrs(attrs, build); build.push_str(">"); - if let ElementBody::Block(block) = element.body { + if let ElementBody::Block { block } = body { self.markups(block.markups, build); build.push_str("</"); - self.name(element_name, build); + self.name(name, build); build.push_str(">"); } } - fn name(&self, name: HtmlName, build: &mut Builder) { - build.push_escaped(&name.to_string()); + fn name(&self, name: TokenStream, build: &mut Builder) { + build.push_escaped(&name_to_string(name)); } - fn name_or_markup(&self, name: HtmlNameOrMarkup, build: &mut Builder) { - match name { - HtmlNameOrMarkup::HtmlName(name) => self.name(name, build), - HtmlNameOrMarkup::Markup(markup) => self.markup(markup, build), - } - } - - fn attr(&self, name: HtmlName, value: AttributeType, build: &mut Builder) { - match value { - AttributeType::Normal { value, .. } => { - build.push_str(" "); - self.name(name, build); - build.push_str("=\""); - self.markup(value, build); - build.push_str("\""); - } - AttributeType::Optional { - toggler: Toggler { cond, .. }, - .. - } => { - let inner_value: Expr = parse_quote!(inner_value); - - let body = { - let mut build = self.builder(); + fn attrs(&self, attrs: Vec<Attr>, build: &mut Builder) { + for NamedAttr { name, attr_type } in desugar_attrs(attrs) { + match attr_type { + AttrType::Normal { value } => { build.push_str(" "); - self.name(name, &mut build); + self.name(name, build); build.push_str("=\""); - self.splice(inner_value.clone(), &mut build); + self.markup(value, build); build.push_str("\""); - build.finish() - }; - build.push_tokens(quote!(if let Some(#inner_value) = (#cond) { #body })); - } - AttributeType::Empty(None) => { - build.push_str(" "); - self.name(name, build); - } - AttributeType::Empty(Some(Toggler { cond, .. })) => { - let body = { - let mut build = self.builder(); + } + AttrType::Optional { + toggler: Toggler { cond, .. }, + } => { + let inner_value = quote!(inner_value); + let body = { + let mut build = self.builder(); + build.push_str(" "); + self.name(name, &mut build); + build.push_str("=\""); + self.splice(inner_value.clone(), &mut build); + build.push_str("\""); + build.finish() + }; + build.push_tokens(quote!(if let Some(#inner_value) = (#cond) { #body })); + } + AttrType::Empty { toggler: None } => { build.push_str(" "); - self.name(name, &mut build); - build.finish() - }; - build.push_tokens(quote!(if (#cond) { #body })); - } - } - } - - fn attrs(&self, attrs: Vec<Attribute>, build: &mut Builder) { - let (classes, id, named_attrs) = split_attrs(attrs); - - if !classes.is_empty() { - let mut toggle_class_exprs = vec![]; - - build.push_str(" "); - self.name(parse_quote!(class), build); - build.push_str("=\""); - for (i, (name, toggler)) in classes.into_iter().enumerate() { - if let Some(toggler) = toggler { - toggle_class_exprs.push((i > 0, name, toggler)); - } else { - if i > 0 { + self.name(name, build); + } + AttrType::Empty { + toggler: Some(Toggler { cond, .. }), + } => { + let body = { + let mut build = self.builder(); build.push_str(" "); - } - self.name_or_markup(name, build); + self.name(name, &mut build); + build.finish() + }; + build.push_tokens(quote!(if (#cond) { #body })); } } - - for (not_first, name, toggler) in toggle_class_exprs { - let body = { - let mut build = self.builder(); - if not_first { - build.push_str(" "); - } - self.name_or_markup(name, &mut build); - build.finish() - }; - build.push_tokens(quote!(if (#toggler) { #body })); - } - - build.push_str("\""); } - - if let Some(id) = id { - build.push_str(" "); - self.name(parse_quote!(id), build); - build.push_str("=\""); - self.name_or_markup(id, build); - build.push_str("\""); - } - - for (name, attr_type) in named_attrs { - self.attr(name, attr_type, build); - } - } - - fn control_flow<E: Into<Element>>(&self, control_flow: ControlFlow<E>, build: &mut Builder) { - match control_flow.kind { - ControlFlowKind::If(if_) => self.control_flow_if(if_, build), - ControlFlowKind::Let(let_) => self.control_flow_let(let_, build), - ControlFlowKind::For(for_) => self.control_flow_for(for_, build), - ControlFlowKind::While(while_) => self.control_flow_while(while_, build), - ControlFlowKind::Match(match_) => self.control_flow_match(match_, build), - } - } - - fn control_flow_if<E: Into<Element>>( - &self, - IfExpr { - if_token, - cond, - then_branch, - else_branch, - }: IfExpr<E>, - build: &mut Builder, - ) { - build.push_tokens(quote!(#if_token #cond)); - self.block(then_branch, build); - - if let Some((_, else_token, if_or_block)) = else_branch { - build.push_tokens(quote!(#else_token)); - self.control_flow_if_or_block(*if_or_block, build); - } - } - - fn control_flow_if_or_block<E: Into<Element>>( - &self, - if_or_block: IfOrBlock<E>, - build: &mut Builder, - ) { - match if_or_block { - IfOrBlock::If(if_) => self.control_flow_if(if_, build), - IfOrBlock::Block(block) => self.block(block, build), - } - } - - fn control_flow_let(&self, let_: Local, build: &mut Builder) { - build.push_tokens(let_.to_token_stream()); - } - - fn control_flow_for<E: Into<Element>>( - &self, - ForExpr { - for_token, - pat, - in_token, - expr, - body, - }: ForExpr<E>, - build: &mut Builder, - ) { - build.push_tokens(quote!(#for_token #pat #in_token (#expr))); - self.block(body, build); - } - - fn control_flow_while<E: Into<Element>>( - &self, - WhileExpr { - while_token, - cond, - body, - }: WhileExpr<E>, - build: &mut Builder, - ) { - build.push_tokens(quote!(#while_token #cond)); - self.block(body, build); - } - - fn control_flow_match<E: Into<Element>>( - &self, - MatchExpr { - match_token, - expr, - brace_token, - arms, - }: MatchExpr<E>, - build: &mut Builder, - ) { - let arms = { - let mut build = self.builder(); - for MatchArm { - pat, - guard, - fat_arrow_token, - body, - comma_token, - } in arms - { - build.push_tokens(quote!(#pat)); - if let Some((if_token, cond)) = guard { - build.push_tokens(quote!(#if_token #cond)); - } - build.push_tokens(quote!(#fat_arrow_token)); - self.block( - Block { - brace_token: Brace(Span::call_site()), - markups: Markups { - markups: vec![body], - }, - }, - &mut build, - ); - build.push_tokens(quote!(#comma_token)); - } - build.finish() - }; - - let mut arm_block = TokenStream::new(); - - brace_token.surround(&mut arm_block, |tokens| { - arms.to_tokens(tokens); - }); - - build.push_tokens(quote!(#match_token #expr #arm_block)); } } //////////////////////////////////////////////////////// -#[allow(clippy::type_complexity)] -fn split_attrs( - attrs: Vec<Attribute>, -) -> ( - Vec<(HtmlNameOrMarkup, Option<Expr>)>, - Option<HtmlNameOrMarkup>, - Vec<(HtmlName, AttributeType)>, -) { - let mut classes = vec![]; - let mut id = None; +fn desugar_attrs(attrs: Vec<Attr>) -> Vec<NamedAttr> { + let mut classes_static = vec![]; + let mut classes_toggled = vec![]; + let mut ids = vec![]; let mut named_attrs = vec![]; - for attr in attrs { match attr { - Attribute::Class { name, toggler, .. } => { - classes.push((name, toggler.map(|toggler| toggler.cond))) - } - Attribute::Id { name, .. } => id = Some(name), - Attribute::Named { name, attr_type } => named_attrs.push((name, attr_type)), + Attr::Class { + name, + toggler: Some(toggler), + .. + } => classes_toggled.push((name, toggler)), + Attr::Class { + name, + toggler: None, + .. + } => classes_static.push(name), + Attr::Id { name, .. } => ids.push(name), + Attr::Named { named_attr } => named_attrs.push(named_attr), } } + let classes = desugar_classes_or_ids("class", classes_static, classes_toggled); + let ids = desugar_classes_or_ids("id", ids, vec![]); + classes.into_iter().chain(ids).chain(named_attrs).collect() +} - (classes, id, named_attrs) +fn desugar_classes_or_ids( + attr_name: &'static str, + values_static: Vec<Markup>, + values_toggled: Vec<(Markup, Toggler)>, +) -> Option<NamedAttr> { + if values_static.is_empty() && values_toggled.is_empty() { + return None; + } + let mut markups = Vec::new(); + let mut leading_space = false; + for name in values_static { + markups.extend(prepend_leading_space(name, &mut leading_space)); + } + for (name, Toggler { cond, cond_span }) in values_toggled { + let body = Block { + markups: prepend_leading_space(name, &mut leading_space), + // TODO: is this correct? + outer_span: cond_span, + }; + markups.push(Markup::Special { + segments: vec![Special { + at_span: SpanRange::call_site(), + head: quote!(if (#cond)), + body, + }], + }); + } + Some(NamedAttr { + name: TokenStream::from(TokenTree::Ident(Ident::new(attr_name, Span::call_site()))), + attr_type: AttrType::Normal { + value: Markup::Block(Block { + markups, + outer_span: SpanRange::call_site(), + }), + }, + }) +} + +fn prepend_leading_space(name: Markup, leading_space: &mut bool) -> Vec<Markup> { + let mut markups = Vec::new(); + if *leading_space { + markups.push(Markup::Literal { + content: " ".to_owned(), + span: name.span(), + }); + } + *leading_space = true; + markups.push(name); + markups } //////////////////////////////////////////////////////// struct Builder { - output_ident: Ident, - tokens: TokenStream, + output_ident: TokenTree, + tokens: Vec<TokenTree>, tail: String, } impl Builder { - fn new(output_ident: Ident) -> Builder { + fn new(output_ident: TokenTree) -> Builder { Builder { output_ident, - tokens: TokenStream::new(), + tokens: Vec::new(), tail: String::new(), } } - fn push_str(&mut self, string: &'static str) { + fn push_str(&mut self, string: &str) { self.tail.push_str(string); } @@ -370,8 +294,8 @@ impl Builder { } let push_str_expr = { let output_ident = self.output_ident.clone(); - let tail = &self.tail; - quote!(#output_ident.push_str(#tail);) + let string = TokenTree::Literal(Literal::string(&self.tail)); + quote!(#output_ident.push_str(#string);) }; self.tail.clear(); self.tokens.extend(push_str_expr); @@ -379,6 +303,6 @@ impl Builder { fn finish(mut self) -> TokenStream { self.cut(); - self.tokens + self.tokens.into_iter().collect() } } diff --git a/helpers/pagetop-macros/src/maud/parse.rs b/helpers/pagetop-macros/src/maud/parse.rs new file mode 100644 index 00000000..d24cea6e --- /dev/null +++ b/helpers/pagetop-macros/src/maud/parse.rs @@ -0,0 +1,752 @@ +use proc_macro2::{Delimiter, Ident, Literal, Spacing, Span, TokenStream, TokenTree}; +use proc_macro_error::{abort, abort_call_site, emit_error, SpanRange}; +use std::collections::HashMap; + +use syn::Lit; + +use crate::maud::ast; + +pub fn parse(input: TokenStream) -> Vec<ast::Markup> { + Parser::new(input).markups() +} + +#[derive(Clone)] +struct Parser { + /// If we're inside an attribute, then this contains the attribute name. + current_attr: Option<String>, + input: <TokenStream as IntoIterator>::IntoIter, +} + +impl Iterator for Parser { + type Item = TokenTree; + + fn next(&mut self) -> Option<TokenTree> { + self.input.next() + } +} + +impl Parser { + fn new(input: TokenStream) -> Parser { + Parser { + current_attr: None, + input: input.into_iter(), + } + } + + fn with_input(&self, input: TokenStream) -> Parser { + Parser { + current_attr: self.current_attr.clone(), + input: input.into_iter(), + } + } + + /// Returns the next token in the stream without consuming it. + fn peek(&mut self) -> Option<TokenTree> { + self.clone().next() + } + + /// Returns the next two tokens in the stream without consuming them. + fn peek2(&mut self) -> Option<(TokenTree, Option<TokenTree>)> { + let mut clone = self.clone(); + clone.next().map(|first| (first, clone.next())) + } + + /// Advances the cursor by one step. + fn advance(&mut self) { + self.next(); + } + + /// Advances the cursor by two steps. + fn advance2(&mut self) { + self.next(); + self.next(); + } + + /// Parses multiple blocks of markup. + fn markups(&mut self) -> Vec<ast::Markup> { + let mut result = Vec::new(); + loop { + match self.peek2() { + None => break, + Some((TokenTree::Punct(ref punct), _)) if punct.as_char() == ';' => self.advance(), + Some((TokenTree::Punct(ref punct), Some(TokenTree::Ident(ref ident)))) + if punct.as_char() == '@' && *ident == "let" => + { + self.advance2(); + let keyword = TokenTree::Ident(ident.clone()); + result.push(self.let_expr(punct.span(), keyword)); + } + _ => result.push(self.markup()), + } + } + result + } + + /// Parses a single block of markup. + fn markup(&mut self) -> ast::Markup { + let token = match self.peek() { + Some(token) => token, + None => { + abort_call_site!("unexpected end of input"); + } + }; + let markup = match token { + // Literal + TokenTree::Literal(literal) => { + self.advance(); + self.literal(literal) + } + // Special form + TokenTree::Punct(ref punct) if punct.as_char() == '@' => { + self.advance(); + let at_span = punct.span(); + match self.next() { + Some(TokenTree::Ident(ident)) => { + let keyword = TokenTree::Ident(ident.clone()); + match ident.to_string().as_str() { + "if" => { + let mut segments = Vec::new(); + self.if_expr(at_span, vec![keyword], &mut segments); + ast::Markup::Special { segments } + } + "while" => self.while_expr(at_span, keyword), + "for" => self.for_expr(at_span, keyword), + "match" => self.match_expr(at_span, keyword), + "let" => { + let span = SpanRange { + first: at_span, + last: ident.span(), + }; + abort!(span, "`@let` only works inside a block"); + } + other => { + let span = SpanRange { + first: at_span, + last: ident.span(), + }; + abort!(span, "unknown keyword `@{}`", other); + } + } + } + _ => { + abort!(at_span, "expected keyword after `@`"); + } + } + } + // Element + TokenTree::Ident(ident) => { + let ident_string = ident.to_string(); + match ident_string.as_str() { + "if" | "while" | "for" | "match" | "let" => { + abort!( + ident, + "found keyword `{}`", ident_string; + help = "should this be a `@{}`?", ident_string + ); + } + "true" | "false" => { + if let Some(attr_name) = &self.current_attr { + emit_error!( + ident, + r#"attribute value must be a string"#; + help = "to declare an empty attribute, omit the equals sign: `{}`", + attr_name; + help = "to toggle the attribute, use square brackets: `{}[some_boolean_flag]`", + attr_name; + ); + return ast::Markup::ParseError { + span: SpanRange::single_span(ident.span()), + }; + } + } + _ => {} + } + + // `.try_namespaced_name()` should never fail as we've + // already seen an `Ident` + let name = self.try_namespaced_name().expect("identifier"); + self.element(name) + } + // Div element shorthand + TokenTree::Punct(ref punct) if punct.as_char() == '.' || punct.as_char() == '#' => { + let name = TokenTree::Ident(Ident::new("div", punct.span())); + self.element(name.into()) + } + // Splice + TokenTree::Group(ref group) if group.delimiter() == Delimiter::Parenthesis => { + self.advance(); + ast::Markup::Splice { + expr: group.stream(), + outer_span: SpanRange::single_span(group.span()), + } + } + // Block + TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => { + self.advance(); + ast::Markup::Block(self.block(group.stream(), SpanRange::single_span(group.span()))) + } + // ??? + token => { + abort!(token, "invalid syntax"); + } + }; + markup + } + + /// Parses a literal string. + fn literal(&mut self, literal: Literal) -> ast::Markup { + match Lit::new(literal.clone()) { + Lit::Str(lit_str) => { + return ast::Markup::Literal { + content: lit_str.value(), + span: SpanRange::single_span(literal.span()), + } + } + // Boolean literals are idents, so `Lit::Bool` is handled in + // `markup`, not here. + Lit::Int(..) | Lit::Float(..) => { + emit_error!(literal, r#"literal must be double-quoted: `"{}"`"#, literal); + } + Lit::Char(lit_char) => { + emit_error!( + literal, + r#"literal must be double-quoted: `"{}"`"#, + lit_char.value(), + ); + } + _ => { + emit_error!(literal, "expected string"); + } + } + ast::Markup::ParseError { + span: SpanRange::single_span(literal.span()), + } + } + + /// Parses an `@if` expression. + /// + /// The leading `@if` should already be consumed. + fn if_expr(&mut self, at_span: Span, prefix: Vec<TokenTree>, segments: &mut Vec<ast::Special>) { + let mut head = prefix; + let body = loop { + match self.next() { + Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { + break self.block(block.stream(), SpanRange::single_span(block.span())); + } + Some(token) => head.push(token), + None => { + let mut span = ast::span_tokens(head); + span.first = at_span; + abort!(span, "expected body for this `@if`"); + } + } + }; + segments.push(ast::Special { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + body, + }); + self.else_if_expr(segments) + } + + /// Parses an optional `@else if` or `@else`. + /// + /// The leading `@else if` or `@else` should *not* already be consumed. + fn else_if_expr(&mut self, segments: &mut Vec<ast::Special>) { + match self.peek2() { + Some((TokenTree::Punct(ref punct), Some(TokenTree::Ident(ref else_keyword)))) + if punct.as_char() == '@' && *else_keyword == "else" => + { + self.advance2(); + let at_span = punct.span(); + let else_keyword = TokenTree::Ident(else_keyword.clone()); + match self.peek() { + // `@else if` + Some(TokenTree::Ident(ref if_keyword)) if *if_keyword == "if" => { + self.advance(); + let if_keyword = TokenTree::Ident(if_keyword.clone()); + self.if_expr(at_span, vec![else_keyword, if_keyword], segments) + } + // Just an `@else` + _ => match self.next() { + Some(TokenTree::Group(ref group)) + if group.delimiter() == Delimiter::Brace => + { + let body = + self.block(group.stream(), SpanRange::single_span(group.span())); + segments.push(ast::Special { + at_span: SpanRange::single_span(at_span), + head: vec![else_keyword].into_iter().collect(), + body, + }); + } + _ => { + let span = SpanRange { + first: at_span, + last: else_keyword.span(), + }; + abort!(span, "expected body for this `@else`"); + } + }, + } + } + // We didn't find an `@else`; stop + _ => {} + } + } + + /// Parses an `@while` expression. + /// + /// The leading `@while` should already be consumed. + fn while_expr(&mut self, at_span: Span, keyword: TokenTree) -> ast::Markup { + let keyword_span = keyword.span(); + let mut head = vec![keyword]; + let body = loop { + match self.next() { + Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { + break self.block(block.stream(), SpanRange::single_span(block.span())); + } + Some(token) => head.push(token), + None => { + let span = SpanRange { + first: at_span, + last: keyword_span, + }; + abort!(span, "expected body for this `@while`"); + } + } + }; + ast::Markup::Special { + segments: vec![ast::Special { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + body, + }], + } + } + + /// Parses a `@for` expression. + /// + /// The leading `@for` should already be consumed. + fn for_expr(&mut self, at_span: Span, keyword: TokenTree) -> ast::Markup { + let keyword_span = keyword.span(); + let mut head = vec![keyword]; + loop { + match self.next() { + Some(TokenTree::Ident(ref in_keyword)) if *in_keyword == "in" => { + head.push(TokenTree::Ident(in_keyword.clone())); + break; + } + Some(token) => head.push(token), + None => { + let span = SpanRange { + first: at_span, + last: keyword_span, + }; + abort!(span, "missing `in` in `@for` loop"); + } + } + } + let body = loop { + match self.next() { + Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { + break self.block(block.stream(), SpanRange::single_span(block.span())); + } + Some(token) => head.push(token), + None => { + let span = SpanRange { + first: at_span, + last: keyword_span, + }; + abort!(span, "expected body for this `@for`"); + } + } + }; + ast::Markup::Special { + segments: vec![ast::Special { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + body, + }], + } + } + + /// Parses a `@match` expression. + /// + /// The leading `@match` should already be consumed. + fn match_expr(&mut self, at_span: Span, keyword: TokenTree) -> ast::Markup { + let keyword_span = keyword.span(); + let mut head = vec![keyword]; + let (arms, arms_span) = loop { + match self.next() { + Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => { + let span = SpanRange::single_span(body.span()); + break (self.with_input(body.stream()).match_arms(), span); + } + Some(token) => head.push(token), + None => { + let span = SpanRange { + first: at_span, + last: keyword_span, + }; + abort!(span, "expected body for this `@match`"); + } + } + }; + ast::Markup::Match { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + arms, + arms_span, + } + } + + fn match_arms(&mut self) -> Vec<ast::MatchArm> { + let mut arms = Vec::new(); + while let Some(arm) = self.match_arm() { + arms.push(arm); + } + arms + } + + fn match_arm(&mut self) -> Option<ast::MatchArm> { + let mut head = Vec::new(); + loop { + match self.peek2() { + Some((TokenTree::Punct(ref eq), Some(TokenTree::Punct(ref gt)))) + if eq.as_char() == '=' + && gt.as_char() == '>' + && eq.spacing() == Spacing::Joint => + { + self.advance2(); + head.push(TokenTree::Punct(eq.clone())); + head.push(TokenTree::Punct(gt.clone())); + break; + } + Some((token, _)) => { + self.advance(); + head.push(token); + } + None => { + if head.is_empty() { + return None; + } else { + let head_span = ast::span_tokens(head); + abort!(head_span, "unexpected end of @match pattern"); + } + } + } + } + let body = match self.next() { + // $pat => { $stmts } + Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => { + let body = self.block(body.stream(), SpanRange::single_span(body.span())); + // Trailing commas are optional if the match arm is a braced block + if let Some(TokenTree::Punct(ref punct)) = self.peek() { + if punct.as_char() == ',' { + self.advance(); + } + } + body + } + // $pat => $expr + Some(first_token) => { + let mut span = SpanRange::single_span(first_token.span()); + let mut body = vec![first_token]; + loop { + match self.next() { + Some(TokenTree::Punct(ref punct)) if punct.as_char() == ',' => break, + Some(token) => { + span.last = token.span(); + body.push(token); + } + None => break, + } + } + self.block(body.into_iter().collect(), span) + } + None => { + let span = ast::span_tokens(head); + abort!(span, "unexpected end of @match arm"); + } + }; + Some(ast::MatchArm { + head: head.into_iter().collect(), + body, + }) + } + + /// Parses a `@let` expression. + /// + /// The leading `@let` should already be consumed. + fn let_expr(&mut self, at_span: Span, keyword: TokenTree) -> ast::Markup { + let mut tokens = vec![keyword]; + loop { + match self.next() { + Some(token) => match token { + TokenTree::Punct(ref punct) if punct.as_char() == '=' => { + tokens.push(token.clone()); + break; + } + _ => tokens.push(token), + }, + None => { + let mut span = ast::span_tokens(tokens); + span.first = at_span; + abort!(span, "unexpected end of `@let` expression"); + } + } + } + loop { + match self.next() { + Some(token) => match token { + TokenTree::Punct(ref punct) if punct.as_char() == ';' => { + tokens.push(token.clone()); + break; + } + _ => tokens.push(token), + }, + None => { + let mut span = ast::span_tokens(tokens); + span.first = at_span; + abort!( + span, + "unexpected end of `@let` expression"; + help = "are you missing a semicolon?" + ); + } + } + } + ast::Markup::Let { + at_span: SpanRange::single_span(at_span), + tokens: tokens.into_iter().collect(), + } + } + + /// Parses an element node. + /// + /// The element name should already be consumed. + fn element(&mut self, name: TokenStream) -> ast::Markup { + if self.current_attr.is_some() { + let span = ast::span_tokens(name); + abort!(span, "unexpected element"); + } + let attrs = self.attrs(); + let body = match self.peek() { + Some(TokenTree::Punct(ref punct)) + if punct.as_char() == ';' || punct.as_char() == '/' => + { + // Void element + self.advance(); + if punct.as_char() == '/' { + emit_error!( + punct, + "void elements must use `;`, not `/`"; + help = "change this to `;`"; + help = "see https://github.com/lambda-fairy/maud/pull/315 for details"; + ); + } + ast::ElementBody::Void { + semi_span: SpanRange::single_span(punct.span()), + } + } + Some(_) => match self.markup() { + ast::Markup::Block(block) => ast::ElementBody::Block { block }, + markup => { + let markup_span = markup.span(); + abort!( + markup_span, + "element body must be wrapped in braces"; + help = "see https://github.com/lambda-fairy/maud/pull/137 for details" + ); + } + }, + None => abort_call_site!("expected `;`, found end of macro"), + }; + ast::Markup::Element { name, attrs, body } + } + + /// Parses the attributes of an element. + fn attrs(&mut self) -> Vec<ast::Attr> { + let mut attrs = Vec::new(); + loop { + if let Some(name) = self.try_namespaced_name() { + // Attribute + match self.peek() { + // Non-empty attribute + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '=' => { + self.advance(); + // Parse a value under an attribute context + assert!(self.current_attr.is_none()); + self.current_attr = Some(ast::name_to_string(name.clone())); + let attr_type = match self.attr_toggler() { + Some(toggler) => ast::AttrType::Optional { toggler }, + None => { + let value = self.markup(); + ast::AttrType::Normal { value } + } + }; + self.current_attr = None; + attrs.push(ast::Attr::Named { + named_attr: ast::NamedAttr { name, attr_type }, + }); + } + // Empty attribute (legacy syntax) + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '?' => { + self.advance(); + let toggler = self.attr_toggler(); + attrs.push(ast::Attr::Named { + named_attr: ast::NamedAttr { + name: name.clone(), + attr_type: ast::AttrType::Empty { toggler }, + }, + }); + } + // Empty attribute (new syntax) + _ => { + let toggler = self.attr_toggler(); + attrs.push(ast::Attr::Named { + named_attr: ast::NamedAttr { + name: name.clone(), + attr_type: ast::AttrType::Empty { toggler }, + }, + }); + } + } + } else { + match self.peek() { + // Class shorthand + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '.' => { + self.advance(); + let name = self.class_or_id_name(); + let toggler = self.attr_toggler(); + attrs.push(ast::Attr::Class { + dot_span: SpanRange::single_span(punct.span()), + name, + toggler, + }); + } + // ID shorthand + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '#' => { + self.advance(); + let name = self.class_or_id_name(); + attrs.push(ast::Attr::Id { + hash_span: SpanRange::single_span(punct.span()), + name, + }); + } + // If it's not a valid attribute, backtrack and bail out + _ => break, + } + } + } + + let mut attr_map: HashMap<String, Vec<SpanRange>> = HashMap::new(); + let mut has_class = false; + for attr in &attrs { + let name = match attr { + ast::Attr::Class { .. } => { + if has_class { + // Only check the first class to avoid spurious duplicates + continue; + } + has_class = true; + "class".to_string() + } + ast::Attr::Id { .. } => "id".to_string(), + ast::Attr::Named { named_attr } => named_attr + .name + .clone() + .into_iter() + .map(|token| token.to_string()) + .collect(), + }; + let entry = attr_map.entry(name).or_default(); + entry.push(attr.span()); + } + + for (name, spans) in attr_map { + if spans.len() > 1 { + let mut spans = spans.into_iter(); + let first_span = spans.next().expect("spans should be non-empty"); + abort!(first_span, "duplicate attribute `{}`", name); + } + } + + attrs + } + + /// Parses the name of a class or ID. + fn class_or_id_name(&mut self) -> ast::Markup { + if let Some(symbol) = self.try_name() { + ast::Markup::Symbol { symbol } + } else { + self.markup() + } + } + + /// Parses the `[cond]` syntax after an empty attribute or class shorthand. + fn attr_toggler(&mut self) -> Option<ast::Toggler> { + match self.peek() { + Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Bracket => { + self.advance(); + Some(ast::Toggler { + cond: group.stream(), + cond_span: SpanRange::single_span(group.span()), + }) + } + _ => None, + } + } + + /// Parses an identifier, without dealing with namespaces. + fn try_name(&mut self) -> Option<TokenStream> { + let mut result = Vec::new(); + if let Some(token @ TokenTree::Ident(_)) = self.peek() { + self.advance(); + result.push(token); + } else { + return None; + } + let mut expect_ident = false; + loop { + expect_ident = match self.peek() { + Some(TokenTree::Punct(ref punct)) if punct.as_char() == '-' => { + self.advance(); + result.push(TokenTree::Punct(punct.clone())); + true + } + Some(TokenTree::Ident(ref ident)) if expect_ident => { + self.advance(); + result.push(TokenTree::Ident(ident.clone())); + false + } + _ => break, + }; + } + Some(result.into_iter().collect()) + } + + /// Parses a HTML element or attribute name, along with a namespace + /// if necessary. + fn try_namespaced_name(&mut self) -> Option<TokenStream> { + let mut result = vec![self.try_name()?]; + if let Some(TokenTree::Punct(ref punct)) = self.peek() { + if punct.as_char() == ':' { + self.advance(); + result.push(TokenStream::from(TokenTree::Punct(punct.clone()))); + result.push(self.try_name()?); + } + } + Some(result.into_iter().collect()) + } + + /// Parses the given token stream as a Maud expression. + fn block(&mut self, body: TokenStream, outer_span: SpanRange) -> ast::Block { + let markups = self.with_input(body).markups(); + ast::Block { + markups, + outer_span, + } + } +} diff --git a/helpers/pagetop-macros/src/smart_default/body_impl.rs b/helpers/pagetop-macros/src/smart_default/body_impl.rs index f7d59bb5..6a76f904 100644 --- a/helpers/pagetop-macros/src/smart_default/body_impl.rs +++ b/helpers/pagetop-macros/src/smart_default/body_impl.rs @@ -19,7 +19,7 @@ pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> { quote! { #name #body_assignment }, - format!("Returns a `{name}` default."), + format!("Returns a `{}` default.", name), ) } syn::Data::Enum(ref body) => { @@ -44,7 +44,7 @@ pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> { quote! { #name :: #default_variant_name #body_assignment }, - format!("Returns a `{name}::{default_variant_name}` default."), + format!("Returns a `{}::{}` default.", name, default_variant_name), ) } syn::Data::Union(_) => { @@ -109,7 +109,7 @@ fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> { .iter() .map(|field| { let (default_value, default_doc) = field_default_expr_and_doc(field)?; - write!(&mut doc, "{default_doc}, ").unwrap(); + write!(&mut doc, "{}, ", default_doc).unwrap(); Ok(default_value) }) .collect::<Result<Vec<TokenStream>, Error>>()?; @@ -145,7 +145,7 @@ fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String ConversionStrategy::Into => quote!((#field_value).into()), }; - let field_doc = format!("{field_value}"); + let field_doc = format!("{}", field_value); Ok((field_value, field_doc)) } else { Ok(( diff --git a/helpers/pagetop-statics/CHANGELOG.md b/helpers/pagetop-statics/CHANGELOG.md deleted file mode 100644 index 0d6f5b1b..00000000 --- a/helpers/pagetop-statics/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# CHANGELOG - -Este archivo documenta los cambios más relevantes realizados en cada versión. El formato está basado -en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/), y las versiones se numeran siguiendo -las reglas del [Versionado Semántico](https://semver.org/lang/es/). - -Resume la evolución del proyecto para usuarios y colaboradores, destacando nuevas funcionalidades, -correcciones, mejoras durante el desarrollo o cambios en la documentación. Cambios menores o -internos pueden omitirse si no afectan al uso del proyecto. - -## 0.1.2 (2025-09-20) - -### Dependencias - -- Actualiza dependencias para 0.4.0 - -### Documentado - -- Normaliza referencias al nombre PageTop - -## 0.1.1 (2025-08-16) - -### Documentado - -- Cambia el formato para la documentación - -## 0.1.0 (2025-08-09) - -### Añadido - -- Versión inicial diff --git a/helpers/pagetop-statics/Cargo.toml b/helpers/pagetop-statics/Cargo.toml deleted file mode 100644 index 0172a190..00000000 --- a/helpers/pagetop-statics/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "pagetop-statics" -version = "0.1.2" -edition = "2021" - -description = """ - Librería para automatizar la recopilación de recursos estáticos en PageTop. -""" -categories = ["development-tools::build-utils"] -keywords = ["pagetop", "build", "static", "resources", "file"] - -repository.workspace = true -homepage.workspace = true -license.workspace = true -authors.workspace = true - -[features] -default = ["change-detection"] -sort = [] - -[dependencies] -change-detection = { version = "1.2", optional = true } -mime_guess = "2.0" -path-slash = "0.2" - -actix-web.workspace = true -derive_more = "0.99.17" -futures-util = { version = "0.3", default-features = false, features = ["std"] } - -[build-dependencies] -change-detection = { version = "1.2", optional = true } -mime_guess = "2.0" -path-slash = "0.2" diff --git a/helpers/pagetop-statics/LICENSE-APACHE b/helpers/pagetop-statics/LICENSE-APACHE deleted file mode 100644 index 263ddac1..00000000 --- a/helpers/pagetop-statics/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Manuel Cillero - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/helpers/pagetop-statics/LICENSE-MIT b/helpers/pagetop-statics/LICENSE-MIT deleted file mode 100644 index cd8af3d6..00000000 --- a/helpers/pagetop-statics/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Manuel Cillero - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/helpers/pagetop-statics/README.md b/helpers/pagetop-statics/README.md deleted file mode 100644 index cd1da6ac..00000000 --- a/helpers/pagetop-statics/README.md +++ /dev/null @@ -1,56 +0,0 @@ -<div align="center"> - -<h1>PageTop Statics</h1> - -<p>Librería para automatizar la recopilación de recursos estáticos en <strong>PageTop</strong>.</p> - -[![Doc API](https://img.shields.io/docsrs/pagetop-statics?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-statics) -[![Crates.io](https://img.shields.io/crates/v/pagetop-statics.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-statics) -[![Descargas](https://img.shields.io/crates/d/pagetop-statics.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-statics) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-statics#licencia) - -</div> - -## 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. - -## Descripción general - -Esta librería permite incluir archivos estáticos en el ejecutable de las aplicaciones PageTop para -servirlos de forma eficiente vía web, con detección de cambios que optimizan el tiempo de -compilación. - -## Créditos - -Para ello, adapta el código de los *crates* [static-files](https://crates.io/crates/static_files) -(versión [0.2.5](https://github.com/static-files-rs/static-files/tree/v0.2.5)) y -[actix-web-static-files](https://crates.io/crates/actix_web_static_files) (versión -[4.0.1](https://github.com/kilork/actix-web-static-files/tree/v4.0.1)), desarrollados ambos por -[Alexander Korolev](https://crates.io/users/kilork). - -Estas implementaciones se integran en PageTop para evitar que cada proyecto tenga que declarar -`static-files` manualmente como dependencia en su `Cargo.toml`. - - -# 🚧 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/helpers/pagetop-statics/build.rs b/helpers/pagetop-statics/build.rs deleted file mode 100644 index fcd009c9..00000000 --- a/helpers/pagetop-statics/build.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![allow(dead_code)] -#![doc(html_no_source)] -#![allow(clippy::needless_doctest_main)] - -mod resource { - include!("src/resource.rs"); -} -use resource::generate_resources_mapping; -mod resource_dir { - include!("src/resource_dir.rs"); -} -use resource_dir::resource_dir; -mod sets { - include!("src/sets.rs"); -} -use sets::{generate_resources_sets, SplitByCount}; - -use std::{env, path::Path}; - -fn main() -> std::io::Result<()> { - resource_dir("./tests").build_test()?; - - let out_dir = env::var("OUT_DIR").unwrap(); - - generate_resources_mapping( - "./tests", - None, - Path::new(&out_dir).join("generated_mapping.rs"), - "pagetop_statics", - )?; - - generate_resources_sets( - "./tests", - None, - Path::new(&out_dir).join("generated_sets.rs"), - "sets", - "generate", - &mut SplitByCount::new(2), - "pagetop_statics", - )?; - - Ok(()) -} diff --git a/helpers/pagetop-statics/src/lib.rs b/helpers/pagetop-statics/src/lib.rs deleted file mode 100644 index 6f04bd2a..00000000 --- a/helpers/pagetop-statics/src/lib.rs +++ /dev/null @@ -1,56 +0,0 @@ -/*! -<div align="center"> - -<h1>PageTop Statics</h1> - -<p>Librería para automatizar la recopilación de recursos estáticos en <strong>PageTop</strong>.</p> - -[![Doc API](https://img.shields.io/docsrs/pagetop-statics?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-statics) -[![Crates.io](https://img.shields.io/crates/v/pagetop-statics.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-statics) -[![Descargas](https://img.shields.io/crates/d/pagetop-statics.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop-statics) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-statics#licencia) - -</div> - -## 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. - -## Descripción general - -Esta librería permite incluir archivos estáticos en el ejecutable de las aplicaciones PageTop para -servirlos de forma eficiente vía web, con detección de cambios que optimizan el tiempo de -compilación. - -## Créditos - -Para ello, adapta el código de los *crates* [static-files](https://crates.io/crates/static_files) -(versión [0.2.5](https://github.com/static-files-rs/static-files/tree/v0.2.5)) y -[actix-web-static-files](https://crates.io/crates/actix_web_static_files) (versión -[4.0.1](https://github.com/kilork/actix-web-static-files/tree/v4.0.1)), desarrollados ambos por -[Alexander Korolev](https://crates.io/users/kilork). - -Estas implementaciones se integran en PageTop para evitar que cada proyecto tenga que declarar -`static-files` manualmente como dependencia en su `Cargo.toml`. -*/ - -#![doc(test(no_crate_inject))] -#![doc( - html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" -)] -#![allow(clippy::needless_doctest_main)] - -/// Resource definition and single module based generation. -pub mod resource; -pub use resource::Resource as StaticResource; - -mod resource_dir; -pub use resource_dir::{resource_dir, ResourceDir}; - -mod resource_files; -pub use resource_files::{ResourceFiles, UriSegmentError}; - -/// Support for module based generations. Use it for large data sets (more than 128 Mb). -pub mod sets; diff --git a/helpers/pagetop-statics/src/resource.rs b/helpers/pagetop-statics/src/resource.rs deleted file mode 100644 index 0b81969e..00000000 --- a/helpers/pagetop-statics/src/resource.rs +++ /dev/null @@ -1,249 +0,0 @@ -use path_slash::PathExt; -use std::{ - fs::{self, File, Metadata}, - io::{self, Write}, - path::{Path, PathBuf}, - time::SystemTime, -}; - -/// Static files resource. -pub struct Resource { - pub data: &'static [u8], - pub modified: u64, - pub mime_type: &'static str, -} - -/// Used internally in generated functions. -#[inline] -pub fn new_resource(data: &'static [u8], modified: u64, mime_type: &'static str) -> Resource { - Resource { - data, - modified, - mime_type, - } -} - -pub(crate) const DEFAULT_VARIABLE_NAME: &str = "r"; - -/// Generate resources for `project_dir` using `filter`. -/// Result saved in `generated_filename` and function named as `fn_name`. -/// -/// in `build.rs`: -/// ```rust -/// use std::{env, path::Path}; -/// use pagetop_statics::resource::generate_resources; -/// -/// fn main() { -/// let out_dir = env::var("OUT_DIR").unwrap(); -/// let generated_filename = Path::new(&out_dir).join("generated.rs"); -/// generate_resources("./tests", None, generated_filename, "generate", "pagetop_statics").unwrap(); -/// } -/// ``` -/// -/// in `main.rs`: -/// ```rust -/// include!(concat!(env!("OUT_DIR"), "/generated.rs")); -/// -/// fn main() { -/// let generated_file = generate(); -/// -/// assert_eq!(generated_file.len(), 4); -/// } -/// ``` -pub fn generate_resources<P: AsRef<Path>, G: AsRef<Path>>( - project_dir: P, - filter: Option<fn(p: &Path) -> bool>, - generated_filename: G, - fn_name: &str, - crate_name: &str, -) -> io::Result<()> { - let resources = collect_resources(&project_dir, filter)?; - - let mut f = File::create(&generated_filename)?; - - generate_function_header(&mut f, fn_name, crate_name)?; - generate_uses(&mut f, crate_name)?; - - generate_variable_header(&mut f, DEFAULT_VARIABLE_NAME)?; - generate_resource_inserts(&mut f, &project_dir, DEFAULT_VARIABLE_NAME, resources)?; - generate_variable_return(&mut f, DEFAULT_VARIABLE_NAME)?; - - generate_function_end(&mut f)?; - - Ok(()) -} - -/// Generate resource mapping for `project_dir` using `filter`. -/// Result saved in `generated_filename` as anonymous block which returns HashMap<&'static str, Resource>. -/// -/// in `build.rs`: -/// ```rust -/// -/// use std::{env, path::Path}; -/// use pagetop_statics::resource::generate_resources_mapping; -/// -/// fn main() { -/// let out_dir = env::var("OUT_DIR").unwrap(); -/// let generated_filename = Path::new(&out_dir).join("generated_mapping.rs"); -/// generate_resources_mapping("./tests", None, generated_filename, "pagetop_statics").unwrap(); -/// } -/// ``` -/// -/// in `main.rs`: -/// ```rust -/// use std::collections::HashMap; -/// -/// use pagetop_statics::StaticResource; -/// -/// fn generate_mapping() -> HashMap<&'static str, StaticResource> { -/// include!(concat!(env!("OUT_DIR"), "/generated_mapping.rs")) -/// } -/// -/// fn main() { -/// let generated_file = generate_mapping(); -/// -/// assert_eq!(generated_file.len(), 4); -/// -/// } -/// ``` -pub fn generate_resources_mapping<P: AsRef<Path>, G: AsRef<Path>>( - project_dir: P, - filter: Option<fn(p: &Path) -> bool>, - generated_filename: G, - crate_name: &str, -) -> io::Result<()> { - let resources = collect_resources(&project_dir, filter)?; - - let mut f = File::create(&generated_filename)?; - writeln!(f, "{{")?; - - generate_uses(&mut f, crate_name)?; - - generate_variable_header(&mut f, DEFAULT_VARIABLE_NAME)?; - - generate_resource_inserts(&mut f, &project_dir, DEFAULT_VARIABLE_NAME, resources)?; - - generate_variable_return(&mut f, DEFAULT_VARIABLE_NAME)?; - - writeln!(f, "}}")?; - Ok(()) -} - -#[cfg(not(feature = "sort"))] -pub(crate) fn collect_resources<P: AsRef<Path>>( - path: P, - filter: Option<fn(p: &Path) -> bool>, -) -> io::Result<Vec<(PathBuf, Metadata)>> { - collect_resources_nested(path, filter) -} - -#[cfg(feature = "sort")] -pub(crate) fn collect_resources<P: AsRef<Path>>( - path: P, - filter: Option<fn(p: &Path) -> bool>, -) -> io::Result<Vec<(PathBuf, Metadata)>> { - let mut resources = collect_resources_nested(path, filter)?; - resources.sort_by(|a, b| a.0.cmp(&b.0)); - Ok(resources) -} - -#[inline] -fn collect_resources_nested<P: AsRef<Path>>( - path: P, - filter: Option<fn(p: &Path) -> bool>, -) -> io::Result<Vec<(PathBuf, Metadata)>> { - let mut result = vec![]; - - for entry in fs::read_dir(&path)? { - let entry = entry?; - let path = entry.path(); - - if let Some(ref filter) = filter { - if !filter(path.as_ref()) { - continue; - } - } - - if path.is_dir() { - let nested = collect_resources(path, filter)?; - result.extend(nested); - } else { - result.push((path, entry.metadata()?)); - } - } - - Ok(result) -} - -pub(crate) fn generate_resource_inserts<P: AsRef<Path>, W: Write>( - f: &mut W, - project_dir: &P, - variable_name: &str, - resources: Vec<(PathBuf, Metadata)>, -) -> io::Result<()> { - for resource in &resources { - generate_resource_insert(f, project_dir, variable_name, resource)?; - } - Ok(()) -} - -#[allow(clippy::unnecessary_debug_formatting)] -pub(crate) fn generate_resource_insert<P: AsRef<Path>, W: Write>( - f: &mut W, - project_dir: &P, - variable_name: &str, - resource: &(PathBuf, Metadata), -) -> io::Result<()> { - let (path, metadata) = resource; - let abs_path = path.canonicalize()?; - let key_path = path.strip_prefix(project_dir).unwrap().to_slash().unwrap(); - - let modified = if let Ok(Ok(modified)) = metadata - .modified() - .map(|x| x.duration_since(SystemTime::UNIX_EPOCH)) - { - modified.as_secs() - } else { - 0 - }; - let mime_type = mime_guess::MimeGuess::from_path(path).first_or_octet_stream(); - writeln!( - f, - "{}.insert({:?},n(i!({:?}),{:?},{:?}));", - variable_name, &key_path, &abs_path, modified, &mime_type, - ) -} - -pub(crate) fn generate_function_header<F: Write>( - f: &mut F, - fn_name: &str, - crate_name: &str, -) -> io::Result<()> { - writeln!( - f, - "#[allow(clippy::unreadable_literal)] pub fn {fn_name}() -> ::std::collections::HashMap<&'static str, ::{crate_name}::StaticResource> {{", - ) -} - -pub(crate) fn generate_function_end<F: Write>(f: &mut F) -> io::Result<()> { - writeln!(f, "}}") -} - -pub(crate) fn generate_uses<F: Write>(f: &mut F, crate_name: &str) -> io::Result<()> { - writeln!( - f, - "use ::{crate_name}::resource::new_resource as n; -use ::std::include_bytes as i;", - ) -} - -pub(crate) fn generate_variable_header<F: Write>(f: &mut F, variable_name: &str) -> io::Result<()> { - writeln!( - f, - "let mut {variable_name} = ::std::collections::HashMap::new();", - ) -} - -pub(crate) fn generate_variable_return<F: Write>(f: &mut F, variable_name: &str) -> io::Result<()> { - writeln!(f, "{variable_name}") -} diff --git a/helpers/pagetop-statics/src/resource_dir.rs b/helpers/pagetop-statics/src/resource_dir.rs deleted file mode 100644 index 805e1ed4..00000000 --- a/helpers/pagetop-statics/src/resource_dir.rs +++ /dev/null @@ -1,118 +0,0 @@ -use super::sets::{generate_resources_sets, SplitByCount}; -use std::{ - env, io, - path::{Path, PathBuf}, -}; - -/// Generate resources for `resource_dir`. -/// -/// ```rust,no_run -/// // Generate resources for ./tests dir with file name generated.rs -/// // stored in path defined by OUT_DIR environment variable. -/// // Function name is 'generate' -/// use pagetop_statics::resource_dir; -/// -/// resource_dir("./tests").build().unwrap(); -/// ``` -pub fn resource_dir<P: AsRef<Path>>(resource_dir: P) -> ResourceDir { - ResourceDir { - resource_dir: resource_dir.as_ref().into(), - ..Default::default() - } -} - -/// Resource dir. -/// -/// A builder structure allows to change default settings for: -/// - file filter -/// - generated file name -/// - generated function name -#[derive(Default)] -pub struct ResourceDir { - pub(crate) resource_dir: PathBuf, - pub(crate) filter: Option<fn(p: &Path) -> bool>, - pub(crate) generated_filename: Option<PathBuf>, - pub(crate) generated_fn: Option<String>, - pub(crate) module_name: Option<String>, - pub(crate) count_per_module: Option<usize>, -} - -pub const DEFAULT_MODULE_NAME: &str = "sets"; -pub const DEFAULT_COUNT_PER_MODULE: usize = 256; - -impl ResourceDir { - /// Generates resources for current configuration. - pub fn build(self) -> io::Result<()> { - self.internal_build("pagetop") - } - - /// Generates resources for testing current configuration. - #[allow(dead_code)] - pub(crate) fn build_test(self) -> io::Result<()> { - self.internal_build("pagetop_statics") - } - - fn internal_build(self, crate_name: &str) -> io::Result<()> { - let generated_filename = self.generated_filename.unwrap_or_else(|| { - let out_dir = env::var("OUT_DIR").unwrap(); - - Path::new(&out_dir).join("generated.rs") - }); - let generated_fn = self.generated_fn.unwrap_or_else(|| "generate".into()); - - let module_name = self - .module_name - .unwrap_or_else(|| format!("{}_{}", &generated_fn, DEFAULT_MODULE_NAME)); - - let count_per_module = self.count_per_module.unwrap_or(DEFAULT_COUNT_PER_MODULE); - - generate_resources_sets( - &self.resource_dir, - self.filter, - &generated_filename, - module_name.as_str(), - &generated_fn, - &mut SplitByCount::new(count_per_module), - crate_name, - ) - } - - /// Sets the file filter. - pub fn with_filter(&mut self, filter: fn(p: &Path) -> bool) -> &mut Self { - self.filter = Some(filter); - self - } - - /// Sets the generated filename. - pub fn with_generated_filename<P: AsRef<Path>>(&mut self, generated_filename: P) -> &mut Self { - self.generated_filename = Some(generated_filename.as_ref().into()); - self - } - - /// Sets the generated function name. - pub fn with_generated_fn<S>(&mut self, generated_fn: S) -> &mut Self - where - S: Into<String>, - { - self.generated_fn = Some(generated_fn.into()); - self - } - - /// Sets the generated module name. - /// - /// Default value is based on generated function name and the suffix "sets". - /// Generated module would be overriden by each call. - pub fn with_module_name<S>(&mut self, module_name: S) -> &mut Self - where - S: Into<String>, - { - self.module_name = Some(module_name.into()); - self - } - - /// Sets maximal count of files per module. - pub fn with_count_per_module(&mut self, count_per_module: usize) -> &mut Self { - self.count_per_module = Some(count_per_module); - self - } -} diff --git a/helpers/pagetop-statics/src/resource_files.rs b/helpers/pagetop-statics/src/resource_files.rs deleted file mode 100644 index b487bca9..00000000 --- a/helpers/pagetop-statics/src/resource_files.rs +++ /dev/null @@ -1,396 +0,0 @@ -use super::resource::Resource; -use actix_web::{ - dev::{ - always_ready, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory, - ServiceRequest, ServiceResponse, - }, - error::Error, - guard::{Guard, GuardContext}, - http::{ - header::{self, ContentType}, - Method, StatusCode, - }, - HttpMessage, HttpRequest, HttpResponse, ResponseError, -}; -use derive_more::{Deref, Display, Error}; -use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use std::{collections::HashMap, ops::Deref, rc::Rc}; - -/// Static resource files handling -/// -/// `ResourceFiles` service must be registered with `App::service` method. -/// -/// ```rust -/// use std::collections::HashMap; -/// -/// use actix_web::App; -/// -/// fn main() { -/// // serve root directory with default options: -/// // - resolve index.html -/// let files: HashMap<&'static str, pagetop_statics::StaticResource> = HashMap::new(); -/// let app = App::new() -/// .service(pagetop_statics::ResourceFiles::new("/", files)); -/// // or subpath with additional option to not resolve index.html -/// let files: HashMap<&'static str, pagetop_statics::StaticResource> = HashMap::new(); -/// let app = App::new() -/// .service(pagetop_statics::ResourceFiles::new("/imgs", files) -/// .do_not_resolve_defaults()); -/// } -/// ``` -#[allow(clippy::needless_doctest_main)] -pub struct ResourceFiles { - not_resolve_defaults: bool, - use_guard: bool, - not_found_resolves_to: Option<String>, - inner: Rc<ResourceFilesInner>, -} - -pub struct ResourceFilesInner { - path: String, - files: HashMap<&'static str, Resource>, -} - -const INDEX_HTML: &str = "index.html"; - -impl ResourceFiles { - pub fn new(path: &str, files: HashMap<&'static str, Resource>) -> Self { - let inner = ResourceFilesInner { - path: path.into(), - files, - }; - Self { - inner: Rc::new(inner), - not_resolve_defaults: false, - not_found_resolves_to: None, - use_guard: false, - } - } - - /// By default trying to resolve '.../' to '.../index.html' if it exists. - /// Turn off this resolution by calling this function. - pub fn do_not_resolve_defaults(mut self) -> Self { - self.not_resolve_defaults = true; - self - } - - /// Resolves not found references to this path. - /// - /// This can be useful for angular-like applications. - pub fn resolve_not_found_to<S: ToString>(mut self, path: S) -> Self { - self.not_found_resolves_to = Some(path.to_string()); - self - } - - /// Resolves not found references to root path. - /// - /// This can be useful for angular-like applications. - pub fn resolve_not_found_to_root(self) -> Self { - self.resolve_not_found_to(INDEX_HTML) - } - - /// If this is called, we will use an [actix_web::guard::Guard] to check if this request should be handled. - /// If set to true, we skip using the handler for files that haven't been found, instead of sending 404s. - /// Would be ignored, if `resolve_not_found_to` or `resolve_not_found_to_root` is used. - /// - /// Can be useful if you want to share files on a (sub)path that's also used by a different route handler. - pub fn skip_handler_when_not_found(mut self) -> Self { - self.use_guard = true; - self - } - - fn select_guard(&self) -> Box<dyn Guard> { - if self.not_resolve_defaults { - Box::new(NotResolveDefaultsGuard::from(self)) - } else { - Box::new(ResolveDefaultsGuard::from(self)) - } - } -} - -impl Deref for ResourceFiles { - type Target = ResourceFilesInner; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -struct NotResolveDefaultsGuard { - inner: Rc<ResourceFilesInner>, -} - -impl Guard for NotResolveDefaultsGuard { - fn check(&self, ctx: &GuardContext<'_>) -> bool { - self.inner - .files - .contains_key(ctx.head().uri.path().trim_start_matches('/')) - } -} - -impl From<&ResourceFiles> for NotResolveDefaultsGuard { - fn from(files: &ResourceFiles) -> Self { - Self { - inner: files.inner.clone(), - } - } -} - -struct ResolveDefaultsGuard { - inner: Rc<ResourceFilesInner>, -} - -impl Guard for ResolveDefaultsGuard { - fn check(&self, ctx: &GuardContext<'_>) -> bool { - let path = ctx.head().uri.path().trim_start_matches('/'); - self.inner.files.contains_key(path) - || ((path.is_empty() || path.ends_with('/')) - && self - .inner - .files - .contains_key((path.to_string() + INDEX_HTML).as_str())) - } -} - -impl From<&ResourceFiles> for ResolveDefaultsGuard { - fn from(files: &ResourceFiles) -> Self { - Self { - inner: files.inner.clone(), - } - } -} - -impl HttpServiceFactory for ResourceFiles { - fn register(self, config: &mut AppService) { - let prefix = self.path.trim_start_matches('/'); - let rdef = if config.is_root() { - ResourceDef::root_prefix(prefix) - } else { - ResourceDef::prefix(prefix) - }; - let guards = if self.use_guard && self.not_found_resolves_to.is_none() { - Some(vec![self.select_guard()]) - } else { - None - }; - config.register_service(rdef, guards, self, None); - } -} - -impl ServiceFactory<ServiceRequest> for ResourceFiles { - type Config = (); - type Response = ServiceResponse; - type Error = Error; - type Service = ResourceFilesService; - type InitError = (); - type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>; - - fn new_service(&self, _: ()) -> Self::Future { - ok(ResourceFilesService { - resolve_defaults: !self.not_resolve_defaults, - not_found_resolves_to: self.not_found_resolves_to.clone(), - inner: self.inner.clone(), - }) - .boxed_local() - } -} - -#[derive(Deref)] -pub struct ResourceFilesService { - resolve_defaults: bool, - not_found_resolves_to: Option<String>, - #[deref] - inner: Rc<ResourceFilesInner>, -} - -impl Service<ServiceRequest> for ResourceFilesService { - type Response = ServiceResponse; - type Error = Error; - type Future = Ready<Result<Self::Response, Self::Error>>; - - always_ready!(); - - fn call(&self, req: ServiceRequest) -> Self::Future { - match *req.method() { - Method::HEAD | Method::GET => (), - _ => { - return ok(ServiceResponse::new( - req.into_parts().0, - HttpResponse::MethodNotAllowed() - .insert_header(ContentType::plaintext()) - .insert_header((header::ALLOW, "GET, HEAD")) - .body("This resource only supports GET and HEAD."), - )); - } - } - - let req_path = req.match_info().unprocessed(); - let mut item = self.files.get(req_path); - - if item.is_none() - && self.resolve_defaults - && (req_path.is_empty() || req_path.ends_with('/')) - { - let index_req_path = req_path.to_string() + INDEX_HTML; - item = self.files.get(index_req_path.trim_start_matches('/')); - } - - let (req, response) = if item.is_some() { - let (req, _) = req.into_parts(); - let response = respond_to(&req, item); - (req, response) - } else { - let real_path = match get_pathbuf(req_path) { - Ok(item) => item, - Err(e) => return ok(req.error_response(e)), - }; - - let (req, _) = req.into_parts(); - - let mut item = self.files.get(real_path.as_str()); - - if item.is_none() && self.not_found_resolves_to.is_some() { - let not_found_path = self.not_found_resolves_to.as_ref().unwrap(); - item = self.files.get(not_found_path.as_str()); - } - - let response = respond_to(&req, item); - (req, response) - }; - - ok(ServiceResponse::new(req, response)) - } -} - -fn respond_to(req: &HttpRequest, item: Option<&Resource>) -> HttpResponse { - if let Some(file) = item { - let etag = Some(header::EntityTag::new_strong(format!( - "{:x}:{:x}", - file.data.len(), - file.modified - ))); - - let precondition_failed = !any_match(etag.as_ref(), req); - - let not_modified = !none_match(etag.as_ref(), req); - - let mut resp = HttpResponse::build(StatusCode::OK); - resp.insert_header((header::CONTENT_TYPE, file.mime_type)); - - if let Some(etag) = etag { - resp.insert_header(header::ETag(etag)); - } - - if precondition_failed { - return resp.status(StatusCode::PRECONDITION_FAILED).finish(); - } else if not_modified { - return resp.status(StatusCode::NOT_MODIFIED).finish(); - } - - resp.body(file.data) - } else { - HttpResponse::NotFound().body("Not found") - } -} - -/// Returns true if `req` has no `If-Match` header or one which matches `etag`. -fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { - match req.get_header::<header::IfMatch>() { - None | Some(header::IfMatch::Any) => true, - Some(header::IfMatch::Items(ref items)) => { - if let Some(some_etag) = etag { - for item in items { - if item.strong_eq(some_etag) { - return true; - } - } - } - false - } - } -} - -/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`. -fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { - match req.get_header::<header::IfNoneMatch>() { - Some(header::IfNoneMatch::Any) => false, - Some(header::IfNoneMatch::Items(ref items)) => { - if let Some(some_etag) = etag { - for item in items { - if item.weak_eq(some_etag) { - return false; - } - } - } - true - } - None => true, - } -} - -/// Error type representing invalid characters in a URI path segment. -/// -/// This enum is used to report specific formatting errors in individual segments of a URI path, -/// such as starting, ending, or containing disallowed characters. Each variant wraps the offending -/// character that caused the error. -#[derive(Debug, PartialEq, Display, Error)] -pub enum UriSegmentError { - /// The segment started with the wrapped invalid character. - #[display(fmt = "The segment started with the wrapped invalid character")] - BadStart(#[error(not(source))] char), - - /// The segment contained the wrapped invalid character. - #[display(fmt = "The segment contained the wrapped invalid character")] - BadChar(#[error(not(source))] char), - - /// The segment ended with the wrapped invalid character. - #[display(fmt = "The segment ended with the wrapped invalid character")] - BadEnd(#[error(not(source))] char), -} - -#[cfg(test)] -mod tests_error_impl { - use super::*; - - fn assert_send_and_sync<T: Send + Sync + 'static>() {} - - #[test] - fn test_error_impl() { - // ensure backwards compatibility when migrating away from failure - assert_send_and_sync::<UriSegmentError>(); - } -} - -/// Return `BadRequest` for `UriSegmentError` -impl ResponseError for UriSegmentError { - fn error_response(&self) -> HttpResponse { - HttpResponse::new(StatusCode::BAD_REQUEST) - } -} - -fn get_pathbuf(path: &str) -> Result<String, UriSegmentError> { - let mut buf = Vec::new(); - for segment in path.split('/') { - if segment == ".." { - buf.pop(); - } else if segment.starts_with('.') { - return Err(UriSegmentError::BadStart('.')); - } else if segment.starts_with('*') { - return Err(UriSegmentError::BadStart('*')); - } else if segment.ends_with(':') { - return Err(UriSegmentError::BadEnd(':')); - } else if segment.ends_with('>') { - return Err(UriSegmentError::BadEnd('>')); - } else if segment.ends_with('<') { - return Err(UriSegmentError::BadEnd('<')); - } else if segment.is_empty() { - continue; - } else if cfg!(windows) && segment.contains('\\') { - return Err(UriSegmentError::BadChar('\\')); - } else { - buf.push(segment) - } - } - - Ok(buf.join("/")) -} diff --git a/helpers/pagetop-statics/src/sets.rs b/helpers/pagetop-statics/src/sets.rs deleted file mode 100644 index 1d9299df..00000000 --- a/helpers/pagetop-statics/src/sets.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::{ - fs::{self, File, Metadata}, - io::{self, Write}, - path::{Path, PathBuf}, -}; - -use super::resource::{ - collect_resources, generate_function_end, generate_function_header, generate_resource_insert, - generate_uses, generate_variable_header, generate_variable_return, DEFAULT_VARIABLE_NAME, -}; - -/// Defines the split strategie. -pub trait SetSplitStrategie { - /// Register next file from resources. - fn register(&mut self, path: &Path, metadata: &Metadata); - /// Determine, should we split modules now. - fn should_split(&self) -> bool; - /// Resets internal counters after split. - fn reset(&mut self); -} - -/// Split modules by files count. -pub struct SplitByCount { - current: usize, - max: usize, -} - -impl SplitByCount { - pub fn new(max: usize) -> Self { - Self { current: 0, max } - } -} - -impl SetSplitStrategie for SplitByCount { - fn register(&mut self, _path: &Path, _metadata: &Metadata) { - self.current += 1; - } - - fn should_split(&self) -> bool { - self.current >= self.max - } - - fn reset(&mut self) { - self.current = 0; - } -} - -/// Generate resources for `project_dir` using `filter` -/// breaking them into separate modules using `set_split_strategie` (recommended for large > 128 Mb setups). -/// -/// Result saved in module named `module_name`. It exports -/// only one function named `fn_name`. It is then exported from -/// `generated_filename`. `generated_filename` is also used to determine -/// the parent directory for the module. -/// -/// in `build.rs`: -/// ```rust -/// -/// use std::{env, path::Path}; -/// use pagetop_statics::sets::{generate_resources_sets, SplitByCount}; -/// -/// fn main() { -/// let out_dir = env::var("OUT_DIR").unwrap(); -/// let generated_filename = Path::new(&out_dir).join("generated_sets.rs"); -/// generate_resources_sets( -/// "./tests", -/// None, -/// generated_filename, -/// "sets", -/// "generate", -/// &mut SplitByCount::new(2), -/// "pagetop_statics", -/// ) -/// .unwrap(); -/// } -/// ``` -/// -/// in `main.rs`: -/// ```rust -/// include!(concat!(env!("OUT_DIR"), "/generated_sets.rs")); -/// -/// fn main() { -/// let generated_file = generate(); -/// -/// assert_eq!(generated_file.len(), 4); -/// -/// } -/// ``` -pub fn generate_resources_sets<P, G, S>( - project_dir: P, - filter: Option<fn(p: &Path) -> bool>, - generated_filename: G, - module_name: &str, - fn_name: &str, - set_split_strategie: &mut S, - crate_name: &str, -) -> io::Result<()> -where - P: AsRef<Path>, - G: AsRef<Path>, - S: SetSplitStrategie, -{ - let resources = collect_resources(&project_dir, filter)?; - - let mut generated_file = File::create(&generated_filename)?; - - let module_dir = generated_filename.as_ref().parent().map_or_else( - || PathBuf::from(module_name), - |parent| parent.join(module_name), - ); - fs::create_dir_all(&module_dir)?; - - let mut module_file = File::create(module_dir.join("mod.rs"))?; - - generate_uses(&mut module_file, crate_name)?; - writeln!( - module_file, - " -use ::{crate_name}::StaticResource; -use ::std::collections::HashMap;" - )?; - - let mut modules_count = 1; - - let mut set_file = create_set_module_file(&module_dir, modules_count)?; - let mut should_split = set_split_strategie.should_split(); - - for resource in &resources { - let (path, metadata) = &resource; - if should_split { - set_split_strategie.reset(); - modules_count += 1; - generate_function_end(&mut set_file)?; - set_file = create_set_module_file(&module_dir, modules_count)?; - } - set_split_strategie.register(path, metadata); - should_split = set_split_strategie.should_split(); - - generate_resource_insert(&mut set_file, &project_dir, DEFAULT_VARIABLE_NAME, resource)?; - } - - generate_function_end(&mut set_file)?; - - for module_index in 1..=modules_count { - writeln!(module_file, "mod set_{module_index};")?; - } - - generate_function_header(&mut module_file, fn_name, crate_name)?; - - generate_variable_header(&mut module_file, DEFAULT_VARIABLE_NAME)?; - - for module_index in 1..=modules_count { - writeln!( - module_file, - "set_{module_index}::generate(&mut {DEFAULT_VARIABLE_NAME});", - )?; - } - - generate_variable_return(&mut module_file, DEFAULT_VARIABLE_NAME)?; - - generate_function_end(&mut module_file)?; - - writeln!( - generated_file, - "mod {module_name}; -pub use {module_name}::{fn_name};", - )?; - - Ok(()) -} - -fn create_set_module_file(module_dir: &Path, module_index: usize) -> io::Result<File> { - let mut set_module = File::create(module_dir.join(format!("set_{module_index}.rs")))?; - - writeln!( - set_module, - "#[allow(clippy::wildcard_imports)] -use super::*; -#[allow(clippy::unreadable_literal)] -pub(crate) fn generate({DEFAULT_VARIABLE_NAME}: &mut HashMap<&'static str, StaticResource>) {{", - )?; - - Ok(set_module) -} diff --git a/helpers/pagetop-statics/tests/file1.txt b/helpers/pagetop-statics/tests/file1.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/helpers/pagetop-statics/tests/file2.txt b/helpers/pagetop-statics/tests/file2.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/helpers/pagetop-statics/tests/file3.info b/helpers/pagetop-statics/tests/file3.info deleted file mode 100644 index e69de29b..00000000 diff --git a/helpers/pagetop-statics/tests/index.html b/helpers/pagetop-statics/tests/index.html deleted file mode 100644 index 36f505f2..00000000 --- a/helpers/pagetop-statics/tests/index.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Document</title> -</head> -<body> -</body> -</html> diff --git a/src/app.rs b/src/app.rs index c8ffba11..7c5b8930 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,8 @@ -//! Prepara y ejecuta una aplicación creada con PageTop. +//! Prepare and run an application created with **Pagetop**. mod figfont; -use crate::core::{extension, extension::ExtensionRef}; +use crate::core::{package, package::PackageRef}; use crate::html::Markup; use crate::response::page::{ErrorPage, ResultPage}; use crate::service::HttpRequest; @@ -17,12 +17,6 @@ use substring::Substring; use std::io::Error; use std::sync::LazyLock; -/// Punto de entrada de una aplicación PageTop. -/// -/// No almacena datos, **encapsula** el inicio completo de configuración y puesta en marcha. Para -/// instanciarla se puede usar [`new()`](Application::new) o [`prepare()`](Application::prepare). -/// Después sólo hay que llamar a [`run()`](Application::run) para ejecutar la aplicación (o a -/// [`test()`](Application::test) si se está preparando un entorno de pruebas). pub struct Application; impl Default for Application { @@ -32,59 +26,52 @@ impl Default for Application { } impl Application { - /// Crea una instancia de la aplicación. + /// Creates a new application instance without any package. pub fn new() -> Self { Self::internal_prepare(None) } - /// Prepara una instancia de la aplicación a partir de una extensión raíz. - /// - /// Esa extensión suele declarar: - /// - /// - Sus propias dependencias (que se habilitarán automáticamente). - /// - Una lista de extensiones que deben deshabilitarse si estuvieran activadas. - /// - /// Esto simplifica el arranque en escenarios complejos. - pub fn prepare(root_extension: ExtensionRef) -> Self { - Self::internal_prepare(Some(root_extension)) + /// Prepares an application instance with a specific package. + pub fn prepare(root_package: PackageRef) -> Self { + Self::internal_prepare(Some(root_package)) } - // Método interno para preparar la aplicación, opcionalmente con una extensión. - fn internal_prepare(root_extension: Option<ExtensionRef>) -> Self { - // Al arrancar muestra una cabecera para la aplicación. + // Internal method to prepare the application, optionally with a package. + fn internal_prepare(root_package: Option<PackageRef>) -> Self { + // On startup, show the application banner. Self::show_banner(); - // Inicia gestión de trazas y registro de eventos (logging). + // Starts logging and event tracing. LazyLock::force(&trace::TRACING); - // Valida el identificador de idioma por defecto. + // Validates the default language identifier. LazyLock::force(&locale::DEFAULT_LANGID); - // Registra las extensiones de la aplicación. - extension::all::register_extensions(root_extension); + // Registers the application's packages. + package::all::register_packages(root_package); - // Registra las acciones de las extensiones. - extension::all::register_actions(); + // Registers package actions. + package::all::register_actions(); - // Inicializa las extensiones. - extension::all::initialize_extensions(); + // Initializes the packages. + package::all::init_packages(); Self } - // Muestra una cabecera para la aplicación basada en la configuración. + // Displays the application banner based on the configuration. fn show_banner() { use colored::Colorize; use terminal_size::{terminal_size, Width}; if global::SETTINGS.app.startup_banner.to_lowercase() != "off" { - // Nombre de la aplicación, ajustado al ancho del terminal si es necesario. - let mut app_ff = String::new(); + // Application name, formatted for the terminal width if necessary. + let mut app_ff = "".to_string(); let app_name = &global::SETTINGS.app.name; if let Some((Width(term_width), _)) = terminal_size() { if term_width >= 80 { let maxlen: usize = ((term_width / 10) - 2).into(); - let mut app = app_name.substring(0, maxlen).to_string(); + let mut app = app_name.substring(0, maxlen).to_owned(); if app_name.len() > maxlen { app = format!("{app}..."); } @@ -99,12 +86,12 @@ impl Application { print!("\n{app_ff}"); } - // Descripción de la aplicación. + // Application description. if !global::SETTINGS.app.description.is_empty() { println!("{}", global::SETTINGS.app.description.cyan()); }; - // Versión de PageTop. + // PageTop version. println!( "{} {}\n", "Powered by PageTop".yellow(), @@ -113,15 +100,12 @@ impl Application { } } - /// Arranca el servidor web de la aplicación. - /// - /// Devuelve [`std::io::Error`] si el *socket* no puede enlazarse (por puerto en uso, permisos, - /// etc.). + /// Starts the web server. pub fn run(self) -> Result<service::Server, Error> { - // Genera clave secreta para firmar y verificar cookies. + // Generate the cookie key. let secret_key = service::cookie::Key::generate(); - // Prepara el servidor web. + // Prepares the web server. Ok(service::HttpServer::new(move || { Self::service_app() .wrap(tracing_actix_web::TracingLogger::default()) @@ -148,7 +132,7 @@ impl Application { .run()) } - /// Prepara el servidor web de la aplicación para pruebas. + /// Method for testing, returns a service application instance. pub fn test( self, ) -> service::App< @@ -163,7 +147,7 @@ impl Application { Self::service_app() } - // Configura el servicio web de la aplicación. + // Configures the service application. fn service_app() -> service::App< impl service::Factory< service::Request, @@ -174,7 +158,7 @@ impl Application { >, > { service::App::new() - .configure(extension::all::configure_services) + .configure(package::all::configure_services) .default_service(service::web::route().to(service_not_found)) } } diff --git a/src/app/figfont.rs b/src/app/figfont.rs index 6592d813..1da9d9b9 100644 --- a/src/app/figfont.rs +++ b/src/app/figfont.rs @@ -19,7 +19,7 @@ pub static FIGFONT: LazyLock<FIGfont> = LazyLock::new(|| { "starwars" => starwars, _ => { println!( - "\n FIGfont \"{}\" not found for banner. Using \"Slant\". Check settings.", + "\n FIGfont \"{}\" not found for banner. Using \"Slant\". Check settings files.", global::SETTINGS.app.startup_banner, ); slant diff --git a/src/base.rs b/src/base.rs index d27a9ad5..196f119c 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,9 +1,9 @@ -//! Reúne acciones, componentes, extensiones y temas predefinidos. +//! Base actions, components, packages, and themes. pub mod action; pub mod component; -pub mod extension; +pub mod package; pub mod theme; diff --git a/src/base/action.rs b/src/base/action.rs index 977ae9e2..fe6741c5 100644 --- a/src/base/action.rs +++ b/src/base/action.rs @@ -1,17 +1,9 @@ -//! Acciones predefinidas para alterar el funcionamiento interno de PageTop. - use crate::prelude::*; -/// Tipo de función para manipular componentes y su contexto de renderizado. -/// -/// Se usa en acciones definidas en [`component`] y [`theme`] para alterar el comportamiento de los -/// componentes. -/// -/// Recibe referencias mutables (`&mut`) del componente `component` y del contexto `cx`. pub type FnActionWithComponent<C> = fn(component: &mut C, cx: &mut Context); -pub mod component; +pub mod page; pub mod theme; -pub mod page; +pub mod component; diff --git a/src/base/action/component.rs b/src/base/action/component.rs index 30c7ba4a..03eedf26 100644 --- a/src/base/action/component.rs +++ b/src/base/action/component.rs @@ -1,7 +1,8 @@ -//! Acciones que operan sobre componentes. +mod is_renderable; +pub use is_renderable::*; -mod before_render_component; -pub use before_render_component::*; +mod before_prepare_component; +pub use before_prepare_component::*; -mod after_render_component; -pub use after_render_component::*; +mod after_prepare_component; +pub use after_prepare_component::*; diff --git a/src/base/action/component/after_prepare_component.rs b/src/base/action/component/after_prepare_component.rs new file mode 100644 index 00000000..233c1a7b --- /dev/null +++ b/src/base/action/component/after_prepare_component.rs @@ -0,0 +1,65 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithComponent; + +pub struct AfterPrepare<C: ComponentTrait> { + f: FnActionWithComponent<C>, + referer_type_id: Option<TypeId>, + referer_id: OptionId, + weight: Weight, +} + +impl<C: ComponentTrait> ActionTrait for AfterPrepare<C> { + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } + + fn referer_id(&self) -> Option<String> { + self.referer_id.get() + } + + fn weight(&self) -> Weight { + self.weight + } +} + +impl<C: ComponentTrait> AfterPrepare<C> { + pub fn new(f: FnActionWithComponent<C>) -> Self { + AfterPrepare { + f, + referer_type_id: Some(TypeId::of::<C>()), + referer_id: OptionId::default(), + weight: 0, + } + } + + pub fn filter_by_referer_id(mut self, id: impl Into<String>) -> Self { + self.referer_id.set_value(id); + self + } + + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { + dispatch_actions( + &ActionKey::new(TypeId::of::<Self>(), None, Some(TypeId::of::<C>()), None), + |action: &Self| (action.f)(component, cx), + ); + if let Some(id) = component.id() { + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + None, + Some(TypeId::of::<C>()), + Some(id), + ), + |action: &Self| (action.f)(component, cx), + ); + } + } +} diff --git a/src/base/action/component/after_render_component.rs b/src/base/action/component/after_render_component.rs deleted file mode 100644 index 0cb03347..00000000 --- a/src/base/action/component/after_render_component.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] después de renderizar un componente. -pub struct AfterRender<C: Component> { - f: FnActionWithComponent<C>, - referer_type_id: Option<UniqueId>, - referer_id: AttrId, - weight: Weight, -} - -/// Filtro para despachar [`FnActionWithComponent`] después de renderizar un componente `C`. -impl<C: Component> ActionDispatcher for AfterRender<C> { - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option<UniqueId> { - self.referer_type_id - } - - /// Devuelve el identificador del componente. - fn referer_id(&self) -> Option<String> { - self.referer_id.get() - } - - /// Devuelve el peso para definir el orden de ejecución. - fn weight(&self) -> Weight { - self.weight - } -} - -impl<C: Component> AfterRender<C> { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`]. - pub fn new(f: FnActionWithComponent<C>) -> Self { - AfterRender { - f, - referer_type_id: Some(UniqueId::of::<C>()), - referer_id: AttrId::default(), - weight: 0, - } - } - - /// Afina el registro para ejecutar la acción [`FnActionWithComponent`] sólo para el componente - /// `C` con identificador `id`. - pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self { - self.referer_id.alter_value(id); - self - } - - /// Opcional. Acciones con pesos más bajos se aplican antes. Se pueden usar valores negativos. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } - - // Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - // Primero despacha las acciones para el tipo de componente. - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - None, - Some(UniqueId::of::<C>()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - // Y luego despacha las acciones para el tipo de componente con un identificador dado. - if let Some(id) = component.id() { - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - None, - Some(UniqueId::of::<C>()), - Some(id), - ), - |action: &Self| (action.f)(component, cx), - ); - } - } -} diff --git a/src/base/action/component/before_prepare_component.rs b/src/base/action/component/before_prepare_component.rs new file mode 100644 index 00000000..cedc45db --- /dev/null +++ b/src/base/action/component/before_prepare_component.rs @@ -0,0 +1,65 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithComponent; + +pub struct BeforePrepare<C: ComponentTrait> { + f: FnActionWithComponent<C>, + referer_type_id: Option<TypeId>, + referer_id: OptionId, + weight: Weight, +} + +impl<C: ComponentTrait> ActionTrait for BeforePrepare<C> { + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } + + fn referer_id(&self) -> Option<String> { + self.referer_id.get() + } + + fn weight(&self) -> Weight { + self.weight + } +} + +impl<C: ComponentTrait> BeforePrepare<C> { + pub fn new(f: FnActionWithComponent<C>) -> Self { + BeforePrepare { + f, + referer_type_id: Some(TypeId::of::<C>()), + referer_id: OptionId::default(), + weight: 0, + } + } + + pub fn filter_by_referer_id(mut self, id: impl Into<String>) -> Self { + self.referer_id.set_value(id); + self + } + + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { + dispatch_actions( + &ActionKey::new(TypeId::of::<Self>(), None, Some(TypeId::of::<C>()), None), + |action: &Self| (action.f)(component, cx), + ); + if let Some(id) = component.id() { + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + None, + Some(TypeId::of::<C>()), + Some(id), + ), + |action: &Self| (action.f)(component, cx), + ); + } + } +} diff --git a/src/base/action/component/before_render_component.rs b/src/base/action/component/before_render_component.rs deleted file mode 100644 index 46ff9aae..00000000 --- a/src/base/action/component/before_render_component.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] antes de renderizar el componente. -pub struct BeforeRender<C: Component> { - f: FnActionWithComponent<C>, - referer_type_id: Option<UniqueId>, - referer_id: AttrId, - weight: Weight, -} - -/// Filtro para despachar [`FnActionWithComponent`] antes de renderizar un componente `C`. -impl<C: Component> ActionDispatcher for BeforeRender<C> { - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option<UniqueId> { - self.referer_type_id - } - - /// Devuelve el identificador del componente. - fn referer_id(&self) -> Option<String> { - self.referer_id.get() - } - - /// Devuelve el peso para definir el orden de ejecución. - fn weight(&self) -> Weight { - self.weight - } -} - -impl<C: Component> BeforeRender<C> { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`]. - pub fn new(f: FnActionWithComponent<C>) -> Self { - BeforeRender { - f, - referer_type_id: Some(UniqueId::of::<C>()), - referer_id: AttrId::default(), - weight: 0, - } - } - - /// Afina el registro para ejecutar la acción [`FnActionWithComponent`] sólo para el componente - /// `C` con identificador `id`. - pub fn filter_by_referer_id(mut self, id: impl AsRef<str>) -> Self { - self.referer_id.alter_value(id); - self - } - - /// Opcional. Acciones con pesos más bajos se aplican antes. Se pueden usar valores negativos. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } - - // Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - // Primero despacha las acciones para el tipo de componente. - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - None, - Some(UniqueId::of::<C>()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - // Y luego despacha las aciones para el tipo de componente con un identificador dado. - if let Some(id) = component.id() { - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - None, - Some(UniqueId::of::<C>()), - Some(id), - ), - |action: &Self| (action.f)(component, cx), - ); - } - } -} diff --git a/src/base/action/component/is_renderable.rs b/src/base/action/component/is_renderable.rs new file mode 100644 index 00000000..93159237 --- /dev/null +++ b/src/base/action/component/is_renderable.rs @@ -0,0 +1,77 @@ +use crate::prelude::*; + +pub type FnIsRenderable<C> = fn(component: &C, cx: &mut Context) -> bool; + +pub struct IsRenderable<C: ComponentTrait> { + f: FnIsRenderable<C>, + referer_type_id: Option<TypeId>, + referer_id: OptionId, + weight: Weight, +} + +impl<C: ComponentTrait> ActionTrait for IsRenderable<C> { + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } + + fn referer_id(&self) -> Option<String> { + self.referer_id.get() + } + + fn weight(&self) -> Weight { + self.weight + } +} + +impl<C: ComponentTrait> IsRenderable<C> { + pub fn new(f: FnIsRenderable<C>) -> Self { + IsRenderable { + f, + referer_type_id: Some(TypeId::of::<C>()), + referer_id: OptionId::default(), + weight: 0, + } + } + + pub fn filter_by_referer_id(mut self, id: impl Into<String>) -> Self { + self.referer_id.set_value(id); + self + } + + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &C, cx: &mut Context) -> bool { + let mut renderable = true; + dispatch_actions( + &ActionKey::new(TypeId::of::<Self>(), None, Some(TypeId::of::<C>()), None), + |action: &Self| { + if renderable && !(action.f)(component, cx) { + renderable = false; + } + }, + ); + if renderable { + if let Some(id) = component.id() { + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + None, + Some(TypeId::of::<C>()), + Some(id), + ), + |action: &Self| { + if renderable && !(action.f)(component, cx) { + renderable = false; + } + }, + ); + } + } + renderable + } +} diff --git a/src/base/action/page.rs b/src/base/action/page.rs index b6dbe9ab..9c89e2d1 100644 --- a/src/base/action/page.rs +++ b/src/base/action/page.rs @@ -1,15 +1,3 @@ -//! Acciones para alterar el contenido de las páginas a renderizar. - -use crate::response::page::Page; - -/// Tipo de función para manipular una página durante su construcción o renderizado. -/// -/// Se emplea en acciones orientadas a modificar o inspeccionar una instancia de [`Page`] -/// directamente, sin acceder a los componentes individuales ni al contexto de renderizado. -/// -/// Recibe una referencia mutable (`&mut`) a la página en cuestión. -pub type FnActionWithPage = fn(page: &mut Page); - mod before_render_body; pub use before_render_body::*; diff --git a/src/base/action/page/after_render_body.rs b/src/base/action/page/after_render_body.rs index 0bbfeaba..81a89d87 100644 --- a/src/base/action/page/after_render_body.rs +++ b/src/base/action/page/after_render_body.rs @@ -1,45 +1,33 @@ use crate::prelude::*; -use crate::base::action::page::FnActionWithPage; +pub type FnAfterRenderBody = fn(page: &mut Page); -/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) después de renderizar -/// el cuerpo de la página. -/// -/// Este tipo de acción se despacha después de renderizar el contenido principal de la página -/// (`<body>`), permitiendo ajustes finales sobre la instancia [`Page`]. -/// -/// Las acciones se ejecutan en orden según el [`Weight`] asignado. pub struct AfterRenderBody { - f: FnActionWithPage, + f: FnAfterRenderBody, weight: Weight, } -impl ActionDispatcher for AfterRenderBody { - /// Devuelve el peso para definir el orden de ejecución. +impl ActionTrait for AfterRenderBody { fn weight(&self) -> Weight { self.weight } } impl AfterRenderBody { - /// Permite [registrar](Extension::actions) una nueva acción - /// [`FnActionWithPage`](crate::base::action::page::FnActionWithPage). - pub fn new(f: FnActionWithPage) -> Self { + pub fn new(f: FnAfterRenderBody) -> Self { AfterRenderBody { f, weight: 0 } } - /// Opcional. Acciones con pesos más bajos se aplican antes. Se pueden usar valores negativos. pub fn with_weight(mut self, value: Weight) -> Self { self.weight = value; self } - // Despacha las acciones. #[inline(always)] #[allow(clippy::inline_always)] pub(crate) fn dispatch(page: &mut Page) { dispatch_actions( - &ActionKey::new(UniqueId::of::<Self>(), None, None, None), + &ActionKey::new(TypeId::of::<Self>(), None, None, None), |action: &Self| (action.f)(page), ); } diff --git a/src/base/action/page/before_render_body.rs b/src/base/action/page/before_render_body.rs index 68f4af7d..e0a9d770 100644 --- a/src/base/action/page/before_render_body.rs +++ b/src/base/action/page/before_render_body.rs @@ -1,45 +1,33 @@ use crate::prelude::*; -use crate::base::action::page::FnActionWithPage; +pub type FnBeforeRenderBody = fn(page: &mut Page); -/// Ejecuta [`FnActionWithPage`](crate::base::action::page::FnActionWithPage) antes de renderizar -/// el cuerpo de la página. -/// -/// Este tipo de acción se despacha antes de renderizar el contenido principal de la página -/// (`<body>`), permitiendo ajustes sobre la instancia [`Page`]. -/// -/// Las acciones se ejecutan en orden según el [`Weight`] asignado. pub struct BeforeRenderBody { - f: FnActionWithPage, + f: FnBeforeRenderBody, weight: Weight, } -impl ActionDispatcher for BeforeRenderBody { - /// Devuelve el peso para definir el orden de ejecución. +impl ActionTrait for BeforeRenderBody { fn weight(&self) -> Weight { self.weight } } impl BeforeRenderBody { - /// Permite [registrar](Extension::actions) una nueva acción - /// [`FnActionWithPage`](crate::base::action::page::FnActionWithPage). - pub fn new(f: FnActionWithPage) -> Self { + pub fn new(f: FnBeforeRenderBody) -> Self { BeforeRenderBody { f, weight: 0 } } - /// Opcional. Acciones con pesos más bajos se aplican antes. Se pueden usar valores negativos. pub fn with_weight(mut self, value: Weight) -> Self { self.weight = value; self } - // Despacha las acciones. #[inline(always)] #[allow(clippy::inline_always)] pub(crate) fn dispatch(page: &mut Page) { dispatch_actions( - &ActionKey::new(UniqueId::of::<Self>(), None, None, None), + &ActionKey::new(TypeId::of::<Self>(), None, None, None), |action: &Self| (action.f)(page), ); } diff --git a/src/base/action/theme.rs b/src/base/action/theme.rs index 40988574..8f307d98 100644 --- a/src/base/action/theme.rs +++ b/src/base/action/theme.rs @@ -1,10 +1,8 @@ -//! Acciones lanzadas desde los temas. +mod before_prepare_component; +pub use before_prepare_component::*; -mod before_render_component; -pub use before_render_component::*; +mod after_prepare_component; +pub use after_prepare_component::*; -mod after_render_component; -pub use after_render_component::*; - -mod prepare_render; -pub use prepare_render::*; +mod render_component; +pub use render_component::*; diff --git a/src/base/action/theme/after_prepare_component.rs b/src/base/action/theme/after_prepare_component.rs new file mode 100644 index 00000000..7285aec1 --- /dev/null +++ b/src/base/action/theme/after_prepare_component.rs @@ -0,0 +1,43 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithComponent; + +pub struct AfterPrepare<C: ComponentTrait> { + f: FnActionWithComponent<C>, + theme_type_id: Option<TypeId>, + referer_type_id: Option<TypeId>, +} + +impl<C: ComponentTrait> ActionTrait for AfterPrepare<C> { + fn theme_type_id(&self) -> Option<TypeId> { + self.theme_type_id + } + + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } +} + +impl<C: ComponentTrait> AfterPrepare<C> { + pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self { + AfterPrepare { + f, + theme_type_id: Some(theme.type_id()), + referer_type_id: Some(TypeId::of::<C>()), + } + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + Some(cx.theme().type_id()), + Some(TypeId::of::<C>()), + None, + ), + |action: &Self| (action.f)(component, cx), + ); + } +} diff --git a/src/base/action/theme/after_render_component.rs b/src/base/action/theme/after_render_component.rs deleted file mode 100644 index d0beeb45..00000000 --- a/src/base/action/theme/after_render_component.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] después de que un tema renderice el componente. -pub struct AfterRender<C: Component> { - f: FnActionWithComponent<C>, - theme_type_id: Option<UniqueId>, - referer_type_id: Option<UniqueId>, -} - -/// Filtro para despachar [`FnActionWithComponent`] después de que un tema renderice el componente -/// `C`. -impl<C: Component> ActionDispatcher for AfterRender<C> { - /// Devuelve el identificador de tipo ([`UniqueId`]) del tema. - fn theme_type_id(&self) -> Option<UniqueId> { - self.theme_type_id - } - - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option<UniqueId> { - self.referer_type_id - } -} - -impl<C: Component> AfterRender<C> { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un - /// tema dado. - pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self { - AfterRender { - f, - theme_type_id: Some(theme.type_id()), - referer_type_id: Some(UniqueId::of::<C>()), - } - } - - // Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - Some(cx.theme().type_id()), - Some(UniqueId::of::<C>()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - } -} diff --git a/src/base/action/theme/before_prepare_component.rs b/src/base/action/theme/before_prepare_component.rs new file mode 100644 index 00000000..7c80a655 --- /dev/null +++ b/src/base/action/theme/before_prepare_component.rs @@ -0,0 +1,43 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithComponent; + +pub struct BeforePrepare<C: ComponentTrait> { + f: FnActionWithComponent<C>, + theme_type_id: Option<TypeId>, + referer_type_id: Option<TypeId>, +} + +impl<C: ComponentTrait> ActionTrait for BeforePrepare<C> { + fn theme_type_id(&self) -> Option<TypeId> { + self.theme_type_id + } + + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } +} + +impl<C: ComponentTrait> BeforePrepare<C> { + pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self { + BeforePrepare { + f, + theme_type_id: Some(theme.type_id()), + referer_type_id: Some(TypeId::of::<C>()), + } + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + Some(cx.theme().type_id()), + Some(TypeId::of::<C>()), + None, + ), + |action: &Self| (action.f)(component, cx), + ); + } +} diff --git a/src/base/action/theme/before_render_component.rs b/src/base/action/theme/before_render_component.rs deleted file mode 100644 index ac5ee6b2..00000000 --- a/src/base/action/theme/before_render_component.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::prelude::*; - -use crate::base::action::FnActionWithComponent; - -/// Ejecuta [`FnActionWithComponent`] antes de que un tema renderice el componente. -pub struct BeforeRender<C: Component> { - f: FnActionWithComponent<C>, - theme_type_id: Option<UniqueId>, - referer_type_id: Option<UniqueId>, -} - -/// Filtro para despachar [`FnActionWithComponent`] antes de que un tema renderice el componente -/// `C`. -impl<C: Component> ActionDispatcher for BeforeRender<C> { - /// Devuelve el identificador de tipo ([`UniqueId`]) del tema. - fn theme_type_id(&self) -> Option<UniqueId> { - self.theme_type_id - } - - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option<UniqueId> { - self.referer_type_id - } -} - -impl<C: Component> BeforeRender<C> { - /// Permite [registrar](Extension::actions) una nueva acción [`FnActionWithComponent`] para un - /// tema dado. - pub fn new(theme: ThemeRef, f: FnActionWithComponent<C>) -> Self { - BeforeRender { - f, - theme_type_id: Some(theme.type_id()), - referer_type_id: Some(UniqueId::of::<C>()), - } - } - - // Despacha las acciones. - #[inline] - pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - Some(cx.theme().type_id()), - Some(UniqueId::of::<C>()), - None, - ), - |action: &Self| (action.f)(component, cx), - ); - } -} diff --git a/src/base/action/theme/prepare_render.rs b/src/base/action/theme/prepare_render.rs deleted file mode 100644 index 4a1a2da6..00000000 --- a/src/base/action/theme/prepare_render.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::prelude::*; - -/// Tipo de función para alterar el renderizado de un componente. -/// -/// Permite a un [tema](crate::base::action::theme) sobreescribir el renderizado predeterminado de -/// los componentes. -/// -/// Recibe una referencia al componente `component` y una referencia mutable al contexto `cx`. -pub type FnPrepareRender<C> = fn(component: &C, cx: &mut Context) -> PrepareMarkup; - -/// Ejecuta [`FnPrepareRender`] para preparar el renderizado de un componente. -/// -/// Permite a un tema hacer una implementación nueva del renderizado de un componente. -pub struct PrepareRender<C: Component> { - f: FnPrepareRender<C>, - theme_type_id: Option<UniqueId>, - referer_type_id: Option<UniqueId>, -} - -/// Filtro para despachar [`FnPrepareRender`] que modifica el renderizado de un componente `C`. -impl<C: Component> ActionDispatcher for PrepareRender<C> { - /// Devuelve el identificador de tipo ([`UniqueId`]) del tema. - fn theme_type_id(&self) -> Option<UniqueId> { - self.theme_type_id - } - - /// Devuelve el identificador de tipo ([`UniqueId`]) del componente `C`. - fn referer_type_id(&self) -> Option<UniqueId> { - self.referer_type_id - } -} - -impl<C: Component> PrepareRender<C> { - /// Permite [registrar](Extension::actions) una nueva acción [`FnPrepareRender`] para un tema - /// dado. - pub fn new(theme: ThemeRef, f: FnPrepareRender<C>) -> Self { - PrepareRender { - f, - theme_type_id: Some(theme.type_id()), - referer_type_id: Some(UniqueId::of::<C>()), - } - } - - // Despacha las acciones. Se detiene en cuanto una renderiza. - #[inline] - pub(crate) fn dispatch(component: &C, cx: &mut Context) -> PrepareMarkup { - let mut render_component = PrepareMarkup::None; - dispatch_actions( - &ActionKey::new( - UniqueId::of::<Self>(), - Some(cx.theme().type_id()), - Some(UniqueId::of::<C>()), - None, - ), - |action: &Self| { - if render_component.is_empty() { - render_component = (action.f)(component, cx); - } - }, - ); - render_component - } -} diff --git a/src/base/action/theme/render_component.rs b/src/base/action/theme/render_component.rs new file mode 100644 index 00000000..e79c5c92 --- /dev/null +++ b/src/base/action/theme/render_component.rs @@ -0,0 +1,49 @@ +use crate::prelude::*; + +pub type FnRenderComponent<C> = fn(component: &C, cx: &mut Context) -> Option<Markup>; + +pub struct RenderComponent<C: ComponentTrait> { + f: FnRenderComponent<C>, + theme_type_id: Option<TypeId>, + referer_type_id: Option<TypeId>, +} + +impl<C: ComponentTrait> ActionTrait for RenderComponent<C> { + fn theme_type_id(&self) -> Option<TypeId> { + self.theme_type_id + } + + fn referer_type_id(&self) -> Option<TypeId> { + self.referer_type_id + } +} + +impl<C: ComponentTrait> RenderComponent<C> { + pub fn new(theme: ThemeRef, f: FnRenderComponent<C>) -> Self { + RenderComponent { + f, + theme_type_id: Some(theme.type_id()), + referer_type_id: Some(TypeId::of::<C>()), + } + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(component: &C, cx: &mut Context) -> Option<Markup> { + let mut render_component: Option<Markup> = None; + dispatch_actions( + &ActionKey::new( + TypeId::of::<Self>(), + Some(cx.theme().type_id()), + Some(TypeId::of::<C>()), + None, + ), + |action: &Self| { + if render_component.is_none() { + render_component = (action.f)(component, cx); + } + }, + ); + render_component + } +} diff --git a/src/base/component.rs b/src/base/component.rs index fa9ed2ad..93b22fa1 100644 --- a/src/base/component.rs +++ b/src/base/component.rs @@ -1,51 +1,11 @@ -//! Componentes nativos proporcionados por PageTop. -//! -//! Conviene destacar que PageTop distingue entre: -//! -//! - **Componentes estructurales** que definen el esqueleto de un documento HTML, como [`Template`] -//! y [`Region`], utilizados por [`Page`](crate::response::page::Page) para generar la estructura -//! final. -//! - **Componentes de contenido** (menús, barras, tarjetas, etc.), que se incluyen en las regiones -//! gestionadas por los componentes estructurales. -//! -//! El componente [`Template`] describe cómo maquetar el cuerpo del documento a partir de varias -//! regiones lógicas ([`Region`]). En función de la plantilla seleccionada, determina qué regiones -//! se renderizan y en qué orden. Por ejemplo, la plantilla predeterminada [`Template::DEFAULT`] -//! utiliza las regiones [`Region::HEADER`], [`Region::CONTENT`] y [`Region::FOOTER`]. -//! -//! Un componente [`Region`] es un contenedor lógico asociado a un nombre de región. Su contenido se -//! obtiene del [`Context`](crate::core::component::Context), donde los componentes se registran -//! mediante [`Contextual::with_child_in()`](crate::core::component::Contextual::with_child_in) y -//! otros mecanismos similares, y se integra en el documento a través de [`Template`]. -//! -//! Por su parte, una página ([`Page`](crate::response::page::Page)) representa un documento HTML -//! completo. Implementa [`Contextual`](crate::core::component::Contextual) para mantener su propio -//! [`Context`](crate::core::component::Context), donde gestiona el tema activo, la plantilla -//! seleccionada y los componentes asociados a cada región, y se encarga de generar la estructura -//! final de la página. -//! -//! De este modo, temas y extensiones colaboran sobre una estructura común: las aplicaciones -//! registran componentes en el [`Context`](crate::core::component::Context), las plantillas -//! organizan las regiones y las páginas generan el documento HTML resultante. -//! -//! Los temas pueden sobrescribir [`Template`] para exponer nuevas plantillas o adaptar las -//! predeterminadas, y lo mismo con [`Region`] para añadir regiones adicionales o personalizar su -//! representación. - mod html; pub use html::Html; -mod region; -pub use region::Region; +mod fluent; +pub use fluent::Fluent; -mod template; -pub use template::Template; +mod error403; +pub use error403::Error403; -mod block; -pub use block::Block; - -mod intro; -pub use intro::{Intro, IntroOpening}; - -mod poweredby; -pub use poweredby::PoweredBy; +mod error404; +pub use error404::Error404; diff --git a/src/base/component/block.rs b/src/base/component/block.rs deleted file mode 100644 index b2a754e5..00000000 --- a/src/base/component/block.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::prelude::*; - -/// Componente genérico que representa un bloque de contenido. -/// -/// Los bloques se utilizan como contenedores de otros componentes o contenidos, con un título -/// opcional y un cuerpo que sólo se renderiza si existen componentes hijos (*children*). -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Block { - id : AttrId, - classes : AttrClasses, - title : L10n, - children: Children, -} - -impl Component for Block { - fn new() -> Self { - Block::default() - } - - fn id(&self) -> Option<String> { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes(ClassesOp::Prepend, "block"); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let block_body = self.children().render(cx); - - if block_body.is_empty() { - return PrepareMarkup::None; - } - - let id = cx.required_id::<Block>(self.id()); - - PrepareMarkup::With(html! { - div id=(id) class=[self.classes().get()] { - @if let Some(title) = self.title().lookup(cx) { - h2 class="block__title" { span { (title) } } - } - div class="block__body" { (block_body) } - } - }) - } -} - -impl Block { - // **< Block BUILDER >************************************************************************** - - /// Establece el identificador único (`id`) del bloque. - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef<str>) -> Self { - self.id.alter_value(id); - self - } - - /// Modifica la lista de clases CSS aplicadas al bloque. - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.classes.alter_value(op, classes); - self - } - - /// Establece el título del bloque. - #[builder_fn] - pub fn with_title(mut self, title: L10n) -> Self { - self.title = title; - self - } - - /// Añade un nuevo componente hijo al bloque. - #[inline] - pub fn add_child(mut self, component: impl Component) -> Self { - self.children.add(Child::with(component)); - self - } - - /// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`]. - #[builder_fn] - pub fn with_child(mut self, op: ChildOp) -> Self { - self.children.alter_child(op); - self - } - - // **< Block GETTERS >************************************************************************** - - /// Devuelve las clases CSS asociadas al bloque. - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - /// Devuelve el título del bloque. - pub fn title(&self) -> &L10n { - &self.title - } - - /// Devuelve la lista de componentes (`children`) del bloque. - pub fn children(&self) -> &Children { - &self.children - } -} diff --git a/src/base/component/error403.rs b/src/base/component/error403.rs new file mode 100644 index 00000000..172487d7 --- /dev/null +++ b/src/base/component/error403.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +pub struct Error403; + +impl ComponentTrait for Error403 { + fn new() -> Self { + Error403 + } + + fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup { + PrepareMarkup::With(html! { + div { + h1 { ("FORBIDDEN ACCESS") } + } + }) + } +} diff --git a/src/base/component/error404.rs b/src/base/component/error404.rs new file mode 100644 index 00000000..0292df8f --- /dev/null +++ b/src/base/component/error404.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +pub struct Error404; + +impl ComponentTrait for Error404 { + fn new() -> Self { + Error404 + } + + fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup { + PrepareMarkup::With(html! { + div { + h1 { ("RESOURCE NOT FOUND") } + } + }) + } +} diff --git a/src/base/component/fluent.rs b/src/base/component/fluent.rs new file mode 100644 index 00000000..9e7220fa --- /dev/null +++ b/src/base/component/fluent.rs @@ -0,0 +1,25 @@ +use crate::prelude::*; + +#[derive(AutoDefault)] +pub struct Fluent(L10n); + +impl ComponentTrait for Fluent { + fn new() -> Self { + Fluent::default() + } + + fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { + PrepareMarkup::With(self.0.escaped(cx.langid())) + } +} + +impl Fluent { + pub fn with(l10n: L10n) -> Self { + Fluent(l10n) + } + + pub fn set_l10n(&mut self, l10n: L10n) -> &mut Self { + self.0 = l10n; + self + } +} diff --git a/src/base/component/html.rs b/src/base/component/html.rs index a60d30f9..6d308438 100644 --- a/src/base/component/html.rs +++ b/src/base/component/html.rs @@ -1,89 +1,25 @@ use crate::prelude::*; -/// Componente básico para renderizar dinámicamente código HTML recibiendo el contexto. -/// -/// Este componente permite generar contenido HTML arbitrario, usando la macro `html!` y accediendo -/// opcionalmente al contexto de renderizado. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let component = Html::with(|_| { -/// html! { -/// div class="example" { -/// p { "Hello from PageTop." } -/// } -/// } -/// }); -/// ``` -/// -/// Para renderizar contenido que dependa del contexto, se puede acceder a él dentro del *closure*: -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let component = Html::with(|cx| { -/// let user = cx.param::<String>("username").cloned().unwrap_or("visitor".to_string()); -/// html! { -/// h1 { "Hello, " (user) } -/// } -/// }); -/// ``` -pub struct Html(Box<dyn Fn(&mut Context) -> Markup + Send + Sync>); +#[derive(AutoDefault)] +pub struct Html(Markup); -impl Default for Html { - fn default() -> Self { - Html::with(|_| html! {}) - } -} -impl Component for Html { +impl ComponentTrait for Html { fn new() -> Self { Html::default() } - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - PrepareMarkup::With(self.html(cx)) + fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup { + PrepareMarkup::With(html! { (self.0) }) } } impl Html { - // **< Html BUILDER >*************************************************************************** - - /// Crea una instancia que generará el `Markup`, con acceso opcional al contexto. - /// - /// El método [`prepare_component()`](crate::core::component::Component::prepare_component) - /// delega el renderizado en la función proporcionada, que recibe una referencia mutable al - /// contexto de renderizado ([`Context`]). - pub fn with<F>(f: F) -> Self - where - F: Fn(&mut Context) -> Markup + Send + Sync + 'static, - { - Html(Box::new(f)) + pub fn with(html: Markup) -> Self { + Html(html) } - /// Sustituye la función que genera el `Markup`. - /// - /// Permite a otras extensiones modificar la función de renderizado que se ejecutará cuando - /// [`prepare_component()`](crate::core::component::Component::prepare_component) invoque esta - /// instancia. La nueva función también recibe una referencia al contexto ([`Context`]). - #[builder_fn] - pub fn with_fn<F>(mut self, f: F) -> Self - where - F: Fn(&mut Context) -> Markup + Send + Sync + 'static, - { - self.0 = Box::new(f); + pub fn set_html(&mut self, html: Markup) -> &mut Self { + self.0 = html; self } - - // **< Html GETTERS >*************************************************************************** - - /// Aplica la función interna de renderizado con el [`Context`] proporcionado. - /// - /// Normalmente no se invoca manualmente, ya que el proceso de renderizado de los componentes lo - /// invoca automáticamente durante la construcción de la página. Puede usarse, no obstante, para - /// sobrescribir [`prepare_component()`](crate::core::component::Component::prepare_component) - /// y alterar el comportamiento del componente. - pub fn html(&self, cx: &mut Context) -> Markup { - (self.0)(cx) - } } diff --git a/src/base/component/intro.rs b/src/base/component/intro.rs deleted file mode 100644 index 052f9c60..00000000 --- a/src/base/component/intro.rs +++ /dev/null @@ -1,319 +0,0 @@ -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_child( -/// Block::new() -/// .with_title(L10n::l("intro_custom_block_title")) -/// .add_child(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" { - (PageTopSvg::Color.render(cx)) - } - } - } - 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="noopener 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" { - (PageTopSvg::LineLight.render(cx)) - } - div class="intro-footer__links" { - a href="https://crates.io/crates/pagetop" target="_blank" rel="noopener noreferrer" { ("Crates.io") } - a href="https://docs.rs/pagetop" target="_blank" rel="noopener noreferrer" { ("Docs.rs") } - a href="https://git.cillero.es/manuelcillero/pagetop" target="_blank" rel="noopener 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("Intro title")); - /// ``` - #[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("A short slogan")); - /// ``` - #[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("Learn more"), |_| "/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. - #[inline] - pub fn add_child(mut self, component: impl Component) -> Self { - self.children.add(Child::with(component)); - self - } - - /// Modifica la lista de componentes (`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 componentes (`children`) de la intro. - pub fn children(&self) -> &Children { - &self.children - } -} diff --git a/src/base/component/poweredby.rs b/src/base/component/poweredby.rs deleted file mode 100644 index 797253dc..00000000 --- a/src/base/component/poweredby.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::prelude::*; - -// Enlace a la página oficial de PageTop. -const LINK: &str = "<a href=\"https://pagetop.cillero.es\" rel=\"noopener noreferrer\">PageTop</a>"; - -/// Componente que informa del 'Powered by' (*Funciona con*) típica del pie de página. -/// -/// Por defecto, usando [`default()`](Self::default) sólo se muestra un reconocimiento a PageTop. -/// Sin embargo, se puede usar [`new()`](Self::new) para crear una instancia con un texto de -/// copyright predeterminado. -#[derive(AutoDefault)] -pub struct PoweredBy { - copyright: Option<String>, -} - -impl Component for PoweredBy { - /// Crea una nueva instancia de `PoweredBy`. - /// - /// El copyright se genera automáticamente con el año actual y el nombre de la aplicación - /// configurada en [`global::SETTINGS`]. - fn new() -> Self { - let year = Utc::now().format("%Y").to_string(); - let c = join!(year, " © ", global::SETTINGS.app.name); - PoweredBy { copyright: Some(c) } - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - PrepareMarkup::With(html! { - div id=[self.id()] class="poweredby" { - @if let Some(c) = self.copyright() { - span class="poweredby__copyright" { (c) "." } " " - } - span class="poweredby__pagetop" { - (L10n::l("poweredby_pagetop").with_arg("pagetop_link", LINK).using(cx)) - } - } - }) - } -} - -impl PoweredBy { - // **< PoweredBy BUILDER >********************************************************************** - - /// Establece el texto de copyright que mostrará el componente. - /// - /// Al pasar `Some(valor)` se sobrescribe el texto de copyright por defecto. Al pasar `None` se - /// eliminará, pero en este caso es necesario especificar el tipo explícitamente: - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let p1 = PoweredBy::default().with_copyright(Some("2001 © Foo Inc.")); - /// let p2 = PoweredBy::new().with_copyright(None::<String>); - /// ``` - #[builder_fn] - pub fn with_copyright(mut self, copyright: Option<impl Into<String>>) -> Self { - self.copyright = copyright.map(Into::into); - self - } - - // **< PoweredBy GETTERS >********************************************************************** - - /// Devuelve el texto de copyright actual, si existe. - pub fn copyright(&self) -> Option<&str> { - self.copyright.as_deref() - } -} diff --git a/src/base/component/region.rs b/src/base/component/region.rs deleted file mode 100644 index 5dfa25ce..00000000 --- a/src/base/component/region.rs +++ /dev/null @@ -1,150 +0,0 @@ -use crate::prelude::*; - -/// Componente estructural que renderiza el contenido de una región del documento. -/// -/// `Region` actúa como un contenedor lógico asociado a un nombre de región. Su contenido se obtiene -/// del contexto de renderizado ([`Context`]), donde los componentes suelen registrarse con métodos -/// como [`Contextual::with_child_in()`]. Cada región puede integrarse posteriormente en el cuerpo -/// del documento mediante [`Template`], normalmente desde una página ([`Page`]). -#[derive(AutoDefault)] -pub struct Region { - #[default(AttrName::new(Self::DEFAULT))] - name: AttrName, - #[default(L10n::l("region-content"))] - label: L10n, -} - -impl Component for Region { - fn new() -> Self { - Region::default() - } - - fn id(&self) -> Option<String> { - self.name.get() - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let Some(name) = self.name().get() else { - return PrepareMarkup::None; - }; - let output = cx.render_region(&name); - if output.is_empty() { - return PrepareMarkup::None; - } - PrepareMarkup::With(html! { - div - id=[self.id()] - class=(join!("region region-", &name)) - role="region" - aria-label=[self.label().lookup(cx)] - { - (output) - } - }) - } -} - -impl Region { - /// Región especial situada al **inicio del documento**. - /// - /// Su función es proporcionar un punto estable donde las extensiones puedan inyectar contenido - /// global antes de renderizar el resto de regiones principales (cabecera, contenido, etc.). - /// - /// No suele utilizarse en los temas como una región “visible” dentro del maquetado habitual, - /// sino como punto de anclaje para elementos auxiliares, marcadores técnicos, inicializadores o - /// contenido de depuración que deban situarse en la parte superior del documento. - /// - /// Se considera una región **reservada** para este tipo de usos globales. - pub const PAGETOP: &str = "page-top"; - - /// Región estándar para la **cabecera** del documento. - /// - /// Suele emplearse para mostrar un logotipo, navegación principal, barras superiores, etc. - pub const HEADER: &str = "header"; - - /// Región principal de **contenido**. - /// - /// Es la región donde se espera que se renderice el contenido principal de la página (p. ej. - /// cuerpo de la ruta actual, bloques centrales, vistas principales, etc.). En muchos temas será - /// la región mínima imprescindible para que la página tenga sentido. - pub const CONTENT: &str = "content"; - - /// Región estándar para el **pie de página**. - /// - /// Suele contener información legal, enlaces secundarios, créditos, etc. - pub const FOOTER: &str = "footer"; - - /// Región especial situada al **final del documento**. - /// - /// Pensada para proporcionar un punto estable donde las extensiones puedan inyectar contenido - /// global después de renderizar el resto de regiones principales (cabecera, contenido, etc.). - /// - /// No suele utilizarse en los temas como una región “visible” dentro del maquetado habitual, - /// sino como punto de anclaje para elementos auxiliares asociados a comportamientos dinámicos - /// que deban situarse en la parte inferior del documento. - /// - /// Igual que [`Self::PAGETOP`], se considera una región **reservada** para este tipo de usos - /// globales. - pub const PAGEBOTTOM: &str = "page-bottom"; - - /// Región por defecto que se asigna cuando no se especifica ningún nombre. - /// - /// Por diseño, la región por defecto es la de contenido principal ([`Self::CONTENT`]), de - /// manera que un tema sencillo pueda limitarse a definir una sola región funcional. - pub const DEFAULT: &str = Self::CONTENT; - - /// Prepara una región para el nombre indicado. - /// - /// El valor de `name` se utiliza como nombre de la región y como identificador (`id`) del - /// contenedor. Al renderizarse, este componente mostrará el contenido registrado en el contexto - /// bajo ese nombre. - pub fn named(name: impl AsRef<str>) -> Self { - Region { - name: AttrName::new(name), - label: L10n::default(), - } - } - - /// Prepara una región para el nombre indicado con una etiqueta de accesibilidad. - /// - /// El valor de `name` se utiliza como nombre de la región y como identificador (`id`) del - /// contenedor, mientras que `label` será el texto localizado que se usará como `aria-label` del - /// contenedor. - pub fn labeled(name: impl AsRef<str>, label: L10n) -> Self { - Region { - name: AttrName::new(name), - label, - } - } - - // **< Region BUILDER >************************************************************************* - - /// Establece o modifica el nombre de la región. - #[builder_fn] - pub fn with_name(mut self, name: impl AsRef<str>) -> Self { - self.name.alter_value(name); - self - } - - /// Establece la etiqueta localizada de la región. - /// - /// Esta etiqueta se utiliza como `aria-label` del contenedor predefinido `<div role="region">`, - /// lo que mejora la accesibilidad para lectores de pantalla y otras tecnologías de apoyo. - #[builder_fn] - pub fn with_label(mut self, label: L10n) -> Self { - self.label = label; - self - } - - // **< Region GETTERS >************************************************************************* - - /// Devuelve el nombre de la región. - pub fn name(&self) -> &AttrName { - &self.name - } - - /// Devuelve la etiqueta localizada asociada a la región. - pub fn label(&self) -> &L10n { - &self.label - } -} diff --git a/src/base/component/template.rs b/src/base/component/template.rs deleted file mode 100644 index 6c70d00e..00000000 --- a/src/base/component/template.rs +++ /dev/null @@ -1,84 +0,0 @@ -use crate::prelude::*; - -/// Componente estructural para renderizar plantillas de contenido. -/// -/// `Template` describe cómo se compone el cuerpo del documento a partir de varias regiones lógicas -/// ([`Region`]). En función de su nombre, decide qué regiones se renderizan y en qué orden. -/// -/// Normalmente se invoca desde una página ([`Page`]), que consulta el nombre de plantilla guardado -/// en el [`Context`] y delega en `Template` la composición de las regiones que forman el cuerpo del -/// documento. -/// -/// Los temas pueden sobrescribir este componente para exponer sus propias plantillas o adaptar las -/// plantillas predeterminadas. -#[derive(AutoDefault)] -pub struct Template { - #[default(AttrName::new(Self::DEFAULT))] - name: AttrName, -} - -impl Component for Template { - fn new() -> Self { - Template::default() - } - - fn id(&self) -> Option<String> { - self.name.get() - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let Some(name) = self.name().get() else { - return PrepareMarkup::None; - }; - match name.as_str() { - Self::DEFAULT | Self::ERROR => PrepareMarkup::With(html! { - (Region::labeled(Region::HEADER, L10n::l("region-header")).render(cx)) - (Region::default().render(cx)) - (Region::labeled(Region::FOOTER, L10n::l("region-footer")).render(cx)) - }), - _ => PrepareMarkup::None, - } - } -} - -impl Template { - /// Nombre de la plantilla predeterminada. - /// - /// Por defecto define una estructura básica con las regiones [`Region::HEADER`], - /// [`Region::CONTENT`] y [`Region::FOOTER`], en ese orden. Esta plantilla se usa cuando no se - /// selecciona ninguna otra de forma explícita (ver [`Contextual::with_template()`]). - pub const DEFAULT: &str = "default"; - - /// Nombre de la plantilla de error. - /// - /// Se utiliza para páginas de error u otros estados excepcionales. Por defecto reutiliza - /// la misma estructura que [`Self::DEFAULT`], pero permite a temas y extensiones distinguir - /// el contexto de error para aplicar estilos o contenidos específicos. - pub const ERROR: &str = "error"; - - /// Selecciona la plantilla asociada al nombre indicado. - /// - /// El valor de `name` se utiliza como nombre de la plantilla y como identificador (`id`) del - /// componente. - pub fn named(name: impl AsRef<str>) -> Self { - Template { - name: AttrName::new(name), - } - } - - // **< Template BUILDER >*********************************************************************** - - /// Establece o modifica el nombre de la plantilla seleccionada. - #[builder_fn] - pub fn with_name(mut self, name: impl AsRef<str>) -> Self { - self.name.alter_value(name); - self - } - - // **< Template GETTERS >*********************************************************************** - - /// Devuelve el nombre de la plantilla seleccionada. - pub fn name(&self) -> &AttrName { - &self.name - } -} diff --git a/src/base/extension.rs b/src/base/extension.rs deleted file mode 100644 index 1f94fe23..00000000 --- a/src/base/extension.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Extensiones para funcionalidades avanzadas de PageTop. - -mod welcome; -pub use welcome::Welcome; diff --git a/src/base/extension/welcome.rs b/src/base/extension/welcome.rs deleted file mode 100644 index b875163b..00000000 --- a/src/base/extension/welcome.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::prelude::*; - -/// Página de bienvenida predeterminada de PageTop. -/// -/// Esta extensión se instala por defecto y muestra una página en la ruta raíz (`/`) cuando no se ha -/// configurado ninguna página de inicio personalizada. Permite confirmar que el servidor está -/// funcionando correctamente. -pub struct Welcome; - -impl Extension for Welcome { - fn name(&self) -> L10n { - L10n::l("welcome_extension_name") - } - - fn description(&self) -> L10n { - L10n::l("welcome_extension_description") - } - - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - scfg.route("/", service::web::get().to(homepage)); - } -} - -async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { - let app = &global::SETTINGS.app.name; - - Page::new(request) - .with_title(L10n::l("welcome_title")) - .add_child( - Intro::new() - .add_child( - Block::new() - .with_title(L10n::l("welcome_status_title")) - .add_child(Html::with(move |cx| { - html! { - p { (L10n::l("welcome_status_1").using(cx)) } - p { (L10n::l("welcome_status_2").using(cx)) } - } - })), - ) - .add_child( - Block::new() - .with_title(L10n::l("welcome_support_title")) - .add_child(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/package.rs b/src/base/package.rs new file mode 100644 index 00000000..7b633f33 --- /dev/null +++ b/src/base/package.rs @@ -0,0 +1,133 @@ +use crate::prelude::*; + +pub struct Welcome; + +impl PackageTrait for Welcome { + fn name(&self) -> L10n { + L10n::l("welcome_package_name") + } + + fn description(&self) -> L10n { + L10n::l("welcome_package_description") + } + + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { + scfg.route("/", service::web::get().to(homepage)); + } +} + +async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { + Page::new(request) + .with_title(L10n::l("welcome_page")) + .with_assets(AssetsOp::Theme("Basic")) + .with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##" + body { + background-color: #f3d060; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 20px; + } + .skip__to_content { + display: none; + } + .wrapper { + max-width: 1200px; + width: 100%; + margin: 0 auto; + padding: 0; + } + .container { + padding: 0 16px; + } + .title { + font-size: clamp(3rem, 10vw, 10rem); + letter-spacing: -0.05em; + line-height: 1.2; + margin: 0; + } + .subtitle { + font-size: clamp(1.8rem, 2vw, 3rem); + letter-spacing: -0.02em; + line-height: 1.2; + margin: 0; + } + .powered { + margin: .5em 0 1em; + } + .box-container { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: stretch; + gap: 1.5em; + } + .box { + flex: 1 1 280px; + border: 3px solid #25282a; + box-shadow: 5px 5px 0px #25282a; + box-sizing: border-box; + padding: 0 16px; + } + footer { + margin-top: 5em; + font-size: 14px; + font-weight: 500; + color: #a5282c; + } + "##))) + .with_component(Html::with(html! { + div class="wrapper" { + div class="container" { + h1 class="title" { (L10n::l("welcome_title").markup()) } + + p class="subtitle" { + (L10n::l("welcome_intro").with_arg("app", format!( + "<span style=\"font-weight: bold;\">{}</span>", + &global::SETTINGS.app.name + )).markup()) + } + p class="powered" { + (L10n::l("welcome_powered").with_arg("pagetop", format!( + "<a href=\"{}\" target=\"_blank\">{}</a>", + "https://crates.io/crates/pagetop", "PageTop" + )).markup()) + } + + h2 { (L10n::l("welcome_page").markup()) } + + div class="box-container" { + section class="box" style="background-color: #5eb0e5;" { + h3 { + (L10n::l("welcome_subtitle") + .with_arg("app", &global::SETTINGS.app.name) + .markup()) + } + p { (L10n::l("welcome_text1").markup()) } + p { (L10n::l("welcome_text2").markup()) } + } + section class="box" style="background-color: #aee1cd;" { + h3 { + (L10n::l("welcome_pagetop_title").markup()) + } + p { (L10n::l("welcome_pagetop_text1").markup()) } + p { (L10n::l("welcome_pagetop_text2").markup()) } + p { (L10n::l("welcome_pagetop_text3").markup()) } + } + section class="box" style="background-color: #ebebe3;" { + h3 { + (L10n::l("welcome_issues_title").markup()) + } + p { (L10n::l("welcome_issues_text1").markup()) } + p { + (L10n::l("welcome_issues_text2") + .with_arg("app", &global::SETTINGS.app.name) + .markup()) + } + } + } + + footer { "[ " (L10n::l("welcome_have_fun").markup()) " ]" } + } + } + })) + .render() +} diff --git a/src/base/theme.rs b/src/base/theme.rs index 4a13a4e4..c07ffc56 100644 --- a/src/base/theme.rs +++ b/src/base/theme.rs @@ -1,4 +1,11 @@ -//! Tema básico soportados por PageTop. +use crate::prelude::*; -mod basic; -pub use basic::Basic; +pub struct Basic; + +impl PackageTrait for Basic { + fn theme(&self) -> Option<ThemeRef> { + Some(&Basic) + } +} + +impl ThemeTrait for Basic {} diff --git a/src/base/theme/basic.rs b/src/base/theme/basic.rs deleted file mode 100644 index eb2274f6..00000000 --- a/src/base/theme/basic.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// Es el tema básico que incluye PageTop por defecto. -use crate::prelude::*; - -/// Tema básico por defecto que extiende el funcionamiento predeterminado de [`Theme`]. -pub struct Basic; - -impl Extension for Basic { - fn theme(&self) -> Option<ThemeRef> { - Some(&Self) - } -} - -impl Theme for Basic { - fn before_render_page_body(&self, page: &mut Page) { - page.alter_param("include_basic_assets", true); - } -} diff --git a/src/config.rs b/src/config.rs index 1607a874..07924057 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,74 +1,76 @@ -//! Carga las opciones de configuración. +//! Load configuration settings. //! -//! Estos ajustes se obtienen de archivos [TOML](https://toml.io) como pares `clave = valor` que se -//! mapean a estructuras **fuertemente tipadas** y valores predefinidos. +//! These settings are loaded from [TOML](https://toml.io) files as `key = value` pairs and mapped +//! into type-safe structures with predefined values. //! -//! Siguiendo la metodología [Twelve-Factor App](https://12factor.net/config), PageTop separa el -//! **código** de la **configuración**, lo que permite tener configuraciones diferentes para cada -//! despliegue, como *dev*, *staging* o *production*, sin modificar el código fuente. +//! Following the [Twelve-Factor App](https://12factor.net/config) methodology, `PageTop` separates +//! code from configuration. This approach allows configurations to vary across deployments, such as +//! development, staging, or production, without changing the codebase. //! //! -//! # Orden de carga +//! # Loading configuration settings //! -//! Si tu aplicación necesita archivos de configuración, crea un directorio `config` en la raíz del -//! proyecto, al mismo nivel que el archivo *Cargo.toml* o que el binario de la aplicación. +//! If your application requires configuration files, create a `config` directory in the root of +//! your project, at the same level as the *Cargo.toml* file or the application's binary. //! -//! PageTop carga en este orden, y siempre de forma opcional, los siguientes archivos TOML: +//! `PageTop` automatically loads configuration settings by reading the following TOML files in +//! order (all files are optional): //! -//! 1. **config/common.toml**, para ajustes comunes a todos los entornos. Este enfoque simplifica el -//! mantenimiento al centralizar los valores de configuración comunes. +//! 1. **config/common.toml**, for settings shared across all environments. This approach simplifies +//! maintenance by centralizing common configuration values. //! -//! 2. **config/{rm}.toml**, donde `{rm}` es el valor de la variable de entorno `PAGETOP_RUN_MODE`: +//! 2. **config/{rm}.toml**, where `{rm}` corresponds to the environment variable +//! `PAGETOP_RUN_MODE`: //! -//! * Si `PAGETOP_RUN_MODE` no está definida, se asume el valor `default`, y PageTop intentará -//! cargar *config/default.toml* si el archivo existe. +//! * If `PAGETOP_RUN_MODE` is not set, it defaults to `default`, and `PageTop` attempts to load +//! *config/default.toml* if available. //! -//! * Permite definir configuraciones específicas por entorno, garantizando que cada uno (p. ej., -//! *dev*, *staging* o *production*) disponga de sus propias opciones, como claves de API, URLs -//! o ajustes de rendimiento, sin afectar a los demás. +//! * Useful for environment-specific configurations, ensuring that each environment +//! (e.g., development, staging, production) has its own settings without affecting others, +//! such as API keys, URLs, or performance-related adjustments. //! -//! 3. **config/local.{rm}.toml**, útil para configuraciones locales específicas de la máquina o de -//! la ejecución: +//! 3. **config/local.{rm}.toml**, useful for local machine-specific configurations: //! -//! * Permite añadir o sobrescribir ajustes propios del entorno. Por ejemplo, `local.dev.toml` -//! para desarrollo o `local.production.toml` para retoques en producción. +//! * This file allows you to add or override settings specific to the environment. For example, +//! `local.devel.toml` for development or `local.production.toml` for production tweaks. //! -//! * Facilita que cada desarrollador adapte la configuración a su equipo en un entorno dado. Por -//! lo general no se comparte ni se sube al sistema de control de versiones. +//! * It enables developers to tailor settings for their machines within a given environment and +//! is typically not shared or committed to version control systems. //! -//! 4. **config/local.toml**, para ajustes locales válidos en cualquier entorno, ideal para cambios -//! rápidos o valores temporales que no dependan de un entorno concreto. +//! 4. **config/local.toml**, for general local settings across all environments, ideal for quick +//! adjustments or temporary values not tied to any specific environment. //! -//! Los archivos se combinan en el orden anterior, cada archivo sobrescribe a los anteriores en caso -//! de conflicto. +//! The configuration settings are merged in the order listed above, with later files overriding +//! earlier ones if there are conflicts. //! //! -//! # Cómo añadir opciones de configuración a tu código +//! # Adding configuration settings //! -//! Añade [*serde*](https://docs.rs/serde) en tu archivo *Cargo.toml* con la *feature* `derive`: +//! To give your **module** its own configuration settings, add [*serde*](https://docs.rs/serde) as +//! a dependency in your *Cargo.toml* file with the `derive` feature enabled: //! //! ```toml //! [dependencies] //! serde = { version = "1.0", features = ["derive"] } //! ``` //! -//! Y usa la macro [`include_config!`](crate::include_config) para inicializar tus ajustes en una -//! estructura con tipos seguros. Por ejemplo: +//! Then, use the [`include_config!`](crate::include_config) macro to initialize your settings with +//! type-safe structures and predefined values: //! -//! ```rust,no_run +//! ``` //! use pagetop::prelude::*; //! use serde::Deserialize; //! //! include_config!(SETTINGS: Settings => [ //! // [myapp] -//! "myapp.name" => "Value Name", -//! "myapp.width" => 900, +//! "myapp.name" => "Value Name", +//! "myapp.width" => 900, //! "myapp.height" => 320, //! ]); //! //! #[derive(Debug, Deserialize)] //! pub struct Settings { -//! pub myapp: MyApp, +//! pub myapp: MyApp, //! } //! //! #[derive(Debug, Deserialize)] @@ -80,175 +82,115 @@ //! } //! ``` //! -//! De esta forma estás añadiendo una nueva sección `[myapp]` a la configuración, igual que existen -//! `[app]` o `[server]` en las opciones globales de [`Settings`](crate::global::Settings). +//! This is how global configuration settings are declared (see [`SETTINGS`](crate::global::SETTINGS)). //! -//! Se recomienda proporcionar siempre valores por defecto o usar `Option<T>` para los ajustes -//! opcionales. +//! You can add a new `[myapp]` section in the configuration files using the +//! [TOML syntax](https://toml.io/en/v1.0.0#table), just like the `[log]` or `[server]` sections in +//! the global settings (see [`Settings`](crate::global::Settings)). //! -//! Si la configuración no se inicializa correctamente, la aplicación lanzará *panic* y detendrá la -//! ejecución. +//! It is recommended to initialize all settings with predefined values or use `Option<T>` for +//! optional settings handled within the code. //! -//! Las estructuras de configuración son de **sólo lectura** durante la ejecución. +//! If configuration settings fail to initialize correctly, the application will panic and stop +//! execution. +//! +//! Configuration settings are always read-only. //! //! -//! # Usando tus opciones de configuración +//! # Using your new configuration settings //! -//! ```rust,ignore +//! Access the settings directly in your code: +//! +//! ``` //! use pagetop::prelude::*; //! use crate::config; //! //! fn global_settings() { -//! println!("Nombre de la app: {}", &global::SETTINGS.app.name); -//! println!("Descripción: {}", &global::SETTINGS.app.description); -//! println!("Run mode: {}", &global::SETTINGS.app.run_mode); +//! println!("App name: {}", &global::SETTINGS.app.name); +//! println!("App description: {}", &global::SETTINGS.app.description); +//! println!("Value of PAGETOP_RUN_MODE: {}", &global::SETTINGS.app.run_mode); //! } //! -//! fn extension_settings() { +//! fn package_settings() { //! println!("{} - {:?}", &config::SETTINGS.myapp.name, &config::SETTINGS.myapp.description); //! println!("{}", &config::SETTINGS.myapp.width); //! } //! ``` -use crate::util; +mod data; +mod de; +mod error; +mod file; +mod path; +mod source; +mod value; -use config::builder::DefaultState; -use config::{Config, ConfigBuilder, File}; +use crate::concat_string; +use crate::config::data::ConfigData; +use crate::config::file::File; -use std::env; -use std::path::PathBuf; use std::sync::LazyLock; -// Nombre del directorio de configuración por defecto. -const DEFAULT_CONFIG_DIR: &str = "config"; +use std::env; +use std::path::Path; -// Modo de ejecución por defecto. -const DEFAULT_RUN_MODE: &str = "default"; +/// Original values read from configuration files in `key = value` pairs. +pub static CONFIG_VALUES: LazyLock<ConfigData> = LazyLock::new(|| { + // Identify the configuration directory. + let config_dir = env::var("CARGO_MANIFEST_DIR") + .map(|manifest_dir| { + let manifest_config = Path::new(&manifest_dir).join("config"); + if manifest_config.exists() { + manifest_config.to_string_lossy().to_string() + } else { + "config".to_string() + } + }) + .unwrap_or_else(|_| "config".to_string()); -/// Valores originales cargados desde los archivos de configuración como pares `clave = valor`. -pub static CONFIG_VALUES: LazyLock<ConfigBuilder<DefaultState>> = LazyLock::new(|| { - // CONFIG_DIR (si existe) o DEFAULT_CONFIG_DIR. Si no se puede resolver, se usa tal cual. - let dir = env::var_os("CONFIG_DIR").unwrap_or_else(|| DEFAULT_CONFIG_DIR.into()); - let config_dir = util::resolve_absolute_dir(&dir).unwrap_or_else(|_| PathBuf::from(&dir)); + // Execution mode based on the environment variable PAGETOP_RUN_MODE, defaults to 'default'. + let rm = env::var("PAGETOP_RUN_MODE").unwrap_or_else(|_| "default".into()); - // Modo de ejecución según la variable de entorno PAGETOP_RUN_MODE. Si no está definida, se usa - // por defecto DEFAULT_RUN_MODE (p. ej. PAGETOP_RUN_MODE=production). - let rm = env::var("PAGETOP_RUN_MODE").unwrap_or_else(|_| DEFAULT_RUN_MODE.into()); + // Initialize config values. + let mut values = ConfigData::default(); - Config::builder() - // 1. Configuración común para todos los entornos (common.toml). - .add_source(File::from(config_dir.join("common.toml")).required(false)) - // 2. Configuración específica del entorno (p. ej. default.toml o production.toml). - .add_source(File::from(config_dir.join(format!("{rm}.toml"))).required(false)) - // 3. Configuración local reservada para cada entorno (p. ej. local.default.toml). - .add_source(File::from(config_dir.join(format!("local.{rm}.toml"))).required(false)) - // 4. Configuración local común (local.toml). - .add_source(File::from(config_dir.join("local.toml")).required(false)) - // Guarda el modo de ejecución explícitamente. - .set_override("app.run_mode", rm) - .expect("Failed to set application run mode") + // Merge (optional) configuration files and set the execution mode. + values + // First, add the common configuration for all environments. Defaults to 'common.toml'. + .merge(File::with_name(&concat_string!(config_dir, "/common.toml")).required(false)) + .expect("Failed to merge common configuration (common.toml)") + // Add the environment-specific configuration. Defaults to 'default.toml'. + .merge(File::with_name(&concat_string!(config_dir, "/", rm, ".toml")).required(false)) + .expect(&format!("Failed to merge {rm}.toml configuration")) + // Add reserved local configuration for the environment. Defaults to 'local.default.toml'. + .merge(File::with_name(&concat_string!(config_dir, "/local.", rm, ".toml")).required(false)) + .expect("Failed to merge reserved local environment configuration") + // Add common reserved local configuration. Defaults to 'local.toml'. + .merge(File::with_name(&concat_string!(config_dir, "/local.toml")).required(false)) + .expect("Failed to merge general reserved local configuration") + // Save the execution mode. + .set("app.run_mode", rm) + .expect("Failed to set application run mode"); + + values }); -/// Incluye los ajustes necesarios de la configuración anticipando valores por defecto. -/// -/// # Sintaxis -/// -/// Hay que añadir en nuestra librería el siguiente código: -/// -/// ```rust,ignore -/// include_config!(SETTINGS: Settings => [ -/// "ruta.clave" => valor, -/// // ... -/// ]); -/// ``` -/// -/// donde: -/// -/// * **`SETTINGS_NAME`** es el nombre de la variable global que se usará para referenciar los -/// ajustes. Se recomienda usar `SETTINGS`, aunque no es obligatorio. -/// * **`Settings_Type`** es la referencia a la estructura que define los tipos para deserializar la -/// configuración. Debe implementar `Deserialize` (derivable con `#[derive(Deserialize)]`). -/// * **Lista de pares** con las claves TOML que requieran valores por defecto. Siguen la notación -/// `"seccion.subclave"` para coincidir con el árbol TOML. -/// -/// # Ejemplo básico -/// -/// ```rust,no_run -/// use pagetop::prelude::*; -/// use serde::Deserialize; -/// -/// include_config!(SETTINGS: BlogSettings => [ -/// // [blog] -/// "blog.title" => "Mi Blog", -/// "blog.port" => 8080, -/// ]); -/// -/// #[derive(Debug, Deserialize)] -/// pub struct BlogSettings { -/// pub blog: Blog, -/// } -/// -/// #[derive(Debug, Deserialize)] -/// pub struct Blog { -/// pub title: String, -/// pub description: Option<String>, -/// pub port: u16, -/// } -/// -/// fn print_title() { -/// // Lectura en tiempo de ejecución. -/// println!("Título: {}", SETTINGS.blog.title); -/// } -/// ``` -/// -/// # Buenas prácticas -/// -/// * **Valores por defecto**. Declara un valor por defecto para cada clave obligatoria. Las claves -/// opcionales pueden ser `Option<T>`. -/// -/// * **Secciones únicas**. Agrupa tus claves dentro de una sección exclusiva (p. ej. `[blog]`) para -/// evitar colisiones con otras librerías. -/// -/// * **Solo lectura**. La variable generada es inmutable durante toda la vida del programa. Para -/// configurar distintos entornos (*dev*, *staging*, *prod*) usa los archivos TOML descritos en la -/// documentación de [`config`](crate::config). -/// -/// * **Errores explícitos**. Si la deserialización falla, la macro lanzará un `panic!` con un -/// mensaje que indica la estructura problemática, facilitando la depuración. -/// -/// # Requisitos -/// -/// * Dependencia `serde` con la *feature* `derive`. -/// * Las claves deben coincidir con los campos (*snake case*) de tu estructura `Settings_Type`. -/// -/// ```toml -/// [dependencies] -/// serde = { version = "1.0", features = ["derive"] } -/// ``` #[macro_export] macro_rules! include_config { - ( $SETTINGS_NAME:ident : $Settings_Type:ty => [ $( $k:literal => $v:expr ),* $(,)? ] ) => { + ( $SETTINGS:ident: $Settings:ty => [ $($key:literal => $value:literal),* $(,)? ] ) => { #[doc = concat!( - "Instancia los ajustes de configuración para [`", stringify!($Settings_Type), "`]." + "Assigned or predefined values for configuration settings associated to the ", + "[`", stringify!($Settings), "`] type." )] - #[doc = ""] - #[doc = "Valores por defecto:"] - #[doc = "```text"] - $( - #[doc = concat!($k, " = ", stringify!($v))] - )* - #[doc = "```"] - pub static $SETTINGS_NAME: std::sync::LazyLock<$Settings_Type> = - std::sync::LazyLock::new(|| { - let mut settings = $crate::config::CONFIG_VALUES.clone(); - $( - settings = settings.set_default($k, $v).unwrap(); - )* - settings - .build() - .expect(concat!("Failed to build config for ", stringify!($Settings_Type))) - .try_deserialize::<$Settings_Type>() - .expect(concat!("Error parsing settings for ", stringify!($Settings_Type))) - }); + pub static $SETTINGS: std::sync::LazyLock<$Settings> = std::sync::LazyLock::new(|| { + let mut settings = $crate::config::CONFIG_VALUES.clone(); + $( + settings.set_default($key, $value).unwrap(); + )* + match settings.try_into() { + Ok(s) => s, + Err(e) => panic!("Error parsing settings: {}", e), + } + }); }; } diff --git a/src/config/data.rs b/src/config/data.rs new file mode 100644 index 00000000..22fe8359 --- /dev/null +++ b/src/config/data.rs @@ -0,0 +1,136 @@ +use crate::config::error::*; +use crate::config::path; +use crate::config::source::Source; +use crate::config::value::Value; + +use serde::de::Deserialize; + +use std::collections::HashMap; +use std::fmt::Debug; + +#[derive(Clone, Debug)] +enum ConfigKind { + // A mutable configuration. This is the default. + Mutable { + defaults: HashMap<path::Expression, Value>, + overrides: HashMap<path::Expression, Value>, + sources: Vec<Box<dyn Source + Send + Sync>>, + }, +} + +impl Default for ConfigKind { + fn default() -> Self { + ConfigKind::Mutable { + defaults: HashMap::new(), + overrides: HashMap::new(), + sources: Vec::new(), + } + } +} + +/// A prioritized configuration repository. It maintains a set of configuration sources, fetches +/// values to populate those, and provides them according to the source's priority. +#[derive(Default, Clone, Debug)] +pub struct ConfigData { + kind: ConfigKind, + /// Root of the cached configuration. + pub cache: Value, +} + +impl ConfigData { + /// Merge in a configuration property source. + pub fn merge<T>(&mut self, source: T) -> Result<&mut ConfigData> + where + T: 'static, + T: Source + Send + Sync, + { + match self.kind { + ConfigKind::Mutable { + ref mut sources, .. + } => { + sources.push(Box::new(source)); + } + } + + self.refresh() + } + + /// Refresh the configuration cache with fresh data from added sources. + /// + /// Configuration is automatically refreshed after a mutation operation (`set`, `merge`, + /// `set_default`, etc.). + pub fn refresh(&mut self) -> Result<&mut ConfigData> { + self.cache = match self.kind { + // TODO: We need to actually merge in all the stuff. + ConfigKind::Mutable { + ref overrides, + ref sources, + ref defaults, + } => { + let mut cache: Value = HashMap::<String, Value>::new().into(); + + // Add defaults. + for (key, val) in defaults { + key.set(&mut cache, val.clone()); + } + + // Add sources. + sources.collect_to(&mut cache)?; + + // Add overrides. + for (key, val) in overrides { + key.set(&mut cache, val.clone()); + } + + cache + } + }; + + Ok(self) + } + + pub fn set_default<T>(&mut self, key: &str, value: T) -> Result<&mut ConfigData> + where + T: Into<Value>, + { + match self.kind { + ConfigKind::Mutable { + ref mut defaults, .. + } => { + defaults.insert(key.parse()?, value.into()); + } + }; + + self.refresh() + } + + pub fn set<T>(&mut self, key: &str, value: T) -> Result<&mut ConfigData> + where + T: Into<Value>, + { + match self.kind { + ConfigKind::Mutable { + ref mut overrides, .. + } => { + overrides.insert(key.parse()?, value.into()); + } + }; + + self.refresh() + } + + /// Attempt to deserialize the entire configuration into the requested type. + pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> { + T::deserialize(self) + } +} + +impl Source for ConfigData { + fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result<HashMap<String, Value>> { + self.cache.clone().into_table() + } +} diff --git a/src/config/de.rs b/src/config/de.rs new file mode 100644 index 00000000..875219af --- /dev/null +++ b/src/config/de.rs @@ -0,0 +1,462 @@ +use crate::config::data::ConfigData; +use crate::config::error::*; +use crate::config::value::{Table, Value, ValueKind}; + +use serde::de; +use serde::forward_to_deserialize_any; + +use std::collections::{HashMap, VecDeque}; +use std::iter::Enumerate; + +impl<'de> de::Deserializer<'de> for Value { + type Error = ConfigError; + + #[inline] + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Deserialize based on the underlying type. + match self.kind { + ValueKind::Nil => visitor.visit_unit(), + ValueKind::Integer(i) => visitor.visit_i64(i), + ValueKind::Boolean(b) => visitor.visit_bool(b), + ValueKind::Float(f) => visitor.visit_f64(f), + ValueKind::String(s) => visitor.visit_string(s), + ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)), + ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)), + } + } + + #[inline] + fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_bool(self.into_bool()?) + } + + #[inline] + fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i8(self.into_int()? as i8) + } + + #[inline] + fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i16(self.into_int()? as i16) + } + + #[inline] + fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i32(self.into_int()? as i32) + } + + #[inline] + fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_i64(self.into_int()?) + } + + #[inline] + fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u8(self.into_int()? as u8) + } + + #[inline] + fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u16(self.into_int()? as u16) + } + + #[inline] + fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u32(self.into_int()? as u32) + } + + #[inline] + fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u64(self.into_int()? as u64) + } + + #[inline] + fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f32(self.into_float()? as f32) + } + + #[inline] + fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f64(self.into_float()?) + } + + #[inline] + fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.into_str()?) + } + + #[inline] + fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.into_str()?) + } + + #[inline] + fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Match an explicit nil as None and everything else as Some. + match self.kind { + ValueKind::Nil => visitor.visit_none(), + _ => visitor.visit_some(self), + } + } + + fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + visitor.visit_newtype_struct(self) + } + + fn deserialize_enum<V>( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + visitor.visit_enum(EnumAccess { + value: self, + name, + variants, + }) + } + + forward_to_deserialize_any! { + char seq + bytes byte_buf map struct unit + identifier ignored_any unit_struct tuple_struct tuple + } +} + +struct StrDeserializer<'a>(&'a str); + +impl<'de, 'a> de::Deserializer<'de> for StrDeserializer<'a> { + type Error = ConfigError; + + #[inline] + fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_str(self.0) + } + + forward_to_deserialize_any! { + bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq + bytes byte_buf map struct unit enum newtype_struct + identifier ignored_any unit_struct tuple_struct tuple option + } +} + +struct SeqAccess { + elements: Enumerate<::std::vec::IntoIter<Value>>, +} + +impl SeqAccess { + fn new(elements: Vec<Value>) -> Self { + SeqAccess { + elements: elements.into_iter().enumerate(), + } + } +} + +impl<'de> de::SeqAccess<'de> for SeqAccess { + type Error = ConfigError; + + fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>> + where + T: de::DeserializeSeed<'de>, + { + match self.elements.next() { + Some((idx, value)) => seed + .deserialize(value) + .map(Some) + .map_err(|e| e.prepend_index(idx)), + None => Ok(None), + } + } + + fn size_hint(&self) -> Option<usize> { + match self.elements.size_hint() { + (lower, Some(upper)) if lower == upper => Some(upper), + _ => None, + } + } +} + +struct MapAccess { + elements: VecDeque<(String, Value)>, +} + +impl MapAccess { + fn new(table: HashMap<String, Value>) -> Self { + MapAccess { + elements: table.into_iter().collect(), + } + } +} + +impl<'de> de::MapAccess<'de> for MapAccess { + type Error = ConfigError; + + fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>> + where + K: de::DeserializeSeed<'de>, + { + if let Some((key_s, _)) = self.elements.front() { + let key_de = Value::new(None, key_s as &str); + let key = de::DeserializeSeed::deserialize(seed, key_de)?; + + Ok(Some(key)) + } else { + Ok(None) + } + } + + fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value> + where + V: de::DeserializeSeed<'de>, + { + let (key, value) = self.elements.pop_front().unwrap(); + de::DeserializeSeed::deserialize(seed, value).map_err(|e| e.prepend_key(key)) + } +} + +struct EnumAccess { + value: Value, + name: &'static str, + variants: &'static [&'static str], +} + +impl EnumAccess { + fn variant_deserializer(&self, name: &str) -> Result<StrDeserializer> { + self.variants + .iter() + .find(|&&s| s == name) + .map(|&s| StrDeserializer(s)) + .ok_or_else(|| self.no_constructor_error(name)) + } + + fn table_deserializer(&self, table: &Table) -> Result<StrDeserializer> { + if table.len() == 1 { + self.variant_deserializer(table.iter().next().unwrap().0) + } else { + Err(self.structural_error()) + } + } + + fn no_constructor_error(&self, supposed_variant: &str) -> ConfigError { + ConfigError::Message(format!( + "enum {} does not have variant constructor {}", + self.name, supposed_variant + )) + } + + fn structural_error(&self) -> ConfigError { + ConfigError::Message(format!( + "value of enum {} should be represented by either string or table with exactly one key", + self.name + )) + } +} + +impl<'de> de::EnumAccess<'de> for EnumAccess { + type Error = ConfigError; + type Variant = Self; + + fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)> + where + V: de::DeserializeSeed<'de>, + { + let value = { + let deserializer = match self.value.kind { + ValueKind::String(ref s) => self.variant_deserializer(s), + ValueKind::Table(ref t) => self.table_deserializer(t), + _ => Err(self.structural_error()), + }?; + seed.deserialize(deserializer)? + }; + + Ok((value, self)) + } +} + +impl<'de> de::VariantAccess<'de> for EnumAccess { + type Error = ConfigError; + + fn unit_variant(self) -> Result<()> { + Ok(()) + } + + fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value> + where + T: de::DeserializeSeed<'de>, + { + match self.value.kind { + ValueKind::Table(t) => seed.deserialize(t.into_iter().next().unwrap().1), + _ => unreachable!(), + } + } + + fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + match self.value.kind { + ValueKind::Table(t) => { + de::Deserializer::deserialize_seq(t.into_iter().next().unwrap().1, visitor) + } + _ => unreachable!(), + } + } + + fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + match self.value.kind { + ValueKind::Table(t) => { + de::Deserializer::deserialize_map(t.into_iter().next().unwrap().1, visitor) + } + _ => unreachable!(), + } + } +} + +impl<'de> de::Deserializer<'de> for ConfigData { + type Error = ConfigError; + + #[inline] + fn deserialize_any<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Deserialize based on the underlying type. + match self.cache.kind { + ValueKind::Nil => visitor.visit_unit(), + ValueKind::Integer(i) => visitor.visit_i64(i), + ValueKind::Boolean(b) => visitor.visit_bool(b), + ValueKind::Float(f) => visitor.visit_f64(f), + ValueKind::String(s) => visitor.visit_string(s), + ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)), + ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)), + } + } + + #[inline] + fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_bool(self.cache.into_bool()?) + } + + #[inline] + fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i8(self.cache.into_int()? as i8) + } + + #[inline] + fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i16(self.cache.into_int()? as i16) + } + + #[inline] + fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_i32(self.cache.into_int()? as i32) + } + + #[inline] + fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_i64(self.cache.into_int()?) + } + + #[inline] + fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u8(self.cache.into_int()? as u8) + } + + #[inline] + fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u16(self.cache.into_int()? as u16) + } + + #[inline] + fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u32(self.cache.into_int()? as u32) + } + + #[inline] + fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + // FIXME: This should *fail* if the value does not fit in the requets integer type. + visitor.visit_u64(self.cache.into_int()? as u64) + } + + #[inline] + fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f32(self.cache.into_float()? as f32) + } + + #[inline] + fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_f64(self.cache.into_float()?) + } + + #[inline] + fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.cache.into_str()?) + } + + #[inline] + fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { + visitor.visit_string(self.cache.into_str()?) + } + + #[inline] + fn deserialize_option<V>(self, visitor: V) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + // Match an explicit nil as None and everything else as Some. + match self.cache.kind { + ValueKind::Nil => visitor.visit_none(), + _ => visitor.visit_some(self), + } + } + + fn deserialize_enum<V>( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result<V::Value> + where + V: de::Visitor<'de>, + { + visitor.visit_enum(EnumAccess { + value: self.cache, + name, + variants, + }) + } + + forward_to_deserialize_any! { + char seq + bytes byte_buf map struct unit newtype_struct + identifier ignored_any unit_struct tuple_struct tuple + } +} diff --git a/src/config/error.rs b/src/config/error.rs new file mode 100644 index 00000000..be3649c0 --- /dev/null +++ b/src/config/error.rs @@ -0,0 +1,222 @@ +use nom; +use serde::de; +use serde::ser; + +use std::error::Error; +use std::fmt; +use std::result; + +#[derive(Debug)] +pub enum Unexpected { + Bool(bool), + Integer(i64), + Float(f64), + Str(String), + Unit, + Seq, + Map, +} + +impl fmt::Display for Unexpected { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match *self { + Unexpected::Bool(b) => write!(f, "boolean `{}`", b), + Unexpected::Integer(i) => write!(f, "integer `{}`", i), + Unexpected::Float(v) => write!(f, "floating point `{}`", v), + Unexpected::Str(ref s) => write!(f, "string {:?}", s), + Unexpected::Unit => write!(f, "unit value"), + Unexpected::Seq => write!(f, "sequence"), + Unexpected::Map => write!(f, "map"), + } + } +} + +/// Represents all possible errors that can occur when working with configuration. +pub enum ConfigError { + /// Configuration is frozen and no further mutations can be made. + Frozen, + + /// Configuration property was not found. + NotFound(String), + + /// Configuration path could not be parsed. + PathParse(nom::error::ErrorKind), + + /// Configuration could not be parsed from file. + FileParse { + /// The URI used to access the file (if not loaded from a string). + /// Example: `/path/to/config.json` + uri: Option<String>, + + /// The captured error from attempting to parse the file in its desired format. + /// This is the actual error object from the library used for the parsing. + cause: Box<dyn Error + Send + Sync>, + }, + + /// Value could not be converted into the requested type. + Type { + /// The URI that references the source that the value came from. + /// Example: `/path/to/config.json` or `Environment` or `etcd://localhost` + // TODO: Why is this called Origin but FileParse has a uri field? + origin: Option<String>, + + /// What we found when parsing the value. + unexpected: Unexpected, + + /// What was expected when parsing the value. + expected: &'static str, + + /// The key in the configuration hash of this value (if available where the error is + /// generated). + key: Option<String>, + }, + + /// Custom message. + Message(String), + + /// Unadorned error from a foreign origin. + Foreign(Box<dyn Error + Send + Sync>), +} + +impl ConfigError { + // FIXME: pub(crate). + #[doc(hidden)] + pub fn invalid_type( + origin: Option<String>, + unexpected: Unexpected, + expected: &'static str, + ) -> Self { + ConfigError::Type { + origin, + unexpected, + expected, + key: None, + } + } + + // FIXME: pub(crate). + #[doc(hidden)] + pub fn extend_with_key(self, key: &str) -> Self { + match self { + ConfigError::Type { + origin, + unexpected, + expected, + .. + } => ConfigError::Type { + origin, + unexpected, + expected, + key: Some(key.into()), + }, + + _ => self, + } + } + + fn prepend(self, segment: String, add_dot: bool) -> Self { + let concat = |key: Option<String>| { + let key = key.unwrap_or_default(); + let dot = if add_dot && key.as_bytes().first().unwrap_or(&b'[') != &b'[' { + "." + } else { + "" + }; + format!("{}{}{}", segment, dot, key) + }; + match self { + ConfigError::Type { + origin, + unexpected, + expected, + key, + } => ConfigError::Type { + origin, + unexpected, + expected, + key: Some(concat(key)), + }, + ConfigError::NotFound(key) => ConfigError::NotFound(concat(Some(key))), + _ => self, + } + } + + pub(crate) fn prepend_key(self, key: String) -> Self { + self.prepend(key, true) + } + + pub(crate) fn prepend_index(self, idx: usize) -> Self { + self.prepend(format!("[{}]", idx), false) + } +} + +/// Alias for a `Result` with the error type set to `ConfigError`. +pub type Result<T> = result::Result<T, ConfigError>; + +// Forward Debug to Display for readable panic! messages. +impl fmt::Debug for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", *self) + } +} + +impl fmt::Display for ConfigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConfigError::Frozen => write!(f, "configuration is frozen"), + + ConfigError::PathParse(ref kind) => write!(f, "{}", kind.description()), + + ConfigError::Message(ref s) => write!(f, "{}", s), + + ConfigError::Foreign(ref cause) => write!(f, "{}", cause), + + ConfigError::NotFound(ref key) => { + write!(f, "configuration property {:?} not found", key) + } + + ConfigError::Type { + ref origin, + ref unexpected, + expected, + ref key, + } => { + write!(f, "invalid type: {}, expected {}", unexpected, expected)?; + + if let Some(ref key) = *key { + write!(f, " for key `{}`", key)?; + } + + if let Some(ref origin) = *origin { + write!(f, " in {}", origin)?; + } + + Ok(()) + } + + ConfigError::FileParse { ref cause, ref uri } => { + write!(f, "{}", cause)?; + + if let Some(ref uri) = *uri { + write!(f, " in {}", uri)?; + } + + Ok(()) + } + } + } +} + +impl Error for ConfigError {} + +impl de::Error for ConfigError { + fn custom<T: fmt::Display>(msg: T) -> Self { + ConfigError::Message(msg.to_string()) + } +} + +impl ser::Error for ConfigError { + fn custom<T: fmt::Display>(msg: T) -> Self { + ConfigError::Message(msg.to_string()) + } +} diff --git a/src/config/file.rs b/src/config/file.rs new file mode 100644 index 00000000..00f0c34d --- /dev/null +++ b/src/config/file.rs @@ -0,0 +1,85 @@ +mod source; +mod toml; + +use crate::config::error::*; +use crate::config::source::Source; +use crate::config::value::Value; + +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +use self::source::FileSource; + +#[derive(Clone, Debug)] +pub struct File<T> +where + T: FileSource, +{ + source: T, + /// A required File will error if it cannot be found. + required: bool, +} + +impl File<source::FileSourceFile> { + /// Given the basename of a file, will attempt to locate a file by setting its extension to a + /// registered format. + pub fn with_name(name: &str) -> Self { + File { + source: source::FileSourceFile::new(name.into()), + required: true, + } + } +} + +impl<'a> From<&'a Path> for File<source::FileSourceFile> { + fn from(path: &'a Path) -> Self { + File { + source: source::FileSourceFile::new(path.to_path_buf()), + required: true, + } + } +} + +impl From<PathBuf> for File<source::FileSourceFile> { + fn from(path: PathBuf) -> Self { + File { + source: source::FileSourceFile::new(path), + required: true, + } + } +} + +impl<T: FileSource> File<T> { + pub fn required(mut self, required: bool) -> Self { + self.required = required; + self + } +} + +impl<T: FileSource> Source for File<T> +where + T: 'static, + T: Sync + Send, +{ + fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result<HashMap<String, Value>> { + // Coerce the file contents to a string. + let (uri, contents) = match self.source.resolve().map_err(ConfigError::Foreign) { + Ok((uri, contents)) => (uri, contents), + + Err(error) => { + if !self.required { + return Ok(HashMap::new()); + } + + return Err(error); + } + }; + + // Parse the string using the given format. + toml::parse(uri.as_ref(), &contents).map_err(|cause| ConfigError::FileParse { uri, cause }) + } +} diff --git a/src/config/file/source.rs b/src/config/file/source.rs new file mode 100644 index 00000000..46d9ff00 --- /dev/null +++ b/src/config/file/source.rs @@ -0,0 +1,126 @@ +use std::env; +use std::error::Error; +use std::fmt::Debug; +use std::fs; +use std::io::{self, Read}; +use std::iter::Iterator; +use std::path::{Path, PathBuf}; + +/// Describes where the file is sourced. +pub trait FileSource: Debug + Clone { + fn resolve(&self) -> Result<(Option<String>, String), Box<dyn Error + Send + Sync>>; +} + +/// Describes a file sourced from a file. +#[derive(Clone, Debug)] +pub struct FileSourceFile { + /// Path of configuration file. + name: PathBuf, +} + +impl FileSourceFile { + pub fn new(name: PathBuf) -> FileSourceFile { + FileSourceFile { name } + } + + fn find_file(&self) -> Result<PathBuf, Box<dyn Error + Send + Sync>> { + // First check for an _exact_ match. + let mut filename = env::current_dir()?.as_path().join(self.name.clone()); + if filename.is_file() { + if ["toml"].contains( + &filename + .extension() + .unwrap_or_default() + .to_string_lossy() + .as_ref(), + ) { + return Ok(filename); + } + + Err(Box::new(io::Error::new( + io::ErrorKind::NotFound, + format!( + "configuration file \"{}\" is not of a registered file format", + filename.to_string_lossy() + ), + ))) + } else { + filename.set_extension("toml"); + + if filename.is_file() { + return Ok(filename); + } + + Err(Box::new(io::Error::new( + io::ErrorKind::NotFound, + format!( + "configuration file \"{}\" not found", + self.name.to_string_lossy() + ), + ))) + } + } +} + +impl FileSource for FileSourceFile { + fn resolve(&self) -> Result<(Option<String>, String), Box<dyn Error + Send + Sync>> { + // Find file. + let filename = self.find_file()?; + + // Attempt to use a relative path for the URI. + let base = env::current_dir()?; + let uri = match path_relative_from(&filename, &base) { + Some(value) => value, + None => filename.clone(), + }; + + // Read contents from file. + let mut file = fs::File::open(filename)?; + let mut text = String::new(); + file.read_to_string(&mut text)?; + + Ok((Some(uri.to_string_lossy().into_owned()), text)) + } +} + +// TODO: This should probably be a crate. +// https://github.com/rust-lang/rust/blob/master/src/librustc_trans/back/rpath.rs#L128 +fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> { + use std::path::Component; + + if path.is_absolute() != base.is_absolute() { + if path.is_absolute() { + Some(PathBuf::from(path)) + } else { + None + } + } else { + let mut ita = path.components(); + let mut itb = base.components(); + let mut comps: Vec<Component> = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(Component::CurDir)) => comps.push(a), + (Some(_), Some(Component::ParentDir)) => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| c.as_os_str()).collect()) + } +} diff --git a/src/config/file/toml.rs b/src/config/file/toml.rs new file mode 100644 index 00000000..e8fa06c6 --- /dev/null +++ b/src/config/file/toml.rs @@ -0,0 +1,51 @@ +use crate::config::value::{Value, ValueKind}; + +use toml; + +use std::collections::HashMap; +use std::error::Error; + +pub fn parse( + uri: Option<&String>, + text: &str, +) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> { + // Parse a TOML value from the provided text. + // TODO: Have a proper error fire if the root of a file is ever not a Table + let value = from_toml_value(uri, &toml::from_str(text)?); + match value.kind { + ValueKind::Table(map) => Ok(map), + + _ => Ok(HashMap::new()), + } +} + +fn from_toml_value(uri: Option<&String>, value: &toml::Value) -> Value { + match *value { + toml::Value::String(ref value) => Value::new(uri, value.to_string()), + toml::Value::Float(value) => Value::new(uri, value), + toml::Value::Integer(value) => Value::new(uri, value), + toml::Value::Boolean(value) => Value::new(uri, value), + + toml::Value::Table(ref table) => { + let mut m = HashMap::new(); + + for (key, value) in table { + m.insert(key.clone(), from_toml_value(uri, value)); + } + + Value::new(uri, m) + } + + toml::Value::Array(ref array) => { + let mut l = Vec::new(); + + for value in array { + l.push(from_toml_value(uri, value)); + } + + Value::new(uri, l) + } + + toml::Value::Datetime(ref datetime) => Value::new(uri, datetime.to_string()), + } +} diff --git a/src/config/path.rs b/src/config/path.rs new file mode 100644 index 00000000..72376a95 --- /dev/null +++ b/src/config/path.rs @@ -0,0 +1,167 @@ +use crate::config::error::*; +use crate::config::value::{Value, ValueKind}; + +use std::collections::HashMap; +use std::str::FromStr; + +mod parser; + +#[derive(Debug, Eq, PartialEq, Clone, Hash)] +pub enum Expression { + Identifier(String), + Child(Box<Expression>, String), + Subscript(Box<Expression>, isize), +} + +impl FromStr for Expression { + type Err = ConfigError; + + fn from_str(s: &str) -> Result<Expression> { + parser::from_str(s).map_err(ConfigError::PathParse) + } +} + +fn sindex_to_uindex(index: isize, len: usize) -> usize { + if index >= 0 { + index as usize + } else { + len - (index.unsigned_abs()) + } +} + +impl Expression { + pub fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> Option<&'a mut Value> { + match *self { + Expression::Identifier(ref id) => match root.kind { + ValueKind::Table(ref mut map) => Some( + map.entry(id.clone()) + .or_insert_with(|| Value::new(None, ValueKind::Nil)), + ), + + _ => None, + }, + + Expression::Child(ref expr, ref key) => match expr.get_mut_forcibly(root) { + Some(value) => match value.kind { + ValueKind::Table(ref mut map) => Some( + map.entry(key.clone()) + .or_insert_with(|| Value::new(None, ValueKind::Nil)), + ), + + _ => { + *value = HashMap::<String, Value>::new().into(); + + if let ValueKind::Table(ref mut map) = value.kind { + Some( + map.entry(key.clone()) + .or_insert_with(|| Value::new(None, ValueKind::Nil)), + ) + } else { + unreachable!(); + } + } + }, + + _ => None, + }, + + Expression::Subscript(ref expr, index) => match expr.get_mut_forcibly(root) { + Some(value) => { + match value.kind { + ValueKind::Array(_) => (), + _ => *value = Vec::<Value>::new().into(), + } + + match value.kind { + ValueKind::Array(ref mut array) => { + let index = sindex_to_uindex(index, array.len()); + + if index >= array.len() { + array.resize(index + 1, Value::new(None, ValueKind::Nil)); + } + + Some(&mut array[index]) + } + + _ => None, + } + } + _ => None, + }, + } + } + + pub fn set(&self, root: &mut Value, value: Value) { + match *self { + Expression::Identifier(ref id) => { + // Ensure that root is a table. + match root.kind { + ValueKind::Table(_) => {} + + _ => { + *root = HashMap::<String, Value>::new().into(); + } + } + + match value.kind { + ValueKind::Table(ref incoming_map) => { + // Pull out another table. + let target = if let ValueKind::Table(ref mut map) = root.kind { + map.entry(id.clone()) + .or_insert_with(|| HashMap::<String, Value>::new().into()) + } else { + unreachable!(); + }; + + // Continue the deep merge. + for (key, val) in incoming_map { + Expression::Identifier(key.clone()).set(target, val.clone()); + } + } + + _ => { + if let ValueKind::Table(ref mut map) = root.kind { + // Just do a simple set. + map.insert(id.clone(), value); + } + } + } + } + + Expression::Child(ref expr, ref key) => { + if let Some(parent) = expr.get_mut_forcibly(root) { + match parent.kind { + ValueKind::Table(_) => { + Expression::Identifier(key.clone()).set(parent, value); + } + + _ => { + // Didn't find a table. Oh well. Make a table and do this anyway. + *parent = HashMap::<String, Value>::new().into(); + + Expression::Identifier(key.clone()).set(parent, value); + } + } + } + } + + Expression::Subscript(ref expr, index) => { + if let Some(parent) = expr.get_mut_forcibly(root) { + match parent.kind { + ValueKind::Array(_) => (), + _ => *parent = Vec::<Value>::new().into(), + } + + if let ValueKind::Array(ref mut array) = parent.kind { + let uindex = sindex_to_uindex(index, array.len()); + if uindex >= array.len() { + array.resize(uindex + 1, Value::new(None, ValueKind::Nil)); + } + + array[uindex] = value; + } + } + } + } + } +} diff --git a/src/config/path/parser.rs b/src/config/path/parser.rs new file mode 100644 index 00000000..6bc95276 --- /dev/null +++ b/src/config/path/parser.rs @@ -0,0 +1,131 @@ +use super::Expression; + +use nom::{ + branch::alt, + bytes::complete::{is_a, tag}, + character::complete::{char, digit1, space0}, + combinator::{map, map_res, opt, recognize}, + error::ErrorKind, + sequence::{delimited, pair, preceded}, + Err, IResult, +}; + +use std::str::FromStr; + +fn raw_ident(i: &str) -> IResult<&str, String> { + map( + is_a( + "abcdefghijklmnopqrstuvwxyz \ + ABCDEFGHIJKLMNOPQRSTUVWXYZ \ + 0123456789 \ + _-", + ), + |s: &str| s.to_string(), + )(i) +} + +fn integer(i: &str) -> IResult<&str, isize> { + map_res( + delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0), + FromStr::from_str, + )(i) +} + +fn ident(i: &str) -> IResult<&str, Expression> { + map(raw_ident, Expression::Identifier)(i) +} + +fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> { + let e2 = expr.clone(); + let child = map(preceded(tag("."), raw_ident), move |id| { + Expression::Child(Box::new(expr.clone()), id) + }); + + let subscript = map(delimited(char('['), integer, char(']')), move |num| { + Expression::Subscript(Box::new(e2.clone()), num) + }); + + alt((child, subscript)) +} + +pub fn from_str(input: &str) -> Result<Expression, ErrorKind> { + match ident(input) { + Ok((mut rem, mut expr)) => { + while !rem.is_empty() { + match postfix(expr)(rem) { + Ok((rem_, expr_)) => { + rem = rem_; + expr = expr_; + } + + // Forward Incomplete and Error + result => { + return result.map(|(_, o)| o).map_err(to_error_kind); + } + } + } + + Ok(expr) + } + + // Forward Incomplete and Error + result => result.map(|(_, o)| o).map_err(to_error_kind), + } +} + +pub fn to_error_kind(e: Err<nom::error::Error<&str>>) -> ErrorKind { + match e { + Err::Incomplete(_) => ErrorKind::Complete, + Err::Failure(e) | Err::Error(e) => e.code, + } +} + +#[cfg(test)] +mod test { + use super::Expression::*; + use super::*; + + #[test] + fn test_id() { + let parsed: Expression = from_str("abcd").unwrap(); + assert_eq!(parsed, Identifier("abcd".into())); + } + + #[test] + fn test_id_dash() { + let parsed: Expression = from_str("abcd-efgh").unwrap(); + assert_eq!(parsed, Identifier("abcd-efgh".into())); + } + + #[test] + fn test_child() { + let parsed: Expression = from_str("abcd.efgh").unwrap(); + let expected = Child(Box::new(Identifier("abcd".into())), "efgh".into()); + + assert_eq!(parsed, expected); + + let parsed: Expression = from_str("abcd.efgh.ijkl").unwrap(); + let expected = Child( + Box::new(Child(Box::new(Identifier("abcd".into())), "efgh".into())), + "ijkl".into(), + ); + + assert_eq!(parsed, expected); + } + + #[test] + fn test_subscript() { + let parsed: Expression = from_str("abcd[12]").unwrap(); + let expected = Subscript(Box::new(Identifier("abcd".into())), 12); + + assert_eq!(parsed, expected); + } + + #[test] + fn test_subscript_neg() { + let parsed: Expression = from_str("abcd[-1]").unwrap(); + let expected = Subscript(Box::new(Identifier("abcd".into())), -1); + + assert_eq!(parsed, expected); + } +} diff --git a/src/config/source.rs b/src/config/source.rs new file mode 100644 index 00000000..5e693b68 --- /dev/null +++ b/src/config/source.rs @@ -0,0 +1,87 @@ +use crate::config::error::*; +use crate::config::path; +use crate::config::value::{Value, ValueKind}; + +use std::collections::HashMap; +use std::fmt::Debug; +use std::str::FromStr; + +/// Describes a generic _source_ of configuration properties. +pub trait Source: Debug { + fn clone_into_box(&self) -> Box<dyn Source + Send + Sync>; + + /// Collect all configuration properties available from this source and return a HashMap. + fn collect(&self) -> Result<HashMap<String, Value>>; + + fn collect_to(&self, cache: &mut Value) -> Result<()> { + let props = match self.collect() { + Ok(props) => props, + Err(error) => { + return Err(error); + } + }; + + for (key, val) in &props { + match path::Expression::from_str(key) { + // Set using the path. + Ok(expr) => expr.set(cache, val.clone()), + + // Set diretly anyway. + _ => path::Expression::Identifier(key.clone()).set(cache, val.clone()), + } + } + + Ok(()) + } +} + +impl Clone for Box<dyn Source + Send + Sync> { + fn clone(&self) -> Box<dyn Source + Send + Sync> { + self.clone_into_box() + } +} + +impl Source for Vec<Box<dyn Source + Send + Sync>> { + fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result<HashMap<String, Value>> { + let mut cache: Value = HashMap::<String, Value>::new().into(); + + for source in self { + source.collect_to(&mut cache)?; + } + + if let ValueKind::Table(table) = cache.kind { + Ok(table) + } else { + unreachable!(); + } + } +} + +impl<T> Source for Vec<T> +where + T: Source + Sync + Send, + T: Clone, + T: 'static, +{ + fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> { + Box::new((*self).clone()) + } + + fn collect(&self) -> Result<HashMap<String, Value>> { + let mut cache: Value = HashMap::<String, Value>::new().into(); + + for source in self { + source.collect_to(&mut cache)?; + } + + if let ValueKind::Table(table) = cache.kind { + Ok(table) + } else { + unreachable!(); + } + } +} diff --git a/src/config/value.rs b/src/config/value.rs new file mode 100644 index 00000000..29d62cfe --- /dev/null +++ b/src/config/value.rs @@ -0,0 +1,545 @@ +use crate::config::error::*; + +use serde::de::{Deserialize, Deserializer, Visitor}; + +use std::collections::HashMap; +use std::fmt; +use std::fmt::Display; + +/// Underlying kind of the configuration value. +#[derive(Clone, Debug, Default, PartialEq)] +pub enum ValueKind { + #[default] + Nil, + Boolean(bool), + Integer(i64), + Float(f64), + String(String), + Table(Table), + Array(Array), +} + +pub type Array = Vec<Value>; +pub type Table = HashMap<String, Value>; + +impl<T> From<Option<T>> for ValueKind +where + T: Into<ValueKind>, +{ + fn from(value: Option<T>) -> Self { + match value { + Some(value) => value.into(), + None => ValueKind::Nil, + } + } +} + +impl From<String> for ValueKind { + fn from(value: String) -> Self { + ValueKind::String(value) + } +} + +impl<'a> From<&'a str> for ValueKind { + fn from(value: &'a str) -> Self { + ValueKind::String(value.into()) + } +} + +impl From<i64> for ValueKind { + fn from(value: i64) -> Self { + ValueKind::Integer(value) + } +} + +impl From<f64> for ValueKind { + fn from(value: f64) -> Self { + ValueKind::Float(value) + } +} + +impl From<bool> for ValueKind { + fn from(value: bool) -> Self { + ValueKind::Boolean(value) + } +} + +impl<T> From<HashMap<String, T>> for ValueKind +where + T: Into<Value>, +{ + fn from(values: HashMap<String, T>) -> Self { + let mut r = HashMap::new(); + + for (k, v) in values { + r.insert(k.clone(), v.into()); + } + + ValueKind::Table(r) + } +} + +impl<T> From<Vec<T>> for ValueKind +where + T: Into<Value>, +{ + fn from(values: Vec<T>) -> Self { + let mut l = Vec::new(); + + for v in values { + l.push(v.into()); + } + + ValueKind::Array(l) + } +} + +impl Display for ValueKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ValueKind::String(ref value) => write!(f, "{}", value), + ValueKind::Boolean(value) => write!(f, "{}", value), + ValueKind::Integer(value) => write!(f, "{}", value), + ValueKind::Float(value) => write!(f, "{}", value), + ValueKind::Nil => write!(f, "nil"), + + // TODO: Figure out a nice Display for these + ValueKind::Table(ref table) => write!(f, "{:?}", table), + ValueKind::Array(ref array) => write!(f, "{:?}", array), + } + } +} + +/// A configuration value. +#[derive(Default, Debug, Clone, PartialEq)] +pub struct Value { + /// A description of the original location of the value. + /// + /// A Value originating from a File might contain: + /// ```text + /// Settings.toml + /// ``` + /// + /// A Value originating from the environment would contain: + /// ```text + /// the envrionment + /// ``` + /// + /// A Value originating from a remote source might contain: + /// ```text + /// etcd+http://127.0.0.1:2379 + /// ``` + origin: Option<String>, + + /// Underlying kind of the configuration value. + pub kind: ValueKind, +} + +impl Value { + /// Create a new value instance that will remember its source uri. + pub fn new<V>(origin: Option<&String>, kind: V) -> Self + where + V: Into<ValueKind>, + { + Value { + origin: origin.cloned(), + kind: kind.into(), + } + } + + /// Attempt to deserialize this value into the requested type. + pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> { + T::deserialize(self) + } + + /// Returns `self` as a bool, if possible. + // FIXME: Should this not be `try_into_*` ? + pub fn into_bool(self) -> Result<bool> { + match self.kind { + ValueKind::Boolean(value) => Ok(value), + ValueKind::Integer(value) => Ok(value != 0), + ValueKind::Float(value) => Ok(value != 0.0), + + ValueKind::String(ref value) => { + match value.to_lowercase().as_ref() { + "1" | "true" | "on" | "yes" => Ok(true), + "0" | "false" | "off" | "no" => Ok(false), + + // Unexpected string value + s => Err(ConfigError::invalid_type( + self.origin.clone(), + Unexpected::Str(s.into()), + "a boolean", + )), + } + } + + // Unexpected type + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "a boolean", + )), + ValueKind::Table(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Map, + "a boolean", + )), + ValueKind::Array(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Seq, + "a boolean", + )), + } + } + + /// Returns `self` into an i64, if possible. + // FIXME: Should this not be `try_into_*` ? + pub fn into_int(self) -> Result<i64> { + match self.kind { + ValueKind::Integer(value) => Ok(value), + + ValueKind::String(ref s) => { + match s.to_lowercase().as_ref() { + "true" | "on" | "yes" => Ok(1), + "false" | "off" | "no" => Ok(0), + _ => { + s.parse().map_err(|_| { + // Unexpected string + ConfigError::invalid_type( + self.origin.clone(), + Unexpected::Str(s.clone()), + "an integer", + ) + }) + } + } + } + + #[allow(clippy::bool_to_int_with_if)] + ValueKind::Boolean(value) => Ok(if value { 1 } else { 0 }), + + ValueKind::Float(value) => Ok(value.round() as i64), + + // Unexpected type + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "an integer", + )), + + ValueKind::Table(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Map, + "an integer", + )), + + ValueKind::Array(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Seq, + "an integer", + )), + } + } + + /// Returns `self` into a f64, if possible. + // FIXME: Should this not be `try_into_*` ? + pub fn into_float(self) -> Result<f64> { + match self.kind { + ValueKind::Float(value) => Ok(value), + + ValueKind::String(ref s) => { + match s.to_lowercase().as_ref() { + "true" | "on" | "yes" => Ok(1.0), + "false" | "off" | "no" => Ok(0.0), + _ => { + s.parse().map_err(|_| { + // Unexpected string + ConfigError::invalid_type( + self.origin.clone(), + Unexpected::Str(s.clone()), + "a floating point", + ) + }) + } + } + } + + ValueKind::Integer(value) => Ok(value as f64), + ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }), + + // Unexpected type. + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "a floating point", + )), + ValueKind::Table(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Map, + "a floating point", + )), + ValueKind::Array(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Seq, + "a floating point", + )), + } + } + + /// Returns `self` into a str, if possible. + // FIXME: Should this not be `try_into_*` ? + pub fn into_str(self) -> Result<String> { + match self.kind { + ValueKind::String(value) => Ok(value), + + ValueKind::Boolean(value) => Ok(value.to_string()), + ValueKind::Integer(value) => Ok(value.to_string()), + ValueKind::Float(value) => Ok(value.to_string()), + + // Cannot convert. + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "a string", + )), + ValueKind::Table(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Map, + "a string", + )), + ValueKind::Array(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Seq, + "a string", + )), + } + } + + /// Returns `self` into an array, if possible. + // FIXME: Should this not be `try_into_*` ? + pub fn into_array(self) -> Result<Vec<Value>> { + match self.kind { + ValueKind::Array(value) => Ok(value), + + // Cannot convert. + ValueKind::Float(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Float(value), + "an array", + )), + ValueKind::String(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Str(value), + "an array", + )), + ValueKind::Integer(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Integer(value), + "an array", + )), + ValueKind::Boolean(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Bool(value), + "an array", + )), + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "an array", + )), + ValueKind::Table(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Map, + "an array", + )), + } + } + + /// If the `Value` is a Table, returns the associated Map. + // FIXME: Should this not be `try_into_*` ? + pub fn into_table(self) -> Result<HashMap<String, Value>> { + match self.kind { + ValueKind::Table(value) => Ok(value), + + // Cannot convert. + ValueKind::Float(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Float(value), + "a map", + )), + ValueKind::String(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Str(value), + "a map", + )), + ValueKind::Integer(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Integer(value), + "a map", + )), + ValueKind::Boolean(value) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Bool(value), + "a map", + )), + ValueKind::Nil => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Unit, + "a map", + )), + ValueKind::Array(_) => Err(ConfigError::invalid_type( + self.origin, + Unexpected::Seq, + "a map", + )), + } + } +} + +impl<'de> Deserialize<'de> for Value { + #[inline] + fn deserialize<D>(deserializer: D) -> ::std::result::Result<Value, D::Error> + where + D: Deserializer<'de>, + { + struct ValueVisitor; + + impl<'de> Visitor<'de> for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("any valid configuration value") + } + + #[inline] + fn visit_bool<E>(self, value: bool) -> ::std::result::Result<Value, E> { + Ok(value.into()) + } + + #[inline] + fn visit_i8<E>(self, value: i8) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i16<E>(self, value: i16) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i32<E>(self, value: i32) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_i64<E>(self, value: i64) -> ::std::result::Result<Value, E> { + Ok(value.into()) + } + + #[inline] + fn visit_u8<E>(self, value: u8) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u16<E>(self, value: u16) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u32<E>(self, value: u32) -> ::std::result::Result<Value, E> { + Ok((value as i64).into()) + } + + #[inline] + fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Value, E> { + // FIXME: This is bad + Ok((value as i64).into()) + } + + #[inline] + fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Value, E> { + Ok(value.into()) + } + + #[inline] + fn visit_str<E>(self, value: &str) -> ::std::result::Result<Value, E> + where + E: ::serde::de::Error, + { + self.visit_string(String::from(value)) + } + + #[inline] + fn visit_string<E>(self, value: String) -> ::std::result::Result<Value, E> { + Ok(value.into()) + } + + #[inline] + fn visit_none<E>(self) -> ::std::result::Result<Value, E> { + Ok(Value::new(None, ValueKind::Nil)) + } + + #[inline] + fn visit_some<D>(self, deserializer: D) -> ::std::result::Result<Value, D::Error> + where + D: Deserializer<'de>, + { + Deserialize::deserialize(deserializer) + } + + #[inline] + fn visit_unit<E>(self) -> ::std::result::Result<Value, E> { + Ok(Value::new(None, ValueKind::Nil)) + } + + #[inline] + fn visit_seq<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error> + where + V: ::serde::de::SeqAccess<'de>, + { + let mut vec = Array::new(); + + while let Some(elem) = visitor.next_element()? { + vec.push(elem); + } + + Ok(vec.into()) + } + + fn visit_map<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error> + where + V: ::serde::de::MapAccess<'de>, + { + let mut values = Table::new(); + + while let Some((key, value)) = visitor.next_entry()? { + values.insert(key, value); + } + + Ok(values.into()) + } + } + + deserializer.deserialize_any(ValueVisitor) + } +} + +impl<T> From<T> for Value +where + T: Into<ValueKind>, +{ + fn from(value: T) -> Self { + Value { + origin: None, + kind: value.into(), + } + } +} + +impl Display for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.kind) + } +} diff --git a/src/core.rs b/src/core.rs index ab4d693b..2d65eadf 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,213 +1,87 @@ -//! Tipos y funciones esenciales para crear acciones, componentes, extensiones y temas. +//! Key types and functions for creating actions, components, packages, and themes. + +use crate::util::TypeInfo; use std::any::Any; -/// Selector para identificar segmentos de la ruta de un tipo. -#[derive(Clone, Copy, Debug)] -pub enum TypeInfo { - /// Ruta completa tal y como la devuelve [`core::any::type_name`]. - FullName, - /// Último segmento de la ruta, por ejemplo `Vec<i32>` en lugar de `alloc::vec::Vec<i32>`. - ShortName, - /// Conserva todo **desde** `start` inclusive hasta el final. - NameFrom(isize), - /// Conserva todo **hasta e incluyendo** `end`. - NameTo(isize), - /// Conserva la subruta comprendida entre `start` y `end` (ambos inclusive). - PartialName(isize, isize), -} - -impl TypeInfo { - /// Devuelve el segmento solicitado de la ruta para el tipo `T`. - pub fn of<T: ?Sized>(&self) -> &'static str { - let type_name = std::any::type_name::<T>(); - match self { - TypeInfo::FullName => type_name, - TypeInfo::ShortName => Self::partial(type_name, -1, None), - TypeInfo::NameFrom(start) => Self::partial(type_name, *start, None), - TypeInfo::NameTo(end) => Self::partial(type_name, 0, Some(*end)), - TypeInfo::PartialName(start, end) => Self::partial(type_name, *start, Some(*end)), - } - } - - // Extrae un rango de segmentos de `type_name` (tokens separados por `::`). - // - // Los argumentos `start` y `end` identifican los índices de los segmentos teniendo en cuenta: - // - // * Los índices positivos cuentan **desde la izquierda**, empezando en `0`. - // * Los índices negativos cuentan **desde la derecha**, `-1` es el último. - // * Si `end` es `None`, el corte llega hasta el último segmento. - // * Si la selección resulta vacía por índices desordenados o segmento inexistente, se devuelve - // la cadena vacía. - // - // Ejemplos (con `type_name = "alloc::vec::Vec<i32>"`): - // - // | Llamada | Resultado | - // |------------------------------|--------------------------| - // | `partial(..., 0, None)` | `"alloc::vec::Vec<i32>"` | - // | `partial(..., 1, None)` | `"vec::Vec<i32>"` | - // | `partial(..., -1, None)` | `"Vec<i32>"` | - // | `partial(..., 0, Some(-2))` | `"alloc::vec"` | - // | `partial(..., -5, None)` | `"alloc::vec::Vec<i32>"` | - // - // La porción devuelta vive tanto como `'static` porque `type_name` es `'static` y sólo se - // presta. - fn partial(type_name: &'static str, start: isize, end: Option<isize>) -> &'static str { - let maxlen = type_name.len(); - - // Localiza los límites de cada segmento a nivel 0 de `<...>`. - let mut segments = Vec::new(); - let mut segment_start = 0; // Posición inicial del segmento actual. - let mut angle_brackets = 0; // Profundidad dentro de '<...>'. - let mut previous_char = '\0'; // Se inicializa a carácter nulo, no hay aún carácter previo. - - for (idx, c) in type_name.char_indices() { - match c { - ':' if angle_brackets == 0 => { - if previous_char == ':' { - if segment_start < idx - 1 { - segments.push((segment_start, idx - 1)); // No incluye último '::'. - } - segment_start = idx + 1; // Nuevo segmento tras '::'. - } - } - '<' => angle_brackets += 1, - '>' => angle_brackets -= 1, - _ => {} - } - previous_char = c; - } - - // Incluye el último segmento si lo hubiese. - if segment_start < maxlen { - segments.push((segment_start, maxlen)); - } - - // Calcula la posición inicial. - let start_pos = segments - .get(if start >= 0 { - start as usize - } else { - segments.len().saturating_sub(start.unsigned_abs()) - }) - .map_or(0, |&(s, _)| s); - - // Calcula la posición final. - let end_pos = segments - .get(if let Some(end) = end { - if end >= 0 { - end as usize - } else { - segments.len().saturating_sub(end.unsigned_abs()) - } - } else { - segments.len() - 1 - }) - .map_or(maxlen, |&(_, e)| e); - - // Devuelve la cadena parcial basada en las posiciones calculadas. - if start_pos >= end_pos { - return ""; - } - &type_name[start_pos..end_pos] - } -} - -/// Proporciona información de tipo en tiempo de ejecución y conversión dinámica de tipos. -/// -/// Este *trait* se implementa automáticamente para **todos** los tipos que implementen [`Any`], de -/// modo que basta con traer [`AnyInfo`] al ámbito (`use crate::AnyInfo;`) para disponer de estos -/// métodos adicionales, o usar el [`prelude`](crate::prelude) de PageTop. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let n = 3u32; -/// assert_eq!(n.type_name(), "u32"); -/// ``` -pub trait AnyInfo: Any { - /// Devuelve el nombre totalmente cualificado del tipo. +/// A base trait that extends `Any` to provide metadata and dynamic type casting capabilities. +pub trait AnyBase: Any { + /// Returns the full name of the type. fn type_name(&self) -> &'static str; - /// Devuelve el nombre corto del tipo (último segmento del nombre). + /// Returns a short name for the type. fn short_name(&self) -> &'static str; - /// Devuelve una referencia a `dyn Any` para la conversión dinámica de tipos. + /// Returns a reference to `dyn Any` for dynamic type casting. fn as_any_ref(&self) -> &dyn Any; - /// Devuelve una referencia mutable a `dyn Any` para la conversión dinámica de tipos. + /// Returns a mutable reference to `dyn Any` for dynamic type casting. fn as_any_mut(&mut self) -> &mut dyn Any; } -impl<T: Any> AnyInfo for T { - #[inline] +#[allow(clippy::inline_always)] +impl<T: Any> AnyBase for T { + #[inline(always)] fn type_name(&self) -> &'static str { TypeInfo::FullName.of::<T>() } - #[inline] + #[inline(always)] fn short_name(&self) -> &'static str { TypeInfo::ShortName.of::<T>() } - #[inline] + #[inline(always)] fn as_any_ref(&self) -> &dyn Any { self } - #[inline] + #[inline(always)] fn as_any_mut(&mut self) -> &mut dyn Any { self } } -/// Extiende [`AnyInfo`] con utilidades de *downcasting* para conversión de tipos. -/// -/// Preferible a usar directamente `Any::downcast_ref` porque conserva el *trait bound* [`AnyInfo`], -/// lo que permite seguir llamando a `type_name`, etc. -pub trait AnyCast: AnyInfo { - /// Comprueba si la instancia subyacente es de tipo `T`. +/// A trait for advanced dynamic type manipulation and downcasting. +pub trait AnyTo: AnyBase { + /// Checks if the type is of the specified type `T`. #[inline] fn is<T>(&self) -> bool where - T: AnyInfo, + T: AnyBase, { self.as_any_ref().is::<T>() } - /// Intenta hacer *downcast* de un objeto para obtener una referencia de tipo `T`. + /// Attempts to downcast a reference to the specified type `T`. #[inline] - #[must_use] fn downcast_ref<T>(&self) -> Option<&T> where - T: AnyInfo, + T: AnyBase, { self.as_any_ref().downcast_ref() } - /// Intenta hacer *downcast* de un objeto para obtener una referencia mutable de tipo `T`. + /// Attempts to downcast a mutable reference to the specified type `T`. #[inline] - #[must_use] fn downcast_mut<T>(&mut self) -> Option<&mut T> where - T: AnyInfo, + T: AnyBase, { self.as_any_mut().downcast_mut() } } -/// Implementación automática para cualquier tipo que ya cumpla [`AnyInfo`]. -impl<T: ?Sized + AnyInfo> AnyCast for T {} +impl<T: ?Sized + AnyBase> AnyTo for T {} -// API para definir acciones que alteran el comportamiento predeterminado del código. +// API to define functions that alter the predefined behavior of the code. pub mod action; -// API para construir nuevos componentes. +// API to build new components. pub mod component; -// API para añadir nuevas funcionalidades usando extensiones. -pub mod extension; +// API to add new features with packages. +pub mod package; -// API para añadir y gestionar nuevos temas. +// API to add new layouts with themes. pub mod theme; diff --git a/src/core/action.rs b/src/core/action.rs index 9f81cd52..b960e8b8 100644 --- a/src/core/action.rs +++ b/src/core/action.rs @@ -1,11 +1,5 @@ -//! API para definir acciones que inyectan código en el flujo de la aplicación. -//! -//! Permite crear acciones para que otros *crates* puedan inyectar código usando funciones *ad hoc* -//! que modifican el comportamiento predefinido en puntos concretos del flujo de ejecución de la -//! aplicación. - mod definition; -pub use definition::{ActionBox, ActionDispatcher, ActionKey}; +pub use definition::{ActionBase, ActionBox, ActionKey, ActionTrait}; mod list; use list::ActionsList; @@ -14,31 +8,8 @@ mod all; pub(crate) use all::add_action; pub use all::dispatch_actions; -/// Facilita la implementación del método [`actions()`](crate::core::extension::Extension::actions). -/// -/// Evita escribir repetidamente `Box::new(...)` para cada acción de la lista, manteniendo el código -/// más limpio. -/// -/// # Ejemplo -/// -/// ```rust,ignore -/// # use pagetop::prelude::*; -/// impl Extension for MyTheme { -/// fn actions(&self) -> Vec<ActionBox> { -/// actions_boxed![ -/// action::theme::BeforeRender::<Button>::new(&Self, before_render_button), -/// action::theme::PrepareRender::<Error404>::new(&Self, render_error404), -/// ] -/// } -/// } -/// -/// impl Theme for MyTheme {} -/// -/// fn before_render_button(c: &mut Button, cx: &mut Context) { todo!() } -/// fn render_error404(c: &Error404, cx: &mut Context) -> PrepareMarkup { todo!() } -/// ``` #[macro_export] -macro_rules! actions_boxed { +macro_rules! actions { () => { Vec::<ActionBox>::new() }; diff --git a/src/core/action/all.rs b/src/core/action/all.rs index 2a3dfd2d..76256939 100644 --- a/src/core/action/all.rs +++ b/src/core/action/all.rs @@ -1,32 +1,15 @@ -use crate::core::action::{ActionBox, ActionDispatcher, ActionKey, ActionsList}; - -use parking_lot::RwLock; +use crate::core::action::{ActionBox, ActionKey, ActionTrait, ActionsList}; use std::collections::HashMap; -use std::sync::LazyLock; - -// **< ACCIONES >*********************************************************************************** +use std::sync::{LazyLock, RwLock}; +// Registered actions. static ACTIONS: LazyLock<RwLock<HashMap<ActionKey, ActionsList>>> = LazyLock::new(|| RwLock::new(HashMap::new())); -// **< AÑADIR ACCIONES >**************************************************************************** - -// Registra una nueva acción en el sistema. -// -// Si ya existen acciones con la misma `ActionKey`, la acción se añade a la misma lista. Si no, se -// crea una nueva lista. -// -// Las extensiones llamarán a esta función durante su inicialización para instalar acciones -// personalizadas que modifiquen el comportamiento del *core* o de otros componentes. -pub(crate) fn add_action(action: ActionBox) { - let key = ActionKey::new( - action.type_id(), - action.theme_type_id(), - action.referer_type_id(), - action.referer_id(), - ); - let mut actions = ACTIONS.write(); +pub fn add_action(action: ActionBox) { + let key = action.key(); + let mut actions = ACTIONS.write().unwrap(); if let Some(list) = actions.get_mut(&key) { list.add(action); } else { @@ -36,39 +19,12 @@ pub(crate) fn add_action(action: ActionBox) { } } -// **< DESPLEGAR ACCIONES >************************************************************************* - -/// Despacha y ejecuta las funciones asociadas a una [`ActionKey`]. -/// -/// Permite recorrer de forma segura y ordenada (por peso) la lista de funciones asociadas a una -/// acción específica. -/// -/// # Parámetros genéricos -/// -/// - `A`: Tipo de acción que esperamos procesar. Debe implementar [`ActionDispatcher`]. -/// - `F`: Función asociada a cada acción, devuelve un valor de tipo `B`. -/// -/// # Ejemplo de uso -/// -/// ```rust,ignore -/// pub(crate) fn dispatch(component: &mut C, cx: &mut Context) { -/// dispatch_actions( -/// &ActionKey::new( -/// UniqueId::of::<Self>(), -/// Some(cx.theme().type_id()), -/// Some(UniqueId::of::<C>()), -/// None, -/// ), -/// |action: &Self| (action.f)(component, cx), -/// ); -/// } -/// ``` pub fn dispatch_actions<A, B, F>(key: &ActionKey, f: F) where - A: ActionDispatcher, + A: ActionTrait, F: FnMut(&A) -> B, { - if let Some(list) = ACTIONS.read().get(key) { + if let Some(list) = ACTIONS.read().unwrap().get(key) { list.iter_map(f); } } diff --git a/src/core/action/definition.rs b/src/core/action/definition.rs index 7ebe4104..05ff8b79 100644 --- a/src/core/action/definition.rs +++ b/src/core/action/definition.rs @@ -1,38 +1,21 @@ -use crate::core::AnyInfo; -use crate::{UniqueId, Weight}; +use crate::core::AnyBase; +use crate::{TypeId, Weight}; -/// Tipo dinámico para encapsular cualquier acción que implementa [`ActionDispatcher`]. -pub type ActionBox = Box<dyn ActionDispatcher>; +pub type ActionBox = Box<dyn ActionTrait>; -/// Clave para registrar las acciones y seleccionar las funciones asociadas. -/// -/// Las funciones seleccionadas se van a [despachar](crate::core::action::dispatch_actions) y -/// ejecutar en un punto concreto del flujo de ejecución. #[derive(Eq, PartialEq, Hash)] pub struct ActionKey { - action_type_id: UniqueId, - theme_type_id: Option<UniqueId>, - referer_type_id: Option<UniqueId>, + action_type_id: TypeId, + theme_type_id: Option<TypeId>, + referer_type_id: Option<TypeId>, referer_id: Option<String>, } impl ActionKey { - /// Crea una nueva clave para un tipo de acción. - /// - /// Se crea con los siguientes campos: - /// - /// - `action_type_id`: Tipo de la acción. - /// - `theme_type_id`: Opcional, identificador de tipo ([`UniqueId`]) del tema asociado. - /// - `referer_type_id`: Opcional, identificador de tipo ([`UniqueId`]) del componente referido. - /// - `referer_id`: Opcional, identificador de la instancia (p. ej. para asociar la acción a un - /// componente concreto). - /// - /// Esta clave permitirá seleccionar las funciones a ejecutar para ese tipo de acción, con - /// filtros opcionales por tema, componente, o una instancia concreta según su identificador. pub fn new( - action_type_id: UniqueId, - theme_type_id: Option<UniqueId>, - referer_type_id: Option<UniqueId>, + action_type_id: TypeId, + theme_type_id: Option<TypeId>, + referer_type_id: Option<TypeId>, referer_id: Option<String>, ) -> Self { ActionKey { @@ -44,28 +27,35 @@ impl ActionKey { } } -/// Implementa el filtro predeterminado para despachar las funciones de una acción dada. -/// -/// Las acciones tienen que sobrescribir los métodos para el filtro que apliquen. Por defecto -/// implementa un filtro nulo. -pub trait ActionDispatcher: AnyInfo + Send + Sync { - /// Identificador de tipo ([`UniqueId`]) del tema asociado. En este caso devuelve `None`. - fn theme_type_id(&self) -> Option<UniqueId> { +pub trait ActionBase { + fn key(&self) -> ActionKey; +} + +pub trait ActionTrait: ActionBase + AnyBase + Send + Sync { + fn theme_type_id(&self) -> Option<TypeId> { None } - /// Identificador de tipo ([`UniqueId`]) del objeto referido. En este caso devuelve `None`. - fn referer_type_id(&self) -> Option<UniqueId> { + fn referer_type_id(&self) -> Option<TypeId> { None } - /// Identificador del objeto referido. En este caso devuelve `None`. fn referer_id(&self) -> Option<String> { None } - /// Funciones con pesos más bajos se aplican antes. En este caso siempre devuelve `0`. fn weight(&self) -> Weight { 0 } } + +impl<A: ActionTrait> ActionBase for A { + fn key(&self) -> ActionKey { + ActionKey { + action_type_id: self.type_id(), + theme_type_id: self.theme_type_id(), + referer_type_id: self.referer_type_id(), + referer_id: self.referer_id(), + } + } +} diff --git a/src/core/action/list.rs b/src/core/action/list.rs index 57c89fe6..fec25027 100644 --- a/src/core/action/list.rs +++ b/src/core/action/list.rs @@ -1,9 +1,9 @@ -use crate::core::action::{ActionBox, ActionDispatcher}; -use crate::core::AnyCast; +use crate::core::action::{ActionBox, ActionTrait}; +use crate::core::AnyTo; use crate::trace; use crate::AutoDefault; -use parking_lot::RwLock; +use std::sync::RwLock; #[derive(AutoDefault)] pub struct ActionsList(RwLock<Vec<ActionBox>>); @@ -14,7 +14,7 @@ impl ActionsList { } pub fn add(&mut self, action: ActionBox) { - let mut list = self.0.write(); + let mut list = self.0.write().unwrap(); list.push(action); list.sort_by_key(|a| a.weight()); } @@ -22,12 +22,13 @@ impl ActionsList { pub fn iter_map<A, B, F>(&self, mut f: F) where Self: Sized, - A: ActionDispatcher, + A: ActionTrait, F: FnMut(&A) -> B, { let _: Vec<_> = self .0 .read() + .unwrap() .iter() .rev() .map(|a| { diff --git a/src/core/component.rs b/src/core/component.rs index 9c9ade2e..5a02ee0e 100644 --- a/src/core/component.rs +++ b/src/core/component.rs @@ -1,71 +1,14 @@ -//! API para construir nuevos componentes. +mod context; +pub use context::{AssetsOp, Context, ErrorParam}; +pub type FnContextualPath = fn(cx: &Context) -> &str; mod definition; -pub use definition::{Component, ComponentRender}; +pub use definition::{ComponentBase, ComponentTrait}; + +mod classes; +pub use classes::{ComponentClasses, ComponentClassesOp}; mod children; pub use children::Children; -pub use children::{Child, ChildOp}; -pub use children::{Typed, TypedOp}; - -mod context; -pub use context::{Context, ContextError, ContextOp, Contextual}; - -/// Alias de función (*callback*) para **determinar si un componente se renderiza o no**. -/// -/// Puede usarse para permitir que una instancia concreta de un tipo de componente dado decida -/// dinámicamente durante el proceso de renderizado ([`Component::is_renderable()`]) si se renderiza -/// o no. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// #[derive(AutoDefault)] -/// struct SampleComponent { -/// renderable: Option<FnIsRenderable>, -/// } -/// -/// impl Component for SampleComponent { -/// fn new() -> Self { -/// Self::default() -/// } -/// -/// fn is_renderable(&self, cx: &mut Context) -> bool { -/// // Si hay callback, se usa; en caso contrario, se renderiza por defecto. -/// self.renderable.map_or(true, |f| f(cx)) -/// } -/// -/// fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup { -/// PrepareMarkup::Escaped("Visible component".into()) -/// } -/// } -/// -/// impl SampleComponent { -/// /// Asigna una función que decidirá si el componente se renderiza o no. -/// #[builder_fn] -/// pub fn with_renderable(mut self, f: Option<FnIsRenderable>) -> Self { -/// self.renderable = f; -/// self -/// } -/// } -/// -/// fn sample() { -/// let mut cx = Context::default().with_param("user_logged_in", true); -/// -/// // Se instancia un componente que sólo se renderiza si `user_logged_in` es `true`. -/// let mut component = SampleComponent::new().with_renderable(Some(|cx: &Context| { -/// cx.param::<bool>("user_logged_in").copied().unwrap_or(false) -/// })); -/// -/// // Aquí simplemente se comprueba que compila y se puede invocar. -/// let _markup = component.render(&mut cx); -/// } -/// ``` -pub type FnIsRenderable = fn(cx: &Context) -> bool; - -/// Alias de función (*callback*) para **resolver una URL** según el contexto de renderizado. -/// -/// Se usa para generar enlaces dinámicos en función del contexto (petición, idioma, etc.). Debe -/// devolver una referencia válida durante el renderizado. -pub type FnPathByContext = fn(cx: &Context) -> &str; +pub use children::{ChildComponent, ChildOp}; +pub use children::{TypedComponent, TypedOp}; diff --git a/src/core/component/children.rs b/src/core/component/children.rs index b3670433..9a5db05b 100644 --- a/src/core/component/children.rs +++ b/src/core/component/children.rs @@ -1,303 +1,208 @@ -use crate::core::component::{Component, Context}; +use crate::core::component::{ComponentTrait, Context}; use crate::html::{html, Markup}; -use crate::{builder_fn, AutoDefault, UniqueId}; +use crate::{fn_builder, TypeId}; -use parking_lot::RwLock; +use std::sync::{Arc, RwLock}; -pub use parking_lot::RwLockReadGuard as ComponentReadGuard; -pub use parking_lot::RwLockWriteGuard as ComponentWriteGuard; +#[derive(Clone)] +pub struct ChildComponent(Arc<RwLock<dyn ComponentTrait>>); -use std::sync::Arc; -use std::vec::IntoIter; - -/// Representa un componente encapsulado de forma segura y compartida. -/// -/// Esta estructura permite manipular y renderizar un componente que implemente [`Component`], y -/// habilita acceso concurrente mediante [`Arc<RwLock<_>>`]. -#[derive(AutoDefault, Clone)] -pub struct Child(Option<Arc<RwLock<dyn Component>>>); - -impl Child { - /// Crea un nuevo `Child` a partir de un componente. - pub fn with(component: impl Component) -> Self { - Child(Some(Arc::new(RwLock::new(component)))) +impl ChildComponent { + pub fn with(component: impl ComponentTrait) -> Self { + ChildComponent(Arc::new(RwLock::new(component))) } - // **< Child BUILDER >************************************************************************** + // ChildComponent RENDER. - /// Establece un componente nuevo, o lo vacía. - /// - /// Si se proporciona `Some(component)`, se encapsula como [`Child`]; y si es `None`, se limpia. - #[builder_fn] - pub fn with_component<C: Component>(mut self, component: Option<C>) -> Self { - if let Some(c) = component { - self.0 = Some(Arc::new(RwLock::new(c))); - } else { - self.0 = None; - } - self - } - - // **< Child GETTERS >************************************************************************** - - /// Devuelve el identificador del componente, si existe y está definido. - #[inline] - pub fn id(&self) -> Option<String> { - self.0.as_ref().and_then(|c| c.read().id()) - } - - // **< Child RENDER >*************************************************************************** - - /// Renderiza el componente con el contexto proporcionado. pub fn render(&self, cx: &mut Context) -> Markup { - self.0.as_ref().map_or(html! {}, |c| c.write().render(cx)) + self.0.write().unwrap().render(cx) } - // **< Child HELPERS >************************************************************************** + // ChildComponent HELPERS. - // Devuelve el [`UniqueId`] del tipo del componente, si existe. - #[inline] - fn type_id(&self) -> Option<UniqueId> { - self.0.as_ref().map(|c| c.read().type_id()) + fn type_id(&self) -> TypeId { + self.0.read().unwrap().type_id() + } + + fn id(&self) -> String { + self.0.read().unwrap().id().unwrap_or_default() } } // ************************************************************************************************* -/// Variante tipada de [`Child`] para evitar conversiones de tipo durante el uso. -/// -/// Esta estructura permite manipular y renderizar un componente concreto que implemente -/// [`Component`], y habilita acceso concurrente mediante [`Arc<RwLock<_>>`]. -#[derive(AutoDefault, Clone)] -pub struct Typed<C: Component>(Option<Arc<RwLock<C>>>); +pub struct TypedComponent<C: ComponentTrait>(Arc<RwLock<C>>); -impl<C: Component> Typed<C> { - /// Crea un nuevo `Typed` a partir de un componente. +impl<C: ComponentTrait> Clone for TypedComponent<C> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl<C: ComponentTrait> TypedComponent<C> { pub fn with(component: C) -> Self { - Typed(Some(Arc::new(RwLock::new(component)))) + TypedComponent(Arc::new(RwLock::new(component))) } - // **< Typed BUILDER >************************************************************************** + // TypedComponent RENDER. - /// Establece un componente nuevo, o lo vacía. - /// - /// Si se proporciona `Some(component)`, se encapsula como [`Typed`]; y si es `None`, se limpia. - #[builder_fn] - pub fn with_component(mut self, component: Option<C>) -> Self { - self.0 = component.map(|c| Arc::new(RwLock::new(c))); - self - } - - // **< Typed GETTERS >************************************************************************** - - /// Devuelve el identificador del componente, si existe y está definido. - #[inline] - pub fn id(&self) -> Option<String> { - self.0.as_ref().and_then(|c| c.read().id()) - } - - /// Devuelve una **referencia inmutable** al componente interno. - /// - /// - Devuelve `Some(ComponentReadGuard<C>)` si existe el componente, o `None` si está vacío. - /// - Permite realizar **múltiples lecturas concurrentes**. - /// - Mientras el *guard* esté activo, no se pueden realizar escrituras concurrentes (ver - /// [`borrow_mut`](Self::borrow_mut)). - /// - Se recomienda mantener el *guard* **el menor tiempo posible** para evitar bloqueos - /// innecesarios. - /// - /// # Ejemplo - /// - /// Lectura del nombre del componente: - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let typed = Typed::with(Html::with(|_| html! { "Prueba" })); - /// { - /// if let Some(component) = typed.borrow() { - /// assert_eq!(component.name(), "Html"); - /// } - /// }; // El *guard* se libera aquí, antes del *drop* de `typed`. - /// ``` - pub fn borrow(&self) -> Option<ComponentReadGuard<'_, C>> { - self.0.as_ref().map(|a| a.read()) - } - - /// Obtiene una **referencia mutable exclusiva** al componente interno. - /// - /// - Devuelve `Some(ComponentWriteGuard<C>)` si existe el componente, o `None` si está vacío. - /// - **Exclusivo**: mientras el *guard* esté activo, no habrá otros lectores ni escritores. - /// - Usar sólo para operaciones que **modifican** el estado interno. - /// - Igual que con [`borrow`](Self::borrow), se recomienda mantener el *guard* en un **ámbito - /// reducido**. - /// - /// # Ejemplo - /// - /// Acceso mutable (ámbito corto): - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let typed = Typed::with(Block::new().with_title(L10n::n("Título"))); - /// { - /// if let Some(mut component) = typed.borrow_mut() { - /// component.alter_title(L10n::n("Nuevo título")); - /// } - /// }; // El *guard* se libera aquí, antes del *drop* de `typed`. - /// ``` - pub fn borrow_mut(&self) -> Option<ComponentWriteGuard<'_, C>> { - self.0.as_ref().map(|a| a.write()) - } - - // **< Typed RENDER >*************************************************************************** - - /// Renderiza el componente con el contexto proporcionado. pub fn render(&self, cx: &mut Context) -> Markup { - self.0.as_ref().map_or(html! {}, |c| c.write().render(cx)) + self.0.write().unwrap().render(cx) } - // **< Typed HELPERS >************************************************************************** + // TypedComponent HELPERS. - // Método interno para convertir un componente tipado en un [`Child`]. - #[inline] - fn into(self) -> Child { - if let Some(c) = &self.0 { - Child(Some(c.clone())) - } else { - Child(None) - } + fn to_child(&self) -> ChildComponent { + ChildComponent(self.0.clone()) } } // ************************************************************************************************* -/// Operaciones para componentes hijo [`Child`] en una lista [`Children`]. pub enum ChildOp { - Add(Child), - AddMany(Vec<Child>), - InsertAfterId(&'static str, Child), - InsertBeforeId(&'static str, Child), - Prepend(Child), - PrependMany(Vec<Child>), + Add(ChildComponent), + InsertAfterId(&'static str, ChildComponent), + InsertBeforeId(&'static str, ChildComponent), + Prepend(ChildComponent), RemoveById(&'static str), - ReplaceById(&'static str, Child), + ReplaceById(&'static str, ChildComponent), Reset, } -/// Operaciones con un componente hijo tipado [`Typed<C>`] en una lista [`Children`]. -pub enum TypedOp<C: Component> { - Add(Typed<C>), - AddMany(Vec<Typed<C>>), - InsertAfterId(&'static str, Typed<C>), - InsertBeforeId(&'static str, Typed<C>), - Prepend(Typed<C>), - PrependMany(Vec<Typed<C>>), +pub enum TypedOp<C: ComponentTrait> { + Add(TypedComponent<C>), + InsertAfterId(&'static str, TypedComponent<C>), + InsertBeforeId(&'static str, TypedComponent<C>), + Prepend(TypedComponent<C>), RemoveById(&'static str), - ReplaceById(&'static str, Typed<C>), + ReplaceById(&'static str, TypedComponent<C>), Reset, } -/// Lista ordenada de componentes hijo ([`Child`]) mantenida por un componente padre. -/// -/// Esta lista permite añadir, modificar, renderizar y consultar componentes hijo en orden de -/// inserción, soportando operaciones avanzadas como inserción relativa o reemplazo por -/// identificador. -#[derive(AutoDefault, Clone)] -pub struct Children(Vec<Child>); +#[derive(Clone, Default)] +pub struct Children(Vec<ChildComponent>); impl Children { - /// Crea una lista vacía. pub fn new() -> Self { Children::default() } - /// Crea una lista con un componente hijo inicial. - pub fn with(child: Child) -> Self { - Children::default().with_child(ChildOp::Add(child)) + pub fn with(child: ChildComponent) -> Self { + Children::default().with_value(ChildOp::Add(child)) } - // Fusiona varias listas de `Children` en una sola. pub(crate) fn merge(mixes: &[Option<&Children>]) -> Self { let mut opt = Children::default(); for m in mixes.iter().flatten() { - opt.0.extend(m.0.iter().cloned()); + opt.0.append(&mut m.0.clone()); } opt } - // **< Children BUILDER >*********************************************************************** + // Children BUILDER. - /// Ejecuta una operación con [`ChildOp`] en la lista. - #[builder_fn] - pub fn with_child(mut self, op: ChildOp) -> Self { + #[fn_builder] + pub fn set_value(&mut self, op: ChildOp) -> &mut Self { match op { ChildOp::Add(any) => self.add(any), - ChildOp::AddMany(many) => self.add_many(many), ChildOp::InsertAfterId(id, any) => self.insert_after_id(id, any), ChildOp::InsertBeforeId(id, any) => self.insert_before_id(id, any), ChildOp::Prepend(any) => self.prepend(any), - ChildOp::PrependMany(many) => self.prepend_many(many), ChildOp::RemoveById(id) => self.remove_by_id(id), ChildOp::ReplaceById(id, any) => self.replace_by_id(id, any), ChildOp::Reset => self.reset(), - } - } - - /// Ejecuta una operación con [`TypedOp`] en la lista. - #[builder_fn] - pub fn with_typed<C: Component>(mut self, op: TypedOp<C>) -> Self { - match op { - TypedOp::Add(typed) => self.add(typed.into()), - TypedOp::AddMany(many) => self.add_many(many.into_iter().map(Typed::<C>::into)), - TypedOp::InsertAfterId(id, typed) => self.insert_after_id(id, typed.into()), - TypedOp::InsertBeforeId(id, typed) => self.insert_before_id(id, typed.into()), - TypedOp::Prepend(typed) => self.prepend(typed.into()), - TypedOp::PrependMany(many) => self.prepend_many(many.into_iter().map(Typed::<C>::into)), - TypedOp::RemoveById(id) => self.remove_by_id(id), - TypedOp::ReplaceById(id, typed) => self.replace_by_id(id, typed.into()), - TypedOp::Reset => self.reset(), - } - } - - /// Añade un componente hijo al final de la lista. - /// - /// Es un atajo para `children.alter_child(ChildOp::Add(child))`. - #[inline] - pub fn add(&mut self, child: Child) -> &mut Self { - self.0.push(child); + }; self } - // **< Children GETTERS >*********************************************************************** + #[fn_builder] + pub fn set_typed<C: ComponentTrait + Default>(&mut self, op: TypedOp<C>) -> &mut Self { + match op { + TypedOp::Add(typed) => self.add(typed.to_child()), + TypedOp::InsertAfterId(id, typed) => self.insert_after_id(id, typed.to_child()), + TypedOp::InsertBeforeId(id, typed) => self.insert_before_id(id, typed.to_child()), + TypedOp::Prepend(typed) => self.prepend(typed.to_child()), + TypedOp::RemoveById(id) => self.remove_by_id(id), + TypedOp::ReplaceById(id, typed) => self.replace_by_id(id, typed.to_child()), + TypedOp::Reset => self.reset(), + }; + self + } + + #[inline] + fn add(&mut self, child: ChildComponent) { + self.0.push(child); + } + + #[inline] + fn insert_after_id(&mut self, id: &str, child: ChildComponent) { + match self.0.iter().position(|c| c.id() == id) { + Some(index) => self.0.insert(index + 1, child), + _ => self.0.push(child), + }; + } + + #[inline] + fn insert_before_id(&mut self, id: &str, child: ChildComponent) { + match self.0.iter().position(|c| c.id() == id) { + Some(index) => self.0.insert(index, child), + _ => self.0.insert(0, child), + }; + } + + #[inline] + fn prepend(&mut self, child: ChildComponent) { + self.0.insert(0, child); + } + + #[inline] + fn remove_by_id(&mut self, id: &str) { + if let Some(index) = self.0.iter().position(|c| c.id() == id) { + self.0.remove(index); + } + } + + #[inline] + fn replace_by_id(&mut self, id: &str, child: ChildComponent) { + for c in &mut self.0 { + if c.id() == id { + *c = child; + break; + } + } + } + + #[inline] + fn reset(&mut self) { + self.0.clear(); + } + + // Children GETTERS. - /// Devuelve el número de componentes hijo de la lista. pub fn len(&self) -> usize { self.0.len() } - /// Indica si la lista está vacía. pub fn is_empty(&self) -> bool { self.0.is_empty() } - /// Devuelve el primer componente hijo con el identificador indicado, si existe. - pub fn get_by_id(&self, id: impl AsRef<str>) -> Option<&Child> { - let id = Some(id.as_ref()); - self.0.iter().find(|c| c.id().as_deref() == id) + pub fn get_by_id(&self, id: impl Into<String>) -> Option<&ChildComponent> { + let id = id.into(); + self.0.iter().find(|c| c.id() == id) } - /// Devuelve un iterador sobre los componentes hijo con el identificador indicado. - pub fn iter_by_id<'a>(&'a self, id: &'a str) -> impl Iterator<Item = &'a Child> + 'a { - self.0.iter().filter(move |c| c.id().as_deref() == Some(id)) + pub fn iter_by_id(&self, id: impl Into<String>) -> impl Iterator<Item = &ChildComponent> { + let id = id.into(); + self.0.iter().filter(move |&c| c.id() == id) } - /// Devuelve un iterador sobre los componentes hijo con el identificador de tipo ([`UniqueId`]) - /// indicado. - pub fn iter_by_type_id(&self, type_id: UniqueId) -> impl Iterator<Item = &Child> { - self.0.iter().filter(move |c| c.type_id() == Some(type_id)) + pub fn iter_by_type_id(&self, type_id: TypeId) -> impl Iterator<Item = &ChildComponent> { + self.0.iter().filter(move |&c| c.type_id() == type_id) } - // **< Children RENDER >************************************************************************ + // Children RENDER. - /// Renderiza todos los componentes hijo, en orden. pub fn render(&self, cx: &mut Context) -> Markup { html! { @for c in &self.0 { @@ -305,143 +210,4 @@ impl Children { } } } - - // **< Children HELPERS >*********************************************************************** - - // Añade más de un componente hijo al final de la lista (en el orden recibido). - #[inline] - fn add_many<I>(&mut self, iter: I) -> &mut Self - where - I: IntoIterator<Item = Child>, - { - self.0.extend(iter); - self - } - - // Inserta un hijo después del componente con el `id` dado, o al final si no se encuentra. - #[inline] - fn insert_after_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self { - let id = Some(id.as_ref()); - match self.0.iter().position(|c| c.id().as_deref() == id) { - Some(index) => self.0.insert(index + 1, child), - _ => self.0.push(child), - }; - self - } - - // Inserta un hijo antes del componente con el `id` dado, o al principio si no se encuentra. - #[inline] - fn insert_before_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self { - let id = Some(id.as_ref()); - match self.0.iter().position(|c| c.id().as_deref() == id) { - Some(index) => self.0.insert(index, child), - _ => self.0.insert(0, child), - }; - self - } - - // Inserta un hijo al principio de la colección. - #[inline] - fn prepend(&mut self, child: Child) -> &mut Self { - self.0.insert(0, child); - self - } - - // Inserta más de un componente hijo al principio de la lista (manteniendo el orden recibido). - #[inline] - fn prepend_many<I>(&mut self, iter: I) -> &mut Self - where - I: IntoIterator<Item = Child>, - { - let buf: Vec<Child> = iter.into_iter().collect(); - self.0.splice(0..0, buf); - self - } - - // Elimina el primer hijo con el `id` dado. - #[inline] - fn remove_by_id(&mut self, id: impl AsRef<str>) -> &mut Self { - let id = Some(id.as_ref()); - if let Some(index) = self.0.iter().position(|c| c.id().as_deref() == id) { - self.0.remove(index); - } - self - } - - // Sustituye el primer hijo con el `id` dado por otro componente. - #[inline] - fn replace_by_id(&mut self, id: impl AsRef<str>, child: Child) -> &mut Self { - let id = Some(id.as_ref()); - for c in &mut self.0 { - if c.id().as_deref() == id { - *c = child; - break; - } - } - self - } - - // Elimina todos los componentes hijo de la lista. - #[inline] - fn reset(&mut self) -> &mut Self { - self.0.clear(); - self - } -} - -impl IntoIterator for Children { - type Item = Child; - type IntoIter = IntoIter<Child>; - - /// Consume la estructura `Children`, devolviendo un iterador que consume los elementos. - /// - /// # Ejemplo de uso: - /// - /// ```rust,ignore - /// let children = Children::new().with(child1).with(child2); - /// for child in children { - /// println!("{:?}", child.id()); - /// } - /// ``` - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a Children { - type Item = &'a Child; - type IntoIter = std::slice::Iter<'a, Child>; - - /// Itera sobre una referencia inmutable de `Children`, devolviendo un iterador de referencia. - /// - /// # Ejemplo de uso: - /// - /// ```rust,ignore - /// let children = Children::new().with(child1).with(child2); - /// for child in &children { - /// println!("{:?}", child.id()); - /// } - /// ``` - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -impl<'a> IntoIterator for &'a mut Children { - type Item = &'a mut Child; - type IntoIter = std::slice::IterMut<'a, Child>; - - /// Itera sobre una referencia mutable de `Children`, devolviendo un iterador mutable. - /// - /// # Ejemplo de uso: - /// - /// ```rust,ignore - /// let mut children = Children::new().with(child1).with(child2); - /// for child in &mut children { - /// child.render(&mut context); - /// } - /// ``` - fn into_iter(self) -> Self::IntoIter { - self.0.iter_mut() - } } diff --git a/src/core/component/classes.rs b/src/core/component/classes.rs new file mode 100644 index 00000000..a61809c6 --- /dev/null +++ b/src/core/component/classes.rs @@ -0,0 +1,19 @@ +use crate::core::component::ComponentBase; +use crate::html::{ClassesOp, OptionClasses}; + +pub trait ComponentClassesOp { + fn with_classes(self, op: ClassesOp, classes: impl Into<String>) -> Self; +} + +pub trait ComponentClasses: ComponentBase + ComponentClassesOp { + fn set_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self; + + fn classes(&self) -> &OptionClasses; +} + +impl<C: ComponentBase + ComponentClasses> ComponentClassesOp for C { + fn with_classes(mut self, op: ClassesOp, classes: impl Into<String>) -> Self { + self.set_classes(op, classes); + self + } +} diff --git a/src/core/component/context.rs b/src/core/component/context.rs index 5cc5d2e8..45bdcb87 100644 --- a/src/core/component/context.rs +++ b/src/core/component/context.rs @@ -1,551 +1,184 @@ -use crate::base::component::Template; +use crate::concat_string; use crate::core::component::ChildOp; -use crate::core::theme::all::DEFAULT_THEME; +use crate::core::theme::all::{theme_by_short_name, DEFAULT_THEME}; use crate::core::theme::{ChildrenInRegions, ThemeRef}; -use crate::core::TypeInfo; use crate::html::{html, Markup}; use crate::html::{Assets, Favicon, JavaScript, StyleSheet}; -use crate::locale::{LangId, LangMatch, LanguageIdentifier, DEFAULT_LANGID, FALLBACK_LANGID}; +use crate::locale::{LanguageIdentifier, DEFAULT_LANGID}; use crate::service::HttpRequest; -use crate::{builder_fn, join}; +use crate::util::TypeInfo; -use std::any::Any; use std::collections::HashMap; +use std::error::Error; +use std::str::FromStr; -/// Operaciones para modificar recursos asociados al contexto ([`Context`]) de un documento. -pub enum ContextOp { - /// Define el *favicon* del documento. Sobrescribe cualquier valor anterior. +use std::fmt; + +pub enum AssetsOp { + LangId(&'static LanguageIdentifier), + Theme(&'static str), + Layout(&'static str), + // Favicon. SetFavicon(Option<Favicon>), - /// Define el *favicon* solo si no se ha establecido previamente. SetFaviconIfNone(Favicon), - - /// Añade una hoja de estilos CSS al documento. + // Stylesheets. AddStyleSheet(StyleSheet), - /// Elimina una hoja de estilos por su ruta o identificador. RemoveStyleSheet(&'static str), - - /// Añade un script JavaScript al documento. + // JavaScripts. AddJavaScript(JavaScript), - /// Elimina un script por su ruta o identificador. RemoveJavaScript(&'static str), } -/// Errores de acceso a parámetros dinámicos del contexto. -/// -/// - [`ContextError::ParamNotFound`]: la clave no existe. -/// - [`ContextError::ParamTypeMismatch`]: la clave existe, pero el valor guardado no coincide con -/// el tipo solicitado. Incluye nombre de la clave (`key`), tipo esperado (`expected`) y tipo -/// realmente guardado (`saved`) para facilitar el diagnóstico. #[derive(Debug)] -pub enum ContextError { - ParamNotFound, - ParamTypeMismatch { - key: &'static str, - expected: &'static str, - saved: &'static str, - }, +pub enum ErrorParam { + NotFound, + ParseError(String), } -/// Interfaz para gestionar el **contexto de renderizado** de un documento HTML. -/// -/// `Contextual` extiende [`LangId`] para establecer el idioma del documento y añade métodos para: -/// -/// - Almacenar la **solicitud HTTP** de origen. -/// - Seleccionar el **tema** y la **plantilla** de renderizado. -/// - Administrar **recursos** del documento como el icono [`Favicon`], las hojas de estilo -/// [`StyleSheet`] o los scripts [`JavaScript`] mediante [`ContextOp`]. -/// - Leer y mantener **parámetros dinámicos tipados** de contexto. -/// - Generar **identificadores únicos** por tipo de componente. -/// -/// Lo implementan, típicamente, estructuras que manejan el contexto de renderizado, como -/// [`Context`](crate::core::component::Context) o [`Page`](crate::response::page::Page). -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// # use pagetop_aliner::Aliner; -/// fn prepare_context<C: Contextual>(cx: C) -> C { -/// cx.with_langid(&LangMatch::resolve("es-ES")) -/// .with_theme(&Aliner) -/// .with_template(Template::DEFAULT) -/// .with_assets(ContextOp::SetFavicon(Some(Favicon::new().with_icon("/favicon.ico")))) -/// .with_assets(ContextOp::AddStyleSheet(StyleSheet::from("/css/app.css"))) -/// .with_assets(ContextOp::AddJavaScript(JavaScript::defer("/js/app.js"))) -/// .with_param("usuario_id", 42_i32) -/// } -/// ``` -pub trait Contextual: LangId { - // **< Contextual BUILDER >********************************************************************* - - /// Establece el idioma del documento. - #[builder_fn] - fn with_langid(self, language: &impl LangId) -> Self; - - /// Almacena la solicitud HTTP de origen en el contexto. - #[builder_fn] - fn with_request(self, request: Option<HttpRequest>) -> Self; - - /// Especifica el tema para renderizar el documento. - #[builder_fn] - fn with_theme(self, theme: ThemeRef) -> Self; - - /// Especifica la plantilla para renderizar el documento. - #[builder_fn] - fn with_template(self, template_name: &'static str) -> Self; - - /// Añade o modifica un parámetro dinámico del contexto. - #[builder_fn] - fn with_param<T: 'static>(self, key: &'static str, value: T) -> Self; - - /// Define los recursos del contexto usando [`ContextOp`]. - #[builder_fn] - fn with_assets(self, op: ContextOp) -> Self; - - /// Opera con [`ChildOp`] en una región (`region_name`) del documento. - #[builder_fn] - fn with_child_in(self, region_name: impl AsRef<str>, op: ChildOp) -> Self; - - // **< Contextual GETTERS >********************************************************************* - - /// Devuelve una referencia a la solicitud HTTP asociada, si existe. - fn request(&self) -> Option<&HttpRequest>; - - /// Devuelve el tema que se usará para renderizar el documento. - fn theme(&self) -> ThemeRef; - - /// Devuelve el nombre de la plantilla usada para renderizar el documento. - fn template(&self) -> &str; - - /// Recupera un parámetro como [`Option`]. - fn param<T: 'static>(&self, key: &'static str) -> Option<&T>; - - /// Devuelve el parámetro clonado o el **valor por defecto del tipo** (`T::default()`). - fn param_or_default<T: Default + Clone + 'static>(&self, key: &'static str) -> T { - self.param::<T>(key).cloned().unwrap_or_default() +impl fmt::Display for ErrorParam { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ErrorParam::NotFound => write!(f, "Parameter not found"), + ErrorParam::ParseError(e) => write!(f, "Parse error: {e}"), + } } - - /// Devuelve el parámetro clonado o un **valor por defecto** si no existe. - fn param_or<T: Clone + 'static>(&self, key: &'static str, default: T) -> T { - self.param::<T>(key).cloned().unwrap_or(default) - } - - /// Devuelve el parámetro clonado o el **valor evaluado** por la función `f` si no existe. - fn param_or_else<T: Clone + 'static, F: FnOnce() -> T>(&self, key: &'static str, f: F) -> T { - self.param::<T>(key).cloned().unwrap_or_else(f) - } - - /// Devuelve el Favicon de los recursos del contexto. - fn favicon(&self) -> Option<&Favicon>; - - /// Devuelve las hojas de estilo de los recursos del contexto. - fn stylesheets(&self) -> &Assets<StyleSheet>; - - /// Devuelve los scripts JavaScript de los recursos del contexto. - fn javascripts(&self) -> &Assets<JavaScript>; - - // **< Contextual HELPERS >********************************************************************* - - /// Genera un identificador único por tipo (`<tipo>-<n>`) cuando no se aporta uno explícito. - /// - /// Es útil para componentes u otros elementos HTML que necesitan un identificador predecible si - /// no se proporciona ninguno. - fn required_id<T>(&mut self, id: Option<String>) -> String; } -/// Implementa un **contexto de renderizado** para un documento HTML. -/// -/// Extiende [`Contextual`] con métodos para **instanciar** y configurar un nuevo contexto, -/// **renderizar los recursos** del documento (incluyendo el [`Favicon`], las hojas de estilo -/// [`StyleSheet`] y los scripts [`JavaScript`]), o extender el uso de **parámetros dinámicos -/// tipados** con nuevos métodos. -/// -/// # Ejemplos -/// -/// Crea un nuevo contexto asociado a una solicitud HTTP: -/// -/// ```rust -/// # use pagetop::prelude::*; -/// # use pagetop_aliner::Aliner; -/// fn new_context(request: HttpRequest) -> Context { -/// Context::new(Some(request)) -/// // Establece el idioma del documento a español. -/// .with_langid(&LangMatch::resolve("es-ES")) -/// // Establece el tema para renderizar. -/// .with_theme(&Aliner) -/// // Asigna un favicon. -/// .with_assets(ContextOp::SetFavicon(Some(Favicon::new().with_icon("/favicon.ico")))) -/// // Añade una hoja de estilo externa. -/// .with_assets(ContextOp::AddStyleSheet(StyleSheet::from("/css/style.css"))) -/// // Añade un script JavaScript. -/// .with_assets(ContextOp::AddJavaScript(JavaScript::defer("/js/main.js"))) -/// // Añade un parámetro dinámico al contexto. -/// .with_param("usuario_id", 42) -/// } -/// ``` -/// -/// Y hace operaciones con un contexto dado: -/// -/// ```rust -/// # use pagetop::prelude::*; -/// fn use_context(cx: &mut Context) { -/// // Recupera el tema seleccionado. -/// let active_theme = cx.theme(); -/// assert_eq!(active_theme.short_name(), "aliner"); -/// -/// // Recupera el parámetro a su tipo original. -/// let id: i32 = *cx.get_param::<i32>("usuario_id").unwrap(); -/// assert_eq!(id, 42); -/// -/// // Genera un identificador para un componente de tipo `Menu`. -/// struct Menu; -/// let unique_id = cx.required_id::<Menu>(None); -/// assert_eq!(unique_id, "menu-1"); // Si es el primero generado. -/// } -/// ``` +impl Error for ErrorParam {} + #[rustfmt::skip] pub struct Context { - request : Option<HttpRequest>, // Solicitud HTTP de origen. - langid : &'static LanguageIdentifier, // Identificador de idioma. - theme : ThemeRef, // Referencia al tema usado para renderizar. - template : &'static str, // Nombre de la plantilla usada para renderizar. - favicon : Option<Favicon>, // Favicon, si se ha definido. - stylesheets: Assets<StyleSheet>, // Hojas de estilo CSS. - javascripts: Assets<JavaScript>, // Scripts JavaScript. - regions : ChildrenInRegions, // Regiones de componentes para renderizar. - params : HashMap<&'static str, (Box<dyn Any>, &'static str)>, // Parámetros en ejecución. - id_counter : usize, // Contador para generar identificadores únicos. -} - -impl Default for Context { - fn default() -> Self { - Context::new(None) - } + request : HttpRequest, + langid : &'static LanguageIdentifier, + theme : ThemeRef, + layout : &'static str, + favicon : Option<Favicon>, + stylesheet: Assets<StyleSheet>, + javascript: Assets<JavaScript>, + regions : ChildrenInRegions, + params : HashMap<&'static str, String>, + id_counter: usize, } impl Context { - /// Crea un nuevo contexto asociado a una solicitud HTTP. - /// - /// El contexto inicializa el idioma, el tema y la plantilla por defecto, sin favicon ni otros - /// recursos cargados. #[rustfmt::skip] - pub fn new(request: Option<HttpRequest>) -> Self { - // Se intenta DEFAULT_LANGID. - let langid = DEFAULT_LANGID - // Si es None evalúa la cadena de extracción desde la cabecera HTTP. - .or_else(|| { - request - // Se usa `as_ref()` sobre `Option<HttpRequest>` para no mover el valor. - .as_ref() - .and_then(|req| req.headers().get("Accept-Language")) - .and_then(|value| value.to_str().ok()) - .and_then(|language| LangMatch::resolve(language).as_option()) - }) - // Si todo falla, se recurre a &FALLBACK_LANGID. - .unwrap_or(&FALLBACK_LANGID); - + pub(crate) fn new(request: HttpRequest) -> Self { Context { request, - langid, - theme : *DEFAULT_THEME, - template : Template::DEFAULT, - favicon : None, - stylesheets: Assets::<StyleSheet>::new(), - javascripts: Assets::<JavaScript>::new(), - regions : ChildrenInRegions::default(), - params : HashMap::default(), - id_counter : 0, + langid : &DEFAULT_LANGID, + theme : *DEFAULT_THEME, + layout : "default", + favicon : None, + stylesheet: Assets::<StyleSheet>::new(), + javascript: Assets::<JavaScript>::new(), + regions : ChildrenInRegions::default(), + params : HashMap::<&str, String>::new(), + id_counter: 0, } } - // **< Context RENDER >************************************************************************* - - /// Renderiza los recursos del contexto. - pub fn render_assets(&mut self) -> Markup { - use std::mem::take as mem_take; - - // Extrae temporalmente los recursos. - let favicon = mem_take(&mut self.favicon); // Deja valor por defecto (None) en self. - let stylesheets = mem_take(&mut self.stylesheets); // Assets<StyleSheet>::default() en self. - let javascripts = mem_take(&mut self.javascripts); // Assets<JavaScript>::default() en self. - - // Renderiza con `&mut self` como contexto. - let markup = html! { - @if let Some(fi) = &favicon { - (fi.render(self)) - } - (stylesheets.render(self)) - (javascripts.render(self)) - }; - - // Restaura los campos tal y como estaban. - self.favicon = favicon; - self.stylesheets = stylesheets; - self.javascripts = javascripts; - - markup - } - - /// Renderiza los componentes de la región `region_name`. - pub fn render_region(&mut self, region_name: impl AsRef<str>) -> Markup { - self.regions - .children_for(self.theme, region_name) - .render(self) - } - - // **< Context PARAMS >************************************************************************* - - /// Recupera una *referencia tipada* al parámetro solicitado. - /// - /// Devuelve: - /// - /// - `Ok(&T)` si la clave existe y el tipo coincide. - /// - `Err(ContextError::ParamNotFound)` si la clave no existe. - /// - `Err(ContextError::ParamTypeMismatch)` si la clave existe pero el tipo no coincide. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let cx = Context::new(None) - /// .with_param("usuario_id", 42_i32) - /// .with_param("titulo", "Hola".to_string()); - /// - /// let id: &i32 = cx.get_param("usuario_id").unwrap(); - /// let titulo: &String = cx.get_param("titulo").unwrap(); - /// - /// // Error de tipo: - /// assert!(cx.get_param::<String>("usuario_id").is_err()); - /// ``` - pub fn get_param<T: 'static>(&self, key: &'static str) -> Result<&T, ContextError> { - let (any, type_name) = self.params.get(key).ok_or(ContextError::ParamNotFound)?; - any.downcast_ref::<T>() - .ok_or_else(|| ContextError::ParamTypeMismatch { - key, - expected: TypeInfo::FullName.of::<T>(), - saved: type_name, - }) - } - - /// Recupera el parámetro solicitado y lo elimina del contexto. - /// - /// Devuelve: - /// - /// - `Ok(T)` si la clave existía y el tipo coincide. - /// - `Err(ContextError::ParamNotFound)` si la clave no existe. - /// - `Err(ContextError::ParamTypeMismatch)` si el tipo no coincide. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let mut cx = Context::new(None) - /// .with_param("contador", 7_i32) - /// .with_param("titulo", "Hola".to_string()); - /// - /// let n: i32 = cx.take_param("contador").unwrap(); - /// assert!(cx.get_param::<i32>("contador").is_err()); // ya no está - /// - /// // Error de tipo: - /// assert!(cx.take_param::<i32>("titulo").is_err()); - /// ``` - pub fn take_param<T: 'static>(&mut self, key: &'static str) -> Result<T, ContextError> { - let (boxed, saved) = self.params.remove(key).ok_or(ContextError::ParamNotFound)?; - boxed - .downcast::<T>() - .map(|b| *b) - .map_err(|_| ContextError::ParamTypeMismatch { - key, - expected: TypeInfo::FullName.of::<T>(), - saved, - }) - } - - /// Elimina un parámetro del contexto. Devuelve `true` si la clave existía y se eliminó. - /// - /// Devuelve `false` en caso contrario. Usar cuando sólo interesa borrar la entrada. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let mut cx = Context::new(None).with_param("temp", 1u8); - /// assert!(cx.remove_param("temp")); - /// assert!(!cx.remove_param("temp")); // ya no existe - /// ``` - pub fn remove_param(&mut self, key: &'static str) -> bool { - self.params.remove(key).is_some() - } -} - -/// Permite a [`Context`](crate::core::component::Context) actuar como proveedor de idioma. -/// -/// Devuelve un [`LanguageIdentifier`] siguiendo este orden de prioridad: -/// -/// 1. Un idioma válido establecido explícitamente con [`Context::with_langid`]. -/// 2. El idioma por defecto configurado para la aplicación. -/// 3. Un idioma válido extraído de la cabecera `Accept-Language` del navegador. -/// 4. Y si ninguna de las opciones anteriores aplica, se usa el idioma de respaldo (`"en-US"`). -/// -/// Resulta útil para usar un contexto ([`Context`]) como fuente de traducción en -/// [`L10n::lookup()`](crate::locale::L10n::lookup) o [`L10n::using()`](crate::locale::L10n::using). -impl LangId for Context { - fn langid(&self) -> &'static LanguageIdentifier { - self.langid - } -} - -impl Contextual for Context { - // **< Contextual BUILDER >********************************************************************* - - #[builder_fn] - fn with_request(mut self, request: Option<HttpRequest>) -> Self { - self.request = request; - self - } - - #[builder_fn] - fn with_langid(mut self, language: &impl LangId) -> Self { - self.langid = language.langid(); - self - } - - #[builder_fn] - fn with_theme(mut self, theme: ThemeRef) -> Self { - self.theme = theme; - self - } - - #[builder_fn] - fn with_template(mut self, template_name: &'static str) -> Self { - self.template = template_name; - self - } - - /// Añade o modifica un parámetro dinámico del contexto. - /// - /// El valor se guarda conservando el *nombre del tipo* real para mejorar los mensajes de error - /// posteriores. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let cx = Context::new(None) - /// .with_param("usuario_id", 42_i32) - /// .with_param("titulo", "Hola".to_string()) - /// .with_param("flags", vec!["a", "b"]); - /// ``` - #[builder_fn] - fn with_param<T: 'static>(mut self, key: &'static str, value: T) -> Self { - let type_name = TypeInfo::FullName.of::<T>(); - self.params.insert(key, (Box::new(value), type_name)); - self - } - - #[builder_fn] - fn with_assets(mut self, op: ContextOp) -> Self { + pub fn set_assets(&mut self, op: AssetsOp) -> &mut Self { match op { + AssetsOp::LangId(langid) => { + self.langid = langid; + } + AssetsOp::Theme(theme_name) => { + self.theme = theme_by_short_name(theme_name).unwrap_or(*DEFAULT_THEME); + } + AssetsOp::Layout(layout) => { + self.layout = layout; + } // Favicon. - ContextOp::SetFavicon(favicon) => { + AssetsOp::SetFavicon(favicon) => { self.favicon = favicon; } - ContextOp::SetFaviconIfNone(icon) => { + AssetsOp::SetFaviconIfNone(icon) => { if self.favicon.is_none() { self.favicon = Some(icon); } } // Stylesheets. - ContextOp::AddStyleSheet(css) => { - self.stylesheets.add(css); + AssetsOp::AddStyleSheet(css) => { + self.stylesheet.add(css); } - ContextOp::RemoveStyleSheet(path) => { - self.stylesheets.remove(path); + AssetsOp::RemoveStyleSheet(path) => { + self.stylesheet.remove(path); } - // Scripts JavaScript. - ContextOp::AddJavaScript(js) => { - self.javascripts.add(js); + // JavaScripts. + AssetsOp::AddJavaScript(js) => { + self.javascript.add(js); } - ContextOp::RemoveJavaScript(path) => { - self.javascripts.remove(path); + AssetsOp::RemoveJavaScript(path) => { + self.javascript.remove(path); } } self } - #[builder_fn] - fn with_child_in(mut self, region_name: impl AsRef<str>, op: ChildOp) -> Self { - self.regions.alter_child_in(region_name, op); + pub fn set_in_region(&mut self, region: &'static str, op: ChildOp) -> &mut Self { + self.regions.set_in_region(region, op); self } - // **< Contextual GETTERS >********************************************************************* - - fn request(&self) -> Option<&HttpRequest> { - self.request.as_ref() + pub fn set_param<T: FromStr + ToString>(&mut self, key: &'static str, value: &T) -> &mut Self { + self.params.insert(key, value.to_string()); + self } - fn theme(&self) -> ThemeRef { + // Context GETTERS. + + pub fn request(&self) -> &HttpRequest { + &self.request + } + + pub fn langid(&self) -> &LanguageIdentifier { + self.langid + } + + pub fn theme(&self) -> ThemeRef { self.theme } - fn template(&self) -> &str { - self.template + pub fn layout(&self) -> &str { + self.layout } - /// Recupera un parámetro como [`Option`], simplificando el acceso. - /// - /// A diferencia de [`get_param`](Self::get_param), que devuelve un [`Result`] con información - /// detallada de error, este método devuelve `None` tanto si la clave no existe como si el valor - /// guardado no coincide con el tipo solicitado. - /// - /// Resulta útil en escenarios donde sólo interesa saber si el valor existe y es del tipo - /// correcto, sin necesidad de diferenciar entre error de ausencia o de tipo. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let cx = Context::new(None).with_param("username", "Alice".to_string()); - /// - /// // Devuelve Some(&String) si existe y coincide el tipo. - /// assert_eq!(cx.param::<String>("username").map(|s| s.as_str()), Some("Alice")); - /// - /// // Devuelve None si no existe o si el tipo no coincide. - /// assert!(cx.param::<i32>("username").is_none()); - /// assert!(cx.param::<String>("missing").is_none()); - /// - /// // Acceso con valor por defecto. - /// let user = cx.param::<String>("missing") - /// .cloned() - /// .unwrap_or_else(|| "visitor".to_string()); - /// assert_eq!(user, "visitor"); - /// ``` - fn param<T: 'static>(&self, key: &'static str) -> Option<&T> { - self.get_param::<T>(key).ok() + pub fn regions(&self) -> &ChildrenInRegions { + &self.regions } - fn favicon(&self) -> Option<&Favicon> { - self.favicon.as_ref() + pub fn get_param<T: FromStr + ToString>(&self, key: &'static str) -> Result<T, ErrorParam> { + self.params + .get(key) + .ok_or(ErrorParam::NotFound) + .and_then(|v| T::from_str(v).map_err(|_| ErrorParam::ParseError(v.clone()))) } - fn stylesheets(&self) -> &Assets<StyleSheet> { - &self.stylesheets + // Context RENDER. + + pub fn render_assets(&mut self) -> Markup { + html! { + @if let Some(favicon) = &self.favicon { + (favicon.render()) + } + (self.stylesheet.render()) + (self.javascript.render()) + } } - fn javascripts(&self) -> &Assets<JavaScript> { - &self.javascripts + pub fn render_region(&mut self, region: impl Into<String>) -> Markup { + self.regions + .all_in_region(self.theme, &region.into()) + .render(self) } - // **< Contextual HELPERS >********************************************************************* + // Context EXTRAS. - /// Devuelve un identificador único dentro del contexto para el tipo `T`, si no se proporciona - /// un `id` explícito. - /// - /// Si no se proporciona un `id`, se genera un identificador único en la forma `<tipo>-<número>` - /// donde `<tipo>` es el nombre corto del tipo en minúsculas (sin espacios) y `<número>` es un - /// contador interno incremental. - fn required_id<T>(&mut self, id: Option<String>) -> String { + pub fn remove_param(&mut self, key: &'static str) -> bool { + self.params.remove(key).is_some() + } + + pub fn required_id<T>(&mut self, id: Option<String>) -> String { if let Some(id) = id { id } else { @@ -555,12 +188,12 @@ impl Contextual for Context { .replace(' ', "_") .to_lowercase(); let prefix = if prefix.is_empty() { - "prefix".to_string() + "prefix".to_owned() } else { prefix }; self.id_counter += 1; - join!(prefix, "-", self.id_counter.to_string()) + concat_string!(prefix, "-", self.id_counter.to_string()) } } } diff --git a/src/core/component/definition.rs b/src/core/component/definition.rs index 13b03851..af33c943 100644 --- a/src/core/component/definition.rs +++ b/src/core/component/definition.rs @@ -1,142 +1,66 @@ use crate::base::action; use crate::core::component::Context; -use crate::core::{AnyInfo, TypeInfo}; +use crate::core::AnyBase; use crate::html::{html, Markup, PrepareMarkup}; +use crate::util::TypeInfo; -/// Define la función de renderizado para todos los componentes. -/// -/// Este *trait* se implementa automáticamente en cualquier tipo (componente) que implemente -/// [`Component`], por lo que no requiere ninguna codificación manual. -pub trait ComponentRender { - /// Renderiza el componente usando el contexto proporcionado. +pub trait ComponentBase { fn render(&mut self, cx: &mut Context) -> Markup; } -/// Interfaz común que debe implementar un componente renderizable en PageTop. -/// -/// Se recomienda que los componentes deriven [`AutoDefault`](crate::AutoDefault). También deben -/// implementar explícitamente el método [`new()`](Self::new) y pueden sobrescribir los otros -/// métodos para personalizar su comportamiento. -pub trait Component: AnyInfo + ComponentRender + Send + Sync { - /// Crea una nueva instancia del componente. +pub trait ComponentTrait: AnyBase + ComponentBase + Send + Sync { fn new() -> Self where Self: Sized; - /// Devuelve el nombre del componente. - /// - /// Por defecto se obtiene del nombre corto del tipo usando [`TypeInfo::ShortName`]. fn name(&self) -> &'static str { TypeInfo::ShortName.of::<Self>() } - /// Devuelve una descripción del componente, si existe. - /// - /// Por defecto, no se proporciona ninguna descripción (`None`). fn description(&self) -> Option<String> { None } - /// Devuelve el identificador del componente, si existe. - /// - /// Este identificador puede usarse para referenciar el componente en el HTML. Por defecto, no - /// tiene ningún identificador (`None`). fn id(&self) -> Option<String> { None } - /// Indica si el componente es renderizable. - /// - /// Por defecto, todos los componentes son renderizables (`true`). Sin embargo, este método - /// puede sobrescribirse para decidir dinámicamente si los componentes de este tipo se - /// renderizan o no en función del contexto de renderizado. - /// - /// También puede usarse junto con un alias de función como - /// ([`FnIsRenderable`](crate::core::component::FnIsRenderable)) para permitir que instancias - /// concretas del componente decidan si se renderizan o no. - #[allow(unused_variables)] - fn is_renderable(&self, cx: &mut Context) -> bool { - true - } - - /// Configura el componente justo antes de preparar el renderizado. - /// - /// Este método puede sobrescribirse para modificar la estructura interna del componente o el - /// contexto antes de preparar la renderización del componente. Por defecto no hace nada. #[allow(unused_variables)] fn setup_before_prepare(&mut self, cx: &mut Context) {} - /// Devuelve una representación renderizada del componente. - /// - /// Este método forma parte del ciclo de vida de los componentes y se invoca automáticamente - /// durante el proceso de construcción del documento. Puede sobrescribirse para generar - /// dinámicamente el contenido HTML con acceso al contexto de renderizado. - /// - /// Este método debe ser capaz de preparar el renderizado del componente con los métodos del - /// propio componente y el contexto proporcionado, no debería hacerlo accediendo directamente a - /// los campos de la estructura del componente. Es una forma de garantizar que los programadores - /// podrán sobrescribir este método sin preocuparse por los detalles internos del componente. - /// - /// Por defecto, devuelve [`PrepareMarkup::None`]. #[allow(unused_variables)] fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { PrepareMarkup::None } } -/// Implementa [`render()`](ComponentRender::render) para todos los componentes. -/// -/// El proceso de renderizado de cada componente sigue esta secuencia: -/// -/// 1. Ejecuta [`is_renderable()`](Component::is_renderable) para ver si puede renderizarse en el -/// contexto actual. Si no es así, devuelve un [`Markup`] vacío. -/// 2. Ejecuta [`setup_before_prepare()`](Component::setup_before_prepare) para que el componente -/// pueda ajustar su estructura interna o modificar el contexto. -/// 3. Despacha [`action::theme::BeforeRender<C>`](crate::base::action::theme::BeforeRender) para -/// permitir que el tema realice ajustes previos. -/// 4. Despacha [`action::component::BeforeRender<C>`](crate::base::action::component::BeforeRender) -/// para que otras extensiones puedan también hacer ajustes previos. -/// 5. **Prepara el renderizado del componente**: -/// - Despacha [`action::theme::PrepareRender<C>`](crate::base::action::theme::PrepareRender) -/// para permitir al tema generar un renderizado alternativo. -/// - Si el tema no lo modifica, llama a [`prepare_component()`](Component::prepare_component) -/// para obtener el renderizado por defecto del componente. -/// 6. Despacha [`action::theme::AfterRender<C>`](crate::base::action::theme::AfterRender) para -/// que el tema pueda aplicar ajustes finales. -/// 7. Despacha [`action::component::AfterRender<C>`](crate::base::action::component::AfterRender) -/// para que otras extensiones puedan hacer sus últimos ajustes. -/// 8. Devuelve el [`Markup`] generado en el paso 5. -impl<C: Component> ComponentRender for C { +impl<C: ComponentTrait> ComponentBase for C { fn render(&mut self, cx: &mut Context) -> Markup { - // Si no es renderizable, devuelve un bloque HTML vacío. - if !self.is_renderable(cx) { - return html! {}; - } + if action::component::IsRenderable::dispatch(self, cx) { + // Comprueba el componente antes de prepararlo. + self.setup_before_prepare(cx); - // Configura el componente antes de preparar. - self.setup_before_prepare(cx); + // Acciones del tema antes de preparar el componente. + action::theme::BeforePrepare::dispatch(self, cx); - // Acciones específicas del tema antes de renderizar el componente. - action::theme::BeforeRender::dispatch(self, cx); + // Acciones de los módulos antes de preparar el componente. + action::component::BeforePrepare::dispatch(self, cx); - // Acciones de las extensiones antes de renderizar el componente. - action::component::BeforeRender::dispatch(self, cx); + // Renderiza el componente. + let markup = match action::theme::RenderComponent::dispatch(self, cx) { + Some(html) => html, + None => self.prepare_component(cx).render(), + }; - // Prepara el renderizado del componente. - let prepare = action::theme::PrepareRender::dispatch(self, cx); - let prepare = if prepare.is_empty() { - self.prepare_component(cx) + // Acciones del tema después de preparar el componente. + action::theme::AfterPrepare::dispatch(self, cx); + + // Acciones de los módulos después de preparar el componente. + action::component::AfterPrepare::dispatch(self, cx); + + markup } else { - prepare - }; - - // Acciones específicas del tema después de renderizar el componente. - action::theme::AfterRender::dispatch(self, cx); - - // Acciones de las extensiones después de renderizar el componente. - action::component::AfterRender::dispatch(self, cx); - - // Devuelve el marcado final. - prepare.render() + html! {} + } } } diff --git a/src/core/extension.rs b/src/core/extension.rs deleted file mode 100644 index 6ae6d333..00000000 --- a/src/core/extension.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! API para añadir nuevas funcionalidades usando extensiones. -//! -//! Cada funcionalidad adicional que quiera incorporarse a una aplicación PageTop se debe modelar -//! como una **extensión**. Todas comparten la misma interfaz declarada en [`Extension`]. - -mod definition; -pub use definition::{Extension, ExtensionRef}; - -pub(crate) mod all; diff --git a/src/core/extension/all.rs b/src/core/extension/all.rs deleted file mode 100644 index fa67671d..00000000 --- a/src/core/extension/all.rs +++ /dev/null @@ -1,142 +0,0 @@ -use crate::core::action::add_action; -use crate::core::extension::ExtensionRef; -use crate::core::theme::all::THEMES; -use crate::{global, service, static_files_service, trace}; - -use parking_lot::RwLock; - -use std::sync::LazyLock; - -// **< EXTENSIONES >******************************************************************************** - -static ENABLED_EXTENSIONS: LazyLock<RwLock<Vec<ExtensionRef>>> = - LazyLock::new(|| RwLock::new(Vec::new())); - -static DROPPED_EXTENSIONS: LazyLock<RwLock<Vec<ExtensionRef>>> = - LazyLock::new(|| RwLock::new(Vec::new())); - -// **< REGISTRO DE LAS EXTENSIONES >**************************************************************** - -pub fn register_extensions(root_extension: Option<ExtensionRef>) { - // Prepara la lista de extensiones habilitadas. - let mut enabled_list: Vec<ExtensionRef> = Vec::new(); - - // Primero añade el tema básico a la lista de extensiones habilitadas. - add_to_enabled(&mut enabled_list, &crate::base::theme::Basic); - - // Si se proporciona una extensión raíz inicial, se añade a la lista de extensiones habilitadas. - if let Some(extension) = root_extension { - add_to_enabled(&mut enabled_list, extension); - } - - // Añade la página de bienvenida por defecto a la lista de extensiones habilitadas. - add_to_enabled(&mut enabled_list, &crate::base::extension::Welcome); - - // Guarda la lista final de extensiones habilitadas. - ENABLED_EXTENSIONS.write().append(&mut enabled_list); - - // Prepara una lista de extensiones deshabilitadas. - let mut dropped_list: Vec<ExtensionRef> = Vec::new(); - - // Si se proporciona una extensión raíz, analiza su lista de dependencias. - if let Some(extension) = root_extension { - add_to_dropped(&mut dropped_list, extension); - } - - // Guarda la lista final de extensiones deshabilitadas. - DROPPED_EXTENSIONS.write().append(&mut dropped_list); -} - -fn add_to_enabled(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) { - // Verifica que la extensión no esté en la lista para evitar duplicados. - if !list.iter().any(|e| e.type_id() == extension.type_id()) { - // Añade primero (en orden inverso) las dependencias de la extensión. - for d in extension.dependencies().iter().rev() { - add_to_enabled(list, *d); - } - - // Añade la propia extensión a la lista. - list.push(extension); - - // Comprueba si la extensión tiene un tema asociado que deba registrarse. - if let Some(theme) = extension.theme() { - let mut registered_themes = THEMES.write(); - // Asegura que el tema no esté ya registrado para evitar duplicados. - if !registered_themes - .iter() - .any(|t| t.type_id() == theme.type_id()) - { - registered_themes.push(theme); - trace::debug!("Enabling \"{}\" theme", theme.short_name()); - } - } else { - trace::debug!("Enabling \"{}\" extension", extension.short_name()); - } - } -} - -fn add_to_dropped(list: &mut Vec<ExtensionRef>, extension: ExtensionRef) { - // Recorre las extensiones que la actual recomienda deshabilitar. - for d in &extension.drop_extensions() { - // Verifica que la extensión no esté ya en la lista. - if !list.iter().any(|e| e.type_id() == d.type_id()) { - // Comprueba si la extensión está habilitada. Si es así, registra una advertencia. - if ENABLED_EXTENSIONS - .read() - .iter() - .any(|e| e.type_id() == extension.type_id()) - { - trace::warn!( - "Trying to drop \"{}\" extension which is enabled", - extension.short_name() - ); - } else { - // Si la extensión no está habilitada, se añade a la lista y registra la acción. - list.push(*d); - trace::debug!("Extension \"{}\" dropped", d.short_name()); - // Añade recursivamente las dependencias de la extensión eliminada. - // De este modo, todas las dependencias se tienen en cuenta para ser deshabilitadas. - for dependency in &extension.dependencies() { - add_to_dropped(list, *dependency); - } - } - } - } -} - -// **< REGISTRO DE LAS ACCIONES >******************************************************************* - -pub fn register_actions() { - for extension in ENABLED_EXTENSIONS.read().iter() { - for a in extension.actions().into_iter() { - add_action(a); - } - } -} - -// **< INICIALIZA LAS EXTENSIONES >***************************************************************** - -pub fn initialize_extensions() { - trace::info!("Calling application bootstrap"); - for extension in ENABLED_EXTENSIONS.read().iter() { - extension.initialize(); - } -} - -// **< CONFIGURA LOS SERVICIOS >******************************************************************** - -pub fn configure_services(scfg: &mut service::web::ServiceConfig) { - // Sólo compila durante el desarrollo, para evitar errores 400 en la traza de eventos. - #[cfg(debug_assertions)] - scfg.route( - // Ruta automática lanzada por Chrome DevTools. - "/.well-known/appspecific/com.chrome.devtools.json", - service::web::get().to(|| async { service::HttpResponse::NotFound().finish() }), - ); - - for extension in ENABLED_EXTENSIONS.read().iter() { - extension.configure_service(scfg); - } - - static_files_service!(scfg, [&global::SETTINGS.dev.pagetop_static_dir, assets] => "/"); -} diff --git a/src/core/extension/definition.rs b/src/core/extension/definition.rs deleted file mode 100644 index 520153d0..00000000 --- a/src/core/extension/definition.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::core::action::ActionBox; -use crate::core::theme::ThemeRef; -use crate::core::AnyInfo; -use crate::locale::L10n; -use crate::{actions_boxed, service}; - -/// Representa una referencia a una extensión. -/// -/// Las extensiones se definen como instancias estáticas globales para poder acceder a ellas desde -/// cualquier hilo de la ejecución sin necesidad de sincronización adicional. -pub type ExtensionRef = &'static dyn Extension; - -/// Interfaz común que debe implementar cualquier extensión de PageTop. -/// -/// Este *trait* es fácil de implementar, basta con declarar una estructura de tamaño cero para la -/// extensión y sobreescribir los métodos que sea necesario. -/// -/// ```rust -/// # use pagetop::prelude::*; -/// pub struct Blog; -/// -/// impl Extension for Blog { -/// fn name(&self) -> L10n { L10n::n("Blog") } -/// fn description(&self) -> L10n { L10n::n("Blog system") } -/// } -/// ``` -pub trait Extension: AnyInfo + Send + Sync { - /// Nombre localizado de la extensión legible para el usuario. - /// - /// Predeterminado por el [`short_name()`](AnyInfo::short_name) del tipo asociado a la - /// extensión. - fn name(&self) -> L10n { - L10n::n(self.short_name()) - } - - /// Descripción corta localizada de la extensión para paneles, listados, etc. - fn description(&self) -> L10n { - L10n::default() - } - - /// Devuelve una referencia a esta misma extensión cuando se trata de un tema. - /// - /// Para ello, debe implementar [`Extension`] y también [`Theme`](crate::core::theme::Theme). Si - /// la extensión no es un tema, este método devuelve `None` por defecto. - /// - /// ```rust - /// # use pagetop::prelude::*; - /// pub struct MyTheme; - /// - /// impl Extension for MyTheme { - /// fn theme(&self) -> Option<ThemeRef> { - /// Some(&Self) - /// } - /// } - /// - /// impl Theme for MyTheme {} - /// ``` - fn theme(&self) -> Option<ThemeRef> { - None - } - - /// Otras extensiones que deben habilitarse **antes** de esta. - /// - /// PageTop las resolverá automáticamente respetando el orden durante el arranque de la - /// aplicación. - fn dependencies(&self) -> Vec<ExtensionRef> { - vec![] - } - - /// Devuelve la lista de acciones que la extensión va a registrar. - /// - /// Estas [acciones](crate::core::action) se despachan por orden de registro o por - /// [peso](crate::Weight), permitiendo personalizar el comportamiento de la aplicación en puntos - /// específicos. - fn actions(&self) -> Vec<ActionBox> { - actions_boxed![] - } - - /// Inicializa la extensión durante la fase de arranque de la aplicación. - /// - /// Se llama una sola vez, después de que todas las dependencias se han inicializado y antes de - /// aceptar cualquier petición HTTP. - fn initialize(&self) {} - - /// Configura los servicios web de la extensión, como rutas, *middleware*, acceso a ficheros - /// estáticos, etc., usando [`ServiceConfig`](crate::service::web::ServiceConfig). - /// - /// ```rust,ignore - /// # use pagetop::prelude::*; - /// pub struct ExtensionSample; - /// - /// impl Extension for ExtensionSample { - /// fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - /// scfg.route("/sample", web::get().to(route_sample)); - /// } - /// } - /// ``` - #[allow(unused_variables)] - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {} - - /// Permite crear extensiones para deshabilitar y desinstalar recursos de otras de versiones - /// anteriores de la aplicación. - /// - /// Actualmente no se usa, pero se deja como *placeholder* para futuras implementaciones. - fn drop_extensions(&self) -> Vec<ExtensionRef> { - vec![] - } -} diff --git a/src/core/package.rs b/src/core/package.rs new file mode 100644 index 00000000..e0c6a12e --- /dev/null +++ b/src/core/package.rs @@ -0,0 +1,4 @@ +mod definition; +pub use definition::{PackageRef, PackageTrait}; + +pub(crate) mod all; diff --git a/src/core/package/all.rs b/src/core/package/all.rs new file mode 100644 index 00000000..301d3a02 --- /dev/null +++ b/src/core/package/all.rs @@ -0,0 +1,134 @@ +use crate::core::action::add_action; +use crate::core::package::PackageRef; +use crate::core::theme::all::THEMES; +use crate::{global, include_files, include_files_service, service, trace}; + +use std::sync::{LazyLock, RwLock}; + +// PACKAGES **************************************************************************************** + +static ENABLED_PACKAGES: LazyLock<RwLock<Vec<PackageRef>>> = + LazyLock::new(|| RwLock::new(Vec::new())); + +static DROPPED_PACKAGES: LazyLock<RwLock<Vec<PackageRef>>> = + LazyLock::new(|| RwLock::new(Vec::new())); + +// REGISTER PACKAGES ******************************************************************************* + +pub fn register_packages(root_package: Option<PackageRef>) { + // Initialize a list for packages to be enabled. + let mut enabled_list: Vec<PackageRef> = Vec::new(); + + // Add default theme to the enabled list. + add_to_enabled(&mut enabled_list, &crate::base::theme::Basic); + + // If a root package is provided, add it to the enabled list. + if let Some(package) = root_package { + add_to_enabled(&mut enabled_list, package); + } + + // Add default welcome page package to the enabled list. + add_to_enabled(&mut enabled_list, &crate::base::package::Welcome); + + // Save the final list of enabled packages. + ENABLED_PACKAGES.write().unwrap().append(&mut enabled_list); + + // Initialize a list for packages to be dropped. + let mut dropped_list: Vec<PackageRef> = Vec::new(); + // If a root package is provided, analyze its dropped list. + if let Some(package) = root_package { + add_to_dropped(&mut dropped_list, package); + } + // Save the final list of dropped packages. + DROPPED_PACKAGES.write().unwrap().append(&mut dropped_list); +} + +fn add_to_enabled(list: &mut Vec<PackageRef>, package: PackageRef) { + // Check if the package is not already in the enabled list to avoid duplicates. + if !list.iter().any(|p| p.type_id() == package.type_id()) { + // Add the package dependencies in reverse order first. + for d in package.dependencies().iter().rev() { + add_to_enabled(list, *d); + } + + // Add the package itself to the enabled list. + list.push(package); + + // Check if the package has an associated theme to register. + if let Some(theme) = package.theme() { + let mut registered_themes = THEMES.write().unwrap(); + // Ensure the theme is not already registered to avoid duplicates. + if !registered_themes + .iter() + .any(|t| t.type_id() == theme.type_id()) + { + registered_themes.push(theme); + trace::debug!("Enabling \"{}\" theme", theme.short_name()); + } + } else { + trace::debug!("Enabling \"{}\" package", package.short_name()); + } + } +} + +fn add_to_dropped(list: &mut Vec<PackageRef>, package: PackageRef) { + // Iterate through packages recommended to be dropped. + for d in &package.drop_packages() { + // Check if the package is not already in the dropped list. + if !list.iter().any(|p| p.type_id() == d.type_id()) { + // Check if the package is currently enabled. If so, log a warning. + if ENABLED_PACKAGES + .read() + .unwrap() + .iter() + .any(|p| p.type_id() == package.type_id()) + { + trace::warn!( + "Trying to drop \"{}\" package which is enabled", + package.short_name() + ); + } else { + // If the package is not enabled, add it to the dropped list and log the action. + list.push(*d); + trace::debug!("Package \"{}\" dropped", d.short_name()); + // Recursively add the dependencies of the dropped package to the dropped list. + // This ensures that all dependencies are also considered for dropping. + for dependency in &package.dependencies() { + add_to_dropped(list, *dependency); + } + } + } + } +} + +// REGISTER ACTIONS ******************************************************************************** + +pub fn register_actions() { + for m in ENABLED_PACKAGES.read().unwrap().iter() { + for a in m.actions().into_iter() { + add_action(a); + } + } +} + +// INIT PACKAGES *********************************************************************************** + +pub fn init_packages() { + trace::info!("Calling application bootstrap"); + for m in ENABLED_PACKAGES.read().unwrap().iter() { + m.init(); + } +} + +// CONFIGURE SERVICES ****************************************************************************** + +include_files!(assets); + +pub fn configure_services(scfg: &mut service::web::ServiceConfig) { + for m in ENABLED_PACKAGES.read().unwrap().iter() { + m.configure_service(scfg); + } + include_files_service!( + scfg, assets => "/", [&global::SETTINGS.dev.pagetop_project_dir, "static"] + ); +} diff --git a/src/core/package/definition.rs b/src/core/package/definition.rs new file mode 100644 index 00000000..b1d0a8c9 --- /dev/null +++ b/src/core/package/definition.rs @@ -0,0 +1,39 @@ +use crate::core::action::ActionBox; +use crate::core::theme::ThemeRef; +use crate::core::AnyBase; +use crate::locale::L10n; +use crate::{actions, service}; + +pub type PackageRef = &'static dyn PackageTrait; + +/// Los paquetes deben implementar este *trait*. +pub trait PackageTrait: AnyBase + Send + Sync { + fn name(&self) -> L10n { + L10n::n(self.short_name()) + } + + fn description(&self) -> L10n { + L10n::default() + } + + fn theme(&self) -> Option<ThemeRef> { + None + } + + fn dependencies(&self) -> Vec<PackageRef> { + vec![] + } + + fn drop_packages(&self) -> Vec<PackageRef> { + vec![] + } + + fn actions(&self) -> Vec<ActionBox> { + actions![] + } + + fn init(&self) {} + + #[allow(unused_variables)] + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) {} +} diff --git a/src/core/theme.rs b/src/core/theme.rs index 8774276e..adb99d59 100644 --- a/src/core/theme.rs +++ b/src/core/theme.rs @@ -1,21 +1,5 @@ -//! API para añadir y gestionar nuevos temas. -//! -//! En PageTop un tema es la *piel* de la aplicación. Es responsable último de los estilos, -//! tipografías, espaciados y cualquier otro detalle visual o interactivo (animaciones, scripts de -//! interfaz, etc.). -//! -//! Un tema determina el aspecto final de un documento HTML sin alterar la lógica interna de los -//! componentes ni la estructura del documento, que queda definida por la plantilla -//! ([`Template`](crate::base::component::Template)) utilizada por cada página. -//! -//! Los temas son extensiones que implementan [`Extension`](crate::core::extension::Extension), por -//! lo que se instancian, declaran dependencias y se inician igual que cualquier otra extensión. -//! También deben implementar [`Theme`] y sobrescribir el método -//! [`Extension::theme()`](crate::core::extension::Extension::theme) para que PageTop pueda -//! registrarlos como temas. - mod definition; -pub use definition::{Theme, ThemeRef}; +pub use definition::{ThemeRef, ThemeTrait}; mod regions; pub(crate) use regions::ChildrenInRegions; diff --git a/src/core/theme/all.rs b/src/core/theme/all.rs index 5e6b65b0..6e10eae7 100644 --- a/src/core/theme/all.rs +++ b/src/core/theme/all.rs @@ -1,15 +1,13 @@ use crate::core::theme::ThemeRef; use crate::global; -use parking_lot::RwLock; +use std::sync::{LazyLock, RwLock}; -use std::sync::LazyLock; - -// **< TEMAS >************************************************************************************** +// THEMES ****************************************************************************************** pub static THEMES: LazyLock<RwLock<Vec<ThemeRef>>> = LazyLock::new(|| RwLock::new(Vec::new())); -// **< TEMA PREDETERMINADO >************************************************************************ +// DEFAULT THEME *********************************************************************************** pub static DEFAULT_THEME: LazyLock<ThemeRef> = LazyLock::new(|| match theme_by_short_name(&global::SETTINGS.app.theme) { @@ -17,13 +15,13 @@ pub static DEFAULT_THEME: LazyLock<ThemeRef> = None => &crate::base::theme::Basic, }); -// **< TEMA POR NOMBRE >**************************************************************************** +// THEME BY NAME *********************************************************************************** -// Devuelve el tema identificado por su [`short_name()`](AnyInfo::short_name). -pub fn theme_by_short_name(short_name: &'static str) -> Option<ThemeRef> { +pub fn theme_by_short_name(short_name: &str) -> Option<ThemeRef> { let short_name = short_name.to_lowercase(); match THEMES .read() + .unwrap() .iter() .find(|t| t.short_name().to_lowercase() == short_name) { diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs index de11d1ba..01e1cb04 100644 --- a/src/core/theme/definition.rs +++ b/src/core/theme/definition.rs @@ -1,177 +1,59 @@ -use crate::base::component::Template; -use crate::core::component::{ComponentRender, ContextOp, Contextual}; -use crate::core::extension::Extension; +use crate::core::package::PackageTrait; use crate::global; -use crate::html::{html, Markup, StyleSheet}; +use crate::html::{html, Markup}; use crate::locale::L10n; use crate::response::page::Page; -/// Referencia estática a un tema. -/// -/// Los temas son también extensiones. Por tanto, deben declararse como **instancias estáticas** que -/// implementen [`Theme`] y, a su vez, [`Extension`]. Estas instancias se exponen usando -/// [`Extension::theme()`](crate::core::extension::Extension::theme). -pub type ThemeRef = &'static dyn Theme; +pub type ThemeRef = &'static dyn ThemeTrait; -/// Interfaz común que debe implementar cualquier tema de PageTop. -/// -/// Un tema es una [`Extension`](crate::core::extension::Extension) que define el aspecto general de -/// las páginas: cómo se renderiza el `<head>`, cómo se presenta el `<body>` mediante plantillas -/// ([`Template`]) y qué contenido mostrar en las páginas de error. -/// -/// Todos los métodos de este *trait* tienen una implementación por defecto, por lo que pueden -/// sobrescribirse selectivamente para crear nuevos temas con comportamientos distintos a los -/// predeterminados. -/// -/// El único método **obligatorio** de `Extension` para un tema es [`theme()`](Extension::theme), -/// que debe devolver una referencia estática al propio tema: -/// -/// ```rust -/// # use pagetop::prelude::*; -/// pub struct MyTheme; -/// -/// impl Extension for MyTheme { -/// fn name(&self) -> L10n { -/// L10n::n("My theme") -/// } -/// -/// fn description(&self) -> L10n { -/// L10n::n("A personal theme") -/// } -/// -/// fn theme(&self) -> Option<ThemeRef> { -/// Some(&Self) -/// } -/// } -/// -/// impl Theme for MyTheme {} -/// ``` -pub trait Theme: Extension + Send + Sync { - /// Acciones específicas del tema antes de renderizar el `<body>` de la página. - /// - /// Se invoca antes de que se procese la plantilla ([`Template`]) asociada a la página - /// ([`Page::template()`](crate::response::page::Page::template)). Es un buen lugar para - /// inicializar o ajustar recursos en función del contexto de la página, por ejemplo: - /// - /// - Añadir metadatos o propiedades a la página. - /// - Preparar atributos compartidos. - /// - Registrar *assets* condicionales en el contexto. - #[allow(unused_variables)] - fn before_render_page_body(&self, page: &mut Page) {} - - /// Renderiza el contenido del `<body>` de la página. - /// - /// Por defecto, delega en la plantilla ([`Template`]) asociada a la página - /// ([`Page::template()`](crate::response::page::Page::template)). La plantilla se encarga de - /// procesar las regiones y renderizar los componentes registrados en el contexto. - /// - /// Los temas pueden sobrescribir este método para: - /// - /// - Forzar una plantilla concreta en determinadas páginas. - /// - Envolver el contenido en marcadores adicionales. - /// - Implementar lógicas de composición alternativas. - #[inline] - fn render_page_body(&self, page: &mut Page) -> Markup { - Template::named(page.template()).render(page.context()) +/// Los temas deben implementar este "trait". +pub trait ThemeTrait: PackageTrait + Send + Sync { + fn regions(&self) -> Vec<(&'static str, L10n)> { + vec![("content", L10n::l("content"))] } - /// Acciones específicas del tema después de renderizar el `<body>` de la página. - /// - /// Se invoca tras la generación del contenido del `<body>`. Es útil para: - /// - /// - Ajustar o registrar recursos en función de lo que se haya renderizado. - /// - Realizar *tracing* o recopilar métricas. - /// - Aplicar ajustes finales al estado de la página antes de producir el `<head>` o la - /// respuesta final. - /// - /// La implementación por defecto añade una serie de hojas de estilo básicas (`normalize.css`, - /// `root.css`, `basic.css`) cuando el parámetro `include_basic_assets` de la página está - /// activado. - #[allow(unused_variables)] - fn after_render_page_body(&self, page: &mut Page) { - 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), - )); - } - } - - /// Renderiza el contenido del `<head>` de la página. - /// - /// Aunque en una página el `<head>` se encuentra antes del `<body>`, internamente se renderiza - /// después para contar con los ajustes que hayan ido acumulando los componentes. Por ejemplo, - /// permitiría añadir un archivo de iconos sólo si se ha incluido un icono en la página. - /// - /// Por defecto incluye: - /// - /// - La codificación (`charset="utf-8"`). - /// - El título, usando el título de la página si existe y, en caso contrario, sólo el nombre de - /// la aplicación. - /// - La descripción (`<meta name="description">`), si está definida. - /// - La etiqueta `viewport` básica para diseño adaptable. - /// - Los metadatos (`name`/`content`) y propiedades (`property`/`content`) declarados en la - /// página. - /// - Todos los *assets* registrados en el contexto de la página. - /// - /// Los temas pueden sobrescribir este método para añadir etiquetas adicionales (por ejemplo, - /// *favicons* personalizados, manifest, etiquetas de analítica, etc.). - #[inline] - fn render_page_head(&self, page: &mut Page) -> Markup { + fn render_head(&self, page: &mut Page) -> Markup { let viewport = "width=device-width, initial-scale=1, shrink-to-fit=no"; html! { - meta charset="utf-8"; + head { + meta charset="utf-8"; - @if let Some(title) = page.title() { - title { (global::SETTINGS.app.name) (" | ") (title) } - } @else { - title { (global::SETTINGS.app.name) } + @if let Some(title) = page.title() { + title { (global::SETTINGS.app.name) (" | ") (title) } + } @else { + title { (global::SETTINGS.app.name) } + } + + @if let Some(description) = page.description() { + meta name="description" content=(description); + } + + meta name="viewport" content=(viewport); + @for (name, content) in page.metadata() { + meta name=(name) content=(content) {} + } + + meta http-equiv="X-UA-Compatible" content="IE=edge"; + @for (property, content) in page.properties() { + meta property=(property) content=(content) {} + } + + (page.context().render_assets()) } - - @if let Some(description) = page.description() { - meta name="description" content=(description); - } - - meta name="viewport" content=(viewport); - @for (name, content) in page.metadata() { - meta name=(name) content=(content) {} - } - - meta http-equiv="X-UA-Compatible" content="IE=edge"; - @for (property, content) in page.properties() { - meta property=(property) content=(content) {} - } - - (page.context().render_assets()) } } - /// Contenido predeterminado para la página de error "*403 - Forbidden*". - /// - /// Los temas pueden sobrescribir este método para personalizar el diseño y el contenido de la - /// página de error, manteniendo o no el mensaje de los textos localizados. - fn error403(&self, page: &mut Page) -> Markup { - html! { div { h1 { (L10n::l("error403_notice").using(page)) } } } + #[allow(unused_variables)] + fn before_render_body(&self, page: &mut Page) {} + + fn render_body(&self, page: &mut Page) -> Markup { + html! { + body id=[page.body_id().get()] class=[page.body_classes().get()] { + (page.context().render_region("content")) + } + } } - /// Contenido predeterminado para la página de error "*404 - Not Found*". - /// - /// Los temas pueden sobrescribir este método para personalizar el diseño y el contenido de la - /// página de error, manteniendo o no el mensaje de los textos localizados. - fn error404(&self, page: &mut Page) -> Markup { - html! { div { h1 { (L10n::l("error404_notice").using(page)) } } } - } + #[allow(unused_variables)] + fn after_render_body(&self, page: &mut Page) {} } diff --git a/src/core/theme/regions.rs b/src/core/theme/regions.rs index 259417eb..7e8128c5 100644 --- a/src/core/theme/regions.rs +++ b/src/core/theme/regions.rs @@ -1,111 +1,78 @@ -use crate::base::component::Region; -use crate::core::component::{Child, ChildOp, Children}; +use crate::core::component::{ChildComponent, ChildOp, Children}; use crate::core::theme::ThemeRef; -use crate::{builder_fn, AutoDefault, UniqueId}; - -use parking_lot::RwLock; +use crate::{fn_builder, AutoDefault, TypeId}; use std::collections::HashMap; -use std::sync::LazyLock; +use std::sync::{LazyLock, RwLock}; -// Conjunto de regiones globales asociadas a un tema específico. -static THEME_REGIONS: LazyLock<RwLock<HashMap<UniqueId, ChildrenInRegions>>> = +static THEME_REGIONS: LazyLock<RwLock<HashMap<TypeId, ChildrenInRegions>>> = LazyLock::new(|| RwLock::new(HashMap::new())); -// Conjunto de regiones globales comunes a todos los temas. static COMMON_REGIONS: LazyLock<RwLock<ChildrenInRegions>> = LazyLock::new(|| RwLock::new(ChildrenInRegions::default())); -// Contenedor interno de componentes agrupados por región. #[derive(AutoDefault)] -pub(crate) struct ChildrenInRegions(HashMap<String, Children>); +pub struct ChildrenInRegions(HashMap<&'static str, Children>); impl ChildrenInRegions { - pub fn with(region_name: impl AsRef<str>, child: Child) -> Self { - Self::default().with_child_in(region_name, ChildOp::Add(child)) + pub fn new() -> Self { + ChildrenInRegions::default() } - #[builder_fn] - pub fn with_child_in(mut self, region_name: impl AsRef<str>, op: ChildOp) -> Self { - let name = region_name.as_ref(); - if let Some(region) = self.0.get_mut(name) { - region.alter_child(op); + pub fn with(region: &'static str, child: ChildComponent) -> Self { + ChildrenInRegions::default().with_in_region(region, ChildOp::Add(child)) + } + + #[fn_builder] + pub fn set_in_region(&mut self, region: &'static str, op: ChildOp) -> &mut Self { + if let Some(region) = self.0.get_mut(region) { + region.set_value(op); } else { - self.0 - .insert(name.to_owned(), Children::new().with_child(op)); + self.0.insert(region, Children::new().with_value(op)); } self } - pub fn children_for(&self, theme_ref: ThemeRef, region_name: impl AsRef<str>) -> Children { - let name = region_name.as_ref(); - let common = COMMON_REGIONS.read(); - let themed = THEME_REGIONS.read(); - - if let Some(r) = themed.get(&theme_ref.type_id()) { - Children::merge(&[common.0.get(name), self.0.get(name), r.0.get(name)]) + pub fn all_in_region(&self, theme: ThemeRef, region: &str) -> Children { + let common = COMMON_REGIONS.read().unwrap(); + if let Some(r) = THEME_REGIONS.read().unwrap().get(&theme.type_id()) { + Children::merge(&[common.0.get(region), self.0.get(region), r.0.get(region)]) } else { - Children::merge(&[common.0.get(name), self.0.get(name)]) + Children::merge(&[common.0.get(region), self.0.get(region)]) } } } -/// Permite añadir componentes a regiones globales o específicas de un tema. -/// -/// Según la variante, se pueden añadir componentes ([`add()`](Self::add)) que permanecerán -/// disponibles durante toda la ejecución. -/// -/// Estos componentes se renderizarán automáticamente al procesar los documentos HTML que incluyen -/// estas regiones, como las páginas de contenido ([`Page`](crate::response::page::Page)). pub enum InRegion { - /// Región de contenido por defecto. - Default, - /// Región identificada por el nombre proporcionado. + Content, Named(&'static str), - /// Región identificada por su nombre para un tema concreto. OfTheme(&'static str, ThemeRef), } impl InRegion { - /// Añade un componente a la región indicada por la variante. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// // Banner global, en la región por defecto de cualquier página. - /// InRegion::Default.add(Child::with(Html::with(|_| - /// html! { ("🎉 ¡Bienvenido!") } - /// ))); - /// - /// // Texto en la región "sidebar". - /// InRegion::Named("sidebar").add(Child::with(Html::with(|_| - /// html! { ("Publicidad") } - /// ))); - /// ``` - pub fn add(&self, child: Child) -> &Self { + pub fn add(&self, child: ChildComponent) -> &Self { match self { - InRegion::Default => Self::add_to_common(Region::DEFAULT, child), - InRegion::Named(region_name) => Self::add_to_common(region_name, child), - InRegion::OfTheme(region_name, theme_ref) => { - let mut regions = THEME_REGIONS.write(); - if let Some(r) = regions.get_mut(&theme_ref.type_id()) { - r.alter_child_in(region_name, ChildOp::Add(child)); + InRegion::Content => { + COMMON_REGIONS + .write() + .unwrap() + .set_in_region("content", ChildOp::Add(child)); + } + InRegion::Named(name) => { + COMMON_REGIONS + .write() + .unwrap() + .set_in_region(name, ChildOp::Add(child)); + } + InRegion::OfTheme(region, theme) => { + let mut regions = THEME_REGIONS.write().unwrap(); + if let Some(r) = regions.get_mut(&theme.type_id()) { + r.set_in_region(region, ChildOp::Add(child)); } else { - regions.insert( - theme_ref.type_id(), - ChildrenInRegions::with(region_name, child), - ); + regions.insert(theme.type_id(), ChildrenInRegions::with(region, child)); } } } self } - - #[inline] - fn add_to_common(region_name: &str, child: Child) { - COMMON_REGIONS - .write() - .alter_child_in(region_name, ChildOp::Add(child)); - } } diff --git a/src/datetime.rs b/src/datetime.rs index 2e8b6229..732431df 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,4 +1,4 @@ -//! Soporte a fechas y horas según estándar [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) -//! (basado en [chrono](https://docs.rs/chrono)). +//! [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) date and time handling +//! ([chrono](https://docs.rs/chrono)). pub use chrono::prelude::*; diff --git a/src/global.rs b/src/global.rs index ee07d818..e538958b 100644 --- a/src/global.rs +++ b/src/global.rs @@ -1,4 +1,4 @@ -//! Opciones de configuración globales. +//! Global settings. use crate::include_config; @@ -6,17 +6,17 @@ use serde::Deserialize; include_config!(SETTINGS: Settings => [ // [app] - "app.name" => "PageTop App", + "app.name" => "My App", "app.description" => "Developed with the amazing PageTop framework.", - "app.theme" => "Basic", - "app.language" => "", + "app.theme" => "", + "app.language" => "en-US", + "app.text_direction" => "ltr", "app.startup_banner" => "Slant", // [dev] - "dev.pagetop_static_dir" => "", + "dev.pagetop_project_dir" => "", // [log] - "log.enabled" => true, "log.tracing" => "Info", "log.rolling" => "Stdout", "log.path" => "log", @@ -25,13 +25,13 @@ include_config!(SETTINGS: Settings => [ // [server] "server.bind_address" => "localhost", - "server.bind_port" => 8080, + "server.bind_port" => 8088, "server.session_lifetime" => 604_800, ]); #[derive(Debug, Deserialize)] -/// Tipos para las secciones globales [`[app]`](App), [`[dev]`](Dev), [`[log]`](Log) y -/// [`[server]`](Server) de [`SETTINGS`]. +/// Configuration settings for the global [`[app]`](App), [`[dev]`](Dev), [`[log]`](Log), and +/// [`[server]`](Server) sections (see [`SETTINGS`]). pub struct Settings { pub app: App, pub dev: Dev, @@ -40,73 +40,82 @@ pub struct Settings { } #[derive(Debug, Deserialize)] -/// Sección `[app]` de la configuración. Forma parte de [`Settings`]. +/// Section `[app]` of the configuration settings. +/// +/// See [`Settings`]. pub struct App { - /// Nombre de la aplicación. + /// The name of the application. + /// Default: *"My App"*. pub name: String, - /// Breve descripción de la aplicación. + /// A brief description of the application. + /// Default: *"Developed with the amazing PageTop framework."*. pub description: String, - /// Tema predeterminado. + /// Default theme. + /// Default: *""*. pub theme: String, - /// Idioma por defecto para la aplicación. - /// - /// Si no está definido o no es válido, [`LangId`](crate::locale::LangId) determinará el idioma - /// efectivo para el renderizado en este orden: primero intentará usar el establecido mediante - /// [`Contextual::with_langid()`](crate::core::component::Contextual::with_langid); si no se ha - /// definido explícitamente, probará el indicado en la cabecera `Accept-Language` del navegador; - /// y, si ninguno aplica, se empleará el idioma de respaldo ("en-US"). + /// Default language (localization). + /// Default: *"en-US"*. pub language: String, - /// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o - /// *"Starwars"*. + /// Default text direction: *"ltr"* (left-to-right), *"rtl"* (right-to-left), or *"auto"*. + /// Default: *"ltr"*. + pub text_direction: String, + /// ASCII banner printed at startup: *"Off"*, *"Slant"*, *"Small"*, *"Speed"*, or *"Starwars"*. + /// Default: *"Slant"*. pub startup_banner: String, - /// Modo de ejecución, dado por la variable de entorno `PAGETOP_RUN_MODE`, o *"default"* si no - /// está definido. + /// Default: according to the `PAGETOP_RUN_MODE` environment variable, or *"default"* if unset. pub run_mode: String, } #[derive(Debug, Deserialize)] -/// Sección `[Dev]` de la configuración. Forma parte de [`Settings`]. +/// Section `[dev]` of the configuration settings. +/// +/// See [`Settings`]. pub struct Dev { - /// Directorio desde el que servir los archivos estáticos de PageTop. - /// - /// Por defecto, los archivos se integran en el binario de la aplicación. Si aquí se indica una - /// ruta válida, ya sea absoluta o relativa al directorio del proyecto o del binario en - /// ejecución, se servirán desde el sistema de ficheros en su lugar. Esto es especialmente útil - /// en desarrollo, ya que evita recompilar el proyecto por cambios en estos archivos. - /// - /// Si la cadena está vacía, se ignora este ajuste. - pub pagetop_static_dir: String, + /// Static files required by the application are integrated by default into the executable + /// binary. However, during development, it can be useful to serve these files from their own + /// directory to avoid recompilation every time they are modified. In this case, specify the + /// full path to the project's root directory. + /// Default: *""*. + pub pagetop_project_dir: String, } #[derive(Debug, Deserialize)] -/// Sección `[log]` de la configuración. Forma parte de [`Settings`]. +/// Section `[log]` of the configuration settings. +/// +/// See [`Settings`]. pub struct Log { - /// Gestión de trazas y registro de eventos activado (`true`) o desactivado (`false`). - pub enabled: bool, - /// Opciones, o combinación de opciones separadas por comas, para filtrar las trazas: *"Error"*, - /// *"Warn"*, *"Info"*, *"Debug"* o *"Trace"*. - /// Ejemplo: "Error,actix_server::builder=Info,tracing_actix_web=Debug". + /// Filter, or a comma-separated combination of filters, for execution traces: *"Error"*, + /// *"Warn"*, *"Info"*, *"Debug"*, or *"Trace"*. + /// Example: "Error,actix_server::builder=Info,tracing_actix_web=Debug". + /// Default: *"Info"*. pub tracing: String, - /// Muestra los mensajes de traza en el terminal (*"Stdout"*) o las registra en archivos con - /// rotación: *"Daily"*, *"Hourly"*, *"Minutely"* o *"Endless"*. + /// Displays traces in the terminal (*"Stdout"*) or logs them in files with rotation: *"Daily"*, + /// *"Hourly"*, *"Minutely"*, or *"Endless"*. + /// Default: *"Stdout"*. pub rolling: String, - /// Directorio para los archivos de traza (si `rolling` ≠ *"Stdout"*). + /// Directory for trace files (if `rolling` != *"Stdout"*). + /// Default: *"log"*. pub path: String, - /// Prefijo para los archivos de traza (si `rolling` ≠ *"Stdout"*). + /// Prefix for trace files (if `rolling` != *"Stdout"*). + /// Default: *"tracing.log"*. pub prefix: String, - /// Formato de salida de las trazas. Opciones: *"Full"*, *"Compact"*, *"Pretty"* o *"Json"*. + /// Trace output format. Options are *"Full"*, *"Compact"*, *"Pretty"*, or *"Json"*. + /// Default: *"Full"*. pub format: String, } #[derive(Debug, Deserialize)] -/// Sección `[server]` de la configuración. Forma parte de [`Settings`]. +/// Section `[server]` of the configuration settings. +/// +/// See [`Settings`]. pub struct Server { - /// Dirección de enlace para el servidor web. + /// Web server bind address. + /// Default: *"localhost"*. pub bind_address: String, - /// Puerto de escucha del servidor web. + /// Web server bind port. + /// Default: *8088*. pub bind_port: u16, - /// Duración de la cookie de sesión en segundos (p. ej., `604_800` para una semana). - /// - /// El valor `0` indica que la cookie permanecerá activa hasta que se cierre el navegador. + /// Session cookie duration in seconds (0 means "until the browser is closed"). + /// Default: *604800* (7 days). pub session_lifetime: i64, } diff --git a/src/html.rs b/src/html.rs index 82fdcd73..939316b7 100644 --- a/src/html.rs +++ b/src/html.rs @@ -1,168 +1,51 @@ -//! HTML en código. +//! HTML in code. mod maud; -pub use maud::{display, html, html_private, Escaper, Markup, PreEscaped, DOCTYPE}; - -// **< HTML DOCUMENT ASSETS >*********************************************************************** +pub use maud::{html, html_private, Markup, PreEscaped, DOCTYPE}; mod assets; pub use assets::favicon::Favicon; pub use assets::javascript::JavaScript; pub use assets::stylesheet::{StyleSheet, TargetMedia}; -pub use assets::{Asset, Assets}; +pub(crate) use assets::Assets; -mod logo; -pub use logo::PageTopSvg; +mod opt_id; +pub use opt_id::OptionId; -// **< HTML DOCUMENT CONTEXT >********************************************************************** +mod opt_name; +pub use opt_name::OptionName; -/// **Obsoleto desde la versión 0.5.0**: usar [`core::component::Context`] en su lugar. -#[deprecated(since = "0.5.0", note = "Moved to `pagetop::core::component::Context`")] -pub type Context = crate::core::component::Context; +mod opt_string; +pub use opt_string::OptionString; -/// **Obsoleto desde la versión 0.5.0**: usar [`core::component::ContextOp`] en su lugar. -#[deprecated( - since = "0.5.0", - note = "Moved to `pagetop::core::component::ContextOp`" -)] -pub type ContextOp = crate::core::component::ContextOp; +mod opt_translated; +pub use opt_translated::OptionTranslated; -/// **Obsoleto desde la versión 0.5.0**: usar [`core::component::Contextual`] en su lugar. -#[deprecated( - since = "0.5.0", - note = "Moved to `pagetop::core::component::Contextual`" -)] -pub trait Contextual: crate::core::component::Contextual {} +mod opt_classes; +pub use opt_classes::{ClassesOp, OptionClasses}; -/// **Obsoleto desde la versión 0.5.0**: usar [`core::component::ContextError`] en su lugar. -#[deprecated( - since = "0.5.0", - note = "Moved to `pagetop::core::component::ContextError`" -)] -pub type ContextError = crate::core::component::ContextError; +mod opt_component; +pub use opt_component::OptionComponent; -/// **Obsoleto desde la versión 0.5.0**: usar [`ContextOp`] en su lugar. -#[deprecated(since = "0.5.0", note = "Use `ContextOp` instead")] -pub type AssetsOp = crate::core::component::ContextOp; +pub mod unit; -// **< HTML ATTRIBUTES >**************************************************************************** +use crate::AutoDefault; -mod attr_id; -pub use attr_id::AttrId; -/// **Obsoleto desde la versión 0.4.0**: usar [`AttrId`] en su lugar. -#[deprecated(since = "0.4.0", note = "Use `AttrId` instead")] -pub type OptionId = AttrId; - -mod attr_name; -pub use attr_name::AttrName; -/// **Obsoleto desde la versión 0.4.0**: usar [`AttrName`] en su lugar. -#[deprecated(since = "0.4.0", note = "Use `AttrName` instead")] -pub type OptionName = AttrName; - -mod attr_value; -pub use attr_value::AttrValue; -/// **Obsoleto desde la versión 0.4.0**: usar [`AttrValue`] en su lugar. -#[deprecated(since = "0.4.0", note = "Use `AttrValue` instead")] -pub type OptionString = AttrValue; - -mod attr_l10n; -pub use attr_l10n::AttrL10n; -/// **Obsoleto desde la versión 0.4.0**: usar [`AttrL10n`] en su lugar. -#[deprecated(since = "0.4.0", note = "Use `AttrL10n` instead")] -pub type OptionTranslated = AttrL10n; - -mod attr_classes; -pub use attr_classes::{AttrClasses, ClassesOp}; -/// **Obsoleto desde la versión 0.4.0**: usar [`AttrClasses`] en su lugar. -#[deprecated(since = "0.4.0", note = "Use `AttrClasses` instead")] -pub type OptionClasses = AttrClasses; - -use crate::{core, AutoDefault}; - -/// **Obsoleto desde la versión 0.4.0**: usar [`Typed`](crate::core::component::Typed) en su lugar. -#[deprecated( - since = "0.4.0", - note = "Use `pagetop::core::component::Typed` instead" -)] -#[allow(type_alias_bounds)] -pub type OptionComponent<C: core::component::Component> = core::component::Typed<C>; - -mod unit; -pub use unit::UnitValue; - -// **< HTML PrepareMarkup >************************************************************************* - -/// Prepara contenido HTML para su conversión a [`Markup`]. -/// -/// Este tipo encapsula distintos orígenes de contenido HTML (texto plano, HTML sin escapar o -/// fragmentos ya procesados) para renderizarlos de forma homogénea en plantillas, sin interferir -/// con el uso estándar de [`Markup`]. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Texto normal, se escapa automáticamente para evitar inyección de HTML. -/// let fragment = PrepareMarkup::Escaped("Hola <b>mundo</b>".to_string()); -/// assert_eq!(fragment.into_string(), "Hola &lt;b&gt;mundo&lt;/b&gt;"); -/// -/// // HTML literal, se inserta directamente, sin escapado adicional. -/// let raw_html = PrepareMarkup::Raw("<b>negrita</b>".to_string()); -/// assert_eq!(raw_html.into_string(), "<b>negrita</b>"); -/// -/// // Fragmento ya preparado con la macro `html!`. -/// let prepared = PrepareMarkup::With(html! { -/// h2 { "Título de ejemplo" } -/// p { "Este es un párrafo con contenido dinámico." } -/// }); -/// assert_eq!( -/// prepared.into_string(), -/// "<h2>Título de ejemplo</h2><p>Este es un párrafo con contenido dinámico.</p>" -/// ); -/// ``` -#[derive(AutoDefault, Clone)] +#[derive(AutoDefault)] pub enum PrepareMarkup { - /// No se genera contenido HTML (equivale a `html! {}`). #[default] None, - /// Texto plano que se **escapará automáticamente** para que no sea interpretado como HTML. - /// - /// Úsalo con textos que provengan de usuarios u otras fuentes externas para garantizar la - /// seguridad contra inyección de código. + Text(&'static str), Escaped(String), - /// HTML literal que se inserta **sin escapado adicional**. - /// - /// Úsalo únicamente para contenido generado de forma confiable o controlada, ya que cualquier - /// etiqueta o script incluido será renderizado directamente en el documento. - Raw(String), - /// Fragmento HTML ya preparado como [`Markup`], listo para insertarse directamente. - /// - /// Normalmente proviene de expresiones `html! { ... }`. With(Markup), } impl PrepareMarkup { - /// Devuelve `true` si el contenido está vacío y no generará HTML al renderizar. - pub fn is_empty(&self) -> bool { - match self { - PrepareMarkup::None => true, - PrepareMarkup::Escaped(text) => text.is_empty(), - PrepareMarkup::Raw(string) => string.is_empty(), - PrepareMarkup::With(markup) => markup.is_empty(), - } - } - - /// Convierte el contenido en una cadena HTML renderizada. Usar sólo para pruebas o depuración. - pub fn into_string(&self) -> String { - self.render().into_string() - } - - // Integra el renderizado fácilmente en la macro [`html!`]. - pub(crate) fn render(&self) -> Markup { + pub fn render(&self) -> Markup { match self { PrepareMarkup::None => html! {}, - PrepareMarkup::Escaped(text) => html! { (text) }, - PrepareMarkup::Raw(string) => html! { (PreEscaped(string)) }, + PrepareMarkup::Text(text) => html! { (text) }, + PrepareMarkup::Escaped(string) => html! { (PreEscaped(string)) }, PrepareMarkup::With(markup) => html! { (markup) }, } } diff --git a/src/html/assets.rs b/src/html/assets.rs index fe5f5b7c..c8b8f1b9 100644 --- a/src/html/assets.rs +++ b/src/html/assets.rs @@ -2,92 +2,51 @@ pub mod favicon; pub mod javascript; pub mod stylesheet; -use crate::core::component::Context; use crate::html::{html, Markup}; use crate::{AutoDefault, Weight}; -/// Representación genérica de un script [`JavaScript`](crate::html::JavaScript) o una hoja de -/// estilos [`StyleSheet`](crate::html::StyleSheet). -/// -/// Estos recursos se incluyen en los conjuntos de recursos ([`Assets`]) que suelen renderizarse en -/// un documento HTML. -/// -/// Cada recurso se identifica por un **nombre único** ([`Asset::name()`]), usado como clave; y un -/// **peso** ([`Asset::weight()`]), que determina su orden relativo de renderizado. -pub trait Asset { - /// Devuelve el nombre del recurso, utilizado como clave única. - fn name(&self) -> &str; +pub trait AssetsTrait { + fn name(&self) -> &String; - /// Devuelve el peso del recurso, usado para ordenar el renderizado de menor a mayor peso. fn weight(&self) -> Weight; - /// Renderiza el recurso en el contexto proporcionado. - fn render(&self, cx: &mut Context) -> Markup; + fn render(&self) -> Markup; } -/// Gestión común para conjuntos de recursos como [`JavaScript`](crate::html::JavaScript) y -/// [`StyleSheet`](crate::html::StyleSheet). -/// -/// Se emplea normalmente para agrupar, administrar y renderizar los recursos de un documento HTML. -/// Cada recurso se identifica por un nombre único ([`Asset::name()`]) y tiene asociado un peso -/// ([`Asset::weight()`]) que determina su orden de renderizado. -/// -/// Durante el renderizado, los recursos se procesan en orden ascendente de peso. En caso de -/// igualdad, se respeta el orden de inserción. #[derive(AutoDefault)] -pub struct Assets<T>(Vec<T>); +pub(crate) struct Assets<T>(Vec<T>); -impl<T: Asset> Assets<T> { - /// Crea un nuevo conjunto vacío de recursos. - /// - /// Normalmente no se instancia directamente, sino como parte de la gestión de recursos que - /// hacen páginas o temas. +impl<T: AssetsTrait> Assets<T> { pub fn new() -> Self { - Self(Vec::new()) + Assets::<T>(Vec::<T>::new()) } - /// Inserta un recurso. - /// - /// Si no existe otro con el mismo nombre, lo añade. Si ya existe y su peso era mayor, lo - /// reemplaza. Y si su peso era menor o igual, entonces no realiza ningún cambio. - /// - /// Devuelve `true` si el recurso fue insertado o reemplazado. - pub fn add(&mut self, asset: T) -> bool { + pub fn add(&mut self, asset: T) -> &mut Self { match self.0.iter().position(|x| x.name() == asset.name()) { Some(index) => { if self.0[index].weight() > asset.weight() { self.0.remove(index); self.0.push(asset); - true - } else { - false } } - _ => { - self.0.push(asset); - true - } - } + _ => self.0.push(asset), + }; + self } - /// Elimina un recurso por nombre. - /// - /// Devuelve `true` si el recurso existía y fue eliminado. - pub fn remove(&mut self, name: impl AsRef<str>) -> bool { - if let Some(index) = self.0.iter().position(|x| x.name() == name.as_ref()) { + pub fn remove(&mut self, name: &'static str) -> &mut Self { + if let Some(index) = self.0.iter().position(|x| x.name() == name) { self.0.remove(index); - true - } else { - false - } + }; + self } - pub fn render(&self, cx: &mut Context) -> Markup { - let mut assets = self.0.iter().collect::<Vec<_>>(); - assets.sort_by_key(|a| a.weight()); + pub fn render(&mut self) -> Markup { + let assets = &mut self.0; + assets.sort_by_key(AssetsTrait::weight); html! { @for a in assets { - (a.render(cx)) + (a.render()) } } } diff --git a/src/html/assets/favicon.rs b/src/html/assets/favicon.rs index 7598603c..366da4ff 100644 --- a/src/html/assets/favicon.rs +++ b/src/html/assets/favicon.rs @@ -1,142 +1,71 @@ -use crate::core::component::Context; use crate::html::{html, Markup}; use crate::AutoDefault; -/// Un **Favicon** es un recurso gráfico que usa el navegador como icono asociado al sitio. -/// -/// Es universalmente aceptado para mostrar el icono del sitio (`.ico`, `.png`, `.svg`, ...) en -/// pestañas, marcadores o accesos directos. -/// -/// Este tipo permite construir de forma fluida las distintas variantes de un *favicon*, ya sea un -/// icono estándar, un icono Apple para la pantalla de inicio, o un icono para Safari con color. -/// También puede aplicar colores al tema o configuraciones específicas para *tiles* de Windows. -/// -/// > **Nota** -/// > Los archivos de los iconos deben estar disponibles en el servidor web de la aplicación. Pueden -/// > servirse usando [`static_files_service!`](crate::static_files_service). -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let favicon = Favicon::new() -/// // Estándar de facto admitido por todos los navegadores. -/// .with_icon("/icons/favicon.ico") -/// -/// // Variante del favicon con tamaños explícitos: 32×32 y 16×16. -/// .with_icon_for_sizes("/icons/favicon-32.png", "32x32") -/// .with_icon_for_sizes("/icons/favicon-16.png", "16x16") -/// -/// // Icono específico para accesos directos en la pantalla de inicio de iOS. -/// .with_apple_touch_icon("/icons/apple-touch-icon.png", "180x180") -/// -/// // Icono vectorial con color dinámico para pestañas ancladas en Safari. -/// .with_mask_icon("/icons/safari-pinned-tab.svg", "#5bbad5") -/// -/// // Personaliza la barra superior del navegador en Android Chrome (y soportado en otros). -/// .with_theme_color("#ffffff") -/// -/// // Personalizaciones específicas para "tiles" en Windows. -/// .with_ms_tile_color("#da532c") -/// .with_ms_tile_image("/icons/mstile-144x144.png"); -/// ``` #[derive(AutoDefault)] pub struct Favicon(Vec<Markup>); impl Favicon { - /// Crea un nuevo `Favicon` vacío. - /// - /// Equivalente a `Favicon::default()`. Se recomienda iniciar la secuencia de configuración - /// desde aquí. pub fn new() -> Self { Favicon::default() } - // **< Favicon BUILDER >************************************************************************ + // Favicon BUILDER. - /// Le añade un icono genérico apuntando a `image`. El tipo MIME se infiere automáticamente a - /// partir de la extensión. - pub fn with_icon(self, image: impl Into<String>) -> Self { - self.add_icon_item("icon", image.into(), None, None) + pub fn with_icon(self, image: &str) -> Self { + self.add_icon_item("icon", image, None, None) } - /// Le añade un icono genérico con atributo `sizes`, útil para indicar resoluciones específicas. - /// - /// El atributo `sizes` informa al navegador de las dimensiones de la imagen para que seleccione - /// el recurso más adecuado. Puede enumerar varias dimensiones separadas por espacios, p. ej. - /// `"16x16 32x32 48x48"` o usar `any` para iconos escalables (SVG). - /// - /// No es imprescindible, pero puede mejorar la selección del icono más adecuado. - pub fn with_icon_for_sizes(self, image: impl Into<String>, sizes: impl Into<String>) -> Self { - self.add_icon_item("icon", image.into(), Some(sizes.into()), None) + pub fn with_icon_for_sizes(self, image: &str, sizes: &str) -> Self { + self.add_icon_item("icon", image, Some(sizes), None) } - /// Le añade un *Apple Touch Icon*, usado por dispositivos iOS para las pantallas de inicio. - /// - /// Se recomienda indicar también el tamaño, p. ej. `"256x256"`. - pub fn with_apple_touch_icon(self, image: impl Into<String>, sizes: impl Into<String>) -> Self { - self.add_icon_item("apple-touch-icon", image.into(), Some(sizes.into()), None) + pub fn with_apple_touch_icon(self, image: &str, sizes: &str) -> Self { + self.add_icon_item("apple-touch-icon", image, Some(sizes), None) } - /// Le añade un icono para el navegador Safari, con un color dinámico. - /// - /// El atributo `color` lo usa Safari para colorear el trazado SVG cuando el icono se muestra en - /// modo *Pinned Tab*. Aunque Safari 12+ acepta *favicons normales*, este método garantiza - /// compatibilidad con versiones anteriores. - pub fn with_mask_icon(self, image: impl Into<String>, color: impl Into<String>) -> Self { - self.add_icon_item("mask-icon", image.into(), None, Some(color.into())) + pub fn with_mask_icon(self, image: &str, color: &str) -> Self { + self.add_icon_item("mask-icon", image, None, Some(color)) } - /// Define el color del tema (`<meta name="theme-color">`). - /// - /// Lo usan algunos navegadores para colorear la barra de direcciones o interfaces. - pub fn with_theme_color(mut self, color: impl Into<String>) -> Self { + pub fn with_manifest(self, file: &str) -> Self { + self.add_icon_item("manifest", file, None, None) + } + + pub fn with_theme_color(mut self, color: &str) -> Self { self.0.push(html! { - meta name="theme-color" content=(color.into()); + meta name="theme-color" content=(color); }); self } - /// Define el color del *tile* en Windows (`<meta name="msapplication-TileColor">`). - pub fn with_ms_tile_color(mut self, color: impl Into<String>) -> Self { + pub fn with_ms_tile_color(mut self, color: &str) -> Self { self.0.push(html! { - meta name="msapplication-TileColor" content=(color.into()); + meta name="msapplication-TileColor" content=(color); }); self } - /// Define la imagen del *tile* en Windows (`<meta name="msapplication-TileImage">`). - pub fn with_ms_tile_image(mut self, image: impl Into<String>) -> Self { + pub fn with_ms_tile_image(mut self, image: &str) -> Self { self.0.push(html! { - meta name="msapplication-TileImage" content=(image.into()); + meta name="msapplication-TileImage" content=(image); }); self } - // Función interna que centraliza la creación de las etiquetas `<link>`. - // - // - `icon_rel`: indica el tipo de recurso (`"icon"`, `"apple-touch-icon"`, etc.). - // - `icon_source`: URL del recurso. - // - `icon_sizes`: tamaños opcionales. - // - `icon_color`: color opcional (solo relevante para `mask-icon`). - // - // También infiere automáticamente el tipo MIME (`type`) según la extensión del archivo. fn add_icon_item( mut self, icon_rel: &str, - icon_source: String, - icon_sizes: Option<String>, - icon_color: Option<String>, + icon_source: &str, + icon_sizes: Option<&str>, + icon_color: Option<&str>, ) -> Self { let icon_type = match icon_source.rfind('.') { - Some(i) => match icon_source[i..].to_string().to_lowercase().as_str() { - ".avif" => Some("image/avif"), + Some(i) => match icon_source[i..].to_owned().to_lowercase().as_str() { ".gif" => Some("image/gif"), ".ico" => Some("image/x-icon"), - ".jpg" | ".jpeg" => Some("image/jpeg"), + ".jpg" => Some("image/jpg"), ".png" => Some("image/png"), ".svg" => Some("image/svg+xml"), - ".webp" => Some("image/webp"), _ => None, }, _ => None, @@ -152,13 +81,9 @@ impl Favicon { self } - // **< Favicon RENDER >************************************************************************* + // Favicon PREPARE. - /// Renderiza el **Favicon** completo con todas las etiquetas declaradas. - /// - /// El parámetro `Context` se acepta por coherencia con el resto de *assets*, aunque en este - /// caso es ignorado. - pub fn render(&self, _cx: &mut Context) -> Markup { + pub(crate) fn render(&self) -> Markup { html! { @for item in &self.0 { (item) diff --git a/src/html/assets/javascript.rs b/src/html/assets/javascript.rs index 6394842a..76d84673 100644 --- a/src/html/assets/javascript.rs +++ b/src/html/assets/javascript.rs @@ -1,208 +1,34 @@ -use crate::core::component::Context; -use crate::html::assets::Asset; -use crate::html::{html, Markup, PreEscaped}; -use crate::{join, join_pair, AutoDefault, Weight}; +use crate::html::assets::AssetsTrait; +use crate::html::{html, Markup}; +use crate::{concat_string, AutoDefault, Weight}; -// Define el origen del recurso JavaScript y cómo debe cargarse en el navegador. -// -// Los distintos modos de carga permiten optimizar el rendimiento y controlar el comportamiento del -// script en relación con el análisis del documento HTML y la ejecución del resto de scripts. -// -// - [`From`] - Carga estándar con la etiqueta `<script src="...">`. -// - [`Defer`] - Igual que [`From`], pero con el atributo `defer`, descarga en paralelo y se -// ejecuta tras el análisis del documento HTML, respetando el orden de -// aparición. -// - [`Async`] - Igual que [`From`], pero con el atributo `async`, descarga en paralelo y se -// ejecuta en cuanto esté listo, **sin garantizar** el orden relativo respecto a -// otros scripts. -// - [`Inline`] - Inserta el código directamente en la etiqueta `<script>`. -// - [`OnLoad`] - Inserta el código JavaScript y lo ejecuta tras el evento `DOMContentLoaded`. -// - [`OnLoadAsync`] - Igual que [`OnLoad`], pero con manejador asíncrono (`async`), útil si dentro -// del código JavaScript se utiliza `await`. #[derive(AutoDefault)] enum Source { #[default] From(String), Defer(String), Async(String), - // `name`, `closure(Context) -> String`. - Inline(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>), - // `name`, `closure(Context) -> String` (se ejecuta tras `DOMContentLoaded`). - OnLoad(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>), - // `name`, `closure(Context) -> String` (manejador `async` tras `DOMContentLoaded`). - OnLoadAsync(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>), + Inline(String, String), + OnLoad(String, String), } -/// Define un recurso **JavaScript** para incluir en un documento HTML. -/// -/// Este tipo permite añadir scripts externos o embebidos con distintas estrategias de carga -/// (`defer`, `async`, *inline*, etc.) y [pesos](crate::Weight) para controlar el orden de inserción -/// en el documento. -/// -/// > **Nota** -/// > Los archivos de los scripts deben estar disponibles en el servidor web de la aplicación. -/// > Pueden servirse usando [`static_files_service!`](crate::static_files_service). -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Script externo con carga diferida, versión de caché y prioridad en el renderizado. -/// let script = JavaScript::defer("/assets/js/app.js") -/// .with_version("1.2.3") -/// .with_weight(-10); -/// -/// // Script embebido que se ejecuta tras la carga del documento. -/// let script = JavaScript::on_load("init_tooltips", |_| r#" -/// const tooltips = document.querySelectorAll('[data-tooltip]'); -/// for (const el of tooltips) { -/// el.addEventListener('mouseenter', showTooltip); -/// } -/// "#.to_string()); -/// -/// // Script embebido con manejador asíncrono (`async`) que puede usar `await`. -/// let mut cx = Context::new(None).with_param("user_id", 7u32); -/// -/// let js = JavaScript::on_load_async("hydrate", |cx| { -/// // Ejemplo: lectura de un parámetro del contexto para inyectarlo en el código. -/// let uid: u32 = cx.param_or_default("user_id"); -/// format!(r#" -/// const USER_ID = {}; -/// await Promise.resolve(USER_ID); -/// // Aquí se podría hidratar la interfaz o cargar módulos dinámicos: -/// // await import('/assets/js/hydrate.js'); -/// "#, uid) -/// }); -/// ``` #[rustfmt::skip] #[derive(AutoDefault)] pub struct JavaScript { - source : Source, // Fuente y estrategia de carga del script. - version: String, // Versión del recurso para la caché del navegador. - weight : Weight, // Peso que determina el orden. + source : Source, + prefix : &'static str, + version: &'static str, + weight : Weight, } -impl JavaScript { - /// Crea un **script externo** que se carga y ejecuta de forma inmediata, en orden con el resto - /// del documento HTML. - /// - /// Equivale a `<script src="...">`. - pub fn from(path: impl Into<String>) -> Self { - JavaScript { - source: Source::From(path.into()), - ..Default::default() - } - } - - /// Crea un **script externo** con el atributo `defer`, que se descarga en paralelo y se ejecuta - /// tras analizar completamente el documento HTML, **respetando el orden** de inserción. - /// - /// Equivale a `<script src="..." defer>`. Suele ser la opción recomendada para scripts no - /// críticos. - pub fn defer(path: impl Into<String>) -> Self { - JavaScript { - source: Source::Defer(path.into()), - ..Default::default() - } - } - - /// Crea un **script externo** con el atributo `async`, que se descarga en paralelo y se ejecuta - /// tan pronto como esté disponible. - /// - /// Equivale a `<script src="..." async>`. **No garantiza** el orden relativo con otros scripts. - pub fn asynchronous(path: impl Into<String>) -> Self { - JavaScript { - source: Source::Async(path.into()), - ..Default::default() - } - } - - /// Crea un **script embebido** directamente en el documento HTML. - /// - /// Equivale a `<script>...</script>`. El parámetro `name` se usa como identificador interno del - /// script. - /// - /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado. - pub fn inline<F>(name: impl Into<String>, f: F) -> Self - where - F: Fn(&mut Context) -> String + Send + Sync + 'static, - { - JavaScript { - source: Source::Inline(name.into(), Box::new(f)), - ..Default::default() - } - } - - /// Crea un **script embebido** que se ejecuta cuando **el DOM está listo**. - /// - /// El código se envuelve en un `addEventListener('DOMContentLoaded',function(){...})` que lo - /// ejecuta tras analizar el documento HTML, **no** espera imágenes ni otros recursos externos. - /// Útil para inicializaciones que no dependen de `await`. El parámetro `name` se usa como - /// identificador interno del script. - /// - /// Los scripts con `defer` se ejecutan antes de `DOMContentLoaded`. - /// - /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado. - pub fn on_load<F>(name: impl Into<String>, f: F) -> Self - where - F: Fn(&mut Context) -> String + Send + Sync + 'static, - { - JavaScript { - source: Source::OnLoad(name.into(), Box::new(f)), - ..Default::default() - } - } - - /// Crea un **script embebido** con un **manejador asíncrono**. - /// - /// El código se envuelve en un `addEventListener('DOMContentLoaded',async()=>{...})`, que - /// emplea una función `async` para que el cuerpo devuelto por la función *closure* pueda usar - /// `await`. Ideal para hidratar la interfaz, cargar módulos dinámicos o realizar lecturas - /// iniciales. - /// - /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado. - pub fn on_load_async<F>(name: impl Into<String>, f: F) -> Self - where - F: Fn(&mut Context) -> String + Send + Sync + 'static, - { - JavaScript { - source: Source::OnLoadAsync(name.into(), Box::new(f)), - ..Default::default() - } - } - - // **< JavaScript BUILDER >********************************************************************* - - /// Asocia una **versión** al recurso (usada para control de la caché del navegador). - /// - /// Si `version` está vacío, **no** se añade ningún parámetro a la URL. - pub fn with_version(mut self, version: impl Into<String>) -> Self { - self.version = version.into(); - self - } - - /// Modifica el **peso** del recurso. - /// - /// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden - /// de creación. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } -} - -impl Asset for JavaScript { - /// Devuelve el nombre del recurso, utilizado como clave única. - /// - /// Para scripts externos es la ruta del recurso; para scripts embebidos, un identificador. - fn name(&self) -> &str { +impl AssetsTrait for JavaScript { + fn name(&self) -> &String { match &self.source { Source::From(path) => path, Source::Defer(path) => path, Source::Async(path) => path, Source::Inline(name, _) => name, Source::OnLoad(name, _) => name, - Source::OnLoadAsync(name, _) => name, } } @@ -210,28 +36,76 @@ impl Asset for JavaScript { self.weight } - // **< JavaScript RENDER >********************************************************************** - - fn render(&self, cx: &mut Context) -> Markup { + fn render(&self) -> Markup { match &self.source { Source::From(path) => html! { - script src=(join_pair!(path, "?v=", &self.version)) {}; + script src=(concat_string!(path, self.prefix, self.version)) {}; }, Source::Defer(path) => html! { - script src=(join_pair!(path, "?v=", &self.version)) defer {}; + script src=(concat_string!(path, self.prefix, self.version)) defer {}; }, Source::Async(path) => html! { - script src=(join_pair!(path, "?v=", &self.version)) async {}; + script src=(concat_string!(path, self.prefix, self.version)) async {}; }, - Source::Inline(_, f) => html! { - script { (PreEscaped((f)(cx))) }; + Source::Inline(_, code) => html! { + script { (code) }; }, - Source::OnLoad(_, f) => html! { script { (PreEscaped(join!( - "document.addEventListener(\"DOMContentLoaded\",function(){", (f)(cx), "});" - ))) } }, - Source::OnLoadAsync(_, f) => html! { script { (PreEscaped(join!( - "document.addEventListener(\"DOMContentLoaded\",async()=>{", (f)(cx), "});" - ))) } }, + Source::OnLoad(_, code) => html! { (concat_string!( + "document.addEventListener('DOMContentLoaded',function(){", + code, + "});" + )) }, } } } + +impl JavaScript { + pub fn from(path: impl Into<String>) -> Self { + JavaScript { + source: Source::From(path.into()), + ..Default::default() + } + } + + pub fn defer(path: impl Into<String>) -> Self { + JavaScript { + source: Source::Defer(path.into()), + ..Default::default() + } + } + + pub fn asynchronous(path: impl Into<String>) -> Self { + JavaScript { + source: Source::Async(path.into()), + ..Default::default() + } + } + + pub fn inline(name: impl Into<String>, script: impl Into<String>) -> Self { + JavaScript { + source: Source::Inline(name.into(), script.into()), + ..Default::default() + } + } + + pub fn on_load(name: impl Into<String>, script: impl Into<String>) -> Self { + JavaScript { + source: Source::OnLoad(name.into(), script.into()), + ..Default::default() + } + } + + pub fn with_version(mut self, version: &'static str) -> Self { + (self.prefix, self.version) = if version.is_empty() { + ("", "") + } else { + ("?v=", version) + }; + self + } + + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } +} diff --git a/src/html/assets/stylesheet.rs b/src/html/assets/stylesheet.rs index abadef8a..5dd65a97 100644 --- a/src/html/assets/stylesheet.rs +++ b/src/html/assets/stylesheet.rs @@ -1,158 +1,33 @@ -use crate::core::component::Context; -use crate::html::assets::Asset; +use crate::html::assets::AssetsTrait; use crate::html::{html, Markup, PreEscaped}; -use crate::{join_pair, AutoDefault, Weight}; +use crate::{concat_string, AutoDefault, Weight}; -// Define el origen del recurso CSS y cómo se incluye en el documento. -// -// Los estilos pueden cargarse desde un archivo externo o estar embebidos directamente en una -// etiqueta `<style>`. -// -// - [`From`] - Carga la hoja de estilos desde un archivo externo, insertándola mediante una -// etiqueta `<link>` con `rel="stylesheet"`. -// - [`Inline`] - Inserta directamente el contenido CSS dentro de una etiqueta `<style>`. #[derive(AutoDefault)] enum Source { #[default] From(String), - // `name`, `closure(Context) -> String`. - Inline(String, Box<dyn Fn(&mut Context) -> String + Send + Sync>), + Inline(String, String), } -/// Define el medio objetivo para la hoja de estilos. -/// -/// Permite especificar en qué contexto se aplica el CSS, adaptándose a diferentes dispositivos o -/// situaciones de impresión. -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] pub enum TargetMedia { - /// Se aplica en todos los casos (el atributo `media` se omite). - #[default] Default, - /// Se aplica cuando el documento se imprime. Print, - /// Se aplica en pantallas. Screen, - /// Se aplica en dispositivos que convierten el texto a voz. Speech, } -/// Devuelve el valor para el atributo `media` (`Some(...)`) o `None` para `Default`. -#[rustfmt::skip] -impl TargetMedia { - const fn as_str(self) -> Option<&'static str> { - match self { - TargetMedia::Default => None, - TargetMedia::Print => Some("print"), - TargetMedia::Screen => Some("screen"), - TargetMedia::Speech => Some("speech"), - } - } -} - -/// Define un recurso **StyleSheet** para incluir en un documento HTML. -/// -/// Este tipo permite incluir hojas de estilo CSS externas o embebidas, con soporte para medios -/// específicos (`screen`, `print`, etc.) y [pesos](crate::Weight) que determinan el orden de -/// inserción en el documento. -/// -/// > **Nota** -/// > Las hojas de estilo CSS deben estar disponibles en el servidor web de la aplicación. Pueden -/// > servirse usando [`static_files_service!`](crate::static_files_service). -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Crea una hoja de estilos externa con control de versión y medio específico (`screen`). -/// let stylesheet = StyleSheet::from("/assets/css/main.css") -/// .with_version("2.0.1") -/// .for_media(TargetMedia::Screen) -/// .with_weight(-10); -/// -/// // Crea una hoja de estilos embebida en el documento HTML. -/// let embedded = StyleSheet::inline("custom_theme", |_| r#" -/// body { -/// background-color: #f5f5f5; -/// font-family: 'Segoe UI', sans-serif; -/// } -/// "#.to_string()); -/// ``` #[rustfmt::skip] #[derive(AutoDefault)] pub struct StyleSheet { - source : Source, // Fuente y modo de inclusión del CSS. - version: String, // Versión del recurso para la caché del navegador. - media : TargetMedia, // Medio objetivo para los estilos (`print`, `screen`, ...). - weight : Weight, // Peso que determina el orden. + source : Source, + prefix : &'static str, + version: &'static str, + media : Option<&'static str>, + weight : Weight, } -impl StyleSheet { - /// Crea una hoja de estilos externa. - /// - /// Equivale a `<link rel="stylesheet" href="...">`. - pub fn from(path: impl Into<String>) -> Self { - StyleSheet { - source: Source::From(path.into()), - ..Default::default() - } - } - - /// Crea una hoja de estilos embebida directamente en el documento HTML. - /// - /// Equivale a `<style>...</style>`. El parámetro `name` se usa como identificador interno del - /// recurso. - /// - /// La función *closure* recibirá el [`Context`] por si se necesita durante el renderizado. - pub fn inline<F>(name: impl Into<String>, f: F) -> Self - where - F: Fn(&mut Context) -> String + Send + Sync + 'static, - { - StyleSheet { - source: Source::Inline(name.into(), Box::new(f)), - ..Default::default() - } - } - - // **< StyleSheet BUILDER >********************************************************************* - - /// Asocia una versión al recurso (usada para control de la caché del navegador). - /// - /// Si `version` está vacío, no se añade ningún parámetro a la URL. - pub fn with_version(mut self, version: impl Into<String>) -> Self { - self.version = version.into(); - self - } - - /// Modifica el peso del recurso. - /// - /// Los recursos se renderizan de menor a mayor peso. Por defecto es `0`, que respeta el orden - /// de creación. - pub fn with_weight(mut self, value: Weight) -> Self { - self.weight = value; - self - } - - // **< StyleSheet HELPERS >********************************************************************* - - /// Especifica el medio donde se aplican los estilos. - /// - /// Según el argumento `media`: - /// - /// - `TargetMedia::Default` - Se aplica en todos los casos (medio por defecto). - /// - `TargetMedia::Print` - Se aplica cuando el documento se imprime. - /// - `TargetMedia::Screen` - Se aplica en pantallas. - /// - `TargetMedia::Speech` - Se aplica en dispositivos que convierten el texto a voz. - pub fn for_media(mut self, media: TargetMedia) -> Self { - self.media = media; - self - } -} - -impl Asset for StyleSheet { - /// Devuelve el nombre del recurso, utilizado como clave única. - /// - /// Para hojas de estilos externas es la ruta del recurso; para las embebidas, un identificador. - fn name(&self) -> &str { +impl AssetsTrait for StyleSheet { + fn name(&self) -> &String { match &self.source { Source::From(path) => path, Source::Inline(name, _) => name, @@ -163,19 +38,58 @@ impl Asset for StyleSheet { self.weight } - // **< StyleSheet RENDER >********************************************************************** - - fn render(&self, cx: &mut Context) -> Markup { + fn render(&self) -> Markup { match &self.source { Source::From(path) => html! { link rel="stylesheet" - href=(join_pair!(path, "?v=", &self.version)) - media=[self.media.as_str()]; + href=(concat_string!(path, self.prefix, self.version)) + media=[self.media]; }, - Source::Inline(_, f) => html! { - style { (PreEscaped((f)(cx))) }; + Source::Inline(_, code) => html! { + style { (PreEscaped(code)) }; }, } } } + +impl StyleSheet { + pub fn from(path: impl Into<String>) -> Self { + StyleSheet { + source: Source::From(path.into()), + ..Default::default() + } + } + + pub fn inline(name: impl Into<String>, styles: impl Into<String>) -> Self { + StyleSheet { + source: Source::Inline(name.into(), styles.into()), + ..Default::default() + } + } + + pub fn with_version(mut self, version: &'static str) -> Self { + (self.prefix, self.version) = if version.is_empty() { + ("", "") + } else { + ("?v=", version) + }; + self + } + + pub fn with_weight(mut self, value: Weight) -> Self { + self.weight = value; + self + } + + #[rustfmt::skip] + pub fn for_media(mut self, media: &TargetMedia) -> Self { + self.media = match media { + TargetMedia::Default => None, + TargetMedia::Print => Some("print"), + TargetMedia::Screen => Some("screen"), + TargetMedia::Speech => Some("speech"), + }; + self + } +} diff --git a/src/html/attr_id.rs b/src/html/attr_id.rs deleted file mode 100644 index a1d8a1da..00000000 --- a/src/html/attr_id.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{builder_fn, AutoDefault}; - -/// Identificador normalizado para el atributo `id` o similar de HTML. -/// -/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso: -/// -/// - Se eliminan los espacios al principio y al final. -/// - Se convierte a minúsculas. -/// - Se sustituyen los espacios intermedios por guiones bajos (`_`). -/// - Si el resultado es una cadena vacía, se guarda `None`. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let id = AttrId::new(" main Section "); -/// assert_eq!(id.as_str(), Some("main_section")); -/// -/// let empty = AttrId::default(); -/// assert_eq!(empty.get(), None); -/// ``` -#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)] -pub struct AttrId(Option<String>); - -impl AttrId { - /// Crea un nuevo `AttrId` normalizando el valor. - pub fn new(value: impl AsRef<str>) -> Self { - AttrId::default().with_value(value) - } - - // **< AttrId BUILDER >************************************************************************* - - /// Establece un identificador nuevo normalizando el valor. - #[builder_fn] - pub fn with_value(mut self, value: impl AsRef<str>) -> Self { - let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_"); - self.0 = if value.is_empty() { None } else { Some(value) }; - self - } - - // **< AttrId GETTERS >************************************************************************* - - /// Devuelve el identificador normalizado, si existe. - pub fn get(&self) -> Option<String> { - self.0.as_ref().cloned() - } - - /// Devuelve el identificador normalizado (sin clonar), si existe. - pub fn as_str(&self) -> Option<&str> { - self.0.as_deref() - } - - /// Devuelve el identificador normalizado (propiedad), si existe. - pub fn into_inner(self) -> Option<String> { - self.0 - } - - /// `true` si no hay valor. - pub fn is_empty(&self) -> bool { - self.0.is_none() - } -} diff --git a/src/html/attr_l10n.rs b/src/html/attr_l10n.rs deleted file mode 100644 index 86d1c4a3..00000000 --- a/src/html/attr_l10n.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::html::Markup; -use crate::locale::{L10n, LangId}; -use crate::{builder_fn, AutoDefault}; - -/// Texto para [traducir](crate::locale) en atributos HTML. -/// -/// Encapsula un [`L10n`] para manejar traducciones de forma segura en atributos. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Traducción por clave en las locales por defecto de PageTop. -/// let hello = AttrL10n::new(L10n::l("test-hello-world")); -/// -/// // Español disponible. -/// assert_eq!( -/// hello.lookup(&LangMatch::resolve("es-ES")), -/// Some("¡Hola mundo!".to_string()) -/// ); -/// -/// // Japonés no disponible, traduce al idioma de respaldo ("en-US"). -/// assert_eq!( -/// hello.lookup(&LangMatch::resolve("ja-JP")), -/// Some("Hello world!".to_string()) -/// ); -/// -/// // Uso típico en un atributo: -/// let title = hello.value(&LangMatch::resolve("es-ES")); -/// // Ejemplo: html! { a title=(title) { "Link" } } -/// ``` -#[derive(AutoDefault, Clone, Debug)] -pub struct AttrL10n(L10n); - -impl AttrL10n { - /// Crea una nueva instancia `AttrL10n`. - pub fn new(value: L10n) -> Self { - AttrL10n(value) - } - - // **< AttrL10n BUILDER >*********************************************************************** - - /// Establece una traducción nueva. - #[builder_fn] - pub fn with_value(mut self, value: L10n) -> Self { - self.0 = value; - self - } - - // **< AttrL10n GETTERS >*********************************************************************** - - /// Devuelve la traducción para `language`, si existe. - pub fn lookup(&self, language: &impl LangId) -> Option<String> { - self.0.lookup(language) - } - - /// Devuelve la traducción para `language` o una cadena vacía si no existe. - pub fn value(&self, language: &impl LangId) -> String { - self.0.lookup(language).unwrap_or_default() - } - - /// **Obsoleto desde la versión 0.4.0**: no recomendado para atributos HTML. - #[deprecated(since = "0.4.0", note = "For attributes use `lookup()` or `value()`")] - pub fn to_markup(&self, language: &impl LangId) -> Markup { - self.0.using(language) - } -} diff --git a/src/html/attr_name.rs b/src/html/attr_name.rs deleted file mode 100644 index 1741695c..00000000 --- a/src/html/attr_name.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::{builder_fn, AutoDefault}; - -/// Nombre normalizado para el atributo `name` o similar de HTML. -/// -/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso: -/// -/// - Se eliminan los espacios al principio y al final. -/// - Se convierte a minúsculas. -/// - Se sustituyen los espacios intermedios por guiones bajos (`_`). -/// - Si el resultado es una cadena vacía, se guarda `None`. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let name = AttrName::new(" DISplay name "); -/// assert_eq!(name.as_str(), Some("display_name")); -/// -/// let empty = AttrName::default(); -/// assert_eq!(empty.get(), None); -/// ``` -#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)] -pub struct AttrName(Option<String>); - -impl AttrName { - /// Crea un nuevo `AttrName` normalizando el valor. - pub fn new(value: impl AsRef<str>) -> Self { - AttrName::default().with_value(value) - } - - // **< AttrName BUILDER >*********************************************************************** - - /// Establece un nombre nuevo normalizando el valor. - #[builder_fn] - pub fn with_value(mut self, value: impl AsRef<str>) -> Self { - let value = value.as_ref().trim().to_ascii_lowercase().replace(' ', "_"); - self.0 = if value.is_empty() { None } else { Some(value) }; - self - } - - // **< AttrName GETTERS >*********************************************************************** - - /// Devuelve el nombre normalizado, si existe. - pub fn get(&self) -> Option<String> { - self.0.as_ref().cloned() - } - - /// Devuelve el nombre normalizado (sin clonar), si existe. - pub fn as_str(&self) -> Option<&str> { - self.0.as_deref() - } - - /// Devuelve el nombre normalizado (propiedad), si existe. - pub fn into_inner(self) -> Option<String> { - self.0 - } - - /// `true` si no hay valor. - pub fn is_empty(&self) -> bool { - self.0.is_none() - } -} diff --git a/src/html/attr_value.rs b/src/html/attr_value.rs deleted file mode 100644 index b20dec3d..00000000 --- a/src/html/attr_value.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{builder_fn, AutoDefault}; - -/// Cadena normalizada para renderizar en atributos HTML. -/// -/// Este tipo encapsula `Option<String>` garantizando un valor normalizado para su uso: -/// -/// - Se eliminan los espacios al principio y al final. -/// - Si el resultado es una cadena vacía, se guarda `None`. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let s = AttrValue::new(" a new string "); -/// assert_eq!(s.as_str(), Some("a new string")); -/// -/// let empty = AttrValue::default(); -/// assert_eq!(empty.get(), None); -/// ``` -#[derive(AutoDefault, Clone, Debug, Hash, Eq, PartialEq)] -pub struct AttrValue(Option<String>); - -impl AttrValue { - /// Crea un nuevo `AttrValue` normalizando el valor. - pub fn new(value: impl AsRef<str>) -> Self { - AttrValue::default().with_value(value) - } - - // **< AttrValue BUILDER >********************************************************************** - - /// Establece una cadena nueva normalizando el valor. - #[builder_fn] - pub fn with_value(mut self, value: impl AsRef<str>) -> Self { - let value = value.as_ref().trim(); - self.0 = if value.is_empty() { - None - } else { - Some(value.to_string()) - }; - self - } - - // **< AttrValue GETTERS >********************************************************************** - - /// Devuelve la cadena normalizada, si existe. - pub fn get(&self) -> Option<String> { - self.0.as_ref().cloned() - } - - /// Devuelve la cadena normalizada (sin clonar), si existe. - pub fn as_str(&self) -> Option<&str> { - self.0.as_deref() - } - - /// Devuelve la cadena normalizada (propiedad), si existe. - pub fn into_inner(self) -> Option<String> { - self.0 - } - - /// `true` si no hay valor. - pub fn is_empty(&self) -> bool { - self.0.is_none() - } -} diff --git a/src/html/logo.rs b/src/html/logo.rs deleted file mode 100644 index fd604414..00000000 --- a/src/html/logo.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::core::component::Context; -use crate::html::{html, Markup}; -use crate::locale::L10n; -use crate::AutoDefault; - -/// Representación SVG del **logotipo de PageTop** para incrustar en HTML. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// fn render_logo(cx: &mut Context) -> PrepareMarkup { -/// PrepareMarkup::With(html! { -/// div class="logo_color" { -/// (PageTopSvg::Color.render(cx)) -/// } -/// div class="line_dark" { -/// (PageTopSvg::LineDark.render(cx)) -/// } -/// div class="line_light" { -/// (PageTopSvg::LineLight.render(cx)) -/// } -/// div class="line_red" { -/// (PageTopSvg::LineRGB(255, 0, 0).render(cx)) -/// } -/// }) -/// }; -/// ``` - -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum PageTopSvg { - /// Versión por defecto con el logotipo a color. - #[default] - Color, - /// Versión monocroma (línea) oscura, ideal sobre fondos claros. - LineDark, - /// Versión monocroma (línea) clara, ideal sobre fondos oscuros. - LineLight, - /// Versión monocroma configurable por RGB. - LineRGB(u8, u8, u8), -} - -impl PageTopSvg { - /// Renderiza el SVG del logotipo según la variante elegida. - pub fn render(&self, cx: &Context) -> Markup { - let path_fills = match self { - Self::Color => self.logo_color(), - Self::LineDark => self.logo_line(10, 11, 9), - Self::LineLight => self.logo_line(255, 255, 255), - Self::LineRGB(r, g, b) => self.logo_line(*r, *g, *b), - }; - html! { - 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_fills) - } - } - } - - // **< PageTopSvg HELPERS >********************************************************************* - - fn logo_color(&self) -> Markup { - html! { - path fill="rgb(255,184,75)" d="M 633,61 L 633,61 C 579,61 527,75 480,102 433,129 395,167 368,214 341,261 327,313 327,367 L 327,1244 327,1245 C 327,1299 341,1351 368,1398 395,1445 433,1483 480,1510 527,1537 579,1551 633,1551 L 982,1550 982,1551 C 1036,1551 1088,1537 1135,1510 1182,1483 1220,1445 1247,1398 1274,1351 1288,1299 1288,1245 L 1288,367 1288,367 1288,367 C 1288,313 1274,261 1247,214 1220,167 1182,129 1135,102 1088,75 1036,61 982,61 L 633,61 Z" {} - path fill="rgb(158,96,0)" d="M 1389,573 L 1328,573 1328,460 1449,460 1449,573 1389,573 Z M 1491,627 L 1430,627 1430,404 1551,404 1551,627 1491,627 Z M 222,573 L 161,573 161,460 282,460 282,573 222,573 Z M 120,627 L 59,627 59,404 180,404 180,627 120,627 Z" {} - path fill="rgb(150,0,184)" d="M 678,1040 L 678,1040 C 645,1040 612,1049 583,1065 554,1082 530,1106 513,1135 497,1164 488,1197 488,1230 L 488,1230 488,1230 C 488,1263 497,1296 513,1325 530,1354 554,1378 583,1395 612,1411 645,1420 678,1420 L 940,1420 940,1420 C 973,1420 1006,1411 1035,1395 1064,1378 1088,1354 1105,1325 1121,1296 1130,1263 1130,1230 L 1130,1230 1130,1230 1130,1230 C 1130,1197 1121,1164 1105,1135 1088,1106 1064,1082 1035,1065 1006,1049 973,1040 940,1040 L 678,1040 Z M 488,1040 L 488,1040 488,1040 488,1040 488,1040 488,1040 488,1238 488,1238 488,1238 488,1238 488,1238 1130,1238 1130,1238 1130,1238 1130,1238 1130,1238 1130,1238 1130,1040 1130,1040 1130,1040 1130,1040 1130,1040 488,1040 Z" {} - path fill="rgb(255,255,255)" d="M 518,1128 L 488,1128 488,1066 547,1066 547,1128 518,1128 Z M 966,1128 L 908,1128 908,1066 1024,1066 1024,1128 966,1128 Z" {} - path fill="rgb(221,255,149)" d="M 898,71 C 940,48 987,36 1035,36 1086,36 1137,50 1181,77 1226,104 1263,142 1289,189 1314,236 1328,288 1328,342 1328,396 1314,448 1289,495 1281,509 1272,522 1262,535 L 898,71 Z M 1337,429 C 1307,460 1272,480 1233,488 1192,496 1148,490 1107,470 1066,451 1029,418 999,375 969,332 948,281 938,227 927,173 928,117 940,66 943,51 948,36 953,22 L 1337,429 Z" {} - path fill="rgb(146,128,99)" d="M 703,99 C 717,121 722,149 719,178 715,208 702,238 682,267 661,295 634,320 602,340 571,360 536,373 501,379 467,385 434,383 405,373 377,363 355,346 341,323 327,301 322,273 325,244 329,214 342,184 362,155 383,127 410,102 442,82 473,62 508,49 543,43 577,37 610,39 639,49 667,59 689,76 703,99 L 703,99 Z" {} - path fill="rgb(146,128,99)" d="M 672,550 C 683,568 685,590 678,615 671,640 655,668 632,694 609,720 580,745 547,765 514,785 479,801 446,810 412,819 381,821 355,816 329,811 310,799 299,782 288,765 286,742 293,717 301,692 316,665 339,638 362,612 391,588 424,567 457,547 492,531 526,523 559,514 591,511 616,516 642,521 661,533 672,550 L 672,550 Z" {} - path fill="rgb(146,128,99)" d="M 455,160 L 456,160 C 430,160 404,167 381,180 359,193 340,212 327,234 314,257 307,283 307,309 L 307,580 307,580 C 307,606 314,632 327,655 340,677 359,696 381,709 404,722 430,729 456,729 L 529,729 529,729 C 555,729 581,722 604,709 626,696 645,677 658,655 671,632 678,606 678,580 L 677,308 678,309 678,309 C 678,283 671,257 658,234 645,212 626,193 604,180 581,167 555,160 529,160 L 455,160 Z" {} - path fill="rgb(240,0,0)" d="M 698,1332 L 699,1332 C 696,1332 694,1333 691,1334 689,1335 687,1337 686,1339 685,1342 684,1344 684,1347 L 684,1405 684,1405 C 684,1408 685,1410 686,1413 687,1415 689,1417 691,1418 694,1419 696,1420 699,1420 L 914,1419 914,1420 C 917,1420 919,1419 922,1418 924,1417 926,1415 927,1413 928,1410 929,1408 929,1405 L 929,1346 929,1347 929,1347 C 929,1344 928,1342 927,1339 926,1337 924,1335 922,1334 919,1333 917,1332 914,1332 L 698,1332 Z M 643,780 C 643,797 638,814 630,830 621,845 608,858 593,867 577,875 560,880 543,880 525,880 508,875 492,867 477,858 464,845 455,830 447,814 442,797 442,780 442,762 447,745 455,729 464,714 477,701 492,692 508,684 525,679 542,679 560,679 577,684 593,692 608,701 621,714 630,729 638,745 643,762 643,779 L 643,780 Z M 1171,780 C 1171,797 1166,814 1158,830 1149,845 1136,858 1121,867 1105,875 1088,880 1071,880 1053,880 1036,875 1020,867 1005,858 992,845 983,830 975,814 970,797 970,780 970,762 975,745 983,729 992,714 1005,701 1020,692 1036,684 1053,679 1071,679 1088,679 1105,684 1121,692 1136,701 1149,714 1158,729 1166,745 1171,762 1171,779 L 1171,780 Z" {} - path fill="rgb(10,11,9)" 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(10,11,9)" 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(10,11,9)" 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" {} - } - } - - fn logo_line(&self, r: u8, g: u8, b: u8) -> Markup { - let logo_rgb = format!("rgb({r},{g},{b})"); - html! { - path fill=(logo_rgb) 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=(logo_rgb) 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=(logo_rgb) 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" {} - } - } -} diff --git a/src/html/maud.rs b/src/html/maud.rs index 65360360..801895e5 100644 --- a/src/html/maud.rs +++ b/src/html/maud.rs @@ -1,4 +1,4 @@ -// #![no_std] +//#![no_std] //! A macro for writing HTML templates. //! @@ -7,18 +7,18 @@ //! //! [book]: https://maud.lambda.xyz/ -// #![doc(html_root_url = "https://docs.rs/maud/0.27.0")] +//#![doc(html_root_url = "https://docs.rs/maud/0.25.0")] extern crate alloc; -use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc}; +use alloc::{borrow::Cow, boxed::Box, string::String}; use core::fmt::{self, Arguments, Display, Write}; pub use pagetop_macros::html; mod escape; -/// Adaptador que escapa los caracteres especiales de HTML. +/// An adapter that escapes HTML special characters. /// /// The following characters are escaped: /// @@ -35,7 +35,7 @@ mod escape; /// # Example /// /// ```rust -/// use pagetop::html::Escaper; +/// use maud::Escaper; /// use std::fmt::Write; /// let mut s = String::new(); /// write!(Escaper::new(&mut s), "<script>launchMissiles()</script>").unwrap(); @@ -50,14 +50,14 @@ impl<'a> Escaper<'a> { } } -impl fmt::Write for Escaper<'_> { +impl<'a> fmt::Write for Escaper<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { escape::escape_to_string(s, self.0); Ok(()) } } -/// Representa un tipo que puede renderizarse como HTML. +/// Represents a type that can be rendered as HTML. /// /// To implement this for your own type, override either the `.render()` /// or `.render_to()` methods; since each is defined in terms of the @@ -69,6 +69,23 @@ impl fmt::Write for Escaper<'_> { /// `.render()` or `.render_to()`. Since the default definitions of /// these methods call each other, not doing this will result in /// infinite recursion. +/// +/// # Example +/// +/// ```rust +/// use maud::{html, Markup, Render}; +/// +/// /// Provides a shorthand for linking to a CSS stylesheet. +/// pub struct Stylesheet(&'static str); +/// +/// impl Render for Stylesheet { +/// fn render(&self) -> Markup { +/// html! { +/// link rel="stylesheet" type="text/css" href=(self.0); +/// } +/// } +/// } +/// ``` pub trait Render { /// Renders `self` as a block of `Markup`. fn render(&self) -> Markup { @@ -103,25 +120,25 @@ impl Render for String { } } -impl Render for Cow<'_, str> { +impl<'a> Render for Cow<'a, str> { fn render_to(&self, w: &mut String) { str::render_to(self, w); } } -impl Render for Arguments<'_> { +impl<'a> Render for Arguments<'a> { fn render_to(&self, w: &mut String) { let _ = Escaper::new(w).write_fmt(*self); } } -impl<T: Render + ?Sized> Render for &T { +impl<'a, T: Render + ?Sized> Render for &'a T { fn render_to(&self, w: &mut String) { T::render_to(self, w); } } -impl<T: Render + ?Sized> Render for &mut T { +impl<'a, T: Render + ?Sized> Render for &'a mut T { fn render_to(&self, w: &mut String) { T::render_to(self, w); } @@ -133,12 +150,6 @@ impl<T: Render + ?Sized> Render for Box<T> { } } -impl<T: Render + ?Sized> Render for Arc<T> { - fn render_to(&self, w: &mut String) { - T::render_to(self, w); - } -} - macro_rules! impl_render_with_display { ($($ty:ty)*) => { $( @@ -173,19 +184,19 @@ impl_render_with_itoa! { u8 u16 u32 u64 u128 usize } -/// Renderiza un valor usando su implementación de [`Display`]. +/// Renders a value using its [`Display`] impl. /// /// # Example /// /// ```rust -/// use pagetop::prelude::*; +/// use maud::html; /// use std::net::Ipv4Addr; /// /// let ip_address = Ipv4Addr::new(127, 0, 0, 1); /// /// let markup = html! { /// "My IP address is: " -/// (display(ip_address)) +/// (maud::display(ip_address)) /// }; /// /// assert_eq!(markup.into_string(), "My IP address is: 127.0.0.1"); @@ -202,9 +213,9 @@ pub fn display(value: impl Display) -> impl Render { DisplayWrapper(value) } -/// Contenedor que renderiza el valor interno sin escapar. +/// A wrapper that renders the inner value without escaping. #[derive(Debug, Clone, Copy)] -pub struct PreEscaped<T>(pub T); +pub struct PreEscaped<T: AsRef<str>>(pub T); impl<T: AsRef<str>> Render for PreEscaped<T> { fn render_to(&self, w: &mut String) { @@ -212,7 +223,7 @@ impl<T: AsRef<str>> Render for PreEscaped<T> { } } -/// Un bloque de marcado es una cadena que no necesita ser escapada. +/// A block of markup is a string that does not need to be escaped. /// /// The `html!` macro expands to an expression of this type. pub type Markup = PreEscaped<String>; @@ -221,39 +232,35 @@ impl Markup { pub fn is_empty(&self) -> bool { self.0.is_empty() } - - pub fn as_str(&self) -> &str { - self.0.as_str() - } } -impl<T: Into<String>> PreEscaped<T> { +impl<T: AsRef<str> + Into<String>> PreEscaped<T> { /// Converts the inner value to a string. pub fn into_string(self) -> String { self.0.into() } } -impl<T: Into<String>> From<PreEscaped<T>> for String { +impl<T: AsRef<str> + Into<String>> From<PreEscaped<T>> for String { fn from(value: PreEscaped<T>) -> String { value.into_string() } } -impl<T: Default> Default for PreEscaped<T> { +impl<T: AsRef<str> + Default> Default for PreEscaped<T> { fn default() -> Self { Self(Default::default()) } } -/// La cadena literal `<!DOCTYPE html>`. +/// The literal string `<!DOCTYPE html>`. /// /// # Example /// /// A minimal web page: /// /// ```rust -/// use pagetop::prelude::*; +/// use maud::{DOCTYPE, html}; /// /// let markup = html! { /// (DOCTYPE) @@ -273,35 +280,10 @@ pub const DOCTYPE: PreEscaped<&'static str> = PreEscaped("<!DOCTYPE html>"); mod actix_support { extern crate alloc; - use core::{ - pin::Pin, - task::{Context, Poll}, - }; - use crate::html::PreEscaped; - use actix_web::{ - body::{BodySize, MessageBody}, - http::header, - web::Bytes, - HttpRequest, HttpResponse, Responder, - }; + use actix_web::{http::header, HttpRequest, HttpResponse, Responder}; use alloc::string::String; - impl MessageBody for PreEscaped<String> { - type Error = <String as MessageBody>::Error; - - fn size(&self) -> BodySize { - self.0.size() - } - - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll<Option<Result<Bytes, Self::Error>>> { - Pin::new(&mut self.0).poll_next(cx) - } - } - impl Responder for PreEscaped<String> { type Body = String; diff --git a/src/html/attr_classes.rs b/src/html/opt_classes.rs similarity index 53% rename from src/html/attr_classes.rs rename to src/html/opt_classes.rs index bb88f587..453991cd 100644 --- a/src/html/attr_classes.rs +++ b/src/html/opt_classes.rs @@ -1,57 +1,38 @@ -use crate::{builder_fn, AutoDefault}; +//! **OptionClasses** implements a *helper* for dynamically adding class names to components. +//! +//! This *helper* differentiates between default classes (generally associated with styles provided +//! by the theme) and user classes (for customizing components based on application styles). +//! +//! Classes can be added using [Add]. Operations to [Remove], [Replace] or [Toggle] a class, as well +//! as [Clear] all classes, are also provided. +//! +//! **OptionClasses** assumes that the order of the classes is irrelevant +//! (<https://stackoverflow.com/a/1321712>), and duplicate classes will not be allowed. + +use crate::{fn_builder, AutoDefault}; -/// Operaciones disponibles sobre la lista de clases en [`AttrClasses`]. pub enum ClassesOp { - /// Añade al final (si no existe). Add, - /// Añade al principio. Prepend, - /// Elimina coincidencias. Remove, - /// Sustituye una o varias por las nuevas (`Replace("old other")`). Replace(String), - /// Alterna presencia/ausencia. Toggle, - /// Sustituye toda la lista. Set, } -/// Cadena de clases CSS normalizadas para el atributo `class` de HTML. -/// -/// Permite construir y modificar dinámicamente con [`ClassesOp`] una lista de clases CSS -/// normalizadas. -/// -/// # Normalización -/// -/// - El [orden de las clases no es relevante](https://stackoverflow.com/a/1321712) en CSS. -/// - No se permiten clases duplicadas. -/// - Las clases se convierten a minúsculas. -/// - Las clases vacías se ignoran. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let classes = AttrClasses::new("Btn btn-primary") -/// .with_value(ClassesOp::Add, "Active") -/// .with_value(ClassesOp::Remove, "btn-primary"); -/// -/// assert_eq!(classes.get(), Some("btn active".to_string())); -/// assert!(classes.contains("active")); -/// ``` -#[derive(AutoDefault, Clone, Debug)] -pub struct AttrClasses(Vec<String>); +#[derive(AutoDefault)] +pub struct OptionClasses(Vec<String>); -impl AttrClasses { - pub fn new(classes: impl AsRef<str>) -> Self { - AttrClasses::default().with_value(ClassesOp::Prepend, classes) +impl OptionClasses { + pub fn new(classes: impl Into<String>) -> Self { + OptionClasses::default().with_value(ClassesOp::Prepend, classes) } - // **< AttrClasses BUILDER >******************************************************************** + // OptionClasses BUILDER. - #[builder_fn] - pub fn with_value(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - let classes = classes.as_ref().to_ascii_lowercase(); + #[fn_builder] + pub fn set_value(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self { + let classes: String = classes.into(); let classes: Vec<&str> = classes.split_ascii_whitespace().collect(); if classes.is_empty() { @@ -113,9 +94,8 @@ impl AttrClasses { } } - // **< AttrClasses GETTERS >******************************************************************** + // OptionClasses GETTERS. - /// Devuelve la cadena de clases, si existe. pub fn get(&self) -> Option<String> { if self.0.is_empty() { None @@ -124,9 +104,8 @@ impl AttrClasses { } } - /// Devuelve `true` si la clase está presente. - pub fn contains(&self, class: impl AsRef<str>) -> bool { - let class = class.as_ref(); - self.0.iter().any(|c| c == class) + pub fn contains(&self, class: impl Into<String>) -> bool { + let class: String = class.into(); + self.0.iter().any(|c| c.eq(&class)) } } diff --git a/src/html/opt_component.rs b/src/html/opt_component.rs new file mode 100644 index 00000000..5082a939 --- /dev/null +++ b/src/html/opt_component.rs @@ -0,0 +1,46 @@ +use crate::core::component::{ComponentTrait, Context, TypedComponent}; +use crate::fn_builder; +use crate::html::{html, Markup}; + +pub struct OptionComponent<C: ComponentTrait>(Option<TypedComponent<C>>); + +impl<C: ComponentTrait> Default for OptionComponent<C> { + fn default() -> Self { + OptionComponent(None) + } +} + +impl<C: ComponentTrait> OptionComponent<C> { + pub fn new(component: C) -> Self { + OptionComponent::default().with_value(Some(component)) + } + + // OptionComponent BUILDER. + + #[fn_builder] + pub fn set_value(&mut self, component: Option<C>) -> &mut Self { + if let Some(component) = component { + self.0 = Some(TypedComponent::with(component)); + } else { + self.0 = None; + } + self + } + + // OptionComponent GETTERS. + + pub fn get(&self) -> Option<TypedComponent<C>> { + if let Some(value) = &self.0 { + return Some(value.clone()); + } + None + } + + pub fn render(&self, cx: &mut Context) -> Markup { + if let Some(component) = &self.0 { + component.render(cx) + } else { + html! {} + } + } +} diff --git a/src/html/opt_id.rs b/src/html/opt_id.rs new file mode 100644 index 00000000..80e98325 --- /dev/null +++ b/src/html/opt_id.rs @@ -0,0 +1,29 @@ +use crate::{fn_builder, AutoDefault}; + +#[derive(AutoDefault)] +pub struct OptionId(Option<String>); + +impl OptionId { + pub fn new(value: impl Into<String>) -> Self { + OptionId::default().with_value(value) + } + + // OptionId BUILDER. + + #[fn_builder] + pub fn set_value(&mut self, value: impl Into<String>) -> &mut Self { + self.0 = Some(value.into().trim().replace(' ', "_")); + self + } + + // OptionId GETTERS. + + pub fn get(&self) -> Option<String> { + if let Some(value) = &self.0 { + if !value.is_empty() { + return Some(value.to_owned()); + } + } + None + } +} diff --git a/src/html/opt_name.rs b/src/html/opt_name.rs new file mode 100644 index 00000000..5ba0c486 --- /dev/null +++ b/src/html/opt_name.rs @@ -0,0 +1,29 @@ +use crate::{fn_builder, AutoDefault}; + +#[derive(AutoDefault)] +pub struct OptionName(Option<String>); + +impl OptionName { + pub fn new(value: impl Into<String>) -> Self { + OptionName::default().with_value(value) + } + + // OptionName BUILDER. + + #[fn_builder] + pub fn set_value(&mut self, value: impl Into<String>) -> &mut Self { + self.0 = Some(value.into().trim().replace(' ', "_")); + self + } + + // OptionName GETTERS. + + pub fn get(&self) -> Option<String> { + if let Some(value) = &self.0 { + if !value.is_empty() { + return Some(value.to_owned()); + } + } + None + } +} diff --git a/src/html/opt_string.rs b/src/html/opt_string.rs new file mode 100644 index 00000000..7de22486 --- /dev/null +++ b/src/html/opt_string.rs @@ -0,0 +1,29 @@ +use crate::{fn_builder, AutoDefault}; + +#[derive(AutoDefault)] +pub struct OptionString(Option<String>); + +impl OptionString { + pub fn new(value: impl Into<String>) -> Self { + OptionString::default().with_value(value) + } + + // OptionString BUILDER. + + #[fn_builder] + pub fn set_value(&mut self, value: impl Into<String>) -> &mut Self { + self.0 = Some(value.into().trim().to_owned()); + self + } + + // OptionString GETTERS. + + pub fn get(&self) -> Option<String> { + if let Some(value) = &self.0 { + if !value.is_empty() { + return Some(value.to_owned()); + } + } + None + } +} diff --git a/src/html/opt_translated.rs b/src/html/opt_translated.rs new file mode 100644 index 00000000..e50a073f --- /dev/null +++ b/src/html/opt_translated.rs @@ -0,0 +1,30 @@ +use crate::html::Markup; +use crate::locale::{L10n, LanguageIdentifier}; +use crate::{fn_builder, AutoDefault}; + +#[derive(AutoDefault)] +pub struct OptionTranslated(L10n); + +impl OptionTranslated { + pub fn new(value: L10n) -> Self { + OptionTranslated(value) + } + + // OptionTranslated BUILDER. + + #[fn_builder] + pub fn set_value(&mut self, value: L10n) -> &mut Self { + self.0 = value; + self + } + + // OptionTranslated GETTERS. + + pub fn using(&self, langid: &LanguageIdentifier) -> Option<String> { + self.0.using(langid) + } + + pub fn escaped(&self, langid: &LanguageIdentifier) -> Markup { + self.0.escaped(langid) + } +} diff --git a/src/html/unit.rs b/src/html/unit.rs index 4a99de16..5a153c55 100644 --- a/src/html/unit.rs +++ b/src/html/unit.rs @@ -1,283 +1,56 @@ use crate::AutoDefault; -use serde::{Deserialize, Deserializer}; - use std::fmt; -use std::str::FromStr; -/// Representa una **unidad CSS** lista para formatear o deserializar. -/// -/// ## Unidades soportadas -/// -/// - **Absolutas** *(valores enteros, `isize`)*: -/// - `Cm(isize)` - `cm` (centímetros) -/// - `In(isize)` - `in` (pulgadas; `1in = 96px = 2.54cm`) -/// - `Mm(isize)` - `mm` (milímetros) -/// - `Pc(isize)` - `pc` (picas; `1pc = 12pt`) -/// - `Pt(isize)` - `pt` (puntos; `1pt = 1/72in`) -/// - `Px(isize)` - `px` (píxeles; `1px = 1/96in`) -/// -/// - **Relativas** *(valores decimales, `f32`)*: -/// - `RelEm(f32)` - `em` (relativa al tamaño de fuente del elemento) -/// - `RelRem(f32)` - `rem` (relativa al tamaño de fuente de `:root`) -/// - `RelPct(f32)` - `%` (porcentaje relativo al elemento padre) -/// - `RelVh(f32)` - `vh` (1% de la **altura** del viewport) -/// - `RelVw(f32)` - `vw` (1% del **ancho** del viewport) -/// -/// ## Valores especiales -/// -/// - `None` - equivale a un texto vacío (`""`), útil para atributos opcionales. -/// - `Auto` - equivale a `"auto"`. -/// - `Zero` - equivale a `"0"` (cero sin unidad). -/// -/// ## Características -/// -/// - Soporta unidades **absolutas** (`cm`, `in`, `mm`, `pc`, `pt`, `px`) y **relativas** (`em`, -/// `rem`, `%`, `vh`, `vw`). -/// - `FromStr` para convertir desde texto (p. ej., `"12px"`, `"1.25rem"`, `"auto"`). -/// - `Display` para formatear a cadena (p. ej., `UnitValue::Px(12)` genera `"12px"`). -/// - `Deserialize` delega en `FromStr`, garantizando una gramática única. -/// -/// ## Ejemplos -/// -/// ```rust -/// # use pagetop::prelude::*; -/// use std::str::FromStr; -/// -/// assert_eq!(UnitValue::from_str("16px").unwrap(), UnitValue::Px(16)); -/// assert_eq!(UnitValue::from_str("1.25rem").unwrap(), UnitValue::RelRem(1.25)); -/// assert_eq!(UnitValue::from_str("33%").unwrap(), UnitValue::RelPct(33.0)); -/// assert_eq!(UnitValue::from_str("auto").unwrap(), UnitValue::Auto); -/// assert_eq!(UnitValue::from_str("").unwrap(), UnitValue::None); -/// assert_eq!(UnitValue::from_str("0").unwrap(), UnitValue::Zero); -/// ``` -/// -/// ## Notas -/// -/// - Las absolutas **no aceptan** decimales (p. ej., `"1.5px"` sería erróneo). -/// - Se aceptan signos `+`/`-` en todas las unidades (p. ej., `"-12px"`, `"+0.5em"`). -/// - La comparación de unidad es *case-insensitive* al interpretar el texto (`"PX"`, `"Px"`, …). -/// - **Sobre píxeles**: Los píxeles (px) son relativos al dispositivo de visualización. En -/// dispositivos con baja densidad de píxeles (dpi), 1px equivale a un píxel (punto) del -/// dispositivo. En impresoras y pantallas de alta resolución, 1px implica múltiples píxeles del -/// dispositivo. -/// - **Sobre `em` y `rem`**: -/// - `em` es **relativo al tamaño de fuente *del propio elemento***. Si el elemento hereda o -/// cambia su `font-size`, todos los valores en `em` dentro de él **se escalan en cascada**. -/// - `rem` es **relativo al tamaño de fuente del elemento raíz** (`:root`/`html`), **no se verá -/// afectado** por cambios de `font-size` en elementos anidados. -/// - Ejemplo: si `:root { font-size: 16px }` y un contenedor tiene `font-size: 20px`, entonces -/// dentro del contenedor `1em == 20px` pero `1rem == 16px`. -/// - Uso típico: `rem` para tipografía y espaciados globales (consistencia al cambiar la base del -/// sitio); `em` para tamaños que deban escalar **con el propio componente** (p. ej., -/// `padding: 0.5em` que crece si el componente aumenta su `font-size`). -/// - **Sobre el viewport**: Si el ancho de la ventana del navegador es de 50cm, 1vw equivale a -/// 0.5cm (1vw siempre es 1% del ancho del viewport, independientemente del zoom del navegador o -/// la densidad de píxeles del dispositivo). +// About pixels: Pixels (px) are relative to the viewing device. For low-dpi devices, 1px is one +// device pixel (dot) of the display. For printers and high resolution screens 1px implies multiple +// device pixels. + +// About em: 2em means 2 times the size of the current font. The em and rem units are practical in +// creating perfectly scalable layout! + +// About viewport: If the browser window size is 50cm wide, 1vw = 0.5cm. + #[rustfmt::skip] -#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)] -pub enum UnitValue { +#[derive(AutoDefault)] +pub enum Value { #[default] None, Auto, - /// Cero sin unidad. - Zero, - /// Centímetros. - Cm(isize), - /// Pulgadas (1in = 96px = 2.54cm). - In(isize), - /// Milímetros. - Mm(isize), - /// Picas (1pc = 12pt). - Pc(isize), - /// Puntos (1pt = 1/72in). - Pt(isize), - /// Píxeles (1px = 1/96in). - Px(isize), - /// Relativo al tamaño de la fuente del elemento. - RelEm(f32), - /// Relativo al tamaño de la fuente del elemento raíz. - RelRem(f32), - /// Porcentaje relativo al elemento padre. - RelPct(f32), - /// Relativo al 1% de la altura del viewport. - RelVh(f32), - /// Relativo al 1% del ancho del viewport. - RelVw(f32), + + Cm(isize), // Centimeters. + In(isize), // Inches (1in = 96px = 2.54cm). + Mm(isize), // Millimeters. + Pc(isize), // Picas (1pc = 12pt). + Pt(isize), // Points (1pt = 1/72 of 1in). + Px(isize), // Pixels (1px = 1/96th of 1in). + + RelEm(f32), // Relative to the font-size of the element. + RelPct(f32), // Percentage relative to the parent element. + RelRem(f32), // Relative to font-size of the root element. + RelVh(f32), // Relative to 1% of the height of the viewport. + RelVw(f32), // Relative to 1% of the value of the viewport. } -impl UnitValue { - /// Indica si el valor es **medible**, incluyendo `Zero` sin unidad. - /// - /// Devuelve `false` para [`UnitValue::None`] y [`UnitValue::Auto`]. - /// - /// # Ejemplos - /// - /// ```rust - /// # use pagetop::prelude::*; - /// // Numéricos (incluido el cero sin unidad). - /// assert!(UnitValue::Zero.is_measurable()); - /// assert!(UnitValue::Px(0).is_measurable()); - /// assert!(UnitValue::Px(10).is_measurable()); - /// assert!(UnitValue::RelPct(33.0).is_measurable()); - /// // No numéricos. - /// assert!(!UnitValue::None.is_measurable()); - /// assert!(!UnitValue::Auto.is_measurable()); - /// ``` - #[inline] - pub const fn is_measurable(&self) -> bool { - !matches!(self, UnitValue::None | UnitValue::Auto) - } -} - -/// Formatea la unidad como cadena CSS. -/// -/// Reglas: -/// -/// - `None` - `""` (cadena vacía). -/// - `Auto` - `"auto"`. -/// - `Zero` - `"0"` (cero sin unidad). -/// - Absolutas - entero con su unidad: `Px(12)` a `"12px"`. -/// - Relativas - número en punto flotante; si es entero, se imprime sin decimales: -/// - `RelEm(2.0)` a `"2em"` -/// - `RelPct(33.5)` a `"33.5%"` #[rustfmt::skip] -impl fmt::Display for UnitValue { +impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - UnitValue::None => Ok(()), - UnitValue::Auto => f.write_str("auto"), - UnitValue::Zero => f.write_str("0"), - // Valor absoluto. - UnitValue::Cm(av) => write!(f, "{av}cm"), - UnitValue::In(av) => write!(f, "{av}in"), - UnitValue::Mm(av) => write!(f, "{av}mm"), - UnitValue::Pc(av) => write!(f, "{av}pc"), - UnitValue::Pt(av) => write!(f, "{av}pt"), - UnitValue::Px(av) => write!(f, "{av}px"), - // Valor relativo. - UnitValue::RelEm(rv) => write!(f, "{rv}em"), - UnitValue::RelRem(rv) => write!(f, "{rv}rem"), - UnitValue::RelPct(rv) => write!(f, "{rv}%"), - UnitValue::RelVh(rv) => write!(f, "{rv}vh"), - UnitValue::RelVw(rv) => write!(f, "{rv}vw"), + Value::None => write!(f, ""), + Value::Auto => write!(f, "auto"), + // Absolute value. + Value::Cm(av) => write!(f, "{av}cm"), + Value::In(av) => write!(f, "{av}in"), + Value::Mm(av) => write!(f, "{av}mm"), + Value::Pc(av) => write!(f, "{av}pc"), + Value::Pt(av) => write!(f, "{av}pt"), + Value::Px(av) => write!(f, "{av}px"), + // Relative value. + Value::RelEm(rv) => write!(f, "{rv}em"), + Value::RelPct(rv) => write!(f, "{rv}%"), + Value::RelRem(rv) => write!(f, "{rv}rem"), + Value::RelVh(rv) => write!(f, "{rv}vh"), + Value::RelVw(rv) => write!(f, "{rv}vw"), } } } - -/// Convierte una cadena a [`UnitValue`] siguiendo una gramática CSS acotada. -/// -/// ## Acepta -/// -/// - `""` para `UnitValue::None` -/// - `"auto"` -/// - **Cero sin unidad**: `"0"`, `"+0"`, `"-0"`, `"0.0"`, `"0."`, `".0"` para `UnitValue::Zero` -/// - Porcentaje: `"<n>%"` (p. ej., `"33%"`, `"33 %"`) -/// - Absolutas enteras: `"<entero><unidad>"`, p. ej., `"12px"`, `"-5pt"` -/// - Relativas decimales: `"<float><unidad>"`, p. ej., `"1.25rem"`, `"-0.5vh"`, `".5em"`, `"1.rem"` -/// -/// (Se toleran espacios entre número y unidad: `"12 px"`, `"1.5 rem"`). -/// -/// ## Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// use std::str::FromStr; -/// -/// assert_eq!(UnitValue::from_str("12px").unwrap(), UnitValue::Px(12)); -/// assert!(UnitValue::from_str("12").is_err()); -/// ``` -/// -/// ## Errores de interpretación -/// -/// - Falta la unidad cuando es necesaria (p. ej., `"12"`, excepto para el valor cero). -/// - Decimales en valores que deben ser absolutos (p. ej. `"1.5px"`). -/// - Unidades desconocidas (p. ej., `"10ch"`, no soportada aún). -/// - Notación científica o bases no decimales: `"1e3vw"`, `"0x10px"` (no soportadas). Los ceros a -/// la izquierda (p. ej. `"020px"`) se interpretan en **base 10** (`20px`). -/// -/// La comparación de la unidad es *case-insensitive*. -impl FromStr for UnitValue { - type Err = String; - - fn from_str(input: &str) -> Result<Self, Self::Err> { - let s = input.trim(); - if s.is_empty() { - return Ok(UnitValue::None); - } - if s.eq_ignore_ascii_case("auto") { - return Ok(UnitValue::Auto); - } - - match s.find(|c: char| c.is_ascii_alphabetic() || c == '%') { - None => { - let n: f32 = s - .parse() - .map_err(|e| format!("Invalid number `{s}`: {e}"))?; - if n == 0.0 { - Ok(UnitValue::Zero) - } else { - Err( - "Missing unit (expected one of cm,in,mm,pc,pt,px,em,rem,vh,vw, or %)" - .to_string(), - ) - } - } - Some(split_pos) => { - let (num_str, unit_str) = s.split_at(split_pos); - let u = unit_str.trim(); - let n = num_str.trim(); - - let parse_abs = |n_s: &str| -> Result<isize, String> { - n_s.parse::<isize>() - .map_err(|e| format!("Invalid integer `{n_s}`: {e}")) - }; - let parse_rel = |n_s: &str| -> Result<f32, String> { - n_s.parse::<f32>() - .map_err(|e| format!("Invalid float `{n_s}`: {e}")) - }; - - match u.to_ascii_lowercase().as_str() { - // Unidades absolutas. - "cm" => Ok(UnitValue::Cm(parse_abs(n)?)), - "in" => Ok(UnitValue::In(parse_abs(n)?)), - "mm" => Ok(UnitValue::Mm(parse_abs(n)?)), - "pc" => Ok(UnitValue::Pc(parse_abs(n)?)), - "pt" => Ok(UnitValue::Pt(parse_abs(n)?)), - "px" => Ok(UnitValue::Px(parse_abs(n)?)), - // Unidades relativas. - "em" => Ok(UnitValue::RelEm(parse_rel(n)?)), - "rem" => Ok(UnitValue::RelRem(parse_rel(n)?)), - "vh" => Ok(UnitValue::RelVh(parse_rel(n)?)), - "vw" => Ok(UnitValue::RelVw(parse_rel(n)?)), - // Porcentaje como unidad. - "%" => Ok(UnitValue::RelPct(parse_rel(n)?)), - // Unidad desconocida. - _ => Err(format!("Unknown unit: `{u}`")), - } - } - } - } -} - -/// Deserializa desde una cadena usando la misma gramática que [`FromStr`]. -/// -/// ### Ejemplo con `serde_json` -/// ```rust -/// # use pagetop::prelude::*; -/// use serde::Deserialize; -/// -/// #[derive(Deserialize)] -/// struct Style { width: UnitValue } -/// -/// // "{\"width\":\"12px\"}" deserializa como `Style { width: UnitValue::Px(12) }` -/// ``` -impl<'de> Deserialize<'de> for UnitValue { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - let raw = String::deserialize(deserializer)?; - raw.parse().map_err(serde::de::Error::custom) - } -} diff --git a/src/lib.rs b/src/lib.rs index a2683495..a5369718 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,168 +1,119 @@ -/*! -<div align="center"> - -<img src="https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/banner.png" /> - -<h1>PageTop</h1> - -<p>Un entorno para el desarrollo de soluciones web modulares, extensibles y configurables.</p> - -[![Doc API](https://img.shields.io/docsrs/pagetop?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop) -[![Crates.io](https://img.shields.io/crates/v/pagetop.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop) -[![Descargas](https://img.shields.io/crates/d/pagetop.svg?label=Descargas&style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop) -[![Licencia](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?label=Licencia&style=for-the-badge)](https://git.cillero.es/manuelcillero/pagetop#licencia) - -<br> -</div> - -PageTop reivindica la esencia de la web clásica usando [Rust](https://www.rust-lang.org/es) para la -creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y JavaScript. -Ofrece un conjunto de herramientas que los desarrolladores pueden implementar, extender o adaptar -según las necesidades de cada proyecto, incluyendo: - - * **Acciones** (*actions*): alteran la lógica interna de una funcionalidad interceptando su flujo - de ejecución. - * **Componentes** (*components*): encapsulan HTML, CSS y JavaScript en unidades funcionales, - configurables y reutilizables. - * **Extensiones** (*extensions*): añaden, extienden o personalizan funcionalidades usando las APIs - de PageTop o de terceros. - * **Temas** (*themes*): son extensiones que permiten modificar la apariencia de páginas y - componentes sin comprometer su funcionalidad. - - -# ⚡️ Guía rápida - -La aplicación más sencilla de PageTop se ve así: - -```rust,no_run -use pagetop::prelude::*; - -#[pagetop::main] -async fn main() -> std::io::Result<()> { - Application::new().run()?.await -} -``` - -Este código arranca el servidor de PageTop. Con la configuración por defecto, muestra una página de -bienvenida accesible desde un navegador local en la dirección `http://localhost:8080`. - -Para personalizar el servicio, se puede crear una extensión de PageTop de la siguiente manera: - -```rust,no_run -use pagetop::prelude::*; - -struct HelloWorld; - -impl Extension for HelloWorld { - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - scfg.route("/", service::web::get().to(hello_world)); - } -} - -async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { - Page::new(request) - .add_child(Html::with(|_| html! { h1 { "Hello World!" } })) - .render() -} - -#[pagetop::main] -async fn main() -> std::io::Result<()> { - Application::prepare(&HelloWorld).run()?.await -} -``` - -Este programa implementa una extensión llamada `HelloWorld` que sirve una página web en la ruta raíz -(`/`) mostrando el texto "Hello world!" dentro de un elemento HTML `<h1>`. - - -# 🧩 Gestión de Dependencias - -Los proyectos que utilizan PageTop gestionan las dependencias con `cargo`, como cualquier otro -proyecto en Rust. - -Sin embargo, es fundamental que cada extensión declare explícitamente sus -[dependencias](core::extension::Extension::dependencies), si las tiene, para que PageTop pueda -estructurar e inicializar la aplicación de forma modular. -*/ +//! <div align="center"> +//! +//! <img src="https://raw.githubusercontent.com/manuelcillero/pagetop/main/static/banner.png" /> +//! +//! <h1>PageTop</h1> +//! +//! <p>An opinionated web framework to build modular <em>Server-Side Rendering</em> web solutions.</p> +//! +//! [![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg?style=for-the-badge)](https://github.com/manuelcillero/pagetop#-license) +//! [![API Docs](https://img.shields.io/docsrs/pagetop?label=API%20Docs&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop) +//! [![Crates.io](https://img.shields.io/crates/v/pagetop.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop) +//! [![Downloads](https://img.shields.io/crates/d/pagetop.svg?style=for-the-badge&logo=transmission)](https://crates.io/crates/pagetop) +//! +//! <br> +//! </div> +//! +//! The `PageTop` core API provides a comprehensive toolkit for extending its functionalities to +//! specific requirements and application scenarios through actions, components, packages, and +//! themes: +//! +//! * **Actions** serve as a mechanism to customize `PageTop`'s internal behavior by intercepting +//! its execution flow. +//! * **Components** encapsulate HTML, CSS, and JavaScript into functional, configurable, and +//! well-defined units. +//! * **Packages** extend or customize existing functionality by interacting with `PageTop` APIs +//! or third-party package APIs. +//! * **Themes** enable developers to alter the appearance of pages and components without +//! affecting their functionality. +//! +//! # ⚡️ Quick start +//! +//! ```rust +//! use pagetop::prelude::*; +//! +//! struct HelloWorld; +//! +//! impl PackageTrait for HelloWorld { +//! fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { +//! scfg.route("/", service::web::get().to(hello_world)); +//! } +//! } +//! +//! async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> { +//! Page::new(request) +//! .with_component(Html::with(html! { h1 { "Hello World!" } })) +//! .render() +//! } +//! +//! #[pagetop::main] +//! async fn main() -> std::io::Result<()> { +//! Application::prepare(&HelloWorld).run()?.await +//! } +//! ``` +//! This program implements a package named `HelloWorld` with one service that returns a web page +//! that greets the world whenever it is accessed from the browser at `http://localhost:8088` (using +//! the [default configuration settings](`global::Server`)). You can find this code in the `PageTop` +//! [examples repository](https://github.com/manuelcillero/pagetop/tree/latest/examples). +//! +//! # 🧩 Dependency Management +//! +//! Projects leveraging `PageTop` will use `cargo` to resolve dependencies, similar to any other +//! Rust project. +//! +//! Nevertheless, it’s crucial that each package explicitly declares its +//! [dependencies](core::package::PackageTrait#method.dependencies), if any, to assist `PageTop` in +//! structuring and initializing the application in a modular fashion. +//! +//! # 🚧 Warning +//! +//! **`PageTop`** framework is currently in active development. The API is unstable and subject to +//! frequent changes. Production use is not recommended until version **0.1.0**. #![cfg_attr(docsrs, feature(doc_cfg))] -#![doc( - html_favicon_url = "https://git.cillero.es/manuelcillero/pagetop/raw/branch/main/static/favicon.ico" -)] -// Alias para que las rutas absolutas `::pagetop::...` generadas por las macros funcionen en el -// propio *crate*, en *crates* externos y en *doctests*. -extern crate self as pagetop; +// RE-EXPORTED ************************************************************************************* -use std::collections::HashMap; -use std::ops::Deref; +pub use concat_string::concat_string; -// **< RE-EXPORTED >******************************************************************************** +/// Enables flexible identifier concatenation in macros, allowing new items with pasted identifiers. +pub use paste::paste; -pub use pagetop_macros::{builder_fn, html, main, test, AutoDefault}; +pub use pagetop_macros::{fn_builder, html, main, test, AutoDefault, ComponentClasses}; -pub use pagetop_statics::{resource, StaticResource}; +pub type StaticResources = std::collections::HashMap<&'static str, static_files::Resource>; -/// Contenedor para un conjunto de recursos embebidos. -#[derive(AutoDefault)] -pub struct StaticResources { - bundle: HashMap<&'static str, StaticResource>, -} +pub use std::any::TypeId; -impl StaticResources { - /// Crea un contenedor para un conjunto de recursos generado por `build.rs` (consultar - /// [`pagetop_build`](https://docs.rs/pagetop-build)). - pub fn new(bundle: HashMap<&'static str, StaticResource>) -> Self { - Self { bundle } - } -} - -impl Deref for StaticResources { - type Target = HashMap<&'static str, StaticResource>; - - fn deref(&self) -> &Self::Target { - &self.bundle - } -} - -/// Identificador único de un tipo estático durante la ejecución de la aplicación. -/// -/// **Nota:** El valor es único sólo dentro del proceso actual y cambia en cada compilación. -pub type UniqueId = std::any::TypeId; - -/// Representa el peso lógico de una instancia en una colección ordenada por pesos. -/// -/// Las instancias con pesos **más bajos**, incluyendo valores negativos (`-128..127`), se situarán -/// antes en la ordenación. pub type Weight = i8; -// **< API >**************************************************************************************** +// API ********************************************************************************************* -// Macros y funciones útiles. +// Useful functions and macros. pub mod util; -// Carga las opciones de configuración. +// Load configuration settings. pub mod config; -// Opciones de configuración globales. +// Global settings. pub mod global; -// Gestión de trazas y registro de eventos de la aplicación. +// Application tracing and event logging. pub mod trace; -// HTML en código. +// HTML in code. pub mod html; -// Localización. +// Localization. pub mod locale; -// Soporte a fechas y horas. +// Date and time handling. pub mod datetime; -// Tipos y funciones esenciales para crear acciones, componentes, extensiones y temas. -pub mod core; -// Respuestas a peticiones web en sus diferentes formatos. -pub mod response; -// Gestión del servidor y servicios web. +// Essential web framework. pub mod service; -// Reúne acciones, componentes, extensiones y temas predefinidos. +// Key types and functions for creating actions, components, packages, and themes. +pub mod core; +// Web request response variants. +pub mod response; +// Base actions, components, packages, and themes. pub mod base; -// Prepara y ejecuta la aplicación. +// Prepare and run the application. pub mod app; -// **< PRELUDE >************************************************************************************ +// The PageTop Prelude ***************************************************************************** pub mod prelude; diff --git a/src/locale.rs b/src/locale.rs index 5c000f7c..6cc79c9f 100644 --- a/src/locale.rs +++ b/src/locale.rs @@ -1,21 +1,19 @@ -//! Localización (L10n). +//! Localization (L10n). //! -//! PageTop utiliza las especificaciones de [Fluent](https://www.projectfluent.org/) para la -//! localización de aplicaciones, y aprovecha [fluent-templates](https://docs.rs/fluent-templates/) -//! para integrar los recursos de traducción directamente en el binario de la aplicación. +//! PageTop uses the [Fluent](https://www.projectfluent.org/) specifications for application +//! localization, leveraging the [fluent-templates](https://docs.rs/fluent-templates/) crate to +//! integrate translation resources directly into the application binary. //! -//! # Sintaxis Fluent (FTL) +//! # Fluent Syntax (FTL) //! -//! El formato empleado para describir los recursos de traducción se denomina -//! [FTL](https://www.projectfluent.org/fluent/guide/). Está diseñado para ser legible y expresivo, -//! permitiendo representar construcciones complejas del lenguaje natural como el género, el plural -//! o las conjugaciones verbales. +//! The format used to describe the translation resources used by Fluent is called +//! [FTL](https://www.projectfluent.org/fluent/guide/). FTL is designed to be both readable and +//! expressive, enabling complex natural language constructs like gender, plurals, and conjugations. //! -//! # Recursos Fluent +//! # Fluent Resources //! -//! Por defecto, las traducciones están en el directorio `src/locale`, con subdirectorios para cada -//! [Identificador de Idioma Unicode](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier) -//! válido. Podríamos tener una estructura como esta: +//! Localization resources are organized in the *src/locale* directory, with subdirectories for +//! each valid [Unicode Language Identifier](https://docs.rs/unic-langid/): //! //! ```text //! src/locale/ @@ -29,12 +27,12 @@ //! ├── es-MX/ //! │ ├── default.ftl //! │ └── main.ftl -//! └── fr-FR/ +//! └── fr/ //! ├── default.ftl //! └── main.ftl //! ``` //! -//! Ejemplo de un archivo en `src/locale/en-US/main.ftl`: +//! Example of a file *src/locale/en-US/main.ftl*: //! //! ```text //! hello-world = Hello world! @@ -50,10 +48,10 @@ //! }. //! ``` //! -//! Y su archivo equivalente para español en `src/locale/es-ES/main.ftl`: +//! Example of the equivalent file *src/locale/es-ES/main.ftl*: //! //! ```text -//! hello-world = ¡Hola, mundo! +//! hello-world = Hola mundo! //! hello-user = ¡Hola, {$userName}! //! shared-photos = //! {$userName} {$photoCount -> @@ -66,218 +64,105 @@ //! }. //! ``` //! +//! # How to apply localization in your code //! -//! # Cómo aplicar la localización en tu código +//! Once you have created your FTL resource directory, use the +//! [`include_locales!`](crate::include_locales) macro to integrate them into your module or +//! application. If your resources are located in the `"src/locale"` directory, simply declare: //! -//! Una vez creado el directorio con los recursos FTL, basta con usar la macro -//! [`include_locales!`](crate::include_locales) para integrarlos en la aplicación. +//! ``` +//! use pagetop::prelude::*; //! -//! Si los recursos se encuentran en el directorio por defecto `src/locale` del *crate*, sólo hay -//! que declarar: -//! -//! ```rust -//! # use pagetop::prelude::*; //! include_locales!(LOCALES_SAMPLE); //! ``` //! -//! Si están ubicados en otro directorio, se puede usar la forma: +//! But if they are in another directory, then you can use: //! -//! ```rust,ignore -//! include_locales!(LOCALES_SAMPLE from "ruta/a/las/traducciones"); //! ``` +//! use pagetop::prelude::*; //! -//! Y *voilà*, sólo queda operar con los idiomas soportados por PageTop usando [`LangMatch`] y -//! traducir textos con [`L10n`]. +//! include_locales!(LOCALES_SAMPLE from "path/to/locale"); +//! ``` use crate::html::{Markup, PreEscaped}; -use crate::{global, hm, AutoDefault}; +use crate::{global, kv, AutoDefault}; pub use fluent_templates; pub use unic_langid::{CharacterDirection, LanguageIdentifier}; -use unic_langid::langid; - use fluent_templates::Loader; use fluent_templates::StaticLoader as Locales; -use std::borrow::Cow; +use unic_langid::langid; + use std::collections::HashMap; use std::sync::LazyLock; use std::fmt; -// Asocia cada identificador de idioma (como "en-US") con su respectivo [`LanguageIdentifier`] y la -// clave en *locale/.../languages.ftl* para obtener el nombre del idioma según la localización. -static LANGUAGES: LazyLock<HashMap<&str, (LanguageIdentifier, &str)>> = LazyLock::new(|| { - hm![ +/// A mapping between language codes (e.g., "en-US") and their corresponding [`LanguageIdentifier`] +/// and locale key names. +static LANGUAGES: LazyLock<HashMap<String, (LanguageIdentifier, &str)>> = LazyLock::new(|| { + kv![ "en" => ( langid!("en-US"), "english" ), - "en-gb" => ( langid!("en-GB"), "english_british" ), - "en-us" => ( langid!("en-US"), "english_united_states" ), + "en-GB" => ( langid!("en-GB"), "english_british" ), + "en-US" => ( langid!("en-US"), "english_united_states" ), "es" => ( langid!("es-ES"), "spanish" ), - "es-es" => ( langid!("es-ES"), "spanish_spain" ), + "es-ES" => ( langid!("es-ES"), "spanish_spain" ), ] }); -// Identificador de idioma de **respaldo** (predefinido a `en-US`). -// -// Se usa cuando el valor del identificador de idioma en las traducciones no corresponde con ningún -// idioma soportado por la aplicación. -pub(crate) static FALLBACK_LANGID: LazyLock<LanguageIdentifier> = - LazyLock::new(|| langid!("en-US")); +pub static FALLBACK_LANGID: LazyLock<LanguageIdentifier> = LazyLock::new(|| langid!("en-US")); -// Identificador de idioma **por defecto** para la aplicación. -// -// Se resuelve a partir de [`global::SETTINGS.app.language`](global::SETTINGS). Si el identificador -// de idioma no es válido o no está disponible, se usa [`FALLBACK_LANGID`]. -pub(crate) static DEFAULT_LANGID: LazyLock<Option<&LanguageIdentifier>> = - LazyLock::new(|| LangMatch::resolve(&global::SETTINGS.app.language).as_option()); +/// Sets the application's default +/// [Unicode Language Identifier](https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier) +/// through `SETTINGS.app.language`. +pub static DEFAULT_LANGID: LazyLock<&LanguageIdentifier> = + LazyLock::new(|| langid_for(&global::SETTINGS.app.language).unwrap_or(&FALLBACK_LANGID)); -/// Representa la fuente de idioma (`LanguageIdentifier`) asociada a un recurso. -/// -/// Este *trait* permite que distintas estructuras expongan su fuente de idioma de forma uniforme. -pub trait LangId { - /// Devuelve el identificador de idioma asociado al recurso. - fn langid(&self) -> &'static LanguageIdentifier; +pub enum LangError { + EmptyLang, + UnknownLang(String), } -/// Operaciones con los idiomas soportados por PageTop. -/// -/// Utiliza [`LangMatch`] para transformar un identificador de idioma en un [`LanguageIdentifier`] -/// soportado por PageTop. -/// -/// # Ejemplos -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Coincidencia exacta. -/// let lang = LangMatch::resolve("es-ES"); -/// assert_eq!(lang.langid().to_string(), "es-ES"); -/// -/// // Coincidencia parcial (retrocede al idioma base si no hay variante regional). -/// let lang = LangMatch::resolve("es-EC"); -/// assert_eq!(lang.langid().to_string(), "es-ES"); // Porque "es-EC" no está soportado. -/// -/// // Idioma no especificado. -/// let lang = LangMatch::resolve(""); -/// assert_eq!(lang, LangMatch::Unspecified); -/// -/// // Idioma no soportado. -/// let lang = LangMatch::resolve("ja-JP"); -/// assert_eq!(lang, LangMatch::Unsupported("ja-JP".to_string())); -/// ``` -/// -/// Con la siguiente instrucción siempre se obtiene un [`LanguageIdentifier`] válido, ya sea porque -/// resuelve un idioma soportado o porque se aplica el idioma por defecto o, en último caso, el de -/// respaldo ("en-US"): -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Idioma por defecto o de respaldo si no resuelve. -/// let lang = LangMatch::resolve("it-IT"); -/// let langid = lang.langid(); -/// ``` -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum LangMatch { - /// Cuando el identificador de idioma es una cadena vacía. - Unspecified, - /// Si encuentra un [`LanguageIdentifier`] en la lista de idiomas soportados por PageTop que - /// coincide exactamente con el identificador de idioma (p. ej. "es-ES"), o con el identificador - /// del idioma base (p. ej. "es"). - Found(&'static LanguageIdentifier), - /// Si el identificador de idioma no está entre los soportados por PageTop. - Unsupported(String), -} - -impl Default for LangMatch { - /// Resuelve al idioma por defecto y, si no está disponible, al idioma de respaldo ("en-US"). - fn default() -> Self { - LangMatch::Found(DEFAULT_LANGID.unwrap_or(&FALLBACK_LANGID)) - } -} - -impl LangMatch { - /// Resuelve `language` y devuelve la variante [`LangMatch`] apropiada. - pub fn resolve(language: impl AsRef<str>) -> Self { - let language = language.as_ref().trim(); - - // Rechaza cadenas vacías. - if language.is_empty() { - return Self::Unspecified; - } - - // Intenta aplicar coincidencia exacta con el código completo (p. ej. "es-MX"). - let lang = language.to_ascii_lowercase(); - if let Some(langid) = LANGUAGES.get(lang.as_str()).map(|(langid, _)| langid) { - return Self::Found(langid); - } - - // Si la variante regional no existe, retrocede al idioma base (p. ej. "es"). - if let Some((base_lang, _)) = lang.split_once('-') { - if let Some(langid) = LANGUAGES.get(base_lang).map(|(langid, _)| langid) { - return Self::Found(langid); - } - } - - // En caso contrario, indica que el idioma no está soportado. - Self::Unsupported(language.to_string()) - } - - /// Devuelve el [`LanguageIdentifier`] si el idioma fue reconocido. - /// - /// Solo retorna `Some` si la variante es [`LangMatch::Found`]. En cualquier otro caso (por - /// ejemplo, si el identificador es vacío o no está soportado), devuelve `None`. - /// - /// Este método es útil cuando se desea acceder directamente al idioma reconocido sin aplicar el - /// idioma por defecto ni el de respaldo. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let lang = LangMatch::resolve("es-ES").as_option(); - /// assert_eq!(lang.unwrap().to_string(), "es-ES"); - /// - /// let lang = LangMatch::resolve("ja-JP").as_option(); - /// assert!(lang.is_none()); - /// ``` - #[inline] - pub fn as_option(&self) -> Option<&'static LanguageIdentifier> { +impl fmt::Display for LangError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - LangMatch::Found(l) => Some(l), - _ => None, + LangError::EmptyLang => write!(f, "The language identifier is empty."), + LangError::UnknownLang(lang) => write!(f, "Unknown language identifier: {lang}"), } } } -/// Permite a [`LangMatch`] actuar como proveedor de idioma. -/// -/// Devuelve el [`LanguageIdentifier`] si la variante es [`LangMatch::Found`]; en caso contrario, -/// devuelve el idioma por defecto de la aplicación y, si tampoco está disponible, el idioma de -/// respaldo ("en-US"). -/// -/// Resulta útil para usar un valor de [`LangMatch`] como fuente de traducción en [`L10n::lookup()`] -/// o [`L10n::using()`]. -impl LangId for LangMatch { - fn langid(&self) -> &'static LanguageIdentifier { - match self { - LangMatch::Found(l) => l, - _ => DEFAULT_LANGID.unwrap_or(&FALLBACK_LANGID), +pub fn langid_for(language: impl Into<String>) -> Result<&'static LanguageIdentifier, LangError> { + let language = language.into(); + if language.is_empty() { + return Err(LangError::EmptyLang); + } + // Attempt to match the full language code (e.g., "es-MX"). + if let Some(langid) = LANGUAGES.get(&language).map(|(langid, _)| langid) { + return Ok(langid); + } + // Fallback to the base language if no sublocale is found (e.g., "es"). + if let Some((base_lang, _)) = language.split_once('-') { + if let Some(langid) = LANGUAGES.get(base_lang).map(|(langid, _)| langid) { + return Ok(langid); } } + Err(LangError::UnknownLang(language)) } #[macro_export] -/// Incluye un conjunto de recursos **Fluent** y textos de traducción propios. +/// Defines a set of localization elements and local translation texts, removing Unicode isolating +/// marks around arguments to improve readability and compatibility in certain rendering contexts. macro_rules! include_locales { - // Se desactiva la inserción de marcas de aislamiento Unicode (FSI/PDI) en los argumentos para - // mejorar la legibilidad y la compatibilidad en ciertos contextos de renderizado. ( $LOCALES:ident $(, $core_locales:literal)? ) => { $crate::locale::fluent_templates::static_loader! { static $LOCALES = { locales: "src/locale", $( core_locales: $core_locales, )? fallback_language: "en-US", - // Elimina marcas de aislamiento Unicode en los argumentos. + // Removes unicode isolating marks around arguments. customise: |bundle| bundle.set_use_isolating(false), }; } @@ -288,7 +173,7 @@ macro_rules! include_locales { locales: $dir_locales, $( core_locales: $core_locales, )? fallback_language: "en-US", - // Elimina marcas de aislamiento Unicode en los argumentos. + // Removes unicode isolating marks around arguments. customise: |bundle| bundle.set_use_isolating(false), }; } @@ -297,49 +182,15 @@ macro_rules! include_locales { include_locales!(LOCALES_PAGETOP); -// Operación de localización a realizar. -// -// * `None` - No se aplica ninguna localización. -// * `Text` - Con una cadena literal que se devolverá tal cual. -// * `Translate` - Con la clave a resolver en el `Locales` indicado. -#[derive(AutoDefault, Clone, Debug)] +#[derive(AutoDefault)] enum L10nOp { #[default] None, - Text(Cow<'static, str>), - Translate(Cow<'static, str>), + Text(String), + Translate(String), } -/// Crea instancias para traducir textos localizados. -/// -/// Cada instancia puede representar: -/// -/// - Un texto puro (`n()`) que no requiere traducción. -/// - Una clave para traducir un texto de las traducciones predefinidas de PageTop (`l()`). -/// - Una clave para traducir de un conjunto concreto de traducciones (`t()`). -/// -/// # Ejemplo -/// -/// Los argumentos dinámicos se añaden con `with_arg()` o `with_args()`. -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Texto literal sin traducción. -/// let raw = L10n::n("© 2025 PageTop").get(); -/// -/// // Traducción simple con clave y argumentos. -/// let hello = L10n::l("greeting") -/// .with_arg("name", "Manuel") -/// .get(); -/// ``` -/// -/// También sirve para traducciones contra un conjunto de recursos concreto. -/// -/// ```rust,ignore -/// // Traducción con clave, conjunto de traducciones y fuente de idioma. -/// let bye = L10n::t("goodbye", &LOCALES_CUSTOM).lookup(&LangMatch::resolve("it")); -/// ``` -#[derive(AutoDefault, Clone)] +#[derive(AutoDefault)] pub struct L10n { op: L10nOp, #[default(&LOCALES_PAGETOP)] @@ -348,26 +199,21 @@ pub struct L10n { } impl L10n { - /// **n** = *“native”*. Crea una instancia con una cadena literal sin traducción. - pub fn n(text: impl Into<Cow<'static, str>>) -> Self { + pub fn n(text: impl Into<String>) -> Self { L10n { op: L10nOp::Text(text.into()), ..Default::default() } } - /// **l** = *“lookup”*. Crea una instancia para traducir usando una clave del conjunto de - /// traducciones predefinidas. - pub fn l(key: impl Into<Cow<'static, str>>) -> Self { + pub fn l(key: impl Into<String>) -> Self { L10n { op: L10nOp::Translate(key.into()), ..Default::default() } } - /// **t** = *“translate”*. Crea una instancia para traducir usando una clave de un conjunto de - /// traducciones específico. - pub fn t(key: impl Into<Cow<'static, str>>, locales: &'static Locales) -> Self { + pub fn t(key: impl Into<String>, locales: &'static Locales) -> Self { L10n { op: L10nOp::Translate(key.into()), locales, @@ -375,109 +221,61 @@ impl L10n { } } - /// Añade un argumento `{$arg}` => `value` a la traducción. pub fn with_arg(mut self, arg: impl Into<String>, value: impl Into<String>) -> Self { self.args.insert(arg.into(), value.into()); self } - /// Añade varios argumentos a la traducción de una sola vez (p. ej. usando la macro [`hm!`], - /// también vec![("k", "v")], incluso un array de duplas u otras colecciones). - pub fn with_args<I, K, V>(mut self, args: I) -> Self - where - I: IntoIterator<Item = (K, V)>, - K: Into<String>, - V: Into<String>, - { - self.args - .extend(args.into_iter().map(|(k, v)| (k.into(), v.into()))); + pub fn with_args(mut self, args: HashMap<String, String>) -> Self { + for (k, v) in args { + self.args.insert(k, v); + } self } - /// Resuelve la traducción usando el idioma por defecto o, si no procede, el de respaldo de la - /// aplicación. - /// - /// Devuelve `None` si no aplica o no encuentra una traducción válida. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let text = L10n::l("greeting").with_arg("name", "Manuel").get(); - /// ``` pub fn get(&self) -> Option<String> { - self.lookup(&LangMatch::default()) + self.using(&DEFAULT_LANGID) } - /// Resuelve la traducción usando la fuente de idioma proporcionada. - /// - /// Devuelve `None` si no aplica o no encuentra una traducción válida. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// struct ResourceLang; - /// - /// impl LangId for ResourceLang { - /// fn langid(&self) -> &'static LanguageIdentifier { - /// LangMatch::resolve("es-MX").langid() - /// } - /// } - /// - /// let r = ResourceLang; - /// let text = L10n::l("greeting").with_arg("name", "Usuario").lookup(&r); - /// ``` - pub fn lookup(&self, language: &impl LangId) -> Option<String> { + pub fn using(&self, langid: &LanguageIdentifier) -> Option<String> { match &self.op { L10nOp::None => None, - L10nOp::Text(text) => Some(text.clone().into_owned()), + L10nOp::Text(text) => Some(text.to_owned()), L10nOp::Translate(key) => { if self.args.is_empty() { - self.locales.try_lookup(language.langid(), key.as_ref()) + self.locales.try_lookup(langid, key) } else { self.locales.try_lookup_with_args( - language.langid(), - key.as_ref(), - &self - .args - .iter() - .map(|(k, v)| (Cow::Owned(k.clone()), v.clone().into())) - .collect::<HashMap<_, _>>(), + langid, + key, + &self.args.iter().fold(HashMap::new(), |mut args, (k, v)| { + args.insert(k.to_string(), v.to_owned().into()); + args + }), ) } } } } - /// Traduce el texto y lo devuelve como [`Markup`] usando la fuente de idioma proporcionada. - /// - /// Si no se encuentra una traducción válida, devuelve una cadena vacía. - /// - /// # Ejemplo - /// - /// ```rust - /// # use pagetop::prelude::*; - /// let html = L10n::l("welcome.message").using(&LangMatch::resolve("es")); - /// ``` - pub fn using(&self, language: &impl LangId) -> Markup { - PreEscaped(self.lookup(language).unwrap_or_default()) + /// Escapes translated text using the default language identifier. + pub fn markup(&self) -> Markup { + PreEscaped(self.get().unwrap_or_default()) } - /// **Obsoleto desde la versión 0.4.0**: usar [`using()`](Self::using) en su lugar. - #[deprecated(since = "0.4.0", note = "Use `using()` instead")] - pub fn to_markup(&self, language: &impl LangId) -> Markup { - self.using(language) + /// Escapes translated text using the specified language identifier. + pub fn escaped(&self, langid: &LanguageIdentifier) -> Markup { + PreEscaped(self.using(langid).unwrap_or_default()) } } -impl fmt::Debug for L10n { +impl fmt::Display for L10n { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("L10n") - .field("op", &self.op) - .field("args", &self.args) - // No se puede mostrar `locales`. Se representa con un texto fijo. - .field("locales", &"<StaticLoader>") - .finish() + let content = match &self.op { + L10nOp::None => "".to_string(), + L10nOp::Text(text) => text.clone(), + L10nOp::Translate(key) => self.get().unwrap_or_else(|| format!("No <{}>", key)), + }; + write!(f, "{content}") } } diff --git a/src/locale/en-US/base.ftl b/src/locale/en-US/base.ftl deleted file mode 100644 index 76baa120..00000000 --- a/src/locale/en-US/base.ftl +++ /dev/null @@ -1,17 +0,0 @@ -# Intro component. -intro_default_title = Hello, world! -intro_default_slogan = Discover⚡{ $app } -intro_default_button = A web solution powered by <strong>PageTop</strong> - -intro_pagetop_label = PageTop version on Crates.io -intro_release_label = Release date -intro_license_label = License - -intro_text1 = PageTop is <strong>an opinionated Rust web development framework</strong> designed to build modular, extensible, and configurable web solutions. -intro_text2 = PageTop brings back the essence of the classic web, renders on the server (SSR) and uses <em>HTML-first</em> components, CSS and JavaScript, <strong>with the performance and security of Rust</strong>. - -intro_code = Code -intro_have_fun = Coding is creating - -# PoweredBy component. -poweredby_pagetop = Powered by { $pagetop_link } diff --git a/src/locale/en-US/sample.ftl b/src/locale/en-US/sample.ftl deleted file mode 100644 index ae5b9a7f..00000000 --- a/src/locale/en-US/sample.ftl +++ /dev/null @@ -1,24 +0,0 @@ -# menus.rs -sample_menus_item_label = Label -sample_menus_item_link = Link -sample_menus_item_blank = External link -sample_menus_item_disabled = Disabled link - -sample_menus_test_title = Dropdown - -sample_menus_dev_header = Intro -sample_menus_dev_getting_started = Getting started -sample_menus_dev_guides = Development guides -sample_menus_dev_forum = Developers forum - -sample_menus_sdk_header = Software Development Kits -sample_menus_sdk_rust = SDKs Rust -sample_menus_sdk_js = SDKs JavaScript -sample_menus_sdk_python = SDKs Python - -sample_menus_plugin_header = Plugins -sample_menus_plugin_auth = Rust Plugin Auth -sample_menus_plugin_cache = Rust Plugin Cache - -sample_menus_item_sign_up = Sign up -sample_menus_item_login = Login diff --git a/src/locale/en-US/test.ftl b/src/locale/en-US/test.ftl deleted file mode 100644 index 3c317fcb..00000000 --- a/src/locale/en-US/test.ftl +++ /dev/null @@ -1,11 +0,0 @@ -test-hello-world = Hello world! -test-hello-user = Hello, { $userName }! -test-shared-photos = - { $userName } { $photoCount -> - [one] added a new photo - *[other] added { $photoCount } new photos - } of { $userGender -> - [male] him and his family - [female] her and her family - *[other] their family - }. diff --git a/src/locale/en-US/theme.ftl b/src/locale/en-US/theme.ftl index f766766d..fd7f228d 100644 --- a/src/locale/en-US/theme.ftl +++ b/src/locale/en-US/theme.ftl @@ -1,9 +1 @@ -# Regions. -region_header = Header -region_content = Content -region_footer = Footer - -error403_notice = FORBIDDEN ACCESS -error404_notice = RESOURCE NOT FOUND - -pagetop_logo = PageTop Logo +content = Content diff --git a/src/locale/en-US/welcome.ftl b/src/locale/en-US/welcome.ftl index ec691d1b..fe45c852 100644 --- a/src/locale/en-US/welcome.ftl +++ b/src/locale/en-US/welcome.ftl @@ -1,12 +1,23 @@ -welcome_extension_name = Default Homepage -welcome_extension_description = Displays a default homepage when none is configured. +welcome_package_name = Default homepage +welcome_package_description = Displays a landing page when none is configured. -welcome_title = Welcome page +welcome_title = Hello world! -welcome_status_title = Status -welcome_status_1 = If you can see this page, it means the <strong>PageTop</strong> 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 <strong>contact the system administrator</strong>. +welcome_intro = Verifying the installation of { $app }. +welcome_powered = A web solution powered by { $pagetop }. -welcome_support_title = Support -welcome_support_1 = To report issues with the <strong>PageTop</strong> framework, use <a href="https://github.com/manuelcillero/pagetop/issues" target="_blank" rel="noopener noreferrer">GitHub</a>. Remember, before opening a new issue, review the existing ones to avoid duplicates. -welcome_support_2 = For issues specific to the application (<strong>{ $app }</strong>), please use its official repository or support channel. +welcome_page = Welcome Page +welcome_subtitle = Are you a { $app } user? +welcome_text1 = If you don't know what this page is about, this probably means that the site is either experiencing problems or is undergoing routine maintenance. +welcome_text2 = If the issue persists, please contact your system administrator for assistance. + +welcome_pagetop_title = About PageTop +welcome_pagetop_text1 = If you can read this page, it means that the <strong>PageTop</strong> server is working properly, but has not yet been configured. +welcome_pagetop_text2 = <strong>PageTop</strong> is a <a href="https://www.rust-lang.org" target="_blank">Rust</a>-based web development framework designed to create modular, extensible, and configurable web solutions. +welcome_pagetop_text3 = For detailed information, please visit the <a href="https://docs.rs/pagetop/latest/pagetop" target="_blank">official technical documentation</a>. + +welcome_issues_title = Reporting Issues +welcome_issues_text1 = To report any issues with <strong>PageTop</strong>, please use <a href="https://github.com/manuelcillero/pagetop/issues" target="_blank">GitHub</a>. However, check the existing error reports to avoid duplicates. +welcome_issues_text2 = For issues specific to <strong>{ $app }</strong>, please refer to its official repository or support channel, rather than directly to <strong>PageTop</strong>. + +welcome_have_fun = Coding is creating diff --git a/src/locale/es-ES/base.ftl b/src/locale/es-ES/base.ftl deleted file mode 100644 index 09867d13..00000000 --- a/src/locale/es-ES/base.ftl +++ /dev/null @@ -1,17 +0,0 @@ -# Intro component. -intro_default_title = ¡Hola, mundo! -intro_default_slogan = Descubre⚡{ $app } -intro_default_button = Una solución web creada con <strong>PageTop</strong> - -intro_pagetop_label = Versión de PageTop en Crates.io -intro_release_label = Lanzamiento -intro_license_label = Licencia - -intro_text1 = PageTop es un <strong>entorno de desarrollo web basado en Rust</strong>, pensado para construir soluciones web modulares, extensibles y configurables. -intro_text2 = PageTop reivindica la esencia de la web clásica, renderiza en el servidor (SSR) utilizando componentes <em>HTML-first</em>, CSS y JavaScript, <strong>con el rendimiento y la seguridad de Rust</strong>. - -intro_code = Código -intro_have_fun = Programar es crear - -# PoweredBy component. -poweredby_pagetop = Funciona con { $pagetop_link } diff --git a/src/locale/es-ES/sample.ftl b/src/locale/es-ES/sample.ftl deleted file mode 100644 index 65785972..00000000 --- a/src/locale/es-ES/sample.ftl +++ /dev/null @@ -1,24 +0,0 @@ -# menus.rs -sample_menus_item_label = Etiqueta -sample_menus_item_link = Enlace -sample_menus_item_blank = Enlace externo -sample_menus_item_disabled = Enlace deshabilitado - -sample_menus_test_title = Desplegable - -sample_menus_dev_header = Introducción -sample_menus_dev_getting_started = Primeros pasos -sample_menus_dev_guides = Guías de desarrollo -sample_menus_dev_forum = Foro de desarrolladores - -sample_menus_sdk_header = Kits de Desarrollo Software -sample_menus_sdk_rust = SDKs de Rust -sample_menus_sdk_js = SDKs de JavaScript -sample_menus_sdk_python = SDKs de Python - -sample_menus_plugin_header = Plugins -sample_menus_plugin_auth = Plugin Rust de autenticación -sample_menus_plugin_cache = Plugin Rust de caché - -sample_menus_item_sign_up = Registrarse -sample_menus_item_login = Iniciar sesión diff --git a/src/locale/es-ES/test.ftl b/src/locale/es-ES/test.ftl deleted file mode 100644 index 02cd22e3..00000000 --- a/src/locale/es-ES/test.ftl +++ /dev/null @@ -1,11 +0,0 @@ -test-hello-world = ¡Hola mundo! -test-hello-user = ¡Hola, { $userName }! -test-shared-photos = - { $userName } { $photoCount -> - [one] ha añadido una nueva foto - *[other] ha añadido { $photoCount } nuevas fotos - } de { $userGender -> - [male] él y su familia - [female] ella y su familia - *[other] la familia - }. diff --git a/src/locale/es-ES/theme.ftl b/src/locale/es-ES/theme.ftl index b8b91449..c2026c6f 100644 --- a/src/locale/es-ES/theme.ftl +++ b/src/locale/es-ES/theme.ftl @@ -1,9 +1 @@ -# Regions. -region_header = Cabecera -region_content = Contenido -region_footer = Pie de página - -error403_notice = ACCESO NO PERMITIDO -error404_notice = RECURSO NO ENCONTRADO - -pagetop_logo = Logotipo de PageTop +content = Contenido diff --git a/src/locale/es-ES/welcome.ftl b/src/locale/es-ES/welcome.ftl index a3e3a184..82dad6f0 100644 --- a/src/locale/es-ES/welcome.ftl +++ b/src/locale/es-ES/welcome.ftl @@ -1,12 +1,23 @@ -welcome_extension_name = Página de inicio predeterminada -welcome_extension_description = Muestra una página de inicio predeterminada cuando no hay ninguna configurada. +welcome_package_name = Página de inicio predeterminada +welcome_package_description = Muestra una página de inicio predeterminada cuando no hay ninguna configurada. -welcome_title = Página de bienvenida +welcome_title = ¡Hola mundo! -welcome_status_title = Estado -welcome_status_1 = Si puedes ver esta página, es porque el servidor de <strong>PageTop</strong> 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, <strong>contacta con el administrador del sistema</strong>. +welcome_intro = Verificando la instalación de { $app }. +welcome_powered = Una solución web creada con { $pagetop }. -welcome_support_title = Soporte -welcome_support_1 = Para comunicar incidencias del propio entorno <strong>PageTop</strong>, utiliza <a href="https://github.com/manuelcillero/pagetop/issues" target="_blank" rel="noopener noreferrer">GitHub</a>. Recuerda, antes de abrir una nueva incidencia, revisa las existentes para evitar duplicados. -welcome_support_2 = Para fallos específicos de la aplicación (<strong>{ $app }</strong>), utiliza su repositorio oficial o su canal de soporte. +welcome_page = Página de Bienvenida +welcome_subtitle = ¿Eres usuario de { $app }? +welcome_text1 = Si no sabes por qué se muestra esta página probablemente significa que el sitio está experimentando problemas o está pasando por un mantenimiento de rutina. +welcome_text2 = Si el problema persiste, por favor póngase en contacto con el administrador del sistema. + +welcome_pagetop_title = Sobre PageTop +welcome_pagetop_text1 = Si puedes leer esta página significa que el servidor <strong>PageTop</strong> funciona correctamente, pero aún no se ha configurado. +welcome_pagetop_text2 = <strong>PageTop</strong> es un entorno de desarrollo web basado en <a href="https://www.rust-lang.org/es" target="_blank">Rust</a>, diseñado para crear soluciones web modulares, extensibles y configurables. +welcome_pagetop_text3 = Para más información visita la <a href="https://docs.rs/pagetop/latest/pagetop" target="_blank">documentación técnica oficial</a>. + +welcome_issues_title = Informando Problemas +welcome_issues_text1 = Para comunicar cualquier problema con <strong>PageTop</strong> utiliza <a href="https://github.com/manuelcillero/pagetop/issues" target="_blank">GitHub</a>. No obstante, comprueba los informes de errores ya existentes para evitar duplicados. +welcome_issues_text2 = Si son fallos específicos de <strong>{ $app }</strong>, por favor acude a su repositorio oficial o canal de soporte, y no al de <strong>PageTop</strong> directamente. + +welcome_have_fun = Programar es crear diff --git a/src/prelude.rs b/src/prelude.rs index 47fbbf64..9d0d465f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,24 +1,23 @@ -//! *Prelude* de PageTop. +//! The `PageTop` Prelude. // RE-EXPORTED. -pub use crate::{builder_fn, html, main, test}; +pub use crate::{concat_string, fn_builder, html, main, paste, test}; -pub use crate::{AutoDefault, StaticResources, UniqueId, Weight}; +pub use crate::{AutoDefault, ComponentClasses, StaticResources, TypeId, Weight}; // MACROS. // crate::util -pub use crate::{hm, join, join_pair}; +pub use crate::kv; // crate::config pub use crate::include_config; // crate::locale pub use crate::include_locales; // crate::service -#[allow(deprecated)] -pub use crate::{include_files, include_files_service, static_files_service}; +pub use crate::{include_files, include_files_service}; // crate::core::action -pub use crate::actions_boxed; +pub use crate::actions; // API. @@ -28,27 +27,20 @@ pub use crate::global; pub use crate::trace; -// No se usa `pub use crate::html::*;` para evitar duplicar alias marcados como obsoletos -// (*deprecated*) porque han sido trasladados a `crate::core::component`. Cuando se retiren estos -// alias obsoletos se volverá a declarar como `pub use crate::html::*;`. -pub use crate::html::{ - display, html_private, Asset, Assets, AttrClasses, AttrId, AttrL10n, AttrName, AttrValue, - ClassesOp, Escaper, Favicon, JavaScript, Markup, PageTopSvg, PreEscaped, PrepareMarkup, - StyleSheet, TargetMedia, UnitValue, DOCTYPE, -}; +pub use crate::html::*; pub use crate::locale::*; pub use crate::datetime::*; pub use crate::service; -pub use crate::service::{HttpMessage, HttpRequest, HttpResponse}; +pub use crate::service::{HttpMessage, HttpRequest}; -pub use crate::core::{AnyCast, AnyInfo, TypeInfo}; +pub use crate::core::{AnyBase, AnyTo}; pub use crate::core::action::*; pub use crate::core::component::*; -pub use crate::core::extension::*; +pub use crate::core::package::*; pub use crate::core::theme::*; pub use crate::response::{json::*, page::*, redirect::*, ResponseError}; diff --git a/src/response.rs b/src/response.rs index 4078d420..e51974b1 100644 --- a/src/response.rs +++ b/src/response.rs @@ -1,4 +1,4 @@ -//! Respuestas a las peticiones web en sus diferentes formatos. +//! Web request response variants. pub use actix_web::ResponseError; diff --git a/src/response/json.rs b/src/response/json.rs index 0878999f..684d2c7b 100644 --- a/src/response/json.rs +++ b/src/response/json.rs @@ -1,39 +1 @@ -//! Extractor y generador de respuestas JSON (reexporta [`actix_web::web::Json`]). -//! -//! # Uso como extractor JSON -//! -//! Convierte automáticamente el cuerpo de una petición con `Content-Type: application/json` en un -//! tipo Rust fuertemente tipado, validando el formato y deserializando con *serde*. -//! -//! ```rust -//! # use pagetop::prelude::*; -//! #[derive(serde::Deserialize)] -//! struct NuevoUsuario { nombre: String, email: String } -//! -//! // Manejador configurado para la ruta POST "/usuarios". -//! async fn crear_usuario(payload: Json<NuevoUsuario>) -> HttpResponse { -//! // `payload` ya es `NuevoUsuario`; si la deserialización falla, -//! // devolverá automáticamente 400 Bad Request con un cuerpo JSON que describe el error. -//! HttpResponse::Ok().finish() -//! } -//! ``` -//! -//! # Uso como generador de respuestas JSON -//! -//! Serializa valores Rust a JSON y genera una respuesta HTTP con el encabezado apropiado -//! `application/json; charset=utf-8`, todo con una llamada compacta. -//! -//! ```rust -//! # use pagetop::prelude::*; -//! #[derive(serde::Serialize)] -//! struct Usuario { id: u32, nombre: String } -//! -//! async fn obtener_usuario() -> Json<Usuario> { -//! Json(Usuario { id: 1, nombre: "Ada".into() }) -//! } -//! ``` -//! -//! `Json<T>` funciona con cualquier tipo que implemente `serde::Serialize` (para respuestas) y/o -//! `serde::Deserialize` (para peticiones). - pub use actix_web::web::Json; diff --git a/src/response/page.rs b/src/response/page.rs index 7d7789d4..340bd0c7 100644 --- a/src/response/page.rs +++ b/src/response/page.rs @@ -4,211 +4,177 @@ pub use error::ErrorPage; pub use actix_web::Result as ResultPage; use crate::base::action; -use crate::base::component::Region; -use crate::core::component::{Child, ChildOp, Component, ComponentRender}; -use crate::core::component::{Context, ContextOp, Contextual}; -use crate::core::theme::ThemeRef; +use crate::core::component::{AssetsOp, Context}; +use crate::core::component::{ChildComponent, ChildOp, ComponentTrait}; +use crate::fn_builder; use crate::html::{html, Markup, DOCTYPE}; -use crate::html::{Assets, Favicon, JavaScript, StyleSheet}; -use crate::html::{AttrClasses, ClassesOp}; -use crate::html::{AttrId, AttrL10n}; -use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier}; +use crate::html::{ClassesOp, OptionClasses, OptionId, OptionTranslated}; +use crate::locale::L10n; use crate::service::HttpRequest; -use crate::{builder_fn, AutoDefault}; -/// Representa una página HTML completa lista para renderizar. -/// -/// Una instancia de `Page` se compone dinámicamente permitiendo establecer título, descripción, -/// regiones donde disponer los componentes, atributos de `<body>` y otros aspectos del contexto de -/// renderizado. +use unic_langid::CharacterDirection; + #[rustfmt::skip] -#[derive(AutoDefault)] pub struct Page { - title : AttrL10n, - description : AttrL10n, + title : OptionTranslated, + description : OptionTranslated, metadata : Vec<(&'static str, &'static str)>, properties : Vec<(&'static str, &'static str)>, - body_id : AttrId, - body_classes: AttrClasses, context : Context, + body_id : OptionId, + body_classes: OptionClasses, + body_skip_to: OptionId, } impl Page { - /// Crea una nueva instancia de página. - /// - /// La solicitud HTTP se guardará en el contexto de renderizado de la página para poder ser - /// recuperada por los componentes si es necesario. #[rustfmt::skip] pub fn new(request: HttpRequest) -> Self { Page { - title : AttrL10n::default(), - description : AttrL10n::default(), + title : OptionTranslated::default(), + description : OptionTranslated::default(), metadata : Vec::default(), properties : Vec::default(), - body_id : AttrId::default(), - body_classes: AttrClasses::default(), - context : Context::new(Some(request)), + context : Context::new(request), + body_id : OptionId::default(), + body_classes: OptionClasses::default(), + body_skip_to: OptionId::default(), } } - // **< Page BUILDER >*************************************************************************** + // Page BUILDER. - /// Establece el título de la página como un valor traducible. - #[builder_fn] - pub fn with_title(mut self, title: L10n) -> Self { - self.title.alter_value(title); + #[fn_builder] + pub fn set_title(&mut self, title: L10n) -> &mut Self { + self.title.set_value(title); self } - /// Establece la descripción de la página como un valor traducible. - #[builder_fn] - pub fn with_description(mut self, description: L10n) -> Self { - self.description.alter_value(description); + #[fn_builder] + pub fn set_description(&mut self, description: L10n) -> &mut Self { + self.description.set_value(description); self } - /// Añade una entrada `<meta name="..." content="...">` al `<head>`. - #[builder_fn] - pub fn with_metadata(mut self, name: &'static str, content: &'static str) -> Self { + #[fn_builder] + pub fn set_metadata(&mut self, name: &'static str, content: &'static str) -> &mut Self { self.metadata.push((name, content)); self } - /// Añade una entrada `<meta property="..." content="...">` al `<head>`. - #[builder_fn] - pub fn with_property(mut self, property: &'static str, content: &'static str) -> Self { + #[fn_builder] + pub fn set_property(&mut self, property: &'static str, content: &'static str) -> &mut Self { self.metadata.push((property, content)); self } - /// Establece el atributo `id` del elemento `<body>`. - #[builder_fn] - pub fn with_body_id(mut self, id: impl AsRef<str>) -> Self { - self.body_id.alter_value(id); + #[fn_builder] + pub fn set_assets(&mut self, op: AssetsOp) -> &mut Self { + self.context.set_assets(op); self } - /// Modifica las clases CSS del elemento `<body>` con una operación sobre [`AttrClasses`]. - #[builder_fn] - pub fn with_body_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self { - self.body_classes.alter_value(op, classes); + #[fn_builder] + pub fn set_body_id(&mut self, id: impl Into<String>) -> &mut Self { + self.body_id.set_value(id); self } - /// **Obsoleto desde la versión 0.4.0**: usar [`add_child()`](Self::add_child) en su lugar. - #[deprecated(since = "0.4.0", note = "Use `add_child()` instead")] - pub fn with_component(self, component: impl Component) -> Self { - self.add_child(component) + #[fn_builder] + pub fn set_body_classes(&mut self, op: ClassesOp, classes: impl Into<String>) -> &mut Self { + self.body_classes.set_value(op, classes); + self } - /// **Obsoleto desde la versión 0.4.0**: usar [`add_child_in()`](Self::add_child_in) en su - /// lugar. - #[deprecated(since = "0.4.0", note = "Use `add_child_in()` instead")] - pub fn with_component_in(self, region_key: &'static str, component: impl Component) -> Self { - self.add_child_in(region_key, component) + #[fn_builder] + pub fn set_body_skip_to(&mut self, id: impl Into<String>) -> &mut Self { + self.body_skip_to.set_value(id); + self } - /// Añade un componente hijo a la región de contenido por defecto. - pub fn add_child(mut self, component: impl Component) -> Self { + #[fn_builder] + pub fn set_layout(&mut self, layout: &'static str) -> &mut Self { + self.context.set_assets(AssetsOp::Layout(layout)); + self + } + + #[fn_builder] + pub fn set_in_region(&mut self, region: &'static str, op: ChildOp) -> &mut Self { + self.context.set_in_region(region, op); + self + } + + pub fn with_component(mut self, component: impl ComponentTrait) -> Self { self.context - .alter_child_in(Region::DEFAULT, ChildOp::Add(Child::with(component))); + .set_in_region("content", ChildOp::Add(ChildComponent::with(component))); self } - /// Añade un componente hijo en la región `region_name` de la página. - pub fn add_child_in(mut self, region_name: &'static str, component: impl Component) -> Self { + pub fn with_component_in( + mut self, + region: &'static str, + component: impl ComponentTrait, + ) -> Self { self.context - .alter_child_in(region_name, ChildOp::Add(Child::with(component))); + .set_in_region(region, ChildOp::Add(ChildComponent::with(component))); self } - /// **Obsoleto desde la versión 0.4.0**: usar [`with_child_in()`](Self::with_child_in) en su - /// lugar. - #[deprecated(since = "0.4.0", note = "Use `with_child_in()` instead")] - pub fn with_child_in_region(mut self, region_key: &'static str, op: ChildOp) -> Self { - self.alter_child_in(region_key, op); - self - } + // Page GETTERS. - /// **Obsoleto desde la versión 0.4.0**: usar [`alter_child_in()`](Self::alter_child_in) en su - /// lugar. - #[deprecated(since = "0.4.0", note = "Use `alter_child_in()` instead")] - pub fn alter_child_in_region(&mut self, region_key: &'static str, op: ChildOp) -> &mut Self { - self.alter_child_in(region_key, op); - self - } - - // **< Page GETTERS >*************************************************************************** - - /// Devuelve el título traducido para el idioma de la página, si existe. pub fn title(&mut self) -> Option<String> { - self.title.lookup(&self.context) + self.title.using(self.context.langid()) } - /// Devuelve la descripción traducida para el idioma de la página, si existe. pub fn description(&mut self) -> Option<String> { - self.description.lookup(&self.context) + self.description.using(self.context.langid()) } - /// Devuelve la lista de metadatos `<meta name=...>`. pub fn metadata(&self) -> &Vec<(&str, &str)> { &self.metadata } - /// Devuelve la lista de propiedades `<meta property=...>`. pub fn properties(&self) -> &Vec<(&str, &str)> { &self.properties } - /// Devuelve el identificador del elemento `<body>`. - pub fn body_id(&self) -> &AttrId { - &self.body_id - } - - /// Devuelve las clases CSS del elemento `<body>`. - pub fn body_classes(&self) -> &AttrClasses { - &self.body_classes - } - - /// Devuelve una referencia mutable al [`Context`] de la página. - /// - /// El [`Context`] actúa como intermediario para muchos métodos de `Page` (idioma, tema, - /// *layout*, recursos, solicitud HTTP, etc.). Resulta especialmente útil cuando un componente - /// o un tema necesita recibir el contexto como parámetro. pub fn context(&mut self) -> &mut Context { &mut self.context } - // **< Page RENDER >**************************************************************************** + pub fn body_id(&self) -> &OptionId { + &self.body_id + } + + pub fn body_classes(&self) -> &OptionClasses { + &self.body_classes + } + + pub fn body_skip_to(&self) -> &OptionId { + &self.body_skip_to + } + + // Page RENDER. - /// Renderiza la página completa en formato HTML. - /// - /// Ejecuta las acciones correspondientes antes y después de renderizar el `<body>`, - /// así como del `<head>`, e inserta los atributos `lang` y `dir` en la etiqueta `<html>`. pub fn render(&mut self) -> ResultPage<Markup, ErrorPage> { - // Acciones específicas del tema antes de renderizar el <body>. - self.context.theme().before_render_page_body(self); + // Theme-specific operations before rendering the page body. + self.context.theme().before_render_body(self); - // Acciones de las extensiones antes de renderizar el <body>. + // Execute package actions before rendering the page body. action::page::BeforeRenderBody::dispatch(self); - // Renderiza el <body>. - let body = html! { - (Region::named(Region::PAGETOP).render(&mut self.context)) - (self.context.theme().render_page_body(self)) - (Region::named(Region::PAGEBOTTOM).render(&mut self.context)) - }; + // Render the page body. + let body = self.context.theme().render_body(self); - // Acciones específicas del tema después de renderizar el <body>. - self.context.theme().after_render_page_body(self); + // Theme-specific operations after rendering the page body. + self.context.theme().after_render_body(self); - // Acciones de las extensiones después de renderizar el <body>. + // Execute package actions after rendering the page body. action::page::AfterRenderBody::dispatch(self); - // Renderiza el <head>. - let head = self.context.theme().render_page_head(self); + // Render the page head. + let head = self.context.theme().render_head(self); - // Compone la página incluyendo los atributos de idioma y dirección del texto. + // Render the full page with language and direction attributes. let lang = &self.context.langid().language; let dir = match self.context.langid().character_direction() { CharacterDirection::LTR => "ltr", @@ -218,101 +184,9 @@ impl Page { Ok(html! { (DOCTYPE) html lang=(lang) dir=(dir) { - head { - (head) - } - body id=[self.body_id().get()] class=[self.body_classes().get()] { - (body) - } + (head) + (body) } }) } } - -impl LangId for Page { - fn langid(&self) -> &'static LanguageIdentifier { - self.context.langid() - } -} - -impl Contextual for Page { - // **< Contextual BUILDER >********************************************************************* - - #[builder_fn] - fn with_request(mut self, request: Option<HttpRequest>) -> Self { - self.context.alter_request(request); - self - } - - #[builder_fn] - fn with_langid(mut self, language: &impl LangId) -> Self { - self.context.alter_langid(language); - self - } - - #[builder_fn] - fn with_theme(mut self, theme: ThemeRef) -> Self { - self.context.alter_theme(theme); - self - } - - #[builder_fn] - fn with_template(mut self, template_name: &'static str) -> Self { - self.context.alter_template(template_name); - self - } - - #[builder_fn] - fn with_param<T: 'static>(mut self, key: &'static str, value: T) -> Self { - self.context.alter_param(key, value); - self - } - - #[builder_fn] - fn with_assets(mut self, op: ContextOp) -> Self { - self.context.alter_assets(op); - self - } - - #[builder_fn] - fn with_child_in(mut self, region_name: impl AsRef<str>, op: ChildOp) -> Self { - self.context.alter_child_in(region_name, op); - self - } - - // **< Contextual GETTERS >********************************************************************* - - fn request(&self) -> Option<&HttpRequest> { - self.context.request() - } - - fn theme(&self) -> ThemeRef { - self.context.theme() - } - - fn template(&self) -> &str { - self.context.template() - } - - fn param<T: 'static>(&self, key: &'static str) -> Option<&T> { - self.context.param(key) - } - - fn favicon(&self) -> Option<&Favicon> { - self.context.favicon() - } - - fn stylesheets(&self) -> &Assets<StyleSheet> { - self.context.stylesheets() - } - - fn javascripts(&self) -> &Assets<JavaScript> { - self.context.javascripts() - } - - // **< Contextual HELPERS >********************************************************************* - - fn required_id<T>(&mut self, id: Option<String>) -> String { - self.context.required_id::<T>(id) - } -} diff --git a/src/response/page/error.rs b/src/response/page/error.rs index 7a590e6e..8493d58f 100644 --- a/src/response/page/error.rs +++ b/src/response/page/error.rs @@ -1,5 +1,4 @@ -use crate::base::component::{Html, Template}; -use crate::core::component::Contextual; +use crate::base::component::{Error403, Error404}; use crate::locale::L10n; use crate::response::ResponseError; use crate::service::http::{header::ContentType, StatusCode}; @@ -7,7 +6,7 @@ use crate::service::{HttpRequest, HttpResponse}; use super::Page; -use std::fmt::{self, Display}; +use std::fmt; #[derive(Debug)] pub enum ErrorPage { @@ -20,49 +19,47 @@ pub enum ErrorPage { Timeout(HttpRequest), } -impl Display for ErrorPage { +impl fmt::Display for ErrorPage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { // Error 304. - ErrorPage::NotModified(_) => f.write_str("Not Modified"), + ErrorPage::NotModified(_) => write!(f, "Not Modified"), // Error 400. - ErrorPage::BadRequest(_) => f.write_str("Bad Client Data"), + ErrorPage::BadRequest(_) => write!(f, "Bad Client Data"), // Error 403. ErrorPage::AccessDenied(request) => { - let mut error_page = Page::new(request.clone()); - let error403 = error_page.theme().error403(&mut error_page); + let error_page = Page::new(request.clone()); if let Ok(page) = error_page .with_title(L10n::n("Error FORBIDDEN")) - .with_template(Template::ERROR) - .add_child(Html::with(move |_| error403.clone())) + .with_layout("error") + .with_component(Error403) .render() { write!(f, "{}", page.into_string()) } else { - f.write_str("Access Denied") + write!(f, "Access Denied") } } // Error 404. ErrorPage::NotFound(request) => { - let mut error_page = Page::new(request.clone()); - let error404 = error_page.theme().error404(&mut error_page); + let error_page = Page::new(request.clone()); if let Ok(page) = error_page .with_title(L10n::n("Error RESOURCE NOT FOUND")) - .with_template(Template::ERROR) - .add_child(Html::with(move |_| error404.clone())) + .with_layout("error") + .with_component(Error404) .render() { write!(f, "{}", page.into_string()) } else { - f.write_str("Not Found") + write!(f, "Not Found") } } // Error 412. - ErrorPage::PreconditionFailed(_) => f.write_str("Precondition Failed"), + ErrorPage::PreconditionFailed(_) => write!(f, "Precondition Failed"), // Error 500. - ErrorPage::InternalError(_) => f.write_str("Internal Error"), + ErrorPage::InternalError(_) => write!(f, "Internal Error"), // Error 504. - ErrorPage::Timeout(_) => f.write_str("Timeout"), + ErrorPage::Timeout(_) => write!(f, "Timeout"), } } } diff --git a/src/response/redirect.rs b/src/response/redirect.rs index a3bec0cd..3b6f0646 100644 --- a/src/response/redirect.rs +++ b/src/response/redirect.rs @@ -1,98 +1,76 @@ -//! Realiza redirecciones HTTP. +//! Perform redirections in HTTP. //! -//! **La redirección de URL** (o *URL forwarding*) es una técnica que permite asignar más de una -//! dirección a un mismo recurso web. HTTP define respuestas ***HTTP redirect*** para ello (ver -//! *[Redirections in HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections)*). +//! **URL redirection**, also known as *URL forwarding*, is a technique to give more than one URL +//! address to a web resource. HTTP has a response called ***HTTP redirect*** for this operation +//! (see [Redirections in HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections)). //! -//! Existen varios tipos de redirección, agrupados en tres grandes categorías: +//! There are several types of redirects, sorted into three categories: //! -//! - **Redirecciones permanentes**. Se usan cuando el cambio de ubicación es definitivo. Indican -//! que la URL original ya no debe emplearse y que ha sido sustituida por la nueva. Los robots de -//! los buscadores, lectores RSS y otros *crawlers* suelen actualizar sus índices con la nueva -//! dirección. +//! * **Permanent redirections**. These redirections are meant to last forever. They imply that +//! the original URL should no longer be used, and replaced with the new one. Search engine +//! robots, RSS readers, and other crawlers will update the original URL for the resource. //! -//! - **Redirecciones temporales**. Se aplican cuando el recurso no puede servirse desde su -//! ubicación canónica pero sí desde otra provisional. En este caso los buscadores **no** deben -//! memorizar la URL alternativa. También son útiles para mostrar páginas de progreso al crear, -//! actualizar o eliminar recursos. +//! * **Temporary redirections**. Sometimes the requested resource can't be accessed from its +//! canonical location, but it can be accessed from another place. In this case, a temporary +//! redirect can be used. Search engine robots and other crawlers don't memorize the new, +//! temporary URL. Temporary redirections are also used when creating, updating, or deleting +//! resources, to show temporary progress pages. //! -//! - **Respuestas especiales**. +//! * **Special redirections**. use crate::service::HttpResponse; -/// Funciones predefinidas para generar respuestas HTTP de redirección. -/// -/// Ofrece atajos para construir respuestas con el código de estado apropiado, añade la cabecera -/// `Location` y la cierra con `.finish()`, evitando repetir la misma secuencia en cada controlador. pub struct Redirect; impl Redirect { - /// Redirección **permanente**. Código de estado **301**. El método GET se conserva tal cual. - /// Otros métodos pueden degradarse a GET. Es una redirección típica para la reorganización de - /// un sitio o aplicación web. - /// - /// Emplear cuando un recurso se ha movido de forma definitiva y la URL antigua debe dejar de - /// usarse. - #[must_use] + /// Permanent redirection. Status Code **301**. GET methods unchanged. Others may or may not be + /// changed to GET. Typical for reorganization of a website. pub fn moved(redirect_to_url: &str) -> HttpResponse { HttpResponse::MovedPermanently() .append_header(("Location", redirect_to_url)) .finish() } - /// Redirección **permanente**. Código de estado **308**. Mantiene método y cuerpo sin cambios. - /// - /// Indicada para reorganizaciones de un sitio o aplicación web en las que también existen - /// métodos distintos de GET (POST, PUT, ...) que no deben degradarse a GET. - #[must_use] + /// Permanent redirection. Status Code **308**. Method and body not changed. Typical for + /// reorganization of a website, with non-GET links/operations. pub fn permanent(redirect_to_url: &str) -> HttpResponse { HttpResponse::PermanentRedirect() .append_header(("Location", redirect_to_url)) .finish() } - /// Redirección **temporal**. Código de estado **302**. El método GET (y normalmente HEAD) se - /// mantiene tal cual. Otros métodos pueden degradarse a GET. - /// - /// Útil cuando un recurso está fuera de servicio de forma imprevista (mantenimiento breve, - /// sobrecarga, ...). - #[must_use] + /// Temporary redirection. Status Code **302**. GET methods unchanged. Others may or may not be + /// changed to GET. Used when the web page is temporarily unavailable for unforeseen reasons. pub fn found(redirect_to_url: &str) -> HttpResponse { HttpResponse::Found() .append_header(("Location", redirect_to_url)) .finish() } - /// Redirección **temporal**. Código de estado **303**. Método GET se mantiene tal cual. Los - /// demás métodos se cambian a GET (se pierde el cuerpo). - /// - /// Se usa típicamente tras un POST o PUT para aplicar el patrón *Post/Redirect/Get*, permite - /// recargar la página de resultados sin volver a ejecutar la operación. - #[must_use] + /// Temporary redirection. Status Code **303**. GET methods unchanged. Others changed to GET + /// (body lost). Used to redirect after a PUT or a POST, so that refreshing the result page + /// doesn't re-trigger the operation. pub fn see_other(redirect_to_url: &str) -> HttpResponse { HttpResponse::SeeOther() .append_header(("Location", redirect_to_url)) .finish() } - /// Redirección **temporal**. Código de estado **307**. Conserva método y cuerpo íntegros. - /// - /// Preferible a [`found`](Self::found) cuando el sitio expone operaciones diferentes de GET que - /// deben respetarse durante la redirección. - #[must_use] + /// Temporary redirection. Status Code **307**. Method and body not changed. The web page is + /// temporarily unavailable for unforeseen reasons. Better than [`found()`](Self::found) when + /// non-GET operations are available on the site. pub fn temporary(redirect_to_url: &str) -> HttpResponse { HttpResponse::TemporaryRedirect() .append_header(("Location", redirect_to_url)) .finish() } - /// Respuesta **especial**. Código de estado **304**. Se envía tras una petición condicional, - /// para indicar que la copia en caché sigue siendo válida y puede utilizarse, evitando - /// transferir de nuevo el recurso. - /// - /// No es una redirección, el cliente debe reutilizar su copia local. - #[must_use] - pub fn not_modified() -> HttpResponse { - HttpResponse::NotModified().finish() + /// Special redirection. Status Code **304**. Redirects a page to the locally cached copy (that + /// was stale). Sent for revalidated conditional requests. Indicates that the cached response is + /// still fresh and can be used. + pub fn not_modified(redirect_to_url: &str) -> HttpResponse { + HttpResponse::NotModified() + .append_header(("Location", redirect_to_url)) + .finish() } } diff --git a/src/service.rs b/src/service.rs index 1b2f766c..e6bc6373 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,4 +1,4 @@ -//! Gestión del servidor y servicios web (con [Actix Web](https://docs.rs/actix-web)). +//! Essential web framework ([actix-web](https://docs.rs/actix-web)). pub use actix_session::Session; pub use actix_web::body::BoxBody; @@ -6,71 +6,55 @@ pub use actix_web::dev::Server; pub use actix_web::dev::ServiceFactory as Factory; pub use actix_web::dev::ServiceRequest as Request; pub use actix_web::dev::ServiceResponse as Response; -pub use actix_web::{cookie, http, rt, web}; -pub use actix_web::{App, Error, HttpMessage, HttpRequest, HttpResponse, HttpServer}; +pub use actix_web::{cookie, get, http, rt, test, web}; +pub use actix_web::{App, Error, HttpMessage, HttpRequest, HttpResponse, HttpServer, Responder}; + pub use actix_web_files::Files as ActixFiles; +pub use actix_web_static_files::ResourceFiles; -pub use pagetop_statics::ResourceFiles; - -#[doc(hidden)] -pub use actix_web::test; - -/// **Obsoleto desde la versión 0.3.0**: usar [`static_files_service!`](crate::static_files_service) -/// en su lugar. -#[deprecated(since = "0.3.0", note = "Use `static_files_service!` instead")] #[macro_export] macro_rules! include_files { - // Forma 1: incluye un conjunto de recursos por nombre. ( $bundle:ident ) => { - $crate::util::paste! { + $crate::paste! { mod [<static_files_ $bundle>] { include!(concat!(env!("OUT_DIR"), "/", stringify!($bundle), ".rs")); } } }; - // Forma 2: asigna a una variable estática $STATIC un conjunto de recursos. - ( $STATIC:ident => $bundle:ident ) => { - $crate::util::paste! { + ( $bundle:ident => $STATIC:ident ) => { + $crate::paste! { mod [<static_files_ $bundle>] { include!(concat!(env!("OUT_DIR"), "/", stringify!($bundle), ".rs")); } - pub static $STATIC: std::sync::LazyLock<$crate::StaticResources> = - std::sync::LazyLock::new( - $crate::StaticResources::new([<static_files_ $bundle>]::$bundle) - ); + static $STATIC: std::sync::LazyLock<StaticResources> = std::sync::LazyLock::new( + [<static_files_ $bundle>]::$bundle + ); } }; } -/// **Obsoleto desde la versión 0.3.0**: usar [`static_files_service!`](crate::static_files_service) -/// en su lugar. -#[deprecated(since = "0.3.0", note = "Use `static_files_service!` instead")] #[macro_export] macro_rules! include_files_service { - ( $scfg:ident, $bundle:ident => $route:expr $(, [$root:expr, $relative:expr])? ) => {{ - $crate::util::paste! { - let span = $crate::trace::debug_span!("Configuring static files ", path = $route); + ( $scfg:ident, $bundle:ident => $path:expr $(, [$root:expr, $relative:expr])? ) => {{ + $crate::paste! { + let span = $crate::trace::debug_span!("Configuring static files ", path = $path); let _ = span.in_scope(|| { - // Determina si se sirven recursos embebidos (`true`) o desde disco (`false`). #[allow(unused_mut)] let mut serve_embedded:bool = true; $( - // Si `$root` y `$relative` no están vacíos, se comprueba la ruta absoluta. if !$root.is_empty() && !$relative.is_empty() { if let Ok(absolute) = $crate::util::absolute_dir($root, $relative) { - // Servimos directamente desde el sistema de ficheros. $scfg.service($crate::service::ActixFiles::new( - $route, + $path, absolute, ).show_files_listing()); serve_embedded = false } } )? - // Si no se localiza el directorio, se exponen entonces los recursos embebidos. if serve_embedded { $scfg.service($crate::service::ResourceFiles::new( - $route, + $path, [<static_files_ $bundle>]::$bundle(), )); } @@ -78,113 +62,3 @@ macro_rules! include_files_service { } }}; } - -/// Configura un servicio web para publicar archivos estáticos. -/// -/// La macro ofrece tres modos para configurar el servicio: -/// -/// - **Sistema de ficheros o embebido** (`[$path, $bundle]`): trata de servir los archivos desde -/// `$path`; y si es una cadena vacía, no existe o no es un directorio, entonces usará el conjunto -/// de recursos `$bundle` integrado en el binario. -/// - **Sólo embebido** (`[$bundle]`): sirve siempre desde el conjunto de recursos `$bundle` -/// integrado en el binario. -/// - **Sólo sistema de ficheros** (`$path`): sin usar corchetes, sirve únicamente desde el sistema -/// de ficheros si existe; en otro caso no registra el servicio. -/// -/// # Argumentos -/// -/// * `$scfg` - Instancia de [`ServiceConfig`](crate::service::web::ServiceConfig) donde aplicar la -/// configuración. -/// * `$path` - Ruta al directorio local con los archivos estáticos. -/// * `$bundle` - Nombre del conjunto de recursos que esta macro integra en el binario. -/// * `$route` - Ruta URL base desde la que se servirán los archivos. -/// -/// # Ejemplos -/// -/// ```rust,ignore -/// # use pagetop::prelude::*; -/// pub struct MyExtension; -/// -/// impl Extension for MyExtension { -/// fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { -/// // Forma 1) Sistema de ficheros o embebido. -/// static_files_service!(scfg, ["/var/www/static", assets] => "/public"); -/// -/// // Forma 2) Siempre embebido. -/// static_files_service!(scfg, [assets] => "/public"); -/// -/// // Forma 3) Sólo sistema de ficheros (no requiere `assets`). -/// static_files_service!(scfg, "/var/www/static" => "/public"); -/// } -/// } -/// ``` -#[macro_export] -macro_rules! static_files_service { - // Forma 1: primero intenta servir desde el sistema de ficheros; si falla, sirve embebido. - ( $scfg:ident, [$path:expr, $bundle:ident] => $route:expr $(,)? ) => {{ - let span = $crate::trace::debug_span!( - "Configuring static files (file system or embedded)", - mode = "fs_or_embedded", - route = $route, - ); - let _ = span.in_scope(|| { - let mut serve_embedded: bool = true; - if !::std::path::Path::new(&$path).as_os_str().is_empty() { - if let Ok(absolute) = $crate::util::resolve_absolute_dir($path) { - $scfg.service($crate::service::ActixFiles::new($route, absolute)); - serve_embedded = false; - } - } - if serve_embedded { - $crate::util::paste! { - mod [<static_files_ $bundle>] { - include!(concat!(env!("OUT_DIR"), "/", stringify!($bundle), ".rs")); - } - $scfg.service($crate::service::ResourceFiles::new( - $route, - [<static_files_ $bundle>]::$bundle(), - )); - } - } - }); - }}; - // Forma 2: sirve siempre embebido. - ( $scfg:ident, [$bundle:ident] => $route:expr $(,)? ) => {{ - let span = $crate::trace::debug_span!( - "Configuring static files (using embedded only)", - mode = "embedded", - route = $route, - ); - let _ = span.in_scope(|| { - $crate::util::paste! { - mod [<static_files_ $bundle>] { - include!(concat!(env!("OUT_DIR"), "/", stringify!($bundle), ".rs")); - } - $scfg.service($crate::service::ResourceFiles::new( - $route, - [<static_files_ $bundle>]::$bundle(), - )); - } - }); - }}; - // Forma 3: intenta servir desde el sistema de ficheros. - ( $scfg:ident, $path:expr => $route:expr $(,)? ) => {{ - let span = $crate::trace::debug_span!( - "Configuring static files (file system only)", - mode = "fs", - route = $route, - ); - let _ = span.in_scope(|| match $crate::util::resolve_absolute_dir($path) { - Ok(absolute) => { - $scfg.service($crate::service::ActixFiles::new($route, absolute)); - } - Err(e) => { - $crate::trace::warn!( - "Static dir not found or invalid for route `{}`: {:?} ({e})", - $route, - $path, - ); - } - }); - }}; -} diff --git a/src/trace.rs b/src/trace.rs index 12e428a8..6e42d790 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -1,17 +1,16 @@ -//! Gestión de trazas y registro de eventos de la aplicación. +//! Application tracing and event logging. //! -//! PageTop recopila información de diagnóstico de la aplicación de forma estructurada y basada en -//! eventos. +//! `PageTop` collects application diagnostic information in a structured and event-based manner. //! -//! En los sistemas asíncronos, interpretar los mensajes de log tradicionales suele volverse -//! complicado. Las tareas individuales se multiplexan en el mismo hilo y los eventos y registros -//! asociados se entremezclan, lo que dificulta seguir la secuencia lógica. +//! In asynchronous systems, interpreting traditional log messages often becomes complicated. +//! Individual tasks are multiplexed to the same thread, and associated events and log messages get +//! intermingled, making it difficult to follow the logical sequence. //! -//! PageTop usa [`tracing`](https://docs.rs/tracing) para registrar eventos estructurados y con -//! información adicional sobre la *temporalidad* y la *causalidad*. A diferencia de un mensaje de -//! log, un *span* (intervalo) tiene un momento de inicio y de fin, puede entrar y salir del flujo -//! de ejecución y puede existir dentro de un árbol anidado de *spans* similares. Además, estos -//! *spans* son estructurados, con la capacidad de registrar tipos de datos y mensajes de texto. +//! `PageTop` uses [`tracing`](https://docs.rs/tracing) to allow **applications** and **modules** to +//! log structured events with added information about *temporality* and *causality*. Unlike a log +//! message, a span has a start and end time, can enter and exit the execution flow, and can exist +//! within a nested tree of similar spans. Additionally, these spans are *structured*, with the +//! ability to record data types and text messages. use crate::global; @@ -23,26 +22,19 @@ use tracing_subscriber::EnvFilter; use std::sync::LazyLock; -/// Trazado y registro de eventos de la aplicación. +/// Application tracing and event logging. /// -/// Para aumentar el rendimiento, un hilo dedicado utiliza un sistema de escritura no bloqueante que -/// actúa de forma periódica en lugar de enviar cada traza o evento al instante. Si el programa -/// termina abruptamente (por ejemplo, debido a un `panic!` o a una llamada a `std::process::exit`), -/// es posible que algunas trazas o eventos no se envíen. +/// To increase performance, a dedicated thread uses a non-blocking writer system that acts +/// periodically instead of sending each trace or event instantly. If the program terminates +/// abruptly (e.g., due to a panic! or a `std::process::exit`), some traces or events might not be +/// sent. /// -/// Dado que las trazas o eventos registrados poco antes de un fallo suelen ser cruciales para -/// diagnosticar la causa, `Lazy<WorkerGuard>` garantiza que todos los registros almacenados se -/// envíen antes de finalizar la ejecución. +/// Since traces or events logged shortly before an application crash are often important for +/// diagnosing the cause of the failure, `Lazy<WorkerGuard>` ensures that all stored logs are sent +/// before terminating execution. + #[rustfmt::skip] pub(crate) static TRACING: LazyLock<WorkerGuard> = LazyLock::new(|| { - if !global::SETTINGS.log.enabled || cfg!(test) || cfg!(feature = "testing") { - // Tracing desactivado, se instala un subscriber nulo. - tracing::subscriber::set_global_default(tracing::subscriber::NoSubscriber::default()) - .expect("Failed to install global NoSubscriber (tracing disabled)"); - let (_, guard) = tracing_appender::non_blocking(std::io::sink()); - return guard; - } - let env_filter = EnvFilter::try_new(&global::SETTINGS.log.tracing) .unwrap_or_else(|_| EnvFilter::new("Info")); diff --git a/src/util.rs b/src/util.rs index 30b994f2..a34ee599 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,37 +1,156 @@ -//! Macros y funciones útiles. +//! Useful functions and macros. use crate::trace; -use std::env; use std::io; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -// **< MACROS INTEGRADAS >************************************************************************** +// USEFUL FUNCTIONS ******************************************************************************** -#[doc(hidden)] -pub use paste::paste; +pub enum TypeInfo { + FullName, + ShortName, + NameFrom(isize), + NameTo(isize), + PartialName(isize, isize), +} -#[doc(hidden)] -pub use concat_string::concat_string; +impl TypeInfo { + pub fn of<T: ?Sized>(&self) -> &'static str { + let type_name = std::any::type_name::<T>(); + match self { + TypeInfo::FullName => type_name, + TypeInfo::ShortName => Self::partial(type_name, -1, None), + TypeInfo::NameFrom(start) => Self::partial(type_name, *start, None), + TypeInfo::NameTo(end) => Self::partial(type_name, 0, Some(*end)), + TypeInfo::PartialName(start, end) => Self::partial(type_name, *start, Some(*end)), + } + } -pub use indoc::{concatdoc, formatdoc, indoc}; + fn partial(type_name: &'static str, start: isize, end: Option<isize>) -> &'static str { + let maxlen = type_name.len(); + let mut segments = Vec::new(); + let mut segment_start = 0; // Start position of the current segment. + let mut angle_brackets = 0; // Counter for tracking '<' and '>'. + let mut previous_char = '\0'; // Initializes to a null character, no previous character. -// **< MACROS ÚTILES >****************************************************************************** + for (idx, c) in type_name.char_indices() { + match c { + ':' if angle_brackets == 0 => { + if previous_char == ':' { + if segment_start < idx - 1 { + segments.push((segment_start, idx - 1)); // Do not include last '::'. + } + segment_start = idx + 1; // Next segment starts after '::'. + } + } + '<' => angle_brackets += 1, + '>' => angle_brackets -= 1, + _ => {} + } + previous_char = c; + } + + // Include the last segment if there's any. + if segment_start < maxlen { + segments.push((segment_start, maxlen)); + } + + // Calculates the start position. + let start_pos = segments + .get(if start >= 0 { + start as usize + } else { + segments.len() - start.unsigned_abs() + }) + .map_or(0, |&(s, _)| s); + + // Calculates the end position. + let end_pos = segments + .get(if let Some(end) = end { + if end >= 0 { + end as usize + } else { + segments.len() - end.unsigned_abs() + } + } else { + segments.len() - 1 + }) + .map_or(maxlen, |&(_, e)| e); + + // Returns the partial string based on the calculated positions. + &type_name[start_pos..end_pos] + } +} + +/// Calculates the absolute directory given a root path and a relative path. +/// +/// # Arguments +/// +/// * `root_path` - A string slice that holds the root path. +/// * `relative_path` - A string slice that holds the relative path. +/// +/// # Returns +/// +/// * `Ok` - If the operation is successful, returns the absolute directory as a `String`. +/// * `Err` - If an I/O error occurs, returns an `io::Error`. +/// +/// # Errors +/// +/// This function will return an error if: +/// - The root path or relative path are invalid. +/// - There is an issue with file system operations, such as reading the directory. +/// +/// # Examples +/// +/// ``` +/// let root = "/home/user"; +/// let relative = "documents"; +/// let abs_dir = absolute_dir(root, relative).unwrap(); +/// println!("{}", abs_dir); +/// ``` +pub fn absolute_dir( + root_path: impl Into<String>, + relative_path: impl Into<String>, +) -> Result<String, io::Error> { + let root_path = PathBuf::from(root_path.into()); + let full_path = root_path.join(relative_path.into()); + let absolute_dir = full_path.to_string_lossy().into(); + + if !full_path.is_absolute() { + let message = format!("Path \"{absolute_dir}\" is not absolute"); + trace::warn!(message); + return Err(io::Error::new(io::ErrorKind::InvalidInput, message)); + } + + if !full_path.exists() { + let message = format!("Path \"{absolute_dir}\" does not exist"); + trace::warn!(message); + return Err(io::Error::new(io::ErrorKind::NotFound, message)); + } + + if !full_path.is_dir() { + let message = format!("Path \"{absolute_dir}\" is not a directory"); + trace::warn!(message); + return Err(io::Error::new(io::ErrorKind::InvalidInput, message)); + } + + Ok(absolute_dir) +} + +// USEFUL MACROS *********************************************************************************** #[macro_export] -/// Macro para construir una colección de pares clave-valor. +/// Macro para construir grupos de pares clave-valor. /// -/// ```rust -/// use pagetop::hm; -/// use std::collections::HashMap; -/// -/// let args:HashMap<&str, String> = hm![ -/// "userName" => "Roberto", -/// "photoCount" => "3", +/// ```rust#ignore +/// let args = kv![ +/// "userName" => "Roberto", +/// "photoCount" => 3, /// "userGender" => "male", /// ]; /// ``` -macro_rules! hm { +macro_rules! kv { ( $($key:expr => $value:expr),* $(,)? ) => {{ let mut a = std::collections::HashMap::new(); $( @@ -40,140 +159,3 @@ macro_rules! hm { a }}; } - -/// Concatena eficientemente varios fragmentos en un [`String`]. -/// -/// Esta macro exporta [`concat_string!`](https://docs.rs/concat-string). Acepta cualquier número de -/// fragmentos que implementen [`AsRef<str>`] y construye un [`String`] con el tamaño óptimo, de -/// forma eficiente y evitando el uso de cadenas de formato que penalicen el rendimiento. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// // Concatena todos los fragmentos directamente. -/// let result = join!("Hello", " ", "World"); -/// assert_eq!(result, "Hello World".to_string()); -/// -/// // También funciona con valores vacíos. -/// let result_with_empty = join!("Hello", "", "World"); -/// assert_eq!(result_with_empty, "HelloWorld".to_string()); -/// -/// // Un único fragmento devuelve el mismo valor. -/// let single_result = join!("Hello"); -/// assert_eq!(single_result, "Hello".to_string()); -/// ``` -#[macro_export] -macro_rules! join { - ($($arg:expr),+) => { - $crate::util::concat_string!($($arg),+) - }; -} - -/// Concatena dos fragmentos en un [`String`] usando un separador. -/// -/// Une los dos fragmentos, que deben implementar [`AsRef<str>`], usando el separador proporcionado. -/// Si uno de ellos está vacío, devuelve directamente el otro; y si ambos están vacíos devuelve un -/// [`String`] vacío. -/// -/// # Ejemplo -/// -/// ```rust -/// # use pagetop::prelude::*; -/// let first = "Hello"; -/// let separator = "-"; -/// let second = "World"; -/// -/// // Concatena los dos fragmentos cuando ambos no están vacíos. -/// let result = join_pair!(first, separator, second); -/// assert_eq!(result, "Hello-World".to_string()); -/// -/// // Si el primer fragmento está vacío, devuelve el segundo. -/// let result_empty_first = join_pair!("", separator, second); -/// assert_eq!(result_empty_first, "World".to_string()); -/// -/// // Si el segundo fragmento está vacío, devuelve el primero. -/// let result_empty_second = join_pair!(first, separator, ""); -/// assert_eq!(result_empty_second, "Hello".to_string()); -/// -/// // Si ambos fragmentos están vacíos, devuelve una cadena vacía. -/// let result_both_empty = join_pair!("", separator, ""); -/// assert_eq!(result_both_empty, "".to_string()); -/// ``` -#[macro_export] -macro_rules! join_pair { - ($first:expr, $separator:expr, $second:expr) => {{ - let first_val = $first; - let second_val = $second; - let separator_val = $separator; - - let first = AsRef::<str>::as_ref(&first_val); - let second = AsRef::<str>::as_ref(&second_val); - let separator = if first.is_empty() || second.is_empty() { - "" - } else { - AsRef::<str>::as_ref(&separator_val) - }; - - $crate::util::concat_string!(first, separator, second) - }}; -} - -// **< FUNCIONES ÚTILES >*************************************************************************** - -/// Resuelve y valida la ruta de un directorio existente, devolviendo una ruta absoluta. -/// -/// - Si la ruta es relativa, se resuelve respecto al directorio del proyecto según la variable de -/// entorno `CARGO_MANIFEST_DIR` (si existe) o, en su defecto, respecto al directorio actual de -/// trabajo. -/// - Normaliza y valida la ruta final (resuelve `.`/`..` y enlaces simbólicos). -/// - Devuelve error si la ruta no existe o no es un directorio. -/// -/// # Ejemplos -/// -/// ```rust,no_run -/// # use pagetop::prelude::*; -/// // Ruta relativa, se resuelve respecto a CARGO_MANIFEST_DIR o al directorio actual (`cwd`). -/// println!("{:#?}", util::resolve_absolute_dir("documents")); -/// -/// // Ruta absoluta, se normaliza y valida tal cual. -/// println!("{:#?}", util::resolve_absolute_dir("/var/www")); -/// ``` -pub fn resolve_absolute_dir<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { - let path = path.as_ref(); - - let candidate = if path.is_absolute() { - path.to_path_buf() - } else { - // Directorio base CARGO_MANIFEST_DIR si está disponible; o current_dir() en su defecto. - env::var_os("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .or_else(|| env::current_dir().ok()) - .unwrap_or_else(|| PathBuf::from(".")) - .join(path) - }; - - // Resuelve `.`/`..`, enlaces simbólicos y obtiene la ruta absoluta en un único paso. - let absolute_dir = candidate.canonicalize()?; - - // Asegura que realmente es un directorio existente. - if absolute_dir.is_dir() { - Ok(absolute_dir) - } else { - Err({ - let msg = format!("Path \"{}\" is not a directory", absolute_dir.display()); - trace::warn!(msg); - io::Error::new(io::ErrorKind::InvalidInput, msg) - }) - } -} - -/// **Obsoleto desde la versión 0.3.0**: usar [`resolve_absolute_dir()`] en su lugar. -#[deprecated(since = "0.3.0", note = "Use `resolve_absolute_dir()` instead")] -pub fn absolute_dir<P, Q>(root_path: P, relative_path: Q) -> io::Result<PathBuf> -where - P: AsRef<Path>, - Q: AsRef<Path>, -{ - resolve_absolute_dir(root_path.as_ref().join(relative_path.as_ref())) -} diff --git a/static/banner.png b/static/banner.png index 422e493e..3ad51741 100644 Binary files a/static/banner.png and b/static/banner.png differ diff --git a/static/css/basic.css b/static/css/basic.css deleted file mode 100644 index 058e1736..00000000 --- a/static/css/basic.css +++ /dev/null @@ -1,21 +0,0 @@ -html { - scroll-behavior: smooth; -} - -body { - margin: 0; - font-family: var(--val-font-family); - font-size: var(--val-fs--base); - font-weight: var(--val-fw--base); - line-height: var(--val-lh--base); - color: var(--val-color--text); - background-color: var(--val-color--bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: transparent; -} - -/* Page layout */ - -.region--footer { - padding-bottom: 2rem; -} diff --git a/static/css/components.css b/static/css/components.css deleted file mode 100644 index ec5d3f00..00000000 --- a/static/css/components.css +++ /dev/null @@ -1,12 +0,0 @@ -/* Icon component */ - -.icon { - width: 1rem; - height: 1rem; -} - -/* PoweredBy component */ - -.poweredby { - text-align: center; -} diff --git a/static/css/intro.css b/static/css/intro.css deleted file mode 100644 index dbc72252..00000000 --- a/static/css/intro.css +++ /dev/null @@ -1,498 +0,0 @@ -: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); -} - -.intro { - position: relative; - min-width: 350px; - color: var(--intro-color); - background-color: var(--intro-bg-color); - - width: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.intro section { - position: relative; - text-align: center; -} - -.intro a { - color: currentColor; - text-decoration: underline; - transition: font-size 0.2s, text-decoration-color 0.2s; -} -.intro a:focus-visible { - outline: var(--intro-focus-outline); - outline-offset: var(--intro-focus-outline-offset); -} -.intro a:hover, -.intro a:hover:visited { - text-decoration-color: var(--intro-color-link); -} - -/* - * Header - */ - -.intro-header { - display: flex; - flex-direction: column-reverse; - width: 100%; - max-width: 80rem; - margin: 0 auto; - padding-bottom: 4rem; - background-image: var(--intro-bg-img-sm); - background-image: var(--intro-bg-img-sm-set); - background-position: top center; - background-position-y: -1rem; - background-size: contain; - background-repeat: no-repeat; -} -.intro-header__body { - padding: 0; - background: none; -} -.intro-header__title { - margin: 0 0 0 1.5rem; - text-align: left; - display: flex; - flex-direction: column; - box-sizing: border-box; - color: #dceefb; - padding: clamp(0rem, -5.4892rem + 23.4206vw, 9.5rem) 1rem 1rem; - font-size: clamp(1.5rem, 0.7231rem + 3.3149vw, 3.375rem); - font-style: italic; - font-weight: 600; - line-height: 110%; - text-shadow: 0 0.125rem 0.1875rem rgba(0, 0, 0, 0.3); -} -.intro-header__title > span { - background: linear-gradient(180deg, #ddff95 30%, #ffb84b 100%); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - font-size: clamp(2.25rem, 1.3177rem + 3.9779vw, 6.5rem); - font-style: normal; - font-weight: 700; - line-height: 110%; - text-shadow: none; -} -.intro-header__image { - display: flex; - justify-content: flex-start; - text-align: right; - width: 100%; -} -.intro-header__monster { - margin: 2rem; - flex-shrink: 1; - width: 280px; - height: 280px; -} -@media (min-width: 64rem) { - .intro-header { - background-image: var(--intro-bg-img); - background-image: var(--intro-bg-img-set); - } - .intro-header__title { - padding: 1.2rem 2rem 2.6rem 2rem; - } - .intro-header__image { - justify-content: flex-end; - } - .intro-header__monster { - margin-right: 12rem; - } -} - -/* - * Content - */ - -.intro-content { - height: auto; - margin-top: 1.6rem; -} -.intro-content__body { - box-sizing: border-box; - max-width: 80rem; -} -.intro-content__body:before, -.intro-content__body:after { - content: ''; - position: absolute; - left: 0; - right: 0; - background: linear-gradient(130deg, rgba(13, 44, 91, 0) 0%, #ddff95 77.4%); - margin: 0 -10.375rem; - filter: blur(2.75rem); - opacity: 0.8; - inset: 11.75rem; -} -.intro-content__body:before { - top: -1rem; -} -.intro-content__body:after { - bottom: -1rem; -} -@media (max-width: 48rem) { - .intro-content__body { - margin-top: -9.8rem; - } - .intro-content__body:before, - .intro-content__body:after { - inset: unset; - } -} -@media (min-width: 64rem) { - .intro-content { - margin-top: 0; - } - .intro-content__body { - margin-top: -5.7rem; - } -} - -.intro-button { - width: 100%; - margin: 0 auto; -} -.intro-button__link { - background: #7f1d1d; - background-image: linear-gradient(to bottom, rgba(255,0,0,0.8), rgba(255,255,255,0)); - background-position: top left, center; - background-size: contain; - background-repeat: no-repeat; - border-radius: 0.75rem; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - font-size: 1.5rem; - line-height: 1.3; - text-decoration: none !important; - transition: transform 0.3s ease-in-out !important; - position: relative; - overflow: hidden; - min-width: 28.875rem; - min-height: 7.6875rem; - outline: none; -} -.intro-button__link::before { - content: ''; - position: absolute; - top: -13.125rem; - left: -10rem; - height: 26.25rem; - width: 26.25rem; - background: linear-gradient(135deg, #ec7bae 50.41%, #9600b8 70.41%); - transform: rotate(45deg); - transition: transform 0.3s ease-in-out; - z-index: 5; -} -.intro-button__text { - display: flex; - flex-direction: column; - flex: 1; - transition: all 0.5s ease-in-out; - position: relative; - z-index: 10; - padding: 1rem 1.5rem; - text-align: left; - color: white; - font-size: 1.65rem; - font-style: normal; - font-weight: 600; - line-height: 130.023%; - letter-spacing: 0.0075rem; -} -.intro-button__text strong { - font-size: 2.625rem; - font-weight: 600; - line-height: 130.023%; - letter-spacing: 0.013125rem; -} -.intro-button__link span { - position: absolute; - display: block; - pointer-events: none; -} -.intro-button__link span:nth-child(1) { - height: 8px; - width: 100%; - top: 0; - left: 0; - background: linear-gradient(to right, rgba(0, 0, 0, 0), #f6e58d); - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - transform: translateX(-100%); - animation: span1 8s linear infinite; - animation-delay: 1s; -} -@keyframes span1 { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } -} -.intro-button__link span:nth-child(2) { - width: 8px; - height: 100%; - top: 0; - right: 0; - background: linear-gradient(to bottom, rgba(0, 0, 0, 0), #f6e58d); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - transform: translateY(100%); - animation: span2 4s linear infinite; - animation-delay: 5s; -} -@keyframes span2 { - 0% { - transform: translateY(-100%); - } - 100% { - transform: translateY(100%); - } -} -.intro-button__link span:nth-child(3) { - height: 8px; - width: 100%; - bottom: 0; - right: 0; - background: linear-gradient(to left, rgba(0, 0, 0, 0), #f6e58d); - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - transform: translateX(100%); - animation: span3 8s linear infinite; - animation-delay: 7s; -} -@keyframes span3 { - 0% { - transform: translateX(100%); - } - 100% { - transform: translateX(-100%); - } -} -.intro-button__link:hover span { - animation-play-state: paused; -} -@media (max-width: 48rem) { - .intro-header { - padding-bottom: 9rem;; - } - .intro-button__link { - height: 6.25rem; - min-width: auto; - border-radius: 0; - } - .intro-button__text { - display: inline; - padding-top: .5rem; - } -} -@media (min-width: 48rem) { - .intro-button { - position: absolute; - top: 0; - left: 50%; - transform: translate(-50%, -50%); - max-width: 29.375rem; - margin-bottom: 0; - } - .intro-button__link:hover { - transition: all .5s; - transform: rotate(-3deg) scale(1.125); - } -} - -.intro-text { - z-index: 1; - width: 100%; - display: flex; - flex-direction: column; - box-sizing: border-box; - align-items: center; - text-align: left; - font-size: 1.3125rem; - font-weight: 400; - line-height: 1.5; - margin-bottom: 0; - background: #fff; - position: relative; -} -.intro-text__children { - padding: 2.5rem 1.063rem 0.75rem; - overflow: hidden; -} -.intro-text__children p { - width: 100%; - line-height: 150%; - font-weight: 400; - font-size: 1.45rem; - margin: 0 0 1.5rem; -} -@media (min-width: 48rem) { - .intro-text { - font-size: 1.375rem; - line-height: 2rem; - } - .intro-button + .intro-text__children { - padding-top: 7rem; - } -} -@media (min-width: 64rem) { - .intro-header { - padding-bottom: 9rem;; - } - .intro-text, - .intro-text__children { - border-radius: 0.75rem; - } - .intro-text { - box-shadow: var(--intro-shadow); - max-width: 60rem; - margin: 0 auto 6rem; - } - .intro-text__children { - padding-left: 4.5rem; - padding-right: 4.5rem; - } -} - -.intro-text__children .block { - position: relative; -} -.intro-text__children .block__title { - margin: 1em 0 .8em; -} -.intro-text__children .block__title span { - display: inline-block; - padding: 10px 30px 14px; - margin: 30px 0 0 20px; - background: white; - border: 5px solid; - border-radius: 30px; - box-shadow: 0 0 0 5px white, inset 0 0 0 5px white; - border-color: orangered; - transform: rotate(-3deg) translateY(-25%); -} -.intro-text__children .block__title:before { - content: ""; - height: 5px; - position: absolute; - top: 50px; - left: -15%; - width: 130%; - z-index: -5; - background: orangered; - box-shadow: 0 0 0 5px white, 0 -10px 0 5px white; - transform: rotate(2deg) translateY(-50%); - transform-origin: top left; -} -.intro-text__children .block__title:after { - content: ""; - height: 70rem; - position: absolute; - top: 42px; - left: -15%; - width: 130%; - z-index: -10; - background: var(--intro-bg-block-1); - transform: rotate(2deg); -} -.intro-text__children .block:nth-of-type(5n+1) .block__title:after { - background: var(--intro-bg-block-1); -} -.intro-text__children .block:nth-of-type(5n+2) .block__title:after { - background: var(--intro-bg-block-2); -} -.intro-text__children .block:nth-of-type(5n+3) .block__title:after { - background: var(--intro-bg-block-3); -} -.intro-text__children .block:nth-of-type(5n+4) .block__title:after { - background: var(--intro-bg-block-4); -} -.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 - */ - -.intro-footer { - width: 100%; - background-color: black; - color: var(--intro-color-gray); - padding-bottom: 2rem; -} - -.intro-footer__body { - display: flex; - justify-content: center; - flex-direction: column; - margin: 0 auto; - padding: 0 10.625rem 2rem; - max-width: 80rem; - font-size: 1.15rem; - font-weight: 300; - line-height: 100%; -} -.intro-footer__body a:visited { - color: var(--intro-color-gray); -} -.intro-footer__logo, -.intro-footer__links { - display: flex; - justify-content: center; - width: 100%; -} -.intro-footer__logo { - max-height: 12.625rem; -} -.intro-footer__logo svg { - width: 100%; -} -.intro-footer__links { - gap: 1.875rem; - flex-wrap: wrap; - margin-top: 2rem; -} -@media (max-width: 48rem) { - .intro-footer__logo { - display: none; - } -} -@media (max-width: 64rem) { - .intro-footer__body { - padding: 0 1rem 2rem; - } -} diff --git a/static/css/normalize.css b/static/css/normalize.css deleted file mode 100644 index 192eb9ce..00000000 --- a/static/css/normalize.css +++ /dev/null @@ -1,349 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} diff --git a/static/css/root.css b/static/css/root.css deleted file mode 100644 index aeab1c67..00000000 --- a/static/css/root.css +++ /dev/null @@ -1,212 +0,0 @@ -: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; - --val-font-monospace: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; - --val-font-family: var(--val-font-sans); - - /* Font size */ - --val-fs--x3l: 2.5rem; - --val-fs--x2l: 2rem; - --val-fs--xl: 1.75rem; - --val-fs--l: 1.5rem; - --val-fs--m: 1.25rem; - --val-fs--base: 1rem; - --val-fs--s: 0.875rem; - --val-fs--xs: 0.75rem; - --val-fs--x2s: 0.5625rem; - --val-fs--x3s: 0.375rem; - - /* Font weight */ - --val-fw--light: 300; - --val-fw--base: 400; - --val-fw--bold: 500; - - /* Line height */ - --val-lh--base: 1.5; - --val-lh--header: 1.2; - - --val-max-width: 90rem; -/* - --val-color-rgb: 33,37,41; - --val-main--bg-rgb: 255,255,255; - --val-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - - --line-height-base: 1.6875rem; - --line-height-s: 1.125rem; - --max-bg-color: 98.125rem; -*/ - --val-gap: 1.125rem; -/* - --content-left: 5.625rem; - --site-header-height-wide: var(--val-gap10); - --container-padding: var(--val-gap); -*/ -} -/* -@media (min-width: 75rem) { - :root { - --container-padding:var(--val-gap2); - } -} - -:root { - --scrollbar-width: 0px; - --grid-col-count: 6; - --grid-gap: var(--val-gap); - --grid-gap-count: calc(var(--grid-col-count) - 1); - --grid-full-width: calc(100vw - var(--val-gap2) - var(--scrollbar-width)); - --grid-col-width: calc((var(--grid-full-width) - (var(--grid-gap-count) * var(--grid-gap))) / var(--grid-col-count)); -} - -@media (min-width: 43.75rem) { - :root { - --grid-col-count:14; - --grid-gap: var(--val-gap2); - } -} - -@media (min-width: 62.5rem) { - :root { - --scrollbar-width:0.9375rem; - } -} - -@media (min-width: 75rem) { - :root { - --grid-full-width:calc(100vw - var(--scrollbar-width) - var(--content-left) - var(--val-gap4)); - } -} - -@media (min-width: 90rem) { - :root { - --grid-full-width:calc(var(--max-width) - var(--val-gap4)); - } -} -*/ -:root { - --val-gap-0-15: calc(0.15 * var(--val-gap)); - --val-gap-0-25: calc(0.25 * var(--val-gap)); - --val-gap-0-35: calc(0.35 * var(--val-gap)); - --val-gap-0-5: calc(0.5 * var(--val-gap)); - --val-gap-0-75: calc(0.75 * var(--val-gap)); - --val-gap-1-5: calc(1.5 * var(--val-gap)); - --val-gap-2: calc(2 * var(--val-gap)); - - --primary-hue: 216; - --primary-sat: 60%; - --val-color--primary: hsl(var(--primary-hue), var(--primary-sat), 50%); - --val-color--primary-light: hsl(var(--primary-hue), var(--primary-sat), 60%); - --val-color--primary-dark: hsl(var(--primary-hue), var(--primary-sat), 40%); - --val-color--primary-link: hsl(var(--primary-hue), var(--primary-sat), 55%); - --val-color--primary-link-hover: hsl(var(--primary-hue), var(--primary-sat), 30%); - --val-color--primary-link-active: hsl(var(--primary-hue), var(--primary-sat), 70%); - - --info-hue: 190; - --info-sat: 90%; - --val-color--info: hsl(var(--info-hue), var(--info-sat), 54%); - --val-color--info-light: hsl(var(--info-hue), var(--info-sat), 70%); - --val-color--info-dark: hsl(var(--info-hue), var(--info-sat), 45%); - --val-color--info-link: hsl(var(--info-hue), var(--info-sat), 30%); - --val-color--info-link-hover: hsl(var(--info-hue), var(--info-sat), 20%); - --val-color--info-link-active: hsl(var(--info-hue), var(--info-sat), 40%); - - --success-hue: 150; - --success-sat: 50%; - --val-color--success: hsl(var(--success-hue), var(--success-sat), 50%); - --val-color--success-light: hsl(var(--success-hue), var(--success-sat), 68%); - --val-color--success-dark: hsl(var(--success-hue), var(--success-sat), 38%); - --val-color--success-link: hsl(var(--success-hue), var(--success-sat), 26%); - --val-color--success-link-hover: hsl(var(--success-hue), var(--success-sat), 18%); - --val-color--success-link-active: hsl(var(--success-hue), var(--success-sat), 36%); - - --warning-hue: 44; - --warning-sat: 100%; - --val-color--warning: hsl(var(--warning-hue), var(--warning-sat), 50%); - --val-color--warning-light: hsl(var(--warning-hue), var(--warning-sat), 60%); - --val-color--warning-dark: hsl(var(--warning-hue), var(--warning-sat), 40%); - --val-color--warning-link: hsl(var(--warning-hue), var(--warning-sat), 30%); - --val-color--warning-link-hover: hsl(var(--warning-hue), var(--warning-sat), 20%); - --val-color--warning-link-active: hsl(var(--warning-hue), var(--warning-sat), 38%); - - --danger-hue: 348; - --danger-sat: 86%; - --val-color--danger: hsl(var(--danger-hue), var(--danger-sat), 50%); - --val-color--danger-light: hsl(var(--danger-hue), var(--danger-sat), 60%); - --val-color--danger-dark: hsl(var(--danger-hue), var(--danger-sat), 35%); - --val-color--danger-link: hsl(var(--danger-hue), var(--danger-sat), 25%); - --val-color--danger-link-hover: hsl(var(--danger-hue), var(--danger-sat), 10%); - --val-color--danger-link-active: hsl(var(--danger-hue), var(--danger-sat), 30%); - - --light-hue: 0; - --light-sat: 0%; - --val-color--light: hsl(var(--light-hue), var(--light-sat), 96%); - --val-color--light-light: hsl(var(--light-hue), var(--light-sat), 98%); - --val-color--light-dark: hsl(var(--light-hue), var(--light-sat), 92%); - - --dark-hue: 0; - --dark-sat: 0%; - --val-color--dark: hsl(var(--dark-hue), var(--dark-sat), 25%); - --val-color--dark-light: hsl(var(--dark-hue), var(--dark-sat), 40%); - --val-color--dark-dark: hsl(var(--dark-hue), var(--dark-sat), 8%); - --val-color--dark-link: hsl(var(--dark-hue), var(--dark-sat), 90%); - --val-color--dark-link-hover: hsl(var(--dark-hue), var(--dark-sat), 100%); - --val-color--dark-link-active: hsl(var(--dark-hue), var(--dark-sat), 70%); - - - - - --gray-hue: 201; - --gray-sat: 15%; - --val-color--gray-5: hsl(var(--gray-hue), var(--gray-sat), 5%); - --val-color--gray-10: hsl(var(--gray-hue), var(--gray-sat) ,11%); - --val-color--gray-20: hsl(var(--gray-hue), var(--gray-sat),20%); - --val-color--gray-45: hsl(var(--gray-hue), var(--gray-sat), 44%); - --val-color--gray-60: hsl(var(--gray-hue), var(--gray-sat), 57%); - --val-color--gray-65: hsl(var(--gray-hue), var(--gray-sat), 63%); - --val-color--gray-70: hsl(var(--gray-hue), var(--gray-sat), 72%); - --val-color--gray-90: hsl(var(--gray-hue), var(--gray-sat), 88%); - --val-color--gray-95: hsl(var(--gray-hue), var(--gray-sat), 93%); - --val-color--gray-100: hsl(var(--gray-hue), var(--gray-sat), 97%); - - - - - --val-color--bg: #fafafa; - --val-color--text: #212529; - --val-color--white: #fff; - -/* - - - --color-text-neutral-soft: var(--color--gray-45); - --color-text-neutral-medium: var(--color--gray-20); - --color-text-neutral-loud: var(--color--gray-5); - --color-text-primary-medium: var(--val-color--primary-40); - --color-text-primary-loud: var(--val-color--primary-30); - --color--black: #000; -*/ -/* - --color--red: #e33f1e; - --color--gold: #fdca40; - --color--green: #3fa21c; - --header-height-wide-when-fixed: calc(6 * var(--val-gap)); - --mobile-nav-width: 31.25rem; - - --val-menu--border-radius: 0.625rem; -*/ - --val-border-radius: 0.375rem; - - /* Menu component */ - --val-menu--color-bg: var(--val-color--bg); - --val-menu--color-highlight: #e91e63; - --val-menu--color-border: rgba(0, 0, 0, 0.1); - --val-menu--color-shadow: rgba(0, 0, 0, 0.06); - --val-menu--line-padding: 0.625rem; - --val-menu--line-height: calc(1.875rem + 1px); - --val-menu--item-height: calc(var(--val-menu--line-padding) + var(--val-menu--line-height)); - --val-menu--item-width-min: 14rem; - --val-menu--item-width-max: 20rem; - --val-menu--item-gap: 1rem; - --val-menu--trigger-width: 2.675rem; - --val-menu--side-width: 20rem; -} diff --git a/static/img/intro-header-sm.avif b/static/img/intro-header-sm.avif deleted file mode 100644 index ec31a6db..00000000 Binary files a/static/img/intro-header-sm.avif and /dev/null differ diff --git a/static/img/intro-header-sm.jpg b/static/img/intro-header-sm.jpg deleted file mode 100644 index f2c875fe..00000000 Binary files a/static/img/intro-header-sm.jpg and /dev/null differ diff --git a/static/img/intro-header-sm.webp b/static/img/intro-header-sm.webp deleted file mode 100644 index 9f7aff87..00000000 Binary files a/static/img/intro-header-sm.webp and /dev/null differ diff --git a/static/img/intro-header.avif b/static/img/intro-header.avif deleted file mode 100644 index 8018555a..00000000 Binary files a/static/img/intro-header.avif and /dev/null differ diff --git a/static/img/intro-header.jpg b/static/img/intro-header.jpg deleted file mode 100644 index 69f1dbe0..00000000 Binary files a/static/img/intro-header.jpg and /dev/null differ diff --git a/static/img/intro-header.webp b/static/img/intro-header.webp deleted file mode 100644 index 7da174cf..00000000 Binary files a/static/img/intro-header.webp and /dev/null differ diff --git a/tests/component_html.rs b/tests/component_html.rs deleted file mode 100644 index 06d77ec9..00000000 --- a/tests/component_html.rs +++ /dev/null @@ -1,63 +0,0 @@ -use pagetop::prelude::*; - -#[pagetop::test] -async fn component_html_renders_static_markup() { - let mut component = Html::with(|_| { - html! { - p { "Test" } - } - }); - - let markup = component.render(&mut Context::default()); - assert_eq!(markup.0, "<p>Test</p>"); -} - -#[pagetop::test] -async fn component_html_renders_using_context_param() { - let mut cx = Context::default().with_param("username", "Alice".to_string()); - - let mut component = Html::with(|cx| { - let name = cx.param::<String>("username").cloned().unwrap_or_default(); - html! { - span { (name) } - } - }); - - let markup = component.render(&mut cx); - assert_eq!(markup.0, "<span>Alice</span>"); -} - -#[pagetop::test] -async fn component_html_allows_replacing_render_function() { - let mut component = Html::with(|_| html! { div { "Original" } }); - - component.alter_fn(|_| html! { div { "Modified" } }); - - let markup = component.render(&mut Context::default()); - assert_eq!(markup.0, "<div>Modified</div>"); -} - -#[pagetop::test] -async fn component_html_default_renders_empty_markup() { - let mut component = Html::default(); - - let markup = component.render(&mut Context::default()); - assert_eq!(markup.0, ""); -} - -#[pagetop::test] -async fn component_html_can_access_http_method() { - let req = service::test::TestRequest::with_uri("/").to_http_request(); - let mut cx = Context::new(Some(req)); - - let mut component = Html::with(|cx| { - let method = cx - .request() - .map(|r| r.method().to_string()) - .unwrap_or_default(); - html! { span { (method) } } - }); - - let markup = component.render(&mut cx); - assert_eq!(markup.0, "<span>GET</span>"); -} diff --git a/tests/component_poweredby.rs b/tests/component_poweredby.rs deleted file mode 100644 index 7e5a062c..00000000 --- a/tests/component_poweredby.rs +++ /dev/null @@ -1,91 +0,0 @@ -use pagetop::prelude::*; - -#[pagetop::test] -async fn poweredby_default_shows_only_pagetop_recognition() { - let _app = service::test::init_service(Application::new().test()).await; - - let mut p = PoweredBy::default(); - let html = p.render(&mut Context::default()); - - // Debe mostrar el bloque de reconocimiento a PageTop. - assert!(html.as_str().contains("poweredby__pagetop")); - - // Y NO debe mostrar el bloque de copyright. - assert!(!html.as_str().contains("poweredby__copyright")); -} - -#[pagetop::test] -async fn poweredby_new_includes_current_year_and_app_name() { - let _app = service::test::init_service(Application::new().test()).await; - - let mut p = PoweredBy::new(); - let html = p.render(&mut Context::default()); - - let year = Utc::now().format("%Y").to_string(); - assert!( - html.as_str().contains(&year), - "HTML should include the current year" - ); - - // El nombre de la app proviene de `global::SETTINGS.app.name`. - let app_name = &global::SETTINGS.app.name; - assert!( - html.as_str().contains(app_name), - "HTML should include the application name" - ); - - // Debe existir el span de copyright. - assert!(html.as_str().contains("poweredby__copyright")); -} - -#[pagetop::test] -async fn poweredby_with_copyright_overrides_text() { - let _app = service::test::init_service(Application::new().test()).await; - - let custom = "2001 © FooBar Inc."; - let mut p = PoweredBy::default().with_copyright(Some(custom)); - let html = p.render(&mut Context::default()); - - assert!(html.as_str().contains(custom)); - assert!(html.as_str().contains("poweredby__copyright")); -} - -#[pagetop::test] -async fn poweredby_with_copyright_none_hides_text() { - let _app = service::test::init_service(Application::new().test()).await; - - let mut p = PoweredBy::new().with_copyright(None::<String>); - let html = p.render(&mut Context::default()); - - assert!(!html.as_str().contains("poweredby__copyright")); - // El reconocimiento a PageTop siempre debe aparecer. - assert!(html.as_str().contains("poweredby__pagetop")); -} - -#[pagetop::test] -async fn poweredby_link_points_to_crates_io() { - let _app = service::test::init_service(Application::new().test()).await; - - let mut p = PoweredBy::default(); - let html = p.render(&mut Context::default()); - - assert!( - html.as_str().contains("https://pagetop.cillero.es"), - "Link should point to pagetop.cillero.es" - ); -} - -#[pagetop::test] -async fn poweredby_getter_reflects_internal_state() { - let _app = service::test::init_service(Application::new().test()).await; - - // Por defecto no hay copyright. - let p0 = PoweredBy::default(); - assert_eq!(p0.copyright(), None); - - // Y `new()` lo inicializa con año + nombre de app. - let p1 = PoweredBy::new(); - let c1 = p1.copyright().expect("Expected copyright to exis"); - assert!(c1.contains(&Utc::now().format("%Y").to_string())); - assert!(c1.contains(&global::SETTINGS.app.name)); -} diff --git a/tests/config.rs b/tests/config.rs deleted file mode 100644 index ff7135c0..00000000 --- a/tests/config.rs +++ /dev/null @@ -1,41 +0,0 @@ -use pagetop::prelude::*; - -use serde::Deserialize; - -use std::env; - -include_config!(SETTINGS: Settings => [ - "test.string_value" => "Test String", - "test.int_value" => -321, - "test.float_value" => 2.3456, -]); - -#[derive(Debug, Deserialize)] -pub struct Settings { - pub test: Test, -} - -#[derive(Debug, Deserialize)] -pub struct Test { - pub string_value: String, - pub int_value: i32, - pub float_value: f32, -} - -#[pagetop::test] -async fn check_global_config() { - env::set_var("PAGETOP_RUN_MODE", "test"); - - assert_eq!(global::SETTINGS.app.run_mode, "test"); - assert_eq!(global::SETTINGS.app.name, "Testing"); - assert_eq!(global::SETTINGS.server.bind_port, 9000); -} - -#[pagetop::test] -async fn check_local_config() { - env::set_var("PAGETOP_RUN_MODE", "test"); - - assert_eq!(SETTINGS.test.string_value, "Modified value"); - assert_eq!(SETTINGS.test.int_value, -321); - assert_eq!(SETTINGS.test.float_value, 8.7654); -} diff --git a/tests/html_pm.rs b/tests/html_pm.rs deleted file mode 100644 index 615ea470..00000000 --- a/tests/html_pm.rs +++ /dev/null @@ -1,144 +0,0 @@ -use pagetop::prelude::*; - -/// Componente mínimo para probar `PrepareMarkup` pasando por el ciclo real -/// de renderizado de componentes (`ComponentRender`). -#[derive(AutoDefault)] -struct TestPrepareComponent { - pm: PrepareMarkup, -} - -impl Component for TestPrepareComponent { - fn new() -> Self { - Self { - pm: PrepareMarkup::None, - } - } - - fn prepare_component(&self, _cx: &mut Context) -> PrepareMarkup { - self.pm.clone() - } -} - -impl TestPrepareComponent { - fn render_pm(pm: PrepareMarkup) -> String { - let mut c = TestPrepareComponent { pm }; - c.render(&mut Context::default()).into_string() - } -} - -#[pagetop::test] -async fn prepare_markup_none_is_empty_string() { - assert_eq!(PrepareMarkup::None.into_string(), ""); -} - -#[pagetop::test] -async fn prepare_markup_escaped_escapes_html_and_ampersands() { - let pm = PrepareMarkup::Escaped("<b>& \" ' </b>".to_string()); - assert_eq!(pm.into_string(), "&lt;b&gt;&amp; &quot; ' &lt;/b&gt;"); -} - -#[pagetop::test] -async fn prepare_markup_raw_is_inserted_verbatim() { - let pm = PrepareMarkup::Raw("<b>bold</b><script>1<2</script>".to_string()); - assert_eq!(pm.into_string(), "<b>bold</b><script>1<2</script>"); -} - -#[pagetop::test] -async fn prepare_markup_with_keeps_structure() { - let pm = PrepareMarkup::With(html! { - h2 { "Sample title" } - p { "This is a paragraph." } - }); - assert_eq!( - pm.into_string(), - "<h2>Sample title</h2><p>This is a paragraph.</p>" - ); -} - -#[pagetop::test] -async fn prepare_markup_unicode_is_preserved() { - // Texto con acentos y emojis debe conservarse (salvo el escape HTML de signos). - let esc = PrepareMarkup::Escaped("Hello, tomorrow coffee ☕ & donuts!".into()); - assert_eq!(esc.into_string(), "Hello, tomorrow coffee ☕ &amp; donuts!"); - - // Raw debe pasar íntegro. - let raw = PrepareMarkup::Raw("Title — section © 2025".into()); - assert_eq!(raw.into_string(), "Title — section © 2025"); -} - -#[pagetop::test] -async fn prepare_markup_is_empty_semantics() { - assert!(PrepareMarkup::None.is_empty()); - - assert!(PrepareMarkup::Escaped(String::new()).is_empty()); - assert!(PrepareMarkup::Escaped("".to_string()).is_empty()); - assert!(!PrepareMarkup::Escaped("x".to_string()).is_empty()); - - assert!(PrepareMarkup::Raw(String::new()).is_empty()); - assert!(PrepareMarkup::Raw("".to_string()).is_empty()); - assert!(!PrepareMarkup::Raw("a".into()).is_empty()); - - assert!(PrepareMarkup::With(html! {}).is_empty()); - assert!(!PrepareMarkup::With(html! { span { "!" } }).is_empty()); - - // Ojo: espacios NO deberían considerarse vacíos (comportamiento actual). - assert!(!PrepareMarkup::Escaped(" ".into()).is_empty()); - assert!(!PrepareMarkup::Raw(" ".into()).is_empty()); -} - -#[pagetop::test] -async fn prepare_markup_does_not_double_escape_when_markup_is_reinjected_in_html_macro() { - let mut cx = Context::default(); - - // Escaped: dentro de `html!` no debe volver a escaparse. - let mut comp = TestPrepareComponent { - pm: PrepareMarkup::Escaped("<i>x</i>".into()), - }; - let markup = comp.render(&mut cx); // Markup - let wrapped_escaped = html! { div { (markup) } }.into_string(); - assert_eq!(wrapped_escaped, "<div>&lt;i&gt;x&lt;/i&gt;</div>"); - - // Raw: tampoco debe escaparse al integrarlo. - let mut comp = TestPrepareComponent { - pm: PrepareMarkup::Raw("<i>x</i>".into()), - }; - let markup = comp.render(&mut cx); - let wrapped_raw = html! { div { (markup) } }.into_string(); - assert_eq!(wrapped_raw, "<div><i>x</i></div>"); - - // With: debe incrustar el Markup tal cual. - let mut comp = TestPrepareComponent { - pm: PrepareMarkup::With(html! { span.title { "ok" } }), - }; - let markup = comp.render(&mut cx); - let wrapped_with = html! { div { (markup) } }.into_string(); - assert_eq!(wrapped_with, "<div><span class=\"title\">ok</span></div>"); -} - -#[pagetop::test] -async fn prepare_markup_equivalence_between_component_render_and_markup_reinjected_in_html_macro() { - let cases = [ - PrepareMarkup::None, - PrepareMarkup::Escaped("<b>x</b>".into()), - PrepareMarkup::Raw("<b>x</b>".into()), - PrepareMarkup::With(html! { b { "x" } }), - ]; - - for pm in cases { - // Vía 1: renderizamos y obtenemos directamente el String. - let via_component = TestPrepareComponent::render_pm(pm.clone()); - - // Vía 2: renderizamos, reinyectamos el Markup en `html!` y volvemos a obtener String. - let via_macro = { - let mut cx = Context::default(); - let mut comp = TestPrepareComponent { pm }; - let markup = comp.render(&mut cx); - html! { (markup) }.into_string() - }; - - assert_eq!( - via_component, via_macro, - "The output of component render and (Markup) inside html! must match" - ); - } -} diff --git a/tests/html_unit.rs b/tests/html_unit.rs deleted file mode 100644 index 6acae935..00000000 --- a/tests/html_unit.rs +++ /dev/null @@ -1,223 +0,0 @@ -use pagetop::prelude::*; - -use std::str::FromStr; - -#[pagetop::test] -async fn unit_value_empty_and_auto_and_zero_without_unit() { - assert_eq!(UnitValue::from_str("").unwrap(), UnitValue::None); - assert_eq!(UnitValue::from_str("auto").unwrap(), UnitValue::Auto); - assert_eq!(UnitValue::from_str("AUTO").unwrap(), UnitValue::Auto); - - // Cero sin unidad. - assert_eq!(UnitValue::from_str("0").unwrap(), UnitValue::Zero); - assert_eq!(UnitValue::from_str("+0").unwrap(), UnitValue::Zero); - assert_eq!(UnitValue::from_str("-0").unwrap(), UnitValue::Zero); -} - -#[pagetop::test] -async fn unit_value_absolute_integers_with_signs_and_spaces_and_case() { - // Positivos, negativos y con espacios. - assert_eq!(UnitValue::from_str("12px").unwrap(), UnitValue::Px(12)); - assert_eq!(UnitValue::from_str("-5pt").unwrap(), UnitValue::Pt(-5)); - assert_eq!(UnitValue::from_str(" 7 cm ").unwrap(), UnitValue::Cm(7)); - assert_eq!(UnitValue::from_str("+9 in").unwrap(), UnitValue::In(9)); - assert_eq!(UnitValue::from_str(" 13 mm ").unwrap(), UnitValue::Mm(13)); - assert_eq!(UnitValue::from_str("4 pc").unwrap(), UnitValue::Pc(4)); - - // Insensibilidad a mayúsculas. - assert_eq!(UnitValue::from_str("10PX").unwrap(), UnitValue::Px(10)); - assert_eq!(UnitValue::from_str("15Pt").unwrap(), UnitValue::Pt(15)); -} - -#[pagetop::test] -async fn unit_value_relative_floats_with_signs_and_spaces_and_case() { - assert_eq!( - UnitValue::from_str("1.25rem").unwrap(), - UnitValue::RelRem(1.25) - ); - assert_eq!( - UnitValue::from_str("-0.5em").unwrap(), - UnitValue::RelEm(-0.5) - ); - assert_eq!( - UnitValue::from_str(" 33% ").unwrap(), - UnitValue::RelPct(33.0) - ); - assert_eq!( - UnitValue::from_str(" -12.5 vh").unwrap(), - UnitValue::RelVh(-12.5) - ); - assert_eq!( - UnitValue::from_str(" 8.0 VW ").unwrap(), - UnitValue::RelVw(8.0) - ); -} - -#[pagetop::test] -async fn unit_value_whitespace_between_number_and_unit_is_allowed() { - // Hay espacio entre número y unidad (la implementación actual lo admite). - assert_eq!(UnitValue::from_str("12 px").unwrap(), UnitValue::Px(12)); - assert_eq!( - UnitValue::from_str("1.5 rem").unwrap(), - UnitValue::RelRem(1.5) - ); - assert_eq!( - UnitValue::from_str("25 %").unwrap(), - UnitValue::RelPct(25.0) - ); -} - -#[pagetop::test] -async fn unit_value_roundtrip_display_keeps_expected_format() { - let cases = [ - ("", UnitValue::None, ""), - ("auto", UnitValue::Auto, "auto"), - ("0", UnitValue::Zero, "0"), - ("12px", UnitValue::Px(12), "12px"), - ("-5pt", UnitValue::Pt(-5), "-5pt"), - ("7cm", UnitValue::Cm(7), "7cm"), - ("33%", UnitValue::RelPct(33.0), "33%"), - ("1.25rem", UnitValue::RelRem(1.25), "1.25rem"), - ("2em", UnitValue::RelEm(2.0), "2em"), - ("-0.5vh", UnitValue::RelVh(-0.5), "-0.5vh"), - ("8vw", UnitValue::RelVw(8.0), "8vw"), - ]; - - for (input, expected_value, expected_display) in cases { - let parsed = UnitValue::from_str(input).unwrap(); - assert_eq!( - parsed, expected_value, - "parsed mismatch for input `{input}`" - ); - assert_eq!( - parsed.to_string(), - expected_display, - "display mismatch for input `{input}`" - ); - } -} - -#[pagetop::test] -async fn unit_value_percentage_trimming_and_signs() { - assert_eq!( - UnitValue::from_str(" 12.5 % ").unwrap(), - UnitValue::RelPct(12.5) - ); - assert_eq!( - UnitValue::from_str("-0.0%").unwrap(), - UnitValue::RelPct(-0.0) - ); - assert_eq!( - UnitValue::from_str("+15%").unwrap(), - UnitValue::RelPct(15.0) - ); -} - -// ERRORES ESPERADOS (no cambiar los mensajes; con is_err() basta). - -#[pagetop::test] -async fn unit_value_errors_missing_unit_for_non_zero() { - assert!( - UnitValue::from_str("12").is_err(), - "non-zero without unit must error" - ); - assert!( - UnitValue::from_str(" -3 ").is_err(), - "non-zero without unit must error" - ); -} - -#[pagetop::test] -async fn unit_value_errors_decimals_in_absolute_units() { - assert!(UnitValue::from_str("1.5px").is_err()); - assert!(UnitValue::from_str("-2.0pt").is_err()); - assert!(UnitValue::from_str("+0.1cm").is_err()); -} - -#[pagetop::test] -async fn unit_value_errors_unknown_units_or_bad_percentages() { - // Unidad no soportada. - assert!(UnitValue::from_str("10ch").is_err()); - assert!(UnitValue::from_str("2q").is_err()); - // Falta número. - assert!(UnitValue::from_str("%").is_err()); - assert!(UnitValue::from_str(" % ").is_err()); -} - -#[pagetop::test] -async fn unit_value_errors_non_numeric_numbers() { - assert!(UnitValue::from_str("NaNem").is_err()); - // Decimal no permitido por FromStr. - assert!(UnitValue::from_str("1,5rem").is_err()); -} - -#[pagetop::test] -async fn unit_value_serde_deserialize_struct_and_array() { - use serde::Deserialize; - - #[derive(Deserialize, Debug, PartialEq)] - struct BoxStyle { - width: UnitValue, - height: UnitValue, - margin: UnitValue, - } - - let json = r#"{ "width": "12px", "height": "1.5rem", "margin": "0" }"#; - let s: BoxStyle = serde_json::from_str(json).unwrap(); - assert_eq!(s.width, UnitValue::Px(12)); - assert_eq!(s.height, UnitValue::RelRem(1.5)); - assert_eq!(s.margin, UnitValue::Zero); - - #[derive(Deserialize, Debug, PartialEq)] - struct Many { - values: Vec<UnitValue>, - } - - let json_arr = r#"{ "values": ["", "auto", "33%", "8vw", "7 cm", "-5pt"] }"#; - let m: Many = serde_json::from_str(json_arr).unwrap(); - assert_eq!( - m.values, - vec![ - UnitValue::None, - UnitValue::Auto, - UnitValue::RelPct(33.0), - UnitValue::RelVw(8.0), - UnitValue::Cm(7), - UnitValue::Pt(-5), - ] - ); -} - -#[pagetop::test] -async fn unit_value_accepts_dot5_and_1dot_shorthand_for_relatives() { - // `.5` y `1.` se parsean correctamente en relativas. - assert_eq!(UnitValue::from_str(".5em").unwrap(), UnitValue::RelEm(0.5)); - assert_eq!( - UnitValue::from_str("1.rem").unwrap(), - UnitValue::RelRem(1.0) - ); - assert_eq!(UnitValue::from_str("1.vh").unwrap(), UnitValue::RelVh(1.0)); - // Sin unidad debe seguir fallando. - assert!(UnitValue::from_str("1.").is_err()); -} - -#[pagetop::test] -async fn unit_value_display_keeps_minus_zero_for_relatives() { - // Comportamiento actual: f32 Display muestra "-0" si el valor es -0.0. - let v = UnitValue::RelEm(-0.0); - // Se acepta cualquiera de los dos formatos como válidos. - let s = v.to_string(); - assert!( - s == "-0em" || s == "0em", - "current Display prints `{s}` for -0.0; both are acceptable in tests" - ); -} - -#[pagetop::test] -async fn unit_value_rejects_non_decimal_notations() { - // Octal, los ceros a la izquierda (p. ej. `"020px"`) se interpretan en **base 10** (`20px`). - assert_eq!(UnitValue::from_str("020px").unwrap(), UnitValue::Px(20)); - // Notación científica y bases no decimales (p. ej., `"1e3vw"`, `"0x10px"`) no están soportadas. - assert!(UnitValue::from_str("1e3vw").is_err()); - assert!(UnitValue::from_str("0x10px").is_err()); -} diff --git a/tests/locale.rs b/tests/locale.rs deleted file mode 100644 index ee7ac27c..00000000 --- a/tests/locale.rs +++ /dev/null @@ -1,58 +0,0 @@ -use pagetop::prelude::*; - -#[pagetop::test] -async fn literal_text() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::n("© 2025 PageTop"); - assert_eq!(l10n.get(), Some("© 2025 PageTop".to_string())); -} - -#[pagetop::test] -async fn translation_without_args() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::l("test-hello-world"); - let translation = l10n.lookup(&LangMatch::resolve("es-ES")); - assert_eq!(translation, Some("¡Hola mundo!".to_string())); -} - -#[pagetop::test] -async fn translation_with_args() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::l("test-hello-user").with_arg("userName", "Manuel"); - let translation = l10n.lookup(&LangMatch::resolve("es-ES")); - assert_eq!(translation, Some("¡Hola, Manuel!".to_string())); -} - -#[pagetop::test] -async fn translation_with_plural_and_select() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::l("test-shared-photos").with_args(vec![ - ("userName", "Roberto"), - ("photoCount", "3"), - ("userGender", "male"), - ]); - let translation = l10n.lookup(&LangMatch::resolve("es-ES")).unwrap(); - assert!(translation.contains("añadido 3 nuevas fotos de él")); -} - -#[pagetop::test] -async fn check_fallback_language() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::l("test-hello-world"); - let translation = l10n.lookup(&LangMatch::resolve("xx-YY")); // Retrocede a "en-US". - assert_eq!(translation, Some("Hello world!".to_string())); -} - -#[pagetop::test] -async fn check_unknown_key() { - let _app = service::test::init_service(Application::new().test()).await; - - let l10n = L10n::l("non-existent-key"); - let translation = l10n.lookup(&LangMatch::resolve("en-US")); - assert_eq!(translation, None); -} diff --git a/tests/main.rs b/tests/main.rs new file mode 100644 index 00000000..b93d337f --- /dev/null +++ b/tests/main.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod server; diff --git a/tests/server/health_check.rs b/tests/server/health_check.rs new file mode 100644 index 00000000..578ffc66 --- /dev/null +++ b/tests/server/health_check.rs @@ -0,0 +1,14 @@ +use pagetop::prelude::*; + +struct HealthCheck; + +impl PackageTrait for HealthCheck {} + +#[pagetop::test] +async fn health_check_works() { + let app = service::test::init_service(Application::prepare(&HealthCheck).test()).await; + let req = service::test::TestRequest::get().uri("/").to_request(); + let _resp = service::test::call_service(&app, req).await; + + // assert_eq!("OK", "OK"); +} diff --git a/tests/server/mod.rs b/tests/server/mod.rs new file mode 100644 index 00000000..f56eeca6 --- /dev/null +++ b/tests/server/mod.rs @@ -0,0 +1 @@ +mod health_check; diff --git a/tests/service.rs b/tests/service.rs deleted file mode 100644 index 5aec398e..00000000 --- a/tests/service.rs +++ /dev/null @@ -1,12 +0,0 @@ -use pagetop::prelude::*; - -#[pagetop::test] -async fn homepage_returns_404() { - let app = service::test::init_service(Application::new().test()).await; - - let req = service::test::TestRequest::get().uri("/").to_request(); - let resp = service::test::call_service(&app, req).await; - - // Comprueba el acceso a la ruta de inicio. - assert_eq!(resp.status(), service::http::StatusCode::OK); -} diff --git a/tests/util.rs b/tests/util.rs deleted file mode 100644 index 70699a74..00000000 --- a/tests/util.rs +++ /dev/null @@ -1,112 +0,0 @@ -use pagetop::prelude::*; - -use std::{env, fs, io}; -use tempfile::TempDir; - -#[cfg(unix)] -mod unix { - use super::*; - - #[pagetop::test] - async fn ok_absolute_dir() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - // /tmp/<rand>/sub - let td = TempDir::new()?; - let sub = td.path().join("sub"); - fs::create_dir(&sub)?; - - let abs = util::resolve_absolute_dir(&sub)?; - assert_eq!(abs, std::fs::canonicalize(&sub)?); - Ok(()) - } - - #[pagetop::test] - async fn ok_relative_dir_with_manifest() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - let td = TempDir::new()?; - let sub = td.path().join("sub"); - fs::create_dir(&sub)?; - - // Fija CARGO_MANIFEST_DIR para que "sub" se resuelva contra td.path() - let prev_manifest_dir = env::var_os("CARGO_MANIFEST_DIR"); - env::set_var("CARGO_MANIFEST_DIR", td.path()); - let res = util::resolve_absolute_dir("sub"); - // Restaura entorno. - match prev_manifest_dir { - Some(v) => env::set_var("CARGO_MANIFEST_DIR", v), - None => env::remove_var("CARGO_MANIFEST_DIR"), - } - - assert_eq!(res?, std::fs::canonicalize(&sub)?); - Ok(()) - } - - #[pagetop::test] - async fn error_not_a_directory() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - let td = TempDir::new()?; - let file = td.path().join("foo.txt"); - fs::write(&file, b"data")?; - - let err = util::resolve_absolute_dir(&file).unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - Ok(()) - } -} - -#[cfg(windows)] -mod windows { - use super::*; - - #[pagetop::test] - async fn ok_absolute_dir() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - // C:\Users\...\Temp\... - let td = TempDir::new()?; - let sub = td.path().join("sub"); - fs::create_dir(&sub)?; - - let abs = util::resolve_absolute_dir(&sub)?; - assert_eq!(abs, std::fs::canonicalize(&sub)?); - Ok(()) - } - - #[pagetop::test] - async fn ok_relative_dir_with_manifest() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - let td = TempDir::new()?; - let sub = td.path().join("sub"); - fs::create_dir(&sub)?; - - // Fija CARGO_MANIFEST_DIR para que "sub" se resuelva contra td.path() - let prev_manifest_dir = env::var_os("CARGO_MANIFEST_DIR"); - env::set_var("CARGO_MANIFEST_DIR", td.path()); - let res = util::resolve_absolute_dir("sub"); - // Restaura entorno. - match prev_manifest_dir { - Some(v) => env::set_var("CARGO_MANIFEST_DIR", v), - None => env::remove_var("CARGO_MANIFEST_DIR"), - } - - assert_eq!(res?, std::fs::canonicalize(&sub)?); - Ok(()) - } - - #[pagetop::test] - async fn error_not_a_directory() -> io::Result<()> { - let _app = service::test::init_service(Application::new().test()).await; - - let td = TempDir::new()?; - let file = td.path().join("foo.txt"); - fs::write(&file, b"data")?; - - let err = util::resolve_absolute_dir(&file).unwrap_err(); - assert_eq!(err.kind(), io::ErrorKind::InvalidInput); - Ok(()) - } -} diff --git a/tools/changelog.sh b/tools/changelog.sh deleted file mode 100755 index bd5f20bb..00000000 --- a/tools/changelog.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# ------------------------------------------------------------------------------ -# Script para generar el archivo de cambios del crate indicado. -# Uso: -# ./tools/changelog.sh <crate> <version> [--stage] -# Ejemplo: -# ./tools/changelog.sh pagetop-macros 0.1.0 # Sólo genera archivo -# ./tools/changelog.sh pagetop 0.1.0 --stage # Prepara archivo para commit -# ------------------------------------------------------------------------------ - -# Configuración -CRATE="${1:-}" -VERSION="${2:-}" -STAGE="${3:-}" -CLIFF_CONFIG=".cargo/cliff.toml" - -# Comprobaciones -if [[ -z "$CRATE" || -z "$VERSION" ]]; then - echo "Usage: $0 <crate> <version> [--stage]" >&2 - exit 1 -fi - -# Dependencias -command -v git-cliff >/dev/null || { - echo "Error: git-cliff is not installed. Use: cargo install git-cliff" - exit 1 -} - -# Cambia al directorio del espacio -cd "$(dirname "$0")/.." || exit 1 - -# ------------------------------------------------------------------------------ -# Determina ruta del archivo y ámbito de los archivos afectados para el crate -# ------------------------------------------------------------------------------ -case "$CRATE" in - pagetop-statics) - CHANGELOG_FILE="helpers/pagetop-statics/CHANGELOG.md" - PATH_FLAGS=(--include-path "helpers/pagetop-statics/**/*") - ;; - pagetop-build) - CHANGELOG_FILE="helpers/pagetop-build/CHANGELOG.md" - PATH_FLAGS=(--include-path "helpers/pagetop-build/**/*") - ;; - pagetop-macros) - CHANGELOG_FILE="helpers/pagetop-macros/CHANGELOG.md" - PATH_FLAGS=(--include-path "helpers/pagetop-macros/**/*") - ;; - pagetop) - CHANGELOG_FILE="CHANGELOG.md" - PATH_FLAGS=( - # Helpers - --exclude-path "helpers/pagetop-statics/**/*" - --exclude-path "helpers/pagetop-build/**/*" - --exclude-path "helpers/pagetop-macros/**/*" - # Extensions - --exclude-path "extensions/pagetop-aliner/**/*" - --exclude-path "extensions/pagetop-bootsier/**/*" - ) - ;; - pagetop-aliner) - CHANGELOG_FILE="extensions/pagetop-aliner/CHANGELOG.md" - PATH_FLAGS=(--include-path "extensions/pagetop-aliner/**/*") - ;; - pagetop-bootsier) - CHANGELOG_FILE="extensions/pagetop-bootsier/CHANGELOG.md" - PATH_FLAGS=(--include-path "extensions/pagetop-bootsier/**/*") - ;; - *) - echo "Error: unsupported crate '$CRATE'" >&2 - exit 1 - ;; -esac - -# ------------------------------------------------------------------------------ -# Genera el CHANGELOG para el crate correspondiente -# ------------------------------------------------------------------------------ -if [[ -f "$CHANGELOG_FILE" ]]; then - # Archivo existe: inserta la nueva sección arriba - OUTPUT_FLAG=(--prepend "$CHANGELOG_FILE") -else - # Primera vez: crea el fichero desde cero - OUTPUT_FLAG=(-o "$CHANGELOG_FILE") -fi -COMMON_ARGS=( - --config "$CLIFF_CONFIG" - "${PATH_FLAGS[@]}" - --tag-pattern "^${CRATE}-v" - --tag "$VERSION" - "${OUTPUT_FLAG[@]}" -) -LAST_TAG="$(git tag --list "${CRATE}-v*" --sort=-v:refname | head -n 1)" -if [[ -n "$LAST_TAG" ]]; then - echo "Generating CHANGELOG for '$CRATE' from tag '$LAST_TAG'" -else - echo "Generating initial CHANGELOG for '$CRATE'" -fi -git-cliff --unreleased "${COMMON_ARGS[@]}" -echo "CHANGELOG generated at '$CHANGELOG_FILE'" - -# Pregunta por la revisión del archivo de cambios generado -echo "Do you want to review the changelog before continuing? [y/N]" -read -r REPLY -if [[ "$REPLY" =~ ^[Yy]$ ]]; then - ${EDITOR:-nano} "$CHANGELOG_FILE" -fi -echo "Do you want to proceed with the release of $CRATE? [y/N]" -read -r REPLY -echo -if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then - echo "Aborting release process." >&2 - exit 1 -fi - -# Si hay cambios y procede, añade al stage (cargo-release hará el commit) -if [[ -n $(git status --porcelain -- "$CHANGELOG_FILE") ]]; then - if [[ "$STAGE" == "--stage" ]]; then - git add "$CHANGELOG_FILE" - echo "Staged $CHANGELOG_FILE for commit" - else - echo "Changes detected in '$CHANGELOG_FILE', but not staged (no --stage flag)" - fi -else - echo "No changes in '$CHANGELOG_FILE', skipping staging" -fi diff --git a/tools/publish.sh b/tools/publish.sh new file mode 100755 index 00000000..168752dd --- /dev/null +++ b/tools/publish.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Navigate to the root workspace directory +cd "$(dirname "$0")" +cd .. + +# Check if there are unstaged changes in the Git repository +if [ -n "$(git status --porcelain)" ]; then + echo "You have local changes!" + exit 1 +fi + +# Updates the 'latest' branch with changes from 'main' +read -p "Do you want to update the 'latest' branch? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]] +then + echo "UPDATING 'latest' branch" + git checkout latest + git merge main + echo "PUSHING updated 'latest' branch to remote repository" + git push origin latest + git checkout main +else + echo "Omitting update of 'latest' branch" +fi + + +# Define a function to publish a crate to crates.io +function publish_crate() { + echo -e "\nPUBLISHING $CRATE" + # Get the last published version from crates.io + PUBLISHED_VERSION=$(cargo search "$CRATE " | grep "^$CRATE = " | sed -E 's/^.*"([^"]+)".*$/\1/') + # Read the current version from Cargo.toml + CURRENT_VERSION=$(grep '^version = ' Cargo.toml | head -n 1 | sed -E 's/^version = "([^"]+)".*$/\1/') + # Compare the versions + if [ "$PUBLISHED_VERSION" = "$CURRENT_VERSION" ]; then + echo "Skipping version $CURRENT_VERSION as it already exists on crates.io" + else + echo "Publishing version $CURRENT_VERSION..." + if [ "$CRATE" = "pagetop" ]; then + cargo publish + else + cp ../../LICENSE-MIT . + cp ../../LICENSE-APACHE . + git add LICENSE-MIT LICENSE-APACHE + cargo publish --allow-dirty + fi + sleep 20 + fi +} + +# If package A depends on package B, B must come before A in this list +HELPERS=( + pagetop-macros + pagetop-build +) + +# Publish all helper crates +pushd helpers > /dev/null 2>&1 +for CRATE in "${HELPERS[@]}"; do + pushd "$CRATE" > /dev/null 2>&1 + publish_crate + popd > /dev/null 2>&1 +done +popd > /dev/null 2>&1 + +# Publish the root crate +CRATE=pagetop; publish_crate + +# Reset local Git repository to clean licenses after publishing +echo -e "\nCleaning local state" +git reset HEAD --hard diff --git a/tools/release.sh b/tools/release.sh deleted file mode 100755 index bb092416..00000000 --- a/tools/release.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# ------------------------------------------------------------------------------ -# Script para publicar un crate individual del workspace con cargo-release. -# Uso: -# ./tools/release.sh <crate> [patch|minor|major] [--execute] -# Ejemplos: -# ./tools/release.sh pagetop-macros patch # Dry run (por defecto) -# ./tools/release.sh pagetop minor --execute # Publicación real -# ------------------------------------------------------------------------------ - -# Configuración -CRATE="${1:-}" -LEVEL="${2:-patch}" -EXECUTE="${3:-}" -CONFIG=".cargo/release.toml" - -# Comprobaciones -if [[ -z "$CRATE" ]]; then - echo "Usage: $0 <crate> [patch|minor|major] [--execute]" - exit 1 -fi -if [[ ! "$LEVEL" =~ ^(patch|minor|major)$ ]]; then - echo "Error: invalid level '$LEVEL'. Use: patch, minor, or major" - exit 1 -fi - -# Dependencias -command -v cargo-release >/dev/null || { - echo "Error: cargo-release is not installed. Use: cargo install cargo-release" - exit 1 -} - -# Cambia al directorio del espacio -cd "$(dirname "$0")/.." || exit 1 - -# ------------------------------------------------------------------------------ -# DRY-RUN (por defecto) o ejecución real con --execute -# ------------------------------------------------------------------------------ -if [[ "$EXECUTE" != "--execute" ]]; then - echo "Running dry-run (default mode). Add --execute to publish" - cargo release --config "$CONFIG" --package "$CRATE" "$LEVEL" -else - echo "Releasing $CRATE ($LEVEL)…" - cargo release --config "$CONFIG" --package "$CRATE" "$LEVEL" --execute - echo "Release completed." -fi