diff --git a/apps/web/modules/bookings/views/bookings-listing-view.tsx b/apps/web/modules/bookings/views/bookings-listing-view.tsx index 3c1e751e08..662db6ac1d 100644 --- a/apps/web/modules/bookings/views/bookings-listing-view.tsx +++ b/apps/web/modules/bookings/views/bookings-listing-view.tsx @@ -7,7 +7,9 @@ import { getSortedRowModel, createColumnHelper, } from "@tanstack/react-table"; -import { useMemo, useState, useEffect } from "react"; +// eslint-disable-next-line no-restricted-imports +import { debounce } from "lodash"; +import { useMemo, useState, useRef, useEffect, useCallback } from "react"; import type { z } from "zod"; import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components"; @@ -105,6 +107,8 @@ function BookingsContent({ status }: BookingsProps) { const { t } = useLocale(); const user = useMeQuery().data; const [isFiltersVisible, setIsFiltersVisible] = useState(false); + const tableContainerRef = useRef(null); + useProperHeightForMobile(tableContainerRef); useEffect(() => { if (user?.isTeamAdminOrOwner && !filterQuery.userIds?.length) { @@ -276,6 +280,7 @@ function BookingsContent({ status }: BookingsProps) { )} ); } + +// Dynamically adjusts DataTable height on mobile to prevent nested scrolling +// and ensure the table fits within the viewport without overflowing (hacky) +function useProperHeightForMobile(ref: React.RefObject) { + const lastOffsetY = useRef(); + const lastWindowHeight = useRef(); + const BOTTOM_NAV_HEIGHT = 64; + const BUFFER = 32; + + const updateHeight = useCallback( + debounce(() => { + if (!ref.current || window.innerWidth >= 640) return; + const rect = ref.current.getBoundingClientRect(); + if (rect.top !== lastOffsetY.current || window.innerHeight !== lastWindowHeight.current) { + lastOffsetY.current = rect.top; + lastWindowHeight.current = window.innerHeight; + const height = window.innerHeight - lastOffsetY.current - BOTTOM_NAV_HEIGHT - BUFFER; + ref.current.style.height = `${height}px`; + } + }, 200), + [ref.current] + ); + + useEffect(() => { + const handleResize = () => { + updateHeight(); + }; + + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("resize", handleResize); + }; + }, [updateHeight]); + + updateHeight(); +} diff --git a/packages/features/data-table/components/DataTableWrapper.tsx b/packages/features/data-table/components/DataTableWrapper.tsx index 105c803ede..8a1218d1dd 100644 --- a/packages/features/data-table/components/DataTableWrapper.tsx +++ b/packages/features/data-table/components/DataTableWrapper.tsx @@ -28,6 +28,7 @@ export type DataTableWrapperProps = { className?: string; containerClassName?: string; children?: React.ReactNode; + tableContainerRef?: React.RefObject; }; export function DataTableWrapper({ @@ -46,8 +47,10 @@ export function DataTableWrapper({ className, containerClassName, children, + tableContainerRef: externalRef, }: DataTableWrapperProps) { - const tableContainerRef = useRef(null); + const internalRef = useRef(null); + const tableContainerRef = externalRef || internalRef; const fetchMoreOnBottomReached = useFetchMoreOnBottomReached({ tableContainerRef, hasNextPage,