@praxisui/settings-panel 9.0.4-rc.26 → 9.0.4-rc.28
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
|
@@ -90,12 +90,19 @@ Peer dependencies (Angular v21):
|
|
|
90
90
|
- `title`, `titleIcon` e `subtitle` formam o cabecalho governado do drawer; o shell resolve espacamento, quebra responsiva e relaciona o subtitulo por `aria-describedby`, sem exigir CSS local do host.
|
|
91
91
|
- O runtime drawer aceita `width`, `minWidth`, `maxWidth`, `resizable` e `persistSizeKey`; o handle visual so aparece quando `resizable=true`.
|
|
92
92
|
- Quando `persistSizeKey` estiver presente, a largura volta no proximo open do mesmo painel.
|
|
93
|
+
- Uma sessao runtime pode manter uma unica surface primaria e navegar entre etapas com `surfaceRuntime.push(frame)`, `replace(frame)` e `back()`.
|
|
94
|
+
- O shell preserva a instancia Angular, o scroll e o alvo de foco dos frames anteriores. O botao Voltar so aparece quando existe historico e devolve o foco para a etapa restaurada.
|
|
95
|
+
- `surfaceRuntime.navigation$` publica `sessionId`, `activeFrameId`, profundidade e `canGoBack`; esse estado e efemero e nunca deve ser persistido em metadata, `x-ui` ou configuracao de componente.
|
|
96
|
+
- Formularios podem registrar `surfaceRuntime.setCanLeave(guard)` para impedir `back`, `replace`, Escape, backdrop ou fechamento enquanto o lifecycle da etapa exigir confirmacao. O guard e runtime-only e nao substitui regras de negocio nem autorizacao.
|
|
97
|
+
- Componentes criados dentro de uma frame podem injetar `SURFACE_DRAWER_REF` e `SURFACE_DRAWER_CONTENT_DATA`. Esses tokens representam exclusivamente a frame ativa e seus inputs efemeros; nao devem ser usados como uma segunda fonte de metadata.
|
|
98
|
+
- O CRUD oficial reutiliza automaticamente a sessao ativa: formularios abertos em modo drawer viram uma nova frame, publicam `save/delete/close` no mesmo ref e retornam ao contexto anterior sem empilhar overlays.
|
|
93
99
|
|
|
94
100
|
## Fronteira canonica
|
|
95
101
|
|
|
96
102
|
- `SettingsPanelService` e `SETTINGS_PANEL_BRIDGE` pertencem a authoring/configuracao.
|
|
97
103
|
- `SURFACE_DRAWER_BRIDGE` pertence ao runtime de `surface.open`.
|
|
98
104
|
- Ambos compartilham a mesma infraestrutura base de side panel, mas com shells diferentes.
|
|
105
|
+
- Tipos de componente e callbacks de navegacao pertencem exclusivamente a sessao runtime; JSON governado continua descrevendo a intencao de abrir a surface, nao uma pilha imperativa serializada.
|
|
99
106
|
|
|
100
107
|
## Customizacao visual
|
|
101
108
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-08-
|
|
3
|
+
"generatedAt": "2026-08-05T00:44:25.177Z",
|
|
4
4
|
"packageName": "@praxisui/settings-panel",
|
|
5
|
-
"packageVersion": "9.0.4-rc.
|
|
5
|
+
"packageVersion": "9.0.4-rc.28",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 1,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, DestroyRef, ViewContainerRef, HostListener, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, InjectionToken, Injector, Injectable, ChangeDetectorRef, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
2
|
+
import { inject, DestroyRef, ViewContainerRef, HostListener, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, InjectionToken, Injector, Injectable, ElementRef, ChangeDetectorRef, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import * as i2 from '@angular/common';
|
|
4
4
|
import { CommonModule, NgStyle, NgClass } from '@angular/common';
|
|
5
5
|
import { CdkTrapFocus } from '@angular/cdk/a11y';
|
|
@@ -17,7 +17,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
17
17
|
import { filter, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
|
18
18
|
import { isObservable, firstValueFrom, of, from, Subject, ReplaySubject, take, BehaviorSubject } from 'rxjs';
|
|
19
19
|
import * as i1$2 from '@praxisui/core';
|
|
20
|
-
import { providePraxisI18n, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisLayerScaleStyleService, SETTINGS_PANEL_BRIDGE, SURFACE_DRAWER_BRIDGE, GlobalConfigService, FieldControlType, IconPickerService, LoggerService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
20
|
+
import { providePraxisI18n, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisLayerScaleStyleService, SETTINGS_PANEL_BRIDGE, SURFACE_DRAWER_BRIDGE, SURFACE_DRAWER_CONTENT_DATA, SURFACE_DRAWER_REF, GlobalConfigService, FieldControlType, IconPickerService, LoggerService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
21
21
|
import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
|
|
22
22
|
import * as i1$1 from '@angular/cdk/overlay';
|
|
23
23
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
@@ -438,6 +438,7 @@ const PRAXIS_SETTINGS_PANEL_EN_US = {
|
|
|
438
438
|
'Collapse panel': 'Collapse panel',
|
|
439
439
|
'Expand panel': 'Expand panel',
|
|
440
440
|
Close: 'Close',
|
|
441
|
+
Back: 'Back',
|
|
441
442
|
Apply: 'Apply',
|
|
442
443
|
'Save & Close': 'Save & Close',
|
|
443
444
|
};
|
|
@@ -460,6 +461,7 @@ const PRAXIS_SETTINGS_PANEL_PT_BR = {
|
|
|
460
461
|
'Collapse panel': 'Reduzir painel',
|
|
461
462
|
'Expand panel': 'Expandir painel',
|
|
462
463
|
Close: 'Fechar',
|
|
464
|
+
Back: 'Voltar',
|
|
463
465
|
Apply: 'Aplicar',
|
|
464
466
|
'Save & Close': 'Salvar & Fechar',
|
|
465
467
|
};
|
|
@@ -1811,11 +1813,15 @@ class SurfaceDrawerComponent {
|
|
|
1811
1813
|
useHostWidth = false;
|
|
1812
1814
|
theme = null;
|
|
1813
1815
|
onClose;
|
|
1816
|
+
onBack;
|
|
1814
1817
|
static nextId = 0;
|
|
1815
1818
|
titleId = `praxis-surface-drawer-title-${SurfaceDrawerComponent.nextId++}`;
|
|
1816
1819
|
subtitleId = `${this.titleId}-description`;
|
|
1817
1820
|
contentRef;
|
|
1818
1821
|
contentHost;
|
|
1822
|
+
body;
|
|
1823
|
+
backButton;
|
|
1824
|
+
frames = [];
|
|
1819
1825
|
constructor(cdr, i18n) {
|
|
1820
1826
|
this.cdr = cdr;
|
|
1821
1827
|
this.i18n = i18n;
|
|
@@ -1832,6 +1838,21 @@ class SurfaceDrawerComponent {
|
|
|
1832
1838
|
: 'Fechar';
|
|
1833
1839
|
return this.i18n.t('Close', undefined, fallback, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
|
|
1834
1840
|
}
|
|
1841
|
+
get backLabel() {
|
|
1842
|
+
const fallback = this.i18n.getLocale().toLowerCase().startsWith('en')
|
|
1843
|
+
? 'Back'
|
|
1844
|
+
: 'Voltar';
|
|
1845
|
+
return this.i18n.t('Back', undefined, fallback, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
|
|
1846
|
+
}
|
|
1847
|
+
get canGoBack() {
|
|
1848
|
+
return this.frames.length > 1;
|
|
1849
|
+
}
|
|
1850
|
+
get navigationDepth() {
|
|
1851
|
+
return this.frames.length;
|
|
1852
|
+
}
|
|
1853
|
+
get activeFrameId() {
|
|
1854
|
+
return this.frames[this.frames.length - 1]?.definition.id ?? '';
|
|
1855
|
+
}
|
|
1835
1856
|
get hasHeader() {
|
|
1836
1857
|
return !!(this.title || this.titleIcon || this.subtitle);
|
|
1837
1858
|
}
|
|
@@ -1845,11 +1866,156 @@ class SurfaceDrawerComponent {
|
|
|
1845
1866
|
this.title = title;
|
|
1846
1867
|
this.cdr.markForCheck();
|
|
1847
1868
|
}
|
|
1869
|
+
ngOnDestroy() {
|
|
1870
|
+
for (const frame of this.frames) {
|
|
1871
|
+
if (!frame.componentRef.hostView.destroyed) {
|
|
1872
|
+
frame.componentRef.destroy();
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
this.frames = [];
|
|
1876
|
+
}
|
|
1848
1877
|
attachContent(component, injector, inputs) {
|
|
1849
|
-
|
|
1850
|
-
|
|
1878
|
+
return this.initializeFrame({
|
|
1879
|
+
id: 'root',
|
|
1880
|
+
title: this.title,
|
|
1881
|
+
titleIcon: this.titleIcon,
|
|
1882
|
+
subtitle: this.subtitle,
|
|
1883
|
+
content: { component, inputs },
|
|
1884
|
+
}, injector, inputs);
|
|
1885
|
+
}
|
|
1886
|
+
initializeFrame(definition, injector, inputs) {
|
|
1887
|
+
this.destroyAllFrames();
|
|
1888
|
+
const frame = this.createFrame(definition, injector, inputs);
|
|
1889
|
+
this.frames = [frame];
|
|
1890
|
+
this.applyFrameHeader(frame);
|
|
1891
|
+
return frame.componentRef;
|
|
1892
|
+
}
|
|
1893
|
+
pushFrame(definition, injector, inputs) {
|
|
1894
|
+
this.captureActiveFrameState();
|
|
1895
|
+
this.detachActiveView();
|
|
1896
|
+
const frame = this.createFrame(definition, injector, inputs);
|
|
1897
|
+
this.frames.push(frame);
|
|
1898
|
+
this.applyFrameHeader(frame);
|
|
1899
|
+
this.focusNavigationControl();
|
|
1900
|
+
return frame.componentRef;
|
|
1901
|
+
}
|
|
1902
|
+
async replaceFrame(definition, injector, inputs) {
|
|
1903
|
+
if (!(await this.canLeaveActiveFrame())) {
|
|
1904
|
+
return null;
|
|
1905
|
+
}
|
|
1906
|
+
this.removeActiveFrame();
|
|
1907
|
+
const frame = this.createFrame(definition, injector, inputs);
|
|
1908
|
+
this.frames.push(frame);
|
|
1909
|
+
this.applyFrameHeader(frame);
|
|
1910
|
+
this.focusNavigationControl();
|
|
1911
|
+
return frame.componentRef;
|
|
1912
|
+
}
|
|
1913
|
+
async backFrame() {
|
|
1914
|
+
if (!this.canGoBack || !(await this.canLeaveActiveFrame())) {
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
this.removeActiveFrame();
|
|
1918
|
+
const previous = this.frames[this.frames.length - 1];
|
|
1919
|
+
if (!previous) {
|
|
1920
|
+
return false;
|
|
1921
|
+
}
|
|
1922
|
+
this.contentHost.insert(previous.componentRef.hostView);
|
|
1923
|
+
this.contentRef = previous.componentRef;
|
|
1924
|
+
this.applyFrameHeader(previous);
|
|
1925
|
+
queueMicrotask(() => {
|
|
1926
|
+
if (this.body?.nativeElement) {
|
|
1927
|
+
this.body.nativeElement.scrollTop = previous.scrollTop;
|
|
1928
|
+
}
|
|
1929
|
+
const target = previous.restoreFocusTo;
|
|
1930
|
+
if (target?.isConnected) {
|
|
1931
|
+
target.focus();
|
|
1932
|
+
}
|
|
1933
|
+
else {
|
|
1934
|
+
this.backButton?.nativeElement.focus();
|
|
1935
|
+
}
|
|
1936
|
+
});
|
|
1937
|
+
return true;
|
|
1938
|
+
}
|
|
1939
|
+
setActiveCanLeave(guard) {
|
|
1940
|
+
const active = this.frames[this.frames.length - 1];
|
|
1941
|
+
if (active) {
|
|
1942
|
+
active.canLeave = guard;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
async canCloseSession() {
|
|
1946
|
+
for (let index = this.frames.length - 1; index >= 0; index -= 1) {
|
|
1947
|
+
const guard = this.frames[index].canLeave;
|
|
1948
|
+
if (guard && !(await Promise.resolve(guard()))) {
|
|
1949
|
+
return false;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
return true;
|
|
1953
|
+
}
|
|
1954
|
+
createFrame(definition, injector, inputs) {
|
|
1955
|
+
const componentRef = this.contentHost.createComponent(definition.content.component, { injector });
|
|
1956
|
+
this.applyInputs(componentRef, inputs);
|
|
1957
|
+
this.contentRef = componentRef;
|
|
1851
1958
|
this.cdr.markForCheck();
|
|
1852
|
-
return
|
|
1959
|
+
return {
|
|
1960
|
+
definition,
|
|
1961
|
+
componentRef,
|
|
1962
|
+
scrollTop: 0,
|
|
1963
|
+
restoreFocusTo: null,
|
|
1964
|
+
canLeave: definition.canLeave ?? null,
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
captureActiveFrameState() {
|
|
1968
|
+
const active = this.frames[this.frames.length - 1];
|
|
1969
|
+
if (!active)
|
|
1970
|
+
return;
|
|
1971
|
+
active.scrollTop = this.body?.nativeElement.scrollTop ?? 0;
|
|
1972
|
+
const focused = typeof document !== 'undefined'
|
|
1973
|
+
? document.activeElement
|
|
1974
|
+
: null;
|
|
1975
|
+
active.restoreFocusTo = focused instanceof HTMLElement ? focused : null;
|
|
1976
|
+
}
|
|
1977
|
+
detachActiveView() {
|
|
1978
|
+
const active = this.frames[this.frames.length - 1];
|
|
1979
|
+
if (!active)
|
|
1980
|
+
return;
|
|
1981
|
+
const index = this.contentHost.indexOf(active.componentRef.hostView);
|
|
1982
|
+
if (index >= 0) {
|
|
1983
|
+
this.contentHost.detach(index);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
removeActiveFrame() {
|
|
1987
|
+
const active = this.frames.pop();
|
|
1988
|
+
if (!active)
|
|
1989
|
+
return;
|
|
1990
|
+
const index = this.contentHost.indexOf(active.componentRef.hostView);
|
|
1991
|
+
if (index >= 0) {
|
|
1992
|
+
this.contentHost.remove(index);
|
|
1993
|
+
}
|
|
1994
|
+
else if (!active.componentRef.hostView.destroyed) {
|
|
1995
|
+
active.componentRef.destroy();
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
destroyAllFrames() {
|
|
1999
|
+
this.contentHost.clear();
|
|
2000
|
+
for (const frame of this.frames) {
|
|
2001
|
+
if (!frame.componentRef.hostView.destroyed) {
|
|
2002
|
+
frame.componentRef.destroy();
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
this.frames = [];
|
|
2006
|
+
}
|
|
2007
|
+
async canLeaveActiveFrame() {
|
|
2008
|
+
const guard = this.frames[this.frames.length - 1]?.canLeave;
|
|
2009
|
+
return guard ? Promise.resolve(guard()) : true;
|
|
2010
|
+
}
|
|
2011
|
+
applyFrameHeader(frame) {
|
|
2012
|
+
this.title = frame.definition.title;
|
|
2013
|
+
this.titleIcon = frame.definition.titleIcon;
|
|
2014
|
+
this.subtitle = frame.definition.subtitle;
|
|
2015
|
+
this.cdr.markForCheck();
|
|
2016
|
+
}
|
|
2017
|
+
focusNavigationControl() {
|
|
2018
|
+
queueMicrotask(() => this.backButton?.nativeElement.focus());
|
|
1853
2019
|
}
|
|
1854
2020
|
applyInputs(componentRef, inputs) {
|
|
1855
2021
|
if (!inputs) {
|
|
@@ -1882,7 +2048,7 @@ class SurfaceDrawerComponent {
|
|
|
1882
2048
|
return new Set(Object.keys(declaredInputs));
|
|
1883
2049
|
}
|
|
1884
2050
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1885
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: SurfaceDrawerComponent, isStandalone: true, selector: "praxis-surface-drawer", viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n [attr.aria-describedby]=\"subtitle ? subtitleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\" [id]=\"subtitleId\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n praxisIconButton=\"close\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n ></button>\n </header>\n }\n\n <section class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;max-width:100%;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:flex-start;gap:var(--pfx-side-panel-header-gap, 12px);min-height:var(--pfx-side-panel-header-height, 64px);padding:var(--pfx-side-panel-header-padding-y, 16px) var(--pfx-side-panel-header-padding-x, 20px);box-sizing:border-box;border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:12px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{flex:0 0 auto;display:grid;place-items:center;width:36px;height:36px;margin-top:1px;border-radius:10px;background:var(--md-sys-color-primary-container, rgba(47, 78, 178, .12));color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1.125rem;font-weight:650;line-height:1.35}.pfx-surface-drawer__subtitle{margin-top:6px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem;line-height:1.45;text-wrap:pretty}.pfx-surface-drawer__close{flex:0 0 auto}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4$1.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: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2051
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: SurfaceDrawerComponent, isStandalone: true, selector: "praxis-surface-drawer", viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "body", first: true, predicate: ["body"], descendants: true, read: ElementRef }, { propertyName: "backButton", first: true, predicate: ["backButton"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n [attr.aria-describedby]=\"subtitle ? subtitleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n @if (canGoBack) {\n <button\n #backButton\n praxisIconButton=\"arrow_back\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__back\"\n [attr.aria-label]=\"backLabel\"\n [matTooltip]=\"backLabel\"\n (click)=\"onBack?.()\"\n ></button>\n }\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\" [id]=\"subtitleId\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n praxisIconButton=\"close\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n ></button>\n </header>\n }\n\n <section #body class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;max-width:100%;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:flex-start;gap:var(--pfx-side-panel-header-gap, 12px);min-height:var(--pfx-side-panel-header-height, 64px);padding:var(--pfx-side-panel-header-padding-y, 16px) var(--pfx-side-panel-header-padding-x, 20px);box-sizing:border-box;border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:12px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{flex:0 0 auto;display:grid;place-items:center;width:36px;height:36px;margin-top:1px;border-radius:10px;background:var(--md-sys-color-primary-container, rgba(47, 78, 178, .12));color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1.125rem;font-weight:650;line-height:1.35}.pfx-surface-drawer__subtitle{margin-top:6px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem;line-height:1.45;text-wrap:pretty}.pfx-surface-drawer__close{flex:0 0 auto}.pfx-surface-drawer__back{flex:0 0 auto;margin-top:1px}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4$1.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: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1886
2052
|
}
|
|
1887
2053
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
|
|
1888
2054
|
type: Component,
|
|
@@ -1895,10 +2061,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
1895
2061
|
CdkTrapFocus,
|
|
1896
2062
|
PraxisIconButtonComponent,
|
|
1897
2063
|
PraxisIconDirective,
|
|
1898
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n [attr.aria-describedby]=\"subtitle ? subtitleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\" [id]=\"subtitleId\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n praxisIconButton=\"close\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n ></button>\n </header>\n }\n\n <section class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;max-width:100%;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:flex-start;gap:var(--pfx-side-panel-header-gap, 12px);min-height:var(--pfx-side-panel-header-height, 64px);padding:var(--pfx-side-panel-header-padding-y, 16px) var(--pfx-side-panel-header-padding-x, 20px);box-sizing:border-box;border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:12px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{flex:0 0 auto;display:grid;place-items:center;width:36px;height:36px;margin-top:1px;border-radius:10px;background:var(--md-sys-color-primary-container, rgba(47, 78, 178, .12));color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1.125rem;font-weight:650;line-height:1.35}.pfx-surface-drawer__subtitle{margin-top:6px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem;line-height:1.45;text-wrap:pretty}.pfx-surface-drawer__close{flex:0 0 auto}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"] }]
|
|
2064
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n [attr.aria-describedby]=\"subtitle ? subtitleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n @if (canGoBack) {\n <button\n #backButton\n praxisIconButton=\"arrow_back\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__back\"\n [attr.aria-label]=\"backLabel\"\n [matTooltip]=\"backLabel\"\n (click)=\"onBack?.()\"\n ></button>\n }\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\" [id]=\"subtitleId\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n praxisIconButton=\"close\"\n size=\"compact\"\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n ></button>\n </header>\n }\n\n <section #body class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;max-width:100%;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:flex-start;gap:var(--pfx-side-panel-header-gap, 12px);min-height:var(--pfx-side-panel-header-height, 64px);padding:var(--pfx-side-panel-header-padding-y, 16px) var(--pfx-side-panel-header-padding-x, 20px);box-sizing:border-box;border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:12px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{flex:0 0 auto;display:grid;place-items:center;width:36px;height:36px;margin-top:1px;border-radius:10px;background:var(--md-sys-color-primary-container, rgba(47, 78, 178, .12));color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1.125rem;font-weight:650;line-height:1.35}.pfx-surface-drawer__subtitle{margin-top:6px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem;line-height:1.45;text-wrap:pretty}.pfx-surface-drawer__close{flex:0 0 auto}.pfx-surface-drawer__back{flex:0 0 auto;margin-top:1px}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"] }]
|
|
1899
2065
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.PraxisI18nService }], propDecorators: { contentHost: [{
|
|
1900
2066
|
type: ViewChild,
|
|
1901
2067
|
args: ['contentHost', { read: ViewContainerRef, static: true }]
|
|
2068
|
+
}], body: [{
|
|
2069
|
+
type: ViewChild,
|
|
2070
|
+
args: ['body', { read: ElementRef }]
|
|
2071
|
+
}], backButton: [{
|
|
2072
|
+
type: ViewChild,
|
|
2073
|
+
args: ['backButton', { read: ElementRef }]
|
|
1902
2074
|
}] } });
|
|
1903
2075
|
|
|
1904
2076
|
function buildSurfaceRuntime(ref) {
|
|
@@ -1910,6 +2082,12 @@ function buildSurfaceRuntime(ref) {
|
|
|
1910
2082
|
complete: (outcome) => ref.complete?.(outcome),
|
|
1911
2083
|
updateTitle: (title) => ref.updateTitle?.(title),
|
|
1912
2084
|
updateSize: (preset) => ref.updateSize?.(preset),
|
|
2085
|
+
navigation$: ref.navigation$,
|
|
2086
|
+
push: (frame) => ref.push?.(frame),
|
|
2087
|
+
replace: (frame) => ref.replace?.(frame),
|
|
2088
|
+
back: () => ref.back?.(),
|
|
2089
|
+
canGoBack: () => ref.canGoBack?.() ?? false,
|
|
2090
|
+
setCanLeave: (guard) => ref.setCanLeave?.(guard),
|
|
1913
2091
|
};
|
|
1914
2092
|
}
|
|
1915
2093
|
function enrichSurfaceContentInputs(inputs, ref) {
|
|
@@ -1955,7 +2133,159 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
1955
2133
|
useFactory: (service, injector, layerScale) => ({
|
|
1956
2134
|
open: (opts) => {
|
|
1957
2135
|
layerScale.ensureInstalled();
|
|
1958
|
-
|
|
2136
|
+
const sessionId = `${opts.id}:${Date.now().toString(36)}`;
|
|
2137
|
+
const navigationState = new BehaviorSubject({
|
|
2138
|
+
sessionId,
|
|
2139
|
+
activeFrameId: opts.id,
|
|
2140
|
+
depth: 1,
|
|
2141
|
+
canGoBack: false,
|
|
2142
|
+
});
|
|
2143
|
+
let panel = null;
|
|
2144
|
+
let activeRef = null;
|
|
2145
|
+
const frameRefs = [];
|
|
2146
|
+
const createContent = (frame, mode, runtimeRef) => {
|
|
2147
|
+
if (!panel)
|
|
2148
|
+
return null;
|
|
2149
|
+
const contentInputs = enrichSurfaceContentInputs(frame.content.inputs, runtimeRef);
|
|
2150
|
+
const contentInjector = Injector.create({
|
|
2151
|
+
providers: [
|
|
2152
|
+
{ provide: BASE_SIDE_PANEL_DATA, useValue: contentInputs },
|
|
2153
|
+
{ provide: BASE_SIDE_PANEL_REF, useValue: runtimeRef },
|
|
2154
|
+
{ provide: SURFACE_DRAWER_CONTENT_DATA, useValue: contentInputs },
|
|
2155
|
+
{ provide: SURFACE_DRAWER_REF, useValue: runtimeRef },
|
|
2156
|
+
],
|
|
2157
|
+
parent: injector,
|
|
2158
|
+
});
|
|
2159
|
+
if (mode === 'initial') {
|
|
2160
|
+
return panel.initializeFrame(frame, contentInjector, contentInputs);
|
|
2161
|
+
}
|
|
2162
|
+
if (mode === 'push') {
|
|
2163
|
+
return panel.pushFrame(frame, contentInjector, contentInputs);
|
|
2164
|
+
}
|
|
2165
|
+
return panel.replaceFrame(frame, contentInjector, contentInputs);
|
|
2166
|
+
};
|
|
2167
|
+
const publishNavigationState = () => {
|
|
2168
|
+
if (!panel)
|
|
2169
|
+
return;
|
|
2170
|
+
navigationState.next({
|
|
2171
|
+
sessionId,
|
|
2172
|
+
activeFrameId: panel.activeFrameId,
|
|
2173
|
+
depth: panel.navigationDepth,
|
|
2174
|
+
canGoBack: panel.canGoBack,
|
|
2175
|
+
});
|
|
2176
|
+
};
|
|
2177
|
+
const closeFrameRef = (frameRef, result) => {
|
|
2178
|
+
const runtime = frameRef;
|
|
2179
|
+
runtime.__closeFrame?.(result);
|
|
2180
|
+
};
|
|
2181
|
+
const navigateBack = async (result) => {
|
|
2182
|
+
const leaving = frameRefs[frameRefs.length - 1];
|
|
2183
|
+
const moved = await panel?.backFrame() ?? false;
|
|
2184
|
+
if (!moved)
|
|
2185
|
+
return false;
|
|
2186
|
+
if (leaving) {
|
|
2187
|
+
frameRefs.pop();
|
|
2188
|
+
closeFrameRef(leaving, result ?? { type: 'back' });
|
|
2189
|
+
}
|
|
2190
|
+
publishNavigationState();
|
|
2191
|
+
return true;
|
|
2192
|
+
};
|
|
2193
|
+
const createFrameRef = (frameId) => {
|
|
2194
|
+
const closedSubject = new Subject();
|
|
2195
|
+
const resultSubject = new Subject();
|
|
2196
|
+
let closed = false;
|
|
2197
|
+
let cleanup = null;
|
|
2198
|
+
const frameRef = {
|
|
2199
|
+
frameId,
|
|
2200
|
+
closed$: closedSubject.asObservable(),
|
|
2201
|
+
result$: resultSubject.asObservable(),
|
|
2202
|
+
navigation$: navigationState.asObservable(),
|
|
2203
|
+
close: (result) => { void navigateBack(result); },
|
|
2204
|
+
emitResult: (result) => resultSubject.next(result),
|
|
2205
|
+
complete: (outcome) => {
|
|
2206
|
+
resultSubject.next(outcome);
|
|
2207
|
+
void navigateBack(outcome);
|
|
2208
|
+
},
|
|
2209
|
+
updateTitle: (title) => activeRef?.updateTitle?.(title),
|
|
2210
|
+
updateSize: (preset) => activeRef?.updateSize?.(preset),
|
|
2211
|
+
push: (frame) => activeRef?.push?.(frame) ?? Promise.resolve(null),
|
|
2212
|
+
replace: (frame) => activeRef?.replace?.(frame) ?? Promise.resolve(null),
|
|
2213
|
+
back: () => activeRef?.back?.() ?? Promise.resolve(false),
|
|
2214
|
+
canGoBack: () => activeRef?.canGoBack?.() ?? false,
|
|
2215
|
+
setCanLeave: (guard) => activeRef?.setCanLeave?.(guard),
|
|
2216
|
+
__closeFrame: (result) => {
|
|
2217
|
+
if (closed)
|
|
2218
|
+
return;
|
|
2219
|
+
closed = true;
|
|
2220
|
+
cleanup?.();
|
|
2221
|
+
cleanup = null;
|
|
2222
|
+
closedSubject.next(result);
|
|
2223
|
+
closedSubject.complete();
|
|
2224
|
+
resultSubject.complete();
|
|
2225
|
+
},
|
|
2226
|
+
__setCleanup: (nextCleanup) => {
|
|
2227
|
+
cleanup?.();
|
|
2228
|
+
cleanup = nextCleanup;
|
|
2229
|
+
},
|
|
2230
|
+
};
|
|
2231
|
+
return frameRef;
|
|
2232
|
+
};
|
|
2233
|
+
const configureRef = (ref) => {
|
|
2234
|
+
activeRef = ref;
|
|
2235
|
+
if (ref.navigation$) {
|
|
2236
|
+
return ref;
|
|
2237
|
+
}
|
|
2238
|
+
ref.navigation$ = navigationState.asObservable();
|
|
2239
|
+
ref.canGoBack = () => panel?.canGoBack ?? false;
|
|
2240
|
+
ref.setCanLeave = (guard) => panel?.setActiveCanLeave(guard);
|
|
2241
|
+
ref.push = async (frame) => {
|
|
2242
|
+
const frameRef = createFrameRef(frame.id);
|
|
2243
|
+
const contentRef = await createContent(frame, 'push', frameRef);
|
|
2244
|
+
if (!contentRef)
|
|
2245
|
+
return null;
|
|
2246
|
+
frameRefs.push(frameRef);
|
|
2247
|
+
frameRef.__setCleanup?.(bindSurfaceContentSelectionResults(contentRef.instance, frameRef));
|
|
2248
|
+
publishNavigationState();
|
|
2249
|
+
return frameRef;
|
|
2250
|
+
};
|
|
2251
|
+
ref.replace = async (frame) => {
|
|
2252
|
+
if ((panel?.navigationDepth ?? 1) === 1) {
|
|
2253
|
+
const rootRef = frameRefs[0]
|
|
2254
|
+
?? ref;
|
|
2255
|
+
const contentRef = await createContent(frame, 'replace', rootRef);
|
|
2256
|
+
if (!contentRef)
|
|
2257
|
+
return null;
|
|
2258
|
+
Object.defineProperty(rootRef, 'frameId', {
|
|
2259
|
+
configurable: true,
|
|
2260
|
+
value: frame.id,
|
|
2261
|
+
});
|
|
2262
|
+
bindSurfaceContentSelectionResults(contentRef.instance, rootRef);
|
|
2263
|
+
publishNavigationState();
|
|
2264
|
+
return rootRef;
|
|
2265
|
+
}
|
|
2266
|
+
const frameRef = createFrameRef(frame.id);
|
|
2267
|
+
const contentRef = await createContent(frame, 'replace', frameRef);
|
|
2268
|
+
if (!contentRef)
|
|
2269
|
+
return null;
|
|
2270
|
+
const replaced = frameRefs.pop();
|
|
2271
|
+
if (replaced)
|
|
2272
|
+
closeFrameRef(replaced, { type: 'replace' });
|
|
2273
|
+
frameRefs.push(frameRef);
|
|
2274
|
+
frameRef.__setCleanup?.(bindSurfaceContentSelectionResults(contentRef.instance, frameRef));
|
|
2275
|
+
publishNavigationState();
|
|
2276
|
+
return frameRef;
|
|
2277
|
+
};
|
|
2278
|
+
ref.back = () => navigateBack();
|
|
2279
|
+
if (frameRefs.length === 0) {
|
|
2280
|
+
Object.defineProperty(ref, 'frameId', {
|
|
2281
|
+
configurable: true,
|
|
2282
|
+
value: opts.id,
|
|
2283
|
+
});
|
|
2284
|
+
frameRefs.push(ref);
|
|
2285
|
+
}
|
|
2286
|
+
return ref;
|
|
2287
|
+
};
|
|
2288
|
+
const result = service.openHost({
|
|
1959
2289
|
component: SurfaceDrawerComponent,
|
|
1960
2290
|
config: {
|
|
1961
2291
|
id: opts.id,
|
|
@@ -1983,26 +2313,32 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
1983
2313
|
minHeight: opts.minHeight,
|
|
1984
2314
|
maxHeight: opts.maxHeight,
|
|
1985
2315
|
},
|
|
1986
|
-
createRef: (overlayRef) =>
|
|
2316
|
+
createRef: (overlayRef) => {
|
|
2317
|
+
const ref = new BaseSidePanelOverlayRef(overlayRef);
|
|
2318
|
+
return configureRef(ref);
|
|
2319
|
+
},
|
|
1987
2320
|
attach: (panelRef, ref) => {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
panelRef.instance.titleIcon = opts.titleIcon;
|
|
1991
|
-
panelRef.instance.subtitle = opts.subtitle;
|
|
2321
|
+
panel = panelRef.instance;
|
|
2322
|
+
configureRef(ref);
|
|
1992
2323
|
panelRef.instance.widthPreset = opts.widthPreset ?? 'default';
|
|
1993
2324
|
panelRef.instance.useHostWidth = !!(opts.width ||
|
|
1994
2325
|
opts.minWidth ||
|
|
1995
2326
|
opts.maxWidth);
|
|
1996
|
-
panelRef.instance.onClose = () =>
|
|
2327
|
+
panelRef.instance.onClose = () => {
|
|
2328
|
+
void panelRef.instance.canCloseSession().then((allowed) => {
|
|
2329
|
+
if (allowed)
|
|
2330
|
+
ref.close({ type: 'close' });
|
|
2331
|
+
});
|
|
2332
|
+
};
|
|
2333
|
+
panelRef.instance.onBack = () => { void activeRef?.back?.(); };
|
|
1997
2334
|
ref.bindTitleUpdater((title) => panelRef.instance.setTitle(title));
|
|
1998
|
-
const
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
});
|
|
2005
|
-
const contentRef = panelRef.instance.attachContent(opts.content.component, contentInjector, contentInputs);
|
|
2335
|
+
const contentRef = createContent({
|
|
2336
|
+
id: opts.id,
|
|
2337
|
+
title: opts.title,
|
|
2338
|
+
titleIcon: opts.titleIcon,
|
|
2339
|
+
subtitle: opts.subtitle,
|
|
2340
|
+
content: opts.content,
|
|
2341
|
+
}, 'initial', frameRefs[0] ?? ref);
|
|
2006
2342
|
const unbindSelectionResults = contentRef?.instance
|
|
2007
2343
|
? bindSurfaceContentSelectionResults(contentRef.instance, ref)
|
|
2008
2344
|
: null;
|
|
@@ -2011,10 +2347,35 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
2011
2347
|
}
|
|
2012
2348
|
panelRef.changeDetectorRef.detectChanges();
|
|
2013
2349
|
},
|
|
2014
|
-
createCloser: (ref) => (reason = 'close') =>
|
|
2015
|
-
|
|
2016
|
-
|
|
2350
|
+
createCloser: (ref) => (reason = 'close') => {
|
|
2351
|
+
void panel?.canCloseSession().then((allowed) => {
|
|
2352
|
+
if (allowed)
|
|
2353
|
+
ref.close({ type: reason });
|
|
2354
|
+
});
|
|
2355
|
+
},
|
|
2356
|
+
onBackdrop: (ref) => {
|
|
2357
|
+
void panel?.canCloseSession().then((allowed) => {
|
|
2358
|
+
if (allowed)
|
|
2359
|
+
ref.close({ type: 'backdrop' });
|
|
2360
|
+
});
|
|
2361
|
+
},
|
|
2362
|
+
onEsc: (ref) => {
|
|
2363
|
+
void panel?.canCloseSession().then((allowed) => {
|
|
2364
|
+
if (allowed)
|
|
2365
|
+
ref.close({ type: 'esc' });
|
|
2366
|
+
});
|
|
2367
|
+
},
|
|
2368
|
+
});
|
|
2369
|
+
result.closed$.subscribe(() => {
|
|
2370
|
+
while (frameRefs.length > 1) {
|
|
2371
|
+
closeFrameRef(frameRefs.pop(), { type: 'close' });
|
|
2372
|
+
}
|
|
2373
|
+
frameRefs.length = 0;
|
|
2374
|
+
navigationState.complete();
|
|
2375
|
+
panel = null;
|
|
2376
|
+
activeRef = null;
|
|
2017
2377
|
});
|
|
2378
|
+
return result;
|
|
2018
2379
|
},
|
|
2019
2380
|
}),
|
|
2020
2381
|
deps: [BaseSidePanelService, Injector, PraxisLayerScaleStyleService],
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/settings-panel",
|
|
3
|
-
"version": "9.0.4-rc.
|
|
3
|
+
"version": "9.0.4-rc.28",
|
|
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": "^21.0.0",
|
|
7
7
|
"@angular/core": "^21.0.0",
|
|
8
8
|
"@angular/cdk": "^21.0.0",
|
|
9
9
|
"@angular/material": "^21.0.0",
|
|
10
|
-
"@praxisui/ai": "^9.0.4-rc.
|
|
11
|
-
"@praxisui/core": "^9.0.4-rc.
|
|
12
|
-
"@praxisui/dynamic-fields": "^9.0.4-rc.
|
|
10
|
+
"@praxisui/ai": "^9.0.4-rc.28",
|
|
11
|
+
"@praxisui/core": "^9.0.4-rc.28",
|
|
12
|
+
"@praxisui/dynamic-fields": "^9.0.4-rc.28",
|
|
13
13
|
"rxjs": "~7.8.0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Type, Injector, ComponentRef, Provider, ChangeDetectorRef, ElementRef, InjectionToken,
|
|
2
|
+
import { Type, Injector, ComponentRef, Provider, OnDestroy, ChangeDetectorRef, ElementRef, InjectionToken, OnInit, AfterViewInit, ViewContainerRef } from '@angular/core';
|
|
3
3
|
import * as rxjs from 'rxjs';
|
|
4
4
|
import { Observable, BehaviorSubject } from 'rxjs';
|
|
5
5
|
import { OverlayRef, Overlay, OverlayContainer } from '@angular/cdk/overlay';
|
|
6
|
-
import { SurfaceOutcome, PraxisI18nService, GlobalConfig, FormConfig, FormValueChangeEvent, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, ComponentAuthoringManifest, ComponentDocMeta } from '@praxisui/core';
|
|
6
|
+
import { SurfaceOutcome, PraxisI18nService, SurfaceDrawerNavigationFrame, SurfaceDrawerNavigationGuard, GlobalConfig, FormConfig, FormValueChangeEvent, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, ComponentAuthoringManifest, ComponentDocMeta } from '@praxisui/core';
|
|
7
7
|
import { MatDialog } from '@angular/material/dialog';
|
|
8
8
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
9
9
|
import { AiProviderCatalogItem } from '@praxisui/ai';
|
|
@@ -353,7 +353,7 @@ declare function providePraxisSettingsPanelBridge(): Provider[];
|
|
|
353
353
|
|
|
354
354
|
declare function providePraxisSurfaceDrawerBridge(): Provider[];
|
|
355
355
|
|
|
356
|
-
declare class SurfaceDrawerComponent {
|
|
356
|
+
declare class SurfaceDrawerComponent implements OnDestroy {
|
|
357
357
|
private readonly cdr;
|
|
358
358
|
private readonly i18n;
|
|
359
359
|
title: string;
|
|
@@ -363,19 +363,42 @@ declare class SurfaceDrawerComponent {
|
|
|
363
363
|
useHostWidth: boolean;
|
|
364
364
|
theme: SidePanelThemeContract | null;
|
|
365
365
|
onClose?: () => void;
|
|
366
|
+
onBack?: () => void;
|
|
366
367
|
private static nextId;
|
|
367
368
|
titleId: string;
|
|
368
369
|
subtitleId: string;
|
|
369
370
|
contentRef?: ComponentRef<any>;
|
|
370
371
|
private contentHost;
|
|
372
|
+
private body?;
|
|
373
|
+
private backButton?;
|
|
374
|
+
private frames;
|
|
371
375
|
constructor(cdr: ChangeDetectorRef, i18n: PraxisI18nService);
|
|
372
376
|
get hostClasses(): string[];
|
|
373
377
|
get hostCssVars(): Record<string, string>;
|
|
374
378
|
get closeLabel(): string;
|
|
379
|
+
get backLabel(): string;
|
|
380
|
+
get canGoBack(): boolean;
|
|
381
|
+
get navigationDepth(): number;
|
|
382
|
+
get activeFrameId(): string;
|
|
375
383
|
get hasHeader(): boolean;
|
|
376
384
|
get widthClass(): string | null;
|
|
377
385
|
setTitle(title: string): void;
|
|
386
|
+
ngOnDestroy(): void;
|
|
378
387
|
attachContent(component: Type<any>, injector: Injector, inputs?: Record<string, unknown>): ComponentRef<any>;
|
|
388
|
+
initializeFrame(definition: SurfaceDrawerNavigationFrame, injector: Injector, inputs?: Record<string, unknown>): ComponentRef<any>;
|
|
389
|
+
pushFrame(definition: SurfaceDrawerNavigationFrame, injector: Injector, inputs?: Record<string, unknown>): ComponentRef<any>;
|
|
390
|
+
replaceFrame(definition: SurfaceDrawerNavigationFrame, injector: Injector, inputs?: Record<string, unknown>): Promise<ComponentRef<any> | null>;
|
|
391
|
+
backFrame(): Promise<boolean>;
|
|
392
|
+
setActiveCanLeave(guard: SurfaceDrawerNavigationGuard | null): void;
|
|
393
|
+
canCloseSession(): Promise<boolean>;
|
|
394
|
+
private createFrame;
|
|
395
|
+
private captureActiveFrameState;
|
|
396
|
+
private detachActiveView;
|
|
397
|
+
private removeActiveFrame;
|
|
398
|
+
private destroyAllFrames;
|
|
399
|
+
private canLeaveActiveFrame;
|
|
400
|
+
private applyFrameHeader;
|
|
401
|
+
private focusNavigationControl;
|
|
379
402
|
private applyInputs;
|
|
380
403
|
private resolveSupportedInputs;
|
|
381
404
|
static ɵfac: i0.ɵɵFactoryDeclaration<SurfaceDrawerComponent, never>;
|