WIP: Añade componente para la gestión de menús #8
243 changed files with 24803 additions and 1114 deletions
|
|
@ -25,7 +25,7 @@ internos pueden omitirse si no afectan al uso del proyecto.
|
|||
- [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_component`
|
||||
- Renombra `with_component` por `add_child`
|
||||
|
||||
### Corregido
|
||||
|
||||
|
|
|
|||
300
Cargo.lock
generated
300
Cargo.lock
generated
|
|
@ -44,9 +44,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.11.1"
|
||||
version = "3.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cceded2fb55f3c4b67068fa64962e2ca59614edc5b03167de9ff82ae803da0"
|
||||
checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
|
|
@ -227,9 +227,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
|
@ -328,9 +328,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.20"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
|
|
@ -343,9 +343,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.11"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
|
|
@ -390,9 +390,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
|
|
@ -400,7 +400,7 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -484,9 +484,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.38"
|
||||
version = "1.2.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
|
|
@ -520,7 +520,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -589,9 +589,9 @@ checksum = "7439becb5fafc780b6f4de382b1a7a3e70234afe783854a4702ee8adbb838609"
|
|||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.15.16"
|
||||
version = "0.15.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef036f0ecf99baef11555578630e2cca559909b4c50822dbba828c252d21c49"
|
||||
checksum = "180e549344080374f9b32ed41bf3b6b57885ff6a289367b3dbc10eea8acc1918"
|
||||
dependencies = [
|
||||
"pathdiff",
|
||||
"serde_core",
|
||||
|
|
@ -703,9 +703,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
|
||||
checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
|
@ -788,7 +788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -805,15 +805,15 @@ checksum = "4742a071cd9694fc86f9fa1a08fa3e53d40cc899d7ee532295da2d085639fbc5"
|
|||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.2"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
|
|
@ -851,7 +851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.16",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -994,9 +994,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
version = "0.32.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
|
|
@ -1356,9 +1356,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.80"
|
||||
version = "0.3.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e"
|
||||
checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -1387,9 +1387,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.175"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
|
@ -1422,11 +1422,10 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
|
|
@ -1447,9 +1446,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
|
|
@ -1474,6 +1473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1520,9 +1520,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -1560,12 +1560,15 @@ dependencies = [
|
|||
"fluent-templates",
|
||||
"indoc",
|
||||
"itoa",
|
||||
"pagetop-aliner",
|
||||
"pagetop-bootsier",
|
||||
"pagetop-build",
|
||||
"pagetop-macros",
|
||||
"pagetop-statics",
|
||||
"parking_lot",
|
||||
"pastey",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"substring",
|
||||
"tempfile",
|
||||
"terminal_size",
|
||||
|
|
@ -1576,6 +1579,23 @@ 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"
|
||||
|
|
@ -1608,9 +1628,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
|
|
@ -1618,15 +1638,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1813,9 +1833,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -1887,18 +1907,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.2"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
||||
checksum = "4a52d8d02cacdb176ef4678de6c052efb4b3da14b78e4db683a4252762be5433"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
@ -1908,9 +1928,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.10"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
|
||||
checksum = "722166aa0d7438abbaa4d5cc2c649dac844e8c56d82fb3d33e9c34b5cd268fc6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
@ -1960,7 +1980,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2004,9 +2024,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.225"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
|
|
@ -2014,18 +2034,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.225"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.225"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2047,9 +2067,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
|
||||
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
|
@ -2112,6 +2132,12 @@ 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"
|
||||
|
|
@ -2161,9 +2187,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
|
|
@ -2210,15 +2236,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.22.0"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2242,11 +2268,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.16"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.16",
|
||||
"thiserror-impl 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2262,9 +2288,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.16"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2355,9 +2381,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.7"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
|
||||
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
|
|
@ -2368,18 +2394,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
|
||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
|
||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
|
@ -2495,9 +2521,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid"
|
||||
|
|
@ -2659,9 +2685,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.103"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819"
|
||||
checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
|
@ -2672,9 +2698,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.103"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c"
|
||||
checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
|
@ -2686,9 +2712,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.103"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0"
|
||||
checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -2696,9 +2722,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.103"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32"
|
||||
checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2709,9 +2735,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.103"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf"
|
||||
checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -2722,27 +2748,27 @@ version = "0.1.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.0"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2751,9 +2777,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2762,32 +2788,26 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2814,16 +2834,16 @@ version = "0.60.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.3",
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.0"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link 0.2.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2844,19 +2864,19 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2867,9 +2887,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
|
@ -2879,9 +2899,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
|
@ -2891,9 +2911,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
|
|
@ -2903,9 +2923,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
|
@ -2915,9 +2935,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
|
@ -2927,9 +2947,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
|
|
@ -2939,9 +2959,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
|
@ -2951,9 +2971,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
|
|
|||
19
Cargo.toml
19
Cargo.toml
|
|
@ -39,7 +39,7 @@ 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 = { version = "1.0", features = ["derive"] }
|
||||
serde.workspace = true
|
||||
|
||||
pagetop-macros.workspace = true
|
||||
pagetop-statics.workspace = true
|
||||
|
|
@ -49,7 +49,10 @@ default = []
|
|||
testing = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.22"
|
||||
tempfile = "3.23"
|
||||
serde_json = "1.0"
|
||||
pagetop-aliner.workspace = true
|
||||
pagetop-bootsier.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
pagetop-build.workspace = true
|
||||
|
|
@ -58,9 +61,13 @@ 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]
|
||||
|
|
@ -71,7 +78,13 @@ authors = ["Manuel Cillero <manuel@cillero.es>"]
|
|||
|
||||
[workspace.dependencies]
|
||||
actix-web = { version = "4.11", default-features = false }
|
||||
|
||||
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 = "." }
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
<p>Un entorno para el desarrollo de soluciones web modulares, extensibles y configurables.</p>
|
||||
|
||||
[](#-licencia)
|
||||
[](https://docs.rs/pagetop)
|
||||
[](https://crates.io/crates/pagetop)
|
||||
[](https://crates.io/crates/pagetop)
|
||||
[](https://git.cillero.es/manuelcillero/pagetop#licencia)
|
||||
|
||||
<br>
|
||||
</div>
|
||||
|
|
@ -60,7 +60,7 @@ impl Extension for HelloWorld {
|
|||
|
||||
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
Page::new(request)
|
||||
.add_component(Html::with(move |_| html! { h1 { "Hello World!" } }))
|
||||
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
|
||||
.render()
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +96,15 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui
|
|||
* **[pagetop-macros](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/helpers/pagetop-macros)**,
|
||||
proporciona una colección de macros que mejoran la experiencia de desarrollo con PageTop.
|
||||
|
||||
## Extensiones
|
||||
|
||||
* **[pagetop-aliner](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner)**,
|
||||
es un tema para demos y pruebas que muestra esquemáticamente la composición de las páginas HTML.
|
||||
|
||||
* **[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.
|
||||
|
||||
|
||||
# 🧪 Pruebas
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ async fn hello_name(
|
|||
) -> ResultPage<Markup, ErrorPage> {
|
||||
let name = path.into_inner();
|
||||
Page::new(request)
|
||||
.add_component(Html::with(move |_| html! { h1 { "Hello " (name) "!" } }))
|
||||
.add_child(Html::with(move |_| html! { h1 { "Hello " (name) "!" } }))
|
||||
.render()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ impl Extension for HelloWorld {
|
|||
|
||||
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
Page::new(request)
|
||||
.add_component(Html::with(move |_| html! { h1 { "Hello World!" } }))
|
||||
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
|
||||
.render()
|
||||
}
|
||||
|
||||
|
|
|
|||
109
examples/navbar-menus.rs
Normal file
109
examples/navbar-menus.rs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
use pagetop::prelude::*;
|
||||
|
||||
use pagetop_bootsier::prelude::*;
|
||||
|
||||
struct SuperMenu;
|
||||
|
||||
impl Extension for SuperMenu {
|
||||
fn dependencies(&self) -> Vec<ExtensionRef> {
|
||||
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::Key("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
|
||||
}
|
||||
21
extensions/pagetop-aliner/Cargo.toml
Normal file
21
extensions/pagetop-aliner/Cargo.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[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
|
||||
201
extensions/pagetop-aliner/LICENSE-APACHE
Normal file
201
extensions/pagetop-aliner/LICENSE-APACHE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
||||
21
extensions/pagetop-aliner/LICENSE-MIT
Normal file
21
extensions/pagetop-aliner/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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.
|
||||
100
extensions/pagetop-aliner/README.md
Normal file
100
extensions/pagetop-aliner/README.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<div align="center">
|
||||
|
||||
<h1>PageTop Aliner</h1>
|
||||
|
||||
<p>Tema de <strong>PageTop</strong> que muestra esquemáticamente la composición de las páginas HTML.</p>
|
||||
|
||||
[](https://docs.rs/pagetop-aliner)
|
||||
[](https://crates.io/crates/pagetop-aliner)
|
||||
[](https://crates.io/crates/pagetop-aliner)
|
||||
[](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner#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-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<ExtensionRef> {
|
||||
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::*;
|
||||
|
||||
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
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.
|
||||
7
extensions/pagetop-aliner/build.rs
Normal file
7
extensions/pagetop-aliner/build.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use pagetop_build::StaticFilesBundle;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
StaticFilesBundle::from_dir("./static", None)
|
||||
.with_name("aliner")
|
||||
.build()
|
||||
}
|
||||
118
extensions/pagetop-aliner/src/lib.rs
Normal file
118
extensions/pagetop-aliner/src/lib.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*!
|
||||
<div align="center">
|
||||
|
||||
<h1>PageTop Aliner</h1>
|
||||
|
||||
<p>Tema para <strong>PageTop</strong> que muestra esquemáticamente la composición de las páginas HTML.</p>
|
||||
|
||||
[](https://docs.rs/pagetop-aliner)
|
||||
[](https://crates.io/crates/pagetop-aliner)
|
||||
[](https://crates.io/crates/pagetop-aliner)
|
||||
[](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-aliner#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-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<ExtensionRef> {
|
||||
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::*;
|
||||
|
||||
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||
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::*;
|
||||
|
||||
/// El tema usa las mismas regiones predefinidas por [`ThemeRegion`].
|
||||
pub type AlinerRegion = ThemeRegion;
|
||||
|
||||
/// Implementa el tema para usar en pruebas que muestran el esquema de páginas HTML.
|
||||
///
|
||||
/// Tema mínimo ideal para **pruebas y demos** que renderiza el **esqueleto HTML** con las mismas
|
||||
/// regiones básicas definidas por [`ThemeRegion`]. No pretende ser un tema para producción, está
|
||||
/// pensado para:
|
||||
///
|
||||
/// - 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<ThemeRef> {
|
||||
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),
|
||||
));
|
||||
}
|
||||
}
|
||||
356
extensions/pagetop-aliner/static/css/styles.css
Normal file
356
extensions/pagetop-aliner/static/css/styles.css
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
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>"; }
|
||||
a::after { content: "</a>"; }
|
||||
abbr::before { content: "<abbr>"; }
|
||||
abbr::after { content: "</abbr>"; }
|
||||
acronym::before { content: "<acronym>"; }
|
||||
acronym::after { content: "</acronym>"; }
|
||||
address::before { content: "<address>"; }
|
||||
address::after { content: "</address>"; }
|
||||
applet::before { content: "<applet>"; }
|
||||
applet::after { content: "</applet>"; }
|
||||
area::before { content: "<area>"; }
|
||||
area::after { content: "</area>"; }
|
||||
article::before { content: "<article>"; }
|
||||
article::after { content: "</article>"; }
|
||||
aside::before { content: "<aside>"; }
|
||||
aside::after { content: "</aside>"; }
|
||||
audio::before { content: "<audio>"; }
|
||||
audio::after { content: "</audio>"; }
|
||||
|
||||
b::before { content: "<b>"; }
|
||||
b::after { content: "</b>"; }
|
||||
base::before { content: "<base>"; }
|
||||
base::after { content: "</base>"; }
|
||||
basefont::before { content: "<basefont>"; }
|
||||
basefont::after { content: "</basefont>"; }
|
||||
bdi::before { content: "<bdi>"; }
|
||||
bdi::after { content: "</bdi>"; }
|
||||
bdo::before { content: "<bdo>"; }
|
||||
bdo::after { content: "</bdo>"; }
|
||||
bgsound::before { content: "<bgsound>"; }
|
||||
bgsound::after { content: "</bgsound>"; }
|
||||
big::before { content: "<big>"; }
|
||||
big::after { content: "</big>"; }
|
||||
blink::before { content: "<blink>"; }
|
||||
blink::after { content: "</blink>"; }
|
||||
blockquote::before { content: "<blockquote>"; }
|
||||
blockquote::after { content: "</blockquote>"; }
|
||||
body::before { content: "<body>"; }
|
||||
body::after { content: "</body>"; }
|
||||
br::before { content: "<br>"; }
|
||||
br::after { content: "</br>"; }
|
||||
button::before { content: "<button>"; }
|
||||
button::after { content: "</button>"; }
|
||||
|
||||
caption::before { content: "<caption>"; }
|
||||
caption::after { content: "</caption>"; }
|
||||
canvas::before { content: "<canvas>"; }
|
||||
canvas::after { content: "</canvas>"; }
|
||||
center::before { content: "<center>"; }
|
||||
center::after { content: "</center>"; }
|
||||
cite::before { content: "<cite>"; }
|
||||
cite::after { content: "</cite>"; }
|
||||
code::before { content: "<code>"; }
|
||||
code::after { content: "</code>"; }
|
||||
col::before { content: "<col>"; }
|
||||
col::after { content: "</col>"; }
|
||||
colgroup::before { content: "<colgroup>"; }
|
||||
colgroup::after { content: "</colgroup>"; }
|
||||
command::before { content: "<command>"; }
|
||||
command::after { content: "</command>"; }
|
||||
content::before { content: "<content>"; }
|
||||
content::after { content: "</content>"; }
|
||||
|
||||
data::before { content: "<data>"; }
|
||||
data::after { content: "</data>"; }
|
||||
datalist::before { content: "<datalist>"; }
|
||||
datalist::after { content: "</datalist>"; }
|
||||
dd::before { content: "<dd>"; }
|
||||
dd::after { content: "</dd>"; }
|
||||
del::before { content: "<del>"; }
|
||||
del::after { content: "</del>"; }
|
||||
details::before { content: "<details>"; }
|
||||
details::after { content: "</details>"; }
|
||||
dfn::before { content: "<dfn>"; }
|
||||
dfn::after { content: "</dfn>"; }
|
||||
dialog::before { content: "<dialog>"; }
|
||||
dialog::after { content: "</dialog>"; }
|
||||
dir::before { content: "<dir>"; }
|
||||
dir::after { content: "</dir>"; }
|
||||
div::before { content: "<div>"; }
|
||||
div::after { content: "</div>"; }
|
||||
dl::before { content: "<dl>"; }
|
||||
dl::after { content: "</dl>"; }
|
||||
dt::before { content: "<dt>"; }
|
||||
dt::after { content: "</dt>"; }
|
||||
|
||||
element::before { content: "<element>"; }
|
||||
element::after { content: "</element>"; }
|
||||
em::before { content: "<em>"; }
|
||||
em::after { content: "</em>"; }
|
||||
embed::before { content: "<embed>"; }
|
||||
embed::after { content: "</embed>"; }
|
||||
|
||||
fieldset::before { content: "<fieldset>"; }
|
||||
fieldset::after { content: "</fieldset>"; }
|
||||
figcaption::before { content: "<figcaption>"; }
|
||||
figcaption::after { content: "</figcaption>"; }
|
||||
figure::before { content: "<figure>"; }
|
||||
figure::after { content: "</figure>"; }
|
||||
font::before { content: "<font>"; }
|
||||
font::after { content: "</font>"; }
|
||||
footer::before { content: "<footer>"; }
|
||||
footer::after { content: "</footer>"; }
|
||||
form::before { content: "<form>"; }
|
||||
form::after { content: "</form>"; }
|
||||
frame::before { content: "<frame>"; }
|
||||
frame::after { content: "</frame>"; }
|
||||
frameset::before { content: "<frameset>"; }
|
||||
frameset::after { content: "</frameset>"; }
|
||||
|
||||
h1::before { content: "<h1>"; }
|
||||
h1::after { content: "</h1>"; }
|
||||
h2::before { content: "<h2>"; }
|
||||
h2::after { content: "</h2>"; }
|
||||
h3::before { content: "<h3>"; }
|
||||
h3::after { content: "</h3>"; }
|
||||
h4::before { content: "<h4>"; }
|
||||
h4::after { content: "</h4>"; }
|
||||
h5::before { content: "<h5>"; }
|
||||
h5::after { content: "</h5>"; }
|
||||
h6::before { content: "<h6>"; }
|
||||
h6::after { content: "</h6>"; }
|
||||
head::before { content: "<head>"; }
|
||||
head::after { content: "</head>"; }
|
||||
header::before { content: "<header>"; }
|
||||
header::after { content: "</header>"; }
|
||||
hgroup::before { content: "<hgroup>"; }
|
||||
hgroup::after { content: "</hgroup>"; }
|
||||
hr::before { content: "<hr>"; }
|
||||
hr::after { content: "</hr>"; }
|
||||
html::before { content: "<html>"; }
|
||||
html::after { content: "</html>"; }
|
||||
|
||||
i::before { content: "<i>"; }
|
||||
i::after { content: "</i>"; }
|
||||
iframe::before { content: "<iframe>"; }
|
||||
iframe::after { content: "</iframe>"; }
|
||||
image::before { content: "<image>"; }
|
||||
image::after { content: "</image>"; }
|
||||
img::before { content: "<img>"; }
|
||||
img::after { content: "</img>"; }
|
||||
input::before { content: "<input>"; }
|
||||
input::after { content: "</input>"; }
|
||||
ins::before { content: "<ins>"; }
|
||||
ins::after { content: "</ins>"; }
|
||||
isindex::before { content: "<isindex>"; }
|
||||
isindex::after { content: "</isindex>"; }
|
||||
|
||||
kbd::before { content: "<kbd>"; }
|
||||
kbd::after { content: "</kbd>"; }
|
||||
keygen::before { content: "<keygen>"; }
|
||||
keygen::after { content: "</keygen>"; }
|
||||
|
||||
label::before { content: "<label>"; }
|
||||
label::after { content: "</label>"; }
|
||||
legend::before { content: "<legend>"; }
|
||||
legend::after { content: "</legend>"; }
|
||||
li::before { content: "<li>"; }
|
||||
li::after { content: "</li>"; }
|
||||
link::before { content: "<link>"; }
|
||||
link::after { content: "</link>"; }
|
||||
listing::before { content: "<listing>"; }
|
||||
listing::after { content: "</listing>"; }
|
||||
|
||||
main::before { content: "<main>"; }
|
||||
main::after { content: "</main>"; }
|
||||
map::before { content: "<map>"; }
|
||||
map::after { content: "</map>"; }
|
||||
mark::before { content: "<mark>"; }
|
||||
mark::after { content: "</mark>"; }
|
||||
marquee::before { content: "<marquee>"; }
|
||||
marquee::after { content: "</marquee>"; }
|
||||
menu::before { content: "<menu>"; }
|
||||
menu::after { content: "</menu>"; }
|
||||
menuitem::before { content: "<menuitem>"; }
|
||||
menuitem::after { content: "</menuitem>"; }
|
||||
meta::before { content: "<meta>"; }
|
||||
meta::after { content: "</meta>"; }
|
||||
meter::before { content: "<meter>"; }
|
||||
meter::after { content: "</meter>"; }
|
||||
multicol::before { content: "<multicol>"; }
|
||||
multicol::after { content: "</multicol>"; }
|
||||
|
||||
nav::before { content: "<nav>"; }
|
||||
nav::after { content: "</nav>"; }
|
||||
nextid::before { content: "<nextid>"; }
|
||||
nextid::after { content: "</nextid>"; }
|
||||
nobr::before { content: "<nobr>"; }
|
||||
nobr::after { content: "</nobr>"; }
|
||||
noembed::before { content: "<noembed>"; }
|
||||
noembed::after { content: "</noembed>"; }
|
||||
noframes::before { content: "<noframes>"; }
|
||||
noframes::after { content: "</noframes>"; }
|
||||
noscript::before { content: "<noscript>"; }
|
||||
noscript::after { content: "</noscript>"; }
|
||||
|
||||
object::before { content: "<object>"; }
|
||||
object::after { content: "</object>"; }
|
||||
ol::before { content: "<ol>"; }
|
||||
ol::after { content: "</ol>"; }
|
||||
optgroup::before { content: "<optgroup>"; }
|
||||
optgroup::after { content: "</optgroup>"; }
|
||||
option::before { content: "<option>"; }
|
||||
option::after { content: "</option>"; }
|
||||
output::before { content: "<output>"; }
|
||||
output::after { content: "</output>"; }
|
||||
|
||||
p::before { content: "<p>"; }
|
||||
p::after { content: "</p>"; }
|
||||
param::before { content: "<param>"; }
|
||||
param::after { content: "</param>"; }
|
||||
picture::before { content: "<picture>"; }
|
||||
picture::after { content: "</picture>"; }
|
||||
plaintext::before { content: "<plaintext>"; }
|
||||
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>"; }
|
||||
1
extensions/pagetop-bootsier/.gitattributes
vendored
Normal file
1
extensions/pagetop-bootsier/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
static/** linguist-vendored
|
||||
22
extensions/pagetop-bootsier/Cargo.toml
Normal file
22
extensions/pagetop-bootsier/Cargo.toml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
[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
|
||||
201
extensions/pagetop-bootsier/LICENSE-APACHE
Normal file
201
extensions/pagetop-bootsier/LICENSE-APACHE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
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.
|
||||
21
extensions/pagetop-bootsier/LICENSE-MIT
Normal file
21
extensions/pagetop-bootsier/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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.
|
||||
100
extensions/pagetop-bootsier/README.md
Normal file
100
extensions/pagetop-bootsier/README.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<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>
|
||||
|
||||
[](https://docs.rs/pagetop-bootsier)
|
||||
[](https://crates.io/crates/pagetop-bootsier)
|
||||
[](https://crates.io/crates/pagetop-bootsier)
|
||||
[](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::*;
|
||||
|
||||
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.
|
||||
20
extensions/pagetop-bootsier/build.rs
Normal file
20
extensions/pagetop-bootsier/build.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
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)
|
||||
}
|
||||
41
extensions/pagetop-bootsier/src/config.rs
Normal file
41
extensions/pagetop-bootsier/src/config.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! 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,
|
||||
}
|
||||
134
extensions/pagetop-bootsier/src/lib.rs
Normal file
134
extensions/pagetop-bootsier/src/lib.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*!
|
||||
<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>
|
||||
|
||||
[](https://docs.rs/pagetop-bootsier)
|
||||
[](https://crates.io/crates/pagetop-bootsier)
|
||||
[](https://crates.io/crates/pagetop-bootsier)
|
||||
[](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::*;
|
||||
|
||||
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::*;
|
||||
}
|
||||
|
||||
/// El tema usa las mismas regiones predefinidas por [`ThemeRegion`].
|
||||
pub type BootsierRegion = ThemeRegion;
|
||||
|
||||
/// 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),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Dropdown
|
||||
dropdown_toggle = Toggle Dropdown
|
||||
|
||||
# Offcanvas
|
||||
offcanvas_close = Close
|
||||
|
||||
# Navbar
|
||||
toggle = Toggle navigation
|
||||
9
extensions/pagetop-bootsier/src/locale/en-US/regions.ftl
Normal file
9
extensions/pagetop-bootsier/src/locale/en-US/regions.ftl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Dropdown
|
||||
dropdown_toggle = Mostrar/ocultar menú
|
||||
|
||||
# Offcanvas
|
||||
offcanvas_close = Cerrar
|
||||
|
||||
# Navbar
|
||||
toggle = Mostrar/ocultar navegación
|
||||
9
extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl
Normal file
9
extensions/pagetop-bootsier/src/locale/es-ES/regions.ftl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
40
extensions/pagetop-bootsier/src/theme.rs
Normal file
40
extensions/pagetop-bootsier/src/theme.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//! 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;
|
||||
20
extensions/pagetop-bootsier/src/theme/aux.rs
Normal file
20
extensions/pagetop-bootsier/src/theme/aux.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//! 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};
|
||||
87
extensions/pagetop-bootsier/src/theme/aux/border.rs
Normal file
87
extensions/pagetop-bootsier/src/theme/aux/border.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
114
extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs
Normal file
114
extensions/pagetop-bootsier/src/theme/aux/breakpoint.rs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
143
extensions/pagetop-bootsier/src/theme/aux/button.rs
Normal file
143
extensions/pagetop-bootsier/src/theme/aux/button.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
375
extensions/pagetop-bootsier/src/theme/aux/color.rs
Normal file
375
extensions/pagetop-bootsier/src/theme/aux/color.rs
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
104
extensions/pagetop-bootsier/src/theme/aux/layout.rs
Normal file
104
extensions/pagetop-bootsier/src/theme/aux/layout.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
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,
|
||||
}
|
||||
117
extensions/pagetop-bootsier/src/theme/aux/rounded.rs
Normal file
117
extensions/pagetop-bootsier/src/theme/aux/rounded.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
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("")
|
||||
}
|
||||
}
|
||||
13
extensions/pagetop-bootsier/src/theme/classes.rs
Normal file
13
extensions/pagetop-bootsier/src/theme/classes.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//! 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};
|
||||
175
extensions/pagetop-bootsier/src/theme/classes/border.rs
Normal file
175
extensions/pagetop-bootsier/src/theme/classes/border.rs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
230
extensions/pagetop-bootsier/src/theme/classes/color.rs
Normal file
230
extensions/pagetop-bootsier/src/theme/classes/color.rs
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
205
extensions/pagetop-bootsier/src/theme/classes/layout.rs
Normal file
205
extensions/pagetop-bootsier/src/theme/classes/layout.rs
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
169
extensions/pagetop-bootsier/src/theme/classes/rounded.rs
Normal file
169
extensions/pagetop-bootsier/src/theme/classes/rounded.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
24
extensions/pagetop-bootsier/src/theme/container.rs
Normal file
24
extensions/pagetop-bootsier/src/theme/container.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
//! 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;
|
||||
184
extensions/pagetop-bootsier/src/theme/container/component.rs
Normal file
184
extensions/pagetop-bootsier/src/theme/container/component.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
72
extensions/pagetop-bootsier/src/theme/container/props.rs
Normal file
72
extensions/pagetop-bootsier/src/theme/container/props.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
extensions/pagetop-bootsier/src/theme/dropdown.rs
Normal file
34
extensions/pagetop-bootsier/src/theme/dropdown.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//! 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};
|
||||
302
extensions/pagetop-bootsier/src/theme/dropdown/component.rs
Normal file
302
extensions/pagetop-bootsier/src/theme/dropdown/component.rs
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
281
extensions/pagetop-bootsier/src/theme/dropdown/item.rs
Normal file
281
extensions/pagetop-bootsier/src/theme/dropdown/item.rs
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
226
extensions/pagetop-bootsier/src/theme/dropdown/props.rs
Normal file
226
extensions/pagetop-bootsier/src/theme/dropdown/props.rs
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
7
extensions/pagetop-bootsier/src/theme/image.rs
Normal file
7
extensions/pagetop-bootsier/src/theme/image.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//! Definiciones para renderizar imágenes ([`Image`]).
|
||||
|
||||
mod props;
|
||||
pub use props::{Size, Source};
|
||||
|
||||
mod component;
|
||||
pub use component::Image;
|
||||
141
extensions/pagetop-bootsier/src/theme/image/component.rs
Normal file
141
extensions/pagetop-bootsier/src/theme/image/component.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
108
extensions/pagetop-bootsier/src/theme/image/props.rs
Normal file
108
extensions/pagetop-bootsier/src/theme/image/props.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
37
extensions/pagetop-bootsier/src/theme/nav.rs
Normal file
37
extensions/pagetop-bootsier/src/theme/nav.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//! 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};
|
||||
135
extensions/pagetop-bootsier/src/theme/nav/component.rs
Normal file
135
extensions/pagetop-bootsier/src/theme/nav/component.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
284
extensions/pagetop-bootsier/src/theme/nav/item.rs
Normal file
284
extensions/pagetop-bootsier/src/theme/nav/item.rs
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
120
extensions/pagetop-bootsier/src/theme/nav/props.rs
Normal file
120
extensions/pagetop-bootsier/src/theme/nav/props.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
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()
|
||||
} */
|
||||
}
|
||||
136
extensions/pagetop-bootsier/src/theme/navbar.rs
Normal file
136
extensions/pagetop-bootsier/src/theme/navbar.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
//! 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;
|
||||
111
extensions/pagetop-bootsier/src/theme/navbar/brand.rs
Normal file
111
extensions/pagetop-bootsier/src/theme/navbar/brand.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
293
extensions/pagetop-bootsier/src/theme/navbar/component.rs
Normal file
293
extensions/pagetop-bootsier/src/theme/navbar/component.rs
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
95
extensions/pagetop-bootsier/src/theme/navbar/item.rs
Normal file
95
extensions/pagetop-bootsier/src/theme/navbar/item.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
98
extensions/pagetop-bootsier/src/theme/navbar/props.rs
Normal file
98
extensions/pagetop-bootsier/src/theme/navbar/props.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
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()
|
||||
} */
|
||||
}
|
||||
27
extensions/pagetop-bootsier/src/theme/offcanvas.rs
Normal file
27
extensions/pagetop-bootsier/src/theme/offcanvas.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//! 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;
|
||||
240
extensions/pagetop-bootsier/src/theme/offcanvas/component.rs
Normal file
240
extensions/pagetop-bootsier/src/theme/offcanvas/component.rs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
119
extensions/pagetop-bootsier/src/theme/offcanvas/props.rs
Normal file
119
extensions/pagetop-bootsier/src/theme/offcanvas/props.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
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()
|
||||
} */
|
||||
}
|
||||
7
extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js
vendored
Normal file
7
extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js.map
vendored
Normal file
1
extensions/pagetop-bootsier/static/js/bootstrap.bundle.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
4494
extensions/pagetop-bootsier/static/js/bootstrap.js
vendored
Normal file
4494
extensions/pagetop-bootsier/static/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
extensions/pagetop-bootsier/static/js/bootstrap.js.map
vendored
Normal file
1
extensions/pagetop-bootsier/static/js/bootstrap.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
108
extensions/pagetop-bootsier/static/scss/_customs.scss
vendored
Normal file
108
extensions/pagetop-bootsier/static/scss/_customs.scss
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// 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)
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
55
extensions/pagetop-bootsier/static/scss/bootsier.scss
vendored
Normal file
55
extensions/pagetop-bootsier/static/scss/bootsier.scss
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
@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
|
||||
153
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_accordion.scss
vendored
Normal file
153
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_accordion.scss
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// 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)};
|
||||
}
|
||||
}
|
||||
}
|
||||
68
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_alert.scss
vendored
Normal file
68
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_alert.scss
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.alert {
|
||||
// scss-docs-start alert-css-vars
|
||||
--#{$prefix}alert-bg: transparent;
|
||||
--#{$prefix}alert-padding-x: #{$alert-padding-x};
|
||||
--#{$prefix}alert-padding-y: #{$alert-padding-y};
|
||||
--#{$prefix}alert-margin-bottom: #{$alert-margin-bottom};
|
||||
--#{$prefix}alert-color: inherit;
|
||||
--#{$prefix}alert-border-color: transparent;
|
||||
--#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color);
|
||||
--#{$prefix}alert-border-radius: #{$alert-border-radius};
|
||||
--#{$prefix}alert-link-color: inherit;
|
||||
// scss-docs-end alert-css-vars
|
||||
|
||||
position: relative;
|
||||
padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x);
|
||||
margin-bottom: var(--#{$prefix}alert-margin-bottom);
|
||||
color: var(--#{$prefix}alert-color);
|
||||
background-color: var(--#{$prefix}alert-bg);
|
||||
border: var(--#{$prefix}alert-border);
|
||||
@include border-radius(var(--#{$prefix}alert-border-radius));
|
||||
}
|
||||
|
||||
// Headings for larger alerts
|
||||
.alert-heading {
|
||||
// Specified to prevent conflicts of changing $headings-color
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
// Provide class for links that match alerts
|
||||
.alert-link {
|
||||
font-weight: $alert-link-font-weight;
|
||||
color: var(--#{$prefix}alert-link-color);
|
||||
}
|
||||
|
||||
|
||||
// Dismissible alerts
|
||||
//
|
||||
// Expand the right padding and account for the close button's positioning.
|
||||
|
||||
.alert-dismissible {
|
||||
padding-right: $alert-dismissible-padding-r;
|
||||
|
||||
// Adjust close link position
|
||||
.btn-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: $stretched-link-z-index + 1;
|
||||
padding: $alert-padding-y * 1.25 $alert-padding-x;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// scss-docs-start alert-modifiers
|
||||
// Generate contextual modifier classes for colorizing the alert
|
||||
@each $state in map-keys($theme-colors) {
|
||||
.alert-#{$state} {
|
||||
--#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||
--#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle);
|
||||
--#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle);
|
||||
--#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||
}
|
||||
}
|
||||
// scss-docs-end alert-modifiers
|
||||
38
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_badge.scss
vendored
Normal file
38
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_badge.scss
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// Base class
|
||||
//
|
||||
// Requires one of the contextual, color modifier classes for `color` and
|
||||
// `background-color`.
|
||||
|
||||
.badge {
|
||||
// scss-docs-start badge-css-vars
|
||||
--#{$prefix}badge-padding-x: #{$badge-padding-x};
|
||||
--#{$prefix}badge-padding-y: #{$badge-padding-y};
|
||||
@include rfs($badge-font-size, --#{$prefix}badge-font-size);
|
||||
--#{$prefix}badge-font-weight: #{$badge-font-weight};
|
||||
--#{$prefix}badge-color: #{$badge-color};
|
||||
--#{$prefix}badge-border-radius: #{$badge-border-radius};
|
||||
// scss-docs-end badge-css-vars
|
||||
|
||||
display: inline-block;
|
||||
padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x);
|
||||
@include font-size(var(--#{$prefix}badge-font-size));
|
||||
font-weight: var(--#{$prefix}badge-font-weight);
|
||||
line-height: 1;
|
||||
color: var(--#{$prefix}badge-color);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
@include border-radius(var(--#{$prefix}badge-border-radius));
|
||||
@include gradient-bg();
|
||||
|
||||
// Empty badges collapse automatically
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Quick fix for badges in buttons
|
||||
.btn .badge {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
40
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_breadcrumb.scss
vendored
Normal file
40
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_breadcrumb.scss
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
.breadcrumb {
|
||||
// scss-docs-start breadcrumb-css-vars
|
||||
--#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x};
|
||||
--#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y};
|
||||
--#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom};
|
||||
@include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size);
|
||||
--#{$prefix}breadcrumb-bg: #{$breadcrumb-bg};
|
||||
--#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius};
|
||||
--#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color};
|
||||
--#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x};
|
||||
--#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color};
|
||||
// scss-docs-end breadcrumb-css-vars
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x);
|
||||
margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom);
|
||||
@include font-size(var(--#{$prefix}breadcrumb-font-size));
|
||||
list-style: none;
|
||||
background-color: var(--#{$prefix}breadcrumb-bg);
|
||||
@include border-radius(var(--#{$prefix}breadcrumb-border-radius));
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
// The separator between breadcrumbs (by default, a forward-slash: "/")
|
||||
+ .breadcrumb-item {
|
||||
padding-left: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||
|
||||
&::before {
|
||||
float: left; // Suppress inline spacings and underlining of the separator
|
||||
padding-right: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||
color: var(--#{$prefix}breadcrumb-divider-color);
|
||||
content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--#{$prefix}breadcrumb-item-active-color);
|
||||
}
|
||||
}
|
||||
147
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_button-group.scss
vendored
Normal file
147
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_button-group.scss
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
216
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_buttons.scss
vendored
Normal file
216
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_buttons.scss
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
//
|
||||
// Base styles
|
||||
//
|
||||
|
||||
.btn {
|
||||
// scss-docs-start btn-css-vars
|
||||
--#{$prefix}btn-padding-x: #{$btn-padding-x};
|
||||
--#{$prefix}btn-padding-y: #{$btn-padding-y};
|
||||
--#{$prefix}btn-font-family: #{$btn-font-family};
|
||||
@include rfs($btn-font-size, --#{$prefix}btn-font-size);
|
||||
--#{$prefix}btn-font-weight: #{$btn-font-weight};
|
||||
--#{$prefix}btn-line-height: #{$btn-line-height};
|
||||
--#{$prefix}btn-color: #{$btn-color};
|
||||
--#{$prefix}btn-bg: transparent;
|
||||
--#{$prefix}btn-border-width: #{$btn-border-width};
|
||||
--#{$prefix}btn-border-color: transparent;
|
||||
--#{$prefix}btn-border-radius: #{$btn-border-radius};
|
||||
--#{$prefix}btn-hover-border-color: transparent;
|
||||
--#{$prefix}btn-box-shadow: #{$btn-box-shadow};
|
||||
--#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity};
|
||||
--#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5);
|
||||
// scss-docs-end btn-css-vars
|
||||
|
||||
display: inline-block;
|
||||
padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x);
|
||||
font-family: var(--#{$prefix}btn-font-family);
|
||||
@include font-size(var(--#{$prefix}btn-font-size));
|
||||
font-weight: var(--#{$prefix}btn-font-weight);
|
||||
line-height: var(--#{$prefix}btn-line-height);
|
||||
color: var(--#{$prefix}btn-color);
|
||||
text-align: center;
|
||||
text-decoration: if($link-decoration == none, null, none);
|
||||
white-space: $btn-white-space;
|
||||
vertical-align: middle;
|
||||
cursor: if($enable-button-pointers, pointer, null);
|
||||
user-select: none;
|
||||
border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color);
|
||||
@include border-radius(var(--#{$prefix}btn-border-radius));
|
||||
@include gradient-bg(var(--#{$prefix}btn-bg));
|
||||
@include box-shadow(var(--#{$prefix}btn-box-shadow));
|
||||
@include transition($btn-transition);
|
||||
|
||||
&:hover {
|
||||
color: var(--#{$prefix}btn-hover-color);
|
||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||
background-color: var(--#{$prefix}btn-hover-bg);
|
||||
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||
}
|
||||
|
||||
.btn-check + &:hover {
|
||||
// override for the checkbox/radio buttons
|
||||
color: var(--#{$prefix}btn-color);
|
||||
background-color: var(--#{$prefix}btn-bg);
|
||||
border-color: var(--#{$prefix}btn-border-color);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
color: var(--#{$prefix}btn-hover-color);
|
||||
@include gradient-bg(var(--#{$prefix}btn-hover-bg));
|
||||
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||
outline: 0;
|
||||
// Avoid using mixin so we can pass custom focus shadow properly
|
||||
@if $enable-shadows {
|
||||
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||
} @else {
|
||||
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-check:focus-visible + & {
|
||||
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||
outline: 0;
|
||||
// Avoid using mixin so we can pass custom focus shadow properly
|
||||
@if $enable-shadows {
|
||||
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||
} @else {
|
||||
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-check:checked + &,
|
||||
:not(.btn-check) + &:active,
|
||||
&:first-child:active,
|
||||
&.active,
|
||||
&.show {
|
||||
color: var(--#{$prefix}btn-active-color);
|
||||
background-color: var(--#{$prefix}btn-active-bg);
|
||||
// Remove CSS gradients if they're enabled
|
||||
background-image: if($enable-gradients, none, null);
|
||||
border-color: var(--#{$prefix}btn-active-border-color);
|
||||
@include box-shadow(var(--#{$prefix}btn-active-shadow));
|
||||
|
||||
&:focus-visible {
|
||||
// Avoid using mixin so we can pass custom focus shadow properly
|
||||
@if $enable-shadows {
|
||||
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||
} @else {
|
||||
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-check:checked:focus-visible + & {
|
||||
// Avoid using mixin so we can pass custom focus shadow properly
|
||||
@if $enable-shadows {
|
||||
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||
} @else {
|
||||
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&.disabled,
|
||||
fieldset:disabled & {
|
||||
color: var(--#{$prefix}btn-disabled-color);
|
||||
pointer-events: none;
|
||||
background-color: var(--#{$prefix}btn-disabled-bg);
|
||||
background-image: if($enable-gradients, none, null);
|
||||
border-color: var(--#{$prefix}btn-disabled-border-color);
|
||||
opacity: var(--#{$prefix}btn-disabled-opacity);
|
||||
@include box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Alternate buttons
|
||||
//
|
||||
|
||||
// scss-docs-start btn-variant-loops
|
||||
@each $color, $value in $theme-colors {
|
||||
.btn-#{$color} {
|
||||
@if $color == "light" {
|
||||
@include button-variant(
|
||||
$value,
|
||||
$value,
|
||||
$hover-background: shade-color($value, $btn-hover-bg-shade-amount),
|
||||
$hover-border: shade-color($value, $btn-hover-border-shade-amount),
|
||||
$active-background: shade-color($value, $btn-active-bg-shade-amount),
|
||||
$active-border: shade-color($value, $btn-active-border-shade-amount)
|
||||
);
|
||||
} @else if $color == "dark" {
|
||||
@include button-variant(
|
||||
$value,
|
||||
$value,
|
||||
$hover-background: tint-color($value, $btn-hover-bg-tint-amount),
|
||||
$hover-border: tint-color($value, $btn-hover-border-tint-amount),
|
||||
$active-background: tint-color($value, $btn-active-bg-tint-amount),
|
||||
$active-border: tint-color($value, $btn-active-border-tint-amount)
|
||||
);
|
||||
} @else {
|
||||
@include button-variant($value, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
.btn-outline-#{$color} {
|
||||
@include button-outline-variant($value);
|
||||
}
|
||||
}
|
||||
// scss-docs-end btn-variant-loops
|
||||
|
||||
|
||||
//
|
||||
// Link buttons
|
||||
//
|
||||
|
||||
// Make a button look and behave like a link
|
||||
.btn-link {
|
||||
--#{$prefix}btn-font-weight: #{$font-weight-normal};
|
||||
--#{$prefix}btn-color: #{$btn-link-color};
|
||||
--#{$prefix}btn-bg: transparent;
|
||||
--#{$prefix}btn-border-color: transparent;
|
||||
--#{$prefix}btn-hover-color: #{$btn-link-hover-color};
|
||||
--#{$prefix}btn-hover-border-color: transparent;
|
||||
--#{$prefix}btn-active-color: #{$btn-link-hover-color};
|
||||
--#{$prefix}btn-active-border-color: transparent;
|
||||
--#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
|
||||
--#{$prefix}btn-disabled-border-color: transparent;
|
||||
--#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows
|
||||
--#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb};
|
||||
|
||||
text-decoration: $link-decoration;
|
||||
@if $enable-gradients {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
text-decoration: $link-hover-decoration;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
color: var(--#{$prefix}btn-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--#{$prefix}btn-hover-color);
|
||||
}
|
||||
|
||||
// No need for an active state here
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Button Sizes
|
||||
//
|
||||
|
||||
.btn-lg {
|
||||
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);
|
||||
}
|
||||
238
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_card.scss
vendored
Normal file
238
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_card.scss
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
226
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_carousel.scss
vendored
Normal file
226
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_carousel.scss
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// 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();
|
||||
}
|
||||
}
|
||||
66
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_close.scss
vendored
Normal file
66
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_close.scss
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// 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();
|
||||
}
|
||||
}
|
||||
41
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_containers.scss
vendored
Normal file
41
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_containers.scss
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Container widths
|
||||
//
|
||||
// Set the container width, and override it for fixed navbars in media queries.
|
||||
|
||||
@if $enable-container-classes {
|
||||
// Single container class with breakpoint max-widths
|
||||
.container,
|
||||
// 100% wide container at all breakpoints
|
||||
.container-fluid {
|
||||
@include make-container();
|
||||
}
|
||||
|
||||
// Responsive containers that are 100% wide until a breakpoint
|
||||
@each $breakpoint, $container-max-width in $container-max-widths {
|
||||
.container-#{$breakpoint} {
|
||||
@extend .container-fluid;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up($breakpoint, $grid-breakpoints) {
|
||||
%responsive-container-#{$breakpoint} {
|
||||
max-width: $container-max-width;
|
||||
}
|
||||
|
||||
// Extend each breakpoint which is smaller or equal to the current breakpoint
|
||||
$extend-breakpoint: true;
|
||||
|
||||
@each $name, $width in $grid-breakpoints {
|
||||
@if ($extend-breakpoint) {
|
||||
.container#{breakpoint-infix($name, $grid-breakpoints)} {
|
||||
@extend %responsive-container-#{$breakpoint};
|
||||
}
|
||||
|
||||
// Once the current breakpoint is reached, stop extending
|
||||
@if ($breakpoint == $name) {
|
||||
$extend-breakpoint: false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
250
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_dropdown.scss
vendored
Normal file
250
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_dropdown.scss
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// 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
|
||||
}
|
||||
9
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_forms.scss
vendored
Normal file
9
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_forms.scss
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@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";
|
||||
302
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_functions.scss
vendored
Normal file
302
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_functions.scss
vendored
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// 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;
|
||||
}
|
||||
39
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_grid.scss
vendored
Normal file
39
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_grid.scss
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// 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();
|
||||
}
|
||||
12
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_helpers.scss
vendored
Normal file
12
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_helpers.scss
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
@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";
|
||||
42
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_images.scss
vendored
Normal file
42
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_images.scss
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// 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;
|
||||
}
|
||||
199
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_list-group.scss
vendored
Normal file
199
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_list-group.scss
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// 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
|
||||
174
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_maps.scss
vendored
Normal file
174
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_maps.scss
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
// 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;
|
||||
42
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_mixins.scss
vendored
Normal file
42
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_mixins.scss
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// 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";
|
||||
240
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_modal.scss
vendored
Normal file
240
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_modal.scss
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// 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
|
||||
197
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_nav.scss
vendored
Normal file
197
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_nav.scss
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
||||
289
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_navbar.scss
vendored
Normal file
289
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_navbar.scss
vendored
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
// 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)};
|
||||
}
|
||||
}
|
||||
}
|
||||
147
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_offcanvas.scss
vendored
Normal file
147
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_offcanvas.scss
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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;
|
||||
}
|
||||
109
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_pagination.scss
vendored
Normal file
109
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_pagination.scss
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
.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);
|
||||
}
|
||||
51
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_placeholders.scss
vendored
Normal file
51
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_placeholders.scss
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
.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%;
|
||||
}
|
||||
}
|
||||
196
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_popover.scss
vendored
Normal file
196
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_popover.scss
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
.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);
|
||||
}
|
||||
68
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_progress.scss
vendored
Normal file
68
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_progress.scss
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
617
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_reboot.scss
vendored
Normal file
617
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_reboot.scss
vendored
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
// 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;
|
||||
}
|
||||
187
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_root.scss
vendored
Normal file
187
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_root.scss
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
: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
|
||||
}
|
||||
}
|
||||
86
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_spinners.scss
vendored
Normal file
86
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_spinners.scss
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// 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};
|
||||
}
|
||||
}
|
||||
}
|
||||
171
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tables.scss
vendored
Normal file
171
extensions/pagetop-bootsier/static/scss/bootstrap-5.3.8/_tables.scss
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue