@praxisui/settings-panel 9.0.4-rc.2 → 9.0.4-rc.20

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
@@ -26,7 +26,7 @@
26
26
  ## Instalacao
27
27
 
28
28
  ```bash
29
- npm i @praxisui/settings-panel@latest
29
+ npm i @praxisui/settings-panel@rc
30
30
  ```
31
31
 
32
32
  ## Minimal standalone content component
@@ -87,6 +87,7 @@ 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.
92
93
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-07-31T10:13:34.390Z",
3
+ "generatedAt": "2026-08-04T11:45:12.207Z",
4
4
  "packageName": "@praxisui/settings-panel",
5
- "packageVersion": "9.0.4-rc.2",
5
+ "packageVersion": "9.0.4-rc.20",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 1,
@@ -1408,6 +1408,7 @@ class BaseSidePanelOverlayRef {
1408
1408
  overlayRef;
1409
1409
  titleUpdater;
1410
1410
  sizePersistor;
1411
+ terminalOutcomeEmitted = false;
1411
1412
  closed$ = this.closedSubject.asObservable();
1412
1413
  result$ = this.resultSubject.asObservable();
1413
1414
  sizeChanged$ = this.sizeChangedSubject.asObservable();
@@ -1423,6 +1424,14 @@ class BaseSidePanelOverlayRef {
1423
1424
  emitResult(result) {
1424
1425
  this.resultSubject.next(result);
1425
1426
  }
1427
+ complete(outcome) {
1428
+ if (this.terminalOutcomeEmitted || !this.overlayRef?.hasAttached()) {
1429
+ return;
1430
+ }
1431
+ this.terminalOutcomeEmitted = true;
1432
+ this.resultSubject.next(outcome);
1433
+ this.close(outcome);
1434
+ }
1426
1435
  updateTitle(title) {
1427
1436
  this.titleUpdater?.(title);
1428
1437
  }
@@ -1804,6 +1813,7 @@ class SurfaceDrawerComponent {
1804
1813
  onClose;
1805
1814
  static nextId = 0;
1806
1815
  titleId = `praxis-surface-drawer-title-${SurfaceDrawerComponent.nextId++}`;
1816
+ subtitleId = `${this.titleId}-description`;
1807
1817
  contentRef;
1808
1818
  contentHost;
1809
1819
  constructor(cdr, i18n) {
@@ -1817,7 +1827,10 @@ class SurfaceDrawerComponent {
1817
1827
  return (this.theme?.cssVars ?? {});
1818
1828
  }
1819
1829
  get closeLabel() {
1820
- return this.i18n.t('Close', undefined, 'Close', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1830
+ const fallback = this.i18n.getLocale().toLowerCase().startsWith('en')
1831
+ ? 'Close'
1832
+ : 'Fechar';
1833
+ return this.i18n.t('Close', undefined, fallback, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1821
1834
  }
1822
1835
  get hasHeader() {
1823
1836
  return !!(this.title || this.titleIcon || this.subtitle);
@@ -1869,7 +1882,7 @@ class SurfaceDrawerComponent {
1869
1882
  return new Set(Object.keys(declaredInputs));
1870
1883
  }
1871
1884
  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:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);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:10px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{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:1rem;font-weight:600}.pfx-surface-drawer__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.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 });
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 });
1873
1886
  }
1874
1887
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
1875
1888
  type: Component,
@@ -1882,7 +1895,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1882
1895
  CdkTrapFocus,
1883
1896
  PraxisIconButtonComponent,
1884
1897
  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:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);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:10px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{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:1rem;font-weight:600}.pfx-surface-drawer__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.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"] }]
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"] }]
1886
1899
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$2.PraxisI18nService }], propDecorators: { contentHost: [{
1887
1900
  type: ViewChild,
1888
1901
  args: ['contentHost', { read: ViewContainerRef, static: true }]
@@ -1894,6 +1907,7 @@ function buildSurfaceRuntime(ref) {
1894
1907
  result$: ref.result$,
1895
1908
  close: (result) => ref.close?.(result),
1896
1909
  emitResult: (result) => ref.emitResult?.(result),
1910
+ complete: (outcome) => ref.complete?.(outcome),
1897
1911
  updateTitle: (title) => ref.updateTitle?.(title),
1898
1912
  updateSize: (preset) => ref.updateSize?.(preset),
1899
1913
  };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@praxisui/settings-panel",
3
- "version": "9.0.4-rc.2",
3
+ "version": "9.0.4-rc.20",
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.2",
11
- "@praxisui/core": "^9.0.4-rc.2",
12
- "@praxisui/dynamic-fields": "^9.0.4-rc.2",
10
+ "@praxisui/ai": "^9.0.4-rc.20",
11
+ "@praxisui/core": "^9.0.4-rc.20",
12
+ "@praxisui/dynamic-fields": "^9.0.4-rc.20",
13
13
  "rxjs": "~7.8.0"
14
14
  },
15
15
  "dependencies": {
@@ -3,7 +3,7 @@ import { Type, Injector, ComponentRef, Provider, ChangeDetectorRef, ElementRef,
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, 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;
@@ -362,6 +365,7 @@ declare class SurfaceDrawerComponent {
362
365
  onClose?: () => void;
363
366
  private static nextId;
364
367
  titleId: string;
368
+ subtitleId: string;
365
369
  contentRef?: ComponentRef<any>;
366
370
  private contentHost;
367
371
  constructor(cdr: ChangeDetectorRef, i18n: PraxisI18nService);