Merged in feat/sw-2879-booking-widget-to-booking-flow-package (pull request #2532)
feat(SW-2879): Move BookingWidget to booking-flow package * Fix lockfile * Fix styling * a tiny little booking widget test * Tiny fixes * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Remove unused scripts * lint:fix * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Tiny lint fixes * update test * Update Input in booking-flow * Clean up comments etc * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Setup tracking context for booking-flow * Add missing use client * Fix temp tracking function * Pass booking to booking-widget * Remove comment * Add use client to booking widget tracking provider * Add use client to tracking functions * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Move debug page * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package * Merge branch 'master' into feat/sw-2879-booking-widget-to-booking-flow-package Approved-by: Bianca Widstam
This commit is contained in:
215
packages/booking-flow/lib/components/TEMP/Modal/index.tsx
Normal file
215
packages/booking-flow/lib/components/TEMP/Modal/index.tsx
Normal file
@@ -0,0 +1,215 @@
|
||||
"use client"
|
||||
|
||||
// TODO this is duplicated from scandic-web
|
||||
|
||||
import { cx } from "class-variance-authority"
|
||||
import { AnimatePresence, motion } from "motion/react"
|
||||
import { type PropsWithChildren, useEffect, useState } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
Modal as AriaModal,
|
||||
ModalOverlay,
|
||||
} from "react-aria-components"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||
import Preamble from "@scandic-hotels/design-system/Preamble"
|
||||
import Subtitle from "@scandic-hotels/design-system/Subtitle"
|
||||
|
||||
import {
|
||||
type AnimationState,
|
||||
AnimationStateEnum,
|
||||
type InnerModalProps,
|
||||
type ModalProps,
|
||||
} from "./modal"
|
||||
import { fade, slideInOut } from "./motionVariants"
|
||||
import { modalContentVariants } from "./variants"
|
||||
|
||||
import styles from "./modal.module.css"
|
||||
|
||||
const MotionOverlay = motion.create(ModalOverlay)
|
||||
const MotionModal = motion.create(AriaModal)
|
||||
|
||||
function InnerModal({
|
||||
animation,
|
||||
onAnimationComplete = () => undefined,
|
||||
setAnimation,
|
||||
onToggle,
|
||||
isOpen,
|
||||
children,
|
||||
title,
|
||||
subtitle,
|
||||
withActions,
|
||||
hideHeader,
|
||||
className,
|
||||
}: PropsWithChildren<InnerModalProps>) {
|
||||
const intl = useIntl()
|
||||
|
||||
const contentClassNames = modalContentVariants({
|
||||
withActions: withActions,
|
||||
})
|
||||
|
||||
function modalStateHandler(newAnimationState: AnimationState) {
|
||||
setAnimation((currentAnimationState) =>
|
||||
newAnimationState === AnimationStateEnum.hidden &&
|
||||
currentAnimationState === AnimationStateEnum.hidden
|
||||
? AnimationStateEnum.unmounted
|
||||
: currentAnimationState
|
||||
)
|
||||
if (newAnimationState === AnimationStateEnum.visible) {
|
||||
onAnimationComplete()
|
||||
}
|
||||
}
|
||||
|
||||
function onOpenChange(state: boolean) {
|
||||
onToggle!(state)
|
||||
}
|
||||
|
||||
return (
|
||||
<MotionOverlay
|
||||
animate={animation}
|
||||
className={styles.overlay}
|
||||
initial="hidden"
|
||||
isDismissable
|
||||
// TODO: Enabling this causes the modal to never unmount.
|
||||
// Seems to be an issue with react-aria-components, see https://github.com/adobe/react-spectrum/issues/7563.
|
||||
// Exit animations can probably be fixed by rewriting to a solution similar to
|
||||
// https://react-spectrum.adobe.com/react-aria/examples/framer-modal-sheet.html.
|
||||
// isExiting={animation === AnimationStateEnum.hidden}
|
||||
onAnimationComplete={modalStateHandler}
|
||||
variants={fade}
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
>
|
||||
<MotionModal
|
||||
className={cx(styles.modal, className)}
|
||||
variants={slideInOut}
|
||||
animate={animation}
|
||||
initial="hidden"
|
||||
>
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
aria-label={intl.formatMessage({
|
||||
defaultMessage: "Dialog",
|
||||
})}
|
||||
>
|
||||
{({ close }) => (
|
||||
<>
|
||||
{!hideHeader && (
|
||||
<header
|
||||
className={`${styles.header} ${!subtitle ? styles.verticalCenter : ""}`}
|
||||
>
|
||||
<div>
|
||||
{title && (
|
||||
<Subtitle type="one" color="uiTextHighContrast">
|
||||
{title}
|
||||
</Subtitle>
|
||||
)}
|
||||
{subtitle && (
|
||||
<Preamble asChild>
|
||||
<span>{subtitle}</span>
|
||||
</Preamble>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={close}
|
||||
type="button"
|
||||
className={styles.close}
|
||||
>
|
||||
<MaterialIcon icon="close" color="Icon/Feedback/Neutral" />
|
||||
</button>
|
||||
</header>
|
||||
)}
|
||||
|
||||
<div className={contentClassNames}>{children}</div>
|
||||
</>
|
||||
)}
|
||||
</Dialog>
|
||||
</MotionModal>
|
||||
</MotionOverlay>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Modal({
|
||||
onAnimationComplete = () => undefined,
|
||||
trigger,
|
||||
isOpen,
|
||||
onToggle,
|
||||
onOpenChange,
|
||||
title,
|
||||
subtitle,
|
||||
children,
|
||||
withActions = false,
|
||||
hideHeader = false,
|
||||
className = "",
|
||||
}: PropsWithChildren<ModalProps>) {
|
||||
const [animation, setAnimation] = useState<AnimationState>(
|
||||
AnimationStateEnum.visible
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof isOpen === "boolean") {
|
||||
setAnimation(
|
||||
isOpen ? AnimationStateEnum.visible : AnimationStateEnum.hidden
|
||||
)
|
||||
}
|
||||
if (isOpen === undefined) {
|
||||
setAnimation(AnimationStateEnum.unmounted)
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const shouldRender = isOpen || animation !== AnimationStateEnum.unmounted
|
||||
|
||||
if (!trigger) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{shouldRender && (
|
||||
<InnerModal
|
||||
onAnimationComplete={onAnimationComplete}
|
||||
animation={animation}
|
||||
setAnimation={setAnimation}
|
||||
onToggle={onToggle}
|
||||
isOpen={isOpen}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
withActions={withActions}
|
||||
hideHeader={hideHeader}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</InnerModal>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<DialogTrigger
|
||||
onOpenChange={(isOpen) => {
|
||||
setAnimation(
|
||||
isOpen ? AnimationStateEnum.visible : AnimationStateEnum.hidden
|
||||
)
|
||||
onOpenChange?.(isOpen)
|
||||
}}
|
||||
>
|
||||
{trigger}
|
||||
<AnimatePresence>
|
||||
{shouldRender && (
|
||||
<InnerModal
|
||||
onAnimationComplete={onAnimationComplete}
|
||||
animation={animation}
|
||||
setAnimation={setAnimation}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
withActions={withActions}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</InnerModal>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</DialogTrigger>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user