@arsedizioni/ars-utils 22.5.3 → 22.5.4

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;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(.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;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(.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;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}.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;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}.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']]
@@ -1 +1 @@
1
- {"version":3,"file":"arsedizioni-ars-utils-ui.shell.mjs","sources":["../../../projects/ars-utils/ui.shell/busy-timer.ts","../../../projects/ars-utils/ui.shell/shell-busy/shell-busy.component.ts","../../../projects/ars-utils/ui.shell/shell-busy/shell-busy.component.html","../../../projects/ars-utils/ui.shell/shell-message/shell-message.component.ts","../../../projects/ars-utils/ui.shell/shell-message/shell-message.component.html","../../../projects/ars-utils/ui.shell/shell.service.ts","../../../projects/ars-utils/ui.shell/public_api.ts","../../../projects/ars-utils/ui.shell/arsedizioni-ars-utils-ui.shell.ts"],"sourcesContent":["import { Subscription, timer } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport type { ShellService } from './shell.service';\n\n/**\n * Shows the busy overlay only if the operation is still running after a debounce delay, so that\n * an operation that answers in 30 ms never makes the screen flash.\n *\n * Whoever creates one owns it: {@link clear} must be called when the operation ends, otherwise\n * the overlay appears after the fact and stays.\n */\nexport class BusyTimer {\n private readonly subscription: Subscription;\n\n /**\n * Arms the timer.\n * @param shellService - The service that owns the overlay. A `DialogService` is accepted too,\n * since it extends `ShellService`.\n * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.\n * @param message - Text to display. Defaults to `'Operazione in corso...'`.\n */\n constructor(\n private readonly shellService: ShellService,\n due: number = 100,\n message: string = 'Operazione in corso...'\n ) {\n this.subscription = timer(due).pipe(tap(() => this.shellService.busy(message))).subscribe();\n }\n\n /**\n * Disarms the timer and takes the overlay away.\n * @returns void\n */\n clear(): void {\n this.subscription.unsubscribe();\n this.shellService.clearBusy();\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';\nimport { SafeHtmlPipe } from '@arsedizioni/ars-utils/core';\n\n/** Visual style of the busy indicator. */\nexport type ShellBusyType = 'bar' | 'spinner' | 'wait' | 'hourglass';\n\n/** Progress mode of the bar and of the spinner. */\nexport type ShellBusyMode = 'determinate' | 'indeterminate';\n\n/** Circumference of the spinner circle, in user units. Kept here so the template stays arithmetic-free. */\nconst SPINNER_CIRCUMFERENCE = 2 * Math.PI * 31.5;\n\n/**\n * Overlay that blocks interaction while an operation is running, in the four styles the\n * applications use: progress bar, spinner, hourglass and bare wait.\n *\n * Reproduces `mat-progress-bar` and `mat-progress-spinner` in CSS and SVG rather than importing\n * them, because this overlay is shown from the boot path — an HTTP interceptor calls it before\n * any route exists — and `ui.shell` exists precisely so that path costs no Material.\n *\n * The instance is created once by {@link ShellService} and then reused: showing and hiding is a\n * class on the host, never a `createComponent`, so the 40-odd calls a screen costs nothing.\n */\n@Component({\n selector: 'ars-shell-busy',\n templateUrl: './shell-busy.component.html',\n styleUrl: './shell-busy.component.scss',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [SafeHtmlPipe],\n host: { '[class.ars-hidden]': '!visible()' }\n})\nexport class ShellBusyComponent {\n\n /** Whether the overlay is on screen. Hidden with `display: none`, so it costs no layout. */\n readonly visible = signal(false);\n\n /** Visual style of the indicator. */\n readonly type = signal<ShellBusyType>('bar');\n\n /** Current progress value (0-100), used when {@link progressMode} is `'determinate'`. */\n readonly progress = signal(0);\n\n /** Progress mode of the bar and of the spinner. */\n readonly progressMode = signal<ShellBusyMode>('indeterminate');\n\n /** Message displayed above the indicator. */\n readonly message = signal('');\n\n /** Circumference of the spinner circle, bound to `stroke-dasharray`. */\n protected readonly circumference = SPINNER_CIRCUMFERENCE;\n\n /** Length of the spinner arc still to be drawn, derived from {@link progress}. */\n protected readonly dashOffset = computed(\n () => SPINNER_CIRCUMFERENCE * (1 - Math.min(Math.max(this.progress(), 0), 100) / 100)\n );\n\n /**\n * Updates the overlay state.\n *\n * Same contract as the `BusyDialogComponent` it replaces, down to the two implicit rules that\n * callers rely on: an empty message keeps the previous text (so `wait()` does not wipe it), and\n * a progress above zero forces `'determinate'` whatever the caller passed.\n * @param message - New message to display. An empty string preserves the current one.\n * @param progress - Current progress value (0-100). Above zero it forces determinate mode.\n * @param progressMode - Progress mode to use. Defaults to `'indeterminate'`.\n * @param type - Visual style to use. Defaults to `'bar'`.\n * @returns void\n */\n set(\n message: string,\n progress: number,\n progressMode: ShellBusyMode = 'indeterminate',\n type: ShellBusyType = 'bar'\n ): void {\n if (message) this.message.set(message);\n if (type) this.type.set(type);\n this.progress.set(progress);\n this.progressMode.set(progress > 0 ? 'determinate' : progressMode);\n }\n}\n","<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","import {\n ChangeDetectionStrategy, Component, DestroyRef, ElementRef,\n HostListener, afterNextRender, inject, output, signal, viewChild\n} from '@angular/core';\nimport { SafeHtmlPipe } from '@arsedizioni/ars-utils/core';\nimport { ShellMessageData } from '../definitions';\n\n/**\n * The shell's message panel: a modal that looks like a Material dialog and owes nothing to\n * Material.\n *\n * It exists because shell, interceptors, guards and app initializers must be able to say\n * \"the session is gone\" before a single route has been loaded, and paying for `mat-dialog`,\n * `cdk/overlay` and the rest of that stack on the boot path costs the initial bundle more\n * than every route of the application put together. The look is matched through CSS custom\n * properties (`--ars-*` first, Material's `--mat-sys-*` next, a literal last), so an\n * application that themes Material gets the same surface here for free.\n *\n * Instantiated imperatively by {@link ShellService}, never declared in a template: that is\n * what lets an interceptor open it.\n */\n@Component({\n selector: 'ars-shell-message',\n templateUrl: './shell-message.component.html',\n styleUrl: './shell-message.component.scss',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [SafeHtmlPipe]\n})\nexport class ShellMessageComponent {\n\n /** Emitted when the user dismisses the message, whichever way they did it. */\n readonly closed = output<void>();\n\n private readonly panel = viewChild.required<ElementRef<HTMLElement>>('panel');\n private readonly okButton = viewChild.required<ElementRef<HTMLButtonElement>>('ok');\n\n /** Unique id tying the panel to its heading for assistive technology. */\n protected readonly titleId = `ars-shell-message-title-${Math.random().toString(36).slice(2, 10)}`;\n\n /** Current content, with the defaults the shell relies on already applied. */\n protected readonly data = signal<ShellMessageData>({ message: '', title: 'Informazioni', okCaption: 'Ok', width: 500 });\n\n /** True for a moment after a successful copy, so the button can acknowledge it. */\n protected readonly copied = signal(false);\n\n private dismissTimer?: ReturnType<typeof setTimeout>;\n private copiedTimer?: ReturnType<typeof setTimeout>;\n private closing = false;\n\n constructor() {\n afterNextRender(() => this.okButton().nativeElement.focus());\n inject(DestroyRef).onDestroy(() => {\n clearTimeout(this.dismissTimer);\n clearTimeout(this.copiedTimer);\n });\n }\n\n /**\n * Replaces the content shown by the panel and restarts the auto-dismiss timer.\n *\n * Called on an already open panel by `ShellService.error()` so that a burst of failures\n * updates one message instead of stacking modals nobody can dismiss.\n * @param data - The new content to display.\n * @returns void\n */\n setData(data: ShellMessageData): void {\n this.data.set({ title: 'Informazioni', okCaption: 'Ok', width: 500, ...data });\n this.scheduleDismiss(this.data().dismissAfter);\n }\n\n /**\n * Dismisses the panel. Safe to call more than once: only the first call is announced.\n * @returns void\n */\n close(): void {\n if (this.closing) return;\n this.closing = true;\n clearTimeout(this.dismissTimer);\n this.closed.emit();\n }\n\n /**\n * Closes the panel on Escape, mirroring a Material dialog opened with the default config.\n * @param e - The keyboard event captured at document level.\n * @returns void\n */\n @HostListener('document:keydown', ['$event'])\n protected onKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n if (e.key === 'Tab') this.trapFocus(e);\n }\n\n /**\n * Keeps Tab inside the panel while the message is up.\n *\n * A hand-rolled two-element version of what `cdk/a11y` does, which is all this panel needs:\n * it never holds more than the copy button and the dismiss button.\n * @param e - The Tab keydown event.\n * @returns void\n */\n private trapFocus(e: KeyboardEvent): void {\n const focusable = Array.from(\n this.panel().nativeElement.querySelectorAll<HTMLElement>('button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])')\n );\n if (!focusable.length) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n\n if (e.shiftKey && (active === first || !this.panel().nativeElement.contains(active))) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n }\n\n /**\n * (Re)schedules the auto-dismiss timer, cancelling any pending one so a timer armed for an\n * earlier message can never close a newer one.\n * @param dismissAfter - Delay in milliseconds; no-op when falsy.\n * @returns void\n */\n private scheduleDismiss(dismissAfter?: number): void {\n clearTimeout(this.dismissTimer);\n this.dismissTimer = undefined;\n if (dismissAfter) this.dismissTimer = setTimeout(() => this.close(), dismissAfter);\n }\n\n /**\n * Copies the message and its details to the clipboard, as both HTML and plain text.\n *\n * Unlike `InfoDialogComponent` there is no toast to report the outcome — the shell has no\n * toast — so the button itself acknowledges it for a moment.\n * @returns A promise that resolves once the copy has been attempted.\n */\n protected async copy(): Promise<void> {\n const content = this.panel().nativeElement.querySelector<HTMLElement>('.content');\n if (!content) return;\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/html': new Blob([content.innerHTML], { type: 'text/html' }),\n 'text/plain': new Blob([content.innerText], { type: 'text/plain' })\n })\n ]);\n this.copied.set(true);\n clearTimeout(this.copiedTimer);\n this.copiedTimer = setTimeout(() => this.copied.set(false), 1500);\n } catch {\n // Clipboard permission denied or the document is not focused: nothing worth interrupting\n // the user for, and the text is on screen anyway.\n }\n }\n}\n","<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","import { ApplicationRef, ComponentRef, EnvironmentInjector, Service, createComponent, inject } from '@angular/core';\nimport { SystemUtils } from '@arsedizioni/ars-utils/core';\nimport { Observable, Subject, Subscription } from 'rxjs';\nimport { first } from 'rxjs/operators';\nimport { BusyTimer } from './busy-timer';\nimport { ShellMessageData, ShellMessageRef } from './definitions';\nimport { ShellBusyComponent, ShellBusyMode, ShellBusyType } from './shell-busy/shell-busy.component';\nimport { ShellMessageComponent } from './shell-message/shell-message.component';\n\n/** Grace period before the busy overlay is actually taken away, in milliseconds. */\nconst CLEAR_BUSY_GRACE = 500;\n\n/**\n * Everything the application can say to the user without asking anything back: a message, an\n * error, and the wait overlay. No Angular Material behind any of it.\n *\n * `DialogService` extends this class, so a route component injects `DialogService` and has both\n * halves; shell, HTTP interceptors, route guards and app initializers inject `ShellService` and\n * pay for neither `mat-dialog` nor `cdk/overlay` on the boot path — measured at 445 KB of the\n * 905 KB initial bundle on myARS, more than every route of the application put together.\n *\n * The methods keep the parameter order of the `DialogService` ones they came from, so no call\n * site changed when they moved here.\n *\n * ## Stacking\n *\n * Outside the CDK overlay stack the order is a fixed z-index: the busy overlay sits at 1090 and\n * the message at 1100, both above the `cdk-overlay-container` (1000). So an error raised while a\n * Material dialog is open lands on top of it, which is what an error should do, and the busy\n * overlay covers the dialog it belongs to. The one case a fixed number cannot express is a dialog\n * opened while the overlay is still up — `DialogService.open()` takes the busy away immediately\n * rather than with the grace period, precisely so that case cannot happen.\n */\n@Service()\nexport class ShellService {\n\n private readonly appRef = inject(ApplicationRef);\n private readonly environmentInjector = inject(EnvironmentInjector);\n\n private messageRef?: ComponentRef<ShellMessageComponent>;\n private messageKind?: 'info' | 'error';\n private messageClosed?: Subject<void>;\n private previousOverflow?: string;\n private previouslyFocused?: HTMLElement;\n\n private busyRef?: ComponentRef<ShellBusyComponent>;\n private busyVisible = false;\n private busyActionSubscription?: Subscription;\n private clearBusyTimer?: ReturnType<typeof setTimeout>;\n\n // ---------------------------------------------------------------------------\n // Messages\n // ---------------------------------------------------------------------------\n\n /**\n * Shows an informational message.\n * @param message - HTML message to display.\n * @param title - Heading. Defaults to `'Informazioni'`.\n * @param okCaption - Dismiss button label. Defaults to `'Ok'`.\n * @param width - Maximum panel width in pixels. Defaults to `500`.\n * @param dismissAfter - Auto-close delay in milliseconds (optional).\n * @param details - Optional secondary details text.\n * @returns A handle over the open message, or `null` outside the browser.\n */\n info(\n message: string,\n title: string = 'Informazioni',\n okCaption: string = 'Ok',\n width: number = 500,\n dismissAfter?: number,\n details?: string\n ): ShellMessageRef | null {\n this.clearBusy();\n return this.showMessage('info', { message, title, okCaption, width, dismissAfter, details });\n }\n\n /**\n * Shows an error message. When one is already on screen its content is replaced in place: a\n * failing API answers many requests at once, and each of them must not add a panel of its own.\n * @param message - HTML error message to display.\n * @param log - Optional technical log or stack trace shown in the details panel.\n * @param title - Heading. Defaults to `'Errore'`.\n * @param okCaption - Dismiss button label. Defaults to `'Ok'`.\n * @param width - Maximum panel width in pixels. Defaults to `500`.\n * @param dismissAfter - Auto-close delay in milliseconds (optional).\n * @returns A handle over the open message, or `null` outside the browser.\n */\n error(\n message: string,\n log?: string,\n title: string = 'Errore',\n okCaption: string = 'Ok',\n width: number = 500,\n dismissAfter?: number\n ): ShellMessageRef | null {\n this.clearBusy();\n return this.showMessage('error', { message, title, okCaption, width, dismissAfter, details: log });\n }\n\n /**\n * Closes the message currently on screen, if any. Does not touch the busy overlay.\n * @returns void\n */\n closeMessage(): void {\n if (!this.messageRef) return;\n\n const ref = this.messageRef;\n this.messageRef = undefined;\n this.messageKind = undefined;\n\n this.appRef.detachView(ref.hostView);\n ref.destroy();\n\n if (this.previousOverflow !== undefined) {\n document.body.style.overflow = this.previousOverflow;\n this.previousOverflow = undefined;\n }\n this.previouslyFocused?.focus();\n this.previouslyFocused = undefined;\n\n // Last, so that whoever is waiting on it finds the screen already in its final state.\n const closed = this.messageClosed;\n this.messageClosed = undefined;\n closed?.next();\n closed?.complete();\n }\n\n /**\n * Opens the message panel, or updates the one already open when it is showing the same kind of\n * message.\n * @param kind - Whether this is an informational or an error message.\n * @param data - The content to display.\n * @returns A handle over the open message, or `null` outside the browser.\n */\n private showMessage(kind: 'info' | 'error', data: ShellMessageData): ShellMessageRef | null {\n if (!SystemUtils.isBrowser()) return null;\n\n if (this.messageRef && this.messageKind === kind) {\n this.messageRef.instance.setData(data);\n this.messageRef.changeDetectorRef.detectChanges();\n return this.messageHandle();\n }\n\n // A message of a different kind replaces the one on screen rather than stacking on it.\n this.closeMessage();\n\n const ref = createComponent(ShellMessageComponent, { environmentInjector: this.environmentInjector });\n ref.instance.setData(data);\n ref.instance.closed.subscribe(() => this.closeMessage());\n\n // One subject per panel, not per handle: an `error()` that replaces the text of the message\n // already on screen hands out a second handle, and both must fire when that message goes.\n this.messageClosed = new Subject<void>();\n\n this.previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : undefined;\n this.previousOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n this.appRef.attachView(ref.hostView);\n document.body.appendChild(ref.location.nativeElement as HTMLElement);\n\n this.messageRef = ref;\n this.messageKind = kind;\n return this.messageHandle();\n }\n\n /**\n * Builds the handle returned to callers.\n *\n * The subject is captured now rather than read at call time, so `afterClosed()` keeps\n * answering about the message this handle was given for, even once another one has taken\n * its place on screen.\n * @returns The handle over the message currently on screen.\n */\n private messageHandle(): ShellMessageRef {\n const closed = this.messageClosed!;\n return {\n close: () => this.closeMessage(),\n afterClosed: () => closed.asObservable()\n };\n }\n\n // ---------------------------------------------------------------------------\n // Busy overlay\n // ---------------------------------------------------------------------------\n\n /**\n * Shows or updates the busy overlay.\n *\n * Called dozens of times per screen, so it creates nothing after the first time: the component\n * is built once and then kept, and showing it again is a class on the host element. The state\n * is written to signals and checked synchronously before returning, so the message on screen is\n * always the last one asked for — a second `busy()` in the same task never leaves the previous\n * text behind.\n * @param message - Text to display. An empty string keeps the current one.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param type - Visual style of the overlay. Defaults to `'bar'`.\n * @param action - Optional observable; the overlay is dismissed when it first emits. It\n * replaces the one passed to a previous call, which is unsubscribed.\n * @returns `true` if the overlay was brought up by this call, `false` if it was already up.\n */\n setBusy(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n type: ShellBusyType = 'bar',\n action?: Observable<unknown>\n ): boolean {\n if (!SystemUtils.isBrowser()) return false;\n\n // Cancel any pending clearBusy(): it must never take away an overlay just asked for.\n clearTimeout(this.clearBusyTimer);\n this.clearBusyTimer = undefined;\n\n const created = !this.busyVisible;\n const ref = this.ensureBusy();\n ref.instance.set(message, progress, progressMode, type);\n ref.instance.visible.set(true);\n this.busyVisible = true;\n ref.changeDetectorRef.detectChanges();\n\n // The action always replaces the previous one. Keeping the first, as the previous\n // implementation did, meant the overlay outlived the operation that was actually running.\n this.busyActionSubscription?.unsubscribe();\n this.busyActionSubscription = action?.pipe(first()).subscribe(() => this.clearBusy());\n\n return created;\n }\n\n /**\n * Shows or updates the busy overlay using the progress-bar style.\n * @param message - Text to display inside the overlay.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busy(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n action?: Observable<unknown>\n ): boolean {\n return this.setBusy(message, progress, progressMode, 'bar', action);\n }\n\n /**\n * Shows or updates the busy overlay using the spinner style.\n * @param message - Text to display inside the overlay.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busySpinner(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n action?: Observable<unknown>\n ): boolean {\n return this.setBusy(message, progress, progressMode, 'spinner', action);\n }\n\n /**\n * Shows or updates the busy overlay using the hourglass style.\n * @param message - Text to display inside the overlay.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busyHourglass(message: string, action?: Observable<unknown>): boolean {\n return this.setBusy(message, -1, 'indeterminate', 'hourglass', action);\n }\n\n /**\n * Shows a bare wait indicator, with no panel and no message.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n wait(action?: Observable<unknown>): boolean {\n return this.setBusy('', -1, 'indeterminate', 'wait', action);\n }\n\n /**\n * Returns a {@link BusyTimer} that brings the overlay up only if the operation is still running\n * after a debounce delay.\n * @param message - Text to display. Defaults to `'Operazione in corso...'`.\n * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.\n * @returns A timer that must be disposed with `clear()` when the operation ends.\n */\n busyTimer(message: string = 'Operazione in corso...', due: number = 100): BusyTimer {\n return new BusyTimer(this, due, message);\n }\n\n /**\n * Takes the busy overlay away.\n *\n * By default it waits a short grace period, so that a chain of quick operations does not make\n * the overlay blink between one and the next; any `setBusy()` in the meantime cancels the\n * pending removal. Pass `true` when something is about to be drawn underneath it and the wait\n * would be visible — that is what `DialogService.open()` does.\n * @param immediate - When `true`, removes the overlay without waiting for the grace period.\n * @returns void\n */\n clearBusy(immediate: boolean = false): void {\n clearTimeout(this.clearBusyTimer);\n this.clearBusyTimer = undefined;\n\n if (immediate) {\n this.hideBusy();\n return;\n }\n this.clearBusyTimer = setTimeout(() => {\n this.clearBusyTimer = undefined;\n this.hideBusy();\n }, CLEAR_BUSY_GRACE);\n }\n\n /**\n * Hides the busy overlay without destroying it, so the next call can reuse the instance.\n * @returns void\n */\n private hideBusy(): void {\n this.busyActionSubscription?.unsubscribe();\n this.busyActionSubscription = undefined;\n if (!this.busyVisible) return;\n this.busyVisible = false;\n this.busyRef?.instance.visible.set(false);\n this.busyRef?.changeDetectorRef.detectChanges();\n }\n\n /**\n * Returns the busy component, creating and attaching it the first time it is needed.\n * @returns The reused busy component reference.\n */\n private ensureBusy(): ComponentRef<ShellBusyComponent> {\n if (this.busyRef) return this.busyRef;\n\n const ref = createComponent(ShellBusyComponent, { environmentInjector: this.environmentInjector });\n this.appRef.attachView(ref.hostView);\n document.body.appendChild(ref.location.nativeElement as HTMLElement);\n this.busyRef = ref;\n return ref;\n }\n}\n","/*\n * Public API Surface of @arsedizioni/ars-utils/ui.shell\n */\nexport * from './definitions';\nexport * from './busy-timer';\nexport * from './shell.service';\nexport * from './shell-message/shell-message.component';\nexport * from './shell-busy/shell-busy.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"arsedizioni-ars-utils-ui.shell.mjs","sources":["../../../projects/ars-utils/ui.shell/busy-timer.ts","../../../projects/ars-utils/ui.shell/shell-busy/shell-busy.component.ts","../../../projects/ars-utils/ui.shell/shell-busy/shell-busy.component.html","../../../projects/ars-utils/ui.shell/shell-message/shell-message.component.ts","../../../projects/ars-utils/ui.shell/shell-message/shell-message.component.html","../../../projects/ars-utils/ui.shell/shell.service.ts","../../../projects/ars-utils/ui.shell/public_api.ts","../../../projects/ars-utils/ui.shell/arsedizioni-ars-utils-ui.shell.ts"],"sourcesContent":["import { Subscription, timer } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport type { ShellService } from './shell.service';\n\n/**\n * Shows the busy overlay only if the operation is still running after a debounce delay, so that\n * an operation that answers in 30 ms never makes the screen flash.\n *\n * Whoever creates one owns it: {@link clear} must be called when the operation ends, otherwise\n * the overlay appears after the fact and stays.\n */\nexport class BusyTimer {\n private readonly subscription: Subscription;\n\n /**\n * Arms the timer.\n * @param shellService - The service that owns the overlay. A `DialogService` is accepted too,\n * since it extends `ShellService`.\n * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.\n * @param message - Text to display. Defaults to `'Operazione in corso...'`.\n */\n constructor(\n private readonly shellService: ShellService,\n due: number = 100,\n message: string = 'Operazione in corso...'\n ) {\n this.subscription = timer(due).pipe(tap(() => this.shellService.busy(message))).subscribe();\n }\n\n /**\n * Disarms the timer and takes the overlay away.\n * @returns void\n */\n clear(): void {\n this.subscription.unsubscribe();\n this.shellService.clearBusy();\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { SafeHtmlPipe, ThemeService, ThemeType } from '@arsedizioni/ars-utils/core';\n\n/** Visual style of the busy indicator. */\nexport type ShellBusyType = 'bar' | 'spinner' | 'wait' | 'hourglass';\n\n/** Progress mode of the bar and of the spinner. */\nexport type ShellBusyMode = 'determinate' | 'indeterminate';\n\n/** Circumference of the spinner circle, in user units. Kept here so the template stays arithmetic-free. */\nconst SPINNER_CIRCUMFERENCE = 2 * Math.PI * 31.5;\n\n/**\n * Overlay that blocks interaction while an operation is running, in the four styles the\n * applications use: progress bar, spinner, hourglass and bare wait.\n *\n * Reproduces `mat-progress-bar` and `mat-progress-spinner` in CSS and SVG rather than importing\n * them, because this overlay is shown from the boot path — an HTTP interceptor calls it before\n * any route exists — and `ui.shell` exists precisely so that path costs no Material.\n *\n * The instance is created once by {@link ShellService} and then reused: showing and hiding is a\n * class on the host, never a `createComponent`, so the 40-odd calls a screen costs nothing.\n */\n@Component({\n selector: 'ars-shell-busy',\n templateUrl: './shell-busy.component.html',\n styleUrl: './shell-busy.component.scss',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [SafeHtmlPipe],\n host: { '[class.ars-hidden]': '!visible()', '[class.ars-dark]': 'dark()' }\n})\nexport class ShellBusyComponent {\n\n private readonly themeService = inject(ThemeService);\n\n /**\n * True when the panel must paint itself dark.\n *\n * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not\n * always agree: the service is what the user chose, and it may be forcing dark on a machine\n * that is on light. Following the OS instead would leave this panel the only light thing on a\n * dark screen.\n */\n protected readonly dark = signal(false);\n\n /**\n * Keeps {@link dark} in step with the service, now and at every later change.\n *\n * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the\n * operating system is currently on.\n * @returns void\n */\n private followTheme(): void {\n const resolve = (theme: ThemeType): boolean =>\n (theme === 'auto' ? this.themeService.getTheme() : theme) === 'dark';\n this.dark.set(resolve(this.themeService.getTheme()));\n this.themeService.changed\n .pipe(takeUntilDestroyed())\n .subscribe(theme => this.dark.set(resolve(theme)));\n }\n\n /** Whether the overlay is on screen. Hidden with `display: none`, so it costs no layout. */\n readonly visible = signal(false);\n\n /** Visual style of the indicator. */\n readonly type = signal<ShellBusyType>('bar');\n\n /** Current progress value (0-100), used when {@link progressMode} is `'determinate'`. */\n readonly progress = signal(0);\n\n /** Progress mode of the bar and of the spinner. */\n readonly progressMode = signal<ShellBusyMode>('indeterminate');\n\n /** Message displayed above the indicator. */\n readonly message = signal('');\n\n /** Circumference of the spinner circle, bound to `stroke-dasharray`. */\n protected readonly circumference = SPINNER_CIRCUMFERENCE;\n\n constructor() {\n this.followTheme();\n }\n\n /** Length of the spinner arc still to be drawn, derived from {@link progress}. */\n protected readonly dashOffset = computed(\n () => SPINNER_CIRCUMFERENCE * (1 - Math.min(Math.max(this.progress(), 0), 100) / 100)\n );\n\n /**\n * Updates the overlay state.\n *\n * Same contract as the `BusyDialogComponent` it replaces, down to the two implicit rules that\n * callers rely on: an empty message keeps the previous text (so `wait()` does not wipe it), and\n * a progress above zero forces `'determinate'` whatever the caller passed.\n * @param message - New message to display. An empty string preserves the current one.\n * @param progress - Current progress value (0-100). Above zero it forces determinate mode.\n * @param progressMode - Progress mode to use. Defaults to `'indeterminate'`.\n * @param type - Visual style to use. Defaults to `'bar'`.\n * @returns void\n */\n set(\n message: string,\n progress: number,\n progressMode: ShellBusyMode = 'indeterminate',\n type: ShellBusyType = 'bar'\n ): void {\n if (message) this.message.set(message);\n if (type) this.type.set(type);\n this.progress.set(progress);\n this.progressMode.set(progress > 0 ? 'determinate' : progressMode);\n }\n}\n","<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","import {\n ChangeDetectionStrategy, Component, DestroyRef, ElementRef,\n HostListener, afterNextRender, inject, output, signal, viewChild\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { SafeHtmlPipe, ThemeService, ThemeType } from '@arsedizioni/ars-utils/core';\nimport { ShellMessageData } from '../definitions';\n\n/**\n * The shell's message panel: a modal that looks like a Material dialog and owes nothing to\n * Material.\n *\n * It exists because shell, interceptors, guards and app initializers must be able to say\n * \"the session is gone\" before a single route has been loaded, and paying for `mat-dialog`,\n * `cdk/overlay` and the rest of that stack on the boot path costs the initial bundle more\n * than every route of the application put together. The look is matched through CSS custom\n * properties (`--ars-*` first, Material's `--mat-sys-*` next, a literal last), so an\n * application that themes Material gets the same surface here for free.\n *\n * Instantiated imperatively by {@link ShellService}, never declared in a template: that is\n * what lets an interceptor open it.\n */\n@Component({\n selector: 'ars-shell-message',\n templateUrl: './shell-message.component.html',\n styleUrl: './shell-message.component.scss',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [SafeHtmlPipe],\n host: { '[class.ars-dark]': 'dark()' }\n})\nexport class ShellMessageComponent {\n\n /** Emitted when the user dismisses the message, whichever way they did it. */\n readonly closed = output<void>();\n\n private readonly themeService = inject(ThemeService);\n\n private readonly panel = viewChild.required<ElementRef<HTMLElement>>('panel');\n private readonly okButton = viewChild.required<ElementRef<HTMLButtonElement>>('ok');\n\n /** Unique id tying the panel to its heading for assistive technology. */\n protected readonly titleId = `ars-shell-message-title-${Math.random().toString(36).slice(2, 10)}`;\n\n /** Current content, with the defaults the shell relies on already applied. */\n protected readonly data = signal<ShellMessageData>({ message: '', title: 'Informazioni', okCaption: 'Ok', width: 500 });\n\n /** True for a moment after a successful copy, so the button can acknowledge it. */\n protected readonly copied = signal(false);\n\n private dismissTimer?: ReturnType<typeof setTimeout>;\n private copiedTimer?: ReturnType<typeof setTimeout>;\n private closing = false;\n\n /**\n * True when the panel must paint itself dark.\n *\n * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not\n * always agree: the service is what the user chose, and it may be forcing dark on a machine\n * that is on light. Following the OS instead would leave this panel the only light thing on a\n * dark screen.\n */\n protected readonly dark = signal(false);\n\n /**\n * Keeps {@link dark} in step with the service, now and at every later change.\n *\n * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the\n * operating system is currently on.\n * @returns void\n */\n private followTheme(): void {\n const resolve = (theme: ThemeType): boolean =>\n (theme === 'auto' ? this.themeService.getTheme() : theme) === 'dark';\n this.dark.set(resolve(this.themeService.getTheme()));\n this.themeService.changed\n .pipe(takeUntilDestroyed())\n .subscribe(theme => this.dark.set(resolve(theme)));\n }\n\n constructor() {\n this.followTheme();\n afterNextRender(() => this.okButton().nativeElement.focus());\n inject(DestroyRef).onDestroy(() => {\n clearTimeout(this.dismissTimer);\n clearTimeout(this.copiedTimer);\n });\n }\n\n /**\n * Replaces the content shown by the panel and restarts the auto-dismiss timer.\n *\n * Called on an already open panel by `ShellService.error()` so that a burst of failures\n * updates one message instead of stacking modals nobody can dismiss.\n * @param data - The new content to display.\n * @returns void\n */\n setData(data: ShellMessageData): void {\n this.data.set({ title: 'Informazioni', okCaption: 'Ok', width: 500, ...data });\n this.scheduleDismiss(this.data().dismissAfter);\n }\n\n /**\n * Dismisses the panel. Safe to call more than once: only the first call is announced.\n * @returns void\n */\n close(): void {\n if (this.closing) return;\n this.closing = true;\n clearTimeout(this.dismissTimer);\n this.closed.emit();\n }\n\n /**\n * Closes the panel on Escape, mirroring a Material dialog opened with the default config.\n * @param e - The keyboard event captured at document level.\n * @returns void\n */\n @HostListener('document:keydown', ['$event'])\n protected onKeydown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n this.close();\n return;\n }\n if (e.key === 'Tab') this.trapFocus(e);\n }\n\n /**\n * Keeps Tab inside the panel while the message is up.\n *\n * A hand-rolled two-element version of what `cdk/a11y` does, which is all this panel needs:\n * it never holds more than the copy button and the dismiss button.\n * @param e - The Tab keydown event.\n * @returns void\n */\n private trapFocus(e: KeyboardEvent): void {\n const focusable = Array.from(\n this.panel().nativeElement.querySelectorAll<HTMLElement>('button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])')\n );\n if (!focusable.length) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n\n if (e.shiftKey && (active === first || !this.panel().nativeElement.contains(active))) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n }\n\n /**\n * (Re)schedules the auto-dismiss timer, cancelling any pending one so a timer armed for an\n * earlier message can never close a newer one.\n * @param dismissAfter - Delay in milliseconds; no-op when falsy.\n * @returns void\n */\n private scheduleDismiss(dismissAfter?: number): void {\n clearTimeout(this.dismissTimer);\n this.dismissTimer = undefined;\n if (dismissAfter) this.dismissTimer = setTimeout(() => this.close(), dismissAfter);\n }\n\n /**\n * Copies the message and its details to the clipboard, as both HTML and plain text.\n *\n * Unlike `InfoDialogComponent` there is no toast to report the outcome — the shell has no\n * toast — so the button itself acknowledges it for a moment.\n * @returns A promise that resolves once the copy has been attempted.\n */\n protected async copy(): Promise<void> {\n const content = this.panel().nativeElement.querySelector<HTMLElement>('.content');\n if (!content) return;\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n 'text/html': new Blob([content.innerHTML], { type: 'text/html' }),\n 'text/plain': new Blob([content.innerText], { type: 'text/plain' })\n })\n ]);\n this.copied.set(true);\n clearTimeout(this.copiedTimer);\n this.copiedTimer = setTimeout(() => this.copied.set(false), 1500);\n } catch {\n // Clipboard permission denied or the document is not focused: nothing worth interrupting\n // the user for, and the text is on screen anyway.\n }\n }\n}\n","<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","import { ApplicationRef, ComponentRef, EnvironmentInjector, Service, createComponent, inject } from '@angular/core';\nimport { SystemUtils } from '@arsedizioni/ars-utils/core';\nimport { Observable, Subject, Subscription } from 'rxjs';\nimport { first } from 'rxjs/operators';\nimport { BusyTimer } from './busy-timer';\nimport { ShellMessageData, ShellMessageRef } from './definitions';\nimport { ShellBusyComponent, ShellBusyMode, ShellBusyType } from './shell-busy/shell-busy.component';\nimport { ShellMessageComponent } from './shell-message/shell-message.component';\n\n/** Grace period before the busy overlay is actually taken away, in milliseconds. */\nconst CLEAR_BUSY_GRACE = 500;\n\n/**\n * Everything the application can say to the user without asking anything back: a message, an\n * error, and the wait overlay. No Angular Material behind any of it.\n *\n * `DialogService` extends this class, so a route component injects `DialogService` and has both\n * halves; shell, HTTP interceptors, route guards and app initializers inject `ShellService` and\n * pay for neither `mat-dialog` nor `cdk/overlay` on the boot path — measured at 445 KB of the\n * 905 KB initial bundle on myARS, more than every route of the application put together.\n *\n * The methods keep the parameter order of the `DialogService` ones they came from, so no call\n * site changed when they moved here.\n *\n * ## Stacking\n *\n * Outside the CDK overlay stack the order is a fixed z-index: the busy overlay sits at 1090 and\n * the message at 1100, both above the `cdk-overlay-container` (1000). So an error raised while a\n * Material dialog is open lands on top of it, which is what an error should do, and the busy\n * overlay covers the dialog it belongs to. The one case a fixed number cannot express is a dialog\n * opened while the overlay is still up — `DialogService.open()` takes the busy away immediately\n * rather than with the grace period, precisely so that case cannot happen.\n */\n@Service()\nexport class ShellService {\n\n private readonly appRef = inject(ApplicationRef);\n private readonly environmentInjector = inject(EnvironmentInjector);\n\n private messageRef?: ComponentRef<ShellMessageComponent>;\n private messageKind?: 'info' | 'error';\n private messageClosed?: Subject<void>;\n private previousOverflow?: string;\n private previouslyFocused?: HTMLElement;\n\n private busyRef?: ComponentRef<ShellBusyComponent>;\n private busyVisible = false;\n private busyActionSubscription?: Subscription;\n private clearBusyTimer?: ReturnType<typeof setTimeout>;\n\n // ---------------------------------------------------------------------------\n // Messages\n // ---------------------------------------------------------------------------\n\n /**\n * Shows an informational message.\n * @param message - HTML message to display.\n * @param title - Heading. Defaults to `'Informazioni'`.\n * @param okCaption - Dismiss button label. Defaults to `'Ok'`.\n * @param width - Maximum panel width in pixels. Defaults to `500`.\n * @param dismissAfter - Auto-close delay in milliseconds (optional).\n * @param details - Optional secondary details text.\n * @returns A handle over the open message, or `null` outside the browser.\n */\n info(\n message: string,\n title: string = 'Informazioni',\n okCaption: string = 'Ok',\n width: number = 500,\n dismissAfter?: number,\n details?: string\n ): ShellMessageRef | null {\n this.clearBusy();\n return this.showMessage('info', { message, title, okCaption, width, dismissAfter, details });\n }\n\n /**\n * Shows an error message. When one is already on screen its content is replaced in place: a\n * failing API answers many requests at once, and each of them must not add a panel of its own.\n * @param message - HTML error message to display.\n * @param log - Optional technical log or stack trace shown in the details panel.\n * @param title - Heading. Defaults to `'Errore'`.\n * @param okCaption - Dismiss button label. Defaults to `'Ok'`.\n * @param width - Maximum panel width in pixels. Defaults to `500`.\n * @param dismissAfter - Auto-close delay in milliseconds (optional).\n * @returns A handle over the open message, or `null` outside the browser.\n */\n error(\n message: string,\n log?: string,\n title: string = 'Errore',\n okCaption: string = 'Ok',\n width: number = 500,\n dismissAfter?: number\n ): ShellMessageRef | null {\n this.clearBusy();\n return this.showMessage('error', { message, title, okCaption, width, dismissAfter, details: log });\n }\n\n /**\n * Closes the message currently on screen, if any. Does not touch the busy overlay.\n * @returns void\n */\n closeMessage(): void {\n if (!this.messageRef) return;\n\n const ref = this.messageRef;\n this.messageRef = undefined;\n this.messageKind = undefined;\n\n this.appRef.detachView(ref.hostView);\n ref.destroy();\n\n if (this.previousOverflow !== undefined) {\n document.body.style.overflow = this.previousOverflow;\n this.previousOverflow = undefined;\n }\n this.previouslyFocused?.focus();\n this.previouslyFocused = undefined;\n\n // Last, so that whoever is waiting on it finds the screen already in its final state.\n const closed = this.messageClosed;\n this.messageClosed = undefined;\n closed?.next();\n closed?.complete();\n }\n\n /**\n * Opens the message panel, or updates the one already open when it is showing the same kind of\n * message.\n * @param kind - Whether this is an informational or an error message.\n * @param data - The content to display.\n * @returns A handle over the open message, or `null` outside the browser.\n */\n private showMessage(kind: 'info' | 'error', data: ShellMessageData): ShellMessageRef | null {\n if (!SystemUtils.isBrowser()) return null;\n\n if (this.messageRef && this.messageKind === kind) {\n this.messageRef.instance.setData(data);\n this.messageRef.changeDetectorRef.detectChanges();\n return this.messageHandle();\n }\n\n // A message of a different kind replaces the one on screen rather than stacking on it.\n this.closeMessage();\n\n const ref = createComponent(ShellMessageComponent, { environmentInjector: this.environmentInjector });\n ref.instance.setData(data);\n ref.instance.closed.subscribe(() => this.closeMessage());\n\n // One subject per panel, not per handle: an `error()` that replaces the text of the message\n // already on screen hands out a second handle, and both must fire when that message goes.\n this.messageClosed = new Subject<void>();\n\n this.previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : undefined;\n this.previousOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n this.appRef.attachView(ref.hostView);\n document.body.appendChild(ref.location.nativeElement as HTMLElement);\n\n this.messageRef = ref;\n this.messageKind = kind;\n return this.messageHandle();\n }\n\n /**\n * Builds the handle returned to callers.\n *\n * The subject is captured now rather than read at call time, so `afterClosed()` keeps\n * answering about the message this handle was given for, even once another one has taken\n * its place on screen.\n * @returns The handle over the message currently on screen.\n */\n private messageHandle(): ShellMessageRef {\n const closed = this.messageClosed!;\n return {\n close: () => this.closeMessage(),\n afterClosed: () => closed.asObservable()\n };\n }\n\n // ---------------------------------------------------------------------------\n // Busy overlay\n // ---------------------------------------------------------------------------\n\n /**\n * Shows or updates the busy overlay.\n *\n * Called dozens of times per screen, so it creates nothing after the first time: the component\n * is built once and then kept, and showing it again is a class on the host element. The state\n * is written to signals and checked synchronously before returning, so the message on screen is\n * always the last one asked for — a second `busy()` in the same task never leaves the previous\n * text behind.\n * @param message - Text to display. An empty string keeps the current one.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param type - Visual style of the overlay. Defaults to `'bar'`.\n * @param action - Optional observable; the overlay is dismissed when it first emits. It\n * replaces the one passed to a previous call, which is unsubscribed.\n * @returns `true` if the overlay was brought up by this call, `false` if it was already up.\n */\n setBusy(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n type: ShellBusyType = 'bar',\n action?: Observable<unknown>\n ): boolean {\n if (!SystemUtils.isBrowser()) return false;\n\n // Cancel any pending clearBusy(): it must never take away an overlay just asked for.\n clearTimeout(this.clearBusyTimer);\n this.clearBusyTimer = undefined;\n\n const created = !this.busyVisible;\n const ref = this.ensureBusy();\n ref.instance.set(message, progress, progressMode, type);\n ref.instance.visible.set(true);\n this.busyVisible = true;\n ref.changeDetectorRef.detectChanges();\n\n // The action always replaces the previous one. Keeping the first, as the previous\n // implementation did, meant the overlay outlived the operation that was actually running.\n this.busyActionSubscription?.unsubscribe();\n this.busyActionSubscription = action?.pipe(first()).subscribe(() => this.clearBusy());\n\n return created;\n }\n\n /**\n * Shows or updates the busy overlay using the progress-bar style.\n * @param message - Text to display inside the overlay.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busy(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n action?: Observable<unknown>\n ): boolean {\n return this.setBusy(message, progress, progressMode, 'bar', action);\n }\n\n /**\n * Shows or updates the busy overlay using the spinner style.\n * @param message - Text to display inside the overlay.\n * @param progress - Progress value (`-1` = indeterminate). Defaults to `-1`.\n * @param progressMode - Progress mode. Defaults to `'indeterminate'`.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busySpinner(\n message: string,\n progress: number = -1,\n progressMode: ShellBusyMode = 'indeterminate',\n action?: Observable<unknown>\n ): boolean {\n return this.setBusy(message, progress, progressMode, 'spinner', action);\n }\n\n /**\n * Shows or updates the busy overlay using the hourglass style.\n * @param message - Text to display inside the overlay.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n busyHourglass(message: string, action?: Observable<unknown>): boolean {\n return this.setBusy(message, -1, 'indeterminate', 'hourglass', action);\n }\n\n /**\n * Shows a bare wait indicator, with no panel and no message.\n * @param action - Optional observable; the overlay is dismissed when it emits.\n * @returns `true` if the overlay was brought up by this call.\n */\n wait(action?: Observable<unknown>): boolean {\n return this.setBusy('', -1, 'indeterminate', 'wait', action);\n }\n\n /**\n * Returns a {@link BusyTimer} that brings the overlay up only if the operation is still running\n * after a debounce delay.\n * @param message - Text to display. Defaults to `'Operazione in corso...'`.\n * @param due - Delay in milliseconds before the overlay appears. Defaults to `100`.\n * @returns A timer that must be disposed with `clear()` when the operation ends.\n */\n busyTimer(message: string = 'Operazione in corso...', due: number = 100): BusyTimer {\n return new BusyTimer(this, due, message);\n }\n\n /**\n * Takes the busy overlay away.\n *\n * By default it waits a short grace period, so that a chain of quick operations does not make\n * the overlay blink between one and the next; any `setBusy()` in the meantime cancels the\n * pending removal. Pass `true` when something is about to be drawn underneath it and the wait\n * would be visible — that is what `DialogService.open()` does.\n * @param immediate - When `true`, removes the overlay without waiting for the grace period.\n * @returns void\n */\n clearBusy(immediate: boolean = false): void {\n clearTimeout(this.clearBusyTimer);\n this.clearBusyTimer = undefined;\n\n if (immediate) {\n this.hideBusy();\n return;\n }\n this.clearBusyTimer = setTimeout(() => {\n this.clearBusyTimer = undefined;\n this.hideBusy();\n }, CLEAR_BUSY_GRACE);\n }\n\n /**\n * Hides the busy overlay without destroying it, so the next call can reuse the instance.\n * @returns void\n */\n private hideBusy(): void {\n this.busyActionSubscription?.unsubscribe();\n this.busyActionSubscription = undefined;\n if (!this.busyVisible) return;\n this.busyVisible = false;\n this.busyRef?.instance.visible.set(false);\n this.busyRef?.changeDetectorRef.detectChanges();\n }\n\n /**\n * Returns the busy component, creating and attaching it the first time it is needed.\n * @returns The reused busy component reference.\n */\n private ensureBusy(): ComponentRef<ShellBusyComponent> {\n if (this.busyRef) return this.busyRef;\n\n const ref = createComponent(ShellBusyComponent, { environmentInjector: this.environmentInjector });\n this.appRef.attachView(ref.hostView);\n document.body.appendChild(ref.location.nativeElement as HTMLElement);\n this.busyRef = ref;\n return ref;\n }\n}\n","/*\n * Public API Surface of @arsedizioni/ars-utils/ui.shell\n */\nexport * from './definitions';\nexport * from './busy-timer';\nexport * from './shell.service';\nexport * from './shell-message/shell-message.component';\nexport * from './shell-busy/shell-busy.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arsedizioni/ars-utils",
3
- "version": "22.5.3",
3
+ "version": "22.5.4",
4
4
  "author": {
5
5
  "email": "software@arsedizioni.it",
6
6
  "name": "Fabio Buscaroli, Alberto Doria"
@@ -1,100 +1,130 @@
1
- :root {
1
+ // Palette ARS.
2
+ //
3
+ // I token vivono in tre posti e non in due, e l'ordine conta:
4
+ //
5
+ // :root i valori chiari, sempre
6
+ // @media (prefers-color-scheme: dark) il sistema operativo, finche' nessuno ha scelto
7
+ // body.light / body.dark la scelta dell'utente, che batte il sistema
8
+ //
9
+ // L'ultima riga e' quella che serve: `ThemeService` scrive `light` o `dark` su <body>, anche
10
+ // quando la preferenza e' 'auto' (in quel caso risolve lui contro il sistema). Senza quei due
11
+ // blocchi, un utente che forza il tema scuro su una macchina chiara si ritrova i componenti
12
+ // Material scuri e tutto cio' che dipende da questi token ancora chiaro.
13
+
14
+ @mixin ars-light-tokens {
15
+
16
+ // Colors
17
+ --ars-color-link: #03A9F4;
18
+ --ars-color-link-low: #0288D1;
19
+ --ars-color-accent: #7894ae;
20
+ --ars-color-accent-low: #456179;
21
+ --ars-color-primary: #00a293;
22
+ --ars-color-primary-low: #006a60;
23
+ --ars-color-primary-hi: #12c0ae;
24
+ --ars-color-secondary: #4a635f;
25
+ --ars-color-secondary-low: #27403c;
26
+ --ars-color-error: #ff5449;
27
+ --ars-color-error-low: #a80710;
28
+ --ars-color-ok: #388E3C;
29
+ --ars-color-ok-low: #29692c;
30
+ --ars-color-warning: #FFC107;
31
+ --ars-color-warning-low: #FFA000;
32
+ --ars-color-text: #191c1b;
33
+ --ars-color-text-low: #e0e2e5;
34
+ --ars-color-divider: #757d87;
35
+ --ars-color-overlay: rgba(255, 255, 255, 0.75);
36
+ --ars-color-background: #f7faf8;
37
+ // Dialog surface: shared by the Material dialogs and by ui.shell, which reproduces
38
+ // their look without Material. Each one resolves to the app Material theme when there is
39
+ // one, and to a literal when there is not.
40
+ --ars-dialog-background-color: var(--mat-sys-surface-container-high, #e6eae8);
41
+ --ars-color-on-primary: var(--mat-sys-on-primary, #ffffff);
42
+ --ars-scrim-color: rgba(0, 0, 0, 0.32);
43
+ --ars-dialog-radius: 28px;
44
+ --ars-dialog-elevation: 0 4px 8px 3px rgba(0, 0, 0, 0.15), 0 1px 3px 0 rgba(0, 0, 0, 0.3);
45
+ // Busy overlay and progress indicators (ui.shell reproduces them without Material)
46
+ --ars-busy-scrim-color: rgba(0, 0, 0, 0.42);
47
+ --ars-progress-track-color: var(--mat-sys-surface-container-highest, #d5dbd8);
48
+ --ars-busy-bare-color: #ffffff;
49
+ // Stacking: cdk-overlay-container sits at 1000, so the shell overlays go above it and the
50
+ // busy one below the message, which is the order the CDK stack used to give for free.
51
+ --ars-shell-z-index: 1100;
52
+ --ars-busy-z-index: 1090;
53
+ // Drawer
54
+ --ars-drawer-background-color: #eff1ef;
55
+ --ars-drawer-divider-color: #6f7977;
56
+ // Item
57
+ --ars-item-hover-background-color: var(--mat-icon-button-ripple-color);
58
+ --ars-item-selected-background-color: #EAEEED;
59
+ --ars-dialog-item-selected-background-color: rgba(206, 209, 210, 0.9) !important;
60
+ --ars-dialog-item-hover-background-color: rgba(234, 236, 239, 0.9) !important;
61
+ // Filter Box
62
+ --ars-filterbox-filtered-background-color: #f9fbe7;
63
+ // File Preview
64
+ --ars-filepreview-content-background-color: #f7faf8;
65
+ }
66
+
67
+ @mixin ars-dark-tokens {
68
+
2
69
  // Colors
3
70
  --ars-color-link: #03A9F4;
4
71
  --ars-color-link-low: #0288D1;
5
- --ars-color-accent: #7894ae;
6
- --ars-color-accent-low: #456179;
7
- --ars-color-primary: #00a293;
8
- --ars-color-primary-low: #006a60;
9
- --ars-color-primary-hi: #12c0ae;
10
- --ars-color-secondary: #4a635f;
11
- --ars-color-secondary-low: #27403c;
72
+ --ars-color-accent: #adcae6;
73
+ --ars-color-accent-low: #5e7a93;
74
+ --ars-color-primary: #53dbc9;
75
+ --ars-color-primary-low: #008679;
76
+ --ars-color-primary-hi: #74f8e5;
77
+ --ars-color-secondary: #b1ccc6;
78
+ --ars-color-secondary-low: #627c77;
12
79
  --ars-color-error: #ff5449;
13
80
  --ars-color-error-low: #a80710;
14
- --ars-color-ok: #388E3C;
15
- --ars-color-ok-low: #29692c;
81
+ --ars-color-error-text: #191c1b;
82
+ --ars-color-ok: #4CAF50;
83
+ --ars-color-ok-low: #3f8f41;
16
84
  --ars-color-warning: #FFC107;
17
85
  --ars-color-warning-low: #FFA000;
18
- --ars-color-text: #191c1b;
19
- --ars-color-text-low: #e0e2e5;
86
+ --ars-color-warning-text: #191c1b;
87
+ --ars-color-text: #e0e2e5;
88
+ --ars-color-text-low: #191c1b;
20
89
  --ars-color-divider: #757d87;
21
- --ars-color-overlay: rgba(255, 255, 255, 0.75);
22
- --ars-color-background: #f7faf8;
23
- // Dialog surface: shared by the Material dialogs and by ui.shell, which reproduces
24
- // their look without Material. Each one resolves to the app Material theme when there is
25
- // one, and to a literal when there is not.
26
- --ars-dialog-background-color: var(--mat-sys-surface-container-high, #e6eae8);
27
- --ars-color-on-primary: var(--mat-sys-on-primary, #ffffff);
28
- --ars-scrim-color: rgba(0, 0, 0, 0.32);
29
- --ars-dialog-radius: 28px;
30
- --ars-dialog-elevation: 0 4px 8px 3px rgba(0, 0, 0, 0.15), 0 1px 3px 0 rgba(0, 0, 0, 0.3);
31
- // Busy overlay and progress indicators (ui.shell reproduces them without Material)
32
- --ars-busy-scrim-color: rgba(0, 0, 0, 0.42);
33
- --ars-progress-track-color: var(--mat-sys-surface-container-highest, #d5dbd8);
34
- --ars-busy-bare-color: #ffffff;
35
- // Stacking: cdk-overlay-container sits at 1000, so the shell overlays go above it and the
36
- // busy one below the message, which is the order the CDK stack used to give for free.
37
- --ars-shell-z-index: 1100;
38
- --ars-busy-z-index: 1090;
39
- // Drawer
40
- --ars-drawer-background-color: #eff1ef;
41
- --ars-drawer-divider-color: #6f7977;
90
+ --ars-color-overlay: rgba(0, 0, 0, 0.65);
91
+ --ars-color-background: #101413;
92
+
93
+ // Dialog surface
94
+ --ars-dialog-background-color: var(--mat-sys-surface-container-high, #282a29);
95
+ --ars-color-on-primary: var(--mat-sys-on-primary, #00382f);
96
+ --ars-scrim-color: rgba(0, 0, 0, 0.6);
97
+ // Busy overlay and progress indicators
98
+ --ars-busy-scrim-color: rgba(0, 0, 0, 0.6);
99
+ --ars-progress-track-color: var(--mat-sys-surface-container-highest, #3a3d3c);
100
+ // Drawer
101
+ --ars-drawer-background-color: #212223;
102
+ --ars-drawer-divider-color: #899390;
42
103
  // Item
43
104
  --ars-item-hover-background-color: var(--mat-icon-button-ripple-color);
44
- --ars-item-selected-background-color: #EAEEED;
45
- --ars-dialog-item-selected-background-color: rgba(206, 209, 210, 0.9) !important;
46
- --ars-dialog-item-hover-background-color: rgba(234, 236, 239, 0.9) !important;
105
+ --ars-item-selected-background-color: #1B1F1E;
106
+ --ars-dialog-item-selected-background-color: rgba(63, 67, 66, 0.9) !important;
107
+ --ars-dialog-item-hover-background-color: rgba(38, 42, 41, 0.9) !important;
47
108
  // Filter Box
48
- --ars-filterbox-filtered-background-color: #f9fbe7;
109
+ --ars-filterbox-filtered-background-color: #c6c7b8;
49
110
  // File Preview
50
- --ars-filepreview-content-background-color: #f7faf8;
51
- }
52
-
53
- @media (prefers-color-scheme: dark) {
54
-
55
- :root {
56
- // Colors
57
- --ars-color-link: #03A9F4;
58
- --ars-color-link-low: #0288D1;
59
- --ars-color-accent: #adcae6;
60
- --ars-color-accent-low: #5e7a93;
61
- --ars-color-primary: #53dbc9;
62
- --ars-color-primary-low: #008679;
63
- --ars-color-primary-hi: #74f8e5;
64
- --ars-color-secondary: #b1ccc6;
65
- --ars-color-secondary-low: #627c77;
66
- --ars-color-error: #ff5449;
67
- --ars-color-error-low: #a80710;
68
- --ars-color-error-text: #191c1b;
69
- --ars-color-ok: #4CAF50;
70
- --ars-color-ok-low: #3f8f41;
71
- --ars-color-warning: #FFC107;
72
- --ars-color-warning-low: #FFA000;
73
- --ars-color-warning-text: #191c1b;
74
- --ars-color-text: #e0e2e5;
75
- --ars-color-text-low: #191c1b;
76
- --ars-color-divider: #757d87;
77
- --ars-color-overlay: rgba(0, 0, 0, 0.65);
78
- --ars-color-background: #101413;
79
-
80
- // Dialog surface
81
- --ars-dialog-background-color: var(--mat-sys-surface-container-high, #282a29);
82
- --ars-color-on-primary: var(--mat-sys-on-primary, #00382f);
83
- --ars-scrim-color: rgba(0, 0, 0, 0.6);
84
- // Busy overlay and progress indicators
85
- --ars-busy-scrim-color: rgba(0, 0, 0, 0.6);
86
- --ars-progress-track-color: var(--mat-sys-surface-container-highest, #3a3d3c);
87
- // Drawer
88
- --ars-drawer-background-color: #212223;
89
- --ars-drawer-divider-color: #899390;
90
- // Item
91
- --ars-item-hover-background-color: var(--mat-icon-button-ripple-color);
92
- --ars-item-selected-background-color: #1B1F1E;
93
- --ars-dialog-item-selected-background-color: rgba(63, 67, 66, 0.9) !important;
94
- --ars-dialog-item-hover-background-color: rgba(38, 42, 41, 0.9) !important;
95
- // Filter Box
96
- --ars-filterbox-filtered-background-color: #c6c7b8;
97
- // File Preview
98
- --ars-filepreview-content-background-color: #101413;
99
- }
100
- }
111
+ --ars-filepreview-content-background-color: #101413;
112
+ }
113
+
114
+ :root {
115
+ @include ars-light-tokens;
116
+ }
117
+
118
+ @media (prefers-color-scheme: dark) {
119
+ :root {
120
+ @include ars-dark-tokens;
121
+ }
122
+ }
123
+
124
+ body.light {
125
+ @include ars-light-tokens;
126
+ }
127
+
128
+ body.dark {
129
+ @include ars-dark-tokens;
130
+ }
@@ -62,6 +62,24 @@ type ShellBusyMode = 'determinate' | 'indeterminate';
62
62
  * class on the host, never a `createComponent`, so the 40-odd calls a screen costs nothing.
63
63
  */
64
64
  declare class ShellBusyComponent {
65
+ private readonly themeService;
66
+ /**
67
+ * True when the panel must paint itself dark.
68
+ *
69
+ * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not
70
+ * always agree: the service is what the user chose, and it may be forcing dark on a machine
71
+ * that is on light. Following the OS instead would leave this panel the only light thing on a
72
+ * dark screen.
73
+ */
74
+ protected readonly dark: _angular_core.WritableSignal<boolean>;
75
+ /**
76
+ * Keeps {@link dark} in step with the service, now and at every later change.
77
+ *
78
+ * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the
79
+ * operating system is currently on.
80
+ * @returns void
81
+ */
82
+ private followTheme;
65
83
  /** Whether the overlay is on screen. Hidden with `display: none`, so it costs no layout. */
66
84
  readonly visible: _angular_core.WritableSignal<boolean>;
67
85
  /** Visual style of the indicator. */
@@ -74,6 +92,7 @@ declare class ShellBusyComponent {
74
92
  readonly message: _angular_core.WritableSignal<string>;
75
93
  /** Circumference of the spinner circle, bound to `stroke-dasharray`. */
76
94
  protected readonly circumference: number;
95
+ constructor();
77
96
  /** Length of the spinner arc still to be drawn, derived from {@link progress}. */
78
97
  protected readonly dashOffset: _angular_core.Signal<number>;
79
98
  /**
@@ -294,6 +313,7 @@ declare class BusyTimer {
294
313
  declare class ShellMessageComponent {
295
314
  /** Emitted when the user dismisses the message, whichever way they did it. */
296
315
  readonly closed: _angular_core.OutputEmitterRef<void>;
316
+ private readonly themeService;
297
317
  private readonly panel;
298
318
  private readonly okButton;
299
319
  /** Unique id tying the panel to its heading for assistive technology. */
@@ -305,6 +325,23 @@ declare class ShellMessageComponent {
305
325
  private dismissTimer?;
306
326
  private copiedTimer?;
307
327
  private closing;
328
+ /**
329
+ * True when the panel must paint itself dark.
330
+ *
331
+ * Read from {@link ThemeService} and not from `prefers-color-scheme`, because the two do not
332
+ * always agree: the service is what the user chose, and it may be forcing dark on a machine
333
+ * that is on light. Following the OS instead would leave this panel the only light thing on a
334
+ * dark screen.
335
+ */
336
+ protected readonly dark: _angular_core.WritableSignal<boolean>;
337
+ /**
338
+ * Keeps {@link dark} in step with the service, now and at every later change.
339
+ *
340
+ * `'auto'` is resolved through `getTheme()`, which is the only thing that knows what the
341
+ * operating system is currently on.
342
+ * @returns void
343
+ */
344
+ private followTheme;
308
345
  constructor();
309
346
  /**
310
347
  * Replaces the content shown by the panel and restarts the auto-dismiss timer.