@arsedizioni/ars-utils 22.6.86 → 22.6.88

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.
Files changed (79) hide show
  1. package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs +3158 -3344
  2. package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs.map +1 -1
  3. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs +7616 -4772
  4. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs.map +1 -1
  5. package/fesm2022/arsedizioni-ars-utils-core.date.mjs +321 -702
  6. package/fesm2022/arsedizioni-ars-utils-core.date.mjs.map +1 -1
  7. package/fesm2022/arsedizioni-ars-utils-core.markdown.mjs +445 -590
  8. package/fesm2022/arsedizioni-ars-utils-core.markdown.mjs.map +1 -1
  9. package/fesm2022/arsedizioni-ars-utils-core.mjs +1735 -2370
  10. package/fesm2022/arsedizioni-ars-utils-core.mjs.map +1 -1
  11. package/fesm2022/arsedizioni-ars-utils-core.validators.mjs +1280 -1470
  12. package/fesm2022/arsedizioni-ars-utils-core.validators.mjs.map +1 -1
  13. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs +1097 -1528
  14. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs.map +1 -1
  15. package/fesm2022/arsedizioni-ars-utils-support.common.mjs +198 -294
  16. package/fesm2022/arsedizioni-ars-utils-support.common.mjs.map +1 -1
  17. package/fesm2022/arsedizioni-ars-utils-ui.controls.date.mjs +115 -117
  18. package/fesm2022/arsedizioni-ars-utils-ui.controls.date.mjs.map +1 -1
  19. package/fesm2022/arsedizioni-ars-utils-ui.controls.mjs +1607 -613
  20. package/fesm2022/arsedizioni-ars-utils-ui.controls.mjs.map +1 -1
  21. package/fesm2022/arsedizioni-ars-utils-ui.controls.tree.mjs +840 -367
  22. package/fesm2022/arsedizioni-ars-utils-ui.controls.tree.mjs.map +1 -1
  23. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.auth.mjs +1634 -651
  24. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.auth.mjs.map +1 -1
  25. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.mjs +1255 -792
  26. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.mjs.map +1 -1
  27. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.prompt.mjs +1410 -357
  28. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.prompt.mjs.map +1 -1
  29. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.select.mjs +2401 -944
  30. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.select.mjs.map +1 -1
  31. package/fesm2022/arsedizioni-ars-utils-ui.files.mjs +1553 -741
  32. package/fesm2022/arsedizioni-ars-utils-ui.files.mjs.map +1 -1
  33. package/fesm2022/arsedizioni-ars-utils-ui.filters.mjs +901 -430
  34. package/fesm2022/arsedizioni-ars-utils-ui.filters.mjs.map +1 -1
  35. package/fesm2022/arsedizioni-ars-utils-ui.help.mjs +737 -530
  36. package/fesm2022/arsedizioni-ars-utils-ui.help.mjs.map +1 -1
  37. package/fesm2022/arsedizioni-ars-utils-ui.mjs +6638 -1755
  38. package/fesm2022/arsedizioni-ars-utils-ui.mjs.map +1 -1
  39. package/fesm2022/arsedizioni-ars-utils-ui.navigation.mjs +1479 -1207
  40. package/fesm2022/arsedizioni-ars-utils-ui.navigation.mjs.map +1 -1
  41. package/fesm2022/arsedizioni-ars-utils-ui.notifications.mjs +1003 -270
  42. package/fesm2022/arsedizioni-ars-utils-ui.notifications.mjs.map +1 -1
  43. package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs +497 -278
  44. package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs.map +1 -1
  45. package/fesm2022/arsedizioni-ars-utils-ui.paginator.mjs +48 -52
  46. package/fesm2022/arsedizioni-ars-utils-ui.paginator.mjs.map +1 -1
  47. package/fesm2022/arsedizioni-ars-utils-ui.shell.mjs +464 -704
  48. package/fesm2022/arsedizioni-ars-utils-ui.shell.mjs.map +1 -1
  49. package/fesm2022/arsedizioni-ars-utils-ui.tinymce.mjs +724 -657
  50. package/fesm2022/arsedizioni-ars-utils-ui.tinymce.mjs.map +1 -1
  51. package/fesm2022/arsedizioni-ars-utils.mjs +3 -19
  52. package/fesm2022/arsedizioni-ars-utils.mjs.map +1 -1
  53. package/package.json +1 -1
  54. package/types/arsedizioni-ars-utils-clipper.common.d.ts +1151 -1163
  55. package/types/arsedizioni-ars-utils-clipper.ui.d.ts +1588 -1426
  56. package/types/arsedizioni-ars-utils-core.d.ts +1169 -1172
  57. package/types/arsedizioni-ars-utils-core.date.d.ts +204 -208
  58. package/types/arsedizioni-ars-utils-core.markdown.d.ts +128 -126
  59. package/types/arsedizioni-ars-utils-core.validators.d.ts +270 -236
  60. package/types/arsedizioni-ars-utils-evolution.common.d.ts +1054 -1067
  61. package/types/arsedizioni-ars-utils-support.common.d.ts +225 -234
  62. package/types/arsedizioni-ars-utils-ui.controls.d.ts +440 -278
  63. package/types/arsedizioni-ars-utils-ui.controls.date.d.ts +24 -28
  64. package/types/arsedizioni-ars-utils-ui.controls.tree.d.ts +172 -136
  65. package/types/arsedizioni-ars-utils-ui.d.ts +2035 -821
  66. package/types/arsedizioni-ars-utils-ui.dialogs.auth.d.ts +327 -322
  67. package/types/arsedizioni-ars-utils-ui.dialogs.d.ts +343 -347
  68. package/types/arsedizioni-ars-utils-ui.dialogs.prompt.d.ts +167 -167
  69. package/types/arsedizioni-ars-utils-ui.dialogs.select.d.ts +373 -367
  70. package/types/arsedizioni-ars-utils-ui.files.d.ts +335 -283
  71. package/types/arsedizioni-ars-utils-ui.filters.d.ts +232 -160
  72. package/types/arsedizioni-ars-utils-ui.help.d.ts +204 -211
  73. package/types/arsedizioni-ars-utils-ui.navigation.d.ts +811 -646
  74. package/types/arsedizioni-ars-utils-ui.notifications.d.ts +74 -78
  75. package/types/arsedizioni-ars-utils-ui.oauth.d.ts +102 -62
  76. package/types/arsedizioni-ars-utils-ui.paginator.d.ts +19 -22
  77. package/types/arsedizioni-ars-utils-ui.shell.d.ts +376 -382
  78. package/types/arsedizioni-ars-utils-ui.tinymce.d.ts +244 -231
  79. package/types/arsedizioni-ars-utils.d.ts +1 -3
@@ -1,707 +1,467 @@
1
- import { timer, Subject } from 'rxjs';
2
- import { tap, first } from 'rxjs/operators';
3
- import * as i0 from '@angular/core';
4
- import { inject, signal, computed, ChangeDetectionStrategy, Component, output, viewChild, afterNextRender, DestroyRef, HostListener, ApplicationRef, EnvironmentInjector, createComponent, Service } from '@angular/core';
5
- import { ThemeService, SafeHtmlPipe, SystemUtils } from '@arsedizioni/ars-utils/core';
6
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
-
8
- /**
9
- * Shows the busy overlay only if the operation is still running after a debounce delay, so that
10
- * an operation that answers in 30 ms never makes the screen flash.
11
- *
12
- * Whoever creates one owns it: {@link clear} must be called when the operation ends, otherwise
13
- * the overlay appears after the fact and stays.
14
- */
15
- class BusyTimer {
16
- /**
17
- * Arms the timer.
18
- * @param shellService - The service that owns the overlay. A `DialogService` is accepted too,
19
- * since it extends `ShellService`.
20
- * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.
21
- * @param message - Text to display. Defaults to `'Operazione in corso...'`.
22
- */
23
- constructor(shellService, due = 100, message = 'Operazione in corso...') {
24
- this.shellService = shellService;
25
- this.subscription = timer(due).pipe(tap(() => this.shellService.busy(message))).subscribe();
26
- }
27
- /**
28
- * Disarms the timer and takes the overlay away.
29
- * @returns void
30
- */
31
- clear() {
32
- this.subscription.unsubscribe();
33
- this.shellService.clearBusy();
34
- }
35
- }
36
-
37
- /** Circumference of the spinner circle, in user units. Kept here so the template stays arithmetic-free. */
1
+ import { Subject, timer } from "rxjs";
2
+ import { first, tap } from "rxjs/operators";
3
+ import * as i0 from "@angular/core";
4
+ import { ApplicationRef, ChangeDetectionStrategy, Component, DestroyRef, EnvironmentInjector, HostListener, Service, afterNextRender, computed, createComponent, inject, output, signal, viewChild } from "@angular/core";
5
+ import { SafeHtmlPipe, SystemUtils, ThemeService } from "@arsedizioni/ars-utils/core";
6
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
7
+ var BusyTimer = class {
8
+ constructor(shellService, due = 100, message = "Operazione in corso...") {
9
+ this.shellService = shellService;
10
+ this.subscription = timer(due).pipe(tap(() => this.shellService.busy(message))).subscribe();
11
+ }
12
+ clear() {
13
+ this.subscription.unsubscribe();
14
+ this.shellService.clearBusy();
15
+ }
16
+ };
38
17
  const SPINNER_CIRCUMFERENCE = 2 * Math.PI * 31.5;
