removing handwritten JS
This commit is contained in:
@@ -67,19 +67,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile: a backdrop behind an open navbar dropdown. It absorbs taps
|
||||
outside the menu — closing the dropdown rather than letting the tap
|
||||
reach the page — and dims the page to show the menu is modal. It sits
|
||||
below the dropdown content (z-50) so the menu items stay tappable. */
|
||||
/* Mobile: a dimming backdrop behind an open navbar dropdown, driven by
|
||||
CSS alone. `:has()` shows it whenever a dropdown holds focus; a tap
|
||||
outside the menu blurs the trigger, which closes the dropdown. The
|
||||
delayed `visibility` transition keeps the backdrop hit-testable for a
|
||||
beat after that tap, so the tap lands on the backdrop instead of
|
||||
falling through to the page. It sits below the dropdown content
|
||||
(z-50) so the menu items stay tappable. */
|
||||
#nav-backdrop { display: none; }
|
||||
@media (max-width: 767px) {
|
||||
#nav-backdrop {
|
||||
display: block;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 40;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.15s ease, visibility 0s linear 0.2s;
|
||||
}
|
||||
.navbar:has(.dropdown:focus-within) ~ #nav-backdrop {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: opacity 0.15s ease, visibility 0s;
|
||||
}
|
||||
#nav-backdrop.nav-backdrop--on { display: block; }
|
||||
}
|
||||
</style>
|
||||
{% block head %}{% endblock head %}
|
||||
@@ -139,11 +150,15 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- The language buttons submit this form; the /lang handler sets
|
||||
the `lang` cookie and redirects back. The theme buttons are
|
||||
type="button" so they never submit. -->
|
||||
<form method="post" action="/lang">
|
||||
<ul tabindex="0"
|
||||
class="menu dropdown-content z-50 mt-3 w-56 rounded-box border border-base-300 bg-base-100 p-2 shadow-lg">
|
||||
<li class="menu-title">{{ t(key="settings-language", lang=lang) }}</li>
|
||||
<li>
|
||||
<button type="button" onclick="setLang('en')" class="{% if lang == 'en' %}active{% endif %}">
|
||||
<button type="submit" name="lang" value="en" class="{% if lang == 'en' %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
@@ -159,7 +174,7 @@
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" onclick="setLang('sk')" class="{% if lang == 'sk' %}active{% endif %}">
|
||||
<button type="submit" name="lang" value="sk" class="{% if lang == 'sk' %}active{% endif %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
@@ -218,6 +233,7 @@
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -229,38 +245,6 @@
|
||||
{% block content %}{% endblock content %}
|
||||
</main>
|
||||
|
||||
<script>
|
||||
function setLang(l) {
|
||||
document.cookie = 'lang=' + l + ';path=/;max-age=31536000';
|
||||
location.reload();
|
||||
}
|
||||
|
||||
// Mobile: while a navbar dropdown is open, a full-screen backdrop covers
|
||||
// the page so a tap outside the menu only closes it, instead of also
|
||||
// activating whatever sat underneath. Preventing the backdrop's mousedown
|
||||
// keeps the trigger focused (menu open) until the tap's click lands on the
|
||||
// backdrop itself, which then blurs the trigger to close the menu.
|
||||
(function () {
|
||||
var backdrop = document.getElementById('nav-backdrop');
|
||||
if (!backdrop) return;
|
||||
var hide = function () { backdrop.classList.remove('nav-backdrop--on'); };
|
||||
document.querySelectorAll('.navbar .dropdown').forEach(function (dd) {
|
||||
dd.addEventListener('focusin', function () {
|
||||
backdrop.classList.add('nav-backdrop--on');
|
||||
});
|
||||
dd.addEventListener('focusout', function (e) {
|
||||
if (!dd.contains(e.relatedTarget)) hide();
|
||||
});
|
||||
});
|
||||
backdrop.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
||||
backdrop.addEventListener('click', function () {
|
||||
if (document.activeElement && document.activeElement.blur) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
hide();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% block js %}{% endblock js %}
|
||||
</body>
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
//! court. Booked slots are coloured; free slots are blank. The same grid is
|
||||
//! reused by the admin dashboard with `is_admin = true`.
|
||||
|
||||
use axum::http::{header, HeaderMap};
|
||||
use axum::response::Redirect;
|
||||
use axum_extra::extract::cookie::CookieJar;
|
||||
use chrono::{Datelike, Duration, NaiveDate, Utc};
|
||||
use loco_rs::prelude::*;
|
||||
@@ -312,6 +314,46 @@ pub async fn index(
|
||||
format::render().view(&v, "calendar/week.html", &page)
|
||||
}
|
||||
|
||||
pub fn routes() -> Routes {
|
||||
Routes::new().add("/", get(index))
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LangForm {
|
||||
pub lang: String,
|
||||
}
|
||||
|
||||
/// Switches the UI language. The navbar's language buttons post here; the
|
||||
/// `lang` cookie is set server-side and the visitor is bounced back to the
|
||||
/// page they came from. This replaces the old client-side `setLang` script.
|
||||
#[debug_handler]
|
||||
pub async fn set_lang(headers: HeaderMap, Form(form): Form<LangForm>) -> Result<Response> {
|
||||
// Only the two supported languages; anything else falls back to Slovak,
|
||||
// matching `current_lang`.
|
||||
let lang = if form.lang == "en" { "en" } else { "sk" };
|
||||
let cookie = format!("lang={lang}; Path=/; Max-Age=31536000; SameSite=Lax");
|
||||
Ok((
|
||||
[(header::SET_COOKIE, cookie)],
|
||||
Redirect::to(&back_path(&headers)),
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
|
||||
/// On-site path of the page that submitted the form, read from `Referer`.
|
||||
/// Scheme and host are stripped so a stale or foreign header can only ever
|
||||
/// bounce the visitor to a path on this site, never off it.
|
||||
fn back_path(headers: &HeaderMap) -> String {
|
||||
let raw = headers
|
||||
.get(header::REFERER)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("/");
|
||||
match raw.split_once("://") {
|
||||
Some((_, rest)) => match rest.find('/') {
|
||||
Some(i) => rest[i..].to_string(),
|
||||
None => "/".to_string(),
|
||||
},
|
||||
None => raw.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Routes {
|
||||
Routes::new()
|
||||
.add("/", get(index))
|
||||
.add("/lang", post(set_lang))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user