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.
@@ -1,5 +1,6 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { Signal, InjectionToken, EnvironmentProviders } from '@angular/core';
3
+ import { HttpContextToken, HttpInterceptorFn, HttpContext } from '@angular/common/http';
3
4
 
4
5
  /**
5
6
  * Where the indicator is placed relative to the document.
@@ -248,5 +249,408 @@ declare const HUB_LOADING_CONFIG: InjectionToken<HubLoadingConfig>;
248
249
  */
249
250
  declare function provideHubLoading(config?: Partial<HubLoadingConfig>): EnvironmentProviders;
250
251
 
251
- export { HUB_LOADING_CONFIG, HUB_LOADING_DEFAULT_CONFIG, HubLoadingComponent, HubLoadingService, provideHubLoading };
252
- export type { HubLoadingConfig, HubLoadingImageAnimation, HubLoadingMode, HubLoadingOptions, HubLoadingSize, HubLoadingVariant };
252
+ /**
253
+ * Where the bar is placed relative to the document.
254
+ *
255
+ * `inline` takes part in normal flow and reserves its own strip, so the page never
256
+ * shifts when the bar appears; `overlay` is absolutely positioned against the nearest
257
+ * positioned ancestor — the classic "hanging off the bottom edge of a navbar" case, and
258
+ * the consumer owns that containing block; `fixed` pins the bar to the viewport, offset
259
+ * by `--hub-loading-bar-offset` so it can sit under a navbar that is itself fixed.
260
+ */
261
+ type HubLoadingBarMode = 'inline' | 'overlay' | 'fixed';
262
+ /** Which edge the `overlay` and `fixed` modes attach to; ignored by `inline`. */
263
+ type HubLoadingBarPlacement = 'top' | 'bottom';
264
+ /**
265
+ * Function deciding how much to advance on each trickle tick.
266
+ *
267
+ * Page loads have no honest percentage — the browser does not know how many bytes are
268
+ * left, and a resolver knows even less. The bar therefore advances in steps that shrink
269
+ * as it fills, so it always seems to be moving while never reaching the end on its own.
270
+ *
271
+ * @param progress - Current value, 0–100.
272
+ * @returns Amount to add, in the same 0–100 scale. Return `0` to stall.
273
+ */
274
+ type HubLoadingBarTrickle = (progress: number) => number;
275
+ /**
276
+ * Application-wide defaults for the loading bar.
277
+ *
278
+ * Fully resolved (no optional members) so the component, the service and the
279
+ * integrations read a value without re-implementing the fallback chain at each call site.
280
+ * Durations are milliseconds; progress values are on a 0–100 scale.
281
+ */
282
+ interface HubLoadingBarConfig {
283
+ /**
284
+ * Value the bar jumps to the instant it appears.
285
+ *
286
+ * Never zero: a bar that starts empty reads as a bar that is not working. The jump is
287
+ * the acknowledgement that the request was received.
288
+ */
289
+ min: number;
290
+ /**
291
+ * Ceiling the trickle may not cross.
292
+ *
293
+ * Below 100 on purpose. Only {@link HubLoadingBarService.complete} knows the work is
294
+ * actually done, so a trickle that reached 100 would promise an ending it cannot
295
+ * deliver and then sit there, full and lying.
296
+ */
297
+ max: number;
298
+ /** Milliseconds between trickle ticks. */
299
+ trickleSpeed: number;
300
+ /** Whether the bar advances on its own while it waits. */
301
+ trickle: boolean;
302
+ /** Step function driving each tick; see {@link HubLoadingBarTrickle}. */
303
+ trickleFn: HubLoadingBarTrickle;
304
+ /**
305
+ * Grace period before the bar is painted at all.
306
+ *
307
+ * Work that finishes inside this window never shows a bar. A 40 ms navigation that
308
+ * flashes a progress bar looks broken, not fast — this is what keeps a cached route
309
+ * from flickering.
310
+ *
311
+ * `0` reveals the bar synchronously rather than on the next macrotask, so a caller who
312
+ * opted out of the grace period still sees it for work that settles within its own task.
313
+ */
314
+ delay: number;
315
+ /**
316
+ * How long the completed bar stays on screen at 100% before it fades away.
317
+ *
318
+ * Wants to be at least `--hub-loading-bar-speed`, or the bar is dismissed before its
319
+ * own fill animation has arrived at the end.
320
+ */
321
+ completeDelay: number;
322
+ /** Default accent: a semantic name, a colour literal or a `var(...)` reference. */
323
+ color: string | null;
324
+ /** Whether the leading edge carries the soft glow that suggests motion. */
325
+ glow: boolean;
326
+ /** Accessible name announced through `role="progressbar"`. */
327
+ ariaLabel: string;
328
+ }
329
+
330
+ /**
331
+ * The thin strip that reports page-level progress — the bar under the navbar.
332
+ *
333
+ * By default the component draws whatever {@link HubLoadingBarService} is doing, which is
334
+ * what makes a single `<hub-loading-bar />` in the shell enough for the whole
335
+ * application: the router integration and the HTTP interceptor drive the service, and
336
+ * this element follows. Bind `progress` to take it over instead, for a bar reporting one
337
+ * known quantity — an upload, an import — independently of everything else.
338
+ *
339
+ * The `progress` input therefore has three meanings, and the difference matters:
340
+ * unbound (`undefined`) follows the service; a number drives the bar directly; `null`
341
+ * hides it. This mirrors how `HubLoadingService` already reads `undefined` as "leave it
342
+ * alone" and `null` as "clear it".
343
+ *
344
+ * Note the deliberate gap in the accessibility contract. While the service is trickling,
345
+ * the number on screen is invented — nothing knows the real percentage of a page load —
346
+ * so `aria-valuenow` is withheld, which is exactly how ARIA spells an indeterminate
347
+ * progressbar. Announcing a made-up "43%" would be worse than announcing nothing. The
348
+ * value is published only when a caller has bound a real one.
349
+ *
350
+ * Styles are unencapsulated, like the rest of the library, so a consumer can retheme the
351
+ * bar from a global stylesheet.
352
+ *
353
+ * @example
354
+ * ```html
355
+ * <!-- Hanging off the bottom edge of a navbar, driven by the service -->
356
+ * <nav class="navbar position-relative">
357
+ * …
358
+ * <hub-loading-bar mode="overlay" placement="bottom" />
359
+ * </nav>
360
+ *
361
+ * <!-- Reporting one known quantity -->
362
+ * <hub-loading-bar [progress]="uploaded()" color="success" />
363
+ * ```
364
+ */
365
+ declare class HubLoadingBarComponent {
366
+ /** Application-wide defaults; also the source of every input's default value. */
367
+ private readonly config;
368
+ /** The shared page-level state this bar renders unless `progress` is bound. */
369
+ private readonly service;
370
+ /**
371
+ * Placement of the strip. `overlay` needs a positioned ancestor to attach to;
372
+ * `fixed` pins it to the viewport at `--hub-loading-bar-offset`.
373
+ */
374
+ readonly mode: _angular_core.InputSignal<HubLoadingBarMode>;
375
+ /** Edge the `overlay` and `fixed` modes attach to; `inline` ignores it. */
376
+ readonly placement: _angular_core.InputSignal<HubLoadingBarPlacement>;
377
+ /**
378
+ * Takes the bar over. Leave it unbound to follow {@link HubLoadingBarService}; bind a
379
+ * number (0–100) to drive it directly, or `null` to hide it.
380
+ */
381
+ readonly progress: _angular_core.InputSignal<number | null | undefined>;
382
+ /**
383
+ * Sweeps a fragment back and forth instead of filling.
384
+ *
385
+ * The honest choice when there is no percentage worth inventing — a long stream, a
386
+ * job with no reported stages.
387
+ */
388
+ readonly indeterminate: _angular_core.InputSignalWithTransform<boolean, unknown>;
389
+ /** Soft glow trailing the leading edge, which is what reads as movement. */
390
+ readonly glow: _angular_core.InputSignalWithTransform<boolean, unknown>;
391
+ /**
392
+ * Accent for the fill. Accepts a semantic name (`primary`), a CSS colour literal or a
393
+ * `var(...)` reference — normalised by `resolveHubAccent()` into the single
394
+ * `--hub-loading-bar-accent` slot.
395
+ */
396
+ readonly color: _angular_core.InputSignal<string | null>;
397
+ /** Accessible name for the host's `role="progressbar"`. */
398
+ readonly ariaLabel: _angular_core.InputSignal<string>;
399
+ /** Whether a caller has taken the bar over rather than following the service. */
400
+ private readonly _manual;
401
+ /** Current fill, from whichever source is in charge. */
402
+ private readonly _value;
403
+ /** Whether the strip is painted at all. */
404
+ protected readonly _visible: _angular_core.Signal<boolean>;
405
+ /** Fill as a CSS length, consumed by the stylesheet's single runtime slot. */
406
+ protected readonly _fill: _angular_core.Signal<string>;
407
+ /**
408
+ * The value published to assistive technology: only ever a real one.
409
+ *
410
+ * `null` removes the attribute, which is how ARIA marks a progressbar indeterminate —
411
+ * the correct answer both while the service trickles an invented number and while the
412
+ * `indeterminate` sweep is running. A bar that is not painted publishes nothing either:
413
+ * the host is `aria-hidden` by then, so a stale value would only ever be misleading.
414
+ */
415
+ protected readonly _announcedValue: _angular_core.Signal<number | null>;
416
+ /** Mode and placement modifiers; kept as one binding so neither can drop the other. */
417
+ protected readonly _modifierClasses: _angular_core.Signal<string>;
418
+ /**
419
+ * Single accent slot consumed by the stylesheet. `null` leaves the binding off
420
+ * entirely, so the token's own cascade default stays in effect.
421
+ */
422
+ protected readonly _accent: _angular_core.Signal<string | null>;
423
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HubLoadingBarComponent, never>;
424
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HubLoadingBarComponent, "hub-loading-bar", never, { "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "progress": { "alias": "progress"; "required": false; "isSignal": true; }; "indeterminate": { "alias": "indeterminate"; "required": false; "isSignal": true; }; "glow": { "alias": "glow"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
425
+ }
426
+
427
+ /**
428
+ * Drives the page-level loading bar: the thin strip under the navbar that says
429
+ * "something is on its way" without pretending to know how long it will take.
430
+ *
431
+ * Three decisions shape the whole service, and each exists because the naive version is
432
+ * worse:
433
+ *
434
+ * - **Reference counting, not a boolean.** A route change and the three requests its
435
+ * page fires are four independent callers. With a boolean, the first one to finish
436
+ * would take the bar down while the other three were still working. The bar completes
437
+ * when the count reaches zero, and {@link completeAll} is the escape hatch for a caller
438
+ * that never balanced its `start()`.
439
+ * - **A grace period before anything is painted.** Work that finishes within `delay`
440
+ * never shows a bar at all. A cached route that flashes a progress bar for 40 ms reads
441
+ * as a glitch, not as speed.
442
+ * - **A trickle that never reaches the end.** Nothing here knows the real percentage, so
443
+ * the bar advances in shrinking steps and stops at `max`. Only `complete()` may show
444
+ * 100, because only `complete()` knows it is true.
445
+ *
446
+ * Timers run outside the Angular zone. Inside it, a 250 ms interval would trigger change
447
+ * detection for the whole application on every tick and — far worse — would keep
448
+ * `ApplicationRef.isStable` false forever, which hangs server-side rendering. The state
449
+ * is signals, so change detection is still scheduled correctly when a value actually
450
+ * changes. On the server no timer is created at all: the counter stays truthful and the
451
+ * rendered HTML carries no bar to mismatch on hydration.
452
+ *
453
+ * @example
454
+ * ```typescript
455
+ * private readonly bar = inject(HubLoadingBarService);
456
+ *
457
+ * async import(): Promise<void> {
458
+ * this.bar.start();
459
+ * try {
460
+ * await this.api.import();
461
+ * } finally {
462
+ * this.bar.complete();
463
+ * }
464
+ * }
465
+ * ```
466
+ */
467
+ declare class HubLoadingBarService {
468
+ private readonly config;
469
+ private readonly zone;
470
+ private readonly platformId;
471
+ /** Number of callers currently waiting on something. */
472
+ private readonly pending;
473
+ /** Current fill, 0–100. */
474
+ private readonly _progress;
475
+ /** Whether the bar is painted right now; false during the grace period. */
476
+ private readonly _visible;
477
+ /** Handles of the running timers, so each can be cancelled independently. */
478
+ private readonly timers;
479
+ /** Current fill, 0–100. Bound by `<hub-loading-bar>`; safe to read during SSR. */
480
+ readonly progress: Signal<number>;
481
+ /**
482
+ * Whether the bar is on screen. Differs from {@link isActive} at both ends of a cycle:
483
+ * false while the grace period runs, and still true during the completion tail.
484
+ */
485
+ readonly isVisible: Signal<boolean>;
486
+ /**
487
+ * Whether any caller is still waiting — the honest "is the page loading?" question,
488
+ * regardless of whether the bar has decided to show itself yet.
489
+ */
490
+ readonly isActive: Signal<boolean>;
491
+ constructor();
492
+ /**
493
+ * Registers one caller. The first one starts a cycle; the rest simply join the count.
494
+ */
495
+ start(): void;
496
+ /**
497
+ * Retires one caller. Once none are left the bar runs to 100% and fades away — or, if
498
+ * the grace period swallowed the whole operation, disappears without ever having been
499
+ * seen.
500
+ */
501
+ complete(): void;
502
+ /** Drops every pending caller and completes the bar immediately. */
503
+ completeAll(): void;
504
+ /**
505
+ * Moves the bar to an exact value and reveals it, bypassing the grace period.
506
+ *
507
+ * For work whose real percentage is known — a file upload, a paged import. The value
508
+ * is not capped at `max`, because a caller reporting a true 100 is not guessing;
509
+ * finishing the cycle is still {@link complete}'s job.
510
+ *
511
+ * @param value - Target fill, 0–100. Values outside the range are clamped.
512
+ */
513
+ set(value: number): void;
514
+ /**
515
+ * Advances the bar and reveals it, bypassing the grace period.
516
+ *
517
+ * @param amount - Step to add. Omitted, the configured trickle curve decides, which is
518
+ * what makes an unknown wait keep moving without ever arriving.
519
+ */
520
+ inc(amount?: number): void;
521
+ /**
522
+ * Cancels everything at once: no completion animation, no pending callers, nothing on
523
+ * screen. For an error handler that wants the bar gone rather than finished.
524
+ */
525
+ reset(): void;
526
+ /** Paints the bar and starts the trickle, if a caller is waiting on one. */
527
+ private reveal;
528
+ /**
529
+ * Runs the completion, shared by {@link complete} and {@link completeAll}.
530
+ *
531
+ * The fill is deliberately left at 100 once the bar is hidden. `start()` rewinds it
532
+ * while the bar is invisible, where the stylesheet suppresses the transition, so the
533
+ * next cycle begins from `min` without the previous one being seen to unwind.
534
+ */
535
+ private finish;
536
+ /** Starts the trickle interval, unless it is disabled, already running or unneeded. */
537
+ private startTrickling;
538
+ /** Adds one step, capped at `max` so the trickle can never claim to be finished. */
539
+ private advance;
540
+ /** Schedules a one-shot timer outside the Angular zone, replacing any previous one. */
541
+ private schedule;
542
+ /** Cancels one timer if it is running. Safe to call for a timer that is not. */
543
+ private clearTimer;
544
+ /** Cancels every timer; used by `reset()` and on injector teardown. */
545
+ private clearAllTimers;
546
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<HubLoadingBarService, never>;
547
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<HubLoadingBarService>;
548
+ }
549
+
550
+ /**
551
+ * Default trickle curve: large steps early, then progressively smaller ones.
552
+ *
553
+ * The shape is what makes an invented number believable. Moving fast at the start
554
+ * matches the part of a page load that really is fast (the request goes out, the shell
555
+ * responds); slowing near the end matches the part nobody can predict, and leaves room
556
+ * for `complete()` to arrive without the bar having to jump backwards.
557
+ *
558
+ * Exported so a consumer can wrap it rather than rewrite it.
559
+ *
560
+ * @param progress - Current value, 0–100.
561
+ * @returns Amount to add on this tick.
562
+ */
563
+ declare function hubLoadingBarTrickle(progress: number): number;
564
+ /**
565
+ * Neutral defaults applied when an application provides no configuration.
566
+ *
567
+ * These are the values documented as each input's default, so overriding the token
568
+ * silently re-bases the whole application without touching a template.
569
+ */
570
+ declare const HUB_LOADING_BAR_DEFAULT_CONFIG: HubLoadingBarConfig;
571
+ /**
572
+ * Resolved defaults shared by `<hub-loading-bar>` and `HubLoadingBarService`.
573
+ *
574
+ * Declared with a root factory so the token is always injectable, even when the
575
+ * application never calls {@link provideHubLoadingBar}.
576
+ */
577
+ declare const HUB_LOADING_BAR_CONFIG: InjectionToken<HubLoadingBarConfig>;
578
+ /**
579
+ * Registers application-wide loading-bar defaults — the accent, the pacing, the
580
+ * translated label — so individual call sites stay bare.
581
+ *
582
+ * @param config - Values overriding {@link HUB_LOADING_BAR_DEFAULT_CONFIG}; omitted keys keep their default.
583
+ * @returns Environment providers for the application bootstrap.
584
+ */
585
+ declare function provideHubLoadingBar(config?: Partial<HubLoadingBarConfig>): EnvironmentProviders;
586
+
587
+ /**
588
+ * Marks a request as invisible to the loading bar.
589
+ *
590
+ * The escape hatch is not a nicety. A poll on a timer, a heartbeat, an autosave — any
591
+ * request the reader did not ask for — would otherwise hold the bar open forever, and a
592
+ * progress bar that never finishes is worse than none.
593
+ */
594
+ declare const HUB_LOADING_BAR_SKIP: HttpContextToken<boolean>;
595
+ /**
596
+ * Builds the `HttpContext` that hides one request from the loading bar.
597
+ *
598
+ * @param context - Existing context to extend; a fresh one by default.
599
+ * @returns The context, with the skip flag set.
600
+ *
601
+ * @example
602
+ * ```typescript
603
+ * this.http.get('/api/notifications', { context: withoutHubLoadingBar() });
604
+ * ```
605
+ */
606
+ declare function withoutHubLoadingBar(context?: HttpContext): HttpContext;
607
+ /**
608
+ * Holds the loading bar open for the lifetime of every HTTP request.
609
+ *
610
+ * The bar's reference counter is what makes this safe to combine with the router
611
+ * integration and with hand-written `start()` calls: six parallel requests are six
612
+ * callers, and the bar completes when the last of them does, not the first.
613
+ *
614
+ * `finalize` is the balancing point rather than a `tap` on success, because it also fires
615
+ * when the request errors and when the caller unsubscribes — a cancelled typeahead is the
616
+ * commonest way a naive interceptor strands the bar at 90%.
617
+ *
618
+ * Opt a request out with {@link withoutHubLoadingBar}.
619
+ *
620
+ * @example
621
+ * ```typescript
622
+ * provideHttpClient(withInterceptors([hubLoadingBarInterceptor]))
623
+ * ```
624
+ */
625
+ declare const hubLoadingBarInterceptor: HttpInterceptorFn;
626
+
627
+ /**
628
+ * Drives the loading bar from router navigation, which is the "page is loading" the bar
629
+ * is named after: it starts when a navigation begins and completes when it settles —
630
+ * including when it is cancelled by a guard or fails, because a bar left running after a
631
+ * rejected navigation is a bar that never goes away.
632
+ *
633
+ * Navigations are tracked with a flag rather than by pairing events one-to-one. The
634
+ * router's event sequence varies with configuration (a blocking initial navigation, a
635
+ * redirect, a skipped same-URL navigation), and the flag guarantees exactly one
636
+ * `start()` / `complete()` pair per navigation whatever order the events arrive in — a
637
+ * missed `NavigationStart` during bootstrap can no longer leave an unmatched `complete()`
638
+ * decrementing somebody else's count.
639
+ *
640
+ * Combine with {@link hubLoadingBarInterceptor} when routes fetch their own data: the
641
+ * navigation settles as soon as the component is created, so without the interceptor the
642
+ * bar finishes while the page is still empty.
643
+ *
644
+ * @returns Environment providers for the application bootstrap.
645
+ *
646
+ * @example
647
+ * ```typescript
648
+ * bootstrapApplication(AppComponent, {
649
+ * providers: [provideRouter(routes), provideHubLoadingBarRouter()]
650
+ * });
651
+ * ```
652
+ */
653
+ declare function provideHubLoadingBarRouter(): EnvironmentProviders;
654
+
655
+ export { HUB_LOADING_BAR_CONFIG, HUB_LOADING_BAR_DEFAULT_CONFIG, HUB_LOADING_BAR_SKIP, HUB_LOADING_CONFIG, HUB_LOADING_DEFAULT_CONFIG, HubLoadingBarComponent, HubLoadingBarService, HubLoadingComponent, HubLoadingService, hubLoadingBarInterceptor, hubLoadingBarTrickle, provideHubLoading, provideHubLoadingBar, provideHubLoadingBarRouter, withoutHubLoadingBar };
656
+ export type { HubLoadingBarConfig, HubLoadingBarMode, HubLoadingBarPlacement, HubLoadingBarTrickle, HubLoadingConfig, HubLoadingImageAnimation, HubLoadingMode, HubLoadingOptions, HubLoadingSize, HubLoadingVariant };