File tree Expand file tree Collapse file tree
packages/examples/next-app/src Expand file tree Collapse file tree Load diff This file was deleted.
Original file line number Diff line number Diff line change 1+ 'use client'
2+
3+ import { useQueryStates } from 'nuqs'
4+ import { searchParams } from '../searchParams'
5+
6+ export function Filter ( ) {
7+ const [ filter , setFilter ] = useQueryStates ( searchParams , {
8+ shallow : false
9+ } )
10+ return (
11+ < div className = "flex flex-col gap-4" >
12+ < input
13+ className = "border border-gray-300 dark:border-gray-600 rounded px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
14+ type = "text"
15+ value = { filter . search }
16+ onChange = { e => setFilter ( { search : e . target . value , page : 1 } ) }
17+ placeholder = "Search users"
18+ />
19+
20+ < div className = "flex gap-2" >
21+ < button
22+ onClick = { ( ) =>
23+ setFilter ( prev => ( {
24+ order : prev . order === 'asc' ? 'desc' : 'asc' ,
25+ page : 1
26+ } ) )
27+ }
28+ className = "rounded px-4 flex-1 py-2 text-sm border border-gray-300 dark:border-gray-600 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 transition-colors"
29+ >
30+ Order:{ ' ' }
31+ { filter . order === 'asc'
32+ ? 'Ascending'
33+ : filter . order === 'desc'
34+ ? 'Descending'
35+ : 'None' }
36+ </ button >
37+
38+ < button
39+ onClick = { ( ) => setFilter ( null ) }
40+ className = "rounded px-4 py-2 text-sm border border-red-400 bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-800 dark:text-red-100 dark:hover:bg-red-700 transition-colors"
41+ >
42+ Reset
43+ </ button >
44+ </ div >
45+ </ div >
46+ )
47+ }
Original file line number Diff line number Diff line change 1+ 'use client'
2+ import Link from 'next/link'
3+ import { useSearchParams } from 'next/navigation'
4+ import { useQueryStates } from 'nuqs'
5+ import { searchParams , serialize } from '../searchParams'
6+
7+ export function Pagination ( { total } : { total : number } ) {
8+ const [ { page, limit } ] = useQueryStates ( searchParams )
9+ const totalPages = Math . ceil ( total / limit )
10+ const currentSearchParams = useSearchParams ( )
11+ return (
12+ < nav className = "mt-2 flex items-center gap-2 mx-auto" >
13+ { Array . from ( { length : totalPages } , ( _ , i ) => {
14+ const p = i + 1
15+ const isActive = p === page
16+ return (
17+ < Link
18+ key = { p }
19+ href = { serialize ( currentSearchParams , { page : p } ) }
20+ className = { `rounded border px-3 py-1 text-sm transition-colors ${
21+ isActive
22+ ? 'bg-gray-800 text-white dark:bg-gray-200 dark:text-black'
23+ : 'hover:bg-gray-100 dark:hover:bg-gray-800'
24+ } `}
25+ >
26+ { p }
27+ </ Link >
28+ )
29+ } ) }
30+ </ nav >
31+ )
32+ }
Load diff This file was deleted.
Original file line number Diff line number Diff line change 1+ import { getUsers } from '@/data'
2+ import { searchParamsCache } from '../searchParams'
3+ import { Pagination } from './pagination'
4+
5+ export async function UsersList ( ) {
6+ const params = searchParamsCache . all ( )
7+ const users = await getUsers ( params )
8+
9+ return (
10+ < div className = "w-full flex flex-col gap-4" >
11+ < ul className = "w-full max-w-sm divide-y divide-gray-200 dark:divide-gray-700 text-center sm:text-left" >
12+ { users . data . map ( user => (
13+ < li key = { user . id } className = "py-2" >
14+ { user . name }
15+ </ li >
16+ ) ) }
17+ </ ul >
18+ < Pagination total = { users . total } />
19+ </ div >
20+ )
21+ }
Original file line number Diff line number Diff line change 11import type { SearchParams } from 'next/dist/server/request/search-params'
22import Image from 'next/image'
33import { Suspense } from 'react'
4- import { Client } from './_components/client'
5- import { Server } from './_components/server'
4+ import { Filter } from './_components/filter'
5+ import { UsersList } from './_components/users-list'
6+ import { searchParamsCache } from './searchParams'
67
78type PageProps = {
89 searchParams : Promise < SearchParams >
910}
1011
1112export default async function Home ( { searchParams } : PageProps ) {
13+ await searchParamsCache . parse ( searchParams )
1214 return (
1315 < div className = "grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]" >
1416 < main className = "flex flex-col gap-[32px] row-start-2 items-center sm:items-start" >
@@ -21,10 +23,10 @@ export default async function Home({ searchParams }: PageProps) {
2123 priority
2224 />
2325 < Suspense >
24- < Server searchParams = { searchParams } />
26+ < Filter />
2527 </ Suspense >
2628 < Suspense >
27- < Client />
29+ < UsersList />
2830 </ Suspense >
2931 < a
3032 className = "w-full rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
Original file line number Diff line number Diff line change 1- import { createLoader , parseAsInteger , parseAsString } from 'nuqs/server'
1+ import {
2+ createSearchParamsCache ,
3+ createSerializer ,
4+ parseAsInteger ,
5+ parseAsString ,
6+ parseAsStringEnum
7+ } from 'nuqs/server'
28
39export const searchParams = {
4- name : parseAsString . withDefault ( '' ) ,
5- count : parseAsInteger . withDefault ( 0 )
10+ page : parseAsInteger . withDefault ( 1 ) ,
11+ limit : parseAsInteger . withDefault ( 5 ) ,
12+ order : parseAsStringEnum ( [ 'asc' , 'desc' ] ) ,
13+ search : parseAsString . withDefault ( '' )
614}
715
8- export const loadSearchParams = createLoader ( searchParams )
16+ export const searchParamsCache = createSearchParamsCache ( searchParams )
17+
18+ export const serialize = createSerializer ( searchParams )
Original file line number Diff line number Diff line change 1+ export const users = [
2+ {
3+ id : 1 ,
4+ name : 'John Doe'
5+ } ,
6+ {
7+ id : 2 ,
8+ name : 'Jane Doe'
9+ } ,
10+ {
11+ id : 3 ,
12+ name : 'Ziad El-Sayed'
13+ } ,
14+ {
15+ id : 4 ,
16+ name : 'Alice Johnson'
17+ } ,
18+ {
19+ id : 5 ,
20+ name : 'Bob Brown'
21+ } ,
22+ {
23+ id : 6 ,
24+ name : 'Charlie Davis'
25+ } ,
26+ {
27+ id : 7 ,
28+ name : 'Diana Evans'
29+ } ,
30+ {
31+ id : 8 ,
32+ name : 'Ethan Harris'
33+ } ,
34+ {
35+ id : 9 ,
36+ name : 'Fiona Clark'
37+ } ,
38+ {
39+ id : 10 ,
40+ name : 'George Miller'
41+ } ,
42+ {
43+ id : 11 ,
44+ name : 'Hannah Wilson'
45+ } ,
46+ {
47+ id : 12 ,
48+ name : 'Ian Taylor'
49+ } ,
50+ {
51+ id : 13 ,
52+ name : 'Karen Anderson'
53+ } ,
54+ {
55+ id : 14 ,
56+ name : 'Liam Thomas'
57+ } ,
58+ {
59+ id : 15 ,
60+ name : 'Mia Rodriguez'
61+ } ,
62+ {
63+ id : 16 ,
64+ name : 'Noah Martinez'
65+ } ,
66+ {
67+ id : 17 ,
68+ name : 'Olivia Lee'
69+ } ,
70+ {
71+ id : 18 ,
72+ name : 'Peter Walker'
73+ } ,
74+ {
75+ id : 19 ,
76+ name : 'Quinn Young'
77+ } ,
78+ {
79+ id : 20 ,
80+ name : 'Riley King'
81+ }
82+ ]
83+
84+ export interface GetUsersOptions {
85+ page : number
86+ limit : number
87+ order : 'asc' | 'desc' | null
88+ search : string
89+ }
90+
91+ export async function getUsers ( {
92+ page,
93+ limit,
94+ order,
95+ search
96+ } : GetUsersOptions ) {
97+ let records = users
98+
99+ if ( search . trim ( ) ) {
100+ const term = search . toLowerCase ( )
101+ records = records . filter ( item => item . name . toLowerCase ( ) . includes ( term ) )
102+ }
103+
104+ if ( order ) {
105+ records = [ ...records ] . sort ( ( a , b ) => {
106+ const valueA = a . name
107+ const valueB = b . name
108+ if ( valueA < valueB ) return order === 'asc' ? - 1 : 1
109+ if ( valueA > valueB ) return order === 'asc' ? 1 : - 1
110+ return 0
111+ } )
112+ }
113+
114+ const total = records . length
115+ const start = ( page - 1 ) * limit
116+ const paginated = records . slice ( start , start + limit )
117+
118+ return {
119+ total,
120+ data : paginated
121+ }
122+ }
You can’t perform that action at this time.
0 commit comments