diff --git a/src/bpp/newsfragments/animacja-paska-nawigacji-wyszukiwanie.feature.rst b/src/bpp/newsfragments/animacja-paska-nawigacji-wyszukiwanie.feature.rst new file mode 100644 index 000000000..be4d9b0fa --- /dev/null +++ b/src/bpp/newsfragments/animacja-paska-nawigacji-wyszukiwanie.feature.rst @@ -0,0 +1,2 @@ +Pasek nawigacji chowa się i wraca płynną animacją przy otwieraniu oraz +zamykaniu okna wyszukiwania globalnego, zamiast znikać i pojawiać się skokowo. diff --git a/src/bpp/static/scss/top_bar.scss b/src/bpp/static/scss/top_bar.scss index ae179ec35..af49c231b 100644 --- a/src/bpp/static/scss/top_bar.scss +++ b/src/bpp/static/scss/top_bar.scss @@ -18,6 +18,49 @@ nav.sticky-header { z-index: 2000; // Must be highest to ensure dropdown appears above all sticky elements backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); + + // Chowanie paska na czas otwartego okna wyszukiwania globalnego. + // + // Modal blokuje przewijanie ustawiajac na `position: fixed` + // z ujemnym `top` (global_search_modal.html). To lamie `position: + // sticky` powyzej: przestaje byc kontenerem scrollujacym, wiec + // pasek wraca na swoja pozycje w dokumencie i - gdy strona byla + // przewinieta - znika skokowo za gorna krawedzia. Dlatego na czas + // trwania modala przypinamy go recznie klasa `global-search-pinned` + // i dopiero wtedy mozemy go plynnie wysunac klasa + // `global-search-hidden`. Obie klasy zdejmuje `closeGlobalSearch()`. + // + // `position: fixed` jest tu wzgledem viewportu, bo zaden przodek + // (, ) nie ma transform/filter/perspective/contain - + // samo `position: relative`/`fixed` na containing blocku dla + // fixed nie tworzy. + transition: transform 0.25s ease-out, opacity 0.25s ease-out; + + // Opoznienie dotyczy stanu DOCELOWEGO przejscia, czyli powrotu paska: + // wjezdza chwile po starcie zwijania okna, zamiast scigac sie z nim. + transition-delay: 0.1s; + + &.global-search-pinned { + position: fixed; + top: 0; + left: 0; + right: 0; + } + + &.global-search-hidden { + transform: translateY(-100%); + opacity: 0; + pointer-events: none; + + // Chowanie ma ruszac natychmiast, rownolegle z rozwijaniem okna. + transition-delay: 0s; + } +} + +@media (prefers-reduced-motion: reduce) { + nav.sticky-header { + transition: none; + } } .top-bar { diff --git a/src/django_bpp/templates/global_search_modal.html b/src/django_bpp/templates/global_search_modal.html index 45aa381f1..0b6da1f82 100644 --- a/src/django_bpp/templates/global_search_modal.html +++ b/src/django_bpp/templates/global_search_modal.html @@ -479,6 +479,12 @@

Jak wyszukiwać?

// Save scroll position and block background scrolling scrollPosition = window.pageYOffset || document.documentElement.scrollTop; + // Zmierz pasek nawigacji ZANIM cokolwiek ruszymy - za chwile + // wypadnie z flow i jego wysokosc bedzie potrzebna do + // kompensacji na . + var nav = document.querySelector('nav.sticky-header'); + var navHeight = nav ? nav.offsetHeight : 0; + // Lock the body completely document.body.style.position = 'fixed'; document.body.style.top = '-' + scrollPosition + 'px'; @@ -490,6 +496,19 @@

Jak wyszukiwać?

// Also lock html element for extra security document.documentElement.style.overflow = 'hidden'; + // Przypnij pasek nawigacji do viewportu i wysun go poza kadr. + // Blokada scrolla powyzej (body -> position: fixed) lamie jego + // `position: sticky`, wiec bez tego pasek znikalby skokowo; + // klasy sa zdejmowane w closeGlobalSearch(). Reflow miedzy + // klasami jest konieczny: bez niego przegladarka scala oba + // zapisy stylu w jeden i przejscia nie ma. + if (nav) { + nav.classList.add('global-search-pinned'); + document.body.style.paddingTop = navHeight + 'px'; + void nav.offsetHeight; + nav.classList.add('global-search-hidden'); + } + // Remove previous animation classes container.classList.remove('animating-out'); @@ -622,11 +641,19 @@

Jak wyszukiwać?

window.closeGlobalSearch = function() { var modal = document.getElementById('globalSearchModal'); var container = document.querySelector('.global-search-container'); + var nav = document.querySelector('nav.sticky-header'); // Add closing animation container.classList.add('animating-out'); modal.classList.add('closing'); + // Pasek wjezdza ROWNOLEGLE ze zwijaniem okna, nie po nim. + // Wlasne opoznienie 0.1s ma w CSS (top_bar.scss), zeby ruch nie + // startowal dokladnie w tej samej klatce co collapseToButton. + if (nav) { + nav.classList.remove('global-search-hidden'); + } + // After animation completes, hide modal and restore scrolling setTimeout(function() { modal.classList.remove('show', 'closing'); @@ -647,6 +674,16 @@

Jak wyszukiwać?

// Restore scroll position window.scrollTo(0, scrollPosition); + // Oddaj pasek z powrotem `position: sticky`. Musi to isc w + // tej samej klatce co przywrocenie scrolla powyzej: pasek + // przypiety (fixed, top 0) i sticky przy top 0 sa w tym + // samym miejscu, wiec przelaczenie jest niewidoczne, a + // kompensacja paddingu znika razem z powrotem nav do flow. + if (nav) { + nav.classList.remove('global-search-pinned'); + document.body.style.paddingTop = ''; + } + // Reset container styles for next opening container.style.transform = ''; container.style.opacity = '';