@arsedizioni/ars-utils 22.5.3 → 22.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  import { timer, Subject } from 'rxjs';
2
2
  import { tap, first } from 'rxjs/operators';
3
3
  import * as i0 from '@angular/core';
4
- import { signal, computed, ChangeDetectionStrategy, Component, output, viewChild, afterNextRender, inject, DestroyRef, HostListener, ApplicationRef, EnvironmentInjector, createComponent, Service } from '@angular/core';
5
- import { SafeHtmlPipe, SystemUtils } from '@arsedizioni/ars-utils/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';
6
7
 
7
8
  /**
8
9
  * Shows the busy overlay only if the operation is still running after a debounce delay, so that
@@ -47,7 +48,32 @@ const SPINNER_CIRCUMFERENCE = 2 * Math.PI * 31.5;
47
48
  * class on the host, never a `createComponent`, so the 40-odd calls a screen costs nothing.
48
49
  */
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
+ }
50
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 */ []));
51
77
  /** Whether the overlay is on screen. Hidden with `display: none`, so it costs no layout. */
52
78
  this.visible = signal(false, /* @ts-ignore */
53
79
  ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
@@ -68,6 +94,7 @@ class ShellBusyComponent {
68
94
  /** Length of the spinner arc still to be drawn, derived from {@link progress}. */
69
95
  this.dashOffset = computed(() => SPINNER_CIRCUMFERENCE * (1 - Math.min(Math.max(this.progress(), 0), 100) / 100), /* @ts-ignore */
70
96
  ...(ngDevMode ? [{ debugName: "dashOffset" }] : /* istanbul ignore next */ []));
97
+ this.followTheme();
71
98
  }
72
99
  /**
73
100
  * Updates the overlay state.
@@ -90,12 +117,12 @@ class ShellBusyComponent {
90
117
  this.progressMode.set(progress > 0 ? 'determinate' : progressMode);
91
118
  }
92
119
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ShellBusyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
93
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ShellBusyComponent, isStandalone: true, selector: "ars-shell-busy", host: { properties: { "class.ars-hidden": "!visible()" } }, 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{position:fixed;inset:0;z-index:var(--ars-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(--ars-color-text, var(--mat-sys-on-surface, #191c1b))}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--ars-busy-scrim-color, rgba(0, 0, 0, .42));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-dialog-radius, 24px);background:var(--ars-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));box-shadow:var(--ars-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(--ars-busy-bare-color, #ffffff)}.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(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8))}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--ars-color-primary, var(--mat-sys-primary, #00a293));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(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8))}.spinner .arc{stroke:var(--ars-color-primary, var(--mat-sys-primary, #00a293));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){.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 }); }
120
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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-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-busy-scrim-color, rgba(0, 0, 0, .42));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #00a293));--_track: var(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-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-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-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-busy-scrim-color, rgba(0, 0, 0, .6));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #53dbc9));--_track: var(--ars-progress-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(1px);backdrop-filter:blur(1px)}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-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){.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 }); }
94
121
  }
95
122
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ShellBusyComponent, decorators: [{
96
123
  type: Component,
97
- args: [{ selector: 'ars-shell-busy', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafeHtmlPipe], host: { '[class.ars-hidden]': '!visible()' }, 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{position:fixed;inset:0;z-index:var(--ars-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(--ars-color-text, var(--mat-sys-on-surface, #191c1b))}:host(.ars-hidden){display:none}.scrim{position:absolute;inset:0;background:var(--ars-busy-scrim-color, rgba(0, 0, 0, .42));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-dialog-radius, 24px);background:var(--ars-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));box-shadow:var(--ars-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(--ars-busy-bare-color, #ffffff)}.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(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8))}.bar-fill{position:absolute;inset:0 auto 0 0;width:0;border-radius:inherit;background:var(--ars-color-primary, var(--mat-sys-primary, #00a293));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(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8))}.spinner .arc{stroke:var(--ars-color-primary, var(--mat-sys-primary, #00a293));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){.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"] }]
98
- }] });
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-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-busy-scrim-color, rgba(0, 0, 0, .42));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #00a293));--_track: var(--ars-progress-track-color, var(--mat-sys-surface-container-highest, #d5dbd8));--_bare: var(--ars-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-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-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-busy-scrim-color, rgba(0, 0, 0, .6));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #53dbc9));--_track: var(--ars-progress-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(1px);backdrop-filter:blur(1px)}.panel{position:relative;min-width:250px;max-width:calc(100% - 24px);margin:12px;padding:24px;border-radius:var(--ars-dialog-radius, 24px);background:var(--_surface);box-shadow:var(--ars-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){.bar.indeterminate .bar-fill,.spinner.indeterminate,.spinner.indeterminate .arc,.hourglass{animation-duration:4s}}\n"] }]
125
+ }], ctorParameters: () => [] });
99
126
 
100
127
  /**
101
128
  * The shell's message panel: a modal that looks like a Material dialog and owes nothing to
@@ -112,9 +139,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
112
139
  * what lets an interceptor open it.
113
140
  */
114
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
+ }
115
156
  constructor() {
116
157
  /** Emitted when the user dismisses the message, whichever way they did it. */
117
158
  this.closed = output();
159
+ this.themeService = inject(ThemeService);
118
160
  this.panel = viewChild.required('panel', /* @ts-ignore */
119
161
  ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
120
162
  this.okButton = viewChild.required('ok', /* @ts-ignore */
@@ -128,6 +170,17 @@ class ShellMessageComponent {
128
170
  this.copied = signal(false, /* @ts-ignore */
129
171
  ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
130
172
  this.closing = false;
173
+ /**
174
+ * True when the panel must paint itself dark.
175
+ *
176
+ * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not
177
+ * always agree: the service is what the user chose, and it may be forcing dark on a machine
178
+ * that is on light. Following the OS instead would leave this panel the only light thing on a
179
+ * dark screen.
180
+ */
181
+ this.dark = signal(false, /* @ts-ignore */
182
+ ...(ngDevMode ? [{ debugName: "dark" }] : /* istanbul ignore next */ []));
183
+ this.followTheme();
131
184
  afterNextRender(() => this.okButton().nativeElement.focus());
132
185
  inject(DestroyRef).onDestroy(() => {
133
186
  clearTimeout(this.dismissTimer);
@@ -235,11 +288,11 @@ class ShellMessageComponent {
235
288
  }
236
289
  }
237
290
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ShellMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
238
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: ShellMessageComponent, isStandalone: true, selector: "ars-shell-message", outputs: { closed: "closed" }, host: { listeners: { "document:keydown": "onKeydown($event)" } }, 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\">\n <div class=\"details-separator\"></div>\n <p class=\"details-label\">Dettaglio:</p>\n <div class=\"details\">\n <div [innerHTML]=\"data().details | safeHtml\"></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{position:fixed;inset:0;z-index:var(--ars-shell-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}.scrim{position:absolute;inset:0;background:var(--ars-scrim-color, rgba(0, 0, 0, .32));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:ars-shell-fade .15s 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-dialog-radius, 28px);background:var(--ars-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));color:var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));box-shadow:var(--ars-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{display:contents}.details-separator{flex:0 0 auto;height:1px;margin:0 24px 8px;background:var(--ars-color-divider, #757d87)}.details-label{flex:0 0 auto;margin:0 0 4px;padding:0 24px;font-size:.75em;text-transform:uppercase;color:var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f))}.details{flex:1 1000 auto;min-height:64px;max-height:350px;padding:4px 24px 8px;overflow-y:auto;overscroll-behavior:contain;background:transparent;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:3px solid var(--ars-color-primary, var(--mat-sys-primary, #00a293));outline-offset:2px}.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{background:var(--ars-item-hover-background-color, rgba(0, 0, 0, .08))}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--ars-color-primary, var(--mat-sys-primary, #00a293));color:var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover{filter:brightness(1.08)}@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}}\n"], dependencies: [{ kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
291
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", 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\">\n <div class=\"details-separator\"></div>\n <p class=\"details-label\">Dettaglio:</p>\n <div class=\"details\">\n <div [innerHTML]=\"data().details | safeHtml\"></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-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-scrim-color, rgba(0, 0, 0, .32));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #00a293));--_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));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-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-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-scrim-color, rgba(0, 0, 0, .6));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #53dbc9));--_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));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:ars-shell-fade .15s 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-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-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{display:contents}.details-separator{flex:0 0 auto;height:1px;margin:0 24px 8px;background:var(--_divider)}.details-label{flex:0 0 auto;margin:0 0 4px;padding:0 24px;font-size:.75em;text-transform:uppercase;color:var(--_secondary)}.details{flex:1 1000 auto;min-height:64px;max-height:350px;padding:4px 24px 8px;overflow-y:auto;overscroll-behavior:contain;background:transparent;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:3px solid var(--_primary);outline-offset:2px}.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{background:var(--_hover)}.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{filter:brightness(1.08)}@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}}\n"], dependencies: [{ kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
239
292
  }
240
293
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ShellMessageComponent, decorators: [{
241
294
  type: Component,
242
- args: [{ selector: 'ars-shell-message', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SafeHtmlPipe], 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\">\n <div class=\"details-separator\"></div>\n <p class=\"details-label\">Dettaglio:</p>\n <div class=\"details\">\n <div [innerHTML]=\"data().details | safeHtml\"></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{position:fixed;inset:0;z-index:var(--ars-shell-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}.scrim{position:absolute;inset:0;background:var(--ars-scrim-color, rgba(0, 0, 0, .32));-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:ars-shell-fade .15s 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-dialog-radius, 28px);background:var(--ars-dialog-background-color, var(--mat-sys-surface-container-high, #e6eae8));color:var(--ars-color-text, var(--mat-sys-on-surface, #191c1b));box-shadow:var(--ars-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{display:contents}.details-separator{flex:0 0 auto;height:1px;margin:0 24px 8px;background:var(--ars-color-divider, #757d87)}.details-label{flex:0 0 auto;margin:0 0 4px;padding:0 24px;font-size:.75em;text-transform:uppercase;color:var(--ars-color-secondary, var(--mat-sys-on-surface-variant, #4a635f))}.details{flex:1 1000 auto;min-height:64px;max-height:350px;padding:4px 24px 8px;overflow-y:auto;overscroll-behavior:contain;background:transparent;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:3px solid var(--ars-color-primary, var(--mat-sys-primary, #00a293));outline-offset:2px}.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{background:var(--ars-item-hover-background-color, rgba(0, 0, 0, .08))}.flat-button{height:40px;padding:0 24px;border-radius:20px;background:var(--ars-color-primary, var(--mat-sys-primary, #00a293));color:var(--ars-color-on-primary, var(--mat-sys-on-primary, #ffffff));font:var(--mat-sys-label-large, 500 14px/20px Roboto, sans-serif)}.flat-button:hover{filter:brightness(1.08)}@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}}\n"] }]
295
+ 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\">\n <div class=\"details-separator\"></div>\n <p class=\"details-label\">Dettaglio:</p>\n <div class=\"details\">\n <div [innerHTML]=\"data().details | safeHtml\"></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-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-scrim-color, rgba(0, 0, 0, .32));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #00a293));--_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));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-z-index, 1100);display:flex;align-items:center;justify-content:center;box-sizing:border-box}:host(.ars-dark){--_surface: var(--ars-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-scrim-color, rgba(0, 0, 0, .6));--_primary: var(--ars-color-primary, var(--mat-sys-primary, #53dbc9));--_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));color-scheme:dark}:host::backdrop{display:none}.scrim{position:absolute;inset:0;background:var(--_scrim);-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:ars-shell-fade .15s 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-dialog-radius, 28px);background:var(--_surface);color:var(--_on-surface);box-shadow:var(--ars-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{display:contents}.details-separator{flex:0 0 auto;height:1px;margin:0 24px 8px;background:var(--_divider)}.details-label{flex:0 0 auto;margin:0 0 4px;padding:0 24px;font-size:.75em;text-transform:uppercase;color:var(--_secondary)}.details{flex:1 1000 auto;min-height:64px;max-height:350px;padding:4px 24px 8px;overflow-y:auto;overscroll-behavior:contain;background:transparent;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:3px solid var(--_primary);outline-offset:2px}.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{background:var(--_hover)}.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{filter:brightness(1.08)}@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}}\n"] }]
243
296
  }], 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: [{
244
297
  type: HostListener,
245
298
  args: ['document:keydown', ['$event']]
@@ -261,12 +314,21 @@ const CLEAR_BUSY_GRACE = 500;
261
314
  *
262
315
  * ## Stacking
263
316
  *
264
- * Outside the CDK overlay stack the order is a fixed z-index: the busy overlay sits at 1090 and
265
- * the message at 1100, both above the `cdk-overlay-container` (1000). So an error raised while a
266
- * Material dialog is open lands on top of it, which is what an error should do, and the busy
267
- * overlay covers the dialog it belongs to. The one case a fixed number cannot express is a dialog
268
- * opened while the overlay is still up — `DialogService.open()` takes the busy away immediately
269
- * rather than with the grace period, precisely so that case cannot happen.
317
+ * Since Angular CDK 22 every overlay is opened as `popover="manual"`, so a Material dialog is not
318
+ * at z-index 1000 any more: it is in the browser top layer, which is painted above the whole
319
+ * document whatever the z-index of the document says. A number, however large, cannot get above
320
+ * it — so these overlays are promoted to the top layer as well, and the order there is the order
321
+ * of promotion: whatever went up last is on top.
322
+ *
323
+ * That gives the right answer by itself in the case that matters, an error raised while a dialog
324
+ * is open. The other three are arranged explicitly: `setBusy()` puts the message back on top
325
+ * after raising the busy overlay, `DialogService.open()` calls {@link raiseMessage} after opening
326
+ * a dialog, and it also takes the busy away immediately rather than with the grace period, so a
327
+ * dialog is never drawn under an overlay that is on its way out.
328
+ *
329
+ * The `--ars-shell-z-index` / `--ars-busy-z-index` pair (1100 / 1090, above the CDK's 1000) is
330
+ * still there and still correct: it is what orders the overlays on a browser without the Popover
331
+ * API, where the CDK does not use it either.
270
332
  */
271
333
  class ShellService {
272
334
  constructor() {
@@ -316,6 +378,7 @@ class ShellService {
316
378
  const ref = this.messageRef;
317
379
  this.messageRef = undefined;
318
380
  this.messageKind = undefined;
381
+ this.leaveTopLayer(ref.location.nativeElement);
319
382
  this.appRef.detachView(ref.hostView);
320
383
  ref.destroy();
321
384
  if (this.previousOverflow !== undefined) {
@@ -343,6 +406,9 @@ class ShellService {
343
406
  if (this.messageRef && this.messageKind === kind) {
344
407
  this.messageRef.instance.setData(data);
345
408
  this.messageRef.changeDetectorRef.detectChanges();
409
+ // Back to the front: a dialog may have been opened since this panel went up, and the top
410
+ // layer orders by promotion, not by z-index.
411
+ this.raiseTopLayer(this.messageRef.location.nativeElement);
346
412
  return this.messageHandle();
347
413
  }
348
414
  // A message of a different kind replaces the one on screen rather than stacking on it.
@@ -358,6 +424,7 @@ class ShellService {
358
424
  document.body.style.overflow = 'hidden';
359
425
  this.appRef.attachView(ref.hostView);
360
426
  document.body.appendChild(ref.location.nativeElement);
427
+ this.enterTopLayer(ref.location.nativeElement);
361
428
  this.messageRef = ref;
362
429
  this.messageKind = kind;
363
430
  return this.messageHandle();
@@ -408,6 +475,10 @@ class ShellService {
408
475
  ref.instance.visible.set(true);
409
476
  this.busyVisible = true;
410
477
  ref.changeDetectorRef.detectChanges();
478
+ this.enterTopLayer(ref.location.nativeElement);
479
+ // The overlay has just been promoted, so it is the topmost element of the top layer: put the
480
+ // message back above it, which is what the 1100/1090 pair used to do on its own.
481
+ this.raiseMessage();
411
482
  // The action always replaces the previous one. Keeping the first, as the previous
412
483
  // implementation did, meant the overlay outlived the operation that was actually running.
413
484
  this.busyActionSubscription?.unsubscribe();
@@ -497,6 +568,7 @@ class ShellService {
497
568
  this.busyVisible = false;
498
569
  this.busyRef?.instance.visible.set(false);
499
570
  this.busyRef?.changeDetectorRef.detectChanges();
571
+ this.leaveTopLayer(this.busyRef?.location.nativeElement);
500
572
  }
501
573
  /**
502
574
  * Returns the busy component, creating and attaching it the first time it is needed.
@@ -511,6 +583,93 @@ class ShellService {
511
583
  this.busyRef = ref;
512
584
  return ref;
513
585
  }
586
+ // ---------------------------------------------------------------------------
587
+ // Top layer
588
+ // ---------------------------------------------------------------------------
589
+ /**
590
+ * Brings the message currently on screen back to the front of the top layer, if there is one.
591
+ *
592
+ * Called by `DialogService` after opening a dialog: an error the user has not dismissed yet
593
+ * must stay above whatever is opened underneath it.
594
+ * @returns void
595
+ */
596
+ raiseMessage() {
597
+ this.raiseTopLayer(this.messageRef?.location.nativeElement);
598
+ }
599
+ /**
600
+ * Whether the browser can promote an element to the top layer through the Popover API.
601
+ *
602
+ * Angular CDK 22 opens every overlay as `popover="manual"`, so a Material dialog does not sit
603
+ * at z-index 1000 any more: it sits in the top layer, which is painted above the whole page
604
+ * whatever the z-index of the page says. The only way to put something above it is to be in
605
+ * the top layer too. Where the API is missing the overlays fall back to their z-index, which
606
+ * is also the CDK's own fallback and therefore still the right order.
607
+ * @returns `true` when `showPopover()` is available.
608
+ */
609
+ supportsTopLayer() {
610
+ if (this.topLayerSupport === undefined) {
611
+ this.topLayerSupport = SystemUtils.isBrowser()
612
+ && typeof HTMLElement !== 'undefined'
613
+ && 'showPopover' in HTMLElement.prototype;
614
+ }
615
+ return this.topLayerSupport;
616
+ }
617
+ /**
618
+ * Promotes an overlay to the top layer, marking it as a manual popover the first time.
619
+ *
620
+ * `manual` and not `auto`: an `auto` popover is light-dismissed by a click anywhere and closed
621
+ * by Escape without telling anyone, which would take the panel off the screen behind the back
622
+ * of the component that owns it.
623
+ * @param el - The host element of the overlay. Ignored when undefined.
624
+ * @returns void
625
+ */
626
+ enterTopLayer(el) {
627
+ if (!el || !this.supportsTopLayer())
628
+ return;
629
+ if (el.getAttribute('popover') !== 'manual')
630
+ el.setAttribute('popover', 'manual');
631
+ // Throws if the element is already showing, or is not connected yet.
632
+ try {
633
+ if (!el.matches(':popover-open'))
634
+ el.showPopover();
635
+ }
636
+ catch { /* already up */ }
637
+ }
638
+ /**
639
+ * Takes an overlay out of the top layer.
640
+ * @param el - The host element of the overlay. Ignored when undefined.
641
+ * @returns void
642
+ */
643
+ leaveTopLayer(el) {
644
+ if (!el || !this.supportsTopLayer())
645
+ return;
646
+ try {
647
+ if (el.matches(':popover-open'))
648
+ el.hidePopover();
649
+ }
650
+ catch { /* already down */ }
651
+ }
652
+ /**
653
+ * Moves an overlay back to the front of the top layer.
654
+ *
655
+ * The top layer is a stack ordered by promotion, so the only way to raise an element already
656
+ * in it is to take it out and put it back.
657
+ * @param el - The host element of the overlay. Ignored when undefined.
658
+ * @returns void
659
+ */
660
+ raiseTopLayer(el) {
661
+ if (!el || !this.supportsTopLayer())
662
+ return;
663
+ if (!el.matches(':popover-open')) {
664
+ this.enterTopLayer(el);
665
+ return;
666
+ }
667
+ try {
668
+ el.hidePopover();
669
+ el.showPopover();
670
+ }
671
+ catch { /* the element is on its way out */ }
672
+ }
514
673
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: ShellService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
515
674
  static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.3", ngImport: i0, type: ShellService }); }
516
675
  }