@praxisui/settings-panel 4.0.0-beta.0 → 5.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,6 +80,9 @@ Peer dependencies (Angular v20):
80
80
  - O host observa `SettingsPanelRef.applied$` e `saved$` para aplicar e persistir mudancas.
81
81
  - O shell autoral agora pode ligar resize horizontal compartilhado pela base comum, com `resizable`, `minWidth`, `maxWidth` e `persistSizeKey`.
82
82
  - O `ref` base pode observar `sizeChanged$` quando o host precisar reagir a resize programatico ou interativo.
83
+ - `expanded: true` abre o painel ja expandido de forma efetiva, respeitando `maxWidth`.
84
+ - Expandir temporariamente o painel nao substitui a preferencia persistida do usuario; apenas o resize manual continua sendo persistido.
85
+ - Quando um editor autoral tentar abrir outro painel sobre o atual, o shell passa pela mesma mediacao de fechamento do proprio painel. Se `onBeforeClose` vetar o fechamento, a substituicao nao acontece.
83
86
 
84
87
  ### 2. Drawer runtime
85
88
 
@@ -125,6 +128,11 @@ Hosts devem inferir skin, densidade, spacing e motion sobre os tokens `--pfx-sid
125
128
  - `open(config: SettingsPanelConfig): SettingsPanelRef`
126
129
  - `config.id: string`
127
130
  - `config.title?: string`
131
+ - `config.expanded?: boolean`
132
+ - `config.resizable?: boolean`
133
+ - `config.persistSizeKey?: string`
134
+ - `config.minWidth?: string`
135
+ - `config.maxWidth?: string`
128
136
  - `config.content: { component: Type<any>; inputs?: Record<string, any> }`
129
137
 
130
138
  ### Ref
@@ -132,9 +140,11 @@ Hosts devem inferir skin, densidade, spacing e motion sobre os tokens `--pfx-sid
132
140
  - `applied$: Observable<any>`
133
141
  - `saved$: Observable<any>`
134
142
  - `closed$: Observable<SettingsPanelCloseReason>`
143
+ - `sizeChanged$: Observable<{ width: string }>`
135
144
  - `apply(value: any)`
136
145
  - `save(value: any)`
137
146
  - `reset()`
147
+ - `updateSize(width, options?)`
138
148
  - `close(reason?: SettingsPanelCloseReason)`
139
149
 
140
150
  ### Injection Tokens
@@ -164,6 +174,20 @@ Opcionais:
164
174
  - `customActions$?`
165
175
  - `hideDefaultButtons$?`
166
176
 
177
+ ### `onBeforeClose`
178
+
179
+ `onBeforeClose` pertence ao contrato canonico de authoring. Ele pode:
180
+
181
+ - permitir cleanup antes do fechamento
182
+ - vetar o fechamento retornando `false`
183
+ - ser usado tanto no `Cancel` local quanto na tentativa de substituir um painel autoral ja aberto
184
+
185
+ Na pratica:
186
+
187
+ - `Cancel`, `Esc` e `backdrop` continuam passando pela confirmacao de descarte quando houver `dirty`
188
+ - antes disso, o shell consulta `onBeforeClose`
189
+ - se `onBeforeClose` retornar `false`, o painel continua aberto e o host nao substitui silenciosamente a sessao atual
190
+
167
191
  ## Exemplo de authoring
168
192
 
169
193
  ```ts
@@ -180,6 +204,14 @@ ref.saved$.pipe(take(1)).subscribe((cfg: FilterConfig) => {
180
204
  });
181
205
  ```
182
206
 
207
+ ## Resize e persistencia
208
+
209
+ - `resizable` fica ligado por padrao no shell autoral
210
+ - `persistSizeKey` controla a restauracao da largura no proximo open do mesmo painel
211
+ - `sizeChanged$` emite a largura aplicada de fato
212
+ - resize manual persiste a largura
213
+ - `expanded` e `toggleExpand()` nao sobrescrevem a largura persistida; eles sao tratados como estado transitório de layout
214
+
183
215
  ## Build local
184
216
 
185
217
  ```bash
@@ -15,7 +15,7 @@ import * as i5 from '@angular/material/tooltip';
15
15
  import { MatTooltipModule } from '@angular/material/tooltip';
16
16
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
17
17
  import { filter, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
18
- import { isObservable, firstValueFrom, of, Subject, BehaviorSubject } from 'rxjs';
18
+ import { isObservable, firstValueFrom, of, from, Subject, ReplaySubject, take, BehaviorSubject } from 'rxjs';
19
19
  import * as i1$1 from '@praxisui/core';
20
20
  import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisLayerScaleStyleService, SETTINGS_PANEL_BRIDGE, SURFACE_DRAWER_BRIDGE, GlobalConfigService, FieldControlType, IconPickerService, LoggerService } from '@praxisui/core';
21
21
  import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
@@ -714,19 +714,34 @@ class SettingsPanelComponent {
714
714
  }
715
715
  }
716
716
  }
717
+ initializeLayout(state) {
718
+ this.width = state.width;
719
+ this.minWidth = state.minWidth;
720
+ this.maxWidth = state.maxWidth;
721
+ if (state.expanded) {
722
+ this.collapsedWidthBeforeExpand = state.width;
723
+ this.width = this.computeExpandedWidth();
724
+ this.expanded = true;
725
+ this.ref?.updateSize(this.width, { persist: false });
726
+ return;
727
+ }
728
+ this.expanded = false;
729
+ this.collapsedWidthBeforeExpand = null;
730
+ }
717
731
  toggleExpand() {
718
732
  if (!this.expanded) {
719
733
  this.collapsedWidthBeforeExpand = this.width;
720
734
  const expandedWidth = this.computeExpandedWidth();
721
735
  this.width = expandedWidth;
722
- this.ref.updateSize(expandedWidth);
736
+ this.ref.updateSize(expandedWidth, { persist: false });
723
737
  this.expanded = true;
724
738
  }
725
739
  else {
726
740
  const restoredWidth = this.collapsedWidthBeforeExpand ?? this.width;
727
741
  this.width = restoredWidth;
728
- this.ref.updateSize(restoredWidth);
742
+ this.ref.updateSize(restoredWidth, { persist: false });
729
743
  this.expanded = false;
744
+ this.collapsedWidthBeforeExpand = null;
730
745
  }
731
746
  this.cdr.markForCheck();
732
747
  }
@@ -783,27 +798,23 @@ class SettingsPanelComponent {
783
798
  }
784
799
  }
785
800
  onCancel() {
786
- of(this.isDirty)
787
- .pipe(switchMap((dirty) => {
788
- if (!dirty) {
789
- return of(true);
790
- }
791
- const dialogData = {
792
- title: this.tx('Discard Changes'),
793
- message: this.tx('You have unsaved changes. Are you sure you want to discard them?'),
794
- confirmText: this.tx('Discard'),
795
- cancelText: this.tx('Continue Editing'),
796
- type: 'warning',
797
- icon: 'warning',
798
- };
799
- return this.dialog
800
- .open(ConfirmDialogComponent, { data: dialogData, autoFocus: false })
801
- .afterClosed();
802
- }), filter((confirmed) => confirmed))
801
+ this.requestClose('cancel')
802
+ .pipe(filter((confirmed) => confirmed))
803
803
  .subscribe(() => {
804
804
  this.ref.close('cancel');
805
805
  });
806
806
  }
807
+ requestClose(reason) {
808
+ return this.runBeforeCloseHook(reason).pipe(switchMap((allowed) => {
809
+ if (!allowed) {
810
+ return of(false);
811
+ }
812
+ if (reason === 'cancel' || reason === 'backdrop' || reason === 'esc') {
813
+ return this.confirmDiscardIfNeeded();
814
+ }
815
+ return of(true);
816
+ }));
817
+ }
807
818
  handleKeydown(event) {
808
819
  if (event.key === 'Escape') {
809
820
  event.preventDefault();
@@ -863,8 +874,44 @@ class SettingsPanelComponent {
863
874
  const parsed = Number.parseFloat(normalized.slice(0, -2));
864
875
  return Number.isFinite(parsed) ? parsed : null;
865
876
  }
877
+ runBeforeCloseHook(reason) {
878
+ const instance = this.contentRef?.instance;
879
+ const hook = instance?.onBeforeClose;
880
+ if (typeof hook !== 'function') {
881
+ return of(true);
882
+ }
883
+ try {
884
+ const result = hook.call(instance, reason);
885
+ if (isObservable(result)) {
886
+ return result;
887
+ }
888
+ if (result instanceof Promise) {
889
+ return from(result);
890
+ }
891
+ return of(result !== false);
892
+ }
893
+ catch {
894
+ return of(false);
895
+ }
896
+ }
897
+ confirmDiscardIfNeeded() {
898
+ if (!this.isDirty) {
899
+ return of(true);
900
+ }
901
+ const dialogData = {
902
+ title: this.tx('Discard Changes'),
903
+ message: this.tx('You have unsaved changes. Are you sure you want to discard them?'),
904
+ confirmText: this.tx('Discard'),
905
+ cancelText: this.tx('Continue Editing'),
906
+ type: 'warning',
907
+ icon: 'warning',
908
+ };
909
+ return this.dialog
910
+ .open(ConfirmDialogComponent, { data: dialogData, autoFocus: false })
911
+ .afterClosed();
912
+ }
866
913
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
867
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)", "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
914
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)", "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
868
915
  }
869
916
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, decorators: [{
870
917
  type: Component,
@@ -878,7 +925,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
878
925
  CdkTrapFocus,
879
926
  MatProgressSpinnerModule,
880
927
  MatDialogModule,
881
- ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
928
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
882
929
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MatDialog }], propDecorators: { contentHost: [{
883
930
  type: ViewChild,
884
931
  args: ['contentHost', { read: ViewContainerRef, static: true }]
@@ -931,11 +978,13 @@ class SettingsPanelRef {
931
978
  bindSizePersistor(fn) {
932
979
  this.sizePersistor = fn;
933
980
  }
934
- updateSize(width) {
981
+ updateSize(width, options) {
935
982
  this.overlayRef.updateSize({ width });
936
983
  const serialized = typeof width === 'number' ? `${width}px` : width;
937
984
  if (typeof serialized === 'string') {
938
- this.sizePersistor?.(serialized);
985
+ if (options?.persist !== false) {
986
+ this.sizePersistor?.(serialized);
987
+ }
939
988
  this.sizeChangedSubject.next({ width: serialized });
940
989
  }
941
990
  }
@@ -955,10 +1004,74 @@ class SettingsPanelRef {
955
1004
  this.sizeChangedSubject.complete();
956
1005
  }
957
1006
  }
1007
+ class DeferredSettingsPanelRef {
1008
+ appliedSubject = new Subject();
1009
+ savedSubject = new Subject();
1010
+ resetSubject = new Subject();
1011
+ closedSubject = new ReplaySubject(1);
1012
+ sizeChangedSubject = new Subject();
1013
+ target;
1014
+ completed = false;
1015
+ applied$ = this.appliedSubject.asObservable();
1016
+ saved$ = this.savedSubject.asObservable();
1017
+ reset$ = this.resetSubject.asObservable();
1018
+ closed$ = this.closedSubject.asObservable();
1019
+ sizeChanged$ = this.sizeChangedSubject.asObservable();
1020
+ resolve(target) {
1021
+ if (this.completed) {
1022
+ return;
1023
+ }
1024
+ this.target = target;
1025
+ target.applied$.subscribe((value) => this.appliedSubject.next(value));
1026
+ target.saved$.subscribe((value) => this.savedSubject.next(value));
1027
+ target.reset$.subscribe(() => this.resetSubject.next());
1028
+ target.sizeChanged$.subscribe((value) => this.sizeChangedSubject.next(value));
1029
+ target.closed$.subscribe((reason) => {
1030
+ this.closedSubject.next(reason);
1031
+ this.complete();
1032
+ });
1033
+ }
1034
+ apply(value) {
1035
+ this.target?.apply(value);
1036
+ }
1037
+ save(value) {
1038
+ this.target?.save(value);
1039
+ }
1040
+ reset() {
1041
+ this.target?.reset();
1042
+ }
1043
+ bindSizePersistor(_fn) { }
1044
+ updateSize(width, options) {
1045
+ this.target?.updateSize(width, options);
1046
+ }
1047
+ close(reason = 'cancel') {
1048
+ if (this.target) {
1049
+ this.target.close(reason);
1050
+ return;
1051
+ }
1052
+ if (this.completed) {
1053
+ return;
1054
+ }
1055
+ this.closedSubject.next(reason);
1056
+ this.complete();
1057
+ }
1058
+ complete() {
1059
+ if (this.completed) {
1060
+ return;
1061
+ }
1062
+ this.completed = true;
1063
+ this.appliedSubject.complete();
1064
+ this.savedSubject.complete();
1065
+ this.resetSubject.complete();
1066
+ this.closedSubject.complete();
1067
+ this.sizeChangedSubject.complete();
1068
+ }
1069
+ }
958
1070
 
959
1071
  const SETTINGS_PANEL_DATA = SETTINGS_PANEL_DATA$1;
960
1072
  const SETTINGS_PANEL_REF = new InjectionToken('SETTINGS_PANEL_REF');
961
1073
 
1074
+ const PRAXIS_BASE_SIDE_PANEL_I18N_NAMESPACE = 'praxis-side-panel';
962
1075
  class BaseSidePanelComponent {
963
1076
  cdr;
964
1077
  i18n;
@@ -1012,10 +1125,10 @@ class BaseSidePanelComponent {
1012
1125
  };
1013
1126
  }
1014
1127
  get closeLabel() {
1015
- return this.i18n.t('Close', undefined, 'Close', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1128
+ return this.i18n.t('Close', undefined, 'Close', PRAXIS_BASE_SIDE_PANEL_I18N_NAMESPACE);
1016
1129
  }
1017
1130
  get resizeHandleLabel() {
1018
- return this.i18n.t('Resize panel', undefined, 'Resize panel', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1131
+ return this.i18n.t('Resize panel', undefined, 'Resize panel', PRAXIS_BASE_SIDE_PANEL_I18N_NAMESPACE);
1019
1132
  }
1020
1133
  get hasHeader() {
1021
1134
  return !!(this.title || this.titleIcon || this.subtitle);
@@ -1453,6 +1566,7 @@ class SettingsPanelService {
1453
1566
  baseSidePanelService;
1454
1567
  injector;
1455
1568
  currentRef;
1569
+ currentPanelInstance;
1456
1570
  i18n = inject(PraxisI18nService);
1457
1571
  layerScale = inject(PraxisLayerScaleStyleService);
1458
1572
  constructor(baseSidePanelService, injector) {
@@ -1465,6 +1579,26 @@ class SettingsPanelService {
1465
1579
  * same overlay when the provided id matches.
1466
1580
  */
1467
1581
  open(config) {
1582
+ if (this.currentRef && this.currentPanelInstance) {
1583
+ const deferredRef = new DeferredSettingsPanelRef();
1584
+ const currentRef = this.currentRef;
1585
+ const currentPanelInstance = this.currentPanelInstance;
1586
+ currentPanelInstance
1587
+ .requestClose('cancel')
1588
+ .pipe(take(1))
1589
+ .subscribe((allowed) => {
1590
+ if (!allowed) {
1591
+ deferredRef.close('cancel');
1592
+ return;
1593
+ }
1594
+ currentRef.close('cancel');
1595
+ deferredRef.resolve(this.openFresh(config));
1596
+ });
1597
+ return deferredRef;
1598
+ }
1599
+ return this.openFresh(config);
1600
+ }
1601
+ openFresh(config) {
1468
1602
  this.layerScale.ensureInstalled();
1469
1603
  const ref = this.baseSidePanelService.openHost({
1470
1604
  component: SettingsPanelComponent,
@@ -1494,13 +1628,15 @@ class SettingsPanelService {
1494
1628
  ? config.title
1495
1629
  : this.i18n.t(PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, undefined, PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1496
1630
  panelRef.instance.titleIcon = config.titleIcon;
1497
- panelRef.instance.width =
1498
- resolvedConfig.width ?? '720px';
1499
- panelRef.instance.minWidth = resolvedConfig.minWidth;
1500
- panelRef.instance.maxWidth = resolvedConfig.maxWidth;
1501
1631
  panelRef.instance.resizable = resolvedConfig.resizable ?? true;
1502
1632
  panelRef.instance.persistSizeKey = resolvedConfig.persistSizeKey;
1503
- panelRef.instance.expanded = config.expanded || false;
1633
+ panelRef.instance.initializeLayout({
1634
+ width: resolvedConfig.width ?? '720px',
1635
+ minWidth: resolvedConfig.minWidth,
1636
+ maxWidth: resolvedConfig.maxWidth,
1637
+ expanded: config.expanded || false,
1638
+ });
1639
+ this.currentPanelInstance = panelRef.instance;
1504
1640
  const inputs = config.content.inputs;
1505
1641
  const injector = Injector.create({
1506
1642
  providers: [
@@ -1519,6 +1655,7 @@ class SettingsPanelService {
1519
1655
  ref.closed$.subscribe(() => {
1520
1656
  if (this.currentRef === ref) {
1521
1657
  this.currentRef = undefined;
1658
+ this.currentPanelInstance = undefined;
1522
1659
  }
1523
1660
  });
1524
1661
  this.currentRef = ref;
@@ -1527,6 +1664,7 @@ class SettingsPanelService {
1527
1664
  close(reason = 'cancel') {
1528
1665
  this.baseSidePanelService.closeScope('settings-panel', reason);
1529
1666
  this.currentRef = undefined;
1667
+ this.currentPanelInstance = undefined;
1530
1668
  }
1531
1669
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, deps: [{ token: BaseSidePanelService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1532
1670
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, providedIn: 'root' });
@@ -4310,4 +4448,4 @@ const SETTINGS_PANEL_AI_CAPABILITIES = {
4310
4448
  * Generated bundle index. Do not edit.
4311
4449
  */
4312
4450
 
4313
- export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };
4451
+ export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, DeferredSettingsPanelRef, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };
package/index.d.ts CHANGED
@@ -131,7 +131,35 @@ declare class SettingsPanelRef {
131
131
  save(value: any): void;
132
132
  reset(): void;
133
133
  bindSizePersistor(fn: (width: string) => void): void;
134
- updateSize(width: string | number): void;
134
+ updateSize(width: string | number, options?: {
135
+ persist?: boolean;
136
+ }): void;
137
+ close(reason?: SettingsPanelCloseReason): void;
138
+ private complete;
139
+ }
140
+ declare class DeferredSettingsPanelRef {
141
+ private readonly appliedSubject;
142
+ private readonly savedSubject;
143
+ private readonly resetSubject;
144
+ private readonly closedSubject;
145
+ private readonly sizeChangedSubject;
146
+ private target?;
147
+ private completed;
148
+ applied$: rxjs.Observable<any>;
149
+ saved$: rxjs.Observable<any>;
150
+ reset$: rxjs.Observable<void>;
151
+ closed$: rxjs.Observable<SettingsPanelCloseReason>;
152
+ sizeChanged$: rxjs.Observable<{
153
+ width: string;
154
+ }>;
155
+ resolve(target: SettingsPanelRef): void;
156
+ apply(value: any): void;
157
+ save(value: any): void;
158
+ reset(): void;
159
+ bindSizePersistor(_fn: (width: string) => void): void;
160
+ updateSize(width: string | number, options?: {
161
+ persist?: boolean;
162
+ }): void;
135
163
  close(reason?: SettingsPanelCloseReason): void;
136
164
  private complete;
137
165
  }
@@ -292,6 +320,7 @@ declare class SettingsPanelService {
292
320
  private readonly baseSidePanelService;
293
321
  private injector;
294
322
  private currentRef?;
323
+ private currentPanelInstance?;
295
324
  private readonly i18n;
296
325
  private readonly layerScale;
297
326
  constructor(baseSidePanelService: BaseSidePanelService, injector: Injector);
@@ -301,6 +330,7 @@ declare class SettingsPanelService {
301
330
  * same overlay when the provided id matches.
302
331
  */
303
332
  open(config: SettingsPanelConfig): SettingsPanelRef;
333
+ private openFresh;
304
334
  close(reason?: SettingsPanelCloseReason): void;
305
335
  static ɵfac: i0.ɵɵFactoryDeclaration<SettingsPanelService, never>;
306
336
  static ɵprov: i0.ɵɵInjectableDeclaration<SettingsPanelService>;
@@ -338,6 +368,12 @@ declare class SurfaceDrawerComponent {
338
368
  static ɵcmp: i0.ɵɵComponentDeclaration<SurfaceDrawerComponent, "praxis-surface-drawer", never, {}, {}, never, never, true, never>;
339
369
  }
340
370
 
371
+ interface SettingsPanelLayoutState {
372
+ width: string;
373
+ minWidth?: string;
374
+ maxWidth?: string;
375
+ expanded?: boolean;
376
+ }
341
377
  declare class SettingsPanelComponent {
342
378
  private cdr;
343
379
  private dialog;
@@ -382,10 +418,12 @@ declare class SettingsPanelComponent {
382
418
  onSave(): void;
383
419
  private emitSave;
384
420
  private formatClock;
421
+ initializeLayout(state: SettingsPanelLayoutState): void;
385
422
  toggleExpand(): void;
386
423
  onResizeHandlePointerDown(event: PointerEvent): void;
387
424
  onResizeHandleKeydown(event: KeyboardEvent): void;
388
425
  onCancel(): void;
426
+ requestClose(reason: SettingsPanelCloseReason): Observable<boolean>;
389
427
  handleKeydown(event: KeyboardEvent): void;
390
428
  onDocumentPointerMove(event: PointerEvent): void;
391
429
  onDocumentPointerEnd(event: PointerEvent): void;
@@ -394,6 +432,8 @@ declare class SettingsPanelComponent {
394
432
  private clampWidthPx;
395
433
  private computeExpandedWidth;
396
434
  private parseWidthPx;
435
+ private runBeforeCloseHook;
436
+ private confirmDiscardIfNeeded;
397
437
  static ɵfac: i0.ɵɵFactoryDeclaration<SettingsPanelComponent, never>;
398
438
  static ɵcmp: i0.ɵɵComponentDeclaration<SettingsPanelComponent, "praxis-settings-panel", never, {}, {}, never, never, true, never>;
399
439
  }
@@ -672,5 +712,5 @@ interface CapabilityCatalog extends AiCapabilityCatalog {
672
712
  }
673
713
  declare const SETTINGS_PANEL_AI_CAPABILITIES: CapabilityCatalog;
674
714
 
675
- export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };
715
+ export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, DeferredSettingsPanelRef, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };
676
716
  export type { BaseSidePanelCloseReason, BaseSidePanelContent, BaseSidePanelOpenOptions, BaseSidePanelRef, BaseSidePanelResizeAxis, BaseSidePanelResult, Capability, CapabilityCatalog, CapabilityCategory, GlobalConfigEditorFieldSpec, GlobalConfigEditorGroup, GlobalConfigEditorOption, GlobalConfigEditorState, OpenGlobalConfigOptions, SettingsPanelAction, SettingsPanelCloseReason, SettingsPanelConfig, SettingsValueProvider, SidePanelCssVarName, SidePanelMotionContract, SidePanelThemeContract, SidePanelWidthPreset, ValueKind };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@praxisui/settings-panel",
3
- "version": "4.0.0-beta.0",
3
+ "version": "5.0.0-beta.0",
4
4
  "description": "Settings panel for Praxis UI libraries: open editors for configuration at runtime and persist settings.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^20.0.0",
7
7
  "@angular/core": "^20.0.0",
8
8
  "@angular/cdk": "^20.0.0",
9
9
  "@angular/material": "^20.0.0",
10
- "@praxisui/ai": "^4.0.0-beta.0",
11
- "@praxisui/core": "^4.0.0-beta.0",
12
- "@praxisui/dynamic-fields": "^4.0.0-beta.0"
10
+ "@praxisui/ai": "^5.0.0-beta.0",
11
+ "@praxisui/core": "^5.0.0-beta.0",
12
+ "@praxisui/dynamic-fields": "^5.0.0-beta.0"
13
13
  },
14
14
  "dependencies": {
15
15
  "tslib": "^2.3.0"