Skip to content

Commit 54c6f73

Browse files
committed
Added navigation rsc vite example
Copied over from vitejs/vite-plugin-react#567
1 parent 5c6ea2c commit 54c6f73

18 files changed

+2484
-0
lines changed

NavigationReact/sample/rsc-vite/package-lock.json

Lines changed: 1777 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "rsc-vite",
3+
"description": "",
4+
"version": "1.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"@vitejs/plugin-rsc": "latest",
13+
"navigation": "^6.3.0",
14+
"navigation-react": "^4.12.0",
15+
"react": "^19.1.0",
16+
"react-dom": "^19.1.0"
17+
},
18+
"devDependencies": {
19+
"@types/react": "^19.1.0",
20+
"@types/react-dom": "^19.1.0",
21+
"@vitejs/plugin-react": "latest",
22+
"vite": "^7.0.2"
23+
}
24+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { SceneView } from 'navigation-react'
2+
import NavigationProvider from './NavigationProvider'
3+
import HmrProvider from './HmrProvider'
4+
import People from './People'
5+
import Person from './Person'
6+
7+
const App = async ({ url }: any) => {
8+
return (
9+
<html>
10+
<head>
11+
<title>Navigation React</title>
12+
</head>
13+
<body>
14+
<NavigationProvider url={url}>
15+
<HmrProvider>
16+
<SceneView active="people">
17+
<People />
18+
</SceneView>
19+
<SceneView active="person" refetch={['id']}>
20+
<Person />
21+
</SceneView>
22+
</HmrProvider>
23+
</NavigationProvider>
24+
</body>
25+
</html>
26+
)
27+
}
28+
29+
export default App;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use client'
2+
import { startTransition, useOptimistic } from 'react'
3+
import { RefreshLink, useNavigationEvent } from 'navigation-react'
4+
5+
const Filter = () => {
6+
const { data, stateNavigator } = useNavigationEvent()
7+
const { name } = data
8+
const [optimisticName, setOptimisticName] = useOptimistic(
9+
name || '',
10+
(_, newName) => newName,
11+
)
12+
return (
13+
<div>
14+
<div>
15+
<label htmlFor="name">Name</label>
16+
<input
17+
id="name"
18+
value={optimisticName}
19+
onChange={({ target: { value } }) => {
20+
startTransition(() => {
21+
setOptimisticName(value)
22+
stateNavigator.refresh({ ...data, name: value, page: null })
23+
})
24+
}}
25+
/>
26+
</div>
27+
Page size
28+
<RefreshLink navigationData={{ size: 5, page: null }} includeCurrentData>
29+
5
30+
</RefreshLink>
31+
<RefreshLink navigationData={{ size: 10, page: null }} includeCurrentData>
32+
10
33+
</RefreshLink>
34+
</div>
35+
)
36+
}
37+
38+
export default Filter
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { RefreshLink, useNavigationEvent } from 'navigation-react'
2+
import { getFriends } from './data'
3+
import Gender from './Gender'
4+
5+
const Friends = async () => {
6+
const {
7+
data: { show, id, gender },
8+
} = useNavigationEvent()
9+
const friends = show ? await getFriends(id, gender) : null
10+
return (
11+
<>
12+
<RefreshLink
13+
navigationData={{ show: !show }}
14+
includeCurrentData
15+
>{`${!show ? 'Show' : 'Hide'} Friends`}</RefreshLink>
16+
{show && (
17+
<>
18+
<Gender />
19+
<ul>
20+
{friends?.map(({ id, name }) => (
21+
<li key={id}>
22+
<RefreshLink navigationData={{ id }} includeCurrentData>
23+
{name}
24+
</RefreshLink>
25+
</li>
26+
))}
27+
</ul>
28+
</>
29+
)}
30+
</>
31+
)
32+
}
33+
34+
export default Friends
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use client'
2+
import { startTransition } from 'react'
3+
import { useNavigationEvent } from 'navigation-react'
4+
import { useOptimistic } from 'react'
5+
6+
const Gender = () => {
7+
const { data, stateNavigator } = useNavigationEvent()
8+
const { gender } = data
9+
const [optimisticGender, setOptimisticGender] = useOptimistic(
10+
gender || '',
11+
(_, newGender) => newGender,
12+
)
13+
return (
14+
<div>
15+
<label htmlFor="gender">Gender</label>
16+
<select
17+
id="gender"
18+
value={optimisticGender}
19+
onChange={({ target: { value } }) => {
20+
startTransition(() => {
21+
setOptimisticGender(value)
22+
stateNavigator.refresh({ ...data, gender: value })
23+
})
24+
}}
25+
>
26+
<option value=""></option>
27+
<option value="male">Male</option>
28+
<option value="female">Female</option>
29+
<option value="other">Other</option>
30+
</select>
31+
</div>
32+
)
33+
}
34+
35+
export default Gender
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use client'
2+
import { useContext, useEffect } from 'react'
3+
import { BundlerContext, useNavigationEvent } from 'navigation-react'
4+
5+
const HmrProvider = ({ children }: any) => {
6+
const { setRoot, deserialize } = useContext(BundlerContext)
7+
const { stateNavigator } = useNavigationEvent()
8+
useEffect(() => {
9+
const onHmrReload = () => {
10+
const {
11+
stateContext: {
12+
state,
13+
data,
14+
crumbs,
15+
nextCrumb: { crumblessUrl },
16+
},
17+
} = stateNavigator
18+
const root = deserialize(
19+
stateNavigator.historyManager.getHref(crumblessUrl),
20+
{
21+
method: 'put',
22+
headers: { 'Content-Type': 'application/json' },
23+
body: {
24+
crumbs: crumbs.map(({ state, data }) => ({
25+
state: state.key,
26+
data,
27+
})),
28+
state: state.key,
29+
data,
30+
},
31+
},
32+
)
33+
stateNavigator.historyManager.stop()
34+
setRoot(root)
35+
}
36+
import.meta.hot?.on("rsc:update", onHmrReload);
37+
return () => import.meta.hot?.off("rsc:update", onHmrReload);
38+
})
39+
return children
40+
}
41+
42+
export default HmrProvider
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client'
2+
import { useMemo } from 'react'
3+
import { StateNavigator, HTML5HistoryManager } from 'navigation'
4+
import { NavigationHandler } from 'navigation-react'
5+
import stateNavigator from './stateNavigator'
6+
7+
const historyManager = new HTML5HistoryManager()
8+
9+
const NavigationProvider = ({ url, children }: any) => {
10+
const clientNavigator = useMemo(() => {
11+
historyManager.stop()
12+
const clientNavigator = new StateNavigator(stateNavigator, historyManager)
13+
clientNavigator.navigateLink(url)
14+
return clientNavigator
15+
}, [])
16+
return (
17+
<NavigationHandler stateNavigator={clientNavigator}>
18+
{children}
19+
</NavigationHandler>
20+
)
21+
}
22+
23+
export default NavigationProvider
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { RefreshLink, useNavigationEvent } from 'navigation-react'
2+
3+
const Pager = ({ totalRowCount }: { totalRowCount: number }) => {
4+
const {
5+
data: { page, size },
6+
} = useNavigationEvent()
7+
const lastPage = Math.ceil(totalRowCount / size)
8+
return (
9+
<div>
10+
<ul>
11+
{totalRowCount ? (
12+
<>
13+
<li>
14+
<RefreshLink
15+
navigationData={{ page: 1 }}
16+
includeCurrentData
17+
disableActive
18+
>
19+
First
20+
</RefreshLink>
21+
</li>
22+
<li>
23+
<RefreshLink
24+
navigationData={{ page: Math.max(page - 1, 1) }}
25+
includeCurrentData
26+
disableActive
27+
>
28+
Previous
29+
</RefreshLink>
30+
</li>
31+
<li>
32+
<RefreshLink
33+
navigationData={{ page: Math.min(lastPage, page + 1) }}
34+
includeCurrentData
35+
disableActive
36+
>
37+
Next
38+
</RefreshLink>
39+
</li>
40+
<li>
41+
<RefreshLink
42+
navigationData={{ page: lastPage }}
43+
includeCurrentData
44+
disableActive
45+
>
46+
Last
47+
</RefreshLink>
48+
</li>
49+
</>
50+
) : (
51+
<>
52+
<li>First</li>
53+
<li>Previous</li>
54+
<li>Next</li>
55+
<li>Last</li>
56+
</>
57+
)}
58+
</ul>
59+
Total Count {totalRowCount}
60+
</div>
61+
)
62+
}
63+
64+
export default Pager
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { searchPeople } from './data'
2+
import {
3+
NavigationLink,
4+
RefreshLink,
5+
useNavigationEvent,
6+
} from 'navigation-react'
7+
import Filter from './Filter'
8+
import Pager from './Pager'
9+
10+
const People = async () => {
11+
const {
12+
data: { name, page, size, sort },
13+
} = useNavigationEvent()
14+
const { people, count } = await searchPeople(name, page, size, sort)
15+
return (
16+
<>
17+
<h1>People</h1>
18+
<Filter />
19+
<table>
20+
<thead>
21+
<tr>
22+
<th>
23+
<RefreshLink
24+
navigationData={{ sort: sort === 'asc' ? 'desc' : 'asc' }}
25+
includeCurrentData
26+
>
27+
Name
28+
</RefreshLink>
29+
</th>
30+
<th>Date of Birth</th>
31+
</tr>
32+
</thead>
33+
<tbody>
34+
{people.map(({ id, name, dateOfBirth }) => (
35+
<tr key={id}>
36+
<td>
37+
<NavigationLink stateKey="person" navigationData={{ id: id }}>
38+
{name}
39+
</NavigationLink>
40+
</td>
41+
<td>{dateOfBirth}</td>
42+
</tr>
43+
))}
44+
</tbody>
45+
</table>
46+
<Pager totalRowCount={count} />
47+
</>
48+
)
49+
}
50+
51+
export default People

0 commit comments

Comments
 (0)