@praxisui/settings-panel 9.0.4-rc.3 → 9.0.4-rc.31
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
|
@@ -87,14 +87,22 @@ Peer dependencies (Angular v21):
|
|
|
87
87
|
- O shell runtime dedicado fica disponivel por `SurfaceDrawerComponent`.
|
|
88
88
|
- O bridge canonico de runtime e `providePraxisSurfaceDrawerBridge()`.
|
|
89
89
|
- Esse drawer runtime nao deve mostrar `Apply`, `Save`, `Reset` ou `Cancel` por heranca.
|
|
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.
|
|
90
91
|
- O runtime drawer aceita `width`, `minWidth`, `maxWidth`, `resizable` e `persistSizeKey`; o handle visual so aparece quando `resizable=true`.
|
|
91
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.
|
|
92
99
|
|
|
93
100
|
## Fronteira canonica
|
|
94
101
|
|
|
95
102
|
- `SettingsPanelService` e `SETTINGS_PANEL_BRIDGE` pertencem a authoring/configuracao.
|
|
96
103
|
- `SURFACE_DRAWER_BRIDGE` pertence ao runtime de `surface.open`.
|
|
97
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.
|
|
98
106
|
|
|
99
107
|
## Customizacao visual
|
|
100
108
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-
|
|
3
|
+
"generatedAt": "2026-08-05T03:38:59.493Z",
|
|
4
4
|
"packageName": "@praxisui/settings-panel",
|
|
5
|
-
"packageVersion": "9.0.4-rc.
|
|
5
|
+
"packageVersion": "9.0.4-rc.31",
|
|
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
|
};
|
|
@@ -1408,6 +1410,7 @@ class BaseSidePanelOverlayRef {
|
|
|
1408
1410
|
overlayRef;
|
|
1409
1411
|
titleUpdater;
|
|
1410
1412
|
sizePersistor;
|
|
1413
|
+
terminalOutcomeEmitted = false;
|
|
1411
1414
|
closed$ = this.closedSubject.asObservable();
|
|
1412
1415
|
result$ = this.resultSubject.asObservable();
|
|
1413
1416
|
sizeChanged$ = this.sizeChangedSubject.asObservable();
|
|
@@ -1423,6 +1426,14 @@ class BaseSidePanelOverlayRef {
|
|
|
1423
1426
|
emitResult(result) {
|
|
1424
1427
|
this.resultSubject.next(result);
|
|
1425
1428
|
}
|
|
1429
|
+
complete(outcome) {
|
|
1430
|
+
if (this.terminalOutcomeEmitted || !this.overlayRef?.hasAttached()) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
this.terminalOutcomeEmitted = true;
|
|
1434
|
+
this.resultSubject.next(outcome);
|
|
1435
|
+
this.close(outcome);
|
|
1436
|
+
}
|
|
1426
1437
|
updateTitle(title) {
|
|
1427
1438
|
this.titleUpdater?.(title);
|
|
1428
1439
|
}
|
|
@@ -1802,10 +1813,15 @@ class SurfaceDrawerComponent {
|
|
|
1802
1813
|
useHostWidth = false;
|
|
1803
1814
|
theme = null;
|
|
1804
1815
|
onClose;
|
|
1816
|
+
onBack;
|
|
1805
1817
|
static nextId = 0;
|
|
1806
1818
|
titleId = `praxis-surface-drawer-title-${SurfaceDrawerComponent.nextId++}`;
|
|
1819
|
+
subtitleId = `${this.titleId}-description`;
|
|
1807
1820
|
contentRef;
|
|
1808
1821
|
contentHost;
|
|
1822
|
+
body;
|
|
1823
|
+
backButton;
|
|
1824
|
+
frames = [];
|
|
1809
1825
|
constructor(cdr, i18n) {
|
|
1810
1826
|
this.cdr = cdr;
|
|
1811
1827
|
this.i18n = i18n;
|
|
@@ -1817,7 +1833,25 @@ class SurfaceDrawerComponent {
|
|
|
1817
1833
|
return (this.theme?.cssVars ?? {});
|
|
1818
1834
|
}
|
|
1819
1835
|
get closeLabel() {
|
|
1820
|
-
|
|
1836
|
+
const fallback = this.i18n.getLocale().toLowerCase().startsWith('en')
|
|
1837
|
+
? 'Close'
|
|
1838
|
+
: 'Fechar';
|
|
1839
|
+
return this.i18n.t('Close', undefined, fallback, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
|
|
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 ?? '';
|
|
1821
1855
|
}
|
|
1822
1856
|
get hasHeader() {
|
|
1823
1857
|
return !!(this.title || this.titleIcon || this.subtitle);
|
|
@@ -1832,11 +1866,156 @@ class SurfaceDrawerComponent {
|
|
|
1832
1866
|
this.title = title;
|
|
1833
1867
|
this.cdr.markForCheck();
|
|
1834
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
|
+
}
|
|
1835
1877
|
attachContent(component, injector, inputs) {
|
|
1836
|
-
|
|
1837
|
-
|
|
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;
|
|
1838
1958
|
this.cdr.markForCheck();
|
|
1839
|
-
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());
|
|
1840
2019
|
}
|
|
1841
2020
|
applyInputs(componentRef, inputs) {
|
|
1842
2021
|
if (!inputs) {
|
|
@@ -1869,7 +2048,7 @@ class SurfaceDrawerComponent {
|
|
|
1869
2048
|
return new Set(Object.keys(declaredInputs));
|
|
1870
2049
|
}
|
|
1871
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 });
|
|
1872
|
-
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 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\">{{ 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:
|
|
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 });
|
|
1873
2052
|
}
|
|
1874
2053
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
|
|
1875
2054
|
type: Component,
|
|
@@ -1882,10 +2061,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
1882
2061
|
CdkTrapFocus,
|
|
1883
2062
|
PraxisIconButtonComponent,
|
|
1884
2063
|
PraxisIconDirective,
|
|
1885
|
-
], 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 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\">{{ 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:
|
|
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"] }]
|
|
1886
2065
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.PraxisI18nService }], propDecorators: { contentHost: [{
|
|
1887
2066
|
type: ViewChild,
|
|
1888
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 }]
|
|
1889
2074
|
}] } });
|
|
1890
2075
|
|
|
1891
2076
|
function buildSurfaceRuntime(ref) {
|
|
@@ -1894,8 +2079,15 @@ function buildSurfaceRuntime(ref) {
|
|
|
1894
2079
|
result$: ref.result$,
|
|
1895
2080
|
close: (result) => ref.close?.(result),
|
|
1896
2081
|
emitResult: (result) => ref.emitResult?.(result),
|
|
2082
|
+
complete: (outcome) => ref.complete?.(outcome),
|
|
1897
2083
|
updateTitle: (title) => ref.updateTitle?.(title),
|
|
1898
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),
|
|
1899
2091
|
};
|
|
1900
2092
|
}
|
|
1901
2093
|
function enrichSurfaceContentInputs(inputs, ref) {
|
|
@@ -1941,7 +2133,159 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
1941
2133
|
useFactory: (service, injector, layerScale) => ({
|
|
1942
2134
|
open: (opts) => {
|
|
1943
2135
|
layerScale.ensureInstalled();
|
|
1944
|
-
|
|
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({
|
|
1945
2289
|
component: SurfaceDrawerComponent,
|
|
1946
2290
|
config: {
|
|
1947
2291
|
id: opts.id,
|
|
@@ -1969,26 +2313,32 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
1969
2313
|
minHeight: opts.minHeight,
|
|
1970
2314
|
maxHeight: opts.maxHeight,
|
|
1971
2315
|
},
|
|
1972
|
-
createRef: (overlayRef) =>
|
|
2316
|
+
createRef: (overlayRef) => {
|
|
2317
|
+
const ref = new BaseSidePanelOverlayRef(overlayRef);
|
|
2318
|
+
return configureRef(ref);
|
|
2319
|
+
},
|
|
1973
2320
|
attach: (panelRef, ref) => {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
panelRef.instance.titleIcon = opts.titleIcon;
|
|
1977
|
-
panelRef.instance.subtitle = opts.subtitle;
|
|
2321
|
+
panel = panelRef.instance;
|
|
2322
|
+
configureRef(ref);
|
|
1978
2323
|
panelRef.instance.widthPreset = opts.widthPreset ?? 'default';
|
|
1979
2324
|
panelRef.instance.useHostWidth = !!(opts.width ||
|
|
1980
2325
|
opts.minWidth ||
|
|
1981
2326
|
opts.maxWidth);
|
|
1982
|
-
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?.(); };
|
|
1983
2334
|
ref.bindTitleUpdater((title) => panelRef.instance.setTitle(title));
|
|
1984
|
-
const
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
});
|
|
1991
|
-
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);
|
|
1992
2342
|
const unbindSelectionResults = contentRef?.instance
|
|
1993
2343
|
? bindSurfaceContentSelectionResults(contentRef.instance, ref)
|
|
1994
2344
|
: null;
|
|
@@ -1997,10 +2347,35 @@ function providePraxisSurfaceDrawerBridge() {
|
|
|
1997
2347
|
}
|
|
1998
2348
|
panelRef.changeDetectorRef.detectChanges();
|
|
1999
2349
|
},
|
|
2000
|
-
createCloser: (ref) => (reason = 'close') =>
|
|
2001
|
-
|
|
2002
|
-
|
|
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;
|
|
2003
2377
|
});
|
|
2378
|
+
return result;
|
|
2004
2379
|
},
|
|
2005
2380
|
}),
|
|
2006
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.31",
|
|
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.31",
|
|
11
|
+
"@praxisui/core": "^9.0.4-rc.31",
|
|
12
|
+
"@praxisui/dynamic-fields": "^9.0.4-rc.31",
|
|
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 { 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';
|
|
@@ -224,6 +224,7 @@ interface BaseSidePanelRef<T = unknown> {
|
|
|
224
224
|
}>;
|
|
225
225
|
close(result?: BaseSidePanelResult<T>): void;
|
|
226
226
|
emitResult?(result: BaseSidePanelResult<T>): void;
|
|
227
|
+
complete?(outcome: SurfaceOutcome<T>): void;
|
|
227
228
|
updateTitle?(title: string): void;
|
|
228
229
|
updateSize?(size: SidePanelWidthPreset | string | {
|
|
229
230
|
width?: string;
|
|
@@ -260,6 +261,7 @@ declare class BaseSidePanelOverlayRef<T = unknown> implements BaseSidePanelRef<T
|
|
|
260
261
|
private overlayRef;
|
|
261
262
|
private titleUpdater?;
|
|
262
263
|
private sizePersistor?;
|
|
264
|
+
private terminalOutcomeEmitted;
|
|
263
265
|
readonly closed$: rxjs.Observable<BaseSidePanelResult<T> | undefined>;
|
|
264
266
|
readonly result$: rxjs.Observable<BaseSidePanelResult<T>>;
|
|
265
267
|
readonly sizeChanged$: rxjs.Observable<{
|
|
@@ -269,6 +271,7 @@ declare class BaseSidePanelOverlayRef<T = unknown> implements BaseSidePanelRef<T
|
|
|
269
271
|
bindTitleUpdater(fn: (title: string) => void): void;
|
|
270
272
|
bindSizePersistor(fn: (width: string) => void): void;
|
|
271
273
|
emitResult(result: BaseSidePanelResult<T>): void;
|
|
274
|
+
complete(outcome: SurfaceOutcome<T>): void;
|
|
272
275
|
updateTitle(title: string): void;
|
|
273
276
|
updateSize(size: string | {
|
|
274
277
|
width?: string;
|
|
@@ -350,7 +353,7 @@ declare function providePraxisSettingsPanelBridge(): Provider[];
|
|
|
350
353
|
|
|
351
354
|
declare function providePraxisSurfaceDrawerBridge(): Provider[];
|
|
352
355
|
|
|
353
|
-
declare class SurfaceDrawerComponent {
|
|
356
|
+
declare class SurfaceDrawerComponent implements OnDestroy {
|
|
354
357
|
private readonly cdr;
|
|
355
358
|
private readonly i18n;
|
|
356
359
|
title: string;
|
|
@@ -360,18 +363,42 @@ declare class SurfaceDrawerComponent {
|
|
|
360
363
|
useHostWidth: boolean;
|
|
361
364
|
theme: SidePanelThemeContract | null;
|
|
362
365
|
onClose?: () => void;
|
|
366
|
+
onBack?: () => void;
|
|
363
367
|
private static nextId;
|
|
364
368
|
titleId: string;
|
|
369
|
+
subtitleId: string;
|
|
365
370
|
contentRef?: ComponentRef<any>;
|
|
366
371
|
private contentHost;
|
|
372
|
+
private body?;
|
|
373
|
+
private backButton?;
|
|
374
|
+
private frames;
|
|
367
375
|
constructor(cdr: ChangeDetectorRef, i18n: PraxisI18nService);
|
|
368
376
|
get hostClasses(): string[];
|
|
369
377
|
get hostCssVars(): Record<string, string>;
|
|
370
378
|
get closeLabel(): string;
|
|
379
|
+
get backLabel(): string;
|
|
380
|
+
get canGoBack(): boolean;
|
|
381
|
+
get navigationDepth(): number;
|
|
382
|
+
get activeFrameId(): string;
|
|
371
383
|
get hasHeader(): boolean;
|
|
372
384
|
get widthClass(): string | null;
|
|
373
385
|
setTitle(title: string): void;
|
|
386
|
+
ngOnDestroy(): void;
|
|
374
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;
|
|
375
402
|
private applyInputs;
|
|
376
403
|
private resolveSupportedInputs;
|
|
377
404
|
static ɵfac: i0.ɵɵFactoryDeclaration<SurfaceDrawerComponent, never>;
|