Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions packages/react/test-app/Pages/InfiniteScroll/Filtering.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { InfiniteScroll, Link, useForm } from '@inertiajs/react'
import { debounce } from 'lodash-es'
import { useCallback, useEffect } from 'react'
import UserCard, { User } from './UserCard'

interface Props {
users: { data: User[] }
filter?: string
search?: string
}

export default ({ users, filter, search }: Props) => {
const { data, setData, get } = useForm({
filter: undefined,
page: undefined,
search: search,
})

const debouncedSearch = useCallback(
debounce(() => {
get('', { replace: true })
}, 250),
[get],
)

useEffect(() => {
if (data.search !== search) {
debouncedSearch()
}
}, [data.search, search, debouncedSearch])

const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setData('search', e.target.value)
}

return (
<div>
<div style={{ marginBottom: '20px', display: 'flex', gap: '10px' }}>
<Link href="">No Filter</Link>
<Link href="?filter=a-m">A-M</Link>
<Link href="?filter=n-z">N-Z</Link>
<div>Current filter: {filter || 'none'}</div>
<div>Current search: {search || 'none'}</div>
<input value={data.search || ''} onChange={handleSearchChange} placeholder="Search..." />
</div>

<InfiniteScroll
data="users"
style={{ display: 'grid', gap: '20px' }}
loading={() => <div style={{ textAlign: 'center', padding: '20px' }}>Loading...</div>}
>
{users.data.map((user) => (
<UserCard key={user.id} user={user} />
))}
</InfiniteScroll>

<div style={{ marginTop: '20px', display: 'flex', gap: '10px' }}>
<Link href="">No Filter</Link>
<Link href="?filter=a-m">A-M</Link>
<Link href="?filter=n-z">N-Z</Link>
<div>Current filter: {filter || 'none'}</div>
<div>Current search: {search || 'none'}</div>
<input value={data.search || ''} onChange={handleSearchChange} placeholder="Search..." />
</div>
</div>
)
}
64 changes: 64 additions & 0 deletions packages/svelte/test-app/Pages/InfiniteScroll/Filtering.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script lang="ts">
import { InfiniteScroll, Link, useForm } from '@inertiajs/svelte'
import { onDestroy } from 'svelte'
import UserCard, { type User } from './UserCard.svelte'

export let users: { data: User[] }
export let filter: string | undefined = undefined
export let search: string | undefined = undefined

const form = useForm({
filter: undefined,
page: undefined,
search: search,
})

let timeoutId: ReturnType<typeof setTimeout> | undefined

onDestroy(() => {
if (timeoutId) {
clearTimeout(timeoutId)
}
})

// Debounced search using manual setTimeout/clearTimeout
$: if ($form.search !== search && $form.search !== undefined) {
// Clear previous timeout
if (timeoutId) {
clearTimeout(timeoutId)
}

// Set new timeout for debounced search
timeoutId = setTimeout(() => {
$form.get('', { replace: true })
}, 250)
}
</script>

<div>
<div style="margin-bottom: 20px; display: flex; gap: 10px">
<Link href="">No Filter</Link>
<Link href="?filter=a-m">A-M</Link>
<Link href="?filter=n-z">N-Z</Link>
<div>Current filter: {filter || 'none'}</div>
<div>Current search: {search || 'none'}</div>
<input bind:value={$form.search} placeholder="Search..." />
</div>

<InfiniteScroll data="users" style="display: grid; gap: 20px">
<div slot="loading" style="text-align: center; padding: 20px">Loading...</div>

{#each users.data as user (user.id)}
<UserCard {user} />
{/each}
</InfiniteScroll>

<div style="margin-top: 20px; display: flex; gap: 10px">
<Link href="">No Filter</Link>
<Link href="?filter=a-m">A-M</Link>
<Link href="?filter=n-z">N-Z</Link>
<div>Current filter: {filter || 'none'}</div>
<div>Current search: {search || 'none'}</div>
<input bind:value={$form.search} placeholder="Search..." />
</div>
</div>
53 changes: 53 additions & 0 deletions packages/vue3/test-app/Pages/InfiniteScroll/Filtering.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { InfiniteScroll, Link, useForm } from '@inertiajs/vue3'
import { debounce } from 'lodash-es'
import { watch } from 'vue'
import { User, default as UserCard } from './UserCard.vue'

const props = defineProps<{
users: { data: User[] }
filter?: string
search?: string
}>()

const form = useForm({
filter: undefined,
page: undefined,
search: props.search,
})

watch(
() => form.search,
debounce(() => form.get('', { replace: true }), 250),
)
</script>

<template>
<div>
<div style="margin-bottom: 20px; display: flex; gap: 10px">
<Link href=""> No Filter </Link>
<Link href="?filter=a-m"> A-M </Link>
<Link href="?filter=n-z"> N-Z </Link>
<div>Current filter: {{ filter || 'none' }}</div>
<div>Current search: {{ search || 'none' }}</div>
<input v-model="form.search" placeholder="Search..." />
</div>

<InfiniteScroll data="users" style="display: grid; gap: 20px">
<UserCard v-for="user in users.data" :key="user.id" :user="user" />

<template #loading>
<div style="text-align: center; padding: 20px">Loading...</div>
</template>
</InfiniteScroll>

<div style="margin-top: 20px; display: flex; gap: 10px">
<Link href=""> No Filter </Link>
<Link href="?filter=a-m"> A-M </Link>
<Link href="?filter=n-z"> N-Z </Link>
<div>Current filter: {{ filter || 'none' }}</div>
<div>Current search: {{ search || 'none' }}</div>
<input v-model="form.search" placeholder="Search..." />
</div>
</div>
</template>
139 changes: 96 additions & 43 deletions tests/app/eloquent.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,102 @@ function makeUser(id) {
}
}

