From 83314f1c951d2c5836242c5720acbdf8507392fb Mon Sep 17 00:00:00 2001 From: Divine <48183131+divine@users.noreply.github.com> Date: Wed, 6 Mar 2024 19:27:05 +0300 Subject: [PATCH] feat(nuxt): custom loading reset/hide delay + force `finish()` (#25932) --- .../src/app/composables/loading-indicator.ts | 54 ++++++++++++------- test/nuxt/composables.test.ts | 15 ++++++ 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/packages/nuxt/src/app/composables/loading-indicator.ts b/packages/nuxt/src/app/composables/loading-indicator.ts index 2003f68841..191841d46d 100644 --- a/packages/nuxt/src/app/composables/loading-indicator.ts +++ b/packages/nuxt/src/app/composables/loading-indicator.ts @@ -7,6 +7,10 @@ export type LoadingIndicatorOpts = { duration: number /** @default 200 */ throttle: number + /** @default 500 */ + hideDelay: number + /** @default 400 */ + resetDelay: number /** * You can provide a custom function to customize the progress estimation, * which is a function that receives the duration of the loading bar (above) @@ -15,22 +19,13 @@ export type LoadingIndicatorOpts = { estimatedProgress?: (duration: number, elapsed: number) => number } -function _hide (isLoading: Ref, progress: Ref) { - if (import.meta.client) { - setTimeout(() => { - isLoading.value = false - setTimeout(() => { progress.value = 0 }, 400) - }, 500) - } -} - export type LoadingIndicator = { _cleanup: () => void progress: Ref isLoading: Ref start: () => void set: (value: number) => void - finish: () => void + finish: (opts: { force?: boolean }) => void clear: () => void } @@ -40,7 +35,7 @@ function defaultEstimatedProgress (duration: number, elapsed: number): number { } function createLoadingIndicator (opts: Partial = {}) { - const { duration = 2000, throttle = 200 } = opts + const { duration = 2000, throttle = 200, hideDelay = 500, resetDelay = 400 } = opts const getProgress = opts.estimatedProgress || defaultEstimatedProgress const nuxtApp = useNuxtApp() const progress = ref(0) @@ -48,7 +43,9 @@ function createLoadingIndicator (opts: Partial = {}) { let done = false let rafId: number - let _throttle: any = null + let throttleTimeout: number | NodeJS.Timeout + let hideTimeout: number | NodeJS.Timeout + let resetTimeout: number | NodeJS.Timeout const start = () => set(0) @@ -60,7 +57,7 @@ function createLoadingIndicator (opts: Partial = {}) { clear() progress.value = at < 0 ? 0 : at if (throttle && import.meta.client) { - _throttle = setTimeout(() => { + throttleTimeout = setTimeout(() => { isLoading.value = true _startProgress() }, throttle) @@ -70,19 +67,40 @@ function createLoadingIndicator (opts: Partial = {}) { } } - function finish () { + function _hide () { + if (import.meta.client) { + hideTimeout = setTimeout(() => { + isLoading.value = false + resetTimeout = setTimeout(() => { progress.value = 0 }, resetDelay) + }, hideDelay) + } + } + + function finish (opts: { force?: boolean } = {}) { progress.value = 100 done = true clear() - _hide(isLoading, progress) + _clearTimeouts() + if (opts.force) { + progress.value = 0 + isLoading.value = false + } else { + _hide() + } + } + + function _clearTimeouts () { + if (import.meta.client) { + clearTimeout(hideTimeout) + clearTimeout(resetTimeout) + } } function clear () { - clearTimeout(_throttle) if (import.meta.client) { + clearTimeout(throttleTimeout) cancelAnimationFrame(rafId) } - _throttle = null } function _startProgress () { @@ -113,7 +131,7 @@ function createLoadingIndicator (opts: Partial = {}) { const unsubLoadingFinishHook = nuxtApp.hook('page:loading:end', () => { finish() }) - const unsubError = nuxtApp.hook('vue:error', finish) + const unsubError = nuxtApp.hook('vue:error', () => finish()) _cleanup = () => { unsubError() diff --git a/test/nuxt/composables.test.ts b/test/nuxt/composables.test.ts index 8311939e0f..8cd41e951d 100644 --- a/test/nuxt/composables.test.ts +++ b/test/nuxt/composables.test.ts @@ -487,6 +487,21 @@ describe('loading state', () => { }) }) +describe('loading state', () => { + it('expect loading state to be changed by force starting/stoping', async () => { + vi.stubGlobal('setTimeout', vi.fn((cb: Function) => cb())) + const nuxtApp = useNuxtApp() + const { isLoading, start, finish } = useLoadingIndicator() + expect(isLoading.value).toBeFalsy() + await nuxtApp.callHook('page:loading:start') + expect(isLoading.value).toBeTruthy() + start() + expect(isLoading.value).toBeTruthy() + finish() + expect(isLoading.value).toBeFalsy() + }) +}) + describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', () => { it('getAppManifest', async () => { const manifest = await getAppManifest()