hoi
This commit is contained in:
87
backend/static/dashboard-transition.js
Normal file
87
backend/static/dashboard-transition.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
(() => {
|
||||||
|
const OPENING_MS = 420;
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const dashboardGrid = document.querySelector(".dashboard-grid");
|
||||||
|
const dashboardLinks = document.querySelectorAll(".dashboard-link-card[href]");
|
||||||
|
|
||||||
|
if (dashboardGrid) {
|
||||||
|
dashboardLinks.forEach((link, index) => {
|
||||||
|
link.style.setProperty("--stagger-delay", `${index * 55}ms`);
|
||||||
|
});
|
||||||
|
|
||||||
|
dashboardGrid.classList.add("is-ready");
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
dashboardGrid.classList.add("is-animated");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboardLinks.forEach((link) => {
|
||||||
|
link.addEventListener("click", (event) => {
|
||||||
|
if (link.classList.contains("is-opening")) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.defaultPrevented ||
|
||||||
|
event.button !== 0 ||
|
||||||
|
event.metaKey ||
|
||||||
|
event.ctrlKey ||
|
||||||
|
event.shiftKey ||
|
||||||
|
event.altKey
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const href = link.getAttribute("href");
|
||||||
|
if (!href || href.startsWith("http")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
if (dashboardGrid) {
|
||||||
|
dashboardGrid.classList.add("is-focusing");
|
||||||
|
}
|
||||||
|
link.classList.add("is-opening");
|
||||||
|
|
||||||
|
// Force layout before running the exit animation for consistent playback.
|
||||||
|
void link.offsetWidth;
|
||||||
|
|
||||||
|
let hasNavigated = false;
|
||||||
|
const navigate = () => {
|
||||||
|
if (hasNavigated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasNavigated = true;
|
||||||
|
window.location.href = href;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof link.animate === "function") {
|
||||||
|
const animation = link.animate(
|
||||||
|
[
|
||||||
|
{ opacity: 1, transform: "translateY(-6px) scale(1)", filter: "blur(0px)" },
|
||||||
|
{ opacity: 0, transform: "translateY(-42px) scale(0.982)", filter: "blur(2px)" },
|
||||||
|
],
|
||||||
|
{
|
||||||
|
duration: OPENING_MS,
|
||||||
|
easing: "cubic-bezier(0.22, 0.61, 0.36, 1)",
|
||||||
|
fill: "forwards",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
animation.finished.then(navigate).catch(navigate);
|
||||||
|
} else {
|
||||||
|
// Fallback if WAAPI is unavailable.
|
||||||
|
link.style.transition = `transform ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1), opacity ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1), filter ${OPENING_MS}ms cubic-bezier(0.22, 0.61, 0.36, 1)`;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
link.style.transform = "translateY(-42px) scale(0.982)";
|
||||||
|
link.style.opacity = "0";
|
||||||
|
link.style.filter = "blur(2px)";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(navigate, OPENING_MS + 240);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -130,11 +130,73 @@ h3 {
|
|||||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid.is-ready .dashboard-link-card {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(14px) scale(0.985);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid.is-ready.is-animated .dashboard-link-card {
|
||||||
|
animation: dashboard-card-in 460ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
|
||||||
|
animation-delay: var(--stagger-delay, 0ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-grid.is-focusing .dashboard-link-card:not(.is-opening) {
|
||||||
|
opacity: 0.56;
|
||||||
|
transform: scale(0.975);
|
||||||
|
filter: saturate(0.82);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dashboard-card-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(14px) scale(0.985);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-link-card {
|
||||||
|
min-height: 140px;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
font-size: 1.14rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 1rem;
|
||||||
|
position: relative;
|
||||||
|
background: #fffdf9;
|
||||||
|
border: 1px solid rgba(39, 66, 53, 0.11);
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
box-shadow: 0 12px 30px rgba(39, 66, 53, 0.12);
|
||||||
|
will-change: transform, opacity, filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-link-card:hover {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
box-shadow: 0 18px 40px rgba(39, 66, 53, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-link-card.is-opening {
|
||||||
|
box-shadow: 0 20px 44px rgba(39, 66, 53, 0.22);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.link-card:hover {
|
.link-card:hover {
|
||||||
transform: translateY(-3px);
|
transform: translateY(-3px);
|
||||||
box-shadow: 0 14px 30px rgba(39, 66, 53, 0.16);
|
box-shadow: 0 14px 30px rgba(39, 66, 53, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-grid .dashboard-link-card:hover {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
box-shadow: 0 18px 40px rgba(39, 66, 53, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.form-card {
|
.form-card {
|
||||||
max-width: 560px;
|
max-width: 560px;
|
||||||
}
|
}
|
||||||
@@ -721,6 +783,26 @@ input[type="file"]:focus {
|
|||||||
.location-actions {
|
.location-actions {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-link-card {
|
||||||
|
min-height: 0;
|
||||||
|
font-size: 1.08rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 380px) {
|
||||||
|
.dashboard-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-link-card {
|
||||||
|
min-height: 118px;
|
||||||
|
aspect-ratio: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-footer {
|
.site-footer {
|
||||||
|
|||||||
@@ -51,5 +51,6 @@
|
|||||||
<a href="{{ url_for('datenschutz') }}">{{ t('privacy') }}</a>
|
<a href="{{ url_for('datenschutz') }}">{{ t('privacy') }}</a>
|
||||||
<a href="{{ url_for('impressum') }}">{{ t('imprint') }}</a>
|
<a href="{{ url_for('impressum') }}">{{ t('imprint') }}</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="{{ url_for('static', filename='dashboard-transition.js', v='20260302c') }}" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
<p>{{ t('hello_guest').format(name=guest_name) }}</p>
|
<p>{{ t('hello_guest').format(name=guest_name) }}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="card-grid">
|
<section class="card-grid dashboard-grid">
|
||||||
<a class="card link-card" href="{{ url_for('rsvp') }}">{{ t('rsvp') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('rsvp') }}">{{ t('rsvp') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('upload') }}">{{ t('upload') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('upload') }}">{{ t('upload') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('gallery') }}">{{ t('gallery') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('gallery') }}">{{ t('gallery') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('info', page='schedule') }}">{{ t('schedule') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('info', page='schedule') }}">{{ t('schedule') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('info', page='hotels') }}">{{ t('hotels') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('info', page='hotels') }}">{{ t('hotels') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('info', page='taxi') }}">{{ t('taxi') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('info', page='taxi') }}">{{ t('taxi') }}</a>
|
||||||
<a class="card link-card" href="{{ url_for('info', page='location') }}">{{ t('location') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('info', page='location') }}">{{ t('location') }}</a>
|
||||||
{% if is_admin %}
|
{% if is_admin %}
|
||||||
<a class="card link-card" href="{{ url_for('host_area') }}">{{ t('host_area') }}</a>
|
<a class="card link-card dashboard-link-card" href="{{ url_for('host_area') }}">{{ t('host_area') }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user