Merge branch 'oneclient/main' of https://github.com/Polyfrost/OneLauncher into refactor/frontend
This commit is contained in:
commit
f025673d9d
12 changed files with 312 additions and 126 deletions
|
|
@ -13,7 +13,7 @@ export function Navbar() {
|
|||
const onClose = () => Window.getCurrent().close();
|
||||
|
||||
return (
|
||||
<nav className="flex flex-row items-center justify-between h-20" data-tauri-drag-region="true">
|
||||
<nav className="flex flex-row items-center justify-between h-20 px-12 z-50" data-tauri-drag-region="true">
|
||||
<div className="flex flex-1 pointer-events-none">
|
||||
<LauncherLogo height={47} width={230} />
|
||||
</div>
|
||||
|
|
|
|||
131
apps/oneclient/frontend/src/components/SheetPage.tsx
Normal file
131
apps/oneclient/frontend/src/components/SheetPage.tsx
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import type { MotionValue } from 'motion/react';
|
||||
import useAppShellStore from '@/stores/appShellStore';
|
||||
import { getLocationName } from '@/utils/locationMapping';
|
||||
import { Button } from '@onelauncher/common/components';
|
||||
import { Outlet, useNavigate } from '@tanstack/react-router';
|
||||
import { ArrowLeftIcon } from '@untitled-theme/icons-react';
|
||||
import { motion, useScroll, useTransform } from 'motion/react';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
export function SheetPage({
|
||||
headerSmall,
|
||||
headerLarge,
|
||||
}: {
|
||||
headerSmall: React.ReactNode;
|
||||
headerLarge: React.ReactNode;
|
||||
}) {
|
||||
const scrollContainer = useRef<HTMLElement>(null);
|
||||
const headerLargeRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { scrollYProgress } = useScroll({
|
||||
axis: 'y',
|
||||
container: scrollContainer,
|
||||
target: headerLargeRef,
|
||||
layoutEffect: false,
|
||||
offset: ['end start', 'start start'],
|
||||
});
|
||||
|
||||
const smallTop = useTransform(scrollYProgress, [0, 1], ['0', '100%']);
|
||||
const smallOpacity = useTransform(scrollYProgress, [0, 1], ['1', '0']);
|
||||
const largeOpacity = useTransform(scrollYProgress, [0, 1], ['0', '1']);
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
<HeaderBackgrounds scrollYProgress={scrollYProgress} />
|
||||
|
||||
<div className="flex flex-col w-full min-h-full h-0">
|
||||
<div className="sticky top-0 left-0 mx-12 flex flex-row gap-4 h-8 overflow-hidden">
|
||||
<GoBackButton />
|
||||
|
||||
<motion.div
|
||||
className="relative w-full h-full left-0" style={{
|
||||
top: smallTop,
|
||||
opacity: smallOpacity,
|
||||
}}
|
||||
>
|
||||
{headerSmall}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<OverlayScrollbarsComponent className="flex flex-col flex-1 mask-t-from-97% pt-4 relative" ref={ref => scrollContainer.current = ref?.osInstance()?.elements().content ?? null}>
|
||||
<div className="flex flex-col mx-12 gap-4">
|
||||
<motion.div className="w-full" ref={headerLargeRef} style={{ opacity: largeOpacity }}>
|
||||
{/* <headerLarge ref={headerLargeRef as React.RefObject<HTMLDivElement>} /> */}
|
||||
{headerLarge}
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
animate={{ bottom: 0 }}
|
||||
className="relative flex flex-col px-10 py-6 mb-8 bg-page-elevated rounded-2xl"
|
||||
initial={{ bottom: '-80px' }}
|
||||
>
|
||||
<Outlet />
|
||||
</motion.div>
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GoBackButton() {
|
||||
const prevLocation = useAppShellStore(state => state.prevLocation);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onClick = () => {
|
||||
if (!prevLocation)
|
||||
return;
|
||||
|
||||
navigate({
|
||||
to: prevLocation.pathname,
|
||||
search: prevLocation.search,
|
||||
hash: prevLocation.hash,
|
||||
state: prevLocation.state,
|
||||
});
|
||||
};
|
||||
|
||||
if (!prevLocation)
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
<Button color="ghost" onClick={onClick}>
|
||||
<ArrowLeftIcon height={20} />
|
||||
<span className="h-5">{getLocationName(prevLocation.pathname)}</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderBackgrounds({
|
||||
scrollYProgress,
|
||||
}: {
|
||||
scrollYProgress: MotionValue<number>;
|
||||
}) {
|
||||
const top = useTransform(scrollYProgress, [1, 0], ['0px', '-60px']);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute left-0 h-full w-full"
|
||||
style={{
|
||||
top,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="absolute -z-10 -top-20 h-50 left-0 w-screen animate-fade animate-duration-200" style={{
|
||||
background: 'rgba(0, 0, 0, 0.20)',
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="relative h-50 top-0 left-0 w-screen animate-fade animate-duration-200"
|
||||
style={{
|
||||
background: 'linear-gradient(180deg, rgba(17, 23, 28, 0.00) 0%, #11171C 60.1%)',
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
|
||||
<div className="relative top-0 left-0 w-full h-full bg-page animate-fade animate-duration-200"></div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import { createRootRouteWithContext, Outlet, useRouter } from '@tanstack/react-r
|
|||
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools';
|
||||
import { RouterProvider } from 'react-aria-components';
|
||||
|
||||
interface MyRouterContext {
|
||||
interface AppRouterContext {
|
||||
queryClient: QueryClient;
|
||||
}
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ declare module 'react-aria-components' {
|
|||
}
|
||||
}
|
||||
|
||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
export const Route = createRootRouteWithContext<AppRouterContext>()({
|
||||
component: RootRoute,
|
||||
});
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ function RootRoute() {
|
|||
navigate={(to, options) => router.navigate({ to, ...options })}
|
||||
useHref={to => router.buildLocation({ to }).href}
|
||||
>
|
||||
<div className="h-screen flex flex-col overflow-hidden text-fg-primary px-12">
|
||||
<div className="h-screen flex flex-col overflow-hidden text-fg-primary">
|
||||
<Outlet />
|
||||
|
||||
<TanStackRouterDevtools />
|
||||
|
|
|
|||
|
|
@ -5,5 +5,13 @@ export const Route = createFileRoute('/app/clusters/')({
|
|||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/app/clusters/"!</div>;
|
||||
return (
|
||||
<div>
|
||||
<div>Hello "/app/clusters/"!</div>
|
||||
|
||||
<div className="w-px h-screen bg-red-100/10"></div>
|
||||
|
||||
<div>Hello "/app/clusters/"!</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { AnimatedOutlet, AnimatedOutletProvider } from '@onelauncher/common/components';
|
||||
import { SheetPage } from '@/components/SheetPage';
|
||||
import { Button } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/app/clusters')({
|
||||
|
|
@ -7,51 +8,38 @@ export const Route = createFileRoute('/app/clusters')({
|
|||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="flex w-full h-full">
|
||||
<BackgroundGradients />
|
||||
|
||||
<AnimatedOutletProvider>
|
||||
<AnimatedOutlet
|
||||
enter={{
|
||||
initial: {
|
||||
bottom: '-100%',
|
||||
opacity: 0,
|
||||
},
|
||||
animate: {
|
||||
bottom: 0,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
exit={{
|
||||
initial: {
|
||||
bottom: 0,
|
||||
opacity: 1,
|
||||
},
|
||||
animate: {
|
||||
bottom: '-100%',
|
||||
opacity: 0,
|
||||
},
|
||||
}}
|
||||
from={Route.id}
|
||||
/>
|
||||
</AnimatedOutletProvider>
|
||||
</div>
|
||||
<SheetPage
|
||||
headerLarge={<HeaderLarge />}
|
||||
headerSmall={<HeaderSmall />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function BackgroundGradients() {
|
||||
function HeaderLarge({ ref }: { ref?: React.Ref<HTMLDivElement> }) {
|
||||
return (
|
||||
<div className="relative">
|
||||
<div style={{
|
||||
background: 'rgba(0, 0, 0, 0.80)',
|
||||
}}
|
||||
>
|
||||
</div>
|
||||
<div style={{
|
||||
background: 'linear-gradient(180deg, rgba(17, 23, 28, 0.00) 0%, #11171C 60.1%)',
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row justify-between items-end" ref={ref}>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-3xl font-semibold">Header</h1>
|
||||
<p className="text-md font-medium text-fg-secondary">Tailored for Hypixel Skyblock.</p>
|
||||
</div>
|
||||
|
||||
<Button color="primary" size="large">
|
||||
Launch
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderSmall() {
|
||||
return (
|
||||
<div className="flex flex-row w-full items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-4">
|
||||
<h1 className="text-2lg h-full font-medium">Header</h1>
|
||||
</div>
|
||||
|
||||
<Button color="primary">
|
||||
Launch
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,46 +15,36 @@ export const Route = createFileRoute('/app/')({
|
|||
component: RouteComponent,
|
||||
});
|
||||
|
||||
const clusters = [
|
||||
{
|
||||
mc_version: '1.8.9',
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
mc_version: '1.21.4',
|
||||
},
|
||||
];
|
||||
|
||||
function RouteComponent() {
|
||||
// const { data: clusters } = useCommandSuspense<Array<ClusterModel | undefined>>(
|
||||
// 'getClusters',
|
||||
// bindings.core.getClusters,
|
||||
// {
|
||||
// select: ((data: Array<ClusterModel>) => {
|
||||
// const sorted = data.sort((a, b) => {
|
||||
// if (a.last_played && b.last_played)
|
||||
// return new Date(b.last_played).getTime() - new Date(a.last_played).getTime();
|
||||
// else if (a.last_played)
|
||||
// return -1; // a has last_played, b does not
|
||||
// else if (b.last_played)
|
||||
// return 1; // b has last_played, a does not
|
||||
const { data: clusters } = useCommandSuspense<Array<ClusterModel | undefined>>(
|
||||
'getClusters',
|
||||
bindings.core.getClusters,
|
||||
{
|
||||
select: ((data: Array<ClusterModel>) => {
|
||||
const sorted = data.sort((a, b) => {
|
||||
if (a.last_played && b.last_played)
|
||||
return new Date(b.last_played).getTime() - new Date(a.last_played).getTime();
|
||||
else if (a.last_played)
|
||||
return -1; // a has last_played, b does not
|
||||
else if (b.last_played)
|
||||
return 1; // b has last_played, a does not
|
||||
|
||||
// const aVersion = a.mc_version.replaceAll('.', '');
|
||||
// const bVersion = b.mc_version.replaceAll('.', '');
|
||||
// return Number.parseInt(bVersion) - Number.parseInt(aVersion);
|
||||
// });
|
||||
const aVersion = a.mc_version.replaceAll('.', '');
|
||||
const bVersion = b.mc_version.replaceAll('.', '');
|
||||
return Number.parseInt(bVersion) - Number.parseInt(aVersion);
|
||||
});
|
||||
|
||||
// return [
|
||||
// sorted[0],
|
||||
// sorted[1],
|
||||
// undefined,
|
||||
// ];
|
||||
// }) as (data: Array<ClusterModel | undefined>) => Array<ClusterModel | undefined>,
|
||||
// },
|
||||
// );
|
||||
return [
|
||||
sorted[0],
|
||||
sorted[1],
|
||||
undefined,
|
||||
];
|
||||
}) as (data: Array<ClusterModel | undefined>) => Array<ClusterModel | undefined>,
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col justify-center">
|
||||
<div className="flex h-full w-full flex-col justify-center p-12">
|
||||
<motion.div {...animations.slideInLeft} className="flex flex-1 flex-col justify-center items-start gap-2" transition={{ ...transitions.spring, delay: 0.2 }}>
|
||||
<h1 className="text-6xl font-bold text-fg-primary">1.8.9</h1>
|
||||
<p className="text-lg font-medium text-fg-secondary">The Bountiful Update</p>
|
||||
|
|
@ -65,9 +55,9 @@ function RouteComponent() {
|
|||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div {...animations.slideInUp} className="flex flex-row transition-[height] max-h-84 h-[28vh] max-xl:h-52 gap-6">
|
||||
{clusters.map(cluster => (
|
||||
<RecentsCard key={cluster?.mc_version} version={cluster?.mc_version} />
|
||||
<motion.div {...animations.slideInUp} className="flex flex-row transition-[height] h-52 gap-6">
|
||||
{clusters.map((cluster, index) => (
|
||||
<RecentsCard key={cluster?.folder_name ?? index} version={cluster?.mc_version} />
|
||||
))}
|
||||
|
||||
<Card blur className="flex flex-col justify-center items-center max-w-24">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { useEffect, type PropsWithChildren } from 'react';
|
||||
import { LoaderSuspense, Navbar } from '@/components';
|
||||
import { GameBackground } from '@/components/GameBackground';
|
||||
import useAppShellStore from '@/stores/appShellStore';
|
||||
import { AnimatedOutlet } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { createFileRoute, useRouter } from '@tanstack/react-router';
|
||||
import { AnimatePresence } from 'motion/react';
|
||||
|
||||
export const Route = createFileRoute('/app')({
|
||||
component: RouteComponent,
|
||||
|
|
@ -10,12 +13,8 @@ export const Route = createFileRoute('/app')({
|
|||
function RouteComponent() {
|
||||
return (
|
||||
<LoaderSuspense spinner={{ size: 'large' }}>
|
||||
<div className="flex flex-col h-full w-full">
|
||||
<Navbar />
|
||||
|
||||
<Background />
|
||||
|
||||
<div className="h-full w-full pb-12">
|
||||
<AppShell>
|
||||
<div className="h-full w-full">
|
||||
<AnimatedOutlet
|
||||
enter={{
|
||||
initial: { opacity: 0 },
|
||||
|
|
@ -29,15 +28,49 @@ function RouteComponent() {
|
|||
transition={{ duration: 0.3, bounce: 0.1, power: 0.2, type: 'spring' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</AppShell>
|
||||
</LoaderSuspense>
|
||||
);
|
||||
}
|
||||
|
||||
function Background() {
|
||||
function AppShell({
|
||||
children,
|
||||
}: PropsWithChildren) {
|
||||
const router = useRouter();
|
||||
const setPrevLocation = useAppShellStore(state => state.setPrevLocation);
|
||||
|
||||
useEffect(() => {
|
||||
const unsub = router.subscribe("onBeforeNavigate", (e) => {
|
||||
setPrevLocation(e.fromLocation ?? null);
|
||||
});
|
||||
|
||||
return () => unsub();
|
||||
}, [router]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full w-full">
|
||||
<AnimatePresence>
|
||||
<BackgroundGradient />
|
||||
</AnimatePresence>
|
||||
|
||||
<Navbar />
|
||||
|
||||
<div className="flex flex-col w-full h-full">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BackgroundGradient() {
|
||||
const background = useAppShellStore(state => state.background);
|
||||
|
||||
if (background === 'none')
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Linear gradient: left -> right */}
|
||||
{/* Linear black gradient: left -> right */}
|
||||
<div
|
||||
className="absolute top-0 left-0 w-screen h-screen -z-10"
|
||||
style={{
|
||||
|
|
@ -46,7 +79,7 @@ function Background() {
|
|||
>
|
||||
</div>
|
||||
|
||||
{/* Radial Gradient */}
|
||||
{/* Radial black gradient */}
|
||||
<div
|
||||
className="absolute top-0 left-0 w-screen h-screen -z-10" style={{
|
||||
background: 'radial-gradient(48.29% 48.29% at 77.29% 50%, rgba(0, 0, 0, 0.00) 0%, rgba(0, 0, 0, 0.64) 100%)',
|
||||
|
|
@ -54,7 +87,7 @@ function Background() {
|
|||
>
|
||||
</div>
|
||||
|
||||
{/* Linear gradient: bottom -> 200 px up */}
|
||||
{/* Linear black gradient: bottom -> 200 px up */}
|
||||
<div
|
||||
className="absolute bottom-0 left-0 w-screen h-50 -z-10" style={{
|
||||
background: 'linear-gradient(180deg, rgba(17, 23, 28, 0.00) 0%, rgba(0, 0, 0, 0.68) 60%)',
|
||||
|
|
|
|||
22
apps/oneclient/frontend/src/stores/appShellStore.ts
Normal file
22
apps/oneclient/frontend/src/stores/appShellStore.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import type { ParsedLocation } from '@tanstack/react-router';
|
||||
import { create } from 'zustand';
|
||||
|
||||
export interface AppShellStore {
|
||||
background: 'gradientOverlay' | 'none';
|
||||
prevLocation: ParsedLocation | null;
|
||||
}
|
||||
|
||||
export interface AppShellStoreActions {
|
||||
setBackground: (background: AppShellStore['background']) => void;
|
||||
setPrevLocation: (location: ParsedLocation | null) => void;
|
||||
}
|
||||
|
||||
export const useAppShellStore = create<AppShellStore & AppShellStoreActions>()(set => ({
|
||||
background: 'gradientOverlay',
|
||||
prevLocation: null,
|
||||
|
||||
setBackground: background => set({ background }),
|
||||
setPrevLocation: location => set({ prevLocation: location }),
|
||||
}));
|
||||
|
||||
export default useAppShellStore;
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import type { ClusterModel } from '@/bindings.gen';
|
||||
import type { FileRoutesByFullPath } from '@/routeTree.gen';
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface AppStore {
|
||||
activeCluster: ClusterModel | null;
|
||||
previousPage: FileRoutesByFullPath | null;
|
||||
}
|
||||
|
||||
interface AppStoreActions {
|
||||
setInitialData: (data: AppStore) => void;
|
||||
setActiveCluster: (cluster: ClusterModel | null) => void;
|
||||
setPreviousPage: (page: FileRoutesByFullPath) => void;
|
||||
}
|
||||
|
||||
const useAppStore = create<AppStore & AppStoreActions>()(set => ({
|
||||
activeCluster: null,
|
||||
previousPage: null,
|
||||
|
||||
setInitialData: (data: AppStore) => set({
|
||||
activeCluster: data.activeCluster,
|
||||
previousPage: data.previousPage,
|
||||
}),
|
||||
setPreviousPage: (page: FileRoutesByFullPath) => set({ previousPage: page }),
|
||||
setActiveCluster: (cluster: ClusterModel | null) => set({ activeCluster: cluster }),
|
||||
}));
|
||||
|
||||
export default useAppStore;
|
||||
39
apps/oneclient/frontend/src/utils/locationMapping.ts
Normal file
39
apps/oneclient/frontend/src/utils/locationMapping.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import type { FileRouteTypes } from '@/routeTree.gen';
|
||||
|
||||
const mapping: Partial<Record<FileRouteTypes['to'], string>> = {
|
||||
'/app': 'Home',
|
||||
'/app/clusters': 'Clusters',
|
||||
};
|
||||
|
||||
export function getLocationName(
|
||||
to: FileRouteTypes['to'] | (string & {}) | undefined | null,
|
||||
): string | undefined {
|
||||
if (!to)
|
||||
return undefined;
|
||||
|
||||
if (to in mapping)
|
||||
return mapping[to as keyof typeof mapping];
|
||||
|
||||
const mostSimilarPath = getMostSimilarPathMapping(to);
|
||||
return mostSimilarPath;
|
||||
}
|
||||
|
||||
function getMostSimilarPathMapping(
|
||||
to: string,
|
||||
): FileRouteTypes['to'] | undefined {
|
||||
let path = to;
|
||||
|
||||
while (path !== '') {
|
||||
if (Object.prototype.hasOwnProperty.call(mapping, path))
|
||||
return path as FileRouteTypes['to'];
|
||||
|
||||
const lastSlash = path.lastIndexOf('/');
|
||||
|
||||
if (lastSlash === 0)
|
||||
return Object.prototype.hasOwnProperty.call(mapping, '/') ? '/' : undefined;
|
||||
|
||||
path = path.substring(0, lastSlash);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ export default defineConfig(
|
|||
'ts/no-use-before-define': 'off',
|
||||
'style/function-paren-newline': ['error', 'consistent'],
|
||||
'react/no-context-provider': 'off',
|
||||
"prefer-const": "off" // disabled due to maximum call stack size
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import type { AnyRoute } from '@tanstack/react-router';
|
||||
import type { MotionProps } from 'motion/react';
|
||||
import { Outlet, useMatch, useRouter } from '@tanstack/react-router';
|
||||
import { AnimatePresence, motion } from 'motion/react';
|
||||
import { motion } from 'motion/react';
|
||||
import { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface TransitionProps {
|
||||
|
|
@ -21,6 +21,7 @@ interface AnimatedOutletProps {
|
|||
transition?: MotionProps['transition'];
|
||||
from: AnyRoute['id'];
|
||||
clone?: boolean;
|
||||
children?: (children: React.ReactNode) => React.ReactNode;
|
||||
}
|
||||
|
||||
interface AnimatedOutletWrapperProps {
|
||||
|
|
@ -82,6 +83,7 @@ export function AnimatedOutlet({
|
|||
transition = { duration: 0.3 },
|
||||
from,
|
||||
clone = true,
|
||||
children,
|
||||
}: AnimatedOutletProps) {
|
||||
const [snapshots, setSnapshots] = useState<Array<{ node: HTMLElement; id: number }>>([]);
|
||||
const [pathname, setPathname] = useState<string | null>(null);
|
||||
|
|
@ -148,7 +150,7 @@ export function AnimatedOutlet({
|
|||
transition={transition}
|
||||
>
|
||||
<div className="w-full h-full">
|
||||
<Outlet />
|
||||
{children?.(<Outlet />) ?? <Outlet />}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue