feat(nuxt): custom loading reset/hide delay + force finish() (#25932)

This commit is contained in:
Divine 2024-03-06 19:27:05 +03:00 committed by GitHub
parent 7095048f3b
commit 83314f1c95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 18 deletions

View File

@ -7,6 +7,10 @@ export type LoadingIndicatorOpts = {
duration: number duration: number
/** @default 200 */ /** @default 200 */
throttle: number throttle: number
/** @default 500 */
hideDelay: number
/** @default 400 */
resetDelay: number
/** /**
* You can provide a custom function to customize the progress estimation, * You can provide a custom function to customize the progress estimation,
* which is a function that receives the duration of the loading bar (above) * 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 estimatedProgress?: (duration: number, elapsed: number) => number
} }
function _hide (isLoading: Ref<boolean>, progress: Ref<number>) {
if (import.meta.client) {
setTimeout(() => {
isLoading.value = false
setTimeout(() => { progress.value = 0 }, 400)
}, 500)
}
}
export type LoadingIndicator = { export type LoadingIndicator = {
_cleanup: () => void _cleanup: () => void
progress: Ref<number> progress: Ref<number>
isLoading: Ref<boolean> isLoading: Ref<boolean>
start: () => void start: () => void
set: (value: number) => void set: (value: number) => void
finish: () => void finish: (opts: { force?: boolean }) => void
clear: () => void clear: () => void
} }
@ -40,7 +35,7 @@ function defaultEstimatedProgress (duration: number, elapsed: number): number {
} }
function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) { function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
const { duration = 2000, throttle = 200 } = opts const { duration = 2000, throttle = 200, hideDelay = 500, resetDelay = 400 } = opts
const getProgress = opts.estimatedProgress || defaultEstimatedProgress const getProgress = opts.estimatedProgress || defaultEstimatedProgress
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
const progress = ref(0) const progress = ref(0)
@ -48,7 +43,9 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
let done = false let done = false
let rafId: number 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) const start = () => set(0)
@ -60,7 +57,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
clear() clear()
progress.value = at < 0 ? 0 : at progress.value = at < 0 ? 0 : at
if (throttle && import.meta.client) { if (throttle && import.meta.client) {
_throttle = setTimeout(() => { throttleTimeout = setTimeout(() => {
isLoading.value = true isLoading.value = true
_startProgress() _startProgress()
}, throttle) }, throttle)
@ -70,19 +67,40 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
} }
} }
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 progress.value = 100
done = true done = true
clear() 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 () { function clear () {
clearTimeout(_throttle)
if (import.meta.client) { if (import.meta.client) {
clearTimeout(throttleTimeout)
cancelAnimationFrame(rafId) cancelAnimationFrame(rafId)
} }
_throttle = null
} }
function _startProgress () { function _startProgress () {
@ -113,7 +131,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
const unsubLoadingFinishHook = nuxtApp.hook('page:loading:end', () => { const unsubLoadingFinishHook = nuxtApp.hook('page:loading:end', () => {
finish() finish()
}) })
const unsubError = nuxtApp.hook('vue:error', finish) const unsubError = nuxtApp.hook('vue:error', () => finish())
_cleanup = () => { _cleanup = () => {
unsubError() unsubError()

View File

@ -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', () => { describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', () => {
it('getAppManifest', async () => { it('getAppManifest', async () => {
const manifest = await getAppManifest() const manifest = await getAppManifest()