This commit is contained in:
Tobias Johansson
2024-11-27 16:11:10 +01:00
parent 2c7842f79a
commit d3dad4c73d
2 changed files with 64 additions and 7 deletions

View File

@@ -1,5 +1,6 @@
"use client"
import { useEffect, useState } from "react"
import { animate, motion, scroll, useAnimate } from "framer-motion"
import { useEffect, useRef, useState } from "react"
import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
@@ -33,6 +34,10 @@ export default function SectionAccordion({
const noBreakfastTitle = intl.formatMessage({ id: "No breakfast" })
const breakfastTitle = intl.formatMessage({ id: "Breakfast buffet" })
const accordionRef = useRef<HTMLDivElement>(null)
const [scope, animate] = useAnimate()
useEffect(() => {
if (step === StepEnum.selectBed && bedType) {
setTitle(bedType.description)
@@ -55,6 +60,49 @@ export default function SectionAccordion({
setIsOpen(currentStep === step)
}, [currentStep, setIsOpen, step])
useEffect(() => {
if (!accordionRef.current) return
const animateAccordion = async () => {
// Measure the initial position of the accordion
const initialOffset = accordionRef.current?.offsetTop || 0
// Start the accordion expansion animation
const accordionAnimation = animate(
scope.current,
{
gridTemplateRows: isOpen
? "var(--header-height) 1fr"
: "var(--header-height) 0fr",
},
{ duration: 0.3, ease: "easeInOut" }
)
if (isOpen) {
// Start scrolling immediately
const scrollAnimation = animate(
window.scrollY,
initialOffset - 100, // 100px offset from top
{
duration: 0.3,
ease: "easeInOut",
onUpdate: (latest) => {
window.scrollTo(0, latest)
},
}
)
// Wait for both animations to complete
await Promise.all([accordionAnimation, scrollAnimation])
} else {
// If closing, just wait for the accordion animation
await accordionAnimation
}
}
animateAccordion()
}, [isOpen, scope, animate])
function onModify() {
navigate(step)
}
@@ -62,7 +110,13 @@ export default function SectionAccordion({
const textColor =
isComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled"
return (
<div className={styles.accordion} data-open={isOpen} data-step={step}>
<motion.div
className={styles.accordion}
data-open={isOpen}
data-step={step}
ref={scope}
transition={{ duration: 0.3, ease: "easeOut" }}
>
<div className={styles.iconWrapper}>
<div className={styles.circle} data-checked={isComplete}>
{isComplete ? (
@@ -70,7 +124,7 @@ export default function SectionAccordion({
) : null}
</div>
</div>
<header className={styles.header}>
<header className={styles.header} ref={accordionRef}>
<button
onClick={onModify}
disabled={!isComplete}
@@ -97,6 +151,6 @@ export default function SectionAccordion({
<div className={styles.content}>
<div className={styles.contentWrapper}>{children}</div>
</div>
</div>
</motion.div>
)
}

View File

@@ -5,7 +5,7 @@
gap: var(--Spacing-x3);
width: 100%;
padding-top: var(--Spacing-x3);
transition: 0.4s ease-out;
/* transition: 0.4s ease-out; */
display: grid;
grid-template-areas: "circle header" "content content";
@@ -13,6 +13,8 @@
grid-template-rows: var(--header-height) 0fr;
column-gap: var(--Spacing-x-one-and-half);
transform-origin: top;
}
.accordion:last-child {
@@ -79,9 +81,9 @@
background-color: var(--Base-Surface-Subtle-Hover);
}
.accordion[data-open="true"] {
/* .accordion[data-open="true"] {
grid-template-rows: var(--header-height) 1fr;
}
} */
.contentWrapper {
padding-bottom: var(--Spacing-x3);
@@ -91,6 +93,7 @@
overflow: hidden;
grid-area: content;
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider-subtle);
transform-origin: top;
}
@media screen and (min-width: 768px) {