Skip to content

Commit a530fad

Browse files
committed
Pkg - Edit README
1 parent b80de90 commit a530fad

File tree

1 file changed

+130
-73
lines changed

1 file changed

+130
-73
lines changed

README.md

Lines changed: 130 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# Vue Global Loader
22

3+
![npm](https://img.shields.io/npm/v/vue-global-loader?color=46c119) ![dependencies](https://img.shields.io/badge/dependencies-0-success) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/vue-global-loader?color=success) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/smastrom/vue-global-loader/tests.yml?branch=main&label=tests)
4+
35
### Global loaders made easy for Vue and Nuxt.
46

57
[Live Demo](https://vue-global-loader.pages.dev/)[Vite Example](https://stackblitz.com/edit/vitejs-vite-umqonc?file=src%2FApp.vue)[Nuxt Example](https://stackblitz.com/edit/nuxt-starter-rpnobz?file=app.vue)
68

79
<br />
810

11+
> :bulb: Please note that this package only works with [Vite](https://vitejs.dev/) and [Nuxt](https://nuxt.com/) setups. Usage without a build-step is not supported. Testing against legacy bundlers (Vue CLI, Webpack) has not been performed.
12+
13+
<br />
14+
915
## Intro
1016

1117
I find it useful to display global loaders in single-page apps. For example:
@@ -14,7 +20,7 @@ I find it useful to display global loaders in single-page apps. For example:
1420
- When navigating to an internal page after a critical operation such as sign-in/sign-out
1521
- When navigating to an internal page with plenty of data
1622

17-
Since I was creating and re-creating the same logic and markup over and over again, I decided to publish a package for it.
23+
Since I was re-creating the same logic and markup over and over again, I decided to publish a package for it.
1824

1925
## Features
2026

@@ -24,7 +30,24 @@ This package simplifies the usage of a single, top-level global loader by:
2430
- Providing a practical API customizable via few key props (get started in 10 seconds)
2531
- Properly disable user interactions with the rest of the app while the loader is displayed
2632
- Announcing a screen reader message when the loader is displayed
27-
- Dynamically update global options from anywhere in the Vue app and set scoped options for the current loader
33+
- Dynamically update options from anywhere in the app and set options scoped to the current loader
34+
35+
## Table of Contents
36+
37+
- [Installation](#installation)
38+
- [Usage](#usage)
39+
- [Customization](#customization)
40+
- [Spinners](#spinners)
41+
- [Options](#options)
42+
- [Default Options](#default-options)
43+
- [Scoped Options](#scoped-options)
44+
- [Updating default options](#updating-default-options)
45+
- [Callbacks / Lifecycle](#callbacks--lifecycle)
46+
- [API](#api)
47+
- [Further Notes](#further-notes)
48+
- [When to use it](#when-to-use-it)
49+
- [When to not use it](#when-to-not-use-it)
50+
- [SPA Loading Templates](#spa-loading-templates)
2851

2952
## Installation
3053

@@ -42,9 +65,9 @@ npm i vue-global-loader
4265

4366
## Usage
4467

45-
> :bulb: No need to state the imports if using **Nuxt**.
68+
### Getting Started
4669

47-
**1. main.js (Single-page app with Vite, Webpack, etc)**
70+
**1. Vite - main.js**
4871

4972
```js
5073
import { createApp } from 'vue'
@@ -54,14 +77,12 @@ import App from './App.vue'
5477

5578
const app = createApp(App)
5679

57-
app.use(globalLoader)
58-
59-
// other app.use() calls...
80+
app.use(globalLoader) // <- Place it at the BEGINNING of the app.use() chain
6081

6182
app.mount('#app')
6283
```
6384

64-
**1. or nuxt.config.ts (if using Nuxt)**
85+
**1. or Nuxt - nuxt.config.ts**
6586

6687
```ts
6788
export default defineNuxtConfig({
@@ -71,117 +92,110 @@ export default defineNuxtConfig({
7192

7293
**2. App.vue (Vite) / app.vue (Nuxt)**
7394

74-
> :bulb: No need to state the imports if using **Nuxt**.
95+
> :bulb: No need to state the imports if using **Nuxt** (everything is auto-imported)
7596
7697
```vue
7798
<script setup>
78-
import { GlobalLoader, RingSpinner } from 'vue-global-loader'
99+
import GlobalLoader from 'vue-global-loader/GlobalLoader.vue'
100+
import CircleSpinner from 'vue-global-loader/CircleSpinner.vue'
79101
</script>
80102
81103
<template>
82104
<GlobalLoader>
83-
<RingSpinner />
105+
<CircleSpinner />
84106
</GlobalLoader>
85107
86108
<!-- RouterView, NuxtLayout, NuxtPage... -->
87109
</template>
88110
```
89111

90-
**Component.vue**
112+
### Usage
113+
114+
`pages/login.vue`
91115

92116
```vue
93117
<script setup>
118+
import { useRouter } from 'vue-router'
94119
import { useGlobalLoader } from 'vue-global-loader'
95120
96121
const { displayLoader, destroyLoader, isLoading } = useGlobalLoader({
97-
screenReaderMessage: 'Redirecting to payment page, please wait...'
122+
screenReaderMessage:
123+
'Signing-in, you will be redirected to the dashboard, please wait...'
98124
})
99125
100-
async function createCheckout() {
126+
const router = useRouter()
127+
128+
async function signIn() {
101129
try {
102-
displayLoader()
103-
const { checkoutUrl } = await fetch('/api/create-checkout').then((res) =>
104-
res.json()
105-
)
106-
window.location.href = checkoutUrl
107-
// No need to call destroyLoader() on success, state is lost when leaving the page
130+
displayLoader() // Display loader...
131+
await auth.signIn()
132+
router.push('/dashboard')
108133
} catch (err) {
109134
console.error(err)
110-
destroyLoader(() => {
111-
// Logged when the loader is removed from the DOM
112-
console.log('Loader destroyed')
113-
})
135+
destroyLoader()
114136
}
115137
}
116138
</script>
117139
118140
<template>
119-
<button :disabled="isLoading" @click="createCheckout">Go to Payment</button>
141+
<button :disabled="isLoading" @click="signIn">Sign-in</button>
120142
</template>
121143
```
122144

123-
<details><summary><strong>Options API</strong></summary>
145+
`pages/dashboard.vue`
124146

125147
```vue
126-
<script>
148+
<script setup>
149+
import { ref, onMounted } from 'vue'
127150
import { useGlobalLoader } from 'vue-global-loader'
128151
129-
export default {
130-
setup() {
131-
const { displayLoader, destroyLoader, isLoading } = useGlobalLoader({
132-
screenReaderMessage: 'Redirecting to payment page, please wait...'
133-
})
152+
const { destroyLoader } = useGlobalLoader()
134153
135-
return { displayLoader, destroyLoader, isLoading }
136-
},
137-
methods: {
138-
async createCheckout() {
139-
try {
140-
this.displayLoader()
141-
const { checkoutUrl } = await fetch('/api/create-checkout').then(
142-
(res) => res.json()
143-
)
144-
window.location.href = checkoutUrl
145-
} catch (err) {
146-
console.error(err)
147-
this.destroyLoader()
148-
}
149-
}
154+
const data = ref(null)
155+
156+
onMounted(async () => {
157+
try {
158+
data.value = await fetchDashboardData()
159+
destroyLoader() // ...destroy loader, UI is ready
160+
} catch (err) {
161+
console.error(err)
162+
// Handle error
150163
}
151-
}
164+
})
152165
</script>
153166
154167
<template>
155-
<button :disabled="isLoading" @click="createCheckout">Go to Payment</button>
168+
<div v-if="data">
169+
<!-- ... -->
170+
</div>
156171
</template>
157172
```
158173

159-
</details>
160-
161174
## Customization
162175

163176
### Spinners
164177

165178
This package ships with 8 spinners that should cover most use cases:
166179

167-
| | Import |
168-
| --------------------------------------------------- | ----------------------------------------------------- |
169-
| ![spinner](https://svgur.com/i/zKJ.svg) | `import { Spinner } from 'vue-global-loader'` |
170-
| ![ring-spinner](https://svgur.com/i/zJk.svg) | `import { RingSpinner } from 'vue-global-loader'` |
171-
| ![ring-dot-spinner](https://svgur.com/i/zKc.svg) | `import { RingDotSpinner } from 'vue-global-loader'` |
172-
| ![circle-bars-spinner](https://svgur.com/i/zHt.svg) | `import { RingBarsSpinner } from 'vue-global-loader'` |
173-
| ![pulse-spinner](https://svgur.com/i/zKK.svg) | `import { PulseSpinner } from 'vue-global-loader'` |
174-
| ![dots-spinner](https://svgur.com/i/zKf.svg) | `import { DotsSpinner } from 'vue-global-loader'` |
175-
| ![bars-spinner](https://svgur.com/i/zHu.svg) | `import { BarsSpinner } from 'vue-global-loader'` |
176-
| ![wave-spinner](https://svgur.com/i/zJ6.svg) | `import { WaveSpinner } from 'vue-global-loader'` |
180+
| | Import |
181+
| --------------------------------------------------- | --------------------------------------------------------------------- |
182+
| ![spinner](https://svgur.com/i/zKJ.svg) | `import CircleSpinner from 'vue-global-loader/CircleSpinner.vue'` |
183+
| ![ring-spinner](https://svgur.com/i/zJk.svg) | `import RingSpinner from 'vue-global-loader/RingSpinner.vue'` |
184+
| ![ring-dot-spinner](https://svgur.com/i/zKc.svg) | `import RingDotSpinner from 'vue-global-loader/RingDotSpinner.vue'` |
185+
| ![circle-bars-spinner](https://svgur.com/i/zHt.svg) | `import RingBarsSpinner from 'vue-global-loader/RingBarsSpinner.vue'` |
186+
| ![pulse-spinner](https://svgur.com/i/zKK.svg) | `import PulseSpinner from 'vue-global-loader/PulseSpinner.vue'` |
187+
| ![dots-spinner](https://svgur.com/i/zKf.svg) | `import DotsSpinner from 'vue-global-loader/DotsSpinner.vue'` |
188+
| ![bars-spinner](https://svgur.com/i/zHu.svg) | `import BarsSpinner from 'vue-global-loader/BarsSpinner.vue'` |
189+
| ![wave-spinner](https://svgur.com/i/zJ6.svg) | `import WaveSpinner from 'vue-global-loader/WaveSpinner.vue'` |
177190

178191
Import the one you prefer and pass it to the default slot:
179192

180-
> :bulb: No need to state the imports if using Nuxt.
193+
> :bulb: No need to state the imports if using **Nuxt** (everything is auto-imported)
181194
182195
```vue
183196
<script setup>
184-
import { GlobalLoader, PulseSpinner } from 'vue-global-loader'
197+
import GlobalLoader from 'vue-global-loader/GlobalLoader.vue'
198+
import PulseSpinner from 'vue-global-loader/PulseSpinner.vue'
185199
</script>
186200
187201
<template>
@@ -193,9 +207,9 @@ import { GlobalLoader, PulseSpinner } from 'vue-global-loader'
193207
</template>
194208
```
195209

196-
There's no need to style the spinners (e.g. the spinner should be 300px wide on desktop, 160px wide on low-res mobile devices, etc). This is already taken care for you.
210+
There's no need to style the spinners (e.g. the spinner should be 140px wide on desktop, 110px wide on mobile devices, animations should be disabled if users prefer reduced motion, etc). This is already taken care for you.
197211

198-
Each spinner already has its own CSS and inherits the `foreground` option specified in your config. You can append a class to override its styles, but it's not recommended and it's better to use a custom spinner.
212+
Each spinner already has its own CSS and inherits the `foregroundColor` option specified in your config. You can append a class to override its styles, but it's not recommended and it's better to use a custom spinner.
199213

200214
#### Custom Spinners
201215

@@ -218,7 +232,7 @@ To use your own spinner, pass a custom SVG (or whatever) to the default slot:
218232
219233
<style>
220234
.MySpinner {
221-
fill: var(--v-gl-fg-color); /* Value of the 'foreground' prop */
235+
fill: var(--v-gl-fg-color); /* Value of the 'foregroundColor' prop */
222236
width: 100px;
223237
height: 100px;
224238
@@ -235,14 +249,14 @@ To use your own spinner, pass a custom SVG (or whatever) to the default slot:
235249
| Prop | Type | Description | Default |
236250
| --------------------- | -------- | ------------------------------------------------------------------ | ------------ |
237251
| `screenReaderMessage` | `string` | Message to announce when displaying the loader. | `Loading` |
238-
| `transitionDuration` | `number` | Enter/leave fade transition duration in ms. Set `0` to disable it. | `300` |
252+
| `transitionDuration` | `number` | Enter/leave fade transition duration in ms. Set `0` to disable it. | `250` |
239253
| `foregroundColor` | `string` | Color of the spinner. | `#000` |
240254
| `backgroundColor` | `string` | Background color of the loading screen. | `#fff` |
241255
| `backgroundOpacity` | `number` | Background opacity of the loading screen. | `1` |
242256
| `backgroundBlur` | `number` | Background blur of the loading screen. | `0` |
243257
| `zIndex` | `number` | Z-index of the loading screen. | `2147483647` |
244258

245-
#### Plugin Options (Default
259+
#### Default Options
246260

247261
To customize defaults, pass the options to the `globalLoader` plugin (if using Vite):
248262

@@ -286,6 +300,8 @@ const { displayLoader, destroyLoader } = useGlobalLoader({
286300

287301
For convenience, you can set new defaults from anywhere in your Vue app using `updateOptions`:
288302

303+
> :bulb: No need to state the imports if using **Nuxt** (everything is auto-imported)
304+
289305
```vue
290306
<script setup>
291307
import { watch } from 'vue'
@@ -310,6 +326,35 @@ watch(
310326
</script>
311327
```
312328

329+
### Callbacks / Transitions Lifecycle
330+
331+
The `GlobalLoader` lifecycle is handled using Vue's [Transition](https://vuejs.org/guide/built-ins/transition.html) hooks. For convenience, `displayLoader` and `destroyLoader` include some syntactic sugar to make it easier to execute code after the fade transition is completed.
332+
333+
#### `displayLoader`
334+
335+
This function returns a promise that resolves after the enter transition is completed or cancelled.
336+
337+
```ts
338+
const { displayLoader } = useGlobalLoader()
339+
const router = useRouter()
340+
341+
await displayLoader() // Wait for the fade transition to complete...
342+
await signOut() // ...mutate the underlying UI
343+
router.push('/') // ...navigate to the homepage and call 'destroyLoader' there
344+
```
345+
346+
#### `destroyLoader`
347+
348+
This function doesn't return a promise but instead, it accepts a callback that is executed after the loader is destroyed.
349+
350+
```ts
351+
const { destroyLoader } = useGlobalLoader()
352+
353+
destroyLoader(() => {
354+
console.log('Loader destroyed')
355+
})
356+
```
357+
313358
## API
314359

315360
```ts
@@ -326,7 +371,7 @@ interface GlobalLoaderOptions {
326371
declare function useGlobalLoader(
327372
scopedOptions?: Partial<GlobalLoaderOptions>
328373
): {
329-
displayLoader: () => void
374+
displayLoader: () => Promise<void>
330375
destroyLoader: (onDestroyed?: () => void) => void
331376
updateOptions: (newOptions: Partial<GlobalLoaderOptions>) => void
332377
options: Readonly<Reactive<GlobalLoaderOptions>>
@@ -342,17 +387,29 @@ Use it when you think it's better for the user to not interact with the rest of
342387

343388
### When to not use it
344389

390+
- To display a loader while your app JS is loading. Use the [SPA Loading Templates](#spa-loading-templates) in plain HTML for that (see below).
345391
- Server-side rendered pages: they are already meant to send the proper content to the client and avoid spinners.
346392
- Non-critical async operations that are quick and obvious, in such case a local loader is better (e.g. spinner in the newsletter form submit button).
347393
- Async operations meant to feed the content of small sub-components, in such case [Suspense](https://vuejs.org/guide/built-ins/suspense.html) is the way to go.
348-
- To display a loader while your app JS is loading. In this case:
349-
- **Vite SPA** - Add the loader markup directly to the `index.html` file (e.g. `<div id="spa-loader">`) and remove it in a top-level (App.vue) _onMounted_ hook via `document.getElementById('spa-loader').remove()`.
350-
- **Nuxt** - Use the built-in [SPA loading indicator](https://nuxt.com/blog/v3-6#spa-loading-indicator).
394+
395+
## SPA Loading Templates
396+
397+
For convenience, ready-made HTML templates for each spinner shipped with this package are available in [this folder](https://github.com/smastrom/vue-global-loader/tree/main/spa-loading-templates).
398+
399+
They can be used to display a global loader that has the same appearance of the one used in your app to be displayed while the app JS is loading.
400+
401+
### Vite SPA
402+
403+
Paste the markup as a direct child of the `body` in the `index.html` file and remove it in a top-level (App.vue) _onMounted_ hook via `document.getElementById('spa_loading_template').remove()`.
404+
405+
### Nuxt
406+
407+
Download the desired html file, rename it and place it in `@/app/spa-loading-template.html`.
351408

352409
## Thanks
353410

354411
[@n3r4zzurr0](https://github.com/n3r4zzurr0) for the awesome spinners.
355412

356413
## License
357414

358-
MIT Licensed - Simone Mastromattei © 2023
415+
MIT

0 commit comments

Comments
 (0)