Skip to content

Commit 5850c8b

Browse files
committed
chore: playground
1 parent 8933c55 commit 5850c8b

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

playground/src/api/cat-facts.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { mande } from 'mande'
2+
3+
export interface CatFacts {
4+
current_page: number
5+
data: Array<{ fact: string; length: number }>
6+
first_page_url: string
7+
from: number
8+
last_page: number
9+
last_page_url: string
10+
links: Array<{
11+
url: string | null
12+
label: string
13+
active: boolean
14+
}>
15+
next_page_url: string | null
16+
path: string
17+
per_page: number
18+
prev_page_url: string | null
19+
to: number
20+
total: number
21+
}
22+
23+
export const factsApi = mande('https://catfact.ninja/facts')
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup lang="ts">
2+
// NOTE: not implemented yet: when there are many files, it's hard to find todos/ and todos.vue, having the layout in the same folder, makes it easier. This is not supported by Nuxt.
3+
</script>
4+
5+
<template>
6+
<div>
7+
<RouterView />
8+
</div>
9+
</template>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<script setup lang="ts">
2+
import { factsApi } from '@/api/cat-facts'
3+
import type { CatFacts } from '@/api/cat-facts'
4+
import { useInfiniteQuery } from '@tanstack/vue-query'
5+
import { VueQueryDevtools } from '@tanstack/vue-query-devtools'
6+
import { computed, onWatcherCleanup, useTemplateRef, watch } from 'vue'
7+
8+
const {
9+
data: facts,
10+
isLoading,
11+
fetchNextPage,
12+
hasNextPage,
13+
} = useInfiniteQuery({
14+
queryKey: ['cat-facts', 'feed'],
15+
queryFn: async ({ pageParam }) =>
16+
factsApi.get<CatFacts>({ query: { page: pageParam, limit: 10 } }),
17+
initialPageParam: 1,
18+
19+
getNextPageParam: (lastPage) => {
20+
// if there is no next page, return null
21+
if (!lastPage.next_page_url) return null
22+
// otherwise return the next page number
23+
return lastPage.current_page + 1
24+
},
25+
})
26+
27+
const mergedPages = computed(() => {
28+
return facts.value?.pages?.flatMap((page) => page.data) || []
29+
})
30+
31+
const loadMoreEl = useTemplateRef('load-more')
32+
33+
watch(loadMoreEl, (el) => {
34+
if (el) {
35+
const observer = new IntersectionObserver(
36+
(entries) => {
37+
if (entries[0]?.isIntersecting) {
38+
fetchNextPage()
39+
}
40+
},
41+
{
42+
rootMargin: '300px',
43+
threshold: [0],
44+
}
45+
)
46+
observer.observe(el)
47+
onWatcherCleanup(() => {
48+
observer.disconnect()
49+
})
50+
}
51+
})
52+
</script>
53+
54+
<template>
55+
<div>
56+
<button :disabled="isLoading" @click="fetchNextPage()">
57+
Load more (or scroll down)
58+
</button>
59+
<template v-if="facts?.pages">
60+
<p>We have loaded {{ facts.pages.length }} facts</p>
61+
<details>
62+
<summary>Show raw</summary>
63+
<pre>{{ facts }}</pre>
64+
</details>
65+
66+
<blockquote v-for="fact in mergedPages!">
67+
{{ fact }}
68+
</blockquote>
69+
70+
<p v-if="hasNextPage" ref="load-more">Loading more...</p>
71+
</template>
72+
</div>
73+
74+
<VueQueryDevtools />
75+
</template>

0 commit comments

Comments
 (0)