mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-24 14:45:15 +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
|
## Props
|
||||||
|
|
||||||
- `color`: The color of the loading bar. It can be set to `false` to turn off explicit color styling.
|
- `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`).
|
- `height`: Height of the loading bar, in pixels (default `3`).
|
||||||
- `duration`: Duration of the loading bar, in milliseconds (default `2000`).
|
- `duration`: Duration of the loading bar, in milliseconds (default `2000`).
|
||||||
- `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`).
|
- `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>`
|
- **type**: `Ref<boolean>`
|
||||||
- **description**: The loading state
|
- **description**: The loading state
|
||||||
|
|
||||||
|
### `error`
|
||||||
|
|
||||||
|
- **type**: `Ref<boolean>`
|
||||||
|
- **description**: The error state
|
||||||
|
|
||||||
### `progress`
|
### `progress`
|
||||||
|
|
||||||
- **type**: `Ref<number>`
|
- **type**: `Ref<number>`
|
||||||
@ -39,7 +44,7 @@ Set `isLoading` to true and start to increase the `progress` value.
|
|||||||
|
|
||||||
### `finish()`
|
### `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()`
|
### `clear()`
|
||||||
|
|
||||||
|
@ -20,20 +20,24 @@ export default defineComponent({
|
|||||||
type: [String, Boolean],
|
type: [String, Boolean],
|
||||||
default: 'repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)',
|
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: {
|
estimatedProgress: {
|
||||||
type: Function as unknown as () => (duration: number, elapsed: number) => number,
|
type: Function as unknown as () => (duration: number, elapsed: number) => number,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup (props, { slots, expose }) {
|
setup (props, { slots, expose }) {
|
||||||
const { progress, isLoading, start, finish, clear } = useLoadingIndicator({
|
const { progress, isLoading, error, start, finish, clear } = useLoadingIndicator({
|
||||||
duration: props.duration,
|
duration: props.duration,
|
||||||
throttle: props.throttle,
|
throttle: props.throttle,
|
||||||
estimatedProgress: props.estimatedProgress,
|
estimatedProgress: props.estimatedProgress,
|
||||||
})
|
})
|
||||||
|
|
||||||
expose({
|
expose({
|
||||||
progress, isLoading, start, finish, clear,
|
progress, isLoading, error, start, finish, clear,
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => h('div', {
|
return () => h('div', {
|
||||||
@ -47,7 +51,7 @@ export default defineComponent({
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
height: `${props.height}px`,
|
height: `${props.height}px`,
|
||||||
opacity: isLoading.value ? 1 : 0,
|
opacity: isLoading.value ? 1 : 0,
|
||||||
background: props.color || undefined,
|
background: error.value ? props.errorColor : props.color || undefined,
|
||||||
backgroundSize: `${(100 / progress.value) * 100}% auto`,
|
backgroundSize: `${(100 / progress.value) * 100}% auto`,
|
||||||
transform: `scaleX(${progress.value}%)`,
|
transform: `scaleX(${progress.value}%)`,
|
||||||
transformOrigin: 'left',
|
transformOrigin: 'left',
|
||||||
|
@ -23,9 +23,10 @@ export type LoadingIndicator = {
|
|||||||
_cleanup: () => void
|
_cleanup: () => void
|
||||||
progress: Ref<number>
|
progress: Ref<number>
|
||||||
isLoading: Ref<boolean>
|
isLoading: Ref<boolean>
|
||||||
|
error: Ref<boolean>
|
||||||
start: () => void
|
start: () => void
|
||||||
set: (value: number) => void
|
set: (value: number) => void
|
||||||
finish: (opts?: { force?: boolean }) => void
|
finish: (opts?: { force?: boolean, error?: boolean }) => void
|
||||||
clear: () => void
|
clear: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
|||||||
const nuxtApp = useNuxtApp()
|
const nuxtApp = useNuxtApp()
|
||||||
const progress = ref(0)
|
const progress = ref(0)
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
const error = ref(false)
|
||||||
let done = false
|
let done = false
|
||||||
let rafId: number
|
let rafId: number
|
||||||
|
|
||||||
@ -47,7 +49,10 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
|||||||
let hideTimeout: number | NodeJS.Timeout
|
let hideTimeout: number | NodeJS.Timeout
|
||||||
let resetTimeout: number | NodeJS.Timeout
|
let resetTimeout: number | NodeJS.Timeout
|
||||||
|
|
||||||
const start = () => set(0)
|
const start = () => {
|
||||||
|
error.value = false
|
||||||
|
set(0)
|
||||||
|
}
|
||||||
|
|
||||||
function set (at = 0) {
|
function set (at = 0) {
|
||||||
if (nuxtApp.isHydrating) {
|
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
|
progress.value = 100
|
||||||
done = true
|
done = true
|
||||||
clear()
|
clear()
|
||||||
_clearTimeouts()
|
_clearTimeouts()
|
||||||
|
if (opts.error) {
|
||||||
|
error.value = true
|
||||||
|
}
|
||||||
if (opts.force) {
|
if (opts.force) {
|
||||||
progress.value = 0
|
progress.value = 0
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
@ -145,6 +153,7 @@ function createLoadingIndicator (opts: Partial<LoadingIndicatorOpts> = {}) {
|
|||||||
_cleanup,
|
_cleanup,
|
||||||
progress: computed(() => progress.value),
|
progress: computed(() => progress.value),
|
||||||
isLoading: computed(() => isLoading.value),
|
isLoading: computed(() => isLoading.value),
|
||||||
|
error: computed(() => error.value),
|
||||||
start,
|
start,
|
||||||
set,
|
set,
|
||||||
finish,
|
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', () => {
|
describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', () => {
|
||||||
it('getAppManifest', async () => {
|
it('getAppManifest', async () => {
|
||||||
const manifest = await getAppManifest()
|
const manifest = await getAppManifest()
|
||||||
|
Loading…
Reference in New Issue
Block a user