Files
web/packages/design-system/lib/components/SidePeek/SelfControlled.tsx
Emma Zettervall 8b56fa84e7 Merged in feat/LOY-522-move-focus-to-newly-loaded-item (pull request #3452)
feat(LOY-522): Move focus to the newly loaded stay in sidepeek for upcoming and previous stay

* feat(LOY-522): Moved focus to the newly loaded stay in sidepeek for upcoming and previous stay


Approved-by: Anton Gunnarsson
2026-01-20 08:10:42 +00:00

90 lines
2.4 KiB
TypeScript

"use client"
import { useEffect } from "react"
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
import { useIntl } from "react-intl"
import usePopStateHandler from "@scandic-hotels/common/hooks/usePopStateHandler"
import { IconButton } from "../IconButton"
import { Typography } from "../Typography"
import SidePeekSEO from "./SidePeekSEO"
import { KeepBodyVisible } from "./KeepBodyVisible"
import styles from "./sidePeek.module.css"
interface SidePeekSelfControlledProps extends React.PropsWithChildren {
title: string
isOpen: boolean
onClose: () => void
sidePeekSEO?: boolean
}
export default function SidePeekSelfControlled({
children,
isOpen,
onClose,
title,
sidePeekSEO = true,
}: SidePeekSelfControlledProps) {
const intl = useIntl()
function handleClose(moveBack = false) {
if (moveBack) {
window.history.back()
} else {
onClose()
}
}
// Only register popstate handler when open
usePopStateHandler(() => handleClose(), isOpen)
useEffect(() => {
if (isOpen) {
window.history.pushState(null, "", window.location.href)
}
}, [isOpen])
return (
<>
<ModalOverlay
className={styles.overlay}
isDismissable
onOpenChange={() => handleClose(true)}
isOpen={isOpen}
>
<Modal className={styles.modal}>
<Dialog className={styles.dialog} aria-label={title}>
<aside className={styles.aside}>
<header className={styles.header}>
<div className={styles.headerContent}>
{title ? (
<Typography variant="Title/md" className={styles.heading}>
<h2>{title}</h2>
</Typography>
) : null}
<IconButton
variant="Muted"
emphasis
onPress={() => handleClose(true)}
aria-label={intl.formatMessage({
id: "common.close",
defaultMessage: "Close",
})}
iconName="close"
/>
</div>
</header>
<div className={styles.sidePeekContent}>{children}</div>
<KeepBodyVisible />
</aside>
</Dialog>
</Modal>
</ModalOverlay>
{sidePeekSEO && <SidePeekSEO title={title}>{children}</SidePeekSEO>}
</>
)
}