39
- /**
40
- * Overlay that blocks interaction while an operation is running, in the four styles the
41
- * applications use: progress bar, spinner, hourglass and bare wait.
42
- *
43
- * Reproduces `mat-progress-bar` and `mat-progress-spinner` in CSS and SVG rather than importing
44
- * them, because this overlay is shown from the boot path — an HTTP interceptor calls it before
45
- * any route exists — and `ui.shell` exists precisely so that path costs no Material.
46
- *
47
- * The instance is created once by {@link ShellService} and then reused: showing and hiding is a
48
- * class on the host, never a `createComponent`, so the 40-odd calls a screen costs nothing.
49
- */
50
- class ShellBusyComponent {
51
- /**
52
- * Keeps {@link dark} in step with the service, now and at every later change.
53
- *
54
- * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the
55
- * operating system is currently on.
56
- * @returns void
57
- */
58
- followTheme() {
59
- const resolve = (theme) => (theme === 'auto' ? this.themeService.getTheme() : theme) === 'dark';
60
- this.dark.set(resolve(this.themeService.getTheme()));
61
- this.themeService.changed
62
- .pipe(takeUntilDestroyed())
63
- .subscribe(theme => this.dark.set(resolve(theme)));
64
- }
65
- constructor() {
66
- this.themeService = inject(ThemeService);
67
- /**
68
- * True when the panel must paint itself dark.
69
- *
70
- * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not
71
- * always agree: the service is what the user chose, and it may be forcing dark on a machine
72
- * that is on light. Following the OS instead would leave this panel the only light thing on a
73
- * dark screen.
74
- */
75
- this.dark = signal(false, /* @ts-ignore */
76
- ...(ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []));
77
- /** Whether the overlay is on screen. Hidden with `display: none`, so it costs no layout. */
78
- this.visible = signal(false, /* @ts-ignore */
79
- ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
80
- /** Visual style of the indicator. */
81
- this.type = signal('bar', /* @ts-ignore */
82
- ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
83
- /** Current progress value (0-100), used when {@link progressMode} is `'determinate'`. */
84
- this.progress = signal(0, /* @ts-ignore */
85
- ...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
86
- /** Progress mode of the bar and of the spinner. */
87
- this.progressMode = signal('indeterminate', /* @ts-ignore */
88
- ...(ngDevMode ? [{ debugName: "progressMode" }] : /* istanbul ignore next */ []));
89
- /** Message displayed above the indicator. */
90
- this.message = signal('', /* @ts-ignore */
91
- ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
92
- /** Circumference of the spinner circle, bound to `stroke-dasharray`. */
93
- this.circumference = SPINNER_CIRCUMFERENCE;
94
- /** Length of the spinner arc still to be drawn, derived from {@link progress}. */
95
- this.dashOffset = computed(() => SPINNER_CIRCUMFERENCE * (1 - Math.min(Math.max(this.progress(), 0), 100) / 100), /* @ts-ignore */
96
- ...(ngDevMode ? [{ debugName: "dashOffset" }] : /* istanbul ignore next */ []));
97
- this.followTheme();
98
- }
99
- /**
100
- * Updates the overlay state.
101
- *
102
- * Same contract as the `BusyDialogComponent` it replaces, down to the two implicit rules that
103
- * callers rely on: an empty message keeps the previous text (so `wait()` does not wipe it), and
104
- * a progress above zero forces `'determinate'` whatever the caller passed.
105
- * @param message - New message to display. An empty string preserves the current one.
106
- * @param progress - Current progress value (0-100). Above zero it forces determinate mode.
107
- * @param progressMode - Progress mode to use. Defaults to `'indeterminate'`.
108
- * @param type - Visual style to use. Defaults to `'bar'`.
109
- * @returns void
110
- */
111
- set(message, progress, progressMode = 'indeterminate', type = 'bar') {
112
- if (message)
113
- this.message.set(message);
114
- if (type)
115
- this.type.set(type);
116
- this.progress.set(progress);
117
- this.progressMode.set(progress > 0 ? 'determinate' : progressMode);
118
- }
119
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellBusyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
120
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: ShellBusyComponent, isStandalone: true, selector: "ars-shell-busy", host: { properties: { "class.ars-hidden": "!visible()", "class.ars-dark": "dark()" } }, ngImport: i0, template: "<div class=\"scrim\"></div>\n\n@if (type() === 'hourglass' || type() === 'wait') {\n<div class=\"bare\">\n <svg class=\"hourglass\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M6 2h12v6l-4 4 4 4v6H6v-6l4-4-4-4zm2 2v3.2l4 4 4-4V4zm0 16h8v-3.2l-4-4-4 4z\" />\n </svg>\n @if (type() === 'hourglass' && message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n</div>\n} @else {\n<div class=\"panel\" role=\"status\" aria-live=\"polite\">\n @if (message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n\n @if (type() === 'bar') {\n <div class=\"bar\" [class.indeterminate]=\"progressMode() === 'indeterminate'\">\n <div class=\"bar-fill\" [style.width.%]=\"progressMode() === 'determinate' ? progress() : null\"></div>\n </div>\n } @else if (type() === 'spinner') {\n <svg class=\"spinner\" [class.indeterminate]=\"progressMode() === 'indeterminate'\" viewBox=\"0 0 70 70\"\n aria-hidden=\"true\">\n <circle class=\"track\" cx=\"35\" cy=\"35\" r=\"31.5\"></circle>\n <circle class=\"arc\" cx=\"35\" cy=\"35\" r=\"31.5\" [style.stroke-dasharray]=\"circumference\"\n [style.stroke-dashoffset]=\"progressMode() === 'determinate' ? dashOffset() : null\"></circle>\n </svg>\n }\n\n @if (progress() > 0) {\n <div class=\"percent\"><b>{{ progress() }}%</b></div>\n }\n</div>\n}\n", styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .28));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #006a60));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-shell-busy-bare-color, #ffffff);color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-busy-z-index, 1090);display:flex;align-items:center;justify-content:center;font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);color:var(--_on-surface)}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .45));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #67d9c9));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #3a3d3c));color-scheme:dark}:host::backdrop{display:none}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-busy-fade .2s cubic-bezier(0,0,.2,1)}@keyframes ars-busy-fade{0%{opacity:0}to{opacity:1}}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-shell-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 0 rgba(0, 0, 0, .2), 0 6px 20px 0 rgba(0, 0, 0, .19));text-align:center}.bare{position:relative;display:flex;flex-direction:column;align-items:center;color:var(--_bare)}.message{font-size:larger;font-weight:600;padding-bottom:15px;text-align:center}.bare .message{padding-top:15px;padding-bottom:0}.percent{padding-top:8px;font-size:smaller}.bar{position:relative;max-width:350px;height:4px;margin:15px auto 0;overflow:hidden;border-radius:2px;background:var(--_track)}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--_primary);transition:width .12s linear}.bar.indeterminate .bar-fill{width:40%;transition:none;animation:ars-busy-bar 1.6s cubic-bezier(.65,.02,.35,1) infinite}@keyframes ars-busy-bar{0%{left:-40%}to{left:100%}}.spinner{display:block;width:70px;height:70px;margin:15px auto 0;transform:rotate(-90deg)}.spinner circle{fill:none;stroke-width:7;stroke-linecap:round}.spinner .track{stroke:var(--_track)}.spinner .arc{stroke:var(--_primary);transition:stroke-dashoffset .12s linear}.spinner.indeterminate{animation:ars-busy-rotate 1.4s linear infinite}.spinner.indeterminate .arc{transition:none;stroke-dashoffset:0;animation:ars-busy-dash 1.4s ease-in-out infinite}@keyframes ars-busy-rotate{0%{transform:rotate(-90deg)}to{transform:rotate(270deg)}}@keyframes ars-busy-dash{0%{stroke-dasharray:15 183}50%{stroke-dasharray:145 53}to{stroke-dasharray:15 183}}.hourglass{width:80px;height:80px;fill:currentColor;animation:ars-busy-hourglass 3s linear infinite}@keyframes ars-busy-hourglass{0%{transform:rotate(360deg)}to{transform:rotate(0)}}@media(prefers-reduced-motion:reduce){.scrim{animation:none}.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"], dependencies: [{ kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
121
- }
122
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellBusyComponent, decorators: [{
123
- type: Component,
124
- args: [{ selector: 'ars-shell-busy', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafeHtmlPipe], host: { '[class.ars-hidden]': '!visible()', '[class.ars-dark]': 'dark()' }, template: "<div class=\"scrim\"></div>\n\n@if (type() === 'hourglass' || type() === 'wait') {\n<div class=\"bare\">\n <svg class=\"hourglass\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M6 2h12v6l-4 4 4 4v6H6v-6l4-4-4-4zm2 2v3.2l4 4 4-4V4zm0 16h8v-3.2l-4-4-4 4z\" />\n </svg>\n @if (type() === 'hourglass' && message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n</div>\n} @else {\n<div class=\"panel\" role=\"status\" aria-live=\"polite\">\n @if (message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n\n @if (type() === 'bar') {\n <div class=\"bar\" [class.indeterminate]=\"progressMode() === 'indeterminate'\">\n <div class=\"bar-fill\" [style.width.%]=\"progressMode() === 'determinate' ? progress() : null\"></div>\n </div>\n } @else if (type() === 'spinner') {\n <svg class=\"spinner\" [class.indeterminate]=\"progressMode() === 'indeterminate'\" viewBox=\"0 0 70 70\"\n aria-hidden=\"true\">\n <circle class=\"track\" cx=\"35\" cy=\"35\" r=\"31.5\"></circle>\n <circle class=\"arc\" cx=\"35\" cy=\"35\" r=\"31.5\" [style.stroke-dasharray]=\"circumference\"\n [style.stroke-dashoffset]=\"progressMode() === 'determinate' ? dashOffset() : null\"></circle>\n </svg>\n }\n\n @if (progress() > 0) {\n <div class=\"percent\"><b>{{ progress() }}%</b></div>\n }\n</div>\n}\n", styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .28));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #006a60));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-shell-busy-bare-color, #ffffff);color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-busy-z-index, 1090);display:flex;align-items:center;justify-content:center;font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);color:var(--_on-surface)}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .45));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #67d9c9));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #3a3d3c));color-scheme:dark}:host::backdrop{display:none}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-busy-fade .2s cubic-bezier(0,0,.2,1)}@keyframes ars-busy-fade{0%{opacity:0}to{opacity:1}}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-shell-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 0 rgba(0, 0, 0, .2), 0 6px 20px 0 rgba(0, 0, 0, .19));text-align:center}.bare{position:relative;display:flex;flex-direction:column;align-items:center;color:var(--_bare)}.message{font-size:larger;font-weight:600;padding-bottom:15px;text-align:center}.bare .message{padding-top:15px;padding-bottom:0}.percent{padding-top:8px;font-size:smaller}.bar{position:relative;max-width:350px;height:4px;margin:15px auto 0;overflow:hidden;border-radius:2px;background:var(--_track)}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--_primary);transition:width .12s linear}.bar.indeterminate .bar-fill{width:40%;transition:none;animation:ars-busy-bar 1.6s cubic-bezier(.65,.02,.35,1) infinite}@keyframes ars-busy-bar{0%{left:-40%}to{left:100%}}.spinner{display:block;width:70px;height:70px;margin:15px auto 0;transform:rotate(-90deg)}.spinner circle{fill:none;stroke-width:7;stroke-linecap:round}.spinner .track{stroke:var(--_track)}.spinner .arc{stroke:var(--_primary);transition:stroke-dashoffset .12s linear}.spinner.indeterminate{animation:ars-busy-rotate 1.4s linear infinite}.spinner.indeterminate .arc{transition:none;stroke-dashoffset:0;animation:ars-busy-dash 1.4s ease-in-out infinite}@keyframes ars-busy-rotate{0%{transform:rotate(-90deg)}to{transform:rotate(270deg)}}@keyframes ars-busy-dash{0%{stroke-dasharray:15 183}50%{stroke-dasharray:145 53}to{stroke-dasharray:15 183}}.hourglass{width:80px;height:80px;fill:currentColor;animation:ars-busy-hourglass 3s linear infinite}@keyframes ars-busy-hourglass{0%{transform:rotate(360deg)}to{transform:rotate(0)}}@media(prefers-reduced-motion:reduce){.scrim{animation:none}.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"] }]
125
- }], ctorParameters: () => [] });
126
-
127
- /**
128
- * The shell's message panel: a modal that looks like a Material dialog and owes nothing to
129
- * Material.
130
- *
131
- * It exists because shell, interceptors, guards and app initializers must be able to say
132
- * "the session is gone" before a single route has been loaded, and paying for `mat-dialog`,
133
- * `cdk/overlay` and the rest of that stack on the boot path costs the initial bundle more
134
- * than every route of the application put together. The look is matched through CSS custom
135
- * properties (`--ars-*` first, Material's `--mat-sys-*` next, a literal last), so an
136
- * application that themes Material gets the same surface here for free.
137
- *
138
- * Instantiated imperatively by {@link ShellService}, never declared in a template: that is
139
- * what lets an interceptor open it.
140
- */
141
- class ShellMessageComponent {
142
- /**
143
- * Keeps {@link dark} in step with the service, now and at every later change.
144
- *
145
- * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the
146
- * operating system is currently on.
147
- * @returns void
148
- */
149
- followTheme() {
150
- const resolve = (theme) => (theme === 'auto' ? this.themeService.getTheme() : theme) === 'dark';
151
- this.dark.set(resolve(this.themeService.getTheme()));
152
- this.themeService.changed
153
- .pipe(takeUntilDestroyed())
154
- .subscribe(theme => this.dark.set(resolve(theme)));
155
- }
156
- constructor() {
157
- /** Emitted when the user dismisses the message, whichever way they did it. */
158
- this.closed = output();
159
- this.themeService = inject(ThemeService);
160
- this.panel = viewChild.required('panel', /* @ts-ignore */
161
- ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
162
- this.okButton = viewChild.required('ok', /* @ts-ignore */
163
- ...(ngDevMode ? [{ debugName: "okButton" }] : /* istanbul ignore next */ []));
164
- /** Unique id tying the panel to its heading for assistive technology. */
165
- this.titleId = `ars-shell-message-title-${Math.random().toString(36).slice(2, 10)}`;
166
- /** Unique id tying the details panel to the header that opens it. */
167
- this.detailsId = `ars-shell-message-details-${Math.random().toString(36).slice(2, 10)}`;
168
- /**
169
- * Whether the technical details are open.
170
- *
171
- * Closed on arrival, and closed again by every {@link setData}: the message is what the user is
172
- * meant to read, and a stack trace unfolded under it is noise until they ask for it.
173
- */
174
- this.detailsOpen = signal(false, /* @ts-ignore */
175
- ...(ngDevMode ? [{ debugName: "detailsOpen" }] : /* istanbul ignore next */ []));
176
- /** Current content, with the defaults the shell relies on already applied. */
177
- this.data = signal({ message: '', title: 'Informazioni', okCaption: 'Ok', width: 500 }, /* @ts-ignore */
178
- ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
179
- /** True for a moment after a successful copy, so the button can acknowledge it. */
180
- this.copied = signal(false, /* @ts-ignore */
181
- ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
182
- this.closing = false;
183
- /**
184
- * True when the panel must paint itself dark.
185
- *
186
- * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not
187
- * always agree: the service is what the user chose, and it may be forcing dark on a machine
188
- * that is on light. Following the OS instead would leave this panel the only light thing on a
189
- * dark screen.
190
- */
191
- this.dark = signal(false, /* @ts-ignore */
192
- ...(ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []));
193
- this.followTheme();
194
- afterNextRender(() => this.okButton().nativeElement.focus());
195
- inject(DestroyRef).onDestroy(() => {
196
- clearTimeout(this.dismissTimer);
197
- clearTimeout(this.copiedTimer);
198
- });
199
- }
200
- /**
201
- * Replaces the content shown by the panel and restarts the auto-dismiss timer.
202
- *
203
- * Called on an already open panel by `ShellService.error()` so that a burst of failures
204
- * updates one message instead of stacking modals nobody can dismiss.
205
- * @param data - The new content to display.
206
- * @returns void
207
- */
208
- setData(data) {
209
- this.data.set({ title: 'Informazioni', okCaption: 'Ok', width: 500, ...data });
210
- this.detailsOpen.set(false);
211
- this.scheduleDismiss(this.data().dismissAfter);
212
- }
213
- /**
214
- * Opens or closes the technical details panel.
215
- * @returns void
216
- */
217
- toggleDetails() {
218
- this.detailsOpen.update(open => !open);
219
- }
220
- /**
221
- * Dismisses the panel. Safe to call more than once: only the first call is announced.
222
- * @returns void
223
- */
224
- close() {
225
- if (this.closing)
226
- return;
227
- this.closing = true;
228
- clearTimeout(this.dismissTimer);
229
- this.closed.emit();
230
- }
231
- /**
232
- * Closes the panel on Escape, mirroring a Material dialog opened with the default config.
233
- * @param e - The keyboard event captured at document level.
234
- * @returns void
235
- */
236
- onKeydown(e) {
237
- if (e.key === 'Escape') {
238
- e.preventDefault();
239
- this.close();
240
- return;
241
- }
242
- if (e.key === 'Tab')
243
- this.trapFocus(e);
244
- }
245
- /**
246
- * Keeps Tab inside the panel while the message is up.
247
- *
248
- * A hand-rolled two-element version of what `cdk/a11y` does, which is all this panel needs:
249
- * it never holds more than the copy button and the dismiss button.
250
- * @param e - The Tab keydown event.
251
- * @returns void
252
- */
253
- trapFocus(e) {
254
- const focusable = Array.from(this.panel().nativeElement.querySelectorAll('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])'));
255
- if (!focusable.length)
256
- return;
257
- const first = focusable[0];
258
- const last = focusable[focusable.length - 1];
259
- const active = document.activeElement;
260
- if (e.shiftKey && (active === first || !this.panel().nativeElement.contains(active))) {
261
- e.preventDefault();
262
- last.focus();
263
- }
264
- else if (!e.shiftKey && active === last) {
265
- e.preventDefault();
266
- first.focus();
267
- }
268
- }
269
- /**
270
- * (Re)schedules the auto-dismiss timer, cancelling any pending one so a timer armed for an
271
- * earlier message can never close a newer one.
272
- * @param dismissAfter - Delay in milliseconds; no-op when falsy.
273
- * @returns void
274
- */
275
- scheduleDismiss(dismissAfter) {
276
- clearTimeout(this.dismissTimer);
277
- this.dismissTimer = undefined;
278
- if (dismissAfter)
279
- this.dismissTimer = setTimeout(() => this.close(), dismissAfter);
280
- }
281
- /**
282
- * Copies the message and its details to the clipboard, as both HTML and plain text.
283
- *
284
- * Unlike `InfoDialogComponent` there is no toast to report the outcome — the shell has no
285
- * toast — so the button itself acknowledges it for a moment.
286
- * @returns A promise that resolves once the copy has been attempted.
287
- */
288
- async copy() {
289
- const content = this.panel().nativeElement.querySelector('.content');
290
- if (!content)
291
- return;
292
- try {
293
- await navigator.clipboard.write([
294
- new ClipboardItem({
295
- 'text/html': new Blob([content.innerHTML], { type: 'text/html' }),
296
- 'text/plain': new Blob([content.innerText], { type: 'text/plain' })
297
- })
298
- ]);
299
- this.copied.set(true);
300
- clearTimeout(this.copiedTimer);
301
- this.copiedTimer = setTimeout(() => this.copied.set(false), 1500);
302
- }
303
- catch {
304
- // Clipboard permission denied or the document is not focused: nothing worth interrupting
305
- // the user for, and the text is on screen anyway.
306
- }
307
- }
308
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
309
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: ShellMessageComponent, isStandalone: true, selector: "ars-shell-message", outputs: { closed: "closed" }, host: { listeners: { "document:keydown": "onKeydown($event)" }, properties: { "class.ars-dark": "dark()" } }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }, { propertyName: "okButton", first: true, predicate: ["ok"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"scrim\" (click)=\"close()\"></div>\n\n<div #panel class=\"panel\" role=\"alertdialog\" aria-modal=\"true\" [attr.aria-labelledby]=\"titleId\"\n [style.max-width.px]=\"data().width\">\n\n <h2 class=\"title\" [id]=\"titleId\" [innerHTML]=\"data().title | safeHtml\"></h2>\n\n <div class=\"content\">\n @if (data().message) {\n <div class=\"message\" [innerHTML]=\"data().message | safeHtml\"></div>\n }\n @if (data().details) {\n <div class=\"details-box\" [class.open]=\"detailsOpen()\">\n <button type=\"button\" class=\"details-header\" (click)=\"toggleDetails()\"\n [attr.aria-expanded]=\"detailsOpen()\" [attr.aria-controls]=\"detailsId\">\n <span class=\"details-label\">Dettaglio</span>\n <svg class=\"chevron\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z\" />\n </svg>\n </button>\n <div class=\"details-body\">\n <div class=\"details\" [id]=\"detailsId\" [attr.aria-hidden]=\"!detailsOpen()\">\n <div class=\"details-text\" [innerHTML]=\"data().details | safeHtml\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"actions\">\n <button type=\"button\" class=\"icon-button\" (click)=\"copy()\"\n [title]=\"copied() ? 'Copiato negli appunti' : 'Copia negli appunti'\" aria-label=\"Copia negli appunti\">\n @if (copied()) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z\" /></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path\n d=\"M16 1H4a2 2 0 0 0-2 2v14h2V3h12zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m0 16H8V7h11z\" /></svg>\n }\n </button>\n <button #ok type=\"button\" class=\"flat-button\" (click)=\"close()\">{{ data().okCaption }}</button>\n </div>\n</div>\n", styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f));--_divider: var(--ars-color-divider, #757d87);--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .24));--_primary: var(--ars-button-background-color, var(--mat-sys-primary, #006a60));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));--_hover: var(--ars-item-hover-background-color, rgba(0, 0, 0, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .05));color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-dialog-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #b1ccc6));--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .45));--_primary: var(---ars-button-background-color, var(--mat-sys-primary, #67d9c9));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #00382f));--_hover: var(--ars-item-hover-background-color, rgba(255, 255, 255, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .22));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-shell-fade .2s cubic-bezier(0,0,.2,1)}.panel{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;margin:12px;max-height:calc(100% - 24px);box-sizing:border-box;border-radius:var(--ars-shell-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 3px rgba(0, 0, 0, .15), 0 1px 3px 0 rgba(0, 0, 0, .3));font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);text-align:left;animation:ars-shell-enter .15s cubic-bezier(0,0,.2,1)}.title{margin:0;padding:24px 24px 16px;font:var(--mat-sys-headline-small, 400 24px/32px Roboto, sans-serif);color:inherit}.content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:0;font:var(--mat-sys-body-medium, 400 14px/20px Roboto, sans-serif)}.message{flex:0 1 auto;min-height:0;padding:0 24px 24px;overflow-y:auto;overscroll-behavior:contain}.details-box{flex:0 1000 auto;min-height:0;display:flex;flex-direction:column;margin:0 16px 4px;border-radius:12px;background:transparent;transition:background-color .2s cubic-bezier(0,0,.2,1)}.details-box.open{background:var(--_sunken)}.details-header{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;height:44px;padding:0 8px;border-radius:12px;background:transparent;color:var(--_secondary);text-align:left}.details-header:hover{background:var(--_hover)}.details-label{font-size:.75em;letter-spacing:.06em;text-transform:uppercase}.chevron{width:20px;height:20px;fill:currentColor;transition:transform .2s cubic-bezier(0,0,.2,1)}.details-box.open .chevron{transform:rotate(180deg)}.details-body{display:grid;grid-template-rows:0fr;min-height:0;max-height:320px;transition:grid-template-rows .2s cubic-bezier(0,0,.2,1)}.details-box.open .details-body{grid-template-rows:1fr}.details{min-height:0;overflow:hidden}.details-box.open .details{overflow-y:auto;overscroll-behavior:contain}.details-text{padding:0 8px 12px;font-size:.85em}.actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:16px 24px 24px}button{font-family:inherit;cursor:pointer;border:none}button:focus-visible{outline:none!important;box-shadow:none!important}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border-radius:50%;background:transparent;color:inherit}.icon-button svg{width:24px;height:24px;fill:currentColor}.icon-button:hover,.icon-button:focus-visible{background:var(--_hover)!important}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--_primary);color:var(--_on-primary);font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover,.flat-button:focus-visible{filter:brightness(1.08)!important}@keyframes ars-shell-fade{0%{opacity:0}to{opacity:1}}@keyframes ars-shell-enter{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.scrim,.panel{animation:none}.details-box,.details-body,.chevron{transition:none}}\n"], dependencies: [{ kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
310
- }
311
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellMessageComponent, decorators: [{
312
- type: Component,
313
- args: [{ selector: 'ars-shell-message', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafeHtmlPipe], host: { '[class.ars-dark]': 'dark()' }, template: "<div class=\"scrim\" (click)=\"close()\"></div>\n\n<div #panel class=\"panel\" role=\"alertdialog\" aria-modal=\"true\" [attr.aria-labelledby]=\"titleId\"\n [style.max-width.px]=\"data().width\">\n\n <h2 class=\"title\" [id]=\"titleId\" [innerHTML]=\"data().title | safeHtml\"></h2>\n\n <div class=\"content\">\n @if (data().message) {\n <div class=\"message\" [innerHTML]=\"data().message | safeHtml\"></div>\n }\n @if (data().details) {\n <div class=\"details-box\" [class.open]=\"detailsOpen()\">\n <button type=\"button\" class=\"details-header\" (click)=\"toggleDetails()\"\n [attr.aria-expanded]=\"detailsOpen()\" [attr.aria-controls]=\"detailsId\">\n <span class=\"details-label\">Dettaglio</span>\n <svg class=\"chevron\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z\" />\n </svg>\n </button>\n <div class=\"details-body\">\n <div class=\"details\" [id]=\"detailsId\" [attr.aria-hidden]=\"!detailsOpen()\">\n <div class=\"details-text\" [innerHTML]=\"data().details | safeHtml\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"actions\">\n <button type=\"button\" class=\"icon-button\" (click)=\"copy()\"\n [title]=\"copied() ? 'Copiato negli appunti' : 'Copia negli appunti'\" aria-label=\"Copia negli appunti\">\n @if (copied()) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z\" /></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path\n d=\"M16 1H4a2 2 0 0 0-2 2v14h2V3h12zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m0 16H8V7h11z\" /></svg>\n }\n </button>\n <button #ok type=\"button\" class=\"flat-button\" (click)=\"close()\">{{ data().okCaption }}</button>\n </div>\n</div>\n", styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f));--_divider: var(--ars-color-divider, #757d87);--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .24));--_primary: var(--ars-button-background-color, var(--mat-sys-primary, #006a60));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));--_hover: var(--ars-item-hover-background-color, rgba(0, 0, 0, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .05));color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-dialog-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #b1ccc6));--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .45));--_primary: var(---ars-button-background-color, var(--mat-sys-primary, #67d9c9));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #00382f));--_hover: var(--ars-item-hover-background-color, rgba(255, 255, 255, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .22));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-shell-fade .2s cubic-bezier(0,0,.2,1)}.panel{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;margin:12px;max-height:calc(100% - 24px);box-sizing:border-box;border-radius:var(--ars-shell-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 3px rgba(0, 0, 0, .15), 0 1px 3px 0 rgba(0, 0, 0, .3));font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);text-align:left;animation:ars-shell-enter .15s cubic-bezier(0,0,.2,1)}.title{margin:0;padding:24px 24px 16px;font:var(--mat-sys-headline-small, 400 24px/32px Roboto, sans-serif);color:inherit}.content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:0;font:var(--mat-sys-body-medium, 400 14px/20px Roboto, sans-serif)}.message{flex:0 1 auto;min-height:0;padding:0 24px 24px;overflow-y:auto;overscroll-behavior:contain}.details-box{flex:0 1000 auto;min-height:0;display:flex;flex-direction:column;margin:0 16px 4px;border-radius:12px;background:transparent;transition:background-color .2s cubic-bezier(0,0,.2,1)}.details-box.open{background:var(--_sunken)}.details-header{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;height:44px;padding:0 8px;border-radius:12px;background:transparent;color:var(--_secondary);text-align:left}.details-header:hover{background:var(--_hover)}.details-label{font-size:.75em;letter-spacing:.06em;text-transform:uppercase}.chevron{width:20px;height:20px;fill:currentColor;transition:transform .2s cubic-bezier(0,0,.2,1)}.details-box.open .chevron{transform:rotate(180deg)}.details-body{display:grid;grid-template-rows:0fr;min-height:0;max-height:320px;transition:grid-template-rows .2s cubic-bezier(0,0,.2,1)}.details-box.open .details-body{grid-template-rows:1fr}.details{min-height:0;overflow:hidden}.details-box.open .details{overflow-y:auto;overscroll-behavior:contain}.details-text{padding:0 8px 12px;font-size:.85em}.actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:16px 24px 24px}button{font-family:inherit;cursor:pointer;border:none}button:focus-visible{outline:none!important;box-shadow:none!important}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border-radius:50%;background:transparent;color:inherit}.icon-button svg{width:24px;height:24px;fill:currentColor}.icon-button:hover,.icon-button:focus-visible{background:var(--_hover)!important}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--_primary);color:var(--_on-primary);font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover,.flat-button:focus-visible{filter:brightness(1.08)!important}@keyframes ars-shell-fade{0%{opacity:0}to{opacity:1}}@keyframes ars-shell-enter{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.scrim,.panel{animation:none}.details-box,.details-body,.chevron{transition:none}}\n"] }]
314
- }], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], okButton: [{ type: i0.ViewChild, args: ['ok', { isSignal: true }] }], onKeydown: [{
315
- type: HostListener,
316
- args: ['document:keydown', ['$event']]
317
- }] } });
318
-
319
- /** Grace period before the busy overlay is actually taken away, in milliseconds. */
18
+ var ShellBusyComponent = class ShellBusyComponent {
19
+ followTheme() {
20
+ const resolve = (theme) => (theme === "auto" ? this.themeService.getTheme() : theme) === "dark";
21
+ this.dark.set(resolve(this.themeService.getTheme()));
22
+ this.themeService.changed.pipe(takeUntilDestroyed()).subscribe((theme) => this.dark.set(resolve(theme)));
23
+ }
24
+ constructor() {
25
+ this.themeService = inject(ThemeService);
26
+ this.dark = signal(false, ...ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []);
27
+ this.visible = signal(false, ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
28
+ this.type = signal("bar", ...ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []);
29
+ this.progress = signal(0, ...ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []);
30
+ this.progressMode = signal("indeterminate", ...ngDevMode ? [{ debugName: "progressMode" }] : /* istanbul ignore next */ []);
31
+ this.message = signal("", ...ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []);
32
+ this.circumference = SPINNER_CIRCUMFERENCE;
33
+ this.dashOffset = computed(() => SPINNER_CIRCUMFERENCE * (1 - Math.min(Math.max(this.progress(), 0), 100) / 100), ...ngDevMode ? [{ debugName: "dashOffset" }] : /* istanbul ignore next */ []);
34
+ this.followTheme();
35
+ }
36
+ set(message, progress, progressMode = "indeterminate", type = "bar") {
37
+ if (message) this.message.set(message);
38
+ if (type) this.type.set(type);
39
+ this.progress.set(progress);
40
+ this.progressMode.set(progress > 0 ? "determinate" : progressMode);
41
+ }
42
+ static {
43
+ this.ɵfac = i0.ɵɵngDeclareFactory({
44
+ minVersion: "12.0.0",
45
+ version: "22.2.1",
46
+ ngImport: i0,
47
+ type: ShellBusyComponent,
48
+ deps: [],
49
+ target: i0.ɵɵFactoryTarget.Component
50
+ });
51
+ }
52
+ static {
53
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
54
+ minVersion: "17.0.0",
55
+ version: "22.2.1",
56
+ type: ShellBusyComponent,
57
+ isStandalone: true,
58
+ selector: "ars-shell-busy",
59
+ host: { properties: {
60
+ "class.ars-hidden": "!visible()",
61
+ "class.ars-dark": "dark()"
62
+ } },
63
+ ngImport: i0,
64
+ template: "<div class=\"scrim\"></div>\n\n@if (type() === 'hourglass' || type() === 'wait') {\n<div class=\"bare\">\n <svg class=\"hourglass\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M6 2h12v6l-4 4 4 4v6H6v-6l4-4-4-4zm2 2v3.2l4 4 4-4V4zm0 16h8v-3.2l-4-4-4 4z\" />\n </svg>\n @if (type() === 'hourglass' && message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n</div>\n} @else {\n<div class=\"panel\" role=\"status\" aria-live=\"polite\">\n @if (message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n\n @if (type() === 'bar') {\n <div class=\"bar\" [class.indeterminate]=\"progressMode() === 'indeterminate'\">\n <div class=\"bar-fill\" [style.width.%]=\"progressMode() === 'determinate' ? progress() : null\"></div>\n </div>\n } @else if (type() === 'spinner') {\n <svg class=\"spinner\" [class.indeterminate]=\"progressMode() === 'indeterminate'\" viewBox=\"0 0 70 70\"\n aria-hidden=\"true\">\n <circle class=\"track\" cx=\"35\" cy=\"35\" r=\"31.5\"></circle>\n <circle class=\"arc\" cx=\"35\" cy=\"35\" r=\"31.5\" [style.stroke-dasharray]=\"circumference\"\n [style.stroke-dashoffset]=\"progressMode() === 'determinate' ? dashOffset() : null\"></circle>\n </svg>\n }\n\n @if (progress() > 0) {\n <div class=\"percent\"><b>{{ progress() }}%</b></div>\n }\n</div>\n}\n",
65
+ styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .28));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #006a60));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-shell-busy-bare-color, #ffffff);color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-busy-z-index, 1090);display:flex;align-items:center;justify-content:center;font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);color:var(--_on-surface)}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .45));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #67d9c9));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #3a3d3c));color-scheme:dark}:host::backdrop{display:none}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-busy-fade .2s cubic-bezier(0,0,.2,1)}@keyframes ars-busy-fade{0%{opacity:0}to{opacity:1}}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-shell-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 0 rgba(0, 0, 0, .2), 0 6px 20px 0 rgba(0, 0, 0, .19));text-align:center}.bare{position:relative;display:flex;flex-direction:column;align-items:center;color:var(--_bare)}.message{font-size:larger;font-weight:600;padding-bottom:15px;text-align:center}.bare .message{padding-top:15px;padding-bottom:0}.percent{padding-top:8px;font-size:smaller}.bar{position:relative;max-width:350px;height:4px;margin:15px auto 0;overflow:hidden;border-radius:2px;background:var(--_track)}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--_primary);transition:width .12s linear}.bar.indeterminate .bar-fill{width:40%;transition:none;animation:ars-busy-bar 1.6s cubic-bezier(.65,.02,.35,1) infinite}@keyframes ars-busy-bar{0%{left:-40%}to{left:100%}}.spinner{display:block;width:70px;height:70px;margin:15px auto 0;transform:rotate(-90deg)}.spinner circle{fill:none;stroke-width:7;stroke-linecap:round}.spinner .track{stroke:var(--_track)}.spinner .arc{stroke:var(--_primary);transition:stroke-dashoffset .12s linear}.spinner.indeterminate{animation:ars-busy-rotate 1.4s linear infinite}.spinner.indeterminate .arc{transition:none;stroke-dashoffset:0;animation:ars-busy-dash 1.4s ease-in-out infinite}@keyframes ars-busy-rotate{0%{transform:rotate(-90deg)}to{transform:rotate(270deg)}}@keyframes ars-busy-dash{0%{stroke-dasharray:15 183}50%{stroke-dasharray:145 53}to{stroke-dasharray:15 183}}.hourglass{width:80px;height:80px;fill:currentColor;animation:ars-busy-hourglass 3s linear infinite}@keyframes ars-busy-hourglass{0%{transform:rotate(360deg)}to{transform:rotate(0)}}@media(prefers-reduced-motion:reduce){.scrim{animation:none}.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"],
66
+ dependencies: [{
67
+ kind: "pipe",
68
+ type: SafeHtmlPipe,
69
+ name: "safeHtml"
70
+ }],
71
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
72
+ });
73
+ }
74
+ };
75
+ i0.ɵɵngDeclareClassMetadata({
76
+ minVersion: "12.0.0",
77
+ version: "22.2.1",
78
+ ngImport: i0,
79
+ type: ShellBusyComponent,
80
+ decorators: [{
81
+ type: Component,
82
+ args: [{
83
+ selector: "ars-shell-busy",
84
+ standalone: true,
85
+ changeDetection: ChangeDetectionStrategy.OnPush,
86
+ imports: [SafeHtmlPipe],
87
+ host: {
88
+ "[class.ars-hidden]": "!visible()",
89
+ "[class.ars-dark]": "dark()"
90
+ },
91
+ template: "<div class=\"scrim\"></div>\n\n@if (type() === 'hourglass' || type() === 'wait') {\n<div class=\"bare\">\n <svg class=\"hourglass\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M6 2h12v6l-4 4 4 4v6H6v-6l4-4-4-4zm2 2v3.2l4 4 4-4V4zm0 16h8v-3.2l-4-4-4 4z\" />\n </svg>\n @if (type() === 'hourglass' && message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n</div>\n} @else {\n<div class=\"panel\" role=\"status\" aria-live=\"polite\">\n @if (message()) {\n <div class=\"message\" [innerHTML]=\"message() | safeHtml\"></div>\n }\n\n @if (type() === 'bar') {\n <div class=\"bar\" [class.indeterminate]=\"progressMode() === 'indeterminate'\">\n <div class=\"bar-fill\" [style.width.%]=\"progressMode() === 'determinate' ? progress() : null\"></div>\n </div>\n } @else if (type() === 'spinner') {\n <svg class=\"spinner\" [class.indeterminate]=\"progressMode() === 'indeterminate'\" viewBox=\"0 0 70 70\"\n aria-hidden=\"true\">\n <circle class=\"track\" cx=\"35\" cy=\"35\" r=\"31.5\"></circle>\n <circle class=\"arc\" cx=\"35\" cy=\"35\" r=\"31.5\" [style.stroke-dasharray]=\"circumference\"\n [style.stroke-dashoffset]=\"progressMode() === 'determinate' ? dashOffset() : null\"></circle>\n </svg>\n }\n\n @if (progress() > 0) {\n <div class=\"percent\"><b>{{ progress() }}%</b></div>\n }\n</div>\n}\n",
92
+ styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .28));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #006a60));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-shell-busy-bare-color, #ffffff);color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-busy-z-index, 1090);display:flex;align-items:center;justify-content:center;font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);color:var(--_on-surface)}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_scrim: var(--ars-shell-busy-scrim-color, rgba(0, 0, 0, .45));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #67d9c9));--_track: var(--ars-shell-busy-track-color, var(--mat-sys-surface-container-highest, #3a3d3c));color-scheme:dark}:host::backdrop{display:none}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-busy-fade .2s cubic-bezier(0,0,.2,1)}@keyframes ars-busy-fade{0%{opacity:0}to{opacity:1}}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-shell-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 0 rgba(0, 0, 0, .2), 0 6px 20px 0 rgba(0, 0, 0, .19));text-align:center}.bare{position:relative;display:flex;flex-direction:column;align-items:center;color:var(--_bare)}.message{font-size:larger;font-weight:600;padding-bottom:15px;text-align:center}.bare .message{padding-top:15px;padding-bottom:0}.percent{padding-top:8px;font-size:smaller}.bar{position:relative;max-width:350px;height:4px;margin:15px auto 0;overflow:hidden;border-radius:2px;background:var(--_track)}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--_primary);transition:width .12s linear}.bar.indeterminate .bar-fill{width:40%;transition:none;animation:ars-busy-bar 1.6s cubic-bezier(.65,.02,.35,1) infinite}@keyframes ars-busy-bar{0%{left:-40%}to{left:100%}}.spinner{display:block;width:70px;height:70px;margin:15px auto 0;transform:rotate(-90deg)}.spinner circle{fill:none;stroke-width:7;stroke-linecap:round}.spinner .track{stroke:var(--_track)}.spinner .arc{stroke:var(--_primary);transition:stroke-dashoffset .12s linear}.spinner.indeterminate{animation:ars-busy-rotate 1.4s linear infinite}.spinner.indeterminate .arc{transition:none;stroke-dashoffset:0;animation:ars-busy-dash 1.4s ease-in-out infinite}@keyframes ars-busy-rotate{0%{transform:rotate(-90deg)}to{transform:rotate(270deg)}}@keyframes ars-busy-dash{0%{stroke-dasharray:15 183}50%{stroke-dasharray:145 53}to{stroke-dasharray:15 183}}.hourglass{width:80px;height:80px;fill:currentColor;animation:ars-busy-hourglass 3s linear infinite}@keyframes ars-busy-hourglass{0%{transform:rotate(360deg)}to{transform:rotate(0)}}@media(prefers-reduced-motion:reduce){.scrim{animation:none}.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"]
93
+ }]
94
+ }],
95
+ ctorParameters: () => []
96
+ });
97
+ var ShellMessageComponent = class ShellMessageComponent {
98
+ followTheme() {
99
+ const resolve = (theme) => (theme === "auto" ? this.themeService.getTheme() : theme) === "dark";
100
+ this.dark.set(resolve(this.themeService.getTheme()));
101
+ this.themeService.changed.pipe(takeUntilDestroyed()).subscribe((theme) => this.dark.set(resolve(theme)));
102
+ }
103
+ constructor() {
104
+ this.closed = output();
105
+ this.themeService = inject(ThemeService);
106
+ this.panel = viewChild.required("panel", ...ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []);
107
+ this.okButton = viewChild.required("ok", ...ngDevMode ? [{ debugName: "okButton" }] : /* istanbul ignore next */ []);
108
+ this.titleId = `ars-shell-message-title-${Math.random().toString(36).slice(2, 10)}`;
109
+ this.detailsId = `ars-shell-message-details-${Math.random().toString(36).slice(2, 10)}`;
110
+ this.detailsOpen = signal(false, ...ngDevMode ? [{ debugName: "detailsOpen" }] : /* istanbul ignore next */ []);
111
+ this.data = signal({
112
+ message: "",
113
+ title: "Informazioni",
114
+ okCaption: "Ok",
115
+ width: 500
116
+ }, ...ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []);
117
+ this.copied = signal(false, ...ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []);
118
+ this.closing = false;
119
+ this.dark = signal(false, ...ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []);
120
+ this.followTheme();
121
+ afterNextRender(() => this.okButton().nativeElement.focus());
122
+ inject(DestroyRef).onDestroy(() => {
123
+ clearTimeout(this.dismissTimer);
124
+ clearTimeout(this.copiedTimer);
125
+ });
126
+ }
127
+ setData(data) {
128
+ this.data.set({
129
+ title: "Informazioni",
130
+ okCaption: "Ok",
131
+ width: 500,
132
+ ...data
133
+ });
134
+ this.detailsOpen.set(false);
135
+ this.scheduleDismiss(this.data().dismissAfter);
136
+ }
137
+ toggleDetails() {
138
+ this.detailsOpen.update((open) => !open);
139
+ }
140
+ close() {
141
+ if (this.closing) return;
142
+ this.closing = true;
143
+ clearTimeout(this.dismissTimer);
144
+ this.closed.emit();
145
+ }
146
+ onKeydown(e) {
147
+ if (e.key === "Escape") {
148
+ e.preventDefault();
149
+ this.close();
150
+ return;
151
+ }
152
+ if (e.key === "Tab") this.trapFocus(e);
153
+ }
154
+ trapFocus(e) {
155
+ const focusable = Array.from(this.panel().nativeElement.querySelectorAll("button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])"));
156
+ if (!focusable.length) return;
157
+ const first = focusable[0];
158
+ const last = focusable[focusable.length - 1];
159
+ const active = document.activeElement;
160
+ if (e.shiftKey && (active === first || !this.panel().nativeElement.contains(active))) {
161
+ e.preventDefault();
162
+ last.focus();
163
+ } else if (!e.shiftKey && active === last) {
164
+ e.preventDefault();
165
+ first.focus();
166
+ }
167
+ }
168
+ scheduleDismiss(dismissAfter) {
169
+ clearTimeout(this.dismissTimer);
170
+ this.dismissTimer = void 0;
171
+ if (dismissAfter) this.dismissTimer = setTimeout(() => this.close(), dismissAfter);
172
+ }
173
+ async copy() {
174
+ const content = this.panel().nativeElement.querySelector(".content");
175
+ if (!content) return;
176
+ try {
177
+ await navigator.clipboard.write([new ClipboardItem({
178
+ "text/html": new Blob([content.innerHTML], { type: "text/html" }),
179
+ "text/plain": new Blob([content.innerText], { type: "text/plain" })
180
+ })]);
181
+ this.copied.set(true);
182
+ clearTimeout(this.copiedTimer);
183
+ this.copiedTimer = setTimeout(() => this.copied.set(false), 1500);
184
+ } catch {}
185
+ }
186
+ static {
187
+ this.ɵfac = i0.ɵɵngDeclareFactory({
188
+ minVersion: "12.0.0",
189
+ version: "22.2.1",
190
+ ngImport: i0,
191
+ type: ShellMessageComponent,
192
+ deps: [],
193
+ target: i0.ɵɵFactoryTarget.Component
194
+ });
195
+ }
196
+ static {
197
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
198
+ minVersion: "17.0.0",
199
+ version: "22.2.1",
200
+ type: ShellMessageComponent,
201
+ isStandalone: true,
202
+ selector: "ars-shell-message",
203
+ outputs: { closed: "closed" },
204
+ host: {
205
+ listeners: { "document:keydown": "onKeydown($event)" },
206
+ properties: { "class.ars-dark": "dark()" }
207
+ },
208
+ viewQueries: [{
209
+ propertyName: "panel",
210
+ first: true,
211
+ predicate: ["panel"],
212
+ descendants: true,
213
+ isSignal: true
214
+ }, {
215
+ propertyName: "okButton",
216
+ first: true,
217
+ predicate: ["ok"],
218
+ descendants: true,
219
+ isSignal: true
220
+ }],
221
+ ngImport: i0,
222
+ template: "<div class=\"scrim\" (click)=\"close()\"></div>\n\n<div #panel class=\"panel\" role=\"alertdialog\" aria-modal=\"true\" [attr.aria-labelledby]=\"titleId\"\n [style.max-width.px]=\"data().width\">\n\n <h2 class=\"title\" [id]=\"titleId\" [innerHTML]=\"data().title | safeHtml\"></h2>\n\n <div class=\"content\">\n @if (data().message) {\n <div class=\"message\" [innerHTML]=\"data().message | safeHtml\"></div>\n }\n @if (data().details) {\n <div class=\"details-box\" [class.open]=\"detailsOpen()\">\n <button type=\"button\" class=\"details-header\" (click)=\"toggleDetails()\"\n [attr.aria-expanded]=\"detailsOpen()\" [attr.aria-controls]=\"detailsId\">\n <span class=\"details-label\">Dettaglio</span>\n <svg class=\"chevron\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z\" />\n </svg>\n </button>\n <div class=\"details-body\">\n <div class=\"details\" [id]=\"detailsId\" [attr.aria-hidden]=\"!detailsOpen()\">\n <div class=\"details-text\" [innerHTML]=\"data().details | safeHtml\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"actions\">\n <button type=\"button\" class=\"icon-button\" (click)=\"copy()\"\n [title]=\"copied() ? 'Copiato negli appunti' : 'Copia negli appunti'\" aria-label=\"Copia negli appunti\">\n @if (copied()) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z\" /></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path\n d=\"M16 1H4a2 2 0 0 0-2 2v14h2V3h12zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m0 16H8V7h11z\" /></svg>\n }\n </button>\n <button #ok type=\"button\" class=\"flat-button\" (click)=\"close()\">{{ data().okCaption }}</button>\n </div>\n</div>\n",
223
+ styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f));--_divider: var(--ars-color-divider, #757d87);--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .24));--_primary: var(--ars-button-background-color, var(--mat-sys-primary, #006a60));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));--_hover: var(--ars-item-hover-background-color, rgba(0, 0, 0, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .05));color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-dialog-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #b1ccc6));--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .45));--_primary: var(---ars-button-background-color, var(--mat-sys-primary, #67d9c9));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #00382f));--_hover: var(--ars-item-hover-background-color, rgba(255, 255, 255, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .22));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-shell-fade .2s cubic-bezier(0,0,.2,1)}.panel{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;margin:12px;max-height:calc(100% - 24px);box-sizing:border-box;border-radius:var(--ars-shell-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 3px rgba(0, 0, 0, .15), 0 1px 3px 0 rgba(0, 0, 0, .3));font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);text-align:left;animation:ars-shell-enter .15s cubic-bezier(0,0,.2,1)}.title{margin:0;padding:24px 24px 16px;font:var(--mat-sys-headline-small, 400 24px/32px Roboto, sans-serif);color:inherit}.content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:0;font:var(--mat-sys-body-medium, 400 14px/20px Roboto, sans-serif)}.message{flex:0 1 auto;min-height:0;padding:0 24px 24px;overflow-y:auto;overscroll-behavior:contain}.details-box{flex:0 1000 auto;min-height:0;display:flex;flex-direction:column;margin:0 16px 4px;border-radius:12px;background:transparent;transition:background-color .2s cubic-bezier(0,0,.2,1)}.details-box.open{background:var(--_sunken)}.details-header{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;height:44px;padding:0 8px;border-radius:12px;background:transparent;color:var(--_secondary);text-align:left}.details-header:hover{background:var(--_hover)}.details-label{font-size:.75em;letter-spacing:.06em;text-transform:uppercase}.chevron{width:20px;height:20px;fill:currentColor;transition:transform .2s cubic-bezier(0,0,.2,1)}.details-box.open .chevron{transform:rotate(180deg)}.details-body{display:grid;grid-template-rows:0fr;min-height:0;max-height:320px;transition:grid-template-rows .2s cubic-bezier(0,0,.2,1)}.details-box.open .details-body{grid-template-rows:1fr}.details{min-height:0;overflow:hidden}.details-box.open .details{overflow-y:auto;overscroll-behavior:contain}.details-text{padding:0 8px 12px;font-size:.85em}.actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:16px 24px 24px}button{font-family:inherit;cursor:pointer;border:none}button:focus-visible{outline:none!important;box-shadow:none!important}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border-radius:50%;background:transparent;color:inherit}.icon-button svg{width:24px;height:24px;fill:currentColor}.icon-button:hover,.icon-button:focus-visible{background:var(--_hover)!important}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--_primary);color:var(--_on-primary);font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover,.flat-button:focus-visible{filter:brightness(1.08)!important}@keyframes ars-shell-fade{0%{opacity:0}to{opacity:1}}@keyframes ars-shell-enter{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.scrim,.panel{animation:none}.details-box,.details-body,.chevron{transition:none}}\n"],
224
+ dependencies: [{
225
+ kind: "pipe",
226
+ type: SafeHtmlPipe,
227
+ name: "safeHtml"
228
+ }],
229
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
230
+ });
231
+ }
232
+ };
233
+ i0.ɵɵngDeclareClassMetadata({
234
+ minVersion: "12.0.0",
235
+ version: "22.2.1",
236
+ ngImport: i0,
237
+ type: ShellMessageComponent,
238
+ decorators: [{
239
+ type: Component,
240
+ args: [{
241
+ selector: "ars-shell-message",
242
+ standalone: true,
243
+ changeDetection: ChangeDetectionStrategy.OnPush,
244
+ imports: [SafeHtmlPipe],
245
+ host: { "[class.ars-dark]": "dark()" },
246
+ template: "<div class=\"scrim\" (click)=\"close()\"></div>\n\n<div #panel class=\"panel\" role=\"alertdialog\" aria-modal=\"true\" [attr.aria-labelledby]=\"titleId\"\n [style.max-width.px]=\"data().width\">\n\n <h2 class=\"title\" [id]=\"titleId\" [innerHTML]=\"data().title | safeHtml\"></h2>\n\n <div class=\"content\">\n @if (data().message) {\n <div class=\"message\" [innerHTML]=\"data().message | safeHtml\"></div>\n }\n @if (data().details) {\n <div class=\"details-box\" [class.open]=\"detailsOpen()\">\n <button type=\"button\" class=\"details-header\" (click)=\"toggleDetails()\"\n [attr.aria-expanded]=\"detailsOpen()\" [attr.aria-controls]=\"detailsId\">\n <span class=\"details-label\">Dettaglio</span>\n <svg class=\"chevron\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path d=\"M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z\" />\n </svg>\n </button>\n <div class=\"details-body\">\n <div class=\"details\" [id]=\"detailsId\" [attr.aria-hidden]=\"!detailsOpen()\">\n <div class=\"details-text\" [innerHTML]=\"data().details | safeHtml\"></div>\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"actions\">\n <button type=\"button\" class=\"icon-button\" (click)=\"copy()\"\n [title]=\"copied() ? 'Copiato negli appunti' : 'Copia negli appunti'\" aria-label=\"Copia negli appunti\">\n @if (copied()) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z\" /></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path\n d=\"M16 1H4a2 2 0 0 0-2 2v14h2V3h12zm3 4H8a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2m0 16H8V7h11z\" /></svg>\n }\n </button>\n <button #ok type=\"button\" class=\"flat-button\" (click)=\"close()\">{{ data().okCaption }}</button>\n </div>\n</div>\n",
247
+ styles: [":host{--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f));--_divider: var(--ars-color-divider, #757d87);--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .24));--_primary: var(--ars-button-background-color, var(--mat-sys-primary, #006a60));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));--_hover: var(--ars-item-hover-background-color, rgba(0, 0, 0, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .05));color-scheme:light;margin:0;border:0;padding:0;background:none;color:inherit;overflow:visible;width:100%;height:100%;position:fixed;inset:0;z-index:var(--ars-shell-dialog-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-shell-dialog-background-color, var(--mat-sys-surface-container-high, #282a29));--_on-surface: var(--ars-color-text, var(--mat-sys-on-surface, #e0e2e5));--_secondary: var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #b1ccc6));--_scrim: var(--ars-shell-dialog-scrim-color, rgba(0, 0, 0, .45));--_primary: var(---ars-button-background-color, var(--mat-sys-primary, #67d9c9));--_on-primary: var(--ars-color-on-primary, var(--mat-sys-on-primary, #00382f));--_hover: var(--ars-item-hover-background-color, rgba(255, 255, 255, .08));--_sunken: var(--ars-shell-dialog-sunken-background-color, rgba(0, 0, 0, .22));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:ars-shell-fade .2s cubic-bezier(0,0,.2,1)}.panel{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;margin:12px;max-height:calc(100% - 24px);box-sizing:border-box;border-radius:var(--ars-shell-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-shell-dialog-elevation, 0 4px 8px 3px rgba(0, 0, 0, .15), 0 1px 3px 0 rgba(0, 0, 0, .3));font-family:var(--mat-sys-body-medium-font, Roboto, sans-serif);text-align:left;animation:ars-shell-enter .15s cubic-bezier(0,0,.2,1)}.title{margin:0;padding:24px 24px 16px;font:var(--mat-sys-headline-small, 400 24px/32px Roboto, sans-serif);color:inherit}.content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:0;font:var(--mat-sys-body-medium, 400 14px/20px Roboto, sans-serif)}.message{flex:0 1 auto;min-height:0;padding:0 24px 24px;overflow-y:auto;overscroll-behavior:contain}.details-box{flex:0 1000 auto;min-height:0;display:flex;flex-direction:column;margin:0 16px 4px;border-radius:12px;background:transparent;transition:background-color .2s cubic-bezier(0,0,.2,1)}.details-box.open{background:var(--_sunken)}.details-header{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;height:44px;padding:0 8px;border-radius:12px;background:transparent;color:var(--_secondary);text-align:left}.details-header:hover{background:var(--_hover)}.details-label{font-size:.75em;letter-spacing:.06em;text-transform:uppercase}.chevron{width:20px;height:20px;fill:currentColor;transition:transform .2s cubic-bezier(0,0,.2,1)}.details-box.open .chevron{transform:rotate(180deg)}.details-body{display:grid;grid-template-rows:0fr;min-height:0;max-height:320px;transition:grid-template-rows .2s cubic-bezier(0,0,.2,1)}.details-box.open .details-body{grid-template-rows:1fr}.details{min-height:0;overflow:hidden}.details-box.open .details{overflow-y:auto;overscroll-behavior:contain}.details-text{padding:0 8px 12px;font-size:.85em}.actions{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:16px 24px 24px}button{font-family:inherit;cursor:pointer;border:none}button:focus-visible{outline:none!important;box-shadow:none!important}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;border-radius:50%;background:transparent;color:inherit}.icon-button svg{width:24px;height:24px;fill:currentColor}.icon-button:hover,.icon-button:focus-visible{background:var(--_hover)!important}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--_primary);color:var(--_on-primary);font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover,.flat-button:focus-visible{filter:brightness(1.08)!important}@keyframes ars-shell-fade{0%{opacity:0}to{opacity:1}}@keyframes ars-shell-enter{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){.scrim,.panel{animation:none}.details-box,.details-body,.chevron{transition:none}}\n"]
248
+ }]
249
+ }],
250
+ ctorParameters: () => [],
251
+ propDecorators: {
252
+ closed: [{
253
+ type: i0.Output,
254
+ args: ["closed"]
255
+ }],
256
+ panel: [{
257
+ type: i0.ViewChild,
258
+ args: ["panel", { isSignal: true }]
259
+ }],
260
+ okButton: [{
261
+ type: i0.ViewChild,
262
+ args: ["ok", { isSignal: true }]
263
+ }],
264
+ onKeydown: [{
265
+ type: HostListener,
266
+ args: ["document:keydown", ["$event"]]
267
+ }]
268
+ }
269
+ });
320
270
  const CLEAR_BUSY_GRACE = 500;
