From f9eabac1b850e062abed79b0362dd93095f0c6cd Mon Sep 17 00:00:00 2001 From: Roc Wong Date: Fri, 21 Jan 2022 17:49:51 +1300 Subject: [PATCH 1/2] feat: support debouncing calls to page provider --- src/Grid.vue | 9 +++++++++ src/demo/App.vue | 1 + src/pipeline.ts | 26 +++++++++++++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Grid.vue b/src/Grid.vue index 0543b69..0c5056c 100644 --- a/src/Grid.vue +++ b/src/Grid.vue @@ -65,6 +65,14 @@ export default defineComponent({ type: Function as PropType, required: true, }, + // Wait a short time until calling the page provider with the latest page + // numbers. It's useful for avoid network requests of skimmed pages. + pageProviderDebounceTime: { + type: Number as PropType, + required: false, + default: 0, + validator: (value: number) => Number.isInteger(value) && value >= 0, + }, // The number of items in a page from the item provider (e.g. a backend API). pageSize: { type: Number as PropType, @@ -92,6 +100,7 @@ export default defineComponent({ // streams of prop length$: fromProp(props, "length"), pageProvider$: fromProp(props, "pageProvider"), + pageProviderDebounceTime$: fromProp(props, "pageProviderDebounceTime"), pageSize$: fromProp(props, "pageSize"), // a stream of item size measurements when it is changed itemRect$: fromResizeObserver(probeRef, "contentRect"), diff --git a/src/demo/App.vue b/src/demo/App.vue index adde4b2..9e8cbd1 100644 --- a/src/demo/App.vue +++ b/src/demo/App.vue @@ -5,6 +5,7 @@ :length="length" :pageSize="pageSize" :pageProvider="pageProvider" + :pageProviderDebounceTime="0" :scrollTo="scrollTo" :class="$style.grid" > diff --git a/src/pipeline.ts b/src/pipeline.ts index 2d5dd66..ace1132 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -1,10 +1,12 @@ import { combineLatest, + debounceTime, distinct, distinctUntilChanged, filter, map, merge, + mergeAll, mergeMap, Observable, of, @@ -218,6 +220,7 @@ export function getContentHeight( interface PipelineInput { length$: Observable; pageProvider$: Observable; + pageProviderDebounceTime$: Observable; pageSize$: Observable; itemRect$: Observable; rootResize$: Observable; @@ -234,6 +237,7 @@ interface PipelineOutput { export function pipeline({ length$, pageProvider$, + pageProviderDebounceTime$, pageSize$, itemRect$, rootResize$, @@ -282,18 +286,26 @@ export function pipeline({ getBufferMeta() ).pipe(distinctUntilChanged(equals)); - const itemsByPage$: Observable = combineLatest([ + const visiblePageNumbers$ = combineLatest([ bufferMeta$, length$, pageSize$, - ]).pipe( - mergeMap(apply(getObservableOfVisiblePageNumbers)), - distinct(identity, merge(pageSize$, pageProvider$)), - withLatestFrom(pageSize$, pageProvider$), - mergeMap(apply(callPageProvider)), - shareReplay(1) + ]).pipe(map(apply(getObservableOfVisiblePageNumbers))); + + const pageNumber$ = pageProviderDebounceTime$.pipe( + switchMap((time) => + visiblePageNumbers$.pipe(time === 0 ? identity : debounceTime(time)) + ), + mergeAll(), + distinct(identity, merge(pageSize$, pageProvider$)) ); + const itemsByPage$: Observable = combineLatest([ + pageNumber$, + pageSize$, + pageProvider$, + ]).pipe(mergeMap(apply(callPageProvider)), shareReplay(1)); + const replayLength$ = length$.pipe(shareReplay(1)); const allItems$: Observable = pageProvider$.pipe( From 4005c78e5f002602f7064fc407930b518e997ac7 Mon Sep 17 00:00:00 2001 From: Roc Wong Date: Fri, 21 Jan 2022 21:25:05 +1300 Subject: [PATCH 2/2] feat: support debouncing calls to page provider --- README.md | 47 ++++++++++++++++++++++++----------------------- src/Grid.vue | 4 ++-- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8ce1653..fbe7131 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ This is a reusable component for Vue 3 that renders a list with a huge number of items (e.g. 1000+ items) as a grid in a performant way. -* [Demo][demo] -* [NPM Package][npm] +- [Demo][demo] +- [NPM Package][npm] ## Features @@ -18,8 +18,8 @@ items (e.g. 1000+ items) as a grid in a performant way. ## Code Examples -* [As an ES module (with a bundler)][esm] -* [As an Universal Module Definition (no bundler)][umd] +- [As an ES module (with a bundler)][esm] +- [As an Universal Module Definition (no bundler)][umd] ## Install @@ -29,20 +29,22 @@ npm install vue-virtual-scroll-grid ## Available Props -| Name | Description | Type | Validation | -|----------------|---------------------------------------------------------------------------|----------------------------------------------------------------|---------------------------------------------------------------------------------| -| `length` | The number of items in the list | `number` | Required, an integer greater than or equal to 0 | -| `pageProvider` | The callback that returns a page of items as a promise | `(pageNumber: number, pageSize: number) => Promise` | Required | -| `pageSize` | The number of items in a page from the item provider (e.g. a backend API) | `number` | Required, an integer greater than or equal to 1 | -| `scrollTo` | Scroll to a specific item by index | `number` | Optional, an integer from 0 to the `length` prop - 1 | +| Name | Description | Type | Validation | +| -------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------- | +| `length` | The number of items in the list | `number` | Required, an integer greater than or equal to 0 | +| `pageProvider` | The callback that returns a page of items as a promise | `(pageNumber: number, pageSize: number) => Promise` | Required | +| `pageProviderDebounceTime` | Debounce window in milliseconds on the calls to `pageProvider` | `number` | Optional, an integer greater than or equal to 0 | +| `pageSize` | The number of items in a page from the item provider (e.g. a backend API) | `number` | Required, an integer greater than or equal to 1 | +| `scrollTo` | Scroll to a specific item by index | `number` | Optional, an integer from 0 to the `length` prop - 1 | Example: ```vue - @@ -121,16 +123,15 @@ items can be 200px x 200px when the view is under 768px and 300px x 500px above Required environment variables: -* `VITE_APP_ID`: An Algolia app ID -* `VITE_SEARCH_ONLY_API_KEY`: The search API key for the Algolia app above +- `VITE_APP_ID`: An Algolia app ID +- `VITE_SEARCH_ONLY_API_KEY`: The search API key for the Algolia app above - -- Setup: `npm install` -- Run dev server: `npm run dev ` -- Lint (type check): `npm run lint ` -- Build the library: `npm run build ` -- Build the demo: `npm run build -- --mode=demo ` -- Preview the locally built demo: `npm run serve ` +* Setup: `npm install` +* Run dev server: `npm run dev ` +* Lint (type check): `npm run lint ` +* Build the library: `npm run build ` +* Build the demo: `npm run build -- --mode=demo ` +* Preview the locally built demo: `npm run serve ` ### How to Release a New Version diff --git a/src/Grid.vue b/src/Grid.vue index 0c5056c..db59ca9 100644 --- a/src/Grid.vue +++ b/src/Grid.vue @@ -65,8 +65,8 @@ export default defineComponent({ type: Function as PropType, required: true, }, - // Wait a short time until calling the page provider with the latest page - // numbers. It's useful for avoid network requests of skimmed pages. + // Debounce window in milliseconds on the calls to `pageProvider`, + // which is useful for avoiding network requests of skimmed pages. pageProviderDebounceTime: { type: Number as PropType, required: false,