Skip to content

Commit

Permalink
feat: JS transitions support
Browse files Browse the repository at this point in the history
feat: memoize transition router
fix: remove unused imports
  • Loading branch information
davelsan committed Jun 4, 2024
1 parent b98fb08 commit 4678b28
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 44 deletions.
57 changes: 54 additions & 3 deletions example/app/page.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
'use client'

import { Link, useRouter } from 'next-view-transitions'
import { Link, useTransitionRouter } from 'next-view-transitions'
import { useState } from "react";

export default function Page() {
const router = useRouter();
const [withCustomTransition, setWithCustomTransition] = useState(false)
const router = useTransitionRouter();

const routerNavigate = () => {
router.push('/demo');
router.push('/demo', {
onTransitionReady: withCustomTransition ? slideInOut: undefined,
});
}

return (
Expand All @@ -20,6 +24,12 @@ export default function Page() {
<p>
<a onClick={routerNavigate}>Go to /demo with router.push →</a>
</p>
<p>
<label>
<input type="checkbox" onChange={() => setWithCustomTransition((prev) => !prev)}/>{' '}
custom transition
</label>
</p>
<h2>Disclaimer</h2>
<p>
This library is aimed at basic use cases of View Transitions and Next.js
Expand Down Expand Up @@ -87,3 +97,44 @@ export default function Component() {
</div>
)
}

function slideInOut() {
document.documentElement.animate(
[
{
opacity: 1,
transform: 'translate(0, 0)',
},
{
opacity: 0,
transform: 'translate(-100%, 0)',
},
],
{
duration: 500,
easing: 'ease-in-out',
fill: 'forwards',
pseudoElement: '::view-transition-old(root)',
}
);

document.documentElement.animate(
[
{
opacity: 0,
transform: 'translate(100%, 0)',
},
{
opacity: 1,
transform: 'translate(0, 0)',
},
],
{
duration: 500,
easing: 'ease-in-out',
fill: 'forwards',
pseudoElement: '::view-transition-new(root)',
}
);
}

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

export { Link } from './link'
export { ViewTransitions } from './transition-context'
export { useRouter } from './use-router'
export { useTransitionRouter } from './use-transition-router'
8 changes: 3 additions & 5 deletions src/link.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import NextLink from 'next/link'
import { useRouter } from './use-router'
import { startTransition, useCallback } from 'react'
import { useSetFinishViewTransition } from './transition-context'
import { useTransitionRouter } from './use-transition-router'
import { useCallback } from 'react'

// copied from https://github.com/vercel/next.js/blob/66f8ffaa7a834f6591a12517618dce1fd69784f6/packages/next/src/client/link.tsx#L180-L191
function isModifiedEvent(event: React.MouseEvent): boolean {
Expand Down Expand Up @@ -38,8 +37,7 @@ function shouldPreserveDefault(
// to navigate, and trigger a view transition.

export function Link(props: React.ComponentProps<typeof NextLink>) {
const router = useRouter()
const finishViewTransition = useSetFinishViewTransition()
const router = useTransitionRouter()

const { href, as, replace, scroll } = props
const onClick = useCallback(
Expand Down
35 changes: 0 additions & 35 deletions src/use-router.ts

This file was deleted.

66 changes: 66 additions & 0 deletions src/use-transition-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useRouter as useNextRouter } from 'next/navigation'
import {startTransition, useCallback, useMemo} from "react";
import { useSetFinishViewTransition } from "./transition-context";
import {
AppRouterInstance,
NavigateOptions
} from "next/dist/shared/lib/app-router-context.shared-runtime";

export type TransitionOptions = {
onTransitionReady?: () => void;
};

type NavigateOptionsWithTransition = NavigateOptions & TransitionOptions;

export type TransitionRouter = AppRouterInstance & {
push: (href: string, options?: NavigateOptionsWithTransition) => void;
replace: (href: string, options?: NavigateOptionsWithTransition) => void;
};

export function useTransitionRouter() {
const router = useNextRouter()
const finishViewTransition = useSetFinishViewTransition()

const triggerTransition = useCallback((cb: () => void, { onTransitionReady }: TransitionOptions = {}) => {
// @ts-ignore
const transition = document.startViewTransition(
() =>
new Promise<void>((resolve) => {
startTransition(() => {
cb();
finishViewTransition(() => resolve)
})
})
)

if (onTransitionReady) {
transition.ready.then(onTransitionReady);
}
}, [])

const push = useCallback((
href: string,
{ onTransitionReady, ...options }: NavigateOptionsWithTransition = {}
) => {
triggerTransition(() => router.push(href, options), {
onTransitionReady
})
}, [triggerTransition, router])

const replace = useCallback((
href: string,
{ onTransitionReady, ...options }: NavigateOptionsWithTransition = {}
) => {
triggerTransition(() => router.replace(href, options), {
onTransitionReady
});
}, [triggerTransition, router]);

return useMemo<TransitionRouter>(
() => ({
...router,
push,
replace,
}),
[push, replace, router]);
}

0 comments on commit 4678b28

Please sign in to comment.