321
- /**
322
- * Everything the application can say to the user without asking anything back: a message, an
323
- * error, and the wait overlay. No Angular Material behind any of it.
324
- *
325
- * `DialogService` extends this class, so a route component injects `DialogService` and has both
326
- * halves; shell, HTTP interceptors, route guards and app initializers inject `ShellService` and
327
- * pay for neither `mat-dialog` nor `cdk/overlay` on the boot path — measured at 445 KB of the
328
- * 905 KB initial bundle on myARS, more than every route of the application put together.
329
- *
330
- * The methods keep the parameter order of the `DialogService` ones they came from, so no call
331
- * site changed when they moved here.
332
- *
333
- * ## Stacking
334
- *
335
- * Since Angular CDK 22 every overlay is opened as `popover="manual"`, so a Material dialog is not
336
- * at z-index 1000 any more: it is in the browser top layer, which is painted above the whole
337
- * document whatever the z-index of the document says. A number, however large, cannot get above
338
- * it — so these overlays are promoted to the top layer as well, and the order there is the order
339
- * of promotion: whatever went up last is on top.
340
- *
341
- * That gives the right answer by itself in the case that matters, an error raised while a dialog
342
- * is open. The other three are arranged explicitly: `setBusy()` puts the message back on top
343
- * after raising the busy overlay, `DialogService.open()` calls {@link raiseMessage} after opening
344
- * a dialog, and it also takes the busy away immediately rather than with the grace period, so a
345
- * dialog is never drawn under an overlay that is on its way out.
346
- *
347
- * The `--ars-shell-dialog-z-index` / `--ars-shell-busy-z-index` pair (1100 / 1090, above the CDK's 1000) is
348
- * still there and still correct: it is what orders the overlays on a browser without the Popover
349
- * API, where the CDK does not use it either.
350
- */
351
- class ShellService {
352
- constructor() {
353
- this.appRef = inject(ApplicationRef);
354
- this.environmentInjector = inject(EnvironmentInjector);
355
- this.busyVisible = false;
356
- }
357
- // ---------------------------------------------------------------------------
358
- // Messages
359
- // ---------------------------------------------------------------------------
360
- /**
361
- * Shows an informational message.
362
- * @param message - HTML message to display.
363
- * @param title - Heading. Defaults to `'Informazioni'`.
364
- * @param okCaption - Dismiss button label. Defaults to `'Ok'`.
365
- * @param width - Maximum panel width in pixels. Defaults to `500`.
366
- * @param dismissAfter - Auto-close delay in milliseconds (optional).
367
- * @param details - Optional secondary details text.
368
- * @returns A handle over the open message, or `null` outside the browser.
369
- */
370
- info(message, title = 'Informazioni', okCaption = 'Ok', width = 500, dismissAfter, details) {
371
- this.clearBusy();
372
- return this.showMessage('info', { message, title, okCaption, width, dismissAfter, details });
373
- }
374
- /**
375
- * Shows an error message. When one is already on screen its content is replaced in place: a
376
- * failing API answers many requests at once, and each of them must not add a panel of its own.
377
- * @param message - HTML error message to display.
378
- * @param log - Optional technical log or stack trace shown in the details panel.
379
- * @param title - Heading. Defaults to `'Errore'`.
380
- * @param okCaption - Dismiss button label. Defaults to `'Ok'`.
381
- * @param width - Maximum panel width in pixels. Defaults to `500`.
382
- * @param dismissAfter - Auto-close delay in milliseconds (optional).
383
- * @returns A handle over the open message, or `null` outside the browser.
384
- */
385
- error(message, log, title = 'Errore', okCaption = 'Ok', width = 500, dismissAfter) {
386
- this.clearBusy();
387
- return this.showMessage('error', { message, title, okCaption, width, dismissAfter, details: log });
388
- }
389
- /**
390
- * Closes the message currently on screen, if any. Does not touch the busy overlay.
391
- * @returns void
392
- */
393
- closeMessage() {
394
- if (!this.messageRef)
395
- return;
396
- const ref = this.messageRef;
397
- this.messageRef = undefined;
398
- this.messageKind = undefined;
399
- this.leaveTopLayer(ref.location.nativeElement);
400
- this.appRef.detachView(ref.hostView);
401
- ref.destroy();
402
- if (this.previousOverflow !== undefined) {
403
- document.body.style.overflow = this.previousOverflow;
404
- this.previousOverflow = undefined;
405
- }
406
- this.previouslyFocused?.focus();
407
- this.previouslyFocused = undefined;
408
- // Last, so that whoever is waiting on it finds the screen already in its final state.
409
- const closed = this.messageClosed;
410
- this.messageClosed = undefined;
411
- closed?.next();
412
- closed?.complete();
413
- }
414
- /**
415
- * Opens the message panel, or updates the one already open when it is showing the same kind of
416
- * message.
417
- * @param kind - Whether this is an informational or an error message.
418
- * @param data - The content to display.
419
- * @returns A handle over the open message, or `null` outside the browser.
420
- */
421
- showMessage(kind, data) {
422
- if (!SystemUtils.isBrowser())
423
- return null;
424
- if (this.messageRef && this.messageKind === kind) {
425
- this.messageRef.instance.setData(data);
426
- this.messageRef.changeDetectorRef.detectChanges();
427
- // Back to the front: a dialog may have been opened since this panel went up, and the top
428
- // layer orders by promotion, not by z-index.
429
- this.raiseTopLayer(this.messageRef.location.nativeElement);
430
- return this.messageHandle();
431
- }
432
- // A message of a different kind replaces the one on screen rather than stacking on it.
433
- this.closeMessage();
434
- const ref = createComponent(ShellMessageComponent, { environmentInjector: this.environmentInjector });
435
- ref.instance.setData(data);
436
- ref.instance.closed.subscribe(() => this.closeMessage());
437
- // One subject per panel, not per handle: an `error()` that replaces the text of the message
438
- // already on screen hands out a second handle, and both must fire when that message goes.
439
- this.messageClosed = new Subject();
440
- this.previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : undefined;
441
- this.previousOverflow = document.body.style.overflow;
442
- document.body.style.overflow = 'hidden';
443
- this.appRef.attachView(ref.hostView);
444
- document.body.appendChild(ref.location.nativeElement);
445
- this.enterTopLayer(ref.location.nativeElement);
446
- this.messageRef = ref;
447
- this.messageKind = kind;
448
- return this.messageHandle();
449
- }
450
- /**
451
- * Builds the handle returned to callers.
452
- *
453
- * The subject is captured now rather than read at call time, so `afterClosed()` keeps
454
- * answering about the message this handle was given for, even once another one has taken
455
- * its place on screen.
456
- * @returns The handle over the message currently on screen.
457
- */
458
- messageHandle() {
459
- const closed = this.messageClosed;
460
- return {
461
- close: () => this.closeMessage(),
462
- afterClosed: () => closed.asObservable()
463
- };
464
- }
465
- // ---------------------------------------------------------------------------
466
- // Busy overlay
467
- // ---------------------------------------------------------------------------
468
- /**
469
- * Shows or updates the busy overlay.
470
- *
471
- * Called dozens of times per screen, so it creates nothing after the first time: the component
472
- * is built once and then kept, and showing it again is a class on the host element. The state
473
- * is written to signals and checked synchronously before returning, so the message on screen is
474
- * always the last one asked for — a second `busy()` in the same task never leaves the previous
475
- * text behind.
476
- * @param message - Text to display. An empty string keeps the current one.
477
- * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.
478
- * @param progressMode - Progress mode. Defaults to `'indeterminate'`.
479
- * @param type - Visual style of the overlay. Defaults to `'bar'`.
480
- * @param action - Optional observable; the overlay is dismissed when it first emits. It
481
- * replaces the one passed to a previous call, which is unsubscribed.
482
- * @returns `true` if the overlay was brought up by this call, `false` if it was already up.
483
- */
484
- setBusy(message, progress = -1, progressMode = 'indeterminate', type = 'bar', action) {
485
- if (!SystemUtils.isBrowser())
486
- return false;
487
- // Cancel any pending clearBusy(): it must never take away an overlay just asked for.
488
- clearTimeout(this.clearBusyTimer);
489
- this.clearBusyTimer = undefined;
490
- const created = !this.busyVisible;
491
- const ref = this.ensureBusy();
492
- ref.instance.set(message, progress, progressMode, type);
493
- ref.instance.visible.set(true);
494
- this.busyVisible = true;
495
- ref.changeDetectorRef.detectChanges();
496
- this.enterTopLayer(ref.location.nativeElement);
497
- // The overlay has just been promoted, so it is the topmost element of the top layer: put the
498
- // message back above it, which is what the 1100/1090 pair used to do on its own.
499
- this.raiseMessage();
500
- // The action always replaces the previous one. Keeping the first, as the previous
501
- // implementation did, meant the overlay outlived the operation that was actually running.
502
- this.busyActionSubscription?.unsubscribe();
503
- this.busyActionSubscription = action?.pipe(first()).subscribe(() => this.clearBusy());
504
- return created;
505
- }
506
- /**
507
- * Shows or updates the busy overlay using the progress-bar style.
508
- * @param message - Text to display inside the overlay.
509
- * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.
510
- * @param progressMode - Progress mode. Defaults to `'indeterminate'`.
511
- * @param action - Optional observable; the overlay is dismissed when it emits.
512
- * @returns `true` if the overlay was brought up by this call.
513
- */
514
- busy(message, progress = -1, progressMode = 'indeterminate', action) {
515
- return this.setBusy(message, progress, progressMode, 'bar', action);
516
- }
517
- /**
518
- * Shows or updates the busy overlay using the spinner style.
519
- * @param message - Text to display inside the overlay.
520
- * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.
521
- * @param progressMode - Progress mode. Defaults to `'indeterminate'`.
522
- * @param action - Optional observable; the overlay is dismissed when it emits.
523
- * @returns `true` if the overlay was brought up by this call.
524
- */
525
- busySpinner(message, progress = -1, progressMode = 'indeterminate', action) {
526
- return this.setBusy(message, progress, progressMode, 'spinner', action);
527
- }
528
- /**
529
- * Shows or updates the busy overlay using the hourglass style.
530
- * @param message - Text to display inside the overlay.
531
- * @param action - Optional observable; the overlay is dismissed when it emits.
532
- * @returns `true` if the overlay was brought up by this call.
533
- */
534
- busyHourglass(message, action) {
535
- return this.setBusy(message, -1, 'indeterminate', 'hourglass', action);
536
- }
537
- /**
538
- * Shows a bare wait indicator, with no panel and no message.
539
- * @param action - Optional observable; the overlay is dismissed when it emits.
540
- * @returns `true` if the overlay was brought up by this call.
541
- */
542
- wait(action) {
543
- return this.setBusy('', -1, 'indeterminate', 'wait', action);
544
- }
545
- /**
546
- * Returns a {@link BusyTimer} that brings the overlay up only if the operation is still running
547
- * after a debounce delay.
548
- * @param message - Text to display. Defaults to `'Operazione in corso...'`.
549
- * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.
550
- * @returns A timer that must be disposed with `clear()` when the operation ends.
551
- */
552
- busyTimer(message = 'Operazione in corso...', due = 100) {
553
- return new BusyTimer(this, due, message);
554
- }
555
- /**
556
- * Takes the busy overlay away.
557
- *
558
- * By default it waits a short grace period, so that a chain of quick operations does not make
559
- * the overlay blink between one and the next; any `setBusy()` in the meantime cancels the
560
- * pending removal. Pass `true` when something is about to be drawn underneath it and the wait
561
- * would be visible — that is what `DialogService.open()` does.
562
- * @param immediate - When `true`, removes the overlay without waiting for the grace period.
563
- * @returns void
564
- */
565
- clearBusy(immediate = false) {
566
- clearTimeout(this.clearBusyTimer);
567
- this.clearBusyTimer = undefined;
568
- if (immediate) {
569
- this.hideBusy();
570
- return;
571
- }
572
- this.clearBusyTimer = setTimeout(() => {
573
- this.clearBusyTimer = undefined;
574
- this.hideBusy();
575
- }, CLEAR_BUSY_GRACE);
576
- }
577
- /**
578
- * Hides the busy overlay without destroying it, so the next call can reuse the instance.
579
- * @returns void
580
- */
581
- hideBusy() {
582
- this.busyActionSubscription?.unsubscribe();
583
- this.busyActionSubscription = undefined;
584
- if (!this.busyVisible)
585
- return;
586
- this.busyVisible = false;
587
- this.busyRef?.instance.visible.set(false);
588
- this.busyRef?.changeDetectorRef.detectChanges();
589
- this.leaveTopLayer(this.busyRef?.location.nativeElement);
590
- }
591
- /**
592
- * Returns the busy component, creating and attaching it the first time it is needed.
593
- * @returns The reused busy component reference.
594
- */
595
- ensureBusy() {
596
- if (this.busyRef)
597
- return this.busyRef;
598
- const ref = createComponent(ShellBusyComponent, { environmentInjector: this.environmentInjector });
599
- this.appRef.attachView(ref.hostView);
600
- document.body.appendChild(ref.location.nativeElement);
601
- this.busyRef = ref;
602
- return ref;
603
- }
604
- // ---------------------------------------------------------------------------
605
- // Top layer
606
- // ---------------------------------------------------------------------------
607
- /**
608
- * Brings the message currently on screen back to the front of the top layer, if there is one.
609
- *
610
- * Called by `DialogService` after opening a dialog: an error the user has not dismissed yet
611
- * must stay above whatever is opened underneath it.
612
- * @returns void
613
- */
614
- raiseMessage() {
615
- this.raiseTopLayer(this.messageRef?.location.nativeElement);
616
- }
617
- /**
618
- * Whether the browser can promote an element to the top layer through the Popover API.
619
- *
620
- * Angular CDK 22 opens every overlay as `popover="manual"`, so a Material dialog does not sit
621
- * at z-index 1000 any more: it sits in the top layer, which is painted above the whole page
622
- * whatever the z-index of the page says. The only way to put something above it is to be in
623
- * the top layer too. Where the API is missing the overlays fall back to their z-index, which
624
- * is also the CDK's own fallback and therefore still the right order.
625
- * @returns `true` when `showPopover()` is available.
626
- */
627
- supportsTopLayer() {
628
- if (this.topLayerSupport === undefined) {
629
- this.topLayerSupport = SystemUtils.isBrowser()
630
- && typeof HTMLElement !== 'undefined'
631
- && 'showPopover' in HTMLElement.prototype;
632
- }
633
- return this.topLayerSupport;
634
- }
635
- /**
636
- * Promotes an overlay to the top layer, marking it as a manual popover the first time.
637
- *
638
- * `manual` and not `auto`: an `auto` popover is light-dismissed by a click anywhere and closed
639
- * by Escape without telling anyone, which would take the panel off the screen behind the back
640
- * of the component that owns it.
641
- * @param el - The host element of the overlay. Ignored when undefined.
642
- * @returns void
643
- */
644
- enterTopLayer(el) {
645
- if (!el || !this.supportsTopLayer())
646
- return;
647
- if (el.getAttribute('popover') !== 'manual')
648
- el.setAttribute('popover', 'manual');
649
- // Throws if the element is already showing, or is not connected yet.
650
- try {
651
- if (!el.matches(':popover-open'))
652
- el.showPopover();
653
- }
654
- catch { /* already up */ }
655
- }
656
- /**
657
- * Takes an overlay out of the top layer.
658
- * @param el - The host element of the overlay. Ignored when undefined.
659
- * @returns void
660
- */
661
- leaveTopLayer(el) {
662
- if (!el || !this.supportsTopLayer())
663
- return;
664
- try {
665
- if (el.matches(':popover-open'))
666
- el.hidePopover();
667
- }
668
- catch { /* already down */ }
669
- }
670
- /**
671
- * Moves an overlay back to the front of the top layer.
672
- *
673
- * The top layer is a stack ordered by promotion, so the only way to raise an element already
674
- * in it is to take it out and put it back.
675
- * @param el - The host element of the overlay. Ignored when undefined.
676
- * @returns void
677
- */
678
- raiseTopLayer(el) {
679
- if (!el || !this.supportsTopLayer())
680
- return;
681
- if (!el.matches(':popover-open')) {
682
- this.enterTopLayer(el);
683
- return;
684
- }
685
- try {
686
- el.hidePopover();
687
- el.showPopover();
688
- }
689
- catch { /* the element is on its way out */ }
690
- }
691
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
692
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.7", ngImport: i0, type: ShellService }); }
693
- }
694
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: ShellService, decorators: [{
695
- type: Service
696
- }] });
697
-
698
- /*
699
- * Public API Surface of @arsedizioni/ars-utils/ui.shell
700
- */
701
-
702
- /**
703
- * Generated bundle index. Do not edit.
704
- */
705
-
271
+ var ShellService = class ShellService {
272
+ constructor() {
273
+ this.appRef = inject(ApplicationRef);
274
+ this.environmentInjector = inject(EnvironmentInjector);
275
+ this.busyVisible = false;
276
+ }
277
+ info(message, title = "Informazioni", okCaption = "Ok", width = 500, dismissAfter, details) {
278
+ this.clearBusy();
279
+ return this.showMessage("info", {
280
+ message,
281
+ title,
282
+ okCaption,
283
+ width,
284
+ dismissAfter,
285
+ details
286
+ });
287
+ }
288
+ error(message, log, title = "Errore", okCaption = "Ok", width = 500, dismissAfter) {
289
+ this.clearBusy();
290
+ return this.showMessage("error", {
291
+ message,
292
+ title,
293
+ okCaption,
294
+ width,
295
+ dismissAfter,
296
+ details: log
297
+ });
298
+ }
299
+ closeMessage() {
300
+ if (!this.messageRef) return;
301
+ const ref = this.messageRef;
302
+ this.messageRef = void 0;
303
+ this.messageKind = void 0;
304
+ this.leaveTopLayer(ref.location.nativeElement);
305
+ this.appRef.detachView(ref.hostView);
306
+ ref.destroy();
307
+ if (this.previousOverflow !== void 0) {
308
+ document.body.style.overflow = this.previousOverflow;
309
+ this.previousOverflow = void 0;
310
+ }
311
+ this.previouslyFocused?.focus();
312
+ this.previouslyFocused = void 0;
313
+ const closed = this.messageClosed;
314
+ this.messageClosed = void 0;
315
+ closed?.next();
316
+ closed?.complete();
317
+ }
318
+ showMessage(kind, data) {
319
+ if (!SystemUtils.isBrowser()) return null;
320
+ if (this.messageRef && this.messageKind === kind) {
321
+ this.messageRef.instance.setData(data);
322
+ this.messageRef.changeDetectorRef.detectChanges();
323
+ this.raiseTopLayer(this.messageRef.location.nativeElement);
324
+ return this.messageHandle();
325
+ }
326
+ this.closeMessage();
327
+ const ref = createComponent(ShellMessageComponent, { environmentInjector: this.environmentInjector });
328
+ ref.instance.setData(data);
329
+ ref.instance.closed.subscribe(() => this.closeMessage());
330
+ this.messageClosed = new Subject();
331
+ this.previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : void 0;
332
+ this.previousOverflow = document.body.style.overflow;
333
+ document.body.style.overflow = "hidden";
334
+ this.appRef.attachView(ref.hostView);
335
+ document.body.appendChild(ref.location.nativeElement);
336
+ this.enterTopLayer(ref.location.nativeElement);
337
+ this.messageRef = ref;
338
+ this.messageKind = kind;
339
+ return this.messageHandle();
340
+ }
341
+ messageHandle() {
342
+ const closed = this.messageClosed;
343
+ return {
344
+ close: () => this.closeMessage(),
345
+ afterClosed: () => closed.asObservable()
346
+ };
347
+ }
348
+ setBusy(message, progress = -1, progressMode = "indeterminate", type = "bar", action) {
349
+ if (!SystemUtils.isBrowser()) return false;
350
+ clearTimeout(this.clearBusyTimer);
351
+ this.clearBusyTimer = void 0;
352
+ const created = !this.busyVisible;
353
+ const ref = this.ensureBusy();
354
+ ref.instance.set(message, progress, progressMode, type);
355
+ ref.instance.visible.set(true);
356
+ this.busyVisible = true;
357
+ ref.changeDetectorRef.detectChanges();
358
+ this.enterTopLayer(ref.location.nativeElement);
359
+ this.raiseMessage();
360
+ this.busyActionSubscription?.unsubscribe();
361
+ this.busyActionSubscription = action?.pipe(first()).subscribe(() => this.clearBusy());
362
+ return created;
363
+ }
364
+ busy(message, progress = -1, progressMode = "indeterminate", action) {
365
+ return this.setBusy(message, progress, progressMode, "bar", action);
366
+ }
367
+ busySpinner(message, progress = -1, progressMode = "indeterminate", action) {
368
+ return this.setBusy(message, progress, progressMode, "spinner", action);
369
+ }
370
+ busyHourglass(message, action) {
371
+ return this.setBusy(message, -1, "indeterminate", "hourglass", action);
372
+ }
373
+ wait(action) {
374
+ return this.setBusy("", -1, "indeterminate", "wait", action);
375
+ }
376
+ busyTimer(message = "Operazione in corso...", due = 100) {
377
+ return new BusyTimer(this, due, message);
378
+ }
379
+ clearBusy(immediate = false) {
380
+ clearTimeout(this.clearBusyTimer);
381
+ this.clearBusyTimer = void 0;
382
+ if (immediate) {
383
+ this.hideBusy();
384
+ return;
385
+ }
386
+ this.clearBusyTimer = setTimeout(() => {
387
+ this.clearBusyTimer = void 0;
388
+ this.hideBusy();
389
+ }, CLEAR_BUSY_GRACE);
390
+ }
391
+ hideBusy() {
392
+ this.busyActionSubscription?.unsubscribe();
393
+ this.busyActionSubscription = void 0;
394
+ if (!this.busyVisible) return;
395
+ this.busyVisible = false;
396
+ this.busyRef?.instance.visible.set(false);
397
+ this.busyRef?.changeDetectorRef.detectChanges();
398
+ this.leaveTopLayer(this.busyRef?.location.nativeElement);
399
+ }
400
+ ensureBusy() {
401
+ if (this.busyRef) return this.busyRef;
402
+ const ref = createComponent(ShellBusyComponent, { environmentInjector: this.environmentInjector });
403
+ this.appRef.attachView(ref.hostView);
404
+ document.body.appendChild(ref.location.nativeElement);
405
+ this.busyRef = ref;
406
+ return ref;
407
+ }
408
+ raiseMessage() {
409
+ this.raiseTopLayer(this.messageRef?.location.nativeElement);
410
+ }
411
+ supportsTopLayer() {
412
+ if (this.topLayerSupport === void 0) this.topLayerSupport = SystemUtils.isBrowser() && typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype;
413
+ return this.topLayerSupport;
414
+ }
415
+ enterTopLayer(el) {
416
+ if (!el || !this.supportsTopLayer()) return;
417
+ if (el.getAttribute("popover") !== "manual") el.setAttribute("popover", "manual");
418
+ try {
419
+ if (!el.matches(":popover-open")) el.showPopover();
420
+ } catch {}
421
+ }
422
+ leaveTopLayer(el) {
423
+ if (!el || !this.supportsTopLayer()) return;
424
+ try {
425
+ if (el.matches(":popover-open")) el.hidePopover();
426
+ } catch {}
427
+ }
428
+ raiseTopLayer(el) {
429
+ if (!el || !this.supportsTopLayer()) return;
430
+ if (!el.matches(":popover-open")) {
431
+ this.enterTopLayer(el);
432
+ return;
433
+ }
434
+ try {
435
+ el.hidePopover();
436
+ el.showPopover();
437
+ } catch {}
438
+ }
439
+ static {
440
+ this.ɵfac = i0.ɵɵngDeclareFactory({
441
+ minVersion: "12.0.0",
442
+ version: "22.2.1",
443
+ ngImport: i0,
444
+ type: ShellService,
445
+ deps: [],
446
+ target: i0.ɵɵFactoryTarget.Service
447
+ });
448
+ }
449
+ static {
450
+ this.ɵprov = i0.ɵɵngDeclareService({
451
+ minVersion: "22.0.0",
452
+ version: "22.2.1",
453
+ ngImport: i0,
454
+ type: ShellService
455
+ });
456
+ }
457
+ };
458
+ i0.ɵɵngDeclareClassMetadata({
459
+ minVersion: "12.0.0",
460
+ version: "22.2.1",
461
+ ngImport: i0,
462
+ type: ShellService,
463
+ decorators: [{ type: Service }]
464
+ });
706
465
  export { BusyTimer, ShellBusyComponent, ShellMessageComponent, ShellService };
707
- //# sourceMappingURL=arsedizioni-ars-utils-ui.shell.mjs.map
466
+
467
+ //# sourceMappingURL=arsedizioni-ars-utils-ui.shell.mjs.map