mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 00:23:53 +00:00
feat(nuxt): allow displaying error state in loading indicator (#27176)
This commit is contained in:
parent
c545c1da5b
commit
58423772a1
@ -30,6 +30,7 @@ You can pass custom HTML or components through the loading indicator's default s
|
||||
## Props
|
||||
|
||||
- `color`: The color of the loading bar. It can be set to `false` to turn off explicit color styling.
|
||||
- `errorColor`: The color of the loading bar when `error` is set to `true`.
|
||||
- `height`: Height of the loading bar, in pixels (default `3`).
|
||||
- `duration`: Duration of the loading bar, in milliseconds (default `2000`).
|
||||
- `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`).
|
||||
|
@ -26,6 +26,11 @@ It hooks into [`page:loading:start`](/docs/api/advanced/hooks#app-hooks-runtime)
|
||||
- **type**: `Ref<boolean>`
|
||||
- **description**: The loading state
|
||||
|
||||
### `error`
|
||||
|
||||
- **type**: `Ref<boolean>`
|
||||
- **description**: The error state
|
||||
|
||||
### `progress`
|
||||
|
||||
- **type**: `Ref<number>`
|
||||
@ -39,7 +44,7 @@ Set `isLoading` to true and start to increase the `progress` value.
|
||||
|
||||
### `finish()`
|
||||
|
||||
Set the `progress` value to `100`, stop all timers and intervals then reset the loading state `500` ms later. `finish` accepts a `{ force: true }` option to skip the interval before the state is reset.
|
||||
Set the `progress` value to `100`, stop all timers and intervals then reset the loading state `500` ms later. `finish` accepts a `{ force: true }` option to skip the interval before the state is reset, and `{ error: true }` to change the loading bar color and set the error property to true.
|
||||
|
||||
### `clear()`
|
||||
|
||||
|
@ -20,20 +20,24 @@ export default defineComponent({
|
||||
type: [String, Boolean],
|
||||
default: 'repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)',
|
||||
},
|
||||
errorColor: {
|
||||
type: String,
|
||||
default: 'repeating-linear-gradient(to right,#f87171 0%,#ef4444 100%)',
|
||||
},
|
||||
estimatedProgress: {
|
||||
type: Function as unknown as () => (duration: number, elapsed: number) => number,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
setup (props, { slots, expose }) {
|
||||
const { progress, isLoading, start, finish, clear } = useLoadingIndicator({
|
||||
const { progress, isLoading, error, start, finish, clear } = useLoadingIndicator({
|
||||
duration: props.duration,
|
||||
throttle: props.throttle,
|
||||
estimatedProgress: props.estimatedProgress,
|
||||
})
|
||||
|
||||
expose({
|
||||
progress, isLoading, start, finish, clear,
|
||||
progress, isLoading, error, start, finish, clear,
|
||||
})
|
||||
|
||||
return () => h('div', {
|
||||
@ -47,7 +51,7 @@ export default defineComponent({
|
||||
width: 'auto',
|
||||
height: `${props.height}px`,
|
||||
opacity: isLoading.value ? 1 : 0,
|
||||
background: props.color || undefined,
|
||||
background: error.value ? props.errorColor : props.color || undefined,
|
||||
backgroundSize: `${(100 / progress.value) * 100}% auto`,
|
||||
transform: `scaleX(${progress.value}%)`,
|
||||
transformOrigin: 'left',
|
||||
|
@ -23,9 +23,10 @@ export type LoadingIndicator = {
|
||||
_cleanup: () => void
|
||||
progress: Ref<number>
|
||||
isLoading: Ref<boolean>
|
||||
error: Ref<boolean>
|
||||
start: () => void
|
||||
set: (value: number) => void
|
||||
finish: (opts?: { force?: boolean }) => void
|
||||
finish: (opts?: { force?: boolean, error?: boolean }) => void
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
@ -40,6 +41,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
const nuxtApp = useNuxtApp()
|
||||
const progress = ref(0)
|
||||
const isLoading = ref(false)
|
||||
const error = ref(false)
|
||||
let done = false
|
||||
let rafId: number
|
||||
|
||||
@ -47,7 +49,10 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
let hideTimeout: number | NodeJS.Timeout
|
||||
let resetTimeout: number | NodeJS.Timeout
|
||||
|
||||
const start = () => set(0)
|
||||
const start = () => {
|
||||
error.value = false
|
||||
set(0)
|
||||
}
|
||||
|
||||
function set (at = 0) {
|
||||
if (nuxtApp.isHydrating) {
|
||||
@ -76,11 +81,14 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function finish (opts: { force?: boolean } = {}) {
|
||||
function finish (opts: { force?: boolean, error?: boolean } = {}) {
|
||||
progress.value = 100
|
||||
done = true
|
||||
clear()
|
||||
_clearTimeouts()
|
||||
if (opts.error) {
|
||||
error.value = true
|
||||
}
|
||||
if (opts.force) {
|
||||
progress.value = 0
|
||||
isLoading.value = false
|
||||
@ -145,6 +153,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
||||
_cleanup,
|
||||
progress: computed(() => progress.value),
|
||||
isLoading: computed(() => isLoading.value),
|
||||
error: computed(() => error.value),
|
||||
start,
|
||||
set,
|
||||
finish,
|
||||
|
@ -515,6 +515,22 @@ describe('loading state', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('loading state', () => {
|
||||
it('expect error from loading state to be changed by finish({ error: true })', async () => {
|
||||
vi.stubGlobal('setTimeout', vi.fn((cb: Function) => cb()))
|
||||
const nuxtApp = useNuxtApp()
|
||||
const { error, start, finish } = useLoadingIndicator()
|
||||
expect(error.value).toBeFalsy()
|
||||
await nuxtApp.callHook('page:loading:start')
|
||||
start()
|
||||
finish({ error: true })
|
||||
expect(error.value).toBeTruthy()
|
||||
start()
|
||||
expect(error.value).toBeFalsy()
|
||||
finish()
|
||||
})
|
||||
})
|
||||
|
||||
describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', () => {
|
||||
it('getAppManifest', async () => {
|
||||
const manifest = await getAppManifest()
|
||||
|
Loading…
Reference in New Issue
Block a user