ng-hub-ui-loading 22.0.0 → 22.1.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/README.md +252 -1
- package/fesm2022/ng-hub-ui-loading.mjs +553 -2
- package/fesm2022/ng-hub-ui-loading.mjs.map +1 -1
- package/package.json +10 -2
- package/styles/_index.scss +1 -0
- package/styles/mixins/_loading-bar-theme.scss +88 -0
- package/types/ng-hub-ui-loading.d.ts +406 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://angular.dev)
|
|
7
7
|
[](LICENSE)
|
|
8
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.
|
|
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. Alongside it, `<hub-loading-bar>` — the thin page-progress strip that sits under the navbar, wired to the router and to `HttpClient`. Zero external dependencies; every colour and dimension is a `--hub-loading-*` CSS custom property.
|
|
10
10
|
|
|
11
11
|
## Documentation and Live Examples
|
|
12
12
|
|
|
@@ -55,6 +55,7 @@ This library is part of the **ng-hub-ui** ecosystem:
|
|
|
55
55
|
- [🎛️ Variants](#️-variants)
|
|
56
56
|
- [🖼️ Image and Branding](#️-image-and-branding)
|
|
57
57
|
- [🧰 Programmatic API](#-programmatic-api)
|
|
58
|
+
- [📊 Page Progress Bar](#-page-progress-bar)
|
|
58
59
|
- [📖 API Reference](#-api-reference)
|
|
59
60
|
- [🎨 Styling / CSS Variables](#-styling--css-variables)
|
|
60
61
|
- [♿ Accessibility](#-accessibility)
|
|
@@ -80,6 +81,8 @@ and that can render in flow, over its own container, or over the whole viewport.
|
|
|
80
81
|
| [`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
82
|
| [`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
|
|
|
84
|
+
> **`<hub-loading-bar>` versus `<hub-progress>`.** They look alike and answer different questions. `hub-progress`, in `ng-hub-ui-metrics`, *displays a value you already know*: it is a data component, and its number is true. `hub-loading-bar` reports that something is happening when nobody knows how long it will take — it invents the number and never lets it reach the end. Use the metrics one for an upload that reports bytes; use this one for the strip under the navbar.
|
|
85
|
+
|
|
83
86
|
The three compose: a skeleton for the list that is arriving, a `<hub-loading mode="overlay">`
|
|
84
87
|
over the panel being refreshed, and a `<hub-progress>` for the upload that reports bytes.
|
|
85
88
|
|
|
@@ -94,6 +97,9 @@ over the panel being refreshed, and a `<hub-progress>` for the upload that repor
|
|
|
94
97
|
- **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
98
|
- **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
99
|
- **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.
|
|
100
|
+
- **Page progress bar** — `<hub-loading-bar>`, the thin strip under the navbar: in flow, attached to a positioned ancestor, or fixed to the viewport at an offset you choose.
|
|
101
|
+
- **Router and HTTP wiring** — `provideHubLoadingBarRouter()` runs the bar for exactly the length of a navigation (including one a guard rejects), `hubLoadingBarInterceptor` for the lifetime of every request, and `withoutHubLoadingBar()` keeps polls and heartbeats out of the count.
|
|
102
|
+
- **A trickle that does not lie** — the bar advances in shrinking steps and stops short of the end, never paints at all for work that finishes inside its grace period, and withholds `aria-valuenow` while the number is invented.
|
|
97
103
|
- **Standalone, `OnPush`, signal inputs** — and SSR-safe: on the server the counter still runs, only the DOM mount is skipped.
|
|
98
104
|
|
|
99
105
|
## 🚀 Quick Start
|
|
@@ -347,6 +353,112 @@ provide directly if you need to compute it from another dependency. Its unconfig
|
|
|
347
353
|
value is exported as `HUB_LOADING_DEFAULT_CONFIG` — the same values listed as each input's
|
|
348
354
|
default below.
|
|
349
355
|
|
|
356
|
+
## 📊 Page Progress Bar
|
|
357
|
+
|
|
358
|
+
`<hub-loading-bar>` is the other half of "something is happening": not *this region is
|
|
359
|
+
busy*, but *the page itself is on its way*. It is the thin strip you already know from
|
|
360
|
+
under a navbar, and it is driven by `HubLoadingBarService`.
|
|
361
|
+
|
|
362
|
+
### The three decisions that make it believable
|
|
363
|
+
|
|
364
|
+
- **It counts its callers.** A navigation and the three requests the page fires on arrival
|
|
365
|
+
are four references. The bar finishes when the last one does, not the first — with a
|
|
366
|
+
boolean, the fastest request would take the bar down while the page was still empty.
|
|
367
|
+
- **It waits before painting anything.** Work that finishes inside `delay` (100 ms by
|
|
368
|
+
default) never shows a bar at all. A cached route that flashes a progress bar for 40 ms
|
|
369
|
+
reads as a glitch, not as speed.
|
|
370
|
+
- **Its trickle never arrives.** Nothing here knows the real percentage, so the bar
|
|
371
|
+
advances in steps that shrink as it fills and stops at `max` (99). Only `complete()` may
|
|
372
|
+
show 100%, because only `complete()` knows it is true.
|
|
373
|
+
|
|
374
|
+
### Placement
|
|
375
|
+
|
|
376
|
+
```html
|
|
377
|
+
<!-- Hanging off the bottom edge of a navbar. position: relative on the navbar is what
|
|
378
|
+
confines the bar to it — exactly the contract of <hub-loading mode="overlay">. -->
|
|
379
|
+
<nav class="navbar" style="position: relative">
|
|
380
|
+
…
|
|
381
|
+
<hub-loading-bar mode="overlay" placement="bottom" />
|
|
382
|
+
</nav>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```html
|
|
386
|
+
<!-- Under a navbar that is itself fixed: pin the bar to the viewport and push it down. -->
|
|
387
|
+
<hub-loading-bar mode="fixed" style="--hub-loading-bar-offset: 56px" />
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```html
|
|
391
|
+
<!-- In the flow. Reserves its own 3px row, so nothing shifts when it appears. -->
|
|
392
|
+
<hub-loading-bar />
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Wiring it to the page
|
|
396
|
+
|
|
397
|
+
The two integrations are opt-in and compose through the counter, so neither needs to know
|
|
398
|
+
about the other.
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
402
|
+
import { provideRouter } from '@angular/router';
|
|
403
|
+
import { hubLoadingBarInterceptor, provideHubLoadingBar, provideHubLoadingBarRouter } from 'ng-hub-ui-loading';
|
|
404
|
+
|
|
405
|
+
bootstrapApplication(AppComponent, {
|
|
406
|
+
providers: [
|
|
407
|
+
provideRouter(routes),
|
|
408
|
+
|
|
409
|
+
// One reference per navigation, released when it settles — including when a guard
|
|
410
|
+
// rejects it or an error ends it, the two cases that normally strand a bar.
|
|
411
|
+
provideHubLoadingBarRouter(),
|
|
412
|
+
|
|
413
|
+
// One reference per request. finalize() balances it on success, on error and on
|
|
414
|
+
// cancellation alike, so a cancelled typeahead cannot leave the bar at 90%.
|
|
415
|
+
provideHttpClient(withInterceptors([hubLoadingBarInterceptor])),
|
|
416
|
+
|
|
417
|
+
provideHubLoadingBar({ color: 'primary', delay: 120 })
|
|
418
|
+
]
|
|
419
|
+
});
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Without the interceptor the bar finishes the moment the navigation settles — which is when
|
|
423
|
+
the component is created, not when its data arrives. With both, it spans the whole wait.
|
|
424
|
+
|
|
425
|
+
Keep the requests the reader never asked about out of the count, or the bar never finishes:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
this.http.get('/api/heartbeat', { context: withoutHubLoadingBar() });
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Driving it by hand
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
private readonly bar = inject(HubLoadingBarService);
|
|
435
|
+
|
|
436
|
+
async import(): Promise<void> {
|
|
437
|
+
this.bar.start();
|
|
438
|
+
try {
|
|
439
|
+
await this.api.import();
|
|
440
|
+
} finally {
|
|
441
|
+
this.bar.complete(); // every start() needs exactly one complete()
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
When the percentage is real, bind it instead and the bar stops following the service:
|
|
447
|
+
|
|
448
|
+
```html
|
|
449
|
+
<!-- A number drives the bar and is published as aria-valuenow -->
|
|
450
|
+
<hub-loading-bar [progress]="uploaded()" color="success" />
|
|
451
|
+
|
|
452
|
+
<!-- null hides it; leaving the input unbound hands the bar back to the service -->
|
|
453
|
+
<hub-loading-bar [progress]="null" />
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
And when there is no percentage worth inventing, sweep instead of filling:
|
|
457
|
+
|
|
458
|
+
```html
|
|
459
|
+
<hub-loading-bar indeterminate [progress]="100" />
|
|
460
|
+
```
|
|
461
|
+
|
|
350
462
|
## 📖 API Reference
|
|
351
463
|
|
|
352
464
|
### `HubLoadingComponent`
|
|
@@ -412,6 +524,86 @@ overlay is always fullscreen.
|
|
|
412
524
|
`HubLoadingConfig` is the same shape with every member required — it is what the injection
|
|
413
525
|
token holds once resolved.
|
|
414
526
|
|
|
527
|
+
### `HubLoadingBarComponent`
|
|
528
|
+
|
|
529
|
+
Selector: `hub-loading-bar`. Standalone, `OnPush`, signal inputs.
|
|
530
|
+
|
|
531
|
+
| Input | Type | Default | Description |
|
|
532
|
+
| --- | --- | --- | --- |
|
|
533
|
+
| `mode` | `'inline' \| 'overlay' \| 'fixed'` | `'inline'` | Where the strip sits. `inline` reserves its own row in the flow; `overlay` is absolutely positioned against the nearest positioned ancestor; `fixed` is pinned to the viewport at `--hub-loading-bar-offset`. |
|
|
534
|
+
| `placement` | `'top' \| 'bottom'` | `'top'` | Edge the `overlay` and `fixed` modes attach to. `inline` ignores it. |
|
|
535
|
+
| `progress` | `number \| null \| undefined` | `undefined` | Three meanings. Unbound: follow `HubLoadingBarService`. A number (0–100): drive the bar directly, and publish it as `aria-valuenow`. `null`: hide the bar. |
|
|
536
|
+
| `indeterminate` | `boolean` | `false` | Sweep a fragment across instead of filling. Read with `booleanAttribute`, so the bare attribute works. |
|
|
537
|
+
| `glow` | `boolean` | `true` | Soft glow trailing the leading edge. |
|
|
538
|
+
| `color` | `string \| null` | `null` | Accent for the fill: semantic name, hex, `oklch()` or `var(...)`. Resolved with `resolveHubAccent()`. |
|
|
539
|
+
| `ariaLabel` | `string` | `'Loading'` | Accessible name of the progressbar. |
|
|
540
|
+
|
|
541
|
+
This component has no outputs and projects no content.
|
|
542
|
+
|
|
543
|
+
> Every default except `mode`'s and `placement`'s comes from the injected
|
|
544
|
+
> `HUB_LOADING_BAR_CONFIG`; `provideHubLoadingBar()` re-bases them application-wide.
|
|
545
|
+
|
|
546
|
+
### `HubLoadingBarService`
|
|
547
|
+
|
|
548
|
+
Injectable (`providedIn: 'root'`). Owns the shared page-progress state that every unbound
|
|
549
|
+
`<hub-loading-bar>` renders. Provide it on a component instead to give one bar its own
|
|
550
|
+
state.
|
|
551
|
+
|
|
552
|
+
| Member | Signature | Description |
|
|
553
|
+
| --- | --- | --- |
|
|
554
|
+
| `start` | `() => void` | Registers one caller. The first begins a cycle — after the grace period, not immediately. |
|
|
555
|
+
| `complete` | `() => void` | Retires one caller. At zero the bar runs to 100% and fades, or disappears unseen if it never got painted. |
|
|
556
|
+
| `completeAll` | `() => void` | Drops every pending caller and completes the bar at once. |
|
|
557
|
+
| `set` | `(value: number) => void` | Moves the bar to an exact value and reveals it without waiting out the grace period. Clamped to 0–100, not to `max`. |
|
|
558
|
+
| `inc` | `(amount?: number) => void` | Advances the bar and reveals it. Without an amount the configured trickle curve decides. Capped at `max`. |
|
|
559
|
+
| `reset` | `() => void` | Cancels everything: no completion animation, no pending callers, nothing on screen. |
|
|
560
|
+
| `progress` | `Signal<number>` | Current fill, 0–100. |
|
|
561
|
+
| `isActive` | `Signal<boolean>` | Whether any caller is still waiting — true even during the grace period. |
|
|
562
|
+
| `isVisible` | `Signal<boolean>` | Whether the bar is actually painted — false during the grace period, still true through the completion tail. |
|
|
563
|
+
|
|
564
|
+
### `provideHubLoadingBar(config?)`
|
|
565
|
+
|
|
566
|
+
Environment provider registering the defaults the bar starts from, through the
|
|
567
|
+
`HUB_LOADING_BAR_CONFIG` injection token. Omitted keys keep their
|
|
568
|
+
`HUB_LOADING_BAR_DEFAULT_CONFIG` value.
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
function provideHubLoadingBar(config?: Partial<HubLoadingBarConfig>): EnvironmentProviders;
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### `HubLoadingBarConfig`
|
|
575
|
+
|
|
576
|
+
| Key | Type | Default | Description |
|
|
577
|
+
| --- | --- | --- | --- |
|
|
578
|
+
| `min` | `number` | `8` | Value the bar jumps to when it appears. Never zero — an empty bar reads as a bar that is not working. |
|
|
579
|
+
| `max` | `number` | `99` | Ceiling the trickle may not cross, so it cannot promise an ending it does not know about. |
|
|
580
|
+
| `trickleSpeed` | `number` | `250` | Milliseconds between trickle ticks. |
|
|
581
|
+
| `trickle` | `boolean` | `true` | Whether the bar advances on its own while it waits. |
|
|
582
|
+
| `trickleFn` | `(progress: number) => number` | `hubLoadingBarTrickle` | Step function. The exported default returns 10 / 4 / 2 / 0.5 as the bar fills. |
|
|
583
|
+
| `delay` | `number` | `100` | Grace period before anything is painted. Work finishing inside it shows no bar. `0` reveals the bar synchronously, so work that settles within its own task is still shown. |
|
|
584
|
+
| `completeDelay` | `number` | `300` | How long the completed bar stays at 100% before fading. Wants to be at least `--hub-loading-bar-speed`. |
|
|
585
|
+
| `color` | `string \| null` | `null` | Default accent. |
|
|
586
|
+
| `glow` | `boolean` | `true` | Default glow. |
|
|
587
|
+
| `ariaLabel` | `string` | `'Loading'` | Default accessible name. |
|
|
588
|
+
|
|
589
|
+
### `provideHubLoadingBarRouter()`
|
|
590
|
+
|
|
591
|
+
Environment provider that runs the bar for exactly the length of each navigation, including
|
|
592
|
+
one a guard cancels and one an error ends. Navigations are tracked with a flag rather than
|
|
593
|
+
by pairing events one for one, so a `NavigationStart` missed during bootstrap cannot leave
|
|
594
|
+
an unmatched `complete()` behind.
|
|
595
|
+
|
|
596
|
+
Requires `@angular/router`, declared as an **optional** peer dependency: nothing else in the
|
|
597
|
+
package touches it.
|
|
598
|
+
|
|
599
|
+
### `hubLoadingBarInterceptor` and `withoutHubLoadingBar()`
|
|
600
|
+
|
|
601
|
+
A functional `HttpInterceptorFn` that holds one reference for the lifetime of every request,
|
|
602
|
+
balanced in `finalize` so an error or a cancellation releases it too. `withoutHubLoadingBar()`
|
|
603
|
+
builds the `HttpContext` that takes one request out of the count — reach for it on anything
|
|
604
|
+
the reader did not ask for, or the bar never finishes. `HUB_LOADING_BAR_SKIP` is the
|
|
605
|
+
underlying `HttpContextToken`.
|
|
606
|
+
|
|
415
607
|
### Exported types
|
|
416
608
|
|
|
417
609
|
```typescript
|
|
@@ -483,6 +675,40 @@ self-contained (no Bootstrap dependency).
|
|
|
483
675
|
Available parameters: `$accent`, `$size`, `$thickness`, `$speed`, `$gap`, `$text-color`,
|
|
484
676
|
`$font-size`, `$backdrop-bg`, `$backdrop-blur`, `$z-index`, `$image-size`.
|
|
485
677
|
|
|
678
|
+
### The loading bar's variables
|
|
679
|
+
|
|
680
|
+
Declared on `:where(.hub-loading-bar)`, same zero-specificity contract. The two durations
|
|
681
|
+
are literals rather than `--hub-sys-transition-*`: the fill has to arrive roughly as the
|
|
682
|
+
next trickle tick lands, so it is coupled to `trickleSpeed`, and borrowing the application's
|
|
683
|
+
transition scale would let a slow theme leave the bar a tick behind the number it is drawing.
|
|
684
|
+
|
|
685
|
+
| Variable | Default | Description |
|
|
686
|
+
| --- | --- | --- |
|
|
687
|
+
| `--hub-loading-bar-accent` | `var(--hub-sys-color-primary, #0d6efd)` | Fill colour. What the `color` input writes into. |
|
|
688
|
+
| `--hub-loading-bar-height` | `3px` | Thickness of the strip. |
|
|
689
|
+
| `--hub-loading-bar-track-bg` | `transparent` | Unfilled track. Transparent so an idle bar draws no permanent line under the navbar. |
|
|
690
|
+
| `--hub-loading-bar-radius` | `0` | Corner radius of the strip and its fill. |
|
|
691
|
+
| `--hub-loading-bar-speed` | `200ms` | How long the fill takes to catch up with a new value. |
|
|
692
|
+
| `--hub-loading-bar-fade` | `300ms` | Fade in and out of the whole strip. |
|
|
693
|
+
| `--hub-loading-bar-easing` | `linear` | Easing of the fill. Linear reads as steady progress rather than as a flourish. |
|
|
694
|
+
| `--hub-loading-bar-glow-color` | `var(--hub-loading-bar-accent)` | Colour of the glow at the leading edge. |
|
|
695
|
+
| `--hub-loading-bar-glow-blur` | `10px` | Blur radius of that glow. |
|
|
696
|
+
| `--hub-loading-bar-glow-spread` | `1px` | Spread radius of that glow. |
|
|
697
|
+
| `--hub-loading-bar-indeterminate-speed` | `1.6s` | Period of one `indeterminate` sweep. Calmed under `prefers-reduced-motion`. |
|
|
698
|
+
| `--hub-loading-bar-offset` | `0px` | Distance from the edge the `overlay` and `fixed` modes attach to — how far under a navbar the bar hangs. |
|
|
699
|
+
| `--hub-loading-bar-z-index` | `var(--hub-sys-zindex-sticky, 1020)` | Stack order of the positioned modes. Chrome level, deliberately below dialogs and toasts. |
|
|
700
|
+
|
|
701
|
+
```css
|
|
702
|
+
hub-loading-bar {
|
|
703
|
+
--hub-loading-bar-accent: var(--hub-sys-color-brand);
|
|
704
|
+
--hub-loading-bar-height: 2px;
|
|
705
|
+
--hub-loading-bar-offset: 56px;
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
`hub-loading-bar-theme()` is the matching Sass mixin, with the same optional-parameter
|
|
710
|
+
contract as `hub-loading-theme()`.
|
|
711
|
+
|
|
486
712
|
### BEM classes
|
|
487
713
|
|
|
488
714
|
The internal structure is stable and addressable, for the cases a token cannot reach:
|
|
@@ -516,6 +742,19 @@ The internal structure is stable and addressable, for the cases a token cannot r
|
|
|
516
742
|
loader reads as a hung UI. The cycle slows to `2.4s` and every rotation or scale is
|
|
517
743
|
swapped for a plain fade, so nothing spins or jumps.
|
|
518
744
|
|
|
745
|
+
### The loading bar
|
|
746
|
+
|
|
747
|
+
- The bar is a `role="progressbar"` with static `aria-valuemin` / `aria-valuemax`, named by
|
|
748
|
+
`ariaLabel`, and `aria-hidden` while it is not painted.
|
|
749
|
+
- **`aria-valuenow` is withheld unless the value is real.** While the service trickles, the
|
|
750
|
+
number on screen is invented — nothing knows the real percentage of a page load — and a
|
|
751
|
+
progressbar without `aria-valuenow` is exactly how ARIA spells "indeterminate". Announcing
|
|
752
|
+
a made-up "43%" would be worse than announcing nothing. The value appears only when a
|
|
753
|
+
caller has bound one, and is withheld again under `indeterminate`.
|
|
754
|
+
- Under `prefers-reduced-motion: reduce` the `indeterminate` sweep slows considerably. The
|
|
755
|
+
determinate fill is left alone on purpose: it is not decoration, it is the value being
|
|
756
|
+
reported, and freezing it would leave a bar that says nothing.
|
|
757
|
+
|
|
519
758
|
## 🖥️ Server-Side Rendering
|
|
520
759
|
|
|
521
760
|
- `<hub-loading>` is declarative markup and CSS with no browser API in the render path,
|
|
@@ -525,16 +764,28 @@ The internal structure is stable and addressable, for the cases a token cannot r
|
|
|
525
764
|
skipped — the reference counter still runs, so `isLoading` stays truthful and hydration
|
|
526
765
|
finds no orphan overlay markup.
|
|
527
766
|
|
|
767
|
+
- `HubLoadingBarService` creates **no timer at all** on the server. This matters more than
|
|
768
|
+
it looks: a repeating interval inside Angular's zone would keep `ApplicationRef.isStable`
|
|
769
|
+
false forever and hang the render. The counter still runs, so `isActive` stays truthful,
|
|
770
|
+
and the bar is never painted server-side, so there is nothing to mismatch on hydration.
|
|
771
|
+
In the browser the timers run outside the zone, where signal writes still schedule change
|
|
772
|
+
detection but a 250 ms tick does not drive a whole application's worth of it.
|
|
773
|
+
|
|
528
774
|
## 📦 Peer Dependencies
|
|
529
775
|
|
|
530
776
|
```json
|
|
531
777
|
{
|
|
532
778
|
"@angular/common": ">=21.0.0",
|
|
533
779
|
"@angular/core": ">=21.0.0",
|
|
780
|
+
"@angular/router": ">=21.0.0",
|
|
534
781
|
"ng-hub-ui-utils": ">=22.8.0"
|
|
535
782
|
}
|
|
536
783
|
```
|
|
537
784
|
|
|
785
|
+
`@angular/router` is **optional** (`peerDependenciesMeta`). Only
|
|
786
|
+
`provideHubLoadingBarRouter()` touches it; everything else in the package works without a
|
|
787
|
+
router.
|
|
788
|
+
|
|
538
789
|
## 📊 Changelog
|
|
539
790
|
|
|
540
791
|
See [CHANGELOG.md](./CHANGELOG.md) for the full version history, and
|