Skip to content

Commit

Permalink
feat: layout routes
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Oct 20, 2022
1 parent 9e2a646 commit 2396438
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 194 deletions.
54 changes: 48 additions & 6 deletions examples/react/kitchen-sink/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ const routeConfig = createRouteConfig().addChildren((createRoute) => [
element: <Authenticated />,
}),
]),
createRoute({
id: 'layout',
element: <LayoutWrapper />,
}).addChildren((createRoute) => [
createRoute({
path: 'layout-a',
element: <LayoutA />,
}),
createRoute({
path: 'layout-b',
element: <LayoutB />,
}),
]),
])

const router = createReactRouter({
Expand Down Expand Up @@ -297,18 +310,22 @@ function Root() {
['.', 'Home'],
['/dashboard', 'Dashboard'],
['/expensive', 'Expensive'],
['/authenticated', 'Authenticated'], // This route has a trailing slash on purpose.
['/authenticated', 'Authenticated'],
['/layout-a', 'Layout A'],
['/layout-b', 'Layout B'],
] as const
).map(([to, label]) => {
return (
<div key={to}>
<route.Link
to={to}
activeOptions={{
// If the route points to the root of it's parent,
// make sure it's only active if it's exact
exact: to === '.',
}}
activeOptions={
{
// If the route points to the root of it's parent,
// make sure it's only active if it's exact
// exact: to === '.',
}
}
className={`block py-2 px-3 text-blue-700`}
// Make "active" links bold
activeProps={{ className: `font-bold` }}
Expand Down Expand Up @@ -867,6 +884,31 @@ function Authenticated() {
)
}

function LayoutWrapper() {
return (
<div>
<div>Layout</div>
<hr />
<Outlet />
</div>
)
}

function LayoutA() {
return (
<div>
<div>Layout A</div>
</div>
)
}
function LayoutB() {
return (
<div>
<div>Layout B</div>
</div>
)
}

function Spinner() {
return <div className="inline-block animate-spin px-3"></div>
}
Expand Down
63 changes: 30 additions & 33 deletions packages/react-router/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
AnyRoute,
RootRouteId,
rootRouteId,
Route,
Router,
} from '@tanstack/router-core'
import {
Expand All @@ -29,6 +28,7 @@ import {
ResolveRelativePath,
NoInfer,
ToOptions,
invariant,
} from '@tanstack/router-core'

export * from '@tanstack/router-core'
Expand Down Expand Up @@ -323,48 +323,45 @@ export function createReactRouter<
useRoute: (routeId) => {
const route = router.getRoute(routeId)
useRouterSubscription(router)
if (!route) {
throw new Error(
`Could not find a route for route "${
routeId as string
}"! Did you forget to add it to your route config?`,
)
}
invariant(
route,
`Could not find a route for route "${
routeId as string
}"! Did you forget to add it to your route config?`,
)
return route
},
useMatch: (routeId) => {
if (routeId === rootRouteId) {
throw new Error(
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
)
}
invariant(
routeId !== rootRouteId,
`"${rootRouteId}" cannot be used with useMatch! Did you mean to useRoute("${rootRouteId}")?`,
)

const runtimeMatch = useMatch()
const match = router.state.matches.find((d) => d.routeId === routeId)

if (!match) {
throw new Error(
`Could not find a match for route "${
routeId as string
}" being rendered in this component!`,
)
}

if (runtimeMatch.routeId !== match?.routeId) {
throw new Error(
`useMatch('${
match?.routeId as string
}') is being called in a component that is meant to render the '${
runtimeMatch.routeId
}' route. Did you mean to 'useRoute(${
match?.routeId as string
})' instead?`,
)
}
invariant(
match,
`Could not find a match for route "${
routeId as string
}" being rendered in this component!`,
)

invariant(
runtimeMatch.routeId == match?.routeId,
`useMatch('${
match?.routeId as string
}') is being called in a component that is meant to render the '${
runtimeMatch.routeId
}' route. Did you mean to 'useRoute(${
match?.routeId as string
})' instead?`,
)

useRouterSubscription(router)

if (!match) {
throw new Error('Match not found!')
invariant('Match not found!')
}

return match
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Route } from './'
import { Route } from '../src'
import { z } from 'zod'
import {
createRouter,
AllRouteInfo,
createRouteConfig,
RelativeToPathAutoComplete,
} from '.'
} from '../src'

// Write a test
describe('everything', () => {
Expand Down Expand Up @@ -129,6 +129,25 @@ describe('everything', () => {
path: '/',
}),
]),
createRoute({
id: 'layout',
element: 'layout-wrapper',
validateSearch: (search) =>
z
.object({
isLayout: z.boolean(),
})
.parse(search),
}).addChildren((createRoute) => [
createRoute({
path: 'layout-a',
element: 'layout-a',
}),
createRoute({
path: 'layout-b',
element: 'layout-b',
}),
]),
])

type MyRoutesInfo = AllRouteInfo<typeof routeConfig>
Expand Down Expand Up @@ -296,6 +315,12 @@ describe('everything', () => {
to: '/dashboard',
})

// @ts-expect-error
router.buildLink({
from: '/',
to: '/does-not-exist',
})

router.getRoute('/').buildLink({
to: '/dashboard/invoices/:invoiceId',
params: {
Expand All @@ -310,19 +335,19 @@ describe('everything', () => {
}),
})

type test = RelativeToPathAutoComplete<
// ^?
MyRoutesInfo['fullPath'],
'/dashboard/invoices',
'..'
>

router.getRoute('/dashboard/invoices/:invoiceId').buildLink({
to: '../test',
search: {
version: 2,
isGood: true,
},
})

router.getRoute('/').buildLink({
to: '/layout-a',
search: (current) => ({
isLayout: !!current.version,
}),
})
})
})
1 change: 1 addition & 0 deletions packages/router-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"dependencies": {
"@babel/runtime": "^7.16.7",
"history": "^5.2.0",
"tiny-invariant": "^1.3.1",
"ts-toolbelt": "^9.6.0"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 2396438

Please sign in to comment.