ng-hub-ui-loading 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/fesm2022/ng-hub-ui-loading.mjs +312 -0
- package/fesm2022/ng-hub-ui-loading.mjs.map +1 -0
- package/package.json +48 -0
- package/styles/_index.scss +1 -0
- package/styles/mixins/_loading-theme.scss +79 -0
- package/types/ng-hub-ui-loading.d.ts +252 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos Morcillo FernΓ‘ndez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
# ng-hub-ui-loading
|
|
2
|
+
|
|
3
|
+
[EspaΓ±ol](./README.es.md) | **English**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/ng-hub-ui-loading)
|
|
6
|
+
[](https://angular.dev)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
Standalone loading block for Angular 22+ β an inline indicator, an overlay pinned over the container that is busy, or a fullscreen curtain, from a single `<hub-loading>` element. Five pure-CSS indicators, an optional logo or image instead of them, an optional message, and a counter-based `HubLoadingService` for the app-wide fullscreen case. Zero external dependencies; every colour and dimension is a `--hub-loading-*` CSS custom property.
|
|
10
|
+
|
|
11
|
+
## Documentation and Live Examples
|
|
12
|
+
|
|
13
|
+
This package is part of [Hub UI](https://hubui.dev/en/), a collection of Angular component libraries for standalone apps.
|
|
14
|
+
|
|
15
|
+
- Docs: https://hubui.dev/en/loading/overview/
|
|
16
|
+
- Live examples: https://hubui.dev/en/loading/examples/
|
|
17
|
+
- Hub UI: https://hubui.dev/en/
|
|
18
|
+
|
|
19
|
+
## π§© Library Family `ng-hub-ui`
|
|
20
|
+
|
|
21
|
+
This library is part of the **ng-hub-ui** ecosystem:
|
|
22
|
+
|
|
23
|
+
- [**ng-hub-ui-action-sheet**](https://www.npmjs.com/package/ng-hub-ui-action-sheet)
|
|
24
|
+
- [**ng-hub-ui-avatar**](https://www.npmjs.com/package/ng-hub-ui-avatar)
|
|
25
|
+
- [**ng-hub-ui-badges**](https://www.npmjs.com/package/ng-hub-ui-badges)
|
|
26
|
+
- [**ng-hub-ui-board**](https://www.npmjs.com/package/ng-hub-ui-board)
|
|
27
|
+
- [**ng-hub-ui-breadcrumbs**](https://www.npmjs.com/package/ng-hub-ui-breadcrumbs)
|
|
28
|
+
- [**ng-hub-ui-buttons**](https://www.npmjs.com/package/ng-hub-ui-buttons)
|
|
29
|
+
- [**ng-hub-ui-calendar**](https://www.npmjs.com/package/ng-hub-ui-calendar)
|
|
30
|
+
- [**ng-hub-ui-ds**](https://www.npmjs.com/package/ng-hub-ui-ds)
|
|
31
|
+
- [**ng-hub-ui-forms**](https://www.npmjs.com/package/ng-hub-ui-forms)
|
|
32
|
+
- [**ng-hub-ui-history**](https://www.npmjs.com/package/ng-hub-ui-history)
|
|
33
|
+
- [**ng-hub-ui-icons**](https://www.npmjs.com/package/ng-hub-ui-icons)
|
|
34
|
+
- [**ng-hub-ui-loading**](https://www.npmjs.com/package/ng-hub-ui-loading) β You are here
|
|
35
|
+
- [**ng-hub-ui-metrics**](https://www.npmjs.com/package/ng-hub-ui-metrics)
|
|
36
|
+
- [**ng-hub-ui-milestones**](https://www.npmjs.com/package/ng-hub-ui-milestones)
|
|
37
|
+
- [**ng-hub-ui-modal**](https://www.npmjs.com/package/ng-hub-ui-modal)
|
|
38
|
+
- [**ng-hub-ui-nav**](https://www.npmjs.com/package/ng-hub-ui-nav)
|
|
39
|
+
- [**ng-hub-ui-paginable**](https://www.npmjs.com/package/ng-hub-ui-paginable)
|
|
40
|
+
- [**ng-hub-ui-panels**](https://www.npmjs.com/package/ng-hub-ui-panels)
|
|
41
|
+
- [**ng-hub-ui-portal**](https://www.npmjs.com/package/ng-hub-ui-portal)
|
|
42
|
+
- [**ng-hub-ui-signature**](https://www.npmjs.com/package/ng-hub-ui-signature)
|
|
43
|
+
- [**ng-hub-ui-skeleton**](https://www.npmjs.com/package/ng-hub-ui-skeleton)
|
|
44
|
+
- [**ng-hub-ui-sortable**](https://www.npmjs.com/package/ng-hub-ui-sortable)
|
|
45
|
+
- [**ng-hub-ui-stepper**](https://www.npmjs.com/package/ng-hub-ui-stepper)
|
|
46
|
+
- [**ng-hub-ui-toast**](https://www.npmjs.com/package/ng-hub-ui-toast)
|
|
47
|
+
- [**ng-hub-ui-utils**](https://www.npmjs.com/package/ng-hub-ui-utils)
|
|
48
|
+
|
|
49
|
+
## π Table of Contents
|
|
50
|
+
|
|
51
|
+
- [π¦ Description](#-description)
|
|
52
|
+
- [β¨ Features](#-features)
|
|
53
|
+
- [π Quick Start](#-quick-start)
|
|
54
|
+
- [π― Modes](#-modes)
|
|
55
|
+
- [ποΈ Variants](#οΈ-variants)
|
|
56
|
+
- [πΌοΈ Image and Branding](#οΈ-image-and-branding)
|
|
57
|
+
- [π§° Programmatic API](#-programmatic-api)
|
|
58
|
+
- [π API Reference](#-api-reference)
|
|
59
|
+
- [π¨ Styling / CSS Variables](#-styling--css-variables)
|
|
60
|
+
- [βΏ Accessibility](#-accessibility)
|
|
61
|
+
- [π₯οΈ Server-Side Rendering](#οΈ-server-side-rendering)
|
|
62
|
+
- [π¦ Peer Dependencies](#-peer-dependencies)
|
|
63
|
+
- [π Changelog](#-changelog)
|
|
64
|
+
- [π€ Contribution](#-contribution)
|
|
65
|
+
- [β Support](#-support)
|
|
66
|
+
- [π License](#-license)
|
|
67
|
+
|
|
68
|
+
## π¦ Description
|
|
69
|
+
|
|
70
|
+
`ng-hub-ui-loading` fills the gap between the family's other "something is happening"
|
|
71
|
+
primitives and the spinner that used to be trapped inside a button: a standalone block
|
|
72
|
+
that shows an activity indicator, an optional image or logo and an optional message,
|
|
73
|
+
and that can render in flow, over its own container, or over the whole viewport.
|
|
74
|
+
|
|
75
|
+
### When to reach for which library
|
|
76
|
+
|
|
77
|
+
| Package | Use it when |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| **`ng-hub-ui-loading`** | You cannot say how far along the work is, and the shape of the result does not matter yet. An activity indicator β in place, over the busy container, or over the whole app. |
|
|
80
|
+
| [`ng-hub-ui-skeleton`](https://www.npmjs.com/package/ng-hub-ui-skeleton) | You already know the shape of what is coming and want the layout to hold its place β structural shimmer placeholders instead of a spinner. |
|
|
81
|
+
| [`ng-hub-ui-metrics`](https://www.npmjs.com/package/ng-hub-ui-metrics) | You know the progress figure β a determinate progress bar, meter or ring that reports a value. |
|
|
82
|
+
|
|
83
|
+
The three compose: a skeleton for the list that is arriving, a `<hub-loading mode="overlay">`
|
|
84
|
+
over the panel being refreshed, and a `<hub-progress>` for the upload that reports bytes.
|
|
85
|
+
|
|
86
|
+
## β¨ Features
|
|
87
|
+
|
|
88
|
+
- **Three modes in one component** β `inline`, container `overlay` and viewport `fullscreen`.
|
|
89
|
+
- **Five pure-CSS indicators** β `spinner`, `dots`, `bars`, `pulse` and `ring`; no JavaScript animation loop, no SVG sprite, no icon font.
|
|
90
|
+
- **Image / logo support** β swap the indicator for your own brand asset and animate it with `spin` or `pulse`.
|
|
91
|
+
- **Optional message and projected content** β a caption under the indicator plus an `<ng-content>` slot for anything else.
|
|
92
|
+
- **Programmatic overlays** β `HubLoadingService` mounts a fullscreen `<hub-loading>` on demand, with reference-counted `show()` / `hide()` so concurrent tasks cannot dismiss each other's overlay.
|
|
93
|
+
- **Application-wide defaults** β `provideHubLoading()` re-bases every input's default, for the service *and* for every `<hub-loading>` in a template, without touching a single markup file.
|
|
94
|
+
- **Any accent colour** β `color` accepts a semantic design-system name, a hex value, `oklch()` or a `var(...)` reference, resolved through `resolveHubAccent()` from `ng-hub-ui-utils`.
|
|
95
|
+
- **CSS-variable theming** β every colour, dimension and speed is a `--hub-loading-*` custom property, with a `hub-loading-theme()` Sass mixin for one-call re-skinning.
|
|
96
|
+
- **Accessible by default** β `role="status"`, `aria-live="polite"` and `aria-busy="true"`, with a configurable `ariaLabel` and a `prefers-reduced-motion` treatment that calms the motion instead of freezing it.
|
|
97
|
+
- **Standalone, `OnPush`, signal inputs** β and SSR-safe: on the server the counter still runs, only the DOM mount is skipped.
|
|
98
|
+
|
|
99
|
+
## π Quick Start
|
|
100
|
+
|
|
101
|
+
### 1. Install
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install ng-hub-ui-loading ng-hub-ui-utils
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> **Theming (recommended):** install the shared design tokens so the loading block β
|
|
108
|
+
> and every other ng-hub-ui library β reads the same palette and dark-mode colours:
|
|
109
|
+
>
|
|
110
|
+
> ```bash
|
|
111
|
+
> npm install ng-hub-ui-ds
|
|
112
|
+
> ```
|
|
113
|
+
>
|
|
114
|
+
> ```css
|
|
115
|
+
> @import 'ng-hub-ui-ds/styles/tokens/hub-tokens.css';
|
|
116
|
+
> ```
|
|
117
|
+
>
|
|
118
|
+
> It is an **optional** peer dependency: the component ships sensible CSS fallbacks
|
|
119
|
+
> and works without it.
|
|
120
|
+
|
|
121
|
+
### 2. Import the standalone component
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { HubLoadingComponent } from 'ng-hub-ui-loading';
|
|
125
|
+
|
|
126
|
+
@Component({
|
|
127
|
+
standalone: true,
|
|
128
|
+
imports: [HubLoadingComponent],
|
|
129
|
+
template: `
|
|
130
|
+
@if (isLoading()) {
|
|
131
|
+
<hub-loading message="Loading resultsβ¦" />
|
|
132
|
+
}
|
|
133
|
+
`
|
|
134
|
+
})
|
|
135
|
+
export class ResultsComponent {
|
|
136
|
+
readonly isLoading = signal(true);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## π― Modes
|
|
141
|
+
|
|
142
|
+
The `mode` input decides where the block paints. Everything else β variant, image,
|
|
143
|
+
message, size, colour β works identically in all three.
|
|
144
|
+
|
|
145
|
+
### `inline` (default)
|
|
146
|
+
|
|
147
|
+
Renders in the document flow, like any other block. Use it inside the empty area it
|
|
148
|
+
is standing in for: a panel body, a table placeholder, a card that has not resolved yet.
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<hub-loading message="Fetching invoicesβ¦" />
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `overlay`
|
|
155
|
+
|
|
156
|
+
Absolutely positioned over its parent, so the stale content stays visible underneath
|
|
157
|
+
while the refresh runs.
|
|
158
|
+
|
|
159
|
+
> The parent must establish a positioning context β give it `position: relative`.
|
|
160
|
+
|
|
161
|
+
```html
|
|
162
|
+
<section class="panel" style="position: relative">
|
|
163
|
+
<article>β¦already rendered contentβ¦</article>
|
|
164
|
+
|
|
165
|
+
@if (refreshing()) {
|
|
166
|
+
<hub-loading mode="overlay" variant="ring" message="Refreshingβ¦" />
|
|
167
|
+
}
|
|
168
|
+
</section>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `fullscreen`
|
|
172
|
+
|
|
173
|
+
Fixed to the viewport, covering the application. Declare it in a template when the
|
|
174
|
+
component owns the state, or let [`HubLoadingService`](#-programmatic-api) mount it for you.
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
@if (booting()) {
|
|
178
|
+
<hub-loading mode="fullscreen" variant="pulse" message="Starting upβ¦" />
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Backdrop
|
|
183
|
+
|
|
184
|
+
`backdrop` paints the translucent layer behind the indicator. It applies to `overlay`
|
|
185
|
+
and `fullscreen` only β an inline block has nothing to cover β and is on by default.
|
|
186
|
+
|
|
187
|
+
```html
|
|
188
|
+
<hub-loading mode="overlay" [backdrop]="false" />
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## ποΈ Variants
|
|
192
|
+
|
|
193
|
+
Five indicators, all drawn with CSS. Pick with the `variant` input.
|
|
194
|
+
|
|
195
|
+
| Variant | Shape |
|
|
196
|
+
| --- | --- |
|
|
197
|
+
| `spinner` | Rotating arc (the default). |
|
|
198
|
+
| `dots` | Three dots pulsing in sequence. |
|
|
199
|
+
| `bars` | Bars rising and falling. |
|
|
200
|
+
| `pulse` | A single expanding, fading disc. |
|
|
201
|
+
| `ring` | A full ring with a travelling highlight. |
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<hub-loading variant="dots" />
|
|
205
|
+
<hub-loading variant="bars" size="lg" color="success" />
|
|
206
|
+
<hub-loading variant="ring" color="#7c3aed" />
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`size` picks between `sm`, `md` (default) and `lg`. Each step retunes the tokens rather
|
|
210
|
+
than hard-coding dimensions β `--hub-loading-size` (`1.5rem` / `2.5rem` / `4rem`), the
|
|
211
|
+
indicator `--hub-loading-thickness` and the message `--hub-loading-font-size` β so any
|
|
212
|
+
value outside those three steps is one CSS custom property away:
|
|
213
|
+
|
|
214
|
+
```css
|
|
215
|
+
.hero-loading {
|
|
216
|
+
--hub-loading-size: 6rem;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`color` accepts a semantic accent name (`primary`, `success`, `brand`β¦), a literal colour
|
|
221
|
+
(`#7c3aed`, `rgb(...)`, `oklch(...)`) or a `var(...)` reference. Barewords resolve to the
|
|
222
|
+
design-system token `var(--hub-sys-color-<name>, <name>)`, so unregistered names and CSS
|
|
223
|
+
named colours still paint.
|
|
224
|
+
|
|
225
|
+
## πΌοΈ Image and Branding
|
|
226
|
+
|
|
227
|
+
Set `image` to a URL or a data URI and it replaces the built-in indicator β the usual
|
|
228
|
+
case being a product logo on the boot screen. `imageAnimation` gives it motion.
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<hub-loading mode="fullscreen" image="/assets/logo.svg" imageAnimation="pulse" message="Preparing your workspaceβ¦" />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| `imageAnimation` | Effect |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| `none` | Static image (the default). |
|
|
237
|
+
| `spin` | Continuous rotation. |
|
|
238
|
+
| `pulse` | Rhythmic scale/opacity beat. |
|
|
239
|
+
|
|
240
|
+
Size the asset with `--hub-loading-image-size`, and use `--hub-loading-speed` to keep the
|
|
241
|
+
animation in step with the rest of your brand's motion.
|
|
242
|
+
|
|
243
|
+
Anything projected into the component renders below the message, which is where extra
|
|
244
|
+
context or an escape hatch belongs:
|
|
245
|
+
|
|
246
|
+
```html
|
|
247
|
+
<hub-loading mode="fullscreen" message="Importing 12,480 rowsβ¦">
|
|
248
|
+
<button type="button" (click)="cancel()">Cancel import</button>
|
|
249
|
+
</hub-loading>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## π§° Programmatic API
|
|
253
|
+
|
|
254
|
+
`HubLoadingService` covers the case the declarative component cannot: work that starts
|
|
255
|
+
in a service, a route guard or an effect, where no template owns the flag. It mounts a
|
|
256
|
+
single fullscreen `<hub-loading>` on `document.body` the first time it is needed.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { inject } from '@angular/core';
|
|
260
|
+
import { HubLoadingService } from 'ng-hub-ui-loading';
|
|
261
|
+
|
|
262
|
+
@Injectable({ providedIn: 'root' })
|
|
263
|
+
export class ReportService {
|
|
264
|
+
private readonly loading = inject(HubLoadingService);
|
|
265
|
+
|
|
266
|
+
async export(): Promise<void> {
|
|
267
|
+
this.loading.show({ message: 'Building the reportβ¦' });
|
|
268
|
+
try {
|
|
269
|
+
await this.buildReport();
|
|
270
|
+
} finally {
|
|
271
|
+
this.loading.hide();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Reference counting
|
|
278
|
+
|
|
279
|
+
`show()` increments an internal counter and `hide()` decrements it; the overlay is
|
|
280
|
+
destroyed only when the counter reaches zero. Two concurrent tasks can therefore each
|
|
281
|
+
call `show()` / `hide()` without the first one to finish tearing down the overlay the
|
|
282
|
+
second still needs. `hideAll()` forces the counter to zero β the right call from a
|
|
283
|
+
global error handler or a route change, where the pending `hide()` calls may never arrive.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
this.loading.show(); // counter 1 β overlay appears
|
|
287
|
+
this.loading.show(); // counter 2 β same overlay
|
|
288
|
+
this.loading.hide(); // counter 1 β overlay stays
|
|
289
|
+
this.loading.hide(); // counter 0 β overlay is destroyed
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The counter is clamped at zero, so a stray extra `hide()` is harmless β it cannot push
|
|
293
|
+
the count negative and turn a later `show()` into a no-op.
|
|
294
|
+
|
|
295
|
+
### Updating a live overlay
|
|
296
|
+
|
|
297
|
+
`update()` changes the visible overlay in place β typically the message, as a long task
|
|
298
|
+
moves through its phases.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
this.loading.show({ variant: 'ring', message: 'Connectingβ¦' });
|
|
302
|
+
this.loading.update({ message: 'Downloadingβ¦' });
|
|
303
|
+
this.loading.update({ message: 'Almost thereβ¦' });
|
|
304
|
+
this.loading.hide();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Options accumulate rather than replace, and the two empty values mean different things:
|
|
308
|
+
an **omitted** key (or `undefined`) leaves the current value alone, while `null` clears it.
|
|
309
|
+
`{ message: null }` therefore removes the caption; `{ message: undefined }` keeps it.
|
|
310
|
+
The accumulated options are dropped once the counter reaches zero, so the next overlay
|
|
311
|
+
never inherits a stale message from a finished operation.
|
|
312
|
+
|
|
313
|
+
`isLoading` is a signal, so the rest of the application can react to the same state:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
readonly busy = this.loading.isLoading; // Signal<boolean>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Defaults with `provideHubLoading()`
|
|
320
|
+
|
|
321
|
+
Register the provider once to re-base the defaults for the whole application β the brand
|
|
322
|
+
image, the preferred variant, a translated label β instead of repeating them at each call
|
|
323
|
+
site. It reaches **both** consumers: the service's overlays *and* every `<hub-loading>`
|
|
324
|
+
written in a template, because each component input falls back to the same configuration.
|
|
325
|
+
A per-instance binding still wins locally, and individual `show()` / `update()` options are
|
|
326
|
+
merged on top.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// app.config.ts
|
|
330
|
+
import { ApplicationConfig } from '@angular/core';
|
|
331
|
+
import { provideHubLoading } from 'ng-hub-ui-loading';
|
|
332
|
+
|
|
333
|
+
export const appConfig: ApplicationConfig = {
|
|
334
|
+
providers: [
|
|
335
|
+
provideHubLoading({
|
|
336
|
+
variant: 'ring',
|
|
337
|
+
color: 'brand',
|
|
338
|
+
backdrop: true,
|
|
339
|
+
ariaLabel: 'Loading, please wait'
|
|
340
|
+
})
|
|
341
|
+
]
|
|
342
|
+
};
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The configuration is backed by the `HUB_LOADING_CONFIG` injection token, which you can
|
|
346
|
+
provide directly if you need to compute it from another dependency. Its unconfigured
|
|
347
|
+
value is exported as `HUB_LOADING_DEFAULT_CONFIG` β the same values listed as each input's
|
|
348
|
+
default below.
|
|
349
|
+
|
|
350
|
+
## π API Reference
|
|
351
|
+
|
|
352
|
+
### `HubLoadingComponent`
|
|
353
|
+
|
|
354
|
+
Selector: `hub-loading`. Standalone, `OnPush`, signal inputs.
|
|
355
|
+
|
|
356
|
+
| Input | Type | Default | Description |
|
|
357
|
+
| --- | --- | --- | --- |
|
|
358
|
+
| `mode` | `'inline' \| 'overlay' \| 'fullscreen'` | `'inline'` | Where the block paints. `overlay` is absolutely positioned over the parent (which needs `position: relative`); `fullscreen` is fixed to the viewport. |
|
|
359
|
+
| `variant` | `'spinner' \| 'dots' \| 'bars' \| 'pulse' \| 'ring'` | `'spinner'` | Which pure-CSS indicator to draw. Ignored when `image` is set. |
|
|
360
|
+
| `image` | `string \| null` | `null` | URL or data URI rendered instead of the built-in indicator. |
|
|
361
|
+
| `imageAnimation` | `'none' \| 'spin' \| 'pulse'` | `'none'` | Animation applied to `image`. |
|
|
362
|
+
| `message` | `string \| null` | `null` | Text rendered under the indicator. |
|
|
363
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Indicator scale; maps onto `--hub-loading-size`, which always overrides it. |
|
|
364
|
+
| `color` | `string \| null` | `null` | Accent colour: semantic name, hex, `rgb()`, `oklch()` or `var(...)`. Resolved with `resolveHubAccent()`. |
|
|
365
|
+
| `backdrop` | `boolean` | `true` | Translucent layer behind the indicator. Applies to `overlay` and `fullscreen` only. Read with `booleanAttribute`, so the bare `backdrop` attribute also works. |
|
|
366
|
+
| `ariaLabel` | `string` | `'Loading'` | Accessible name of the status region. |
|
|
367
|
+
|
|
368
|
+
This component has no outputs. Content projected into it renders below the message.
|
|
369
|
+
|
|
370
|
+
> Every default except `mode`'s comes from the injected `HUB_LOADING_CONFIG`. The values in
|
|
371
|
+
> the table are the unconfigured ones; `provideHubLoading()` re-bases them application-wide.
|
|
372
|
+
|
|
373
|
+
### `HubLoadingService`
|
|
374
|
+
|
|
375
|
+
Injectable (`providedIn: 'root'`). Drives a single fullscreen overlay attached to `document.body`.
|
|
376
|
+
|
|
377
|
+
| Member | Signature | Description |
|
|
378
|
+
| --- | --- | --- |
|
|
379
|
+
| `show` | `(options?: HubLoadingOptions) => void` | Increments the counter and creates the overlay if it is not mounted yet. |
|
|
380
|
+
| `hide` | `() => void` | Decrements the counter; destroys the overlay when it reaches zero. |
|
|
381
|
+
| `hideAll` | `() => void` | Forces the counter to zero and destroys the overlay. |
|
|
382
|
+
| `update` | `(options: HubLoadingOptions) => void` | Applies new options to the visible overlay. |
|
|
383
|
+
| `isLoading` | `Signal<boolean>` | Whether an overlay is currently mounted. |
|
|
384
|
+
|
|
385
|
+
### `provideHubLoading(config?)`
|
|
386
|
+
|
|
387
|
+
Environment provider that registers the defaults every indicator starts from, through the
|
|
388
|
+
`HUB_LOADING_CONFIG` injection token. Omitted keys keep their `HUB_LOADING_DEFAULT_CONFIG`
|
|
389
|
+
value.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
function provideHubLoading(config?: Partial<HubLoadingConfig>): EnvironmentProviders;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### `HubLoadingOptions`
|
|
396
|
+
|
|
397
|
+
The visual options accepted by `show()` and `update()`, and by `provideHubLoading()` as
|
|
398
|
+
application-wide defaults. They mirror the component inputs, minus `mode` β a programmatic
|
|
399
|
+
overlay is always fullscreen.
|
|
400
|
+
|
|
401
|
+
| Option | Type | Description |
|
|
402
|
+
| --- | --- | --- |
|
|
403
|
+
| `variant` | `'spinner' \| 'dots' \| 'bars' \| 'pulse' \| 'ring'` | Indicator to draw. |
|
|
404
|
+
| `image` | `string \| null` | Image or logo replacing the indicator. |
|
|
405
|
+
| `imageAnimation` | `'none' \| 'spin' \| 'pulse'` | Animation applied to `image`. |
|
|
406
|
+
| `message` | `string \| null` | Text under the indicator. |
|
|
407
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | Indicator scale. |
|
|
408
|
+
| `color` | `string \| null` | Accent colour. |
|
|
409
|
+
| `backdrop` | `boolean` | Translucent layer behind the indicator. |
|
|
410
|
+
| `ariaLabel` | `string` | Accessible name of the status region. |
|
|
411
|
+
|
|
412
|
+
`HubLoadingConfig` is the same shape with every member required β it is what the injection
|
|
413
|
+
token holds once resolved.
|
|
414
|
+
|
|
415
|
+
### Exported types
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
type HubLoadingMode = 'inline' | 'overlay' | 'fullscreen';
|
|
419
|
+
type HubLoadingVariant = 'spinner' | 'dots' | 'bars' | 'pulse' | 'ring';
|
|
420
|
+
type HubLoadingSize = 'sm' | 'md' | 'lg';
|
|
421
|
+
type HubLoadingImageAnimation = 'none' | 'spin' | 'pulse';
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Also exported: `HubLoadingOptions`, `HubLoadingConfig`, `HUB_LOADING_CONFIG` and
|
|
425
|
+
`HUB_LOADING_DEFAULT_CONFIG`.
|
|
426
|
+
|
|
427
|
+
## π¨ Styling / CSS Variables
|
|
428
|
+
|
|
429
|
+
The component declares its token defaults on `:where(.hub-loading)` β zero specificity, so
|
|
430
|
+
any consumer rule wins β and each one climbs the family's ladder: the semantic `--hub-sys-*`
|
|
431
|
+
layer first, the `--hub-ref-*` primitive next, a literal last. That is why the block already
|
|
432
|
+
matches your theme, and its dark mode, before you override anything. Values with no honest
|
|
433
|
+
counterpart in the design system (an indicator's diameter, a loop period, a blur radius)
|
|
434
|
+
carry a literal rather than borrowing a `sys` token that means something else.
|
|
435
|
+
|
|
436
|
+
Styles are deliberately unencapsulated, so a global stylesheet can retheme the indicator β
|
|
437
|
+
and so the service-mounted overlay, created outside any component's style scope, is painted.
|
|
438
|
+
|
|
439
|
+
| Variable | Default | Description |
|
|
440
|
+
| --- | --- | --- |
|
|
441
|
+
| `--hub-loading-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Indicator colour. What the `color` input writes into. |
|
|
442
|
+
| `--hub-loading-size` | `2.5rem` | Indicator box size. What the `size` input maps onto. |
|
|
443
|
+
| `--hub-loading-thickness` | `calc(var(--hub-ref-border-width, 1px) * 3)` | Stroke width of the `spinner` and `ring` indicators. |
|
|
444
|
+
| `--hub-loading-speed` | `0.9s` | Duration of one animation cycle, for the indicators and the image animations. |
|
|
445
|
+
| `--hub-loading-gap` | `var(--hub-sys-gap-2, var(--hub-ref-space-2, 0.5rem))` | Space between indicator, message and projected content. |
|
|
446
|
+
| `--hub-loading-text-color` | `var(--hub-sys-text-primary, var(--hub-ref-color-gray-900, #212529))` | Message colour. |
|
|
447
|
+
| `--hub-loading-font-size` | `var(--hub-ref-font-size-sm, 0.875rem)` | Message font size. |
|
|
448
|
+
| `--hub-loading-backdrop-bg` | `color-mix(in srgb, var(--hub-sys-surface-page, #fff) 72%, transparent)` | Backdrop background in `overlay` / `fullscreen`. Follows the theme's own page surface, so the scrim is a white veil on light themes and a dark one on dark themes. |
|
|
449
|
+
| `--hub-loading-backdrop-blur` | `2px` | Backdrop blur radius. |
|
|
450
|
+
| `--hub-loading-z-index` | `var(--hub-sys-zindex-modal, 1055)` | Stack order of the `fullscreen` layer. |
|
|
451
|
+
| `--hub-loading-image-size` | `var(--hub-loading-size)` | Rendered size of the `image` asset β it follows the indicator size until you say otherwise. |
|
|
452
|
+
|
|
453
|
+
```css
|
|
454
|
+
hub-loading {
|
|
455
|
+
--hub-loading-accent: var(--hub-sys-color-brand);
|
|
456
|
+
--hub-loading-size: 3rem;
|
|
457
|
+
--hub-loading-speed: 1.2s;
|
|
458
|
+
--hub-loading-backdrop-blur: 4px;
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### The `hub-loading-theme()` Sass mixin
|
|
463
|
+
|
|
464
|
+
For Sass projects, `hub-loading-theme()` re-skins the block in a single include. Every
|
|
465
|
+
parameter is optional and defaults to `null`, so only the ones you pass are emitted as
|
|
466
|
+
`--hub-loading-*` overrides β the rest keep the component defaults. It is token-based and
|
|
467
|
+
self-contained (no Bootstrap dependency).
|
|
468
|
+
|
|
469
|
+
```scss
|
|
470
|
+
@use 'ng-hub-ui-loading/styles' as hub;
|
|
471
|
+
|
|
472
|
+
.app-shell {
|
|
473
|
+
@include hub.hub-loading-theme(
|
|
474
|
+
$accent: var(--hub-sys-color-brand),
|
|
475
|
+
$size: 3.5rem,
|
|
476
|
+
$speed: 1.2s,
|
|
477
|
+
$backdrop-bg: rgba(15, 23, 42, 0.72),
|
|
478
|
+
$backdrop-blur: 4px
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Available parameters: `$accent`, `$size`, `$thickness`, `$speed`, `$gap`, `$text-color`,
|
|
484
|
+
`$font-size`, `$backdrop-bg`, `$backdrop-blur`, `$z-index`, `$image-size`.
|
|
485
|
+
|
|
486
|
+
### BEM classes
|
|
487
|
+
|
|
488
|
+
The internal structure is stable and addressable, for the cases a token cannot reach:
|
|
489
|
+
|
|
490
|
+
| Class | Element |
|
|
491
|
+
| --- | --- |
|
|
492
|
+
| `.hub-loading` | Host block. |
|
|
493
|
+
| `.hub-loading--inline` Β· `--overlay` Β· `--fullscreen` | Mode modifiers. |
|
|
494
|
+
| `.hub-loading--sm` Β· `--md` Β· `--lg` | Size modifiers; each retunes the size, thickness and font-size tokens. |
|
|
495
|
+
| `.hub-loading--backdrop` | Present only when the scrim is painted (never in `inline` mode). |
|
|
496
|
+
| `.hub-loading__indicator` | The pure-CSS indicator, plus a `--spinner` / `--dots` / `--bars` / `--pulse` / `--ring` modifier. |
|
|
497
|
+
| `.hub-loading__dot` Β· `.hub-loading__bar` | The individual parts of the `dots` and `bars` indicators. |
|
|
498
|
+
| `.hub-loading__image` | The `image` asset, plus `--spin` / `--pulse` when animated. |
|
|
499
|
+
| `.hub-loading__message` | The message text. |
|
|
500
|
+
|
|
501
|
+
## βΏ Accessibility
|
|
502
|
+
|
|
503
|
+
- The block is a **status region**: `role="status"`, `aria-live="polite"` and
|
|
504
|
+
`aria-busy="true"`. A polite live region is announced at the next natural pause, so
|
|
505
|
+
starting a load never interrupts what the user is reading.
|
|
506
|
+
- `ariaLabel` (default `'Loading'`) names that region. Give it something specific when the
|
|
507
|
+
page can have several β `"Loading invoices"` beats a second generic "Loading".
|
|
508
|
+
- Because `message` lives inside the live region, changing it β including through
|
|
509
|
+
`HubLoadingService.update()` β is announced, which is what makes a phased task readable
|
|
510
|
+
without sight.
|
|
511
|
+
- The indicator and the `image` are decorative (`aria-hidden`, empty `alt`), so nothing is
|
|
512
|
+
announced twice. What carries the meaning is `ariaLabel` and the `message`.
|
|
513
|
+
- Render the block only while the work is actually running. A permanently mounted
|
|
514
|
+
`aria-busy="true"` region tells assistive technology the app is forever loading.
|
|
515
|
+
- Under `prefers-reduced-motion: reduce` the animation is **calmed, not frozen** β a frozen
|
|
516
|
+
loader reads as a hung UI. The cycle slows to `2.4s` and every rotation or scale is
|
|
517
|
+
swapped for a plain fade, so nothing spins or jumps.
|
|
518
|
+
|
|
519
|
+
## π₯οΈ Server-Side Rendering
|
|
520
|
+
|
|
521
|
+
- `<hub-loading>` is declarative markup and CSS with no browser API in the render path,
|
|
522
|
+
so it renders on the server like any other component.
|
|
523
|
+
- `HubLoadingService` is safe to call during server rendering: `show()`, `update()`,
|
|
524
|
+
`hide()` and `hideAll()` need no platform guard of their own. Only the DOM mount is
|
|
525
|
+
skipped β the reference counter still runs, so `isLoading` stays truthful and hydration
|
|
526
|
+
finds no orphan overlay markup.
|
|
527
|
+
|
|
528
|
+
## π¦ Peer Dependencies
|
|
529
|
+
|
|
530
|
+
```json
|
|
531
|
+
{
|
|
532
|
+
"@angular/common": ">=21.0.0",
|
|
533
|
+
"@angular/core": ">=21.0.0",
|
|
534
|
+
"ng-hub-ui-utils": ">=22.8.0"
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## π Changelog
|
|
539
|
+
|
|
540
|
+
See [CHANGELOG.md](./CHANGELOG.md) for the full version history, and
|
|
541
|
+
[BREAKING_CHANGES.md](./BREAKING_CHANGES.md) for migration notes.
|
|
542
|
+
|
|
543
|
+
## π€ Contribution
|
|
544
|
+
|
|
545
|
+
Contributions are welcome. Please open an issue to discuss substantial changes before
|
|
546
|
+
submitting a pull request, and make sure to document every library change in `CHANGELOG.md`.
|
|
547
|
+
|
|
548
|
+
## β Support
|
|
549
|
+
|
|
550
|
+
- **Issues**: [GitHub Issues](https://github.com/carlos-morcillo/ng-hub-ui/issues)
|
|
551
|
+
- **Author**: [Carlos Morcillo](https://www.carlosmorcillo.com)
|
|
552
|
+
|
|
553
|
+
## π License
|
|
554
|
+
|
|
555
|
+
MIT Β© [Carlos Morcillo](https://www.carlosmorcillo.com)
|