diff options
Diffstat (limited to 'src/routes/+layout.svelte')
| -rw-r--r-- | src/routes/+layout.svelte | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 625f58b1..8c36bb0f 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -101,6 +101,55 @@ const handleScroll = () => { if (isMenuOpen) isMenuOpen = false; }; +const detailsAnimations = new WeakMap<HTMLDetailsElement, Animation>(); + +const animateDetails = (e: MouseEvent) => { + const summary = (e.target as HTMLElement | null)?.closest("summary"); + if (!summary) return; + const details = summary.parentElement as HTMLDetailsElement | null; + if (!details || details.tagName !== "DETAILS") return; + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; + + e.preventDefault(); + detailsAnimations.get(details)?.cancel(); + + const detailsStyle = getComputedStyle(details); + const closedHeight = + summary.offsetHeight + + parseFloat(detailsStyle.paddingTop) + + parseFloat(detailsStyle.paddingBottom) + + parseFloat(detailsStyle.borderTopWidth) + + parseFloat(detailsStyle.borderBottomWidth); + const startHeight = details.offsetHeight; + + if (details.open) { + details.style.overflow = "hidden"; + const animation = details.animate( + { height: [`${startHeight}px`, `${closedHeight}px`] }, + { duration: 240, easing: "cubic-bezier(0.22, 1, 0.36, 1)" }, + ); + detailsAnimations.set(details, animation); + animation.onfinish = () => { + details.open = false; + details.style.overflow = ""; + detailsAnimations.delete(details); + }; + } else { + details.style.overflow = "hidden"; + details.open = true; + const fullHeight = details.offsetHeight; + const animation = details.animate( + { height: [`${closedHeight}px`, `${fullHeight}px`] }, + { duration: 240, easing: "cubic-bezier(0.22, 1, 0.36, 1)" }, + ); + detailsAnimations.set(details, animation); + animation.onfinish = () => { + details.style.overflow = ""; + detailsAnimations.delete(details); + }; + } +}; + onMount(async () => { if (browser) { if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) { @@ -223,6 +272,8 @@ $: { on:keydown={(e) => { if (e.key === 'Escape' && isMenuOpen) isMenuOpen = false; }} on:click={(e) => { if (isMenuOpen && !(e.target as HTMLElement).closest('.header')) isMenuOpen = false; + + animateDetails(e); }} /> |