From 58678244fc6e047df558140f2b14fe3d12471690 Mon Sep 17 00:00:00 2001 From: Arvid Norlin Date: Tue, 12 Nov 2024 13:34:42 +0100 Subject: [PATCH] fix: refactor GuestRoomsPicker to avoid performance bugs --- components/GuestsRoomsPicker/Dialog.tsx | 130 +++++++++++++++++ .../GuestsRoomsPicker/GuestsRoomsPicker.tsx | 136 ------------------ .../guests-rooms-picker.module.css | 55 ++----- components/GuestsRoomsPicker/index.tsx | 58 ++------ .../bookingWidget/guestsRoomsPicker.ts | 4 - 5 files changed, 158 insertions(+), 225 deletions(-) create mode 100644 components/GuestsRoomsPicker/Dialog.tsx delete mode 100644 components/GuestsRoomsPicker/GuestsRoomsPicker.tsx diff --git a/components/GuestsRoomsPicker/Dialog.tsx b/components/GuestsRoomsPicker/Dialog.tsx new file mode 100644 index 000000000..1c5d8e53d --- /dev/null +++ b/components/GuestsRoomsPicker/Dialog.tsx @@ -0,0 +1,130 @@ +"use client" +import { Dialog } from "react-aria-components" +import { useFormContext } from "react-hook-form" +import { useIntl } from "react-intl" + +import { useGuestsRoomsStore } from "@/stores/guests-rooms" + +import { CloseLargeIcon, PlusCircleIcon, PlusIcon } from "../Icons" +import Button from "../TempDesignSystem/Button" +import Divider from "../TempDesignSystem/Divider" +import Subtitle from "../TempDesignSystem/Text/Subtitle" +import { Tooltip } from "../TempDesignSystem/Tooltip" +import AdultSelector from "./AdultSelector" +import ChildSelector from "./ChildSelector" + +import styles from "./guests-rooms-picker.module.css" + +import { BookingWidgetSchema } from "@/types/components/bookingWidget" + +export default function GuestsRoomsPickerDialog() { + const intl = useIntl() + const doneLabel = intl.formatMessage({ id: "Done" }) + const roomLabel = intl.formatMessage({ id: "Room" }) + const disabledBookingOptionsHeader = intl.formatMessage({ + id: "Disabled booking options header", + }) + const disabledBookingOptionsText = intl.formatMessage({ + id: "Disabled adding room", + }) + const addRoomLabel = intl.formatMessage({ id: "Add Room" }) + + const { getFieldState } = useFormContext() + + const rooms = useGuestsRoomsStore((state) => state.rooms) + + return ( + + {({ close }) => { + return ( + <> +
+ +
+
+ {rooms.map((room, index) => ( +
+
+ + {roomLabel} {index + 1} + + + +
+ +
+ ))} +
+ + {rooms.length < 4 ? ( + + ) : null} + +
+
+
+
+ + {rooms.length < 4 ? ( + + ) : null} + +
+ + +
+ + ) + }} +
+ ) +} diff --git a/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx b/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx deleted file mode 100644 index 8950a1585..000000000 --- a/components/GuestsRoomsPicker/GuestsRoomsPicker.tsx +++ /dev/null @@ -1,136 +0,0 @@ -"use client" -import { useFormContext } from "react-hook-form" -import { useIntl } from "react-intl" - -import { useGuestsRoomsStore } from "@/stores/guests-rooms" - -import { CloseLargeIcon, PlusCircleIcon, PlusIcon } from "../Icons" -import Button from "../TempDesignSystem/Button" -import Divider from "../TempDesignSystem/Divider" -import Subtitle from "../TempDesignSystem/Text/Subtitle" -import { Tooltip } from "../TempDesignSystem/Tooltip" -import AdultSelector from "./AdultSelector" -import ChildSelector from "./ChildSelector" - -import styles from "./guests-rooms-picker.module.css" - -import { BookingWidgetSchema } from "@/types/components/bookingWidget" -import { GuestsRoomsPickerProps } from "@/types/components/bookingWidget/guestsRoomsPicker" - -export default function GuestsRoomsPicker({ - closePicker, -}: GuestsRoomsPickerProps) { - const intl = useIntl() - const doneLabel = intl.formatMessage({ id: "Done" }) - const roomLabel = intl.formatMessage({ id: "Room" }) - const disabledBookingOptionsHeader = intl.formatMessage({ - id: "Disabled booking options header", - }) - const disabledBookingOptionsText = intl.formatMessage({ - id: "Disabled adding room", - }) - const addRoomLabel = intl.formatMessage({ id: "Add Room" }) - - const { getFieldState } = useFormContext() - - const rooms = useGuestsRoomsStore((state) => state.rooms) - - // Not in MVP - // const increaseRoom = useGuestsRoomsStore.use.increaseRoom() - // const decreaseRoom = useGuestsRoomsStore.use.decreaseRoom() - - return ( -
-
- -
-
- {rooms.map((room, index) => ( -
-
- - {roomLabel} {index + 1} - - - -
- {/* Not in MVP - {index > 0 ? ( - - ) : null} */} - -
- ))} -
- - {rooms.length < 4 ? ( - - ) : null} - -
-
-
-
- - {rooms.length < 4 ? ( - - ) : null} - -
- - -
-
- ) -} diff --git a/components/GuestsRoomsPicker/guests-rooms-picker.module.css b/components/GuestsRoomsPicker/guests-rooms-picker.module.css index 23a84fd62..01dea2e78 100644 --- a/components/GuestsRoomsPicker/guests-rooms-picker.module.css +++ b/components/GuestsRoomsPicker/guests-rooms-picker.module.css @@ -1,9 +1,6 @@ .container { overflow: hidden; position: relative; - &[data-isopen="true"] { - overflow: visible; - } } .roomContainer { display: grid; @@ -14,9 +11,6 @@ gap: var(--Spacing-x2); padding-bottom: var(--Spacing-x1); } -.hideWrapper { - background-color: var(--Main-Grey-White); -} .roomHeading { margin-bottom: var(--Spacing-x1); } @@ -39,33 +33,25 @@ margin-top: var(--Spacing-x2); } -@media screen and (max-width: 1366px) { - .hideWrapper { - border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0; - bottom: 0; - left: 0; - position: fixed; - right: 0; - top: 100%; - transition: top 300ms ease; - z-index: 10002; - overflow: hidden; - } +.pickerContainer { + --header-height: 72px; + --sticky-button-height: 140px; + background-color: var(--Main-Grey-White); + display: grid; + border-radius: var(--Corner-radius-Large); + box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1); + + max-width: calc(100vw - 20px); + padding: var(--Spacing-x2) var(--Spacing-x3); + + width: 360px; +} +@media screen and (max-width: 1366px) { .container[data-isopen="true"] .hideWrapper { top: 20px; } - .pickerContainer { - --header-height: 72px; - --sticky-button-height: 140px; - display: grid; - grid-template-areas: - "header" - "content"; - grid-template-rows: var(--header-height) calc(100dvh - var(--header-height)); - position: relative; - } .contentContainer { grid-area: content; overflow-y: scroll; @@ -121,19 +107,6 @@ } @media screen and (min-width: 1367px) { - .hideWrapper { - border-radius: var(--Corner-radius-Large); - box-shadow: 0px 0px 14px 6px rgba(0, 0, 0, 0.1); - left: calc((var(--Spacing-x1) + var(--Spacing-x2)) * -1); - max-width: calc(100vw - 20px); - padding: var(--Spacing-x2) var(--Spacing-x3); - position: absolute; - top: calc(100% + var(--Spacing-x2) + 1px + var(--Spacing-x4)); - width: 360px; - max-height: calc(100dvh - 77px - var(--Spacing-x6)); - overflow-y: auto; - } - .header { display: none; } diff --git a/components/GuestsRoomsPicker/index.tsx b/components/GuestsRoomsPicker/index.tsx index 090fc3803..84ef6f930 100644 --- a/components/GuestsRoomsPicker/index.tsx +++ b/components/GuestsRoomsPicker/index.tsx @@ -1,6 +1,7 @@ "use client" import { useCallback, useEffect, useRef, useState } from "react" +import { Button, DialogTrigger, Popover } from "react-aria-components" import { useFormContext } from "react-hook-form" import { useIntl } from "react-intl" @@ -9,7 +10,7 @@ import { useGuestsRoomsStore } from "@/stores/guests-rooms" import { guestRoomsSchema } from "@/components/Forms/BookingWidget/schema" import Body from "@/components/TempDesignSystem/Text/Body" -import GuestsRoomsPicker from "./GuestsRoomsPicker" +import Dialog from "./Dialog" import styles from "./guests-rooms-picker.module.css" @@ -19,47 +20,16 @@ export default function GuestsRoomsPickerForm({ name: string }) { const intl = useIntl() - const [isOpen, setIsOpen] = useState(false) - const { setValue } = useFormContext() - const { rooms, adultCount, childCount, setIsValidated } = useGuestsRoomsStore( - (state) => ({ - rooms: state.rooms, - adultCount: state.adultCount, - childCount: state.childCount, - setIsValidated: state.setIsValidated, - }) - ) - const ref = useRef(null) - function handleOnClick() { - setIsOpen((prevIsOpen) => !prevIsOpen) - } - const closePicker = useCallback(() => { - const guestRoomsValidData = guestRoomsSchema.safeParse(rooms) - if (guestRoomsValidData.success) { - setIsOpen(false) - setIsValidated(false) - setValue(name, guestRoomsValidData.data, { shouldValidate: true }) - } else { - setIsValidated(true) - } - }, [rooms, name, setValue, setIsValidated, setIsOpen]) - useEffect(() => { - function handleClickOutside(evt: Event) { - const target = evt.target as HTMLElement - if (ref.current && target && !ref.current.contains(target)) { - closePicker() - } - } - document.addEventListener("click", handleClickOutside) - return () => { - document.removeEventListener("click", handleClickOutside) - } - }, [closePicker]) + const { rooms, adultCount, childCount } = useGuestsRoomsStore((state) => ({ + rooms: state.rooms, + adultCount: state.adultCount, + childCount: state.childCount, + })) return ( -
- -
- -
-
+ + + + + ) } diff --git a/types/components/bookingWidget/guestsRoomsPicker.ts b/types/components/bookingWidget/guestsRoomsPicker.ts index 61e8f7d7a..b59744afc 100644 --- a/types/components/bookingWidget/guestsRoomsPicker.ts +++ b/types/components/bookingWidget/guestsRoomsPicker.ts @@ -13,10 +13,6 @@ export type GuestsRoom = { child: Child[] } -export interface GuestsRoomsPickerProps { - closePicker: () => void -} - export type GuestsRoomPickerProps = { index: number }