add fullscreen mode to realtime
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { useFullscreen } from '@/components/fullscreen-toggle';
|
||||||
import { Tooltiper } from '@/components/ui/tooltip';
|
import { Tooltiper } from '@/components/ui/tooltip';
|
||||||
|
import { cn } from '@/utils/cn';
|
||||||
import { bind } from 'bind-event-listener';
|
import { bind } from 'bind-event-listener';
|
||||||
import {
|
import {
|
||||||
ComposableMap,
|
ComposableMap,
|
||||||
@@ -25,12 +26,13 @@ import {
|
|||||||
getBoundingBox,
|
getBoundingBox,
|
||||||
useAnimatedState,
|
useAnimatedState,
|
||||||
} from './map.helpers';
|
} from './map.helpers';
|
||||||
import useActiveMarkers, { calculateMarkerSize } from './markers';
|
import { calculateMarkerSize } from './markers';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
markers: Coordinate[];
|
markers: Coordinate[];
|
||||||
};
|
};
|
||||||
const Map = ({ markers }: Props) => {
|
const Map = ({ markers }: Props) => {
|
||||||
|
const [isFullscreen] = useFullscreen();
|
||||||
const showCenterMarker = false;
|
const showCenterMarker = false;
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const [size, setSize] = useState<{ width: number; height: number } | null>(
|
const [size, setSize] = useState<{ width: number; height: number } | null>(
|
||||||
@@ -54,31 +56,33 @@ const Map = ({ markers }: Props) => {
|
|||||||
const [lat] = useAnimatedState(center.lat);
|
const [lat] = useAnimatedState(center.lat);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ref.current) {
|
requestAnimationFrame(() => {
|
||||||
setSize({
|
if (ref.current) {
|
||||||
width: ref.current.clientWidth,
|
setSize({
|
||||||
height: ref.current.clientHeight,
|
width: ref.current.clientWidth,
|
||||||
});
|
height: ref.current.clientHeight,
|
||||||
}
|
});
|
||||||
}, []);
|
}
|
||||||
|
});
|
||||||
|
}, [isFullscreen]);
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// return bind(window, {
|
return bind(window, {
|
||||||
// type: 'resize',
|
type: 'resize',
|
||||||
// listener() {
|
listener() {
|
||||||
// if (ref.current) {
|
if (ref.current) {
|
||||||
// setSize({
|
setSize({
|
||||||
// width: ref.current.clientWidth,
|
width: ref.current.clientWidth,
|
||||||
// height: ref.current.clientHeight,
|
height: ref.current.clientHeight,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// });
|
});
|
||||||
// }, []);
|
}, []);
|
||||||
|
|
||||||
const adjustSizeBasedOnZoom = (size: number) => {
|
const adjustSizeBasedOnZoom = (size: number) => {
|
||||||
const minMultiplier = 1;
|
const minMultiplier = 1;
|
||||||
const maxMultiplier = 3;
|
const maxMultiplier = 7;
|
||||||
|
|
||||||
// Linearly interpolate the multiplier based on the zoom level
|
// Linearly interpolate the multiplier based on the zoom level
|
||||||
const multiplier =
|
const multiplier =
|
||||||
@@ -88,7 +92,13 @@ const Map = ({ markers }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-0 left-0 right-0 top-16 lg:left-72" ref={ref}>
|
<div
|
||||||
|
className={cn(
|
||||||
|
'fixed bottom-0 left-0 right-0 top-0',
|
||||||
|
!isFullscreen && 'top-16 lg:left-72'
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
{size === null ? (
|
{size === null ? (
|
||||||
<></>
|
<></>
|
||||||
) : (
|
) : (
|
||||||
@@ -102,7 +112,7 @@ const Map = ({ markers }: Props) => {
|
|||||||
scale: 100 * 20,
|
scale: 100 * 20,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomZoomableGroup zoom={zoom * 0.05} center={[long, lat]}>
|
<CustomZoomableGroup zoom={zoom * 0.06} center={[long, lat]}>
|
||||||
<Geographies geography={GEO_MAP_URL}>
|
<Geographies geography={GEO_MAP_URL}>
|
||||||
{({ geographies }) =>
|
{({ geographies }) =>
|
||||||
geographies.map((geo) => (
|
geographies.map((geo) => (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
|
import { Fullscreen, FullscreenToggle } from '@/components/fullscreen-toggle';
|
||||||
import { LazyChart } from '@/components/report/chart/LazyChart';
|
import { LazyChart } from '@/components/report/chart/LazyChart';
|
||||||
|
|
||||||
import PageLayout from '../page-layout';
|
import PageLayout from '../page-layout';
|
||||||
@@ -17,22 +18,25 @@ export default function Page({
|
|||||||
params: { projectId, organizationSlug },
|
params: { projectId, organizationSlug },
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<>
|
||||||
<RealtimeReloader projectId={projectId} />
|
<PageLayout
|
||||||
<PageLayout title="Realtime" {...{ projectId, organizationSlug }} />
|
title={<FullscreenToggle />}
|
||||||
<Suspense>
|
{...{ projectId, organizationSlug }}
|
||||||
<RealtimeMap projectId={projectId} />
|
/>
|
||||||
</Suspense>
|
<Fullscreen>
|
||||||
<div className="pointer-events-none relative z-10 w-full overflow-hidden">
|
<RealtimeReloader projectId={projectId} />
|
||||||
<div className="pointer-events-none grid min-h-[calc(100vh-theme(spacing.16))] items-start gap-4 p-8 md:grid-cols-3">
|
<Suspense>
|
||||||
|
<RealtimeMap projectId={projectId} />
|
||||||
|
</Suspense>
|
||||||
|
<div className="relative z-10 grid min-h-[calc(100vh-theme(spacing.16))] items-start gap-4 overflow-hidden p-8 md:grid-cols-3">
|
||||||
<div className="card bg-background/80 p-4">
|
<div className="card bg-background/80 p-4">
|
||||||
<RealtimeLiveHistogram projectId={projectId} />
|
<RealtimeLiveHistogram projectId={projectId} />
|
||||||
</div>
|
</div>
|
||||||
<div className="pointer-events-auto col-span-2">
|
<div className="col-span-2">
|
||||||
<RealtimeLiveEventsServer projectId={projectId} limit={5} />
|
<RealtimeLiveEventsServer projectId={projectId} limit={5} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="-mt-32 grid gap-4 p-8 md:grid-cols-3">
|
<div className="relative z-10 -mt-32 grid gap-4 p-8 md:grid-cols-3">
|
||||||
<div className="card p-4">
|
<div className="card p-4">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="font-bold">Pages</div>
|
<div className="font-bold">Pages</div>
|
||||||
@@ -130,7 +134,7 @@ export default function Page({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Fullscreen>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
51
apps/dashboard/src/components/fullscreen-toggle.tsx
Normal file
51
apps/dashboard/src/components/fullscreen-toggle.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { cn } from '@/utils/cn';
|
||||||
|
import { FullscreenIcon } from 'lucide-react';
|
||||||
|
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||||
|
|
||||||
|
import { Tooltiper } from './ui/tooltip';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFullscreen = () =>
|
||||||
|
useQueryState(
|
||||||
|
'fullscreen',
|
||||||
|
parseAsBoolean.withDefault(false).withOptions({
|
||||||
|
history: 'push',
|
||||||
|
clearOnDefault: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Fullscreen = (props: Props) => {
|
||||||
|
const [isFullscreen] = useFullscreen();
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
isFullscreen && 'fixed inset-0 z-50 overflow-auto bg-slate-100'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FullscreenToggle = () => {
|
||||||
|
const [, setIsFullscreen] = useFullscreen();
|
||||||
|
return (
|
||||||
|
<Tooltiper content="Toggle fullscreen" asChild>
|
||||||
|
<button
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
onClick={() => {
|
||||||
|
setIsFullscreen((p) => !p);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FullscreenIcon />
|
||||||
|
Realtime
|
||||||
|
</button>
|
||||||
|
</Tooltiper>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user