Skip to content

Commit 4790735

Browse files
feat: adds useNProgress function (vueuse#225)
1 parent 3a6908a commit 4790735

File tree

9 files changed

+316
-0
lines changed

9 files changed

+316
-0
lines changed

packages/integrations/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ npm i <b>@vueuse/integrations</b>
1818
- /Integrations
1919
- [`useAxios`](https://vueuse.js.org/?path=/story/integrations--useaxios) — wrapper for [`axios`](https://github.com/axios/axios)
2020
- [`useCookies`](https://vueuse.js.org/?path=/story/integrations--usecookies) — wrapper for [`universal-cookie`](https://www.npmjs.com/package/universal-cookie)
21+
- [`useNProgress`](https://vueuse.js.org/?path=/story/integrations--usenprogress) — reactive wrapper for [`nprogress`](https://github.com/rstacruz/nprogress)
2122
- [`useQRCode`](https://vueuse.js.org/?path=/story/integrations--useqrcode) — wrapper for [`qrcode`](https://github.com/soldair/node-qrcode)
2223

2324
<!--FUNCTIONS_LIST_ENDS-->

packages/integrations/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './useAxios'
22
export * from './useCookies'
3+
export * from './useNProgress'
34
export * from './useQRCode'

packages/integrations/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,15 @@
3232
},
3333
"optionalDependencies": {
3434
"axios": "^0.20.0",
35+
"nprogress": "^0.2.0",
3536
"qrcode": "^1.4.4",
3637
"universal-cookie": "^4.0.0"
3738
},
3839
"devDependencies": {
40+
"@types/nprogress": "^0.2.0",
3941
"@types/qrcode": "^1.3.5",
4042
"@types/universal-cookie": "^2.2.0",
43+
"nprogress": "^0.2.0",
4144
"qrcode": "^1.4.4",
4245
"universal-cookie": "^4.0.0"
4346
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# useNProgress
2+
3+
> Reactive wrapper for [`nprogress`](https://github.com/rstacruz/nprogress)
4+
5+
## Usage
6+
7+
```js {6}
8+
import { useNProgress } from '@vueuse/integrations'
9+
10+
const { isLoading } = useNProgress()
11+
12+
function toggle() {
13+
isLoading.value = !isLoading.value
14+
}
15+
```
16+
17+
### Passing a progress percentage
18+
19+
You can pass a percentage to indicate where the bar should start from.
20+
21+
```js {3}
22+
import { useNProgress } from '@vueuse/integrations'
23+
24+
const { progress } = useNProgress(0.5)
25+
26+
function done() {
27+
progress.value = 1.0
28+
}
29+
```
30+
31+
> To change the progress percentage, set `progress.value = n`, where n is a number between 0..1.
32+
33+
### Customization
34+
35+
Just edit [nprogress.css](http://ricostacruz.com/nprogress/nprogress.css) to your liking. Tip: you probably only want to find and replace occurrences of #29d.
36+
37+
You can [configure](https://github.com/rstacruz/nprogress#configuration) it by passing an object as a second parameter.
38+
39+
```js {4}
40+
import { useNProgress } from '@vueuse/integrations'
41+
42+
useNProgress(null, {
43+
minimum: 0.1,
44+
// ...
45+
})
46+
```
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { defineComponent } from 'vue-demi'
2+
import { defineDemo, html } from '../../_docs'
3+
import { useNProgress } from '.'
4+
import './style.css'
5+
6+
defineDemo(
7+
{
8+
name: 'useNProgress',
9+
category: '/Integrations',
10+
docs: require('./index.md'),
11+
module,
12+
},
13+
defineComponent({
14+
setup() {
15+
return useNProgress()
16+
},
17+
18+
template: html`
19+
<div>
20+
<note>Click to change progress status</note>
21+
<div class="flex items-center">
22+
<button @click='isLoading = !isLoading'>{{ !isLoading ? 'Start' : 'Stop' }}</button>
23+
</div>
24+
</div>
25+
`,
26+
}),
27+
)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { renderHook } from '../../_tests'
2+
import { useNProgress } from './index'
3+
import { nextTick } from 'vue-demi'
4+
import nprogress from 'nprogress'
5+
6+
describe('useNProgress', () => {
7+
it('should be defined', () => {
8+
expect(useNProgress).toBeDefined()
9+
})
10+
11+
it('should start state as false', () => {
12+
renderHook(() => {
13+
const { isLoading } = useNProgress()
14+
15+
expect(isLoading.value).toBeFalsy()
16+
})
17+
})
18+
19+
it('should start state as true', () => {
20+
const instance = renderHook(() => {
21+
return useNProgress(0.1)
22+
}).vm
23+
24+
expect(instance.isLoading).toBeTruthy()
25+
expect(instance.progress).toBe(0.1)
26+
})
27+
28+
it('should track the manual progress', async() => {
29+
const setProgress = jest.spyOn(nprogress, 'set')
30+
const instance = renderHook(() => {
31+
return useNProgress()
32+
}).vm
33+
34+
expect(instance.isLoading).toBeFalsy()
35+
36+
instance.progress = 0.0
37+
await nextTick()
38+
expect(setProgress).toBeCalledWith(0.0)
39+
expect(instance.isLoading).toBeTruthy()
40+
41+
setProgress.mockClear()
42+
43+
instance.progress = 1.0
44+
await nextTick()
45+
expect(setProgress).toBeCalledWith(1.0)
46+
expect(instance.isLoading).toBeFalsy()
47+
})
48+
49+
it('should update progress state', async() => {
50+
const startProgress = jest.spyOn(nprogress, 'start')
51+
const instance = renderHook(() => {
52+
return useNProgress()
53+
}).vm
54+
55+
expect(instance.isLoading).toBeFalsy()
56+
57+
instance.isLoading = true
58+
await nextTick()
59+
expect(startProgress).toBeCalled()
60+
})
61+
62+
it('should start progress bar', () => {
63+
renderHook(() => {
64+
const startProgress = jest.spyOn(nprogress, 'start')
65+
const { start, isLoading } = useNProgress()
66+
67+
expect(isLoading.value).toBeFalsy()
68+
start()
69+
expect(startProgress).toBeCalled()
70+
expect(isLoading.value).toBeTruthy()
71+
})
72+
})
73+
74+
it('should done and remove progress bar', () => {
75+
renderHook(() => {
76+
const setProgress = jest.spyOn(nprogress, 'set')
77+
const { done, isLoading } = useNProgress(0)
78+
79+
expect(isLoading.value).toBeTruthy()
80+
done()
81+
expect(setProgress).toBeCalledWith(1)
82+
expect(isLoading.value).toBeFalsy()
83+
})
84+
})
85+
86+
it('should remove and remove progress bar', async() => {
87+
const removeProgress = jest.spyOn(nprogress, 'remove')
88+
const instance = renderHook(() => {
89+
return useNProgress(0)
90+
}).vm
91+
92+
expect(instance.isLoading).toBeTruthy()
93+
instance.remove()
94+
await nextTick()
95+
expect(removeProgress).toBeCalled()
96+
expect(instance.isLoading).toBeFalsy()
97+
})
98+
})
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import nprogress, { NProgressOptions } from 'nprogress'
2+
import { MaybeRef, tryOnUnmounted, isNumber, isBoolean } from '@vueuse/shared'
3+
import { ref, isRef, watch } from 'vue-demi'
4+
5+
/**
6+
* Reactive progress bar.
7+
*
8+
* @see {@link https://vueuse.js.org/useNProgress}
9+
* @param currentProgress
10+
* @param options
11+
*/
12+
export function useNProgress(
13+
currentProgress: MaybeRef<number | null | undefined> = null,
14+
options?: NProgressOptions | undefined,
15+
) {
16+
const progress = isRef(currentProgress)
17+
? currentProgress
18+
: ref<number|null>(currentProgress)
19+
const isLoading = ref<boolean|null>(null)
20+
21+
if (options)
22+
nprogress.configure(options)
23+
24+
watch([progress, isLoading], ([p, l]) => {
25+
if (isNumber(p)) {
26+
nprogress.set(p)
27+
isLoading.value = p < 1
28+
}
29+
else if (isBoolean(l)) {
30+
l ? nprogress.start() : nprogress.done()
31+
}
32+
else {
33+
nprogress.remove()
34+
}
35+
}, {
36+
immediate: true,
37+
})
38+
39+
tryOnUnmounted(nprogress.remove)
40+
41+
return {
42+
isLoading,
43+
progress,
44+
start: () => {
45+
progress.value = null
46+
isLoading.value = true
47+
},
48+
done: () => {
49+
progress.value = null
50+
isLoading.value = false
51+
},
52+
remove: () => {
53+
progress.value = null
54+
isLoading.value = null
55+
},
56+
}
57+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* Make clicks pass-through */
2+
#nprogress {
3+
pointer-events: none;
4+
}
5+
6+
#nprogress .bar {
7+
background: #67d391;
8+
9+
position: fixed;
10+
z-index: 1031;
11+
top: 0;
12+
left: 0;
13+
14+
width: 100%;
15+
height: 2px;
16+
}
17+
18+
/* Fancy blur effect */
19+
#nprogress .peg {
20+
display: block;
21+
position: absolute;
22+
right: 0px;
23+
width: 100px;
24+
height: 100%;
25+
box-shadow: 0 0 10px #67d391, 0 0 5px #67d391;
26+
opacity: 1.0;
27+
28+
-webkit-transform: rotate(3deg) translate(0px, -4px);
29+
-ms-transform: rotate(3deg) translate(0px, -4px);
30+
transform: rotate(3deg) translate(0px, -4px);
31+
}
32+
33+
/* Remove these to get rid of the spinner */
34+
#nprogress .spinner {
35+
display: block;
36+
position: fixed;
37+
z-index: 1031;
38+
top: 15px;
39+
left: 15px;
40+
}
41+
42+
#nprogress .spinner-icon {
43+
width: 18px;
44+
height: 18px;
45+
box-sizing: border-box;
46+
47+
border: solid 3px transparent;
48+
border-top-color: #67d391;
49+
border-left-color: #67d391;
50+
border-radius: 50%;
51+
52+
-webkit-animation: nprogress-spinner 400ms linear infinite;
53+
animation: nprogress-spinner 400ms linear infinite;
54+
}
55+
56+
.nprogress-custom-parent {
57+
overflow: hidden;
58+
position: relative;
59+
}
60+
61+
.nprogress-custom-parent #nprogress .spinner,
62+
.nprogress-custom-parent #nprogress .bar {
63+
position: absolute;
64+
}
65+
66+
@-webkit-keyframes nprogress-spinner {
67+
0% { -webkit-transform: rotate(0deg); }
68+
100% { -webkit-transform: rotate(360deg); }
69+
}
70+
@keyframes nprogress-spinner {
71+
0% { transform: rotate(0deg); }
72+
100% { transform: rotate(360deg); }
73+
}

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,6 +2272,11 @@
22722272
resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4"
22732273
integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA==
22742274

2275+
"@types/nprogress@^0.2.0":
2276+
version "0.2.0"
2277+
resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f"
2278+
integrity sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==
2279+
22752280
"@types/parse-json@^4.0.0":
22762281
version "4.0.0"
22772282
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -9828,6 +9833,11 @@ npmlog@^4.1.2:
98289833
gauge "~2.7.3"
98299834
set-blocking "~2.0.0"
98309835

9836+
nprogress@^0.2.0:
9837+
version "0.2.0"
9838+
resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1"
9839+
integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E=
9840+
98319841
nth-check@~1.0.1:
98329842
version "1.0.2"
98339843
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"

0 commit comments

Comments
 (0)