From 1980ae4350ce39c20d7c17935bcdd537fcfa2db1 Mon Sep 17 00:00:00 2001 From: Anton Gunnarsson Date: Thu, 14 Nov 2024 13:24:06 +0100 Subject: [PATCH] Skeleton loader for booking widget on desktop --- app/[lang]/(live)/@bookingwidget/loading.tsx | 10 +-- .../BookingWidget/BookingWidgetSkeleton.tsx | 90 +++++++++++++++++++ .../FormContent/Search/index.tsx | 16 ++++ .../FormContent/Voucher/index.tsx | 52 +++++++++++ .../Header/MainMenu/MyPagesMenu/index.tsx | 11 +++ .../MainMenu/MyPagesMenuWrapper/index.tsx | 11 ++- components/Header/MainMenu/index.tsx | 6 +- components/SkeletonShimmer/index.tsx | 19 ++++ .../SkeletonShimmer/skeleton.module.css | 29 ++++++ 9 files changed, 233 insertions(+), 11 deletions(-) create mode 100644 components/BookingWidget/BookingWidgetSkeleton.tsx create mode 100644 components/SkeletonShimmer/index.tsx create mode 100644 components/SkeletonShimmer/skeleton.module.css diff --git a/app/[lang]/(live)/@bookingwidget/loading.tsx b/app/[lang]/(live)/@bookingwidget/loading.tsx index 5e05ba68c..f62e3acb5 100644 --- a/app/[lang]/(live)/@bookingwidget/loading.tsx +++ b/app/[lang]/(live)/@bookingwidget/loading.tsx @@ -1,17 +1,11 @@ import { env } from "@/env/server" -import LoadingSpinner from "@/components/LoadingSpinner" - -import styles from "./loading.module.css" +import BookingWidgetSkeleton from "@/components/BookingWidget/BookingWidgetSkeleton" export default function LoadingBookingWidget() { if (env.HIDE_FOR_NEXT_RELEASE) { return null } - return ( -
- -
- ) + return } diff --git a/components/BookingWidget/BookingWidgetSkeleton.tsx b/components/BookingWidget/BookingWidgetSkeleton.tsx new file mode 100644 index 000000000..a784f0636 --- /dev/null +++ b/components/BookingWidget/BookingWidgetSkeleton.tsx @@ -0,0 +1,90 @@ +"use client" +import { useIntl } from "react-intl" + +import { SearchIcon } from "@/components/Icons" + +import { SearchSkeleton } from "../Forms/BookingWidget/FormContent/Search" +import { VoucherSkeleton } from "../Forms/BookingWidget/FormContent/Voucher" +import { bookingWidgetVariants } from "../Forms/BookingWidget/variants" +import SkeletonShimmer from "../SkeletonShimmer" +import Button from "../TempDesignSystem/Button" +import Caption from "../TempDesignSystem/Text/Caption" + +import formStyles from "../Forms/BookingWidget/form.module.css" +import formContentStyles from "../Forms/BookingWidget/FormContent/formContent.module.css" +import widgetStyles from "./bookingWidget.module.css" + +export default function BookingWidgetSkeleton() { + const intl = useIntl() + + const classNames = bookingWidgetVariants({ + type: "full", + }) + + return ( +
+
+
+
+
+
+ +
+
+ + {intl.formatMessage( + { id: "booking.nights" }, + { totalNights: 0 } + )} + + +
+
+ + {intl.formatMessage({ id: "Guests & Rooms" })} + + +
+
+
+ +
+
+ +
+
+
+
+ {/*
+ +
+
+
+ */} +
+ ) +} diff --git a/components/Forms/BookingWidget/FormContent/Search/index.tsx b/components/Forms/BookingWidget/FormContent/Search/index.tsx index ac820da51..abbb28112 100644 --- a/components/Forms/BookingWidget/FormContent/Search/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Search/index.tsx @@ -11,6 +11,7 @@ import { import { useFormContext, useWatch } from "react-hook-form" import { useIntl } from "react-intl" +import SkeletonShimmer from "@/components/SkeletonShimmer" import Caption from "@/components/TempDesignSystem/Text/Caption" import Input from "../Input" @@ -203,3 +204,18 @@ export default function Search({ locations }: SearchProps) { ) } + +export function SearchSkeleton() { + return ( +
+
+ + Where to + +
+
+ +
+
+ ) +} diff --git a/components/Forms/BookingWidget/FormContent/Voucher/index.tsx b/components/Forms/BookingWidget/FormContent/Voucher/index.tsx index dce678dda..abb770413 100644 --- a/components/Forms/BookingWidget/FormContent/Voucher/index.tsx +++ b/components/Forms/BookingWidget/FormContent/Voucher/index.tsx @@ -1,4 +1,5 @@ "use client" +import { FormProvider, useForm } from "react-hook-form" import { useIntl } from "react-intl" import Checkbox from "@/components/TempDesignSystem/Form/Checkbox" @@ -78,3 +79,54 @@ export default function Voucher() { ) } + +export function VoucherSkeleton() { + const intl = useIntl() + + const vouchers = intl.formatMessage({ id: "Code / Voucher" }) + const useVouchers = intl.formatMessage({ id: "Use code/voucher" }) + const addVouchers = intl.formatMessage({ id: "Add code" }) + const bonus = intl.formatMessage({ id: "Use bonus cheque" }) + const reward = intl.formatMessage({ id: "Book reward night" }) + + const form = useForm() + + return ( + +
+
+ + +
+
+
+ + + {useVouchers} + + +
+
+ + + {bonus} + + +
+
+ + + {reward} + + +
+
+
+
+ ) +} diff --git a/components/Header/MainMenu/MyPagesMenu/index.tsx b/components/Header/MainMenu/MyPagesMenu/index.tsx index 86f03e0ff..ed2fcf949 100644 --- a/components/Header/MainMenu/MyPagesMenu/index.tsx +++ b/components/Header/MainMenu/MyPagesMenu/index.tsx @@ -6,6 +6,7 @@ import { useIntl } from "react-intl" import useDropdownStore from "@/stores/main-menu" import { ChevronDownSmallIcon } from "@/components/Icons" +import SkeletonShimmer from "@/components/SkeletonShimmer" import Body from "@/components/TempDesignSystem/Text/Body" import useClickOutside from "@/hooks/useClickOutside" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" @@ -73,3 +74,13 @@ export default function MyPagesMenu({ ) } + +export function MyPagesMenuSkeleton() { + return ( + + + + + + ) +} diff --git a/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx b/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx index c4e7fc761..af4a24288 100644 --- a/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx +++ b/components/Header/MainMenu/MyPagesMenuWrapper/index.tsx @@ -10,7 +10,7 @@ import LoginButton from "@/components/LoginButton" import { getIntl } from "@/i18n" import Avatar from "../Avatar" -import MyPagesMenu from "../MyPagesMenu" +import MyPagesMenu, { MyPagesMenuSkeleton } from "../MyPagesMenu" import MyPagesMobileMenu from "../MyPagesMobileMenu" import styles from "./myPagesMenuWrapper.module.css" @@ -62,3 +62,12 @@ export default async function MyPagesMenuWrapper() { ) } + +export function MyPagesMenuWrapperSkeleton() { + return ( +
+ + {/* */} +
+ ) +} diff --git a/components/Header/MainMenu/index.tsx b/components/Header/MainMenu/index.tsx index ff3c8c514..fd089df3e 100644 --- a/components/Header/MainMenu/index.tsx +++ b/components/Header/MainMenu/index.tsx @@ -6,7 +6,9 @@ import { getIntl } from "@/i18n" import { getLang } from "@/i18n/serverContext" import MobileMenuWrapper from "./MobileMenuWrapper" -import MyPagesMenuWrapper from "./MyPagesMenuWrapper" +import MyPagesMenuWrapper, { + MyPagesMenuWrapperSkeleton, +} from "./MyPagesMenuWrapper" import NavigationMenu from "./NavigationMenu" import styles from "./mainMenu.module.css" @@ -32,7 +34,7 @@ export default async function MainMenu() { - + }> diff --git a/components/SkeletonShimmer/index.tsx b/components/SkeletonShimmer/index.tsx new file mode 100644 index 000000000..68586e3ca --- /dev/null +++ b/components/SkeletonShimmer/index.tsx @@ -0,0 +1,19 @@ +import styles from "./skeleton.module.css" + +export default function SkeletonShimmer({ + height, + width, +}: { + height?: string + width?: string +}) { + return ( +
+ ) +} diff --git a/components/SkeletonShimmer/skeleton.module.css b/components/SkeletonShimmer/skeleton.module.css new file mode 100644 index 000000000..36d399ef1 --- /dev/null +++ b/components/SkeletonShimmer/skeleton.module.css @@ -0,0 +1,29 @@ +.shimmer { + background-color: hsla(0, 0%, 85%, 0.5); + position: relative; + overflow: hidden; + border-radius: 4px; + min-height: 1em; +} +.shimmer::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + transform: translateX(-100%); + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0, + rgba(255, 255, 255, 0.2) 20%, + rgba(255, 255, 255, 0.5) 60%, + rgba(255, 255, 255, 0) 100% + ); + animation: shimmer 3s infinite; + content: ""; +} +@keyframes shimmer { + 100% { + transform: translateX(100%); + } +}