export function getUserNames() {
return [
'Adelle Crona DVM',
'Alison Walter PhD',
'Aliza Langosh II',
'Amara DuBuque',
'Amaya Lang',
'Angelica Rodriguez',
'Anjali Windler',
'Ansley Gusikowski',
'Asha Welch II',
'Bailee Zulauf',
'Barrett Heathcote',
'Beryl Morar',
'Bethany Grant',
'Braden Mayert II',
'Breana Herzog',
'Camylle Metz Sr.',
'Carmen Kerluke',
'Casimer McClure',
'Cecil Walsh',
'Chandler McKenzie',
'Chaya Rempel',
'Chelsey Mertz Jr.',
'Coralie Auer',
'Daniella Hoppe',
'Daphnee Douglas',
'Davonte Heathcote',
'Dejah Parisian',
'Demarco Medhurst',
'Dewayne Rau',
'Diamond Gibson PhD',
'Diamond Herzog',
'Dino Predovic I',
'Domingo Luettgen',
'Dora Runolfsdottir',
'Dr. Billy Larkin',
'Dr. Chase Green',
'Dr. Curtis Lehner',
'Einar Crona MD',
'Eloisa Pollich',
'Elsie Goldner',
'Emma Little Sr.',
'Erika Ziemann DDS',
'Ethan Beatty',
'Euna Boehm',
'Euna Kerluke',
'Felton Yost',
'Genesis Hand',
'Hailie Quitzon',
'Helga Waelchi',
'Ibrahim Jakubowski',
'Jack Halvorson',
'Jasmin Stoltenberg',
'Jennie Olson PhD',
'Jimmy Gusikowski',
'Joy Schimmel',
'Kamron Bechtelar DDS',
'Katarina McLaughlin',
'Katharina Towne',
'Kavon Sporer',
'Keshawn Langosh DDS',
'Lacy Johnston V',
'Lauren Thiel',
'Lelia Haley',
'Lonny Hermiston',
'Lupe Jacobs',
'Magdalena Rowe',
'Marjolaine Gleason',
'Mattie Bradtke',
'Miss Amiya Altenwerth',
'Miss Haven Kuhic',
'Miss Janie Bayer',
'Miss Raegan Doyle IV',
'Molly Murray',
'Niko Christiansen Jr.',
'Paxton Koss',
'Reilly Bechtelar',
'Rex Blanda',
'Riley Legros',
'River Pfeffer',
'Rory Lubowitz',
'Rosamond Mueller II',
'Rosario Nicolas Sr.',
'Sandrine Hammes',
'Tad Thompson',
'Talon Fahey DVM',
'Taylor Kuhlman IV',
'Tyler Zieme',
'Vella Price',
'Virginie Beatty',
'Wiley Donnelly',
'Woodrow Kuvalis',
]
}

function getUsers(page = 1, perPage = 15, total = 40, orderByDesc = false) {
// orderByDesc = false
// page = 1 => User 1 ... 15, page = 3 => User 31 ... 40
Expand Down Expand Up @@ -35,29 +131,6 @@ function getUsers(page = 1, perPage = 15, total = 40, orderByDesc = false) {
}
}

export function simplePaginateUsers(page = 1, perPage = 15, total = 40, orderByDesc = false) {
const users = getUsers(page, perPage, total, orderByDesc)
const hasMore = getUsers(page + 1, perPage, total, orderByDesc).length > 0

const paginated = {
current_page: page,
data: users,
from: users[0]?.id || null,
per_page: perPage,
to: users[users.length - 1]?.id || null,
}

return {
paginated,
scrollProp: {
pageName: 'page',
previousPage: page > 1 ? page - 1 : null,
nextPage: hasMore ? page + 1 : null,
currentPage: page,
},
}
}

export function paginateUsers(page = 1, perPage = 15, total = 40, orderByDesc = false) {
const users = getUsers(page, perPage, total, orderByDesc)
const hasMore = getUsers(page + 1, perPage, total, orderByDesc).length > 0
Expand All @@ -78,23 +151,3 @@ export function paginateUsers(page = 1, perPage = 15, total = 40, orderByDesc =
},
}
}

export function cursorPaginateUsers(page = 1, perPage = 15, total = 40, orderByDesc = false) {
const users = getUsers(page, perPage, total, orderByDesc)
const hasMore = getUsers(page + 1, perPage, total, orderByDesc).length > 0

return {
paginated: {
data: users,
per_page: perPage,
next_cursor: hasMore ? page + 1 : null,
prev_cursor: page === 1 ? null : page - 1,
},
scrollProp: {
pageName: 'page',
previousPage: page > 1 ? page - 1 : null,
nextPage: hasMore ? page + 1 : null,
currentPage: page,
},
}
}
Loading