diff --git a/.changeset/ninety-jeans-clean.md b/.changeset/ninety-jeans-clean.md new file mode 100644 index 0000000..53b59ba --- /dev/null +++ b/.changeset/ninety-jeans-clean.md @@ -0,0 +1,18 @@ +--- +'@tuyau/inertia': patch +--- + +Add `useRouter` hook/composable that expose a typesafe `visit` method to manually visit a route. + +```tsx +import { useRouter } from '@tuyau/inertia/vue' + +const router = useRouter() + +router.visit({ + name: 'users.posts.show', + params: { id: 1, postId: 2 } +}, { preserveState: true }) +``` + +Also available for React. diff --git a/docs/content/docs/inertia.md b/docs/content/docs/inertia.md index 96560bc..a1e3291 100644 --- a/docs/content/docs/inertia.md +++ b/docs/content/docs/inertia.md @@ -48,6 +48,19 @@ import { Link } from '@tuyau/inertia/react' Go to post ``` +### Manual visits + +The `useRouter` hook is useful to manually visit a route in your application. It returns an object with a `visit` method that you can use to visit a route. + +```tsx +import { useRouter } from '@tuyau/inertia/react' + +const router = useRouter() + +router.visit('users.posts.show', { id: 1, postId: 2 }) +``` + + ## Vue usage To use the Inertia helpers in your Vue x Inertia project, you must install the Tuyau plugin : @@ -85,3 +98,20 @@ import { Link } from '@tuyau/inertia/vue' Go to post ``` + +### Manual visits + +The `useRouter` hook is useful to manually visit a route in your application. It returns an object with a `visit` method that you can use to visit a route. + + +```vue + + + + Go to post + +``` \ No newline at end of file diff --git a/packages/inertia/src/react/index.tsx b/packages/inertia/src/react/index.tsx index cbbef05..995265e 100644 --- a/packages/inertia/src/react/index.tsx +++ b/packages/inertia/src/react/index.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { Link as InertiaLink } from '@inertiajs/react' import type { TuyauClient, RouteName, GeneratedRoutes } from '@tuyau/client' +import { Link as InertiaLink, router as InertiaRouter } from '@inertiajs/react' import type { ValidatedApi, LinkParams } from '../types.js' @@ -50,3 +50,25 @@ export const Link: >( }, // @ts-expect-error TODO: fix this ) => ReturnType = React.forwardRef(LinkInner) as any + +export function useRouter() { + const tuyau = useTuyau() + if (!tuyau) throw new Error('You must wrap your app in a TuyauProvider') + + const router = { + visit: >( + props: LinkProps, + options?: Parameters[1], + ) => { + const route = tuyau.$route(props.route, (props as any).params) + const url = tuyau.$url(props.route, { params: props.params } as any) + + return InertiaRouter.visit(url, { + ...options, + method: route.method[0], + }) + }, + } + + return router +} diff --git a/packages/inertia/src/vue/index.tsx b/packages/inertia/src/vue/index.tsx index 1af3f10..6b697d9 100644 --- a/packages/inertia/src/vue/index.tsx +++ b/packages/inertia/src/vue/index.tsx @@ -1,8 +1,8 @@ import { defineComponent, h, inject } from 'vue' import type { DefineSetupFnComponent } from 'vue' -import { Link as InertiaLink } from '@inertiajs/vue3' import type { InertiaLinkProps } from '@inertiajs/vue3' import type { RouteName, TuyauClient } from '@tuyau/client' +import { Link as InertiaLink, router as InertiaRouter } from '@inertiajs/vue3' import type { ValidatedApi, LinkParams } from '../types.js' @@ -54,3 +54,25 @@ export const Link: >( }, { props: ['route', 'params'] }, ) + +export function useRouter() { + const tuyau = inject | null>(getClientKey()) + if (!tuyau) throw new Error('You must install the TuyauPlugin before using useRouter') + + const router = { + visit: >( + props: LinkParams & Omit, + options?: Parameters[1], + ) => { + const route = tuyau.$route(props.route, (props as any).params) + const url = tuyau.$url(props.route, { params: props.params } as any) + + return InertiaRouter.visit(url, { + ...options, + method: route.method[0], + }) + }, + } + + return router +} diff --git a/packages/inertia/tests/react.spec.ts b/packages/inertia/tests/react.spec.ts index c1af97d..5897d32 100644 --- a/packages/inertia/tests/react.spec.ts +++ b/packages/inertia/tests/react.spec.ts @@ -2,7 +2,7 @@ import { test } from '@japa/runner' import { createTuyau } from '@tuyau/client' import type { ApiDefinition } from '@tuyau/client' -import { Link, TuyauProvider } from '../src/react/index.js' +import { Link, TuyauProvider, useRouter } from '../src/react/index.js' const routes = [ { @@ -68,4 +68,17 @@ test.group('React | Typings', () => { const client = createTuyau({ api, baseUrl: 'http://localhost' }) TuyauProvider({ client, children: null }) }) + + test('useRouter typing', () => { + const router = useRouter() + + router.visit({ route: 'users.index' }) + router.visit({ route: 'users.comments.edit', params: ['1', '2'] }) + + // @ts-expect-error inexistent route + router.visit({ route: 'foo' }) + + // @ts-expect-error missing params + router.visit({ route: 'users.comments.edit' }) + }).fails() }) diff --git a/packages/inertia/tests/vue.spec.ts b/packages/inertia/tests/vue.spec.ts index a69668f..ec01f7e 100644 --- a/packages/inertia/tests/vue.spec.ts +++ b/packages/inertia/tests/vue.spec.ts @@ -2,7 +2,7 @@ import { test } from '@japa/runner' import { createTuyau } from '@tuyau/client' import type { ApiDefinition } from '@tuyau/client' -import { Link, TuyauPlugin } from '../src/vue/index.js' +import { useRouter, Link, TuyauPlugin } from '../src/vue/index.js' const routes = [ { @@ -70,4 +70,18 @@ test.group('Vue | Typings', () => { client: createTuyau({ baseUrl: 'http://localhost', api }), }) }).fails() + + test('useRouter typing', () => { + const router = useRouter() + + router.visit({ route: 'users.index' }) + router.visit({ route: 'users.comments.edit', params: ['1', '2'] }) + router.visit({ route: 'users.comments.edit', params: ['1', '2'] }, { preserveScroll: true }) + + // @ts-expect-error inexistent route + router.visit({ route: 'foo' }) + + // @ts-expect-error missing params + router.visit({ route: 'users.comments.edit' }) + }).fails() }) diff --git a/playgrounds/inertia-react/inertia/pages/posts/create.tsx b/playgrounds/inertia-react/inertia/pages/posts/create.tsx index fe46cd5..ced4a0c 100644 --- a/playgrounds/inertia-react/inertia/pages/posts/create.tsx +++ b/playgrounds/inertia-react/inertia/pages/posts/create.tsx @@ -1,12 +1,22 @@ import { Head } from '@inertiajs/react' -import { Link } from '@tuyau/inertia/react' +import { Link, useRouter } from '@tuyau/inertia/react' export default function Home() { + const router = useRouter() + return ( <> - + + router.visit({ + route: 'home', + }) + } + > + Go to home + Create post diff --git a/playgrounds/inertia-vue/inertia/pages/posts/create.vue b/playgrounds/inertia-vue/inertia/pages/posts/create.vue index f2dfb69..ca6b9ed 100644 --- a/playgrounds/inertia-vue/inertia/pages/posts/create.vue +++ b/playgrounds/inertia-vue/inertia/pages/posts/create.vue @@ -1,17 +1,28 @@ + AdonisJS {{ version }} x Inertia x Vue.js - Go to Home + Go to home Learn more about AdonisJS and Inertia.js by visiting the AdonisJS documentation.
Create post