@praxisui/core 9.0.65 → 9.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, InjectionToken, Injectable, inject, Inject, Optional, SkipSelf, makeEnvironmentProviders, APP_INITIALIZER, signal, computed, DestroyRef, ENVIRONMENT_INITIALIZER, ErrorHandler, Input, Directive, input, booleanAttribute, ChangeDetectionStrategy, EventEmitter, Output, SecurityContext, ViewContainerRef, Injector, isSignal, effect, SimpleChange, contentChild, HostBinding, output, ElementRef, ChangeDetectorRef, afterNextRender, HostListener, ViewChildren, ViewChild } from '@angular/core';
2
+ import { Component, InjectionToken, Injectable, inject, Inject, Optional, SkipSelf, makeEnvironmentProviders, APP_INITIALIZER, signal, computed, DestroyRef, ENVIRONMENT_INITIALIZER, ErrorHandler, Input, Directive, input, booleanAttribute, ChangeDetectionStrategy, EventEmitter, Output, SecurityContext, ViewContainerRef, Injector, isSignal, effect, SimpleChange, ChangeDetectorRef, ElementRef, afterEveryRender, contentChild, viewChild, HostBinding, output, afterNextRender, HostListener, ViewChildren, ViewChild } from '@angular/core';
3
3
  import * as i1 from '@angular/common/http';
4
4
  import { HttpHeaders, HttpClient, HttpParams, HttpResponse, HttpContextToken, HTTP_INTERCEPTORS, withInterceptors } from '@angular/common/http';
5
- import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, finalize as finalize$1, shareReplay as shareReplay$1, map as map$1, switchMap as switchMap$1, catchError as catchError$1, forkJoin } from 'rxjs';
5
+ import { of, defer, throwError, from, EMPTY, BehaviorSubject, firstValueFrom, Subject, finalize as finalize$1, shareReplay as shareReplay$1, map as map$1, switchMap as switchMap$1, catchError as catchError$1, forkJoin, timeout } from 'rxjs';
6
6
  import { switchMap, map, take, catchError, concatMap, tap, shareReplay, takeUntil, toArray, finalize } from 'rxjs/operators';
7
7
  import * as i1$3 from '@angular/common';
8
- import { Location, DOCUMENT, CommonModule } from '@angular/common';
8
+ import { Location, DOCUMENT, CommonModule, NgComponentOutlet } from '@angular/common';
9
9
  import { Router, ActivatedRoute } from '@angular/router';
10
10
  import * as i1$2 from '@angular/forms';
11
11
  import { Validators, FormGroup, FormControl, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -14,7 +14,7 @@ import * as i1$1 from '@angular/material/icon';
14
14
  import { MatIconRegistry, MatIconModule } from '@angular/material/icon';
15
15
  import * as i2 from '@angular/material/button';
16
16
  import { MatButtonModule } from '@angular/material/button';
17
- import * as i4 from '@angular/material/form-field';
17
+ import * as i3 from '@angular/material/form-field';
18
18
  import { MatFormFieldModule } from '@angular/material/form-field';
19
19
  import * as i5 from '@angular/material/input';
20
20
  import { MatInputModule } from '@angular/material/input';
@@ -22,19 +22,25 @@ import * as i6 from '@angular/material/select';
22
22
  import { MatSelectModule } from '@angular/material/select';
23
23
  import * as i7 from '@angular/material/slide-toggle';
24
24
  import { MatSlideToggleModule } from '@angular/material/slide-toggle';
25
- import * as i8 from '@angular/material/tooltip';
25
+ import * as i3$1 from '@angular/material/tooltip';
26
26
  import { MatTooltipModule } from '@angular/material/tooltip';
27
27
  import { DomSanitizer } from '@angular/platform-browser';
28
- import * as i4$1 from '@angular/material/menu';
29
- import { MatMenuModule } from '@angular/material/menu';
30
- import * as i3 from '@angular/material/progress-spinner';
28
+ import * as i4 from '@angular/material/menu';
29
+ import { MatMenuTrigger, MatMenuModule } from '@angular/material/menu';
30
+ import * as i5$2 from '@angular/cdk/a11y';
31
+ import { A11yModule } from '@angular/cdk/a11y';
32
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
33
+ import * as i5$1 from '@angular/material/radio';
34
+ import { MatRadioModule } from '@angular/material/radio';
35
+ import * as i4$1 from '@angular/cdk/overlay';
36
+ import { OverlayModule } from '@angular/cdk/overlay';
37
+ import * as i3$2 from '@angular/material/progress-spinner';
31
38
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
32
- import * as i3$1 from '@angular/material/button-toggle';
39
+ import * as i3$3 from '@angular/material/button-toggle';
33
40
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
34
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
35
41
  import * as i1$4 from '@angular/material/dialog';
36
42
  import { MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
37
- import * as i8$1 from '@angular/material/chips';
43
+ import * as i8 from '@angular/material/chips';
38
44
  import { MatChipsModule } from '@angular/material/chips';
39
45
  import * as i2$1 from '@angular/material/card';
40
46
  import { MatCardModule } from '@angular/material/card';
@@ -8816,9 +8822,11 @@ const SURFACE_NAVIGATION_I18N_CONFIG = {
8816
8822
  [SURFACE_NAVIGATION_I18N_NAMESPACE]: {
8817
8823
  'pt-BR': {
8818
8824
  'session.navigationRejected': 'Não foi possível abrir esta etapa agora. Revise a etapa em andamento e tente novamente.',
8825
+ 'surface.openFailed': 'Não foi possível abrir esta superfície agora. Tente novamente ou confirme se ela continua disponível.',
8819
8826
  },
8820
8827
  'en-US': {
8821
8828
  'session.navigationRejected': 'This step could not be opened right now. Review the current step and try again.',
8829
+ 'surface.openFailed': 'This surface could not be opened right now. Try again or confirm that it is still available.',
8822
8830
  },
8823
8831
  },
8824
8832
  },
@@ -8830,6 +8838,13 @@ function translateSurfaceNavigationRejected(i18n) {
8830
8838
  : 'Não foi possível abrir esta etapa agora. Revise a etapa em andamento e tente novamente.';
8831
8839
  return i18n.t('session.navigationRejected', undefined, fallback, SURFACE_NAVIGATION_I18N_NAMESPACE);
8832
8840
  }
8841
+ function translateSurfaceOpenFailed(i18n) {
8842
+ const locale = String(i18n.getLocale?.() || 'pt-BR').toLowerCase();
8843
+ const fallback = locale.startsWith('en')
8844
+ ? 'This surface could not be opened right now. Try again or confirm that it is still available.'
8845
+ : 'Não foi possível abrir esta superfície agora. Tente novamente ou confirme se ela continua disponível.';
8846
+ return i18n.t('surface.openFailed', undefined, fallback, SURFACE_NAVIGATION_I18N_NAMESPACE);
8847
+ }
8833
8848
 
8834
8849
  /**
8835
8850
  * Controlled failure emitted when an active runtime surface session refuses a
@@ -8985,7 +9000,16 @@ class GlobalActionService {
8985
9000
  retryable: false,
8986
9001
  };
8987
9002
  }
8988
- return { success: false, error: err?.message || String(err) };
9003
+ if (id === 'surface.open') {
9004
+ return {
9005
+ success: false,
9006
+ error: translateSurfaceOpenFailed(this.i18n),
9007
+ errorCode: 'SURFACE_OPEN_FAILED',
9008
+ retryable: false,
9009
+ };
9010
+ }
9011
+ const message = err && typeof err === 'object' && 'message' in err ? err.message : undefined;
9012
+ return { success: false, error: typeof message === 'string' && message ? message : String(err) };
8989
9013
  }
8990
9014
  }
8991
9015
  async executeRef(ref, context) {
@@ -9105,7 +9129,12 @@ class GlobalActionService {
9105
9129
  }, () => [this.providerRequirement('GLOBAL_DIALOG_SERVICE', this.dialog, 'dialog.open')]);
9106
9130
  this.register('surface.open', async (payload, context) => {
9107
9131
  if (!this.surface)
9108
- return { success: false, error: 'Surface service not available' };
9132
+ return {
9133
+ success: false,
9134
+ error: translateSurfaceOpenFailed(this.i18n),
9135
+ errorCode: 'SURFACE_SERVICE_UNAVAILABLE',
9136
+ retryable: false,
9137
+ };
9109
9138
  const surfacePayload = payload;
9110
9139
  if (!surfacePayload?.widget?.id) {
9111
9140
  return { success: false, error: 'surface.open requires widget.id' };
@@ -16049,6 +16078,178 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
16049
16078
  args: [{ providedIn: 'any' }]
16050
16079
  }] });
16051
16080
 
16081
+ const GOVERNED_COLOR_PALETTE_OPTIONS = new InjectionToken('GOVERNED_COLOR_PALETTE_OPTIONS');
16082
+ class GovernedColorPaletteService {
16083
+ http = inject(HttpClient);
16084
+ options = inject(GOVERNED_COLOR_PALETTE_OPTIONS, {
16085
+ optional: true,
16086
+ });
16087
+ baseUrl = (this.options?.baseUrl ?? '/api/praxis/config/color-palettes').replace(/\/$/, '');
16088
+ cache = new Map();
16089
+ list(familyKey) {
16090
+ const params = familyKey
16091
+ ? new HttpParams().set('familyKey', familyKey)
16092
+ : undefined;
16093
+ return this.http
16094
+ .get(this.baseUrl, {
16095
+ headers: this.headers(),
16096
+ params,
16097
+ })
16098
+ .pipe(map$1((palettes) => palettes.map((palette) => this.assertCompleteProjection(palette))));
16099
+ }
16100
+ get(ref) {
16101
+ const normalizedRef = typeof ref === 'string' ? { paletteKey: ref } : ref;
16102
+ const requestHeaders = this.headers();
16103
+ const cacheKey = this.cacheKey(normalizedRef, requestHeaders);
16104
+ const cached = this.cache.get(cacheKey);
16105
+ const params = normalizedRef.version
16106
+ ? new HttpParams().set('version', normalizedRef.version)
16107
+ : undefined;
16108
+ return this.http
16109
+ .get(`${this.baseUrl}/${encodeURIComponent(normalizedRef.paletteKey)}`, {
16110
+ observe: 'response',
16111
+ params,
16112
+ headers: cached?.etag
16113
+ ? requestHeaders.set('If-None-Match', this.formatEtag(cached.etag))
16114
+ : requestHeaders,
16115
+ })
16116
+ .pipe(map$1((response) => this.acceptResponse(response, normalizedRef, cacheKey)), catchError$1((error) => error.status === 304 && cached
16117
+ ? defer(() => of(this.acceptPalette(cached, normalizedRef, cacheKey)))
16118
+ : throwError(() => error)));
16119
+ }
16120
+ preview(request) {
16121
+ return this.http.post(`${this.baseUrl}/previews`, request, { headers: this.headers() });
16122
+ }
16123
+ capabilities() {
16124
+ return this.http.get(`${this.baseUrl}/capabilities`, { headers: this.headers() });
16125
+ }
16126
+ invalidate(ref) {
16127
+ if (ref === undefined) {
16128
+ this.cache.clear();
16129
+ return;
16130
+ }
16131
+ const normalizedRef = typeof ref === 'string' ? { paletteKey: ref } : ref;
16132
+ const key = this.cacheKey(normalizedRef, this.headers());
16133
+ this.cache.delete(key);
16134
+ }
16135
+ materializeColors(palette, purposes) {
16136
+ return materializeGovernedPaletteColors(palette, purposes);
16137
+ }
16138
+ acceptResponse(response, ref, cacheKey) {
16139
+ const palette = response.body;
16140
+ if (!palette) {
16141
+ throw new Error(`Empty governed palette response: ${ref.paletteKey}`);
16142
+ }
16143
+ return this.acceptPalette(palette, ref, cacheKey);
16144
+ }
16145
+ acceptPalette(palette, ref, cacheKey) {
16146
+ this.assertCompleteProjection(palette);
16147
+ if (palette.paletteKey !== ref.paletteKey) {
16148
+ throw new Error(`Governed palette identity mismatch for ${ref.paletteKey}`);
16149
+ }
16150
+ if (ref.version !== undefined && palette.version !== ref.version) {
16151
+ throw new Error(`Governed palette version mismatch for ${ref.paletteKey}: expected ${ref.version}, received ${palette.version}`);
16152
+ }
16153
+ if (ref.etag !== undefined &&
16154
+ this.normalizeEtag(palette.etag) !== this.normalizeEtag(ref.etag)) {
16155
+ throw new Error(`Governed palette ETag mismatch for ${ref.paletteKey}`);
16156
+ }
16157
+ this.cache.set(cacheKey, palette);
16158
+ return palette;
16159
+ }
16160
+ assertCompleteProjection(palette) {
16161
+ const variant = palette.variant;
16162
+ const completeVariant = !!variant &&
16163
+ typeof variant.key === 'string' &&
16164
+ !!variant.key.trim() &&
16165
+ typeof variant.displayName === 'string' &&
16166
+ !!variant.displayName.trim() &&
16167
+ this.isStringRecord(variant.dimensions);
16168
+ const completeEntries = Array.isArray(palette.entries) &&
16169
+ palette.entries.every((entry) => typeof entry.displayName === 'string' &&
16170
+ !!entry.displayName.trim() &&
16171
+ Array.isArray(entry.aliases) &&
16172
+ entry.aliases.every((alias) => typeof alias === 'string' && !!alias.trim()));
16173
+ if (typeof palette.familyKey !== 'string' ||
16174
+ !palette.familyKey.trim() ||
16175
+ !completeVariant ||
16176
+ !completeEntries) {
16177
+ throw new Error(`Incomplete governed palette projection: ${palette.paletteKey ?? 'unknown'}`);
16178
+ }
16179
+ return palette;
16180
+ }
16181
+ isStringRecord(value) {
16182
+ return (typeof value === 'object' &&
16183
+ value !== null &&
16184
+ !Array.isArray(value) &&
16185
+ Object.values(value).every((item) => typeof item === 'string' && !!item.trim()));
16186
+ }
16187
+ cacheKey(ref, headers) {
16188
+ const tenant = headers.get('X-Tenant-ID') ?? '';
16189
+ const environment = headers.get('X-Env') ?? '';
16190
+ return `${tenant}\u0000${environment}\u0000${ref.paletteKey}\u0000${ref.version ?? 'latest'}`;
16191
+ }
16192
+ headers() {
16193
+ const values = this.options?.headersFactory?.() ?? {};
16194
+ let headers = new HttpHeaders();
16195
+ for (const [key, value] of Object.entries(values)) {
16196
+ if (typeof value === 'string' && value.trim()) {
16197
+ headers = headers.set(key, value);
16198
+ }
16199
+ }
16200
+ return headers;
16201
+ }
16202
+ formatEtag(etag) {
16203
+ const normalized = this.normalizeEtag(etag);
16204
+ return `"${normalized}"`;
16205
+ }
16206
+ normalizeEtag(etag) {
16207
+ return etag.trim().replace(/^W\//, '').replace(/^"|"$/g, '');
16208
+ }
16209
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GovernedColorPaletteService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
16210
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GovernedColorPaletteService, providedIn: 'root' });
16211
+ }
16212
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GovernedColorPaletteService, decorators: [{
16213
+ type: Injectable,
16214
+ args: [{ providedIn: 'root' }]
16215
+ }] });
16216
+ function materializeGovernedPaletteColors(palette, purposes) {
16217
+ return materializeGovernedPaletteEntries(palette, purposes).map((item) => item.color);
16218
+ }
16219
+ /** Preserves entry identity even when multiple tokens materialize to the same color. */
16220
+ function materializeGovernedPaletteEntries(palette, purposes) {
16221
+ if (!palette.validation.valid ||
16222
+ palette.entries.some((entry) => !!entry.fallback && !!entry.fallbackTokenId)) {
16223
+ return [];
16224
+ }
16225
+ const requiredPurposes = new Set(purposes ?? []);
16226
+ const byId = new Map(palette.entries.map((entry) => [entry.tokenId, entry]));
16227
+ return palette.entries
16228
+ .filter((entry) => requiredPurposes.size === 0 ||
16229
+ entry.purposes.some((purpose) => requiredPurposes.has(purpose)))
16230
+ .flatMap((entry) => {
16231
+ const color = materializeEntry(entry, byId, new Set());
16232
+ return color ? [{ entry, color }] : [];
16233
+ });
16234
+ }
16235
+ function materializeEntry(entry, byId, visited) {
16236
+ if (visited.has(entry.tokenId)) {
16237
+ return null;
16238
+ }
16239
+ visited.add(entry.tokenId);
16240
+ const fallbackEntry = entry.fallbackTokenId
16241
+ ? byId.get(entry.fallbackTokenId)
16242
+ : undefined;
16243
+ const fallback = fallbackEntry
16244
+ ? materializeEntry(fallbackEntry, byId, visited)
16245
+ : entry.fallback;
16246
+ if (!fallback) {
16247
+ return entry.cssReference;
16248
+ }
16249
+ const tokenName = /^var\((--[A-Za-z0-9_-]+)\)$/.exec(entry.cssReference)?.[1];
16250
+ return tokenName ? `var(${tokenName}, ${fallback})` : entry.cssReference;
16251
+ }
16252
+
16052
16253
  const PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_OPTIONS = new InjectionToken('PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_OPTIONS');
16053
16254
  const PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_READY = new InjectionToken('PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_READY');
16054
16255
  function providePraxisEnterpriseRuntimeContext(options = {}) {
@@ -20849,6 +21050,17 @@ const RESOURCE_DISCOVERY_I18N_CONFIG = {
20849
21050
  namespaces: {
20850
21051
  [RESOURCE_DISCOVERY_I18N_NAMESPACE]: {
20851
21052
  'pt-BR': {
21053
+ 'quickConnect.resource': 'Recurso de dados',
21054
+ 'quickConnect.loading': 'Carregando recursos…',
21055
+ 'quickConnect.unavailable': 'Não foi possível carregar o catálogo. Tente novamente ou informe a rota avançada.',
21056
+ 'quickConnect.empty': 'O catálogo não publicou recursos disponíveis.',
21057
+ 'quickConnect.advanced': 'Conexão avançada por rota',
21058
+ 'quickConnect.path': 'Rota do recurso (API)',
21059
+ 'quickConnect.invalid': 'Informe uma rota válida da API.',
21060
+ 'quickConnect.retry': 'Tentar novamente',
21061
+ 'quickConnect.hint': 'Escolha um recurso publicado pela API.',
21062
+ 'quickConnect.fields': 'Carregar campos do filtro',
21063
+ 'quickConnect.fieldsError': 'Não foi possível carregar os campos. Verifique o recurso e tente novamente.',
20852
21064
  'workflow.unavailable.default': 'A\u00e7\u00e3o indispon\u00edvel no contexto atual.',
20853
21065
  'workflow.unavailable.withReason': 'A\u00e7\u00e3o indispon\u00edvel: {{reason}}',
20854
21066
  'rowActions.menu.loading': 'Carregando a\u00e7\u00f5es contextuais',
@@ -20861,6 +21073,17 @@ const RESOURCE_DISCOVERY_I18N_CONFIG = {
20861
21073
  'availability.reason.unknown': 'O contexto atual n\u00e3o permite esta a\u00e7\u00e3o.',
20862
21074
  },
20863
21075
  'en-US': {
21076
+ 'quickConnect.resource': 'Data resource',
21077
+ 'quickConnect.loading': 'Loading resources…',
21078
+ 'quickConnect.unavailable': 'The catalog could not be loaded. Retry or enter the advanced resource path.',
21079
+ 'quickConnect.empty': 'The catalog did not publish any available resources.',
21080
+ 'quickConnect.advanced': 'Advanced connection by path',
21081
+ 'quickConnect.path': 'Resource path (API)',
21082
+ 'quickConnect.invalid': 'Enter a valid API resource path.',
21083
+ 'quickConnect.retry': 'Retry',
21084
+ 'quickConnect.hint': 'Choose a resource published by the API.',
21085
+ 'quickConnect.fields': 'Load filter fields',
21086
+ 'quickConnect.fieldsError': 'Fields could not be loaded. Check the resource and retry.',
20864
21087
  'workflow.unavailable.default': 'Action is unavailable in the current context.',
20865
21088
  'workflow.unavailable.withReason': 'Action unavailable: {{reason}}',
20866
21089
  'rowActions.menu.loading': 'Loading contextual actions',
@@ -27539,7 +27762,7 @@ class SurfaceOpenActionEditorComponent {
27539
27762
  </mat-form-field>
27540
27763
  </div>
27541
27764
  </div>
27542
- `, isInline: true, styles: [".surface-editor{display:grid;gap:14px;min-width:0}.surface-section{display:grid;gap:10px;min-width:0;padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:0}.surface-section-title{font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant);margin-bottom:0}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:12px 16px;min-width:0}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%;min-width:0}.surface-component-meta{display:grid;gap:4px;padding:10px 12px;border-radius:8px;background:var(--md-sys-color-surface-container)}.surface-component-title{font-weight:600;color:var(--md-sys-color-on-surface)}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.surface-bindings{display:grid;gap:10px;min-width:0}.surface-binding-row{display:grid;grid-template-columns:minmax(min(220px,100%),1.15fr) minmax(min(240px,100%),1.25fr) minmax(128px,.55fr) auto;gap:10px;align-items:start;min-width:0;padding:10px;border-radius:8px;background:var(--md-sys-color-surface-container)}.surface-binding-row mat-form-field:nth-child(4){grid-column:1 / -2}.surface-binding-remove{color:var(--md-sys-color-error)}.surface-guidance{display:grid;gap:4px;padding:10px 12px;border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-primary-container) 18%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.surface-guidance__item{overflow-wrap:anywhere}.surface-guidance__item--primary{color:var(--md-sys-color-on-surface)}mat-form-field{width:100%;min-width:0}@media(max-width:960px){.surface-section{padding:12px}.surface-span-2{grid-column:1 / -1}.surface-binding-row{grid-template-columns:minmax(0,1fr)}.surface-binding-row mat-form-field:nth-child(4){grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }] });
27765
+ `, isInline: true, styles: [".surface-editor{display:grid;gap:14px;min-width:0}.surface-section{display:grid;gap:10px;min-width:0;padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-low)}.surface-section-header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:0}.surface-section-title{font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant);margin-bottom:0}.surface-section-header .surface-section-title{margin-bottom:0}.surface-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:12px 16px;min-width:0}.surface-preset-row{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.surface-span-2{grid-column:span 2}.surface-span-all{width:100%;min-width:0}.surface-component-meta{display:grid;gap:4px;padding:10px 12px;border-radius:8px;background:var(--md-sys-color-surface-container)}.surface-component-title{font-weight:600;color:var(--md-sys-color-on-surface)}.surface-component-description,.surface-empty{color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.surface-bindings{display:grid;gap:10px;min-width:0}.surface-binding-row{display:grid;grid-template-columns:minmax(min(220px,100%),1.15fr) minmax(min(240px,100%),1.25fr) minmax(128px,.55fr) auto;gap:10px;align-items:start;min-width:0;padding:10px;border-radius:8px;background:var(--md-sys-color-surface-container)}.surface-binding-row mat-form-field:nth-child(4){grid-column:1 / -2}.surface-binding-remove{color:var(--md-sys-color-error)}.surface-guidance{display:grid;gap:4px;padding:10px 12px;border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-primary-container) 18%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;line-height:1.45}.surface-guidance__item{overflow-wrap:anywhere}.surface-guidance__item--primary{color:var(--md-sys-color-on-surface)}mat-form-field{width:100%;min-width:0}@media(max-width:960px){.surface-section{padding:12px}.surface-span-2{grid-column:1 / -1}.surface-binding-row{grid-template-columns:minmax(0,1fr)}.surface-binding-row mat-form-field:nth-child(4){grid-column:auto}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }] });
27543
27766
  }
27544
27767
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceOpenActionEditorComponent, decorators: [{
27545
27768
  type: Component,
@@ -29229,7 +29452,7 @@ const ENUMS = {
29229
29452
  ],
29230
29453
  shellKind: ['dashboard-card', 'none'],
29231
29454
  actionVariant: ['icon', 'text', 'outlined'],
29232
- actionPlacement: ['header', 'window'],
29455
+ actionPlacement: ['header', 'window', 'menu'],
29233
29456
  };
29234
29457
  const CAPS = [
29235
29458
  {
@@ -33132,6 +33355,22 @@ function isRecord(value) {
33132
33355
  return !!value && typeof value === 'object' && !Array.isArray(value);
33133
33356
  }
33134
33357
 
33358
+ /** Private rendered geometry shared by shell recovery and canvas authoring. */
33359
+ function measureWidgetShellHeight(shell) {
33360
+ const body = shell.querySelector(':scope > .pdx-shell-body');
33361
+ if (!body || body.classList.contains('hidden') || !shell.getBoundingClientRect().height)
33362
+ return undefined;
33363
+ const header = shell.querySelector(':scope > .pdx-shell-header');
33364
+ const bodyStyle = getComputedStyle(body);
33365
+ const padding = (parseFloat(bodyStyle.paddingTop) || 0) + (parseFloat(bodyStyle.paddingBottom) || 0);
33366
+ const shellStyle = getComputedStyle(shell);
33367
+ const chrome = (header?.getBoundingClientRect().height || 0)
33368
+ + (parseFloat(shellStyle.borderTopWidth) || 0) + (parseFloat(shellStyle.borderBottomWidth) || 0);
33369
+ // Structural viewport reserve only; not business-component UX certification.
33370
+ const reserve = Math.min(160, Math.max(0, body.scrollHeight - padding));
33371
+ return { minimum: Math.ceil(chrome + padding + reserve), available: shell.getBoundingClientRect().height };
33372
+ }
33373
+
33135
33374
  const WIDGET_SHELL_I18N_NAMESPACE = 'widgetShell';
33136
33375
  const WIDGET_SHELL_I18N_CONFIG = {
33137
33376
  namespaces: {
@@ -33333,7 +33572,142 @@ const BUILTIN_SHELL_PRESETS = {
33333
33572
  },
33334
33573
  };
33335
33574
  class WidgetShellComponent {
33575
+ stickyAncestorHeight = signal(0, ...(ngDevMode ? [{ debugName: "stickyAncestorHeight" }] : /* istanbul ignore next */ []));
33576
+ stickyAncestorObserver;
33577
+ stickyAncestorHeaders = [];
33578
+ get stickyAncestorTop() {
33579
+ const height = this.stickyAncestorHeight();
33580
+ return this.stickyHeaderActive && height > 0
33581
+ ? `calc(var(--pdx-shell-sticky-header-offset, 0px) + ${height}px)` : null;
33582
+ }
33583
+ observeStickyAncestors() {
33584
+ const headers = [];
33585
+ if (this.stickyHeaderActive
33586
+ && this.element.nativeElement.parentElement?.closest('.pdx-shell--sticky-header')) {
33587
+ let ancestor = this.element.nativeElement.parentElement;
33588
+ while (ancestor) {
33589
+ // A distinct scrollport owns its own sticky stack. A header outside
33590
+ // that port must not consume space in the child's scrolling surface.
33591
+ if (['auto', 'scroll', 'hidden', 'overlay'].includes(getComputedStyle(ancestor).overflowY))
33592
+ break;
33593
+ if (ancestor.classList.contains('pdx-shell--sticky-header')) {
33594
+ const header = ancestor.querySelector(':scope > .pdx-shell-header');
33595
+ if (header)
33596
+ headers.push(header);
33597
+ }
33598
+ ancestor = ancestor.parentElement;
33599
+ }
33600
+ }
33601
+ if (headers.length !== this.stickyAncestorHeaders.length
33602
+ || headers.some((header, index) => header !== this.stickyAncestorHeaders[index])) {
33603
+ this.stickyAncestorObserver?.disconnect();
33604
+ this.stickyAncestorObserver = undefined;
33605
+ this.stickyAncestorHeaders = headers;
33606
+ if (headers.length && typeof ResizeObserver !== 'undefined') {
33607
+ this.stickyAncestorObserver = new ResizeObserver(() => this.measureStickyAncestors());
33608
+ headers.forEach(header => this.stickyAncestorObserver.observe(header));
33609
+ }
33610
+ }
33611
+ this.measureStickyAncestors();
33612
+ }
33613
+ measureStickyAncestors() {
33614
+ if (this.destroyRef.destroyed)
33615
+ return;
33616
+ this.stickyAncestorHeight.set(this.stickyAncestorHeaders.reduce((height, header) => height + header.getBoundingClientRect().height, 0));
33617
+ }
33618
+ get stickyHeaderActive() {
33619
+ return this.shell?.stickyHeader === true && this.shellEnabled && this.showHeader
33620
+ && !this.dragSurfaceInteractive && !this.collapsed && !this.expanded
33621
+ && !this.fullscreen && !this.scrollRecovery();
33622
+ }
33623
+ revealStickyHeaderFocus(event) {
33624
+ if (!this.stickyHeaderActive || !(event.target instanceof HTMLElement))
33625
+ return;
33626
+ const header = this.element.nativeElement.querySelector(':scope > .pdx-shell > .pdx-shell-header');
33627
+ if (!header || header.contains(event.target))
33628
+ return;
33629
+ const target = event.target.getBoundingClientRect();
33630
+ const boundary = header.getBoundingClientRect().bottom + 8;
33631
+ if (!target.height || target.top >= boundary)
33632
+ return;
33633
+ let owner = this.element.nativeElement.parentElement;
33634
+ while (owner && !['auto', 'scroll', 'hidden', 'overlay'].includes(getComputedStyle(owner).overflowY)) {
33635
+ owner = owner.parentElement;
33636
+ }
33637
+ (owner ?? header.ownerDocument.scrollingElement)?.scrollBy({ top: target.top - boundary, behavior: 'instant' });
33638
+ }
33336
33639
  i18n = inject(PraxisI18nService);
33640
+ changeDetector = inject(ChangeDetectorRef);
33641
+ destroyRef = inject(DestroyRef);
33642
+ element = inject(ElementRef);
33643
+ scrollRecovery = signal(false, ...(ngDevMode ? [{ debugName: "scrollRecovery" }] : /* istanbul ignore next */ []));
33644
+ scrollResizeObserver;
33645
+ scrollMutationObserver;
33646
+ scrollObservedElements = [];
33647
+ scrollObservedBody;
33648
+ scrollFrame;
33649
+ constructor() {
33650
+ afterEveryRender({ read: () => {
33651
+ this.observeScrollGeometry();
33652
+ this.observeStickyAncestors();
33653
+ } });
33654
+ this.destroyRef.onDestroy(() => {
33655
+ this.disconnectScrollGeometry();
33656
+ this.stickyAncestorObserver?.disconnect();
33657
+ this.stickyAncestorHeaders = [];
33658
+ });
33659
+ }
33660
+ disconnectScrollGeometry() {
33661
+ this.scrollResizeObserver?.disconnect();
33662
+ this.scrollMutationObserver?.disconnect();
33663
+ this.scrollObservedElements = [];
33664
+ this.scrollObservedBody = undefined;
33665
+ if (this.scrollFrame !== undefined)
33666
+ cancelAnimationFrame(this.scrollFrame);
33667
+ this.scrollFrame = undefined;
33668
+ }
33669
+ observeScrollGeometry() {
33670
+ if (this.shell?.bodyLayout !== 'scroll' || this.collapsed || this.expanded || this.fullscreen) {
33671
+ this.disconnectScrollGeometry();
33672
+ this.scrollRecovery.set(false);
33673
+ return;
33674
+ }
33675
+ const shell = this.element.nativeElement.querySelector(':scope > .pdx-shell');
33676
+ const body = shell?.querySelector(':scope > .pdx-shell-body');
33677
+ if (!shell || !body)
33678
+ return;
33679
+ const schedule = () => {
33680
+ if (this.scrollFrame !== undefined || this.destroyRef.destroyed)
33681
+ return;
33682
+ this.scrollFrame = requestAnimationFrame(() => {
33683
+ this.scrollFrame = undefined;
33684
+ if (!this.destroyRef.destroyed)
33685
+ this.observeScrollGeometry();
33686
+ });
33687
+ };
33688
+ const elements = [shell, ...Array.from(shell.children), ...Array.from(body.children)];
33689
+ if (typeof ResizeObserver === 'function' && (elements.length !== this.scrollObservedElements.length
33690
+ || elements.some((element, index) => element !== this.scrollObservedElements[index]))) {
33691
+ this.scrollResizeObserver ??= new ResizeObserver(schedule);
33692
+ this.scrollResizeObserver.disconnect();
33693
+ elements.forEach(element => this.scrollResizeObserver.observe(element));
33694
+ this.scrollObservedElements = elements;
33695
+ }
33696
+ if (typeof MutationObserver === 'function' && this.scrollObservedBody !== body) {
33697
+ this.scrollMutationObserver ??= new MutationObserver(schedule);
33698
+ this.scrollMutationObserver.disconnect();
33699
+ this.scrollMutationObserver.observe(body, { subtree: true, childList: true, characterData: true, attributes: true });
33700
+ this.scrollObservedBody = body;
33701
+ }
33702
+ const measure = measureWidgetShellHeight(shell);
33703
+ const recovery = !!measure && measure.minimum > Math.ceil(measure.available);
33704
+ if (this.scrollRecovery() !== recovery) {
33705
+ this.scrollRecovery.set(recovery);
33706
+ // A scroll position in the outer shell must not hide its header after recovery.
33707
+ if (!recovery)
33708
+ shell.scrollTop = 0;
33709
+ }
33710
+ }
33337
33711
  get hostCollapsed() {
33338
33712
  return this.collapsed && !this.expanded && !this.fullscreen;
33339
33713
  }
@@ -33362,6 +33736,7 @@ class WidgetShellComponent {
33362
33736
  collapsed = false;
33363
33737
  expanded = false;
33364
33738
  fullscreen = false;
33739
+ overlayReturnFocus = null;
33365
33740
  initializedWindowState = false;
33366
33741
  lastWindowStateInputs;
33367
33742
  ngOnChanges(changes) {
@@ -33393,6 +33768,7 @@ class WidgetShellComponent {
33393
33768
  this.shell?.subtitle ||
33394
33769
  this.shell?.icon ||
33395
33770
  this.headerActions.length ||
33771
+ this.overflowHeaderActions.length ||
33396
33772
  this.windowActions.length);
33397
33773
  }
33398
33774
  get headerActions() {
@@ -33404,7 +33780,7 @@ class WidgetShellComponent {
33404
33780
  }
33405
33781
  get overflowHeaderActions() {
33406
33782
  const max = this.maxHeaderActions;
33407
- return this.headerActions.slice(max);
33783
+ return [...this.headerActions.slice(max), ...this.mergedActions.filter(a => this.isVisible(a) && a.placement === 'menu')];
33408
33784
  }
33409
33785
  get maxHeaderActions() {
33410
33786
  return 3;
@@ -33425,9 +33801,26 @@ class WidgetShellComponent {
33425
33801
  }
33426
33802
  return action.pressed === true || action.pressed === 'true';
33427
33803
  }
33804
+ overflowTrigger = viewChild(MatMenuTrigger, ...(ngDevMode ? [{ debugName: "overflowTrigger" }] : /* istanbul ignore next */ []));
33805
+ onOverflowAction(action, event) {
33806
+ if (action.disabled)
33807
+ return;
33808
+ this.overflowTrigger()?.closeMenu();
33809
+ this.onAction(action, event);
33810
+ }
33428
33811
  onAction(action, ev) {
33429
33812
  ev.stopPropagation();
33813
+ if (action.disabled)
33814
+ return;
33815
+ const wasOverlay = this.expanded || this.fullscreen;
33816
+ if (!wasOverlay && (action.id === 'expand' || action.id === 'fullscreen')) {
33817
+ this.overlayReturnFocus =
33818
+ ev.target instanceof Element ? ev.target.closest('button') : null;
33819
+ }
33430
33820
  const handled = this.handleWindowAction(action);
33821
+ if (wasOverlay && !this.expanded && !this.fullscreen) {
33822
+ this.restoreOverlayFocus();
33823
+ }
33431
33824
  const payload = action.payload ?? handled?.payload;
33432
33825
  const event = {
33433
33826
  id: action.id,
@@ -33480,6 +33873,16 @@ class WidgetShellComponent {
33480
33873
  }
33481
33874
  this.dragSurfaceKeydown.emit(event);
33482
33875
  }
33876
+ onAuthoringDragPointerDown(event) {
33877
+ if (!this.dragSurfaceInteractive)
33878
+ return;
33879
+ this.dragSurfacePointerDown.emit(event);
33880
+ }
33881
+ onAuthoringDragKeydown(event) {
33882
+ if (!this.dragSurfaceInteractive)
33883
+ return;
33884
+ this.dragSurfaceKeydown.emit(event);
33885
+ }
33483
33886
  stopIfExpanded(ev) {
33484
33887
  if (this.expanded || this.fullscreen)
33485
33888
  ev.stopPropagation();
@@ -33487,6 +33890,27 @@ class WidgetShellComponent {
33487
33890
  closeOverlay() {
33488
33891
  this.expanded = false;
33489
33892
  this.fullscreen = false;
33893
+ this.restoreOverlayFocus();
33894
+ }
33895
+ onOverlayEscape(event) {
33896
+ if (event.defaultPrevented || (!this.expanded && !this.fullscreen))
33897
+ return;
33898
+ // Connected overlays such as MatSelect consume Escape at the CDK body
33899
+ // dispatcher. A task (not a microtask between native listeners) lets the
33900
+ // complete event propagation finish before treating it as a shell command.
33901
+ setTimeout(() => {
33902
+ if (this.destroyRef.destroyed || event.defaultPrevented || (!this.expanded && !this.fullscreen))
33903
+ return;
33904
+ event.preventDefault();
33905
+ this.closeOverlay();
33906
+ this.changeDetector.markForCheck();
33907
+ }, 0);
33908
+ }
33909
+ restoreOverlayFocus() {
33910
+ if (this.overlayReturnFocus?.isConnected) {
33911
+ this.overlayReturnFocus.focus({ preventScroll: true });
33912
+ }
33913
+ this.overlayReturnFocus = null;
33490
33914
  }
33491
33915
  moreActionsLabel() {
33492
33916
  return this.t('controls.moreActions', 'Mais ações');
@@ -33598,9 +34022,19 @@ class WidgetShellComponent {
33598
34022
  return this.i18n.t(key, undefined, fallback, WIDGET_SHELL_I18N_NAMESPACE);
33599
34023
  }
33600
34024
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
33601
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellComponent, isStandalone: true, selector: "praxis-widget-shell", inputs: { shell: "shell", context: "context", dragSurfaceEnabled: "dragSurfaceEnabled", dragSurfaceLabel: "dragSurfaceLabel" }, outputs: { action: "action", dragSurfacePointerDown: "dragSurfacePointerDown", dragSurfaceKeydown: "dragSurfaceKeydown" }, host: { properties: { "class.pdx-widget-shell-collapsed": "this.hostCollapsed" } }, providers: [providePraxisI18nConfig(WIDGET_SHELL_I18N_CONFIG)], queries: [{ propertyName: "loader", first: true, predicate: DynamicWidgetLoaderDirective, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
34025
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: WidgetShellComponent, isStandalone: true, selector: "praxis-widget-shell", inputs: { shell: "shell", context: "context", dragSurfaceEnabled: "dragSurfaceEnabled", dragSurfaceLabel: "dragSurfaceLabel" }, outputs: { action: "action", dragSurfacePointerDown: "dragSurfacePointerDown", dragSurfaceKeydown: "dragSurfaceKeydown" }, host: { listeners: { "keydown.escape": "onOverlayEscape($event)", "focusin": "revealStickyHeaderFocus($event)" }, properties: { "class.pdx-widget-shell-collapsed": "this.hostCollapsed" } }, providers: [providePraxisI18nConfig(WIDGET_SHELL_I18N_CONFIG)], queries: [{ propertyName: "loader", first: true, predicate: DynamicWidgetLoaderDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "overflowTrigger", first: true, predicate: MatMenuTrigger, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
34026
+ @if (expanded || fullscreen) {
34027
+ <div
34028
+ class="pdx-shell-overlay-surface"
34029
+ [class.expanded]="expanded"
34030
+ [class.fullscreen]="fullscreen"
34031
+ [style.border-radius]="fullscreen ? null : appearance?.card?.borderRadius || null"
34032
+ aria-hidden="true"
34033
+ ></div>
34034
+ }
33602
34035
  <section
33603
34036
  class="pdx-shell"
34037
+ [attr.tabindex]="expanded || fullscreen ? -1 : null"
33604
34038
  [class.no-shell]="!shellEnabled"
33605
34039
  [class.dashboard]="shellEnabled"
33606
34040
  [class.collapsed]="collapsed"
@@ -33608,6 +34042,8 @@ class WidgetShellComponent {
33608
34042
  [class.fullscreen]="fullscreen"
33609
34043
  [class.body-fill]="shell?.bodyLayout === 'fill'"
33610
34044
  [class.body-scroll]="shell?.bodyLayout === 'scroll'"
34045
+ [class.pdx-shell--sticky-header]="stickyHeaderActive"
34046
+ [class.pdx-shell--scroll-recovery]="scrollRecovery() && !collapsed && !expanded && !fullscreen"
33611
34047
  [style.--pdx-shell-card-bg]="appearance?.card?.background || null"
33612
34048
  [style.--pdx-shell-card-border]="appearance?.card?.borderColor || null"
33613
34049
  [style.--pdx-shell-card-radius]="appearance?.card?.borderRadius || null"
@@ -33636,6 +34072,7 @@ class WidgetShellComponent {
33636
34072
  @if (showHeader) {
33637
34073
  <header
33638
34074
  class="pdx-shell-header"
34075
+ [style.top]="stickyAncestorTop"
33639
34076
  [class.pdx-shell-header--drag-enabled]="dragSurfaceInteractive"
33640
34077
  [attr.aria-label]="dragSurfaceInteractive ? dragSurfaceLabel : null"
33641
34078
  [attr.tabindex]="dragSurfaceInteractive ? 0 : null"
@@ -33647,11 +34084,11 @@ class WidgetShellComponent {
33647
34084
  <mat-icon [praxisIcon]="shell?.icon"></mat-icon>
33648
34085
  }
33649
34086
  <div class="pdx-shell-text">
33650
- <div class="pdx-shell-title-text">
34087
+ <div class="pdx-shell-title-text" [matTooltip]="shellText(shell?.title)">
33651
34088
  {{ shellText(shell?.title) }}
33652
34089
  </div>
33653
34090
  @if (shell?.subtitle) {
33654
- <div class="pdx-shell-subtitle">
34091
+ <div class="pdx-shell-subtitle" [matTooltip]="shellText(shell?.subtitle)">
33655
34092
  {{ shellText(shell?.subtitle) }}
33656
34093
  </div>
33657
34094
  }
@@ -33765,6 +34202,7 @@ class WidgetShellComponent {
33765
34202
  @for (action of overflowHeaderActions; track action.id) {
33766
34203
  <button
33767
34204
  mat-menu-item
34205
+ [disabled]="action.disabled"
33768
34206
  [attr.aria-pressed]="
33769
34207
  action.pressed == null ? null : action.pressed
33770
34208
  "
@@ -33772,7 +34210,7 @@ class WidgetShellComponent {
33772
34210
  [attr.aria-expanded]="
33773
34211
  action.ariaExpanded == null ? null : action.ariaExpanded
33774
34212
  "
33775
- (click)="onAction(action, $event)"
34213
+ (click)="onOverflowAction(action, $event)"
33776
34214
  >
33777
34215
  @if (displayActionIcon(action); as actionIcon) {
33778
34216
  <mat-icon [praxisIcon]="actionIcon"></mat-icon>
@@ -33782,6 +34220,18 @@ class WidgetShellComponent {
33782
34220
  }
33783
34221
  </mat-menu>
33784
34222
  </header>
34223
+ } @else if (dragSurfaceInteractive) {
34224
+ <button
34225
+ class="pdx-shell-authoring-drag"
34226
+ praxisIconButton="ms:drag_indicator"
34227
+ size="compact"
34228
+ type="button"
34229
+ [matTooltip]="dragSurfaceLabel || ''"
34230
+ matTooltipPosition="above"
34231
+ [attr.aria-label]="dragSurfaceLabel"
34232
+ (pointerdown)="onAuthoringDragPointerDown($event)"
34233
+ (keydown)="onAuthoringDragKeydown($event)"
34234
+ ></button>
33785
34235
  }
33786
34236
  <div class="pdx-shell-body" [class.hidden]="collapsed">
33787
34237
  <ng-content></ng-content>
@@ -33790,7 +34240,7 @@ class WidgetShellComponent {
33790
34240
  @if (expanded || fullscreen) {
33791
34241
  <div class="pdx-shell-backdrop" (click)="closeOverlay()"></div>
33792
34242
  }
33793
- `, isInline: true, styles: [":host{display:block;height:100%}:host(.pdx-widget-shell-collapsed){height:auto}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var( --pdx-shell-card-bg, var( --pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low) ) );border:1px solid var( --pdx-shell-card-border, var( --pdx-dashboard-card-border, var(--md-sys-color-outline-variant) ) );border-radius:var(--pdx-shell-card-radius, 12px);box-shadow:var( --pdx-shell-card-shadow, 0 4px 12px rgba(15, 23, 42, .06) );overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:10px;padding:8px 10px 7px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:8px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 13px);line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 11px);color:var( --pdx-shell-subtitle-color, var(--md-sys-color-on-surface-variant, currentColor) );white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:4px}.pdx-shell-window-actions{margin-left:auto}@media(max-width:640px){.pdx-shell-header,.pdx-shell-title{align-items:flex-start}.pdx-shell-text{display:grid;gap:2px}.pdx-shell-title-text,.pdx-shell-subtitle{display:-webkit-box;white-space:normal;overflow-wrap:anywhere;-webkit-box-orient:vertical}.pdx-shell-title-text,.pdx-shell-subtitle{-webkit-line-clamp:2}.pdx-shell-actions{flex:0 0 auto}}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-shell-action--pressed:not(.praxis-icon-button){color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 8px 10px 10px 10px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed{height:auto}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.body-fill .pdx-shell-body,.pdx-shell.body-scroll .pdx-shell-body,.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.collapsed .pdx-shell-body{display:none}.pdx-shell.body-fill .pdx-shell-body{overflow:hidden}.pdx-shell.body-scroll .pdx-shell-body{overflow:auto}.pdx-shell.body-fill .pdx-shell-body>*,.pdx-shell.body-scroll .pdx-shell-body>*,.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;inset:0;width:auto;height:auto;transform:none;border-radius:0;z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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 });
34243
+ `, isInline: true, styles: [":host{display:block;height:100%}:host(.pdx-widget-shell-collapsed){height:auto}.pdx-shell{box-sizing:border-box;position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var( --pdx-shell-card-bg, var( --pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low) ) );border:1px solid var( --pdx-shell-card-border, var( --pdx-dashboard-card-border, var(--md-sys-color-outline-variant) ) );border-radius:var(--pdx-shell-card-radius, 12px);box-shadow:var( --pdx-shell-card-shadow, 0 4px 12px rgba(15, 23, 42, .06) );overflow:hidden}.pdx-shell-header{flex-shrink:0;display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 10px 7px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-authoring-drag{flex:0 0 auto;align-self:flex-start;margin:4px 6px 0;z-index:2;cursor:grab;touch-action:none}.pdx-shell-authoring-drag:active{cursor:grabbing}.pdx-shell-title{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 min(14rem,50%);max-width:100%;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{flex:0 0 auto;color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0;flex:1}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 13px);line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 11px);color:var( --pdx-shell-subtitle-color, var(--md-sys-color-on-surface-variant, currentColor) );white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;flex-wrap:wrap;align-items:center;gap:4px;max-width:100%}.pdx-shell-actions{margin-left:auto;min-width:0}.pdx-shell-actions>button{max-width:100%}.pdx-shell-window-actions{margin-left:auto}.pdx-shell-text{display:grid;gap:2px}.pdx-shell-title-text,.pdx-shell-subtitle{display:-webkit-box;white-space:normal;overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:2}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-shell-action--pressed:not(.praxis-icon-button){color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell.dashboard.pdx-shell--sticky-header{overflow:clip}.pdx-shell--sticky-header>.pdx-shell-header{position:sticky;top:var(--pdx-shell-sticky-header-offset, 0px);z-index:2;isolation:isolate;background:var(--md-sys-color-surface-container)}.pdx-shell--sticky-header>.pdx-shell-header:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background:var(--pdx-shell-header-bg, transparent)}.pdx-shell--sticky-header>.pdx-shell-body{isolation:isolate}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 8px 10px 10px 10px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell>.pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed{height:auto}.pdx-shell.collapsed>.pdx-shell-header{border-bottom-color:transparent}.pdx-shell.body-fill>.pdx-shell-body,.pdx-shell.body-scroll>.pdx-shell-body,.pdx-shell.expanded>.pdx-shell-body,.pdx-shell.fullscreen>.pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.collapsed>.pdx-shell-body{display:none}.pdx-shell.body-fill>.pdx-shell-body{overflow:hidden}.pdx-shell.body-scroll>.pdx-shell-body{overflow:auto}.pdx-shell.body-scroll.pdx-shell--scroll-recovery{overflow:auto}.pdx-shell.body-scroll.pdx-shell--scroll-recovery>.pdx-shell-body{flex:0 0 auto;overflow:visible}.pdx-shell.body-fill>.pdx-shell-body>*,.pdx-shell.body-scroll>.pdx-shell-body>*,.pdx-shell.expanded>.pdx-shell-body>*,.pdx-shell.fullscreen>.pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded,.pdx-shell-overlay-surface.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen,.pdx-shell-overlay-surface.fullscreen{position:fixed;inset:0;width:auto;height:auto;transform:none;border-radius:0;z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-overlay-surface{background:var(--md-sys-color-surface);border-radius:var(--pdx-dashboard-card-radius, 16px);pointer-events:none}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { 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 });
33794
34244
  }
33795
34245
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellComponent, decorators: [{
33796
34246
  type: Component,
@@ -33802,9 +34252,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33802
34252
  MatTooltipModule,
33803
34253
  PraxisIconButtonComponent,
33804
34254
  PraxisIconDirective,
33805
- ], providers: [providePraxisI18nConfig(WIDGET_SHELL_I18N_CONFIG)], template: `
34255
+ ], providers: [providePraxisI18nConfig(WIDGET_SHELL_I18N_CONFIG)], host: {
34256
+ '(keydown.escape)': 'onOverlayEscape($event)',
34257
+ '(focusin)': 'revealStickyHeaderFocus($event)',
34258
+ }, template: `
34259
+ @if (expanded || fullscreen) {
34260
+ <div
34261
+ class="pdx-shell-overlay-surface"
34262
+ [class.expanded]="expanded"
34263
+ [class.fullscreen]="fullscreen"
34264
+ [style.border-radius]="fullscreen ? null : appearance?.card?.borderRadius || null"
34265
+ aria-hidden="true"
34266
+ ></div>
34267
+ }
33806
34268
  <section
33807
34269
  class="pdx-shell"
34270
+ [attr.tabindex]="expanded || fullscreen ? -1 : null"
33808
34271
  [class.no-shell]="!shellEnabled"
33809
34272
  [class.dashboard]="shellEnabled"
33810
34273
  [class.collapsed]="collapsed"
@@ -33812,6 +34275,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33812
34275
  [class.fullscreen]="fullscreen"
33813
34276
  [class.body-fill]="shell?.bodyLayout === 'fill'"
33814
34277
  [class.body-scroll]="shell?.bodyLayout === 'scroll'"
34278
+ [class.pdx-shell--sticky-header]="stickyHeaderActive"
34279
+ [class.pdx-shell--scroll-recovery]="scrollRecovery() && !collapsed && !expanded && !fullscreen"
33815
34280
  [style.--pdx-shell-card-bg]="appearance?.card?.background || null"
33816
34281
  [style.--pdx-shell-card-border]="appearance?.card?.borderColor || null"
33817
34282
  [style.--pdx-shell-card-radius]="appearance?.card?.borderRadius || null"
@@ -33840,6 +34305,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33840
34305
  @if (showHeader) {
33841
34306
  <header
33842
34307
  class="pdx-shell-header"
34308
+ [style.top]="stickyAncestorTop"
33843
34309
  [class.pdx-shell-header--drag-enabled]="dragSurfaceInteractive"
33844
34310
  [attr.aria-label]="dragSurfaceInteractive ? dragSurfaceLabel : null"
33845
34311
  [attr.tabindex]="dragSurfaceInteractive ? 0 : null"
@@ -33851,11 +34317,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33851
34317
  <mat-icon [praxisIcon]="shell?.icon"></mat-icon>
33852
34318
  }
33853
34319
  <div class="pdx-shell-text">
33854
- <div class="pdx-shell-title-text">
34320
+ <div class="pdx-shell-title-text" [matTooltip]="shellText(shell?.title)">
33855
34321
  {{ shellText(shell?.title) }}
33856
34322
  </div>
33857
34323
  @if (shell?.subtitle) {
33858
- <div class="pdx-shell-subtitle">
34324
+ <div class="pdx-shell-subtitle" [matTooltip]="shellText(shell?.subtitle)">
33859
34325
  {{ shellText(shell?.subtitle) }}
33860
34326
  </div>
33861
34327
  }
@@ -33969,6 +34435,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33969
34435
  @for (action of overflowHeaderActions; track action.id) {
33970
34436
  <button
33971
34437
  mat-menu-item
34438
+ [disabled]="action.disabled"
33972
34439
  [attr.aria-pressed]="
33973
34440
  action.pressed == null ? null : action.pressed
33974
34441
  "
@@ -33976,7 +34443,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33976
34443
  [attr.aria-expanded]="
33977
34444
  action.ariaExpanded == null ? null : action.ariaExpanded
33978
34445
  "
33979
- (click)="onAction(action, $event)"
34446
+ (click)="onOverflowAction(action, $event)"
33980
34447
  >
33981
34448
  @if (displayActionIcon(action); as actionIcon) {
33982
34449
  <mat-icon [praxisIcon]="actionIcon"></mat-icon>
@@ -33986,6 +34453,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33986
34453
  }
33987
34454
  </mat-menu>
33988
34455
  </header>
34456
+ } @else if (dragSurfaceInteractive) {
34457
+ <button
34458
+ class="pdx-shell-authoring-drag"
34459
+ praxisIconButton="ms:drag_indicator"
34460
+ size="compact"
34461
+ type="button"
34462
+ [matTooltip]="dragSurfaceLabel || ''"
34463
+ matTooltipPosition="above"
34464
+ [attr.aria-label]="dragSurfaceLabel"
34465
+ (pointerdown)="onAuthoringDragPointerDown($event)"
34466
+ (keydown)="onAuthoringDragKeydown($event)"
34467
+ ></button>
33989
34468
  }
33990
34469
  <div class="pdx-shell-body" [class.hidden]="collapsed">
33991
34470
  <ng-content></ng-content>
@@ -33994,8 +34473,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
33994
34473
  @if (expanded || fullscreen) {
33995
34474
  <div class="pdx-shell-backdrop" (click)="closeOverlay()"></div>
33996
34475
  }
33997
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;height:100%}:host(.pdx-widget-shell-collapsed){height:auto}.pdx-shell{position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var( --pdx-shell-card-bg, var( --pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low) ) );border:1px solid var( --pdx-shell-card-border, var( --pdx-dashboard-card-border, var(--md-sys-color-outline-variant) ) );border-radius:var(--pdx-shell-card-radius, 12px);box-shadow:var( --pdx-shell-card-shadow, 0 4px 12px rgba(15, 23, 42, .06) );overflow:hidden}.pdx-shell-header{display:flex;align-items:center;gap:10px;padding:8px 10px 7px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-title{display:flex;align-items:center;gap:8px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 13px);line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 11px);color:var( --pdx-shell-subtitle-color, var(--md-sys-color-on-surface-variant, currentColor) );white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;align-items:center;gap:4px}.pdx-shell-window-actions{margin-left:auto}@media(max-width:640px){.pdx-shell-header,.pdx-shell-title{align-items:flex-start}.pdx-shell-text{display:grid;gap:2px}.pdx-shell-title-text,.pdx-shell-subtitle{display:-webkit-box;white-space:normal;overflow-wrap:anywhere;-webkit-box-orient:vertical}.pdx-shell-title-text,.pdx-shell-subtitle{-webkit-line-clamp:2}.pdx-shell-actions{flex:0 0 auto}}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-shell-action--pressed:not(.praxis-icon-button){color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 8px 10px 10px 10px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell .pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed{height:auto}.pdx-shell.collapsed .pdx-shell-header{border-bottom-color:transparent}.pdx-shell.body-fill .pdx-shell-body,.pdx-shell.body-scroll .pdx-shell-body,.pdx-shell.expanded .pdx-shell-body,.pdx-shell.fullscreen .pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.collapsed .pdx-shell-body{display:none}.pdx-shell.body-fill .pdx-shell-body{overflow:hidden}.pdx-shell.body-scroll .pdx-shell-body{overflow:auto}.pdx-shell.body-fill .pdx-shell-body>*,.pdx-shell.body-scroll .pdx-shell-body>*,.pdx-shell.expanded .pdx-shell-body>*,.pdx-shell.fullscreen .pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen{position:fixed;inset:0;width:auto;height:auto;transform:none;border-radius:0;z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"] }]
33998
- }], propDecorators: { hostCollapsed: [{
34476
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;height:100%}:host(.pdx-widget-shell-collapsed){height:auto}.pdx-shell{box-sizing:border-box;position:relative;height:100%;display:flex;flex-direction:column}.pdx-shell.no-shell{background:transparent;border:none;border-radius:0;box-shadow:none}.pdx-shell.dashboard{background:var( --pdx-shell-card-bg, var( --pdx-dashboard-card-bg, var(--md-sys-color-surface-container-low) ) );border:1px solid var( --pdx-shell-card-border, var( --pdx-dashboard-card-border, var(--md-sys-color-outline-variant) ) );border-radius:var(--pdx-shell-card-radius, 12px);box-shadow:var( --pdx-shell-card-shadow, 0 4px 12px rgba(15, 23, 42, .06) );overflow:hidden}.pdx-shell-header{flex-shrink:0;display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 10px 7px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var( --pdx-shell-header-bg, var(--md-sys-color-surface-container) )}.pdx-shell-header--drag-enabled{cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.pdx-shell-header--drag-enabled:active{cursor:grabbing}.pdx-shell-header--drag-enabled:focus-visible{outline:2px solid color-mix(in srgb,var(--md-sys-color-primary) 72%,white 28%);outline-offset:-2px}.pdx-shell-authoring-drag{flex:0 0 auto;align-self:flex-start;margin:4px 6px 0;z-index:2;cursor:grab;touch-action:none}.pdx-shell-authoring-drag:active{cursor:grabbing}.pdx-shell-title{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 min(14rem,50%);max-width:100%;color:var(--pdx-shell-title-color, inherit)}.pdx-shell-title mat-icon{flex:0 0 auto;color:var(--pdx-shell-icon-color, currentColor)}.pdx-shell-text{min-width:0;flex:1}.pdx-shell-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 13px);line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-subtitle{font-size:var(--pdx-shell-subtitle-size, 11px);color:var( --pdx-shell-subtitle-color, var(--md-sys-color-on-surface-variant, currentColor) );white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-shell-actions,.pdx-shell-window-actions{display:flex;flex-wrap:wrap;align-items:center;gap:4px;max-width:100%}.pdx-shell-actions{margin-left:auto;min-width:0}.pdx-shell-actions>button{max-width:100%}.pdx-shell-window-actions{margin-left:auto}.pdx-shell-text{display:grid;gap:2px}.pdx-shell-title-text,.pdx-shell-subtitle{display:-webkit-box;white-space:normal;overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:2}.pdx-action-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-action-text{padding:0 8px}.pdx-shell-action--pressed:not(.praxis-icon-button){color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}.pdx-action-label{font-size:12px;font-weight:500}.pdx-shell.dashboard.pdx-shell--sticky-header{overflow:clip}.pdx-shell--sticky-header>.pdx-shell-header{position:sticky;top:var(--pdx-shell-sticky-header-offset, 0px);z-index:2;isolation:isolate;background:var(--md-sys-color-surface-container)}.pdx-shell--sticky-header>.pdx-shell-header:before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;background:var(--pdx-shell-header-bg, transparent)}.pdx-shell--sticky-header>.pdx-shell-body{isolation:isolate}.pdx-shell-body{flex:1;min-height:0;padding:var(--pdx-shell-body-padding, 8px 10px 10px 10px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit)}.pdx-shell.no-shell>.pdx-shell-body{padding:0}.pdx-shell-body.hidden{display:none}.pdx-shell.collapsed{height:auto}.pdx-shell.collapsed>.pdx-shell-header{border-bottom-color:transparent}.pdx-shell.body-fill>.pdx-shell-body,.pdx-shell.body-scroll>.pdx-shell-body,.pdx-shell.expanded>.pdx-shell-body,.pdx-shell.fullscreen>.pdx-shell-body{overflow:auto;display:flex;flex-direction:column;min-height:0}.pdx-shell.collapsed>.pdx-shell-body{display:none}.pdx-shell.body-fill>.pdx-shell-body{overflow:hidden}.pdx-shell.body-scroll>.pdx-shell-body{overflow:auto}.pdx-shell.body-scroll.pdx-shell--scroll-recovery{overflow:auto}.pdx-shell.body-scroll.pdx-shell--scroll-recovery>.pdx-shell-body{flex:0 0 auto;overflow:visible}.pdx-shell.body-fill>.pdx-shell-body>*,.pdx-shell.body-scroll>.pdx-shell-body>*,.pdx-shell.expanded>.pdx-shell-body>*,.pdx-shell.fullscreen>.pdx-shell-body>*{flex:1 1 auto;min-height:0;width:100%}.pdx-shell.expanded,.pdx-shell-overlay-surface.expanded{position:fixed;top:10vh;left:50%;width:min(920px,92vw);height:min(640px,82vh);transform:translate(-50%);z-index:var(--praxis-layer-widget-shell-expanded, 1290);box-shadow:var(--mat-elevation-level8)}.pdx-shell.fullscreen,.pdx-shell-overlay-surface.fullscreen{position:fixed;inset:0;width:auto;height:auto;transform:none;border-radius:0;z-index:var(--praxis-layer-widget-shell-fullscreen, 1291);box-shadow:var(--mat-elevation-level8)}.pdx-shell-overlay-surface{background:var(--md-sys-color-surface);border-radius:var(--pdx-dashboard-card-radius, 16px);pointer-events:none}.pdx-shell-backdrop{position:fixed;inset:0;z-index:var(--praxis-layer-widget-shell-backdrop, 1280);background:#0000008c;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}\n"] }]
34477
+ }], ctorParameters: () => [], propDecorators: { hostCollapsed: [{
33999
34478
  type: HostBinding,
34000
34479
  args: ['class.pdx-widget-shell-collapsed']
34001
34480
  }], shell: [{
@@ -34012,7 +34491,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
34012
34491
  type: Output
34013
34492
  }], dragSurfaceKeydown: [{
34014
34493
  type: Output
34015
- }], loader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DynamicWidgetLoaderDirective), { isSignal: true }] }] } });
34494
+ }], loader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DynamicWidgetLoaderDirective), { isSignal: true }] }], overflowTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatMenuTrigger), { isSignal: true }] }] } });
34016
34495
 
34017
34496
  class PraxisResourceIdentityComponent {
34018
34497
  identity = null;
@@ -38449,7 +38928,328 @@ function clone(value) {
38449
38928
  return JSON.parse(JSON.stringify(value));
38450
38929
  }
38451
38930
 
38931
+ const DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE = 'dynamicWidgetPage';
38932
+ const DYNAMIC_WIDGET_PAGE_I18N_CONFIG = {
38933
+ namespaces: {
38934
+ [DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE]: {
38935
+ 'pt-BR': {
38936
+ 'size.title': 'Tamanho…',
38937
+ 'size.presets': 'Larguras da grade',
38938
+ 'size.presetLabel': '{{fraction}} da grade: {{columns}} colunas',
38939
+ 'size.presetLabelSingle': '{{fraction}} da grade: 1 coluna',
38940
+ 'size.presetHelp': 'Fração da grade · número de colunas',
38941
+ 'size.undo': 'Desfazer último redimensionamento',
38942
+ 'size.undoHelp': 'Restaura o widget e os vizinhos alterados no último ajuste desta sessão.',
38943
+ 'size.undoBlocked': 'O layout ou seus limites mudaram depois. O ajuste anterior não pode ser restaurado com segurança.',
38944
+ 'size.undoDone': 'Último redimensionamento desfeito, incluindo os vizinhos afetados.',
38945
+ 'size.width': 'Largura',
38946
+ 'size.height': 'Altura',
38947
+ 'size.columns': 'Colunas da grade',
38948
+ 'size.rows': 'Linhas da grade',
38949
+ 'size.pixels': 'Pixels',
38950
+ 'size.anchor': 'Canto que permanece fixo',
38951
+ 'size.topLeft': 'Superior esquerdo',
38952
+ 'size.topRight': 'Superior direito',
38953
+ 'size.bottomLeft': 'Inferior esquerdo',
38954
+ 'size.bottomRight': 'Inferior direito',
38955
+ 'size.help': 'A prévia inclui os vizinhos afetados. Aplicar confirma; Cancelar descarta o ajuste.',
38956
+ 'size.apply': 'Aplicar',
38957
+ 'size.invalid': 'Informe números inteiros maiores que zero.',
38958
+ 'size.unavailable': 'Esse tamanho excede os limites ou o espaço disponível.',
38959
+ 'size.select': 'Widget {{widget}}. Tab acessa os controles; Esc volta à seleção.',
38960
+ 'size.scope.base': 'Layout base · compartilhado pelos dispositivos que o herdam',
38961
+ 'size.scope.desktop': 'Layout de desktop',
38962
+ 'size.scope.tablet': 'Layout de tablet',
38963
+ 'size.scope.mobile': 'Layout de celular',
38964
+ 'heightValidation.measureFirst': 'Use altura Automática para aplicar a nova geometria; depois reabra o editor para definir a altura ajustável.',
38965
+ 'heightValidation.minimum': 'Use pelo menos {{height}}px nesta largura. Ajuste a altura/máximo ou use Automática.',
38966
+ 'announcements.contentSize': '{{action}}. Largura {{columns}} colunas, altura {{height}} pixels, recuo superior {{top}} pixels.',
38967
+ 'resizeFeedback.valid': 'Ajuste disponível',
38968
+ 'resizeFeedback.minimum': 'Tamanho mínimo atingido',
38969
+ 'resizeFeedback.blocked': 'Limite excedido — prévia limitada ao espaço disponível',
38970
+ 'resizeFeedback.committedLimit': 'Layout ajustado até o limite permitido',
38971
+ 'resizeFeedback.unchanged': 'Tamanho mantido — nenhum ajuste aplicado',
38972
+ 'controls.pageSettings': 'Configurar página',
38973
+ 'controls.componentSettings': 'Configurar conteúdo',
38974
+ 'controls.componentSettingsTooltip': 'Abrir configurações do componente',
38975
+ 'controls.widgetSettings': 'Configurar widget',
38976
+ 'controls.widgetSettingsTooltip': 'Abrir configurações do widget',
38977
+ 'controls.widgetAssistant': 'Abrir assistente do widget',
38978
+ 'controls.widgetAssistantTooltip': 'Abrir o assistente Praxis com contexto deste widget',
38979
+ 'controls.widgetRemove': 'Excluir widget',
38980
+ 'controls.widgetRemoveTooltip': 'Excluir este widget e limpar conexões vinculadas',
38981
+ 'controls.widgetRemoveConfirmTitle': 'Excluir widget?',
38982
+ 'controls.widgetRemoveConfirmMessage': 'O widget "{{widget}}" será removido da página. Conexões e posições vinculadas também serão limpas.',
38983
+ 'controls.widgetRemoveConfirm': 'Excluir widget',
38984
+ 'controls.cancel': 'Cancelar',
38985
+ 'controls.cardSettings': 'Configurar card',
38986
+ 'controls.moreWidgetActions': 'Mais ações do widget',
38987
+ 'controls.widgetContextToolbar': 'Ações do widget {{widget}}',
38988
+ 'controls.widgetContextTooltip': 'Editando {{widget}}',
38989
+ 'controls.widgetLayoutToolbar': 'Ações de layout do widget',
38990
+ 'controls.dragWidget': 'Arrastar widget',
38991
+ 'controls.resizeWidget': 'Redimensionar widget',
38992
+ 'controls.resizeAlternative': 'Controles alternativos de tamanho',
38993
+ 'controls.resizeSelect': 'Selecione um widget',
38994
+ 'controls.resizeClose': 'Fechar controles de tamanho',
38995
+ 'controls.resizeContextHelp': 'Arraste um controle ou use as setas do teclado. Esc fecha os controles.',
38996
+ 'controls.resizeHelp': 'Selecione o widget. Arraste uma borda ou canto pelos botões, ou use as setas do teclado.',
38997
+ 'controls.resize.north': 'Redimensionar widget pela borda superior',
38998
+ 'controls.resize.south': 'Redimensionar widget pela borda inferior',
38999
+ 'controls.resize.east': 'Redimensionar widget pela borda direita',
39000
+ 'controls.resize.west': 'Redimensionar widget pela borda esquerda',
39001
+ 'controls.resize.north-east': 'Redimensionar widget pelo canto superior direito',
39002
+ 'controls.resize.north-west': 'Redimensionar widget pelo canto superior esquerdo',
39003
+ 'controls.resize.south-east': 'Redimensionar widget pelo canto inferior direito',
39004
+ 'controls.resize.south-west': 'Redimensionar widget pelo canto inferior esquerdo',
39005
+ 'announcements.moved': 'Widget movido: {{widget}}',
39006
+ 'announcements.swapped': 'Widget trocado com {{target}}: {{widget}}',
39007
+ 'announcements.resized': 'Widget redimensionado: {{widget}}',
39008
+ 'announcements.interactionCancelled': 'Alteração de layout cancelada.',
39009
+ 'announcements.position': '{{action}} para coluna {{col}}, linha {{row}}, largura {{colSpan}}, altura {{rowSpan}}.',
39010
+ 'announcements.dragBlocked': 'Movimento bloqueado: o widget colidiria com outro item.',
39011
+ 'announcements.resizeBlocked': 'Redimensionamento bloqueado: o widget colidiria com outro item.',
39012
+ },
39013
+ 'en-US': {
39014
+ 'size.title': 'Size…',
39015
+ 'size.presets': 'Grid widths',
39016
+ 'size.presetLabel': '{{fraction}} of the grid: {{columns}} columns',
39017
+ 'size.presetLabelSingle': '{{fraction}} of the grid: 1 column',
39018
+ 'size.presetHelp': 'Grid fraction · number of columns',
39019
+ 'size.undo': 'Undo last resize',
39020
+ 'size.undoHelp': 'Restores the widget and neighbors changed by the last resize in this session.',
39021
+ 'size.undoBlocked': 'The layout or its limits changed afterwards. The previous size cannot be safely restored.',
39022
+ 'size.undoDone': 'Last resize undone, including affected neighbors.',
39023
+ 'size.width': 'Width',
39024
+ 'size.height': 'Height',
39025
+ 'size.columns': 'Grid columns',
39026
+ 'size.rows': 'Grid rows',
39027
+ 'size.pixels': 'Pixels',
39028
+ 'size.anchor': 'Corner to keep fixed',
39029
+ 'size.topLeft': 'Top left',
39030
+ 'size.topRight': 'Top right',
39031
+ 'size.bottomLeft': 'Bottom left',
39032
+ 'size.bottomRight': 'Bottom right',
39033
+ 'size.help': 'The preview includes affected neighbors. Apply confirms; Cancel discards the change.',
39034
+ 'size.apply': 'Apply',
39035
+ 'size.invalid': 'Enter whole numbers greater than zero.',
39036
+ 'size.unavailable': 'This size exceeds the limits or available space.',
39037
+ 'size.select': 'Widget {{widget}}. Tab reaches controls; Escape returns to selection.',
39038
+ 'size.scope.base': 'Base layout · shared by devices that inherit it',
39039
+ 'size.scope.desktop': 'Desktop layout',
39040
+ 'size.scope.tablet': 'Tablet layout',
39041
+ 'size.scope.mobile': 'Mobile layout',
39042
+ 'heightValidation.measureFirst': 'Use Automatic height to apply the new geometry, then reopen the editor to set an adjustable height.',
39043
+ 'heightValidation.minimum': 'Use at least {{height}}px at this width. Adjust the height/maximum, or use Automatic.',
39044
+ 'announcements.contentSize': '{{action}}. Width {{columns}} columns, height {{height}} pixels, top inset {{top}} pixels.',
39045
+ 'resizeFeedback.valid': 'Resize available',
39046
+ 'resizeFeedback.minimum': 'Minimum size reached',
39047
+ 'resizeFeedback.blocked': 'Limit exceeded — preview constrained to available space',
39048
+ 'resizeFeedback.committedLimit': 'Layout resized to the allowed limit',
39049
+ 'resizeFeedback.unchanged': 'Size unchanged — no adjustment applied',
39050
+ 'controls.pageSettings': 'Configure page',
39051
+ 'controls.componentSettings': 'Configure content',
39052
+ 'controls.componentSettingsTooltip': 'Open component settings',
39053
+ 'controls.widgetSettings': 'Configure widget',
39054
+ 'controls.widgetSettingsTooltip': 'Open widget settings',
39055
+ 'controls.widgetAssistant': 'Open widget assistant',
39056
+ 'controls.widgetAssistantTooltip': 'Open the Praxis assistant with this widget context',
39057
+ 'controls.widgetRemove': 'Delete widget',
39058
+ 'controls.widgetRemoveTooltip': 'Delete this widget and clean linked connections',
39059
+ 'controls.widgetRemoveConfirmTitle': 'Delete widget?',
39060
+ 'controls.widgetRemoveConfirmMessage': 'The widget "{{widget}}" will be removed from the page. Linked connections and positions will also be cleaned.',
39061
+ 'controls.widgetRemoveConfirm': 'Delete widget',
39062
+ 'controls.cancel': 'Cancel',
39063
+ 'controls.cardSettings': 'Configure card',
39064
+ 'controls.moreWidgetActions': 'More widget actions',
39065
+ 'controls.widgetContextToolbar': 'Widget {{widget}} actions',
39066
+ 'controls.widgetContextTooltip': 'Editing {{widget}}',
39067
+ 'controls.widgetLayoutToolbar': 'Widget layout actions',
39068
+ 'controls.dragWidget': 'Drag widget',
39069
+ 'controls.resizeWidget': 'Resize widget',
39070
+ 'controls.resizeAlternative': 'Alternative resize controls',
39071
+ 'controls.resizeSelect': 'Select a widget',
39072
+ 'controls.resizeClose': 'Close resize controls',
39073
+ 'controls.resizeContextHelp': 'Drag a control or use the arrow keys. Esc closes the controls.',
39074
+ 'controls.resizeHelp': 'Select the widget. Drag an edge or corner using the buttons, or use the arrow keys.',
39075
+ 'controls.resize.north': 'Resize widget from the top edge',
39076
+ 'controls.resize.south': 'Resize widget from the bottom edge',
39077
+ 'controls.resize.east': 'Resize widget from the right edge',
39078
+ 'controls.resize.west': 'Resize widget from the left edge',
39079
+ 'controls.resize.north-east': 'Resize widget from the top-right corner',
39080
+ 'controls.resize.north-west': 'Resize widget from the top-left corner',
39081
+ 'controls.resize.south-east': 'Resize widget from the bottom-right corner',
39082
+ 'controls.resize.south-west': 'Resize widget from the bottom-left corner',
39083
+ 'announcements.moved': 'Widget moved: {{widget}}',
39084
+ 'announcements.swapped': 'Widget swapped with {{target}}: {{widget}}',
39085
+ 'announcements.resized': 'Widget resized: {{widget}}',
39086
+ 'announcements.interactionCancelled': 'Layout change cancelled.',
39087
+ 'announcements.position': '{{action}} to column {{col}}, row {{row}}, width {{colSpan}}, height {{rowSpan}}.',
39088
+ 'announcements.dragBlocked': 'Move blocked: the widget would overlap another item.',
39089
+ 'announcements.resizeBlocked': 'Resize blocked: the widget would overlap another item.',
39090
+ },
39091
+ },
39092
+ },
39093
+ };
39094
+
39095
+ class CanvasSizeEditorComponent {
39096
+ columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
39097
+ hasUndo = input(false, ...(ngDevMode ? [{ debugName: "hasUndo" }] : /* istanbul ignore next */ []));
39098
+ canUndo = input(false, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
39099
+ undo = output();
39100
+ widthPresets = computed(() => [
39101
+ { numerator: 1, denominator: 3, fraction: '⅓' },
39102
+ { numerator: 1, denominator: 2, fraction: '½' },
39103
+ { numerator: 2, denominator: 3, fraction: '⅔' },
39104
+ { numerator: 1, denominator: 1, fraction: '100%' },
39105
+ ].map(preset => ({ ...preset, width: this.columns() * preset.numerator / preset.denominator }))
39106
+ .filter(preset => Number.isInteger(preset.width) && preset.width >= 1), ...(ngDevMode ? [{ debugName: "widthPresets" }] : /* istanbul ignore next */ []));
39107
+ width = input.required(...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
39108
+ height = input.required(...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
39109
+ contentRows = input(false, ...(ngDevMode ? [{ debugName: "contentRows" }] : /* istanbul ignore next */ []));
39110
+ widgetLabel = input.required(...(ngDevMode ? [{ debugName: "widgetLabel" }] : /* istanbul ignore next */ []));
39111
+ scopeLabel = input.required(...(ngDevMode ? [{ debugName: "scopeLabel" }] : /* istanbul ignore next */ []));
39112
+ error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
39113
+ preview = output();
39114
+ apply = output();
39115
+ cancel = output();
39116
+ i18n = inject(PraxisI18nService);
39117
+ destroyRef = inject(DestroyRef);
39118
+ anchors = [
39119
+ { handle: 'south-east', label: 'size.topLeft' },
39120
+ { handle: 'south-west', label: 'size.topRight' },
39121
+ { handle: 'north-east', label: 'size.bottomLeft' },
39122
+ { handle: 'north-west', label: 'size.bottomRight' },
39123
+ ];
39124
+ form = new FormGroup({
39125
+ width: new FormControl(1, { nonNullable: true, validators: [Validators.required, Validators.min(1), Validators.pattern(/^\d+$/)] }),
39126
+ height: new FormControl(1, { nonNullable: true, validators: [Validators.required, Validators.min(1), Validators.pattern(/^\d+$/)] }),
39127
+ handle: new FormControl('south-east', { nonNullable: true }),
39128
+ });
39129
+ ngOnInit() {
39130
+ this.form.reset({ width: this.width(), height: this.height(), handle: 'south-east' }, { emitEvent: false });
39131
+ this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => this.preview.emit(this.form.valid ? this.form.getRawValue() : null));
39132
+ }
39133
+ setPresetWidth(width) {
39134
+ this.form.markAsDirty();
39135
+ this.form.controls.width.setValue(width);
39136
+ }
39137
+ t(key, params) {
39138
+ return this.i18n.t(key, params, undefined, DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE);
39139
+ }
39140
+ submit() {
39141
+ if (this.form.valid && !this.error())
39142
+ this.apply.emit(this.form.getRawValue());
39143
+ }
39144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CanvasSizeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
39145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: CanvasSizeEditorComponent, isStandalone: true, selector: "praxis-canvas-size-editor", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, hasUndo: { classPropertyName: "hasUndo", publicName: "hasUndo", isSignal: true, isRequired: false, transformFunction: null }, canUndo: { classPropertyName: "canUndo", publicName: "canUndo", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: true, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: true, transformFunction: null }, contentRows: { classPropertyName: "contentRows", publicName: "contentRows", isSignal: true, isRequired: false, transformFunction: null }, widgetLabel: { classPropertyName: "widgetLabel", publicName: "widgetLabel", isSignal: true, isRequired: true, transformFunction: null }, scopeLabel: { classPropertyName: "scopeLabel", publicName: "scopeLabel", isSignal: true, isRequired: true, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { undo: "undo", preview: "preview", apply: "apply", cancel: "cancel" }, ngImport: i0, template: `
39146
+ <form [formGroup]="form" (ngSubmit)="submit()">
39147
+ <header><strong>{{ t('size.title') }}</strong><span>{{ widgetLabel() }}</span></header>
39148
+ <p class="scope">{{ scopeLabel() }}</p>
39149
+ <div class="dimensions">
39150
+ <mat-form-field appearance="outline">
39151
+ <mat-label>{{ t('size.width') }}</mat-label>
39152
+ <input matInput type="number" min="1" step="1" formControlName="width" data-testid="canvas-size-width" />
39153
+ <mat-hint>{{ t('size.columns') }}</mat-hint>
39154
+ </mat-form-field>
39155
+ <mat-form-field appearance="outline">
39156
+ <mat-label>{{ t('size.height') }}</mat-label>
39157
+ <input matInput type="number" min="1" step="1" formControlName="height" data-testid="canvas-size-height" />
39158
+ <mat-hint>{{ t(contentRows() ? 'size.pixels' : 'size.rows') }}</mat-hint>
39159
+ </mat-form-field>
39160
+ </div>
39161
+ <div class="presets" role="group" [attr.aria-label]="t('size.presets')">
39162
+ @for (preset of widthPresets(); track preset.width) {
39163
+ <button mat-stroked-button type="button" (click)="setPresetWidth(preset.width)"
39164
+ [attr.data-size-preset]="preset.fraction" [attr.aria-label]="t(preset.width === 1 ? 'size.presetLabelSingle' : 'size.presetLabel', { fraction: preset.fraction, columns: preset.width })">
39165
+ {{ preset.fraction }} · {{ preset.width }}
39166
+ </button>
39167
+ }
39168
+ </div>
39169
+ <p class="hint">{{ t('size.presetHelp') }}</p>
39170
+ <details>
39171
+ <summary>{{ t('size.anchor') }}</summary>
39172
+ <mat-radio-group formControlName="handle" [attr.aria-label]="t('size.anchor')">
39173
+ @for (anchor of anchors; track anchor.handle) {
39174
+ <mat-radio-button [value]="anchor.handle">{{ t(anchor.label) }}</mat-radio-button>
39175
+ }
39176
+ </mat-radio-group>
39177
+ </details>
39178
+ <p class="hint">{{ t('size.help') }}</p>
39179
+ @if (error() || form.invalid) {
39180
+ <p class="error" role="status">{{ error() || t('size.invalid') }}</p>
39181
+ }
39182
+ @if (hasUndo()) {
39183
+ <div class="recovery">
39184
+ <button mat-button type="button" [disabled]="!canUndo()" (click)="undo.emit()" data-testid="canvas-size-undo">{{ t('size.undo') }}</button>
39185
+ <p class="hint" role="status">{{ t(canUndo() ? 'size.undoHelp' : 'size.undoBlocked') }}</p>
39186
+ </div>
39187
+ }
39188
+ <footer>
39189
+ <button mat-button type="button" (click)="cancel.emit()">{{ t('controls.cancel') }}</button>
39190
+ <button mat-flat-button type="submit" [disabled]="form.invalid || !!error() || form.pristine">{{ t('size.apply') }}</button>
39191
+ </footer>
39192
+ </form>
39193
+ `, isInline: true, styles: [":host{display:block;box-sizing:border-box;width:min(380px,calc(100vw - 32px));max-height:calc(100dvh - 32px);overflow:auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--mat-sys-corner-medium, 12px);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}form{display:grid;gap:16px;padding:16px}header{display:grid;gap:4px;overflow-wrap:anywhere}header span,.scope,.hint{color:var(--md-sys-color-on-surface-variant);font-size:12px}p{margin:0}.dimensions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}mat-form-field{min-width:0}.presets{display:flex;flex-wrap:wrap;gap:8px}.recovery{display:grid;gap:4px;justify-items:start}mat-radio-group{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}summary{cursor:pointer;padding-block:8px}footer{display:flex;gap:8px;justify-content:flex-end}.error{color:var(--md-sys-color-error)}@media(max-width:380px){.dimensions,mat-radio-group{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i5$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i5$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
39194
+ }
39195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CanvasSizeEditorComponent, decorators: [{
39196
+ type: Component,
39197
+ args: [{ selector: 'praxis-canvas-size-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatRadioModule], template: `
39198
+ <form [formGroup]="form" (ngSubmit)="submit()">
39199
+ <header><strong>{{ t('size.title') }}</strong><span>{{ widgetLabel() }}</span></header>
39200
+ <p class="scope">{{ scopeLabel() }}</p>
39201
+ <div class="dimensions">
39202
+ <mat-form-field appearance="outline">
39203
+ <mat-label>{{ t('size.width') }}</mat-label>
39204
+ <input matInput type="number" min="1" step="1" formControlName="width" data-testid="canvas-size-width" />
39205
+ <mat-hint>{{ t('size.columns') }}</mat-hint>
39206
+ </mat-form-field>
39207
+ <mat-form-field appearance="outline">
39208
+ <mat-label>{{ t('size.height') }}</mat-label>
39209
+ <input matInput type="number" min="1" step="1" formControlName="height" data-testid="canvas-size-height" />
39210
+ <mat-hint>{{ t(contentRows() ? 'size.pixels' : 'size.rows') }}</mat-hint>
39211
+ </mat-form-field>
39212
+ </div>
39213
+ <div class="presets" role="group" [attr.aria-label]="t('size.presets')">
39214
+ @for (preset of widthPresets(); track preset.width) {
39215
+ <button mat-stroked-button type="button" (click)="setPresetWidth(preset.width)"
39216
+ [attr.data-size-preset]="preset.fraction" [attr.aria-label]="t(preset.width === 1 ? 'size.presetLabelSingle' : 'size.presetLabel', { fraction: preset.fraction, columns: preset.width })">
39217
+ {{ preset.fraction }} · {{ preset.width }}
39218
+ </button>
39219
+ }
39220
+ </div>
39221
+ <p class="hint">{{ t('size.presetHelp') }}</p>
39222
+ <details>
39223
+ <summary>{{ t('size.anchor') }}</summary>
39224
+ <mat-radio-group formControlName="handle" [attr.aria-label]="t('size.anchor')">
39225
+ @for (anchor of anchors; track anchor.handle) {
39226
+ <mat-radio-button [value]="anchor.handle">{{ t(anchor.label) }}</mat-radio-button>
39227
+ }
39228
+ </mat-radio-group>
39229
+ </details>
39230
+ <p class="hint">{{ t('size.help') }}</p>
39231
+ @if (error() || form.invalid) {
39232
+ <p class="error" role="status">{{ error() || t('size.invalid') }}</p>
39233
+ }
39234
+ @if (hasUndo()) {
39235
+ <div class="recovery">
39236
+ <button mat-button type="button" [disabled]="!canUndo()" (click)="undo.emit()" data-testid="canvas-size-undo">{{ t('size.undo') }}</button>
39237
+ <p class="hint" role="status">{{ t(canUndo() ? 'size.undoHelp' : 'size.undoBlocked') }}</p>
39238
+ </div>
39239
+ }
39240
+ <footer>
39241
+ <button mat-button type="button" (click)="cancel.emit()">{{ t('controls.cancel') }}</button>
39242
+ <button mat-flat-button type="submit" [disabled]="form.invalid || !!error() || form.pristine">{{ t('size.apply') }}</button>
39243
+ </footer>
39244
+ </form>
39245
+ `, styles: [":host{display:block;box-sizing:border-box;width:min(380px,calc(100vw - 32px));max-height:calc(100dvh - 32px);overflow:auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--mat-sys-corner-medium, 12px);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface)}form{display:grid;gap:16px;padding:16px}header{display:grid;gap:4px;overflow-wrap:anywhere}header span,.scope,.hint{color:var(--md-sys-color-on-surface-variant);font-size:12px}p{margin:0}.dimensions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}mat-form-field{min-width:0}.presets{display:flex;flex-wrap:wrap;gap:8px}.recovery{display:grid;gap:4px;justify-items:start}mat-radio-group{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}summary{cursor:pointer;padding-block:8px}footer{display:flex;gap:8px;justify-content:flex-end}.error{color:var(--md-sys-color-error)}@media(max-width:380px){.dimensions,mat-radio-group{grid-template-columns:minmax(0,1fr)}}\n"] }]
39246
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], hasUndo: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasUndo", required: false }] }], canUndo: [{ type: i0.Input, args: [{ isSignal: true, alias: "canUndo", required: false }] }], undo: [{ type: i0.Output, args: ["undo"] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: true }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: true }] }], contentRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentRows", required: false }] }], widgetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "widgetLabel", required: true }] }], scopeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "scopeLabel", required: true }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], preview: [{ type: i0.Output, args: ["preview"] }], apply: [{ type: i0.Output, args: ["apply"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
39247
+
38452
39248
  class DynamicWidgetContextToolbarComponent {
39249
+ showSize = input(false, ...(ngDevMode ? [{ debugName: "showSize" }] : /* istanbul ignore next */ []));
39250
+ sizeDisabled = input(false, ...(ngDevMode ? [{ debugName: "sizeDisabled" }] : /* istanbul ignore next */ []));
39251
+ sizeLabel = input('', ...(ngDevMode ? [{ debugName: "sizeLabel" }] : /* istanbul ignore next */ []));
39252
+ size = output();
38453
39253
  toolbarLabel = '';
38454
39254
  contextLabel = '';
38455
39255
  contextTooltip = '';
@@ -38471,7 +39271,7 @@ class DynamicWidgetContextToolbarComponent {
38471
39271
  shellSettings = new EventEmitter();
38472
39272
  remove = new EventEmitter();
38473
39273
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicWidgetContextToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
38474
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicWidgetContextToolbarComponent, isStandalone: true, selector: "praxis-dynamic-widget-context-toolbar", inputs: { toolbarLabel: { classPropertyName: "toolbarLabel", publicName: "toolbarLabel", isSignal: false, isRequired: false, transformFunction: null }, contextLabel: { classPropertyName: "contextLabel", publicName: "contextLabel", isSignal: false, isRequired: false, transformFunction: null }, contextTooltip: { classPropertyName: "contextTooltip", publicName: "contextTooltip", isSignal: false, isRequired: false, transformFunction: null }, showAssistant: { classPropertyName: "showAssistant", publicName: "showAssistant", isSignal: false, isRequired: false, transformFunction: null }, assistantLabel: { classPropertyName: "assistantLabel", publicName: "assistantLabel", isSignal: false, isRequired: false, transformFunction: null }, assistantTooltip: { classPropertyName: "assistantTooltip", publicName: "assistantTooltip", isSignal: false, isRequired: false, transformFunction: null }, showComponentSettings: { classPropertyName: "showComponentSettings", publicName: "showComponentSettings", isSignal: false, isRequired: false, transformFunction: null }, componentSettingsBusy: { classPropertyName: "componentSettingsBusy", publicName: "componentSettingsBusy", isSignal: true, isRequired: false, transformFunction: null }, componentSettingsLabel: { classPropertyName: "componentSettingsLabel", publicName: "componentSettingsLabel", isSignal: false, isRequired: false, transformFunction: null }, componentSettingsTooltip: { classPropertyName: "componentSettingsTooltip", publicName: "componentSettingsTooltip", isSignal: false, isRequired: false, transformFunction: null }, showShellSettings: { classPropertyName: "showShellSettings", publicName: "showShellSettings", isSignal: false, isRequired: false, transformFunction: null }, shellSettingsLabel: { classPropertyName: "shellSettingsLabel", publicName: "shellSettingsLabel", isSignal: false, isRequired: false, transformFunction: null }, shellSettingsTooltip: { classPropertyName: "shellSettingsTooltip", publicName: "shellSettingsTooltip", isSignal: false, isRequired: false, transformFunction: null }, showRemove: { classPropertyName: "showRemove", publicName: "showRemove", isSignal: false, isRequired: false, transformFunction: null }, moreActionsLabel: { classPropertyName: "moreActionsLabel", publicName: "moreActionsLabel", isSignal: false, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { assistant: "assistant", componentSettings: "componentSettings", shellSettings: "shellSettings", remove: "remove" }, ngImport: i0, template: `
39274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicWidgetContextToolbarComponent, isStandalone: true, selector: "praxis-dynamic-widget-context-toolbar", inputs: { showSize: { classPropertyName: "showSize", publicName: "showSize", isSignal: true, isRequired: false, transformFunction: null }, sizeDisabled: { classPropertyName: "sizeDisabled", publicName: "sizeDisabled", isSignal: true, isRequired: false, transformFunction: null }, sizeLabel: { classPropertyName: "sizeLabel", publicName: "sizeLabel", isSignal: true, isRequired: false, transformFunction: null }, toolbarLabel: { classPropertyName: "toolbarLabel", publicName: "toolbarLabel", isSignal: false, isRequired: false, transformFunction: null }, contextLabel: { classPropertyName: "contextLabel", publicName: "contextLabel", isSignal: false, isRequired: false, transformFunction: null }, contextTooltip: { classPropertyName: "contextTooltip", publicName: "contextTooltip", isSignal: false, isRequired: false, transformFunction: null }, showAssistant: { classPropertyName: "showAssistant", publicName: "showAssistant", isSignal: false, isRequired: false, transformFunction: null }, assistantLabel: { classPropertyName: "assistantLabel", publicName: "assistantLabel", isSignal: false, isRequired: false, transformFunction: null }, assistantTooltip: { classPropertyName: "assistantTooltip", publicName: "assistantTooltip", isSignal: false, isRequired: false, transformFunction: null }, showComponentSettings: { classPropertyName: "showComponentSettings", publicName: "showComponentSettings", isSignal: false, isRequired: false, transformFunction: null }, componentSettingsBusy: { classPropertyName: "componentSettingsBusy", publicName: "componentSettingsBusy", isSignal: true, isRequired: false, transformFunction: null }, componentSettingsLabel: { classPropertyName: "componentSettingsLabel", publicName: "componentSettingsLabel", isSignal: false, isRequired: false, transformFunction: null }, componentSettingsTooltip: { classPropertyName: "componentSettingsTooltip", publicName: "componentSettingsTooltip", isSignal: false, isRequired: false, transformFunction: null }, showShellSettings: { classPropertyName: "showShellSettings", publicName: "showShellSettings", isSignal: false, isRequired: false, transformFunction: null }, shellSettingsLabel: { classPropertyName: "shellSettingsLabel", publicName: "shellSettingsLabel", isSignal: false, isRequired: false, transformFunction: null }, shellSettingsTooltip: { classPropertyName: "shellSettingsTooltip", publicName: "shellSettingsTooltip", isSignal: false, isRequired: false, transformFunction: null }, showRemove: { classPropertyName: "showRemove", publicName: "showRemove", isSignal: false, isRequired: false, transformFunction: null }, moreActionsLabel: { classPropertyName: "moreActionsLabel", publicName: "moreActionsLabel", isSignal: false, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { size: "size", assistant: "assistant", componentSettings: "componentSettings", shellSettings: "shellSettings", remove: "remove" }, ngImport: i0, template: `
38475
39275
  <div
38476
39276
  class="pdx-widget-context-toolbar"
38477
39277
  [class.pdx-widget-context-toolbar--with-context]="!!contextLabel"
@@ -38525,7 +39325,7 @@ class DynamicWidgetContextToolbarComponent {
38525
39325
  (click)="shellSettings.emit()"
38526
39326
  ></button>
38527
39327
  }
38528
- @if (showRemove) {
39328
+ @if (showRemove || showSize()) {
38529
39329
  <button
38530
39330
  praxisIconButton="ms:more_horiz"
38531
39331
  size="dense"
@@ -38536,14 +39336,21 @@ class DynamicWidgetContextToolbarComponent {
38536
39336
  [matMenuTriggerFor]="widgetContextMenu"
38537
39337
  ></button>
38538
39338
  <mat-menu #widgetContextMenu="matMenu">
39339
+ @if (showSize()) {
39340
+ <button mat-menu-item type="button" [disabled]="sizeDisabled()" (click)="size.emit()" data-testid="dynamic-widget-size">
39341
+ <mat-icon [praxisIcon]="'ms:straighten'"></mat-icon><span>{{ sizeLabel() }}</span>
39342
+ </button>
39343
+ }
39344
+ @if (showRemove) {
38539
39345
  <button mat-menu-item type="button" data-testid="dynamic-widget-remove" (click)="remove.emit()">
38540
39346
  <mat-icon [praxisIcon]="'delete'"></mat-icon>
38541
39347
  <span>{{ removeLabel }}</span>
38542
39348
  </button>
39349
+ }
38543
39350
  </mat-menu>
38544
39351
  }
38545
39352
  </div>
38546
- `, isInline: true, styles: [".pdx-widget-context-toolbar{position:absolute;top:-19px;right:12px;z-index:4;display:inline-flex;align-items:center;gap:4px;min-height:34px;padding:2px 4px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 28%,var(--md-sys-color-outline-variant));border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 88%,transparent);box-shadow:0 10px 28px #0f172a38,0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.pdx-widget-context-toolbar--with-context{border-radius:18px}.pdx-widget-context-toolbar__context{display:inline-flex;align-items:center;align-self:stretch;max-width:180px;padding:0 8px 0 10px;border-right:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 64%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:700;letter-spacing:.01em;line-height:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
39353
+ `, isInline: true, styles: [".pdx-widget-context-toolbar{position:absolute;top:-19px;right:12px;z-index:4;display:inline-flex;align-items:center;gap:4px;min-height:34px;padding:2px 4px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 28%,var(--md-sys-color-outline-variant));border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 88%,transparent);box-shadow:0 10px 28px #0f172a38,0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.pdx-widget-context-toolbar--with-context{border-radius:18px}.pdx-widget-context-toolbar__context{display:inline-flex;align-items:center;align-self:stretch;max-width:180px;padding:0 8px 0 10px;border-right:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 64%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:700;letter-spacing:.01em;line-height:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
38547
39354
  }
38548
39355
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicWidgetContextToolbarComponent, decorators: [{
38549
39356
  type: Component,
@@ -38607,7 +39414,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
38607
39414
  (click)="shellSettings.emit()"
38608
39415
  ></button>
38609
39416
  }
38610
- @if (showRemove) {
39417
+ @if (showRemove || showSize()) {
38611
39418
  <button
38612
39419
  praxisIconButton="ms:more_horiz"
38613
39420
  size="dense"
@@ -38618,15 +39425,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
38618
39425
  [matMenuTriggerFor]="widgetContextMenu"
38619
39426
  ></button>
38620
39427
  <mat-menu #widgetContextMenu="matMenu">
39428
+ @if (showSize()) {
39429
+ <button mat-menu-item type="button" [disabled]="sizeDisabled()" (click)="size.emit()" data-testid="dynamic-widget-size">
39430
+ <mat-icon [praxisIcon]="'ms:straighten'"></mat-icon><span>{{ sizeLabel() }}</span>
39431
+ </button>
39432
+ }
39433
+ @if (showRemove) {
38621
39434
  <button mat-menu-item type="button" data-testid="dynamic-widget-remove" (click)="remove.emit()">
38622
39435
  <mat-icon [praxisIcon]="'delete'"></mat-icon>
38623
39436
  <span>{{ removeLabel }}</span>
38624
39437
  </button>
39438
+ }
38625
39439
  </mat-menu>
38626
39440
  }
38627
39441
  </div>
38628
39442
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".pdx-widget-context-toolbar{position:absolute;top:-19px;right:12px;z-index:4;display:inline-flex;align-items:center;gap:4px;min-height:34px;padding:2px 4px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 28%,var(--md-sys-color-outline-variant));border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 88%,transparent);box-shadow:0 10px 28px #0f172a38,0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.pdx-widget-context-toolbar--with-context{border-radius:18px}.pdx-widget-context-toolbar__context{display:inline-flex;align-items:center;align-self:stretch;max-width:180px;padding:0 8px 0 10px;border-right:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 64%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:700;letter-spacing:.01em;line-height:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
38629
- }], propDecorators: { toolbarLabel: [{
39443
+ }], propDecorators: { showSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSize", required: false }] }], sizeDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeDisabled", required: false }] }], sizeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeLabel", required: false }] }], size: [{ type: i0.Output, args: ["size"] }], toolbarLabel: [{
38630
39444
  type: Input
38631
39445
  }], contextLabel: [{
38632
39446
  type: Input
@@ -38666,95 +39480,212 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
38666
39480
  type: Output
38667
39481
  }] } });
38668
39482
 
38669
- const DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE = 'dynamicWidgetPage';
38670
- const DYNAMIC_WIDGET_PAGE_I18N_CONFIG = {
38671
- namespaces: {
38672
- [DYNAMIC_WIDGET_PAGE_I18N_NAMESPACE]: {
38673
- 'pt-BR': {
38674
- 'controls.pageSettings': 'Configurar página',
38675
- 'controls.componentSettings': 'Configurar conteúdo',
38676
- 'controls.componentSettingsTooltip': 'Abrir configurações do componente',
38677
- 'controls.widgetSettings': 'Configurar widget',
38678
- 'controls.widgetSettingsTooltip': 'Abrir configurações do widget',
38679
- 'controls.widgetAssistant': 'Abrir assistente do widget',
38680
- 'controls.widgetAssistantTooltip': 'Abrir o assistente Praxis com contexto deste widget',
38681
- 'controls.widgetRemove': 'Excluir widget',
38682
- 'controls.widgetRemoveTooltip': 'Excluir este widget e limpar conexões vinculadas',
38683
- 'controls.widgetRemoveConfirmTitle': 'Excluir widget?',
38684
- 'controls.widgetRemoveConfirmMessage': 'O widget "{{widget}}" será removido da página. Conexões e posições vinculadas também serão limpas.',
38685
- 'controls.widgetRemoveConfirm': 'Excluir widget',
38686
- 'controls.cancel': 'Cancelar',
38687
- 'controls.cardSettings': 'Configurar card',
38688
- 'controls.moreWidgetActions': 'Mais ações do widget',
38689
- 'controls.widgetContextToolbar': 'Ações do widget {{widget}}',
38690
- 'controls.widgetContextTooltip': 'Editando {{widget}}',
38691
- 'controls.widgetLayoutToolbar': 'Ações de layout do widget',
38692
- 'controls.dragWidget': 'Arrastar widget',
38693
- 'controls.resizeWidget': 'Redimensionar widget',
38694
- 'controls.resize.north': 'Redimensionar widget pela borda superior',
38695
- 'controls.resize.south': 'Redimensionar widget pela borda inferior',
38696
- 'controls.resize.east': 'Redimensionar widget pela borda direita',
38697
- 'controls.resize.west': 'Redimensionar widget pela borda esquerda',
38698
- 'controls.resize.north-east': 'Redimensionar widget pelo canto superior direito',
38699
- 'controls.resize.north-west': 'Redimensionar widget pelo canto superior esquerdo',
38700
- 'controls.resize.south-east': 'Redimensionar widget pelo canto inferior direito',
38701
- 'controls.resize.south-west': 'Redimensionar widget pelo canto inferior esquerdo',
38702
- 'announcements.moved': 'Widget movido: {{widget}}',
38703
- 'announcements.swapped': 'Widget trocado com {{target}}: {{widget}}',
38704
- 'announcements.resized': 'Widget redimensionado: {{widget}}',
38705
- 'announcements.position': '{{action}} para coluna {{col}}, linha {{row}}, largura {{colSpan}}, altura {{rowSpan}}.',
38706
- 'announcements.dragBlocked': 'Movimento bloqueado: o widget colidiria com outro item.',
38707
- 'announcements.resizeBlocked': 'Redimensionamento bloqueado: o widget colidiria com outro item.',
38708
- },
38709
- 'en-US': {
38710
- 'controls.pageSettings': 'Configure page',
38711
- 'controls.componentSettings': 'Configure content',
38712
- 'controls.componentSettingsTooltip': 'Open component settings',
38713
- 'controls.widgetSettings': 'Configure widget',
38714
- 'controls.widgetSettingsTooltip': 'Open widget settings',
38715
- 'controls.widgetAssistant': 'Open widget assistant',
38716
- 'controls.widgetAssistantTooltip': 'Open the Praxis assistant with this widget context',
38717
- 'controls.widgetRemove': 'Delete widget',
38718
- 'controls.widgetRemoveTooltip': 'Delete this widget and clean linked connections',
38719
- 'controls.widgetRemoveConfirmTitle': 'Delete widget?',
38720
- 'controls.widgetRemoveConfirmMessage': 'The widget "{{widget}}" will be removed from the page. Linked connections and positions will also be cleaned.',
38721
- 'controls.widgetRemoveConfirm': 'Delete widget',
38722
- 'controls.cancel': 'Cancel',
38723
- 'controls.cardSettings': 'Configure card',
38724
- 'controls.moreWidgetActions': 'More widget actions',
38725
- 'controls.widgetContextToolbar': 'Widget {{widget}} actions',
38726
- 'controls.widgetContextTooltip': 'Editing {{widget}}',
38727
- 'controls.widgetLayoutToolbar': 'Widget layout actions',
38728
- 'controls.dragWidget': 'Drag widget',
38729
- 'controls.resizeWidget': 'Resize widget',
38730
- 'controls.resize.north': 'Resize widget from the top edge',
38731
- 'controls.resize.south': 'Resize widget from the bottom edge',
38732
- 'controls.resize.east': 'Resize widget from the right edge',
38733
- 'controls.resize.west': 'Resize widget from the left edge',
38734
- 'controls.resize.north-east': 'Resize widget from the top-right corner',
38735
- 'controls.resize.north-west': 'Resize widget from the top-left corner',
38736
- 'controls.resize.south-east': 'Resize widget from the bottom-right corner',
38737
- 'controls.resize.south-west': 'Resize widget from the bottom-left corner',
38738
- 'announcements.moved': 'Widget moved: {{widget}}',
38739
- 'announcements.swapped': 'Widget swapped with {{target}}: {{widget}}',
38740
- 'announcements.resized': 'Widget resized: {{widget}}',
38741
- 'announcements.position': '{{action}} to column {{col}}, row {{row}}, width {{colSpan}}, height {{rowSpan}}.',
38742
- 'announcements.dragBlocked': 'Move blocked: the widget would overlap another item.',
38743
- 'announcements.resizeBlocked': 'Resize blocked: the widget would overlap another item.',
38744
- },
38745
- },
38746
- },
38747
- };
39483
+ /** Internal materialization of the canonical content-row size, never DOM state. */
39484
+ function contentSize(item) {
39485
+ const size = item.contentSize;
39486
+ if (!size || size === 'auto' || !Number.isFinite(size.heightPx) || size.heightPx <= 0)
39487
+ return null;
39488
+ const { min, max } = contentHeightLimits(item);
39489
+ return {
39490
+ heightPx: Math.min(max, Math.max(min, Math.round(size.heightPx))),
39491
+ offsetTopPx: Number.isFinite(size.offsetTopPx) ? Math.max(0, Math.round(size.offsetTopPx)) : 0,
39492
+ };
39493
+ }
39494
+ function contentHeightLimits(item) {
39495
+ const c = item.constraints;
39496
+ const min = Number.isFinite(c?.minHeightPx) ? Math.max(160, Math.round(c.minHeightPx)) : 160;
39497
+ const max = Number.isFinite(c?.maxHeightPx) ? Math.max(min, Math.round(c.maxHeightPx)) : Number.MAX_SAFE_INTEGER;
39498
+ return { min, max };
39499
+ }
39500
+ /** Pixel delta applies to the captured outer rectangle, not changing rowSpan.
39501
+ * North growth stops at the track origin; shrinking retains the opposite edge.
39502
+ */
39503
+ function resizeContentHeight(item, startHeight, delta, north, renderedMinimum = 0) {
39504
+ if (!delta)
39505
+ return { item, constrained: false, minimum: false };
39506
+ const size = contentSize(item);
39507
+ const top = size?.offsetTopPx ?? 0;
39508
+ const height = size?.heightPx ?? startHeight;
39509
+ const limits = contentHeightLimits(item);
39510
+ const min = Math.max(limits.min, Number.isFinite(renderedMinimum) ? Math.ceil(renderedMinimum) : 0);
39511
+ // North gestures also have a spatial maximum: the fixed bottom minus the
39512
+ // track origin. A late content minimum cannot move that opposite edge.
39513
+ const max = north ? Math.min(limits.max, height + top) : limits.max;
39514
+ // A viewport observation must never override an authored maximum or leak
39515
+ // into persisted constraints. Leave an impossible gesture unchanged.
39516
+ if (min > max)
39517
+ return { item, constrained: true, minimum: true };
39518
+ const requested = height + (north ? -delta : delta);
39519
+ if (requested < height && height <= min)
39520
+ return { item, constrained: true, minimum: true };
39521
+ const next = Math.max(min, Math.min(max, requested));
39522
+ if (next === height)
39523
+ return { item, constrained: next !== requested, minimum: next === min };
39524
+ return {
39525
+ item: { ...item, contentSize: { heightPx: Math.round(next), offsetTopPx: north ? Math.max(0, Math.round(top + height - next)) : top } },
39526
+ constrained: next !== requested,
39527
+ minimum: next === min,
39528
+ };
39529
+ }
39530
+
39531
+ /** CSSOM resolves grid tracks to pixels. Unresolved/unsupported lists use the fallback. */
39532
+ function readCanvasTrackSizes(value) {
39533
+ const tracks = value.trim().split(/\s+/);
39534
+ if (!tracks.length || tracks.some((track) => !/^\d+(?:\.\d+)?px$/.test(track))) {
39535
+ return [];
39536
+ }
39537
+ return tracks.map((track) => Number.parseFloat(track));
39538
+ }
39539
+ /**
39540
+ * Snap the moved edge to the nearest rendered track, retaining the pointer's
39541
+ * initial offset. Resize uses the moving end edge for south/east handles, not
39542
+ * the widget origin. Snapshotting avoids feedback from transient layout previews.
39543
+ */
39544
+ function canvasAxisDelta(axis, startIndex, displacement, edge = 'start') {
39545
+ const fallback = Math.max(1, axis.fallbackSize);
39546
+ const gap = Math.max(0, axis.gap);
39547
+ const positions = [];
39548
+ let offset = 0;
39549
+ for (const size of axis.sizes) {
39550
+ positions.push(offset + (edge === 'end' ? size : 0));
39551
+ offset += size + gap;
39552
+ }
39553
+ const extensionStart = offset + (edge === 'end' ? fallback : 0);
39554
+ const step = fallback + gap;
39555
+ const positionAt = (index) => positions[index] ?? extensionStart + (index - positions.length) * step;
39556
+ const target = positionAt(startIndex) + displacement;
39557
+ let nearest = startIndex;
39558
+ let distance = Math.abs(displacement);
39559
+ const consider = (index, position) => {
39560
+ const nextDistance = Math.abs(position - target);
39561
+ // Prefer the original track on ties, including zero-height empty rows.
39562
+ if (nextDistance < distance) {
39563
+ nearest = index;
39564
+ distance = nextDistance;
39565
+ }
39566
+ };
39567
+ positions.forEach((position, index) => consider(index, position));
39568
+ const extensionIndex = positions.length + Math.max(0, Math.round((target - extensionStart) / step));
39569
+ consider(extensionIndex, positionAt(extensionIndex));
39570
+ return nearest - startIndex;
39571
+ }
39572
+
39573
+ const right = (item) => item.col + item.colSpan;
39574
+ const bottom = (item) => item.row + item.rowSpan;
39575
+ const overlaps = (a, b) => a.col < right(b) && right(a) > b.col && a.row < bottom(b) && bottom(a) > b.row;
39576
+ /** Resize only by yielding space, never by pushing unrelated items or swapping.
39577
+ * Opposite edges stay anchored. Ambiguous corner collisions fail closed. Inputs
39578
+ * are the normalized effective device layout, never the transient preview.
39579
+ */
39580
+ function resolveCanvasResizePlacement(key, candidate, source) {
39581
+ const start = source[key];
39582
+ const desired = { ...candidate };
39583
+ const limitedBy = new Set();
39584
+ const rejected = new Set();
39585
+ const sideOf = (other) => {
39586
+ const sides = [];
39587
+ if (right(start) <= other.col && right(desired) > right(start))
39588
+ sides.push('east');
39589
+ if (start.col >= right(other) && desired.col < start.col)
39590
+ sides.push('west');
39591
+ if (bottom(start) <= other.row && bottom(desired) > bottom(start))
39592
+ sides.push('south');
39593
+ if (start.row >= bottom(other) && desired.row < start.row)
39594
+ sides.push('north');
39595
+ return sides.length === 1 ? sides[0] : null;
39596
+ };
39597
+ let left = desired.col, top = desired.row, end = right(desired), foot = bottom(desired);
39598
+ for (const [otherKey, other] of Object.entries(source)) {
39599
+ if (otherKey === key || !overlaps(desired, other))
39600
+ continue;
39601
+ const side = sideOf(other);
39602
+ const constraints = other.constraints;
39603
+ const horizontal = side === 'east' || side === 'west';
39604
+ const minimum = Math.max(1, (horizontal ? constraints?.minColSpan : constraints?.minRowSpan) || 1);
39605
+ const locked = constraints?.lockSize || (constraints?.lockPosition && (side === 'east' || side === 'south'));
39606
+ if (!side || overlaps(start, other)) {
39607
+ rejected.add(otherKey);
39608
+ continue;
39609
+ }
39610
+ const available = locked ? (horizontal ? other.colSpan : other.rowSpan) : minimum;
39611
+ const before = [left, top, end, foot].join();
39612
+ if (side === 'east')
39613
+ end = Math.min(end, right(other) - available);
39614
+ if (side === 'west')
39615
+ left = Math.max(left, other.col + available);
39616
+ if (side === 'south')
39617
+ foot = Math.min(foot, bottom(other) - available);
39618
+ if (side === 'north')
39619
+ top = Math.max(top, other.row + available);
39620
+ if (before !== [left, top, end, foot].join()) {
39621
+ (locked ? rejected : limitedBy).add(otherKey);
39622
+ }
39623
+ }
39624
+ let primary = { ...desired, col: left, row: top, colSpan: end - left, rowSpan: foot - top };
39625
+ // A pre-existing invalid layout or an ambiguous corner must not be repaired by
39626
+ // this gesture. Also reject any clamp that would violate the selected minimum.
39627
+ const ambiguous = [...rejected].some(otherKey => !sideOf(source[otherKey]) || overlaps(start, source[otherKey]));
39628
+ if (ambiguous || primary.colSpan < Math.max(1, start.constraints?.minColSpan || 1)
39629
+ || primary.rowSpan < Math.max(1, start.constraints?.minRowSpan || 1))
39630
+ primary = { ...start };
39631
+ const items = { [key]: primary };
39632
+ const minimumKeys = new Set();
39633
+ for (const [otherKey, other] of Object.entries(source)) {
39634
+ if (otherKey === key || !overlaps(primary, other))
39635
+ continue;
39636
+ const side = sideOf(other);
39637
+ const next = { ...other };
39638
+ if (side === 'east') {
39639
+ next.col = right(primary);
39640
+ next.colSpan = right(other) - next.col;
39641
+ }
39642
+ if (side === 'west')
39643
+ next.colSpan = primary.col - other.col;
39644
+ if (side === 'south') {
39645
+ next.row = bottom(primary);
39646
+ next.rowSpan = bottom(other) - next.row;
39647
+ }
39648
+ if (side === 'north')
39649
+ next.rowSpan = primary.row - other.row;
39650
+ if (!side || overlaps(primary, next) || next.colSpan < Math.max(1, other.constraints?.minColSpan || 1)
39651
+ || next.rowSpan < Math.max(1, other.constraints?.minRowSpan || 1)) {
39652
+ return { items: { [key]: start }, primaryItem: start, limited: true,
39653
+ minimumKeys: [], blockedKeys: [otherKey] };
39654
+ }
39655
+ items[otherKey] = next;
39656
+ const horizontal = side === 'east' || side === 'west';
39657
+ if (horizontal ? next.colSpan === Math.max(1, other.constraints?.minColSpan || 1)
39658
+ : next.rowSpan === Math.max(1, other.constraints?.minRowSpan || 1))
39659
+ minimumKeys.add(otherKey);
39660
+ }
39661
+ // Several neighbors can constrain the same edge; only the tightest ones are
39662
+ // actually at minimum in the final distribution, not every earlier limiter.
39663
+ for (const otherKey of limitedBy) {
39664
+ const item = items[otherKey] || source[otherKey];
39665
+ const side = sideOf(source[otherKey]);
39666
+ const horizontal = side === 'east' || side === 'west';
39667
+ if (horizontal ? item.colSpan === Math.max(1, item.constraints?.minColSpan || 1)
39668
+ : item.rowSpan === Math.max(1, item.constraints?.minRowSpan || 1))
39669
+ minimumKeys.add(otherKey);
39670
+ }
39671
+ if ((primary.colSpan !== start.colSpan && primary.colSpan === Math.max(1, start.constraints?.minColSpan || 1))
39672
+ || (primary.rowSpan !== start.rowSpan && primary.rowSpan === Math.max(1, start.constraints?.minRowSpan || 1)))
39673
+ minimumKeys.add(key);
39674
+ return { items, primaryItem: primary,
39675
+ limited: primary.col !== desired.col || primary.row !== desired.row
39676
+ || primary.colSpan !== desired.colSpan || primary.rowSpan !== desired.rowSpan,
39677
+ minimumKeys: [...minimumKeys], blockedKeys: [...rejected] };
39678
+ }
38748
39679
 
38749
39680
  const CANVAS_RESIZE_HANDLES = [
38750
- { id: 'north', className: 'pdx-canvas-resize--north' },
38751
- { id: 'south', className: 'pdx-canvas-resize--south' },
38752
- { id: 'east', className: 'pdx-canvas-resize--east' },
38753
- { id: 'west', className: 'pdx-canvas-resize--west' },
38754
- { id: 'north-east', className: 'pdx-canvas-resize--north-east' },
38755
- { id: 'north-west', className: 'pdx-canvas-resize--north-west' },
38756
- { id: 'south-east', className: 'pdx-canvas-resize--south-east' },
38757
- { id: 'south-west', className: 'pdx-canvas-resize--south-west' },
39681
+ { id: 'north', icon: 'north' },
39682
+ { id: 'north-east', icon: 'north_east' },
39683
+ { id: 'east', icon: 'east' },
39684
+ { id: 'south-east', icon: 'south_east' },
39685
+ { id: 'south', icon: 'south' },
39686
+ { id: 'south-west', icon: 'south_west' },
39687
+ { id: 'west', icon: 'west' },
39688
+ { id: 'north-west', icon: 'north_west' },
38758
39689
  ];
38759
39690
  class DynamicWidgetPageComponent {
38760
39691
  pageCanvasHost;
@@ -38823,9 +39754,23 @@ class DynamicWidgetPageComponent {
38823
39754
  activeTabs = {};
38824
39755
  widgetDiagnostics = {};
38825
39756
  selectedWidgetKeyState = signal(null, ...(ngDevMode ? [{ debugName: "selectedWidgetKeyState" }] : /* istanbul ignore next */ []));
39757
+ selectedCanvasWidget = computed(() => this.widgets().find((widget) => this.isWidgetSelected(widget.key)), ...(ngDevMode ? [{ debugName: "selectedCanvasWidget" }] : /* istanbul ignore next */ []));
38826
39758
  blockedCanvasWidgetKey = null;
38827
39759
  canvasPreviewState = signal(null, ...(ngDevMode ? [{ debugName: "canvasPreviewState" }] : /* istanbul ignore next */ []));
38828
39760
  canvasPreviewInvalidState = signal(false, ...(ngDevMode ? [{ debugName: "canvasPreviewInvalidState" }] : /* istanbul ignore next */ []));
39761
+ resizeFeedbackState = signal({}, ...(ngDevMode ? [{ debugName: "resizeFeedbackState" }] : /* istanbul ignore next */ []));
39762
+ resizeFeedbackOrigin = signal(null, ...(ngDevMode ? [{ debugName: "resizeFeedbackOrigin" }] : /* istanbul ignore next */ []));
39763
+ resizeFeedbackPositions = [
39764
+ { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetX: 16, offsetY: 16 },
39765
+ { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetX: -16, offsetY: 16 },
39766
+ { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetX: 16, offsetY: -16 },
39767
+ { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetX: -16, offsetY: -16 },
39768
+ ];
39769
+ prepareResizeFeedbackOverlay(element) {
39770
+ // The entire pane must pass through hits, not only its inner presentation.
39771
+ // No focus, backdrop or interactive control belongs to gesture feedback.
39772
+ element.style.pointerEvents = 'none';
39773
+ }
38829
39774
  transientCanvasItemsState = signal({}, ...(ngDevMode ? [{ debugName: "transientCanvasItemsState" }] : /* istanbul ignore next */ []));
38830
39775
  activeCanvasInteraction = null;
38831
39776
  appliedPersisted = false;
@@ -38870,6 +39815,16 @@ class DynamicWidgetPageComponent {
38870
39815
  runtimeObservationRegistration = null;
38871
39816
  hostElement = inject((ElementRef));
38872
39817
  changeDetector = inject(ChangeDetectorRef);
39818
+ lastCanvasResize = signal(null, ...(ngDevMode ? [{ debugName: "lastCanvasResize" }] : /* istanbul ignore next */ []));
39819
+ sizeEditorOrigin = signal(null, ...(ngDevMode ? [{ debugName: "sizeEditorOrigin" }] : /* istanbul ignore next */ []));
39820
+ sizeEditorSeed = signal(null, ...(ngDevMode ? [{ debugName: "sizeEditorSeed" }] : /* istanbul ignore next */ []));
39821
+ sizeEditorError = signal(null, ...(ngDevMode ? [{ debugName: "sizeEditorError" }] : /* istanbul ignore next */ []));
39822
+ projectedSizeActions = new WeakSet();
39823
+ sizeEditorPositions = [
39824
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'top' },
39825
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'top' },
39826
+ ];
39827
+ sizeEditorResizeObserver;
38873
39828
  hostResizeObserver;
38874
39829
  containerWidth;
38875
39830
  widgetLoaders;
@@ -38878,6 +39833,10 @@ class DynamicWidgetPageComponent {
38878
39833
  afterNextRender({ read: () => this.observeHostWidth() });
38879
39834
  }
38880
39835
  ngOnDestroy() {
39836
+ this.closeSizeEditor(false);
39837
+ if (this.activeCanvasInteraction) {
39838
+ this.cancelCanvasInteractionForWidget(this.activeCanvasInteraction.widgetKey);
39839
+ }
38881
39840
  this.destroyed = true;
38882
39841
  this.runtimeObservationRegistration?.destroy();
38883
39842
  this.runtimeObservationRegistration = null;
@@ -38886,6 +39845,17 @@ class DynamicWidgetPageComponent {
38886
39845
  this.compositionRuntime.destroy();
38887
39846
  }
38888
39847
  ngOnChanges(changes) {
39848
+ if (changes['pageIdentity'] || changes['componentInstanceId'])
39849
+ this.lastCanvasResize.set(null);
39850
+ if (changes['page'] || changes['context'] || changes['pageIdentity'] || changes['enableCustomization'] || changes['authoringCapabilities']) {
39851
+ this.closeSizeEditor(false);
39852
+ }
39853
+ if (this.activeCanvasInteraction &&
39854
+ (changes['page'] || changes['context'] || changes['pageIdentity'] ||
39855
+ changes['componentInstanceId'] || changes['enableCustomization'] ||
39856
+ changes['authoringCapabilities'])) {
39857
+ this.cancelCanvasInteractionForWidget(this.activeCanvasInteraction.widgetKey);
39858
+ }
38889
39859
  if (changes['page'] ||
38890
39860
  changes['context'] ||
38891
39861
  changes['enableCustomization'] ||
@@ -40358,6 +41328,10 @@ class DynamicWidgetPageComponent {
40358
41328
  && !this.hasVisibleWidgetShellHeader(widget));
40359
41329
  }
40360
41330
  widgetShellForRender(widget) {
41331
+ const shell = this.widgetShellWithActions(widget);
41332
+ return this.widgetContentSize(widget) ? { ...shell, bodyLayout: 'scroll' } : shell;
41333
+ }
41334
+ widgetShellWithActions(widget) {
40361
41335
  if (!this.shouldProjectWidgetHeaderActions(widget)) {
40362
41336
  return widget.shell;
40363
41337
  }
@@ -40403,7 +41377,8 @@ class DynamicWidgetPageComponent {
40403
41377
  && this.hasVisibleWidgetShellHeader(widget);
40404
41378
  }
40405
41379
  hasWidgetContextAuthoringActions(widget) {
40406
- return this.canRequestWidgetAssistant()
41380
+ return (this.canCustomizeCanvas() && this.isCanvasMode())
41381
+ || this.canRequestWidgetAssistant()
40407
41382
  || this.canOpenWidgetComponentSettings(widget.key)
40408
41383
  || this.canOpenWidgetShellSettings()
40409
41384
  || this.canRemoveWidgets();
@@ -40431,6 +41406,14 @@ class DynamicWidgetPageComponent {
40431
41406
  }
40432
41407
  buildProjectedWidgetShellActions(widget) {
40433
41408
  const actions = [];
41409
+ if (this.canCustomizeCanvas() && this.isCanvasMode()) {
41410
+ const sizeAction = {
41411
+ id: 'widget-size', label: this.sizeEditorLabel(), icon: 'ms:straighten',
41412
+ placement: 'menu', disabled: !!this.currentCanvasItem(widget.key)?.constraints?.lockSize,
41413
+ };
41414
+ this.projectedSizeActions.add(sizeAction);
41415
+ actions.push(sizeAction);
41416
+ }
40434
41417
  if (this.canRequestWidgetAssistant()) {
40435
41418
  actions.push({
40436
41419
  id: 'widget-assistant',
@@ -40468,7 +41451,7 @@ class DynamicWidgetPageComponent {
40468
41451
  tooltip: this.widgetRemoveLabel(),
40469
41452
  icon: 'delete',
40470
41453
  variant: 'icon',
40471
- placement: 'header',
41454
+ placement: 'menu',
40472
41455
  });
40473
41456
  }
40474
41457
  return actions;
@@ -40482,6 +41465,7 @@ class DynamicWidgetPageComponent {
40482
41465
  action.icon || '',
40483
41466
  action.variant || '',
40484
41467
  action.placement || '',
41468
+ String(action.ariaExpanded ?? ''),
40485
41469
  ].join(':'))
40486
41470
  .join('|');
40487
41471
  }
@@ -40524,7 +41508,13 @@ class DynamicWidgetPageComponent {
40524
41508
  this.widgetDiagnosticsChange.emit({ ...this.widgetDiagnostics });
40525
41509
  }
40526
41510
  onShellAction(fromKey, evt) {
40527
- if (evt.id === 'expand' || evt.id === 'fullscreen') {
41511
+ if (evt.id === 'widget-size' && evt.action && this.projectedSizeActions.has(evt.action)) {
41512
+ this.openSizeEditor(fromKey);
41513
+ return;
41514
+ }
41515
+ if (this.sizeEditorOrigin())
41516
+ this.closeSizeEditor(false);
41517
+ if (evt.id === 'expand' || evt.id === 'fullscreen' || evt.id === 'collapse') {
40528
41518
  this.cancelCanvasInteractionForWidget(fromKey);
40529
41519
  }
40530
41520
  if (evt.id === 'widget-assistant') {
@@ -41046,7 +42036,8 @@ class DynamicWidgetPageComponent {
41046
42036
  const editor = this.pageEditorComponent || this.defaultPageEditor;
41047
42037
  if (!editor)
41048
42038
  return;
41049
- const page = this.ensurePageDefinition();
42039
+ // Layout authoring must not capture runtime inputs such as loaded table rows.
42040
+ const page = this.clonePageDefinition(this.authoredPageDefinition ?? this.ensurePageDefinition());
41050
42041
  const uiShell = page.context?.ui?.shell || {};
41051
42042
  const ref = this.settingsPanel.open({
41052
42043
  id: 'dynamic-page-settings',
@@ -41059,6 +42050,8 @@ class DynamicWidgetPageComponent {
41059
42050
  presets: uiShell?.presets || null,
41060
42051
  pagePreset: uiShell?.preset || null,
41061
42052
  authoringCapabilities: this.authoringCapabilities,
42053
+ // Transient Settings Panel data; never part of WidgetPageDefinition.
42054
+ validateRenderedCanvas: (candidate) => this.validateRenderedCanvas(candidate),
41062
42055
  },
41063
42056
  },
41064
42057
  });
@@ -41069,6 +42062,40 @@ class DynamicWidgetPageComponent {
41069
42062
  return this.isAuthoringCapabilityEnabled('pageSettings')
41070
42063
  && !!(this.pageEditorComponent || this.defaultPageEditor);
41071
42064
  }
42065
+ validateRenderedCanvas(candidate) {
42066
+ const device = this.resolveDeviceKind();
42067
+ const canvas = this.resolveCanvas(candidate.canvas, candidate.deviceLayouts?.[device]?.canvas);
42068
+ if (!canvas || canvas.autoRows !== 'content')
42069
+ return null;
42070
+ const baseline = this.authoredPageDefinition;
42071
+ for (const [key, item] of Object.entries(canvas.items)) {
42072
+ if (candidate.deviceLayouts?.[device]?.widgetOverrides?.[key]?.hidden)
42073
+ continue;
42074
+ const size = contentSize(item);
42075
+ if (!size)
42076
+ continue;
42077
+ const override = candidate.deviceLayouts?.[device]?.canvas?.items?.[key];
42078
+ const scope = override?.contentSize !== undefined ? device : 'base';
42079
+ const current = this.canvas?.items[key];
42080
+ const minimum = this.measureCanvasMinimumHeight(key);
42081
+ // A changed width or shell cannot reuse the old DOM as geometry proof.
42082
+ const sameGeometry = !this.destroyed && this.canvas?.autoRows === 'content'
42083
+ && canvas.columns === this.canvas.columns
42084
+ && (canvas.gap || candidate.deviceLayouts?.[device]?.layout?.gap || candidate.layout?.gap || '16px') === this.pageGap
42085
+ && item.colSpan === current?.colSpan
42086
+ && candidate.layoutPreset === baseline?.layoutPreset
42087
+ && candidate.themePreset === baseline?.themePreset
42088
+ && this.sameJsonStructure(candidate.widgets.find(widget => widget.key === key)?.shell, baseline?.widgets.find(widget => widget.key === key)?.shell)
42089
+ && this.sameJsonStructure(candidate.context?.['ui']?.shell ?? {}, baseline?.context?.['ui']?.shell ?? {});
42090
+ if (!sameGeometry || minimum === undefined) {
42091
+ return { widgetKey: key, scope, message: this.t('heightValidation.measureFirst', 'Use altura Automática para aplicar a nova geometria; depois reabra o editor para definir a altura ajustável.') };
42092
+ }
42093
+ if (size.heightPx < minimum) {
42094
+ return { widgetKey: key, scope, message: this.t('heightValidation.minimum', 'Use pelo menos {{height}}px nesta largura. Ajuste a altura/máximo ou use Automática.', { height: minimum }) };
42095
+ }
42096
+ }
42097
+ return null;
42098
+ }
41072
42099
  applyWidgetShell(key, result, persist) {
41073
42100
  if (!result)
41074
42101
  return;
@@ -41101,6 +42128,7 @@ class DynamicWidgetPageComponent {
41101
42128
  'title',
41102
42129
  'subtitle',
41103
42130
  'showHeader',
42131
+ 'stickyHeader',
41104
42132
  'windowActions',
41105
42133
  'collapsed',
41106
42134
  'expanded',
@@ -41321,7 +42349,15 @@ class DynamicWidgetPageComponent {
41321
42349
  const interaction = this.activeCanvasInteraction;
41322
42350
  if (!interaction || interaction.pointerId !== event.pointerId)
41323
42351
  return;
42352
+ if (!this.canCustomizeCanvas() || !this.isCanvasMode()) {
42353
+ this.cancelCanvasInteractionForWidget(interaction.widgetKey);
42354
+ return;
42355
+ }
41324
42356
  event.preventDefault();
42357
+ interaction.minimumContentHeight = this.measureCanvasMinimumHeight(interaction.widgetKey);
42358
+ if (interaction.kind === 'resize') {
42359
+ this.resizeFeedbackOrigin.set({ x: event.clientX, y: event.clientY });
42360
+ }
41325
42361
  const { colDelta, rowDelta } = this.resolveCanvasInteractionDelta(interaction, event.clientX, event.clientY);
41326
42362
  if (interaction.kind === 'drag') {
41327
42363
  const result = this.updateCanvasItem(interaction.widgetKey, (item) => this.applyCanvasDrag(item, interaction, colDelta, rowDelta), false, undefined, undefined, 'drag');
@@ -41341,9 +42377,15 @@ class DynamicWidgetPageComponent {
41341
42377
  const interaction = this.activeCanvasInteraction;
41342
42378
  if (!interaction || interaction.pointerId !== event.pointerId)
41343
42379
  return;
42380
+ if (!this.canCustomizeCanvas() || !this.isCanvasMode()) {
42381
+ this.cancelCanvasInteractionForWidget(interaction.widgetKey);
42382
+ return;
42383
+ }
41344
42384
  event.preventDefault();
41345
- this.releaseCanvasPointerCapture(interaction);
42385
+ interaction.minimumContentHeight = this.measureCanvasMinimumHeight(interaction.widgetKey);
41346
42386
  this.activeCanvasInteraction = null;
42387
+ this.resizeFeedbackOrigin.set(null);
42388
+ this.releaseCanvasPointerCapture(interaction);
41347
42389
  this.canvasPreviewState.set(null);
41348
42390
  this.canvasPreviewInvalidState.set(false);
41349
42391
  this.transientCanvasItemsState.set({});
@@ -41372,13 +42414,21 @@ class DynamicWidgetPageComponent {
41372
42414
  const interaction = this.activeCanvasInteraction;
41373
42415
  if (!interaction || interaction.pointerId !== event.pointerId)
41374
42416
  return;
41375
- this.releaseCanvasPointerCapture(interaction);
41376
- this.activeCanvasInteraction = null;
41377
- this.canvasPreviewState.set(null);
41378
- this.canvasPreviewInvalidState.set(false);
41379
- this.transientCanvasItemsState.set({});
41380
- this.layoutAnnouncement = this.blockedCanvasAnnouncement(interaction.kind);
41381
- this.applyTransientPagePreview(interaction.startPage, this.buildStateRuntime(interaction.startPage.state, interaction.startPage.context));
42417
+ this.cancelCanvasGesture();
42418
+ }
42419
+ onCanvasEscape(event) {
42420
+ if (!(event instanceof KeyboardEvent) || event.key !== 'Escape'
42421
+ || event.defaultPrevented || !this.activeCanvasInteraction)
42422
+ return;
42423
+ event.preventDefault();
42424
+ event.stopPropagation();
42425
+ this.cancelCanvasGesture();
42426
+ }
42427
+ cancelCanvasGesture() {
42428
+ if (!this.activeCanvasInteraction)
42429
+ return;
42430
+ this.cancelCanvasInteractionForWidget(this.activeCanvasInteraction.widgetKey);
42431
+ this.layoutAnnouncement = this.t('announcements.interactionCancelled', 'Alteração de layout cancelada.');
41382
42432
  }
41383
42433
  resolveColumns(layout, deviceLayoutColumnsSelected = false) {
41384
42434
  const base = Math.max(1, Number(layout?.columns) || 2);
@@ -41581,9 +42631,31 @@ class DynamicWidgetPageComponent {
41581
42631
  },
41582
42632
  };
41583
42633
  }
42634
+ const columns = Math.max(1, Number(variant?.columns ?? canvas.columns) || 1);
42635
+ if (columns < canvas.columns) {
42636
+ // Reflow inherited desktop positions only. Explicit device positions are
42637
+ // anchors, including intentional overlays; the authored base stays intact.
42638
+ const anchored = Object.keys(items).filter(key => overrideItems[key]?.col !== undefined && overrideItems[key]?.row !== undefined);
42639
+ for (const key of anchored)
42640
+ items[key] = this.normalizeCanvasItem(items[key], columns);
42641
+ const placed = anchored.map(key => items[key]);
42642
+ const inherited = Object.entries(items)
42643
+ .filter(([key]) => !anchored.includes(key))
42644
+ .sort(([leftKey, left], [rightKey, right]) => left.row - right.row || left.col - right.col || leftKey.localeCompare(rightKey));
42645
+ for (const [key, item] of inherited) {
42646
+ const next = this.normalizeCanvasItem(item, columns);
42647
+ let collisions = placed.filter(other => this.canvasItemsOverlap(next, other));
42648
+ while (collisions.length) {
42649
+ next.row = Math.max(...collisions.map(other => other.row + other.rowSpan));
42650
+ collisions = placed.filter(other => this.canvasItemsOverlap(next, other));
42651
+ }
42652
+ items[key] = next;
42653
+ placed.push(next);
42654
+ }
42655
+ }
41584
42656
  return {
41585
42657
  ...canvas,
41586
- columns: Math.max(1, Number(variant?.columns ?? canvas.columns) || 1),
42658
+ columns,
41587
42659
  rowUnit: variant?.rowUnit ?? canvas.rowUnit,
41588
42660
  gap: variant?.gap ?? canvas.gap,
41589
42661
  autoRows: variant?.autoRows ?? canvas.autoRows,
@@ -41668,7 +42740,10 @@ class DynamicWidgetPageComponent {
41668
42740
  selectWidget(widgetKey) {
41669
42741
  if (!this.canSelectWidgets())
41670
42742
  return;
42743
+ if (this.activeCanvasInteraction && this.activeCanvasInteraction.widgetKey !== widgetKey)
42744
+ return;
41671
42745
  if (this.selectedWidgetKeyState() !== widgetKey) {
42746
+ this.closeSizeEditor(false);
41672
42747
  this.selectedWidgetKeyState.set(widgetKey);
41673
42748
  this.widgetSelectionChange.emit(widgetKey);
41674
42749
  }
@@ -41692,6 +42767,218 @@ class DynamicWidgetPageComponent {
41692
42767
  isWidgetSelected(widgetKey) {
41693
42768
  return this.selectedWidgetKeyState() === widgetKey;
41694
42769
  }
42770
+ canvasInteractionActive() {
42771
+ return this.activeCanvasInteraction !== null;
42772
+ }
42773
+ canShowCanvasEdges(widgetKey) {
42774
+ return this.canCustomizeCanvas() && this.isWidgetSelected(widgetKey)
42775
+ && !this.currentCanvasItem(widgetKey)?.constraints?.lockSize;
42776
+ }
42777
+ canvasSelectionLabel(key) {
42778
+ return this.t('size.select', 'Widget {{widget}}. Tab acessa os controles; Esc volta à seleção.', { widget: this.widgetContextLabel(this.widgets().find(w => w.key === key)) });
42779
+ }
42780
+ onCanvasSelectionKeydown(key, event) {
42781
+ if (event.key !== 'Escape' || this.activeCanvasInteraction || this.sizeEditorOrigin())
42782
+ return;
42783
+ const target = event.target;
42784
+ if (target instanceof HTMLElement && target.hasAttribute('data-canvas-edge')) {
42785
+ event.preventDefault();
42786
+ event.stopPropagation();
42787
+ event.currentTarget.focus({ preventScroll: true });
42788
+ }
42789
+ }
42790
+ isCanvasSizeLocked(key) { return !!this.currentCanvasItem(key)?.constraints?.lockSize; }
42791
+ sizeEditorWidgetLabel() {
42792
+ const widget = this.widgets().find(w => w.key === this.sizeEditorSeed()?.key);
42793
+ return widget ? this.widgetContextLabel(widget) : '';
42794
+ }
42795
+ sizeEditorLabel() { return this.t('size.title', 'Tamanho…'); }
42796
+ sizeEditorScopeLabel() {
42797
+ const seed = this.sizeEditorSeed();
42798
+ const scope = seed && this.authoredPageDefinition?.deviceLayouts?.[seed.device]?.canvas ? seed.device : 'base';
42799
+ return this.t(`size.scope.${scope}`, scope);
42800
+ }
42801
+ openSizeEditor(key) {
42802
+ if (!this.canCustomizeCanvas() || !this.isCanvasMode() || this.canvasInteractionActive() || this.isCanvasWidgetInOverlay(key))
42803
+ return;
42804
+ const item = this.currentCanvasItem(key);
42805
+ if (!item || item.constraints?.lockSize)
42806
+ return;
42807
+ const owner = Array.from(this.pageCanvasHost?.nativeElement.children || [])
42808
+ .find(element => element.getAttribute('data-widget-key') === key);
42809
+ if (!owner)
42810
+ return;
42811
+ this.selectWidget(key);
42812
+ this.sizeEditorError.set(null);
42813
+ const contentRows = this.canvas?.autoRows === 'content';
42814
+ this.sizeEditorSeed.set({ key, item: structuredClone(item), contentRows,
42815
+ height: contentRows ? Math.round(this.measureCanvasContentHeight(key) || 160) : item.rowSpan,
42816
+ columns: this.canvas?.columns || 1, device: this.resolveDeviceKind() });
42817
+ this.sizeEditorOrigin.set(owner);
42818
+ }
42819
+ focusSizeEditor(overlay) {
42820
+ overlay.overlayElement.querySelector('input')?.focus({ preventScroll: true });
42821
+ this.sizeEditorResizeObserver?.disconnect();
42822
+ this.sizeEditorResizeObserver = new ResizeObserver(() => {
42823
+ if (overlay.hasAttached())
42824
+ overlay.updatePosition();
42825
+ });
42826
+ this.sizeEditorResizeObserver.observe(overlay.overlayElement);
42827
+ }
42828
+ closeSizeEditor(restoreFocus) {
42829
+ if (!this.sizeEditorOrigin())
42830
+ return;
42831
+ const owner = this.sizeEditorOrigin();
42832
+ this.sizeEditorResizeObserver?.disconnect();
42833
+ this.sizeEditorResizeObserver = undefined;
42834
+ this.sizeEditorOrigin.set(null);
42835
+ this.sizeEditorSeed.set(null);
42836
+ this.sizeEditorError.set(null);
42837
+ this.clearSizePreview();
42838
+ if (restoreFocus && owner?.isConnected)
42839
+ owner.focus({ preventScroll: true });
42840
+ }
42841
+ onSizeEditorKeydown(event) {
42842
+ if (event.key !== 'Escape')
42843
+ return;
42844
+ event.preventDefault();
42845
+ event.stopPropagation();
42846
+ this.closeSizeEditor(true);
42847
+ }
42848
+ clearSizePreview() {
42849
+ this.transientCanvasItemsState.set({});
42850
+ this.resizeFeedbackState.set({});
42851
+ this.canvasPreviewState.set(null);
42852
+ this.canvasPreviewInvalidState.set(false);
42853
+ }
42854
+ resolveSizeRequest(request) {
42855
+ const seed = this.sizeEditorSeed();
42856
+ if (!seed || !this.canCustomizeCanvas() || this.isCanvasWidgetInOverlay(seed.key))
42857
+ return null;
42858
+ const page = this.ensurePageDefinition();
42859
+ const variant = this.resolveDeviceVariant(page.deviceLayouts);
42860
+ const canvas = this.resolveCanvas(page.canvas, variant?.canvas);
42861
+ const current = canvas?.items[seed.key];
42862
+ if (!canvas || !current || current.constraints?.lockSize || this.resolveDeviceKind() !== seed.device
42863
+ || canvas.columns !== seed.columns || !this.isSameCanvasItem(seed.item, current))
42864
+ return null;
42865
+ if (!Number.isSafeInteger(request.width) || !Number.isSafeInteger(request.height) || request.width < 1 || request.height < 1)
42866
+ return null;
42867
+ const colDelta = (request.width - current.colSpan) * (request.handle.includes('west') ? -1 : 1);
42868
+ const rowDelta = (request.height - seed.height) * (request.handle.includes('north') ? -1 : 1);
42869
+ const result = this.applyCanvasResize(current, { startItem: current, columns: canvas.columns,
42870
+ resizeHandle: request.handle, contentHeight: seed.contentRows ? seed.height : undefined,
42871
+ minimumContentHeight: this.measureCanvasMinimumHeight(seed.key) }, colDelta, rowDelta);
42872
+ if (result.constrained)
42873
+ return null;
42874
+ if (current.constraints?.lockPosition && (result.item.col !== current.col || result.item.row !== current.row
42875
+ || contentSize(result.item)?.offsetTopPx !== contentSize(current)?.offsetTopPx))
42876
+ return null;
42877
+ const placement = resolveCanvasResizePlacement(seed.key, result.item, canvas.items);
42878
+ if (placement.limited)
42879
+ return null;
42880
+ return { item: result.item, canvas, page, key: seed.key };
42881
+ }
42882
+ previewCanvasSize(request) {
42883
+ if (!request) {
42884
+ this.clearSizePreview();
42885
+ this.sizeEditorError.set(null);
42886
+ return;
42887
+ }
42888
+ const resolved = this.resolveSizeRequest(request);
42889
+ if (!resolved) {
42890
+ this.clearSizePreview();
42891
+ this.sizeEditorError.set(this.t('size.unavailable', 'Esse tamanho excede os limites ou o espaço disponível.'));
42892
+ return;
42893
+ }
42894
+ this.sizeEditorError.set(null);
42895
+ this.updateCanvasResize(resolved.key, resolved.item, resolved.canvas, resolved.page, false);
42896
+ }
42897
+ applyCanvasSize(request) {
42898
+ const resolved = this.resolveSizeRequest(request);
42899
+ if (!resolved) {
42900
+ this.previewCanvasSize(request);
42901
+ return;
42902
+ }
42903
+ this.updateCanvasResize(resolved.key, resolved.item, resolved.canvas, resolved.page, true, this.canvasAnnouncement('resize', resolved.key));
42904
+ this.closeSizeEditor(true);
42905
+ }
42906
+ hasCanvasResizeUndo(key) {
42907
+ return this.lastCanvasResize()?.key === key;
42908
+ }
42909
+ canvasResizeUndoPage(key) {
42910
+ const operation = this.lastCanvasResize();
42911
+ if (!operation || operation.key !== key || !this.canCustomizeCanvas() || this.canvasInteractionActive())
42912
+ return null;
42913
+ const page = this.clonePageDefinition(this.ensurePageDefinition());
42914
+ const device = this.resolveDeviceKind();
42915
+ const scope = page.deviceLayouts?.[device]?.canvas ? device : 'base';
42916
+ if (scope !== operation.scope)
42917
+ return null;
42918
+ const canvas = this.resolveCanvas(page.canvas, page.deviceLayouts?.[device]?.canvas);
42919
+ if (!canvas || !this.sameJsonStructure({ ...canvas, items: {} }, { ...operation.canvas, items: {} }))
42920
+ return null;
42921
+ const target = this.resolveCanvasTarget(page);
42922
+ const restored = { ...canvas.items };
42923
+ for (const itemKey of Object.keys(operation.after)) {
42924
+ const current = canvas.items[itemKey];
42925
+ const previous = operation.canvas.items[itemKey];
42926
+ if (!current || !previous || this.isCanvasWidgetInOverlay(itemKey)
42927
+ || current.constraints?.lockSize
42928
+ || !this.sameJsonStructure(current, operation.after[itemKey])
42929
+ || !this.sameJsonStructure(target.items[itemKey], operation.authoredAfter[itemKey]))
42930
+ return null;
42931
+ if (!this.isSameCanvasItem(this.normalizeCanvasItem(previous, canvas.columns), previous))
42932
+ return null;
42933
+ const size = contentSize(previous);
42934
+ if (size && size.heightPx < Math.max(contentHeightLimits(previous).min, this.measureCanvasMinimumHeight(itemKey) || 0))
42935
+ return null;
42936
+ restored[itemKey] = previous;
42937
+ }
42938
+ // Validate the complete inverse distribution against today's unrelated widgets.
42939
+ for (const itemKey of Object.keys(operation.after)) {
42940
+ if (Object.entries(restored).some(([otherKey, item]) => otherKey !== itemKey && this.canvasItemsOverlap(restored[itemKey], item)))
42941
+ return null;
42942
+ }
42943
+ for (const itemKey of Object.keys(operation.after)) {
42944
+ const original = operation.before[itemKey];
42945
+ if (original)
42946
+ target.items[itemKey] = structuredClone(original);
42947
+ else
42948
+ delete target.items[itemKey];
42949
+ }
42950
+ // Removing an inherited override must still resolve to the captured geometry.
42951
+ const effective = this.resolveCanvas(page.canvas, page.deviceLayouts?.[device]?.canvas);
42952
+ if (!effective || Object.keys(operation.after).some(itemKey => !this.sameJsonStructure(effective.items[itemKey], operation.canvas.items[itemKey])))
42953
+ return null;
42954
+ return page;
42955
+ }
42956
+ canUndoCanvasResize(key) {
42957
+ return this.canvasResizeUndoPage(key) !== null;
42958
+ }
42959
+ undoCanvasResize(key) {
42960
+ const page = this.canvasResizeUndoPage(key);
42961
+ if (!page)
42962
+ return;
42963
+ this.lastCanvasResize.set(null);
42964
+ this.closeSizeEditor(true);
42965
+ this.applyAuthoringPageUpdate(page, true);
42966
+ this.layoutAnnouncement = this.t('size.undoDone', 'Último redimensionamento desfeito, incluindo os vizinhos afetados.');
42967
+ }
42968
+ onSelectedCanvasResizePointerDown(handle, event) {
42969
+ const widget = this.selectedCanvasWidget();
42970
+ if (!widget)
42971
+ return;
42972
+ this.startCanvasResize(widget.key, handle, event);
42973
+ if (this.activeCanvasInteraction?.pointerId === event.pointerId && event.currentTarget instanceof HTMLElement) {
42974
+ event.currentTarget.focus({ preventScroll: true });
42975
+ }
42976
+ }
42977
+ onSelectedCanvasResizeKeydown(handle, event) {
42978
+ const widget = this.selectedCanvasWidget();
42979
+ if (widget)
42980
+ this.onCanvasResizeHandleKeydown(widget.key, handle, event);
42981
+ }
41695
42982
  selectCanvasWidget(widgetKey) {
41696
42983
  this.selectWidget(widgetKey);
41697
42984
  }
@@ -41734,36 +43021,35 @@ class DynamicWidgetPageComponent {
41734
43021
  if (!horizontal && !vertical)
41735
43022
  return;
41736
43023
  event.preventDefault();
43024
+ event.stopPropagation();
41737
43025
  const step = event.shiftKey ? 2 : 1;
41738
43026
  const delta = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -step : step;
41739
43027
  this.updateCanvasItem(widgetKey, (item) => {
41740
43028
  if (kind === 'drag') {
41741
43029
  return this.applyCanvasKeyboardDrag(item, event.key, delta);
41742
43030
  }
41743
- return this.applyCanvasKeyboardResize(item, event.key, delta);
43031
+ return this.applyCanvasKeyboardResize(item, event.key, delta, widgetKey);
41744
43032
  }, true, this.canvasAnnouncement(kind, widgetKey), this.blockedCanvasAnnouncement(kind), kind);
41745
43033
  }
41746
43034
  onCanvasResizeHandleKeydown(widgetKey, handle, event) {
41747
43035
  if (!this.canCustomizeCanvas() || !this.isCanvasMode())
41748
43036
  return;
43037
+ if (this.isCanvasWidgetInOverlay(widgetKey) || this.activeCanvasInteraction)
43038
+ return;
41749
43039
  this.selectCanvasWidget(widgetKey);
41750
43040
  const horizontal = event.key === 'ArrowLeft' || event.key === 'ArrowRight';
41751
43041
  const vertical = event.key === 'ArrowUp' || event.key === 'ArrowDown';
41752
43042
  if (!horizontal && !vertical)
41753
43043
  return;
41754
43044
  event.preventDefault();
43045
+ event.stopPropagation();
41755
43046
  const step = event.shiftKey ? 2 : 1;
41756
43047
  const delta = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -step : step;
41757
43048
  const colDelta = horizontal ? delta : 0;
41758
43049
  const rowDelta = vertical ? delta : 0;
41759
43050
  const columns = this.canvas?.columns || 1;
43051
+ const contentHeight = this.measureCanvasContentHeight(widgetKey);
41760
43052
  this.updateCanvasItem(widgetKey, (item) => this.applyCanvasResize(item, {
41761
- kind: 'resize',
41762
- widgetKey,
41763
- pointerId: -1,
41764
- startPage: this.clonePageDefinition(this.ensurePageDefinition()),
41765
- startX: 0,
41766
- startY: 0,
41767
43053
  startItem: {
41768
43054
  ...item,
41769
43055
  constraints: item.constraints
@@ -41771,11 +43057,10 @@ class DynamicWidgetPageComponent {
41771
43057
  : undefined,
41772
43058
  },
41773
43059
  columns,
41774
- columnWidth: 1,
41775
- rowHeight: 1,
41776
43060
  resizeHandle: handle,
41777
- blocked: false,
41778
- }, colDelta, rowDelta), true, this.canvasAnnouncement('resize', widgetKey), this.blockedCanvasAnnouncement('resize'), 'resize');
43061
+ contentHeight,
43062
+ minimumContentHeight: this.measureCanvasMinimumHeight(widgetKey),
43063
+ }, colDelta, contentHeight !== undefined ? rowDelta * 16 : rowDelta), true, this.canvasAnnouncement('resize', widgetKey), this.blockedCanvasAnnouncement('resize'), 'resize');
41779
43064
  }
41780
43065
  applyWidgetLayoutOverrides(widgets, overrides) {
41781
43066
  const rendered = widgets
@@ -41821,7 +43106,10 @@ class DynamicWidgetPageComponent {
41821
43106
  startCanvasInteraction(widgetKey, event, kind, resizeHandle) {
41822
43107
  if (!this.canCustomizeCanvas() || !this.isCanvasMode())
41823
43108
  return;
41824
- if (this.isCanvasOverlayInteraction(event))
43109
+ // One gesture owns the preview until commit/cancel, including across pointer types.
43110
+ if (this.activeCanvasInteraction || event.button > 0 || event.isPrimary === false)
43111
+ return;
43112
+ if (this.isCanvasOverlayInteraction(event) || this.isCanvasWidgetInOverlay(widgetKey))
41825
43113
  return;
41826
43114
  event.preventDefault();
41827
43115
  event.stopPropagation();
@@ -41831,7 +43119,6 @@ class DynamicWidgetPageComponent {
41831
43119
  const metrics = this.currentCanvasMetrics();
41832
43120
  if (!item || !metrics)
41833
43121
  return;
41834
- const pageSnapshot = this.clonePageDefinition(this.ensurePageDefinition());
41835
43122
  const pointerTarget = event.currentTarget instanceof HTMLElement
41836
43123
  ? event.currentTarget
41837
43124
  : undefined;
@@ -41846,7 +43133,6 @@ class DynamicWidgetPageComponent {
41846
43133
  widgetKey,
41847
43134
  pointerId: event.pointerId,
41848
43135
  pointerTarget,
41849
- startPage: pageSnapshot,
41850
43136
  startX: event.clientX,
41851
43137
  startY: event.clientY,
41852
43138
  startItem: {
@@ -41854,8 +43140,10 @@ class DynamicWidgetPageComponent {
41854
43140
  constraints: item.constraints ? { ...item.constraints } : undefined,
41855
43141
  },
41856
43142
  columns: metrics.columns,
41857
- columnWidth: metrics.columnWidth,
41858
- rowHeight: metrics.rowHeight,
43143
+ columnGeometry: metrics.columnGeometry,
43144
+ rowGeometry: metrics.rowGeometry,
43145
+ contentHeight: this.measureCanvasContentHeight(widgetKey),
43146
+ minimumContentHeight: this.measureCanvasMinimumHeight(widgetKey),
41859
43147
  resizeHandle,
41860
43148
  blocked: false,
41861
43149
  swappedWith: null,
@@ -41867,12 +43155,14 @@ class DynamicWidgetPageComponent {
41867
43155
  const interaction = this.activeCanvasInteraction;
41868
43156
  if (!interaction || interaction.widgetKey !== widgetKey)
41869
43157
  return;
41870
- this.releaseCanvasPointerCapture(interaction);
41871
43158
  this.activeCanvasInteraction = null;
43159
+ this.resizeFeedbackOrigin.set(null);
43160
+ this.releaseCanvasPointerCapture(interaction);
41872
43161
  this.blockedCanvasWidgetKey = null;
41873
43162
  this.canvasPreviewState.set(null);
41874
43163
  this.canvasPreviewInvalidState.set(false);
41875
43164
  this.transientCanvasItemsState.set({});
43165
+ this.resizeFeedbackState.set({});
41876
43166
  }
41877
43167
  isCanvasOverlayInteraction(event) {
41878
43168
  const target = event.currentTarget instanceof HTMLElement
@@ -41882,13 +43172,20 @@ class DynamicWidgetPageComponent {
41882
43172
  : null;
41883
43173
  return !!target?.closest('.pdx-shell.expanded, .pdx-shell.fullscreen');
41884
43174
  }
43175
+ isCanvasWidgetInOverlay(widgetKey) {
43176
+ const owner = Array.from(this.pageCanvasHost?.nativeElement.children || [])
43177
+ .find((element) => element.getAttribute('data-widget-key') === widgetKey);
43178
+ return !!owner?.querySelector(':scope > praxis-widget-shell > .pdx-shell.expanded, :scope > praxis-widget-shell > .pdx-shell.fullscreen, :scope > praxis-widget-shell > .pdx-shell.collapsed');
43179
+ }
41885
43180
  currentCanvasMetrics() {
41886
43181
  const canvas = this.canvas;
41887
43182
  const host = this.pageCanvasHost?.nativeElement;
41888
43183
  if (!canvas || !host)
41889
43184
  return null;
41890
43185
  const rect = host.getBoundingClientRect();
41891
- const gap = this.parseCssPixelValue(this.pageGap, 16);
43186
+ const style = host.ownerDocument.defaultView?.getComputedStyle(host);
43187
+ const gap = this.parseCssPixelValue(style?.columnGap || this.pageGap, 16);
43188
+ const rowGap = this.parseCssPixelValue(style?.rowGap || this.pageGap, 16);
41892
43189
  const columns = Math.max(1, Number(canvas.columns) || 1);
41893
43190
  const availableWidth = Math.max(1, rect.width - gap * Math.max(0, columns - 1));
41894
43191
  const columnWidth = availableWidth / columns;
@@ -41897,19 +43194,48 @@ class DynamicWidgetPageComponent {
41897
43194
  : this.parseCssPixelValue(canvas.rowUnit || this.gridAutoRows || '88px', 88);
41898
43195
  return {
41899
43196
  columns,
41900
- columnWidth: Math.max(1, columnWidth),
41901
- rowHeight: Math.max(1, rowHeight),
43197
+ columnGeometry: {
43198
+ sizes: readCanvasTrackSizes(style?.gridTemplateColumns || ''),
43199
+ gap,
43200
+ fallbackSize: Math.max(1, columnWidth),
43201
+ },
43202
+ rowGeometry: {
43203
+ sizes: readCanvasTrackSizes(style?.gridTemplateRows || ''),
43204
+ gap: rowGap,
43205
+ fallbackSize: Math.max(1, rowHeight),
43206
+ },
41902
43207
  };
41903
43208
  }
43209
+ measureCanvasContentHeight(key) {
43210
+ if (this.canvas?.autoRows !== 'content')
43211
+ return undefined;
43212
+ const owner = Array.from(this.pageCanvasHost?.nativeElement.children || [])
43213
+ .find(element => element.getAttribute('data-widget-key') === key);
43214
+ const height = owner?.getBoundingClientRect().height;
43215
+ return height && height > 0 ? height : undefined;
43216
+ }
43217
+ measureCanvasMinimumHeight(key) {
43218
+ if (this.canvas?.autoRows !== 'content')
43219
+ return undefined;
43220
+ const owner = Array.from(this.pageCanvasHost?.nativeElement.children || [])
43221
+ .find(element => element.getAttribute('data-widget-key') === key);
43222
+ const shell = owner?.querySelector(':scope > praxis-widget-shell > .pdx-shell');
43223
+ return shell ? measureWidgetShellHeight(shell)?.minimum : undefined;
43224
+ }
41904
43225
  currentCanvasItem(widgetKey) {
41905
43226
  return this.canvas?.items?.[widgetKey];
41906
43227
  }
41907
43228
  resolveCanvasInteractionDelta(interaction, clientX, clientY) {
41908
43229
  const deltaX = clientX - interaction.startX;
41909
43230
  const deltaY = clientY - interaction.startY;
43231
+ const start = interaction.startItem;
43232
+ const east = interaction.kind === 'resize' &&
43233
+ (interaction.resizeHandle || 'south-east').includes('east');
43234
+ const south = interaction.kind === 'resize' &&
43235
+ (interaction.resizeHandle || 'south-east').includes('south');
41910
43236
  return {
41911
- colDelta: Math.round(deltaX / Math.max(1, interaction.columnWidth)),
41912
- rowDelta: Math.round(deltaY / Math.max(1, interaction.rowHeight)),
43237
+ colDelta: canvasAxisDelta(interaction.columnGeometry, start.col - 1 + (east ? start.colSpan - 1 : 0), deltaX, east ? 'end' : 'start'),
43238
+ rowDelta: interaction.kind === 'resize' && interaction.contentHeight !== undefined ? Math.round(deltaY) : canvasAxisDelta(interaction.rowGeometry, start.row - 1 + (south ? start.rowSpan - 1 : 0), deltaY, south ? 'end' : 'start'),
41913
43239
  };
41914
43240
  }
41915
43241
  updateCanvasItem(widgetKey, updater, persist, announcement, blockedAnnouncement, interactionKind = 'drag') {
@@ -41921,14 +43247,21 @@ class DynamicWidgetPageComponent {
41921
43247
  const current = effectiveCanvas?.items[widgetKey];
41922
43248
  if (!current)
41923
43249
  return { changed: false, blocked: false };
41924
- const candidate = this.normalizeCanvasItem(updater({
43250
+ const update = updater({
41925
43251
  ...current,
41926
43252
  constraints: current.constraints
41927
43253
  ? { ...current.constraints }
41928
43254
  : undefined,
41929
- }), effectiveCanvas?.columns || page.canvas.columns || 1);
43255
+ });
43256
+ const { item: candidateItem, constrained } = 'item' in update
43257
+ ? update : { item: update, constrained: false };
43258
+ const candidate = this.normalizeCanvasItem(candidateItem, effectiveCanvas?.columns || page.canvas.columns || 1);
43259
+ if (interactionKind === 'resize' && effectiveCanvas) {
43260
+ return this.updateCanvasResize(widgetKey, candidate, effectiveCanvas, page, persist, announcement, constrained);
43261
+ }
43262
+ this.resizeFeedbackState.set({});
41930
43263
  const collisionPolicy = effectiveCanvas
41931
- ? this.resolveCanvasCollisionPolicy(effectiveCanvas, interactionKind)
43264
+ ? this.resolveCanvasCollisionPolicy(effectiveCanvas)
41932
43265
  : 'block';
41933
43266
  const placement = effectiveCanvas
41934
43267
  ? this.resolveCanvasPlacement(widgetKey, current, candidate, effectiveCanvas, collisionPolicy)
@@ -41952,7 +43285,7 @@ class DynamicWidgetPageComponent {
41952
43285
  this.blockedCanvasWidgetKey = null;
41953
43286
  this.canvasPreviewInvalidState.set(false);
41954
43287
  const changed = placement.changed;
41955
- if (!changed && !persist) {
43288
+ if (!changed) {
41956
43289
  this.transientCanvasItemsState.set({});
41957
43290
  this.canvasPreviewState.set(null);
41958
43291
  this.canvasPreviewInvalidState.set(false);
@@ -42001,28 +43334,74 @@ class DynamicWidgetPageComponent {
42001
43334
  }
42002
43335
  return { changed, blocked: false, swappedWith: placement.swappedWith };
42003
43336
  }
42004
- applyTransientPagePreview(next, runtime = this.pageRuntime) {
42005
- const normalized = { ...next, state: runtime.state };
42006
- const previewProjection = this.compositionRuntime.projectStateWidgetInputs(this.compositionFactory.create(normalized), {
42007
- widgets: normalized.widgets || [],
42008
- state: {
42009
- primaryValues: this.cloneStateValues(runtime.primaryValues),
42010
- derivedValues: this.cloneStateValues(runtime.derivedValues),
42011
- transientValues: {},
42012
- changedPaths: [],
42013
- diagnostics: [],
42014
- },
42015
- });
42016
- const stateBoundWidgets = previewProjection.widgets;
42017
- const hydrated = this.resolvePagePresets({
42018
- ...normalized,
42019
- widgets: stateBoundWidgets,
42020
- });
42021
- this.pageDefinition = hydrated;
42022
- this.page = hydrated;
42023
- this.pageState = runtime.state;
42024
- this.pageRuntime = runtime;
42025
- this.applyResponsivePresentation(hydrated, stateBoundWidgets, runtime);
43337
+ resizeFeedback(key) {
43338
+ return this.resizeFeedbackState()[key] ?? null;
43339
+ }
43340
+ hasResizeFeedback() {
43341
+ return Object.keys(this.resizeFeedbackState()).length > 0;
43342
+ }
43343
+ resizeFeedbackLabel(state) {
43344
+ const fallback = {
43345
+ valid: 'Ajuste disponível', minimum: 'Tamanho mínimo atingido',
43346
+ blocked: 'Limite excedido — prévia limitada ao espaço disponível',
43347
+ };
43348
+ return this.t(`resizeFeedback.${state}`, fallback[state]);
43349
+ }
43350
+ updateCanvasResize(key, candidate, canvas, page, persist, announcement, constrained = false) {
43351
+ const placement = resolveCanvasResizePlacement(key, candidate, canvas.items);
43352
+ const changed = Object.entries(placement.items).some(([itemKey, item]) => !this.isSameCanvasItem(canvas.items[itemKey], item));
43353
+ const feedback = {};
43354
+ for (const itemKey of Object.keys(placement.items))
43355
+ feedback[itemKey] = 'valid';
43356
+ for (const itemKey of placement.minimumKeys)
43357
+ feedback[itemKey] = 'minimum';
43358
+ const bounded = contentSize(placement.primaryItem);
43359
+ const minimum = Math.max(contentHeightLimits(placement.primaryItem).min, this.measureCanvasMinimumHeight(key) || 0);
43360
+ if (bounded && bounded.heightPx <= minimum)
43361
+ feedback[key] = 'minimum';
43362
+ for (const itemKey of placement.blockedKeys)
43363
+ feedback[itemKey] = 'blocked';
43364
+ const limited = constrained || placement.limited;
43365
+ if (limited)
43366
+ feedback[key] = 'blocked';
43367
+ const message = this.resizeFeedbackLabel(feedback[key]);
43368
+ if (persist) {
43369
+ this.resizeFeedbackState.set({});
43370
+ this.transientCanvasItemsState.set({});
43371
+ this.canvasPreviewState.set(null);
43372
+ this.canvasPreviewInvalidState.set(false);
43373
+ this.blockedCanvasWidgetKey = null;
43374
+ if (changed) {
43375
+ const target = this.resolveCanvasTarget(page);
43376
+ const affected = Object.keys(placement.items).filter(itemKey => !this.isSameCanvasItem(canvas.items[itemKey], placement.items[itemKey]));
43377
+ const before = Object.fromEntries(affected.map(itemKey => [itemKey, target.items[itemKey] ? structuredClone(target.items[itemKey]) : undefined]));
43378
+ for (const [itemKey, item] of Object.entries(placement.items)) {
43379
+ target.items[itemKey] = { ...item, constraints: item.constraints ? { ...item.constraints } : undefined };
43380
+ }
43381
+ const device = this.resolveDeviceKind();
43382
+ this.lastCanvasResize.set({
43383
+ key, scope: page.deviceLayouts?.[device]?.canvas ? device : 'base',
43384
+ canvas: structuredClone(canvas), before,
43385
+ after: Object.fromEntries(affected.map(itemKey => [itemKey, structuredClone(placement.items[itemKey])])),
43386
+ authoredAfter: Object.fromEntries(affected.map(itemKey => [itemKey, structuredClone(target.items[itemKey])])),
43387
+ });
43388
+ this.applyAuthoringPageUpdate(page, true);
43389
+ }
43390
+ this.layoutAnnouncement = this.positionedCanvasAnnouncement(!changed ? this.t('resizeFeedback.unchanged', 'Tamanho mantido — nenhum ajuste aplicado')
43391
+ : limited ? this.t('resizeFeedback.committedLimit', 'Layout ajustado até o limite permitido') : announcement || message, placement.primaryItem);
43392
+ }
43393
+ else {
43394
+ if (JSON.stringify(feedback) !== JSON.stringify(this.resizeFeedbackState())) {
43395
+ this.layoutAnnouncement = Object.entries(feedback).map(([itemKey, state]) => `${this.widgetContextToolbarLabel(itemKey)}: ${this.resizeFeedbackLabel(state)}`).join('. ');
43396
+ }
43397
+ this.resizeFeedbackState.set(feedback);
43398
+ this.transientCanvasItemsState.set(placement.items);
43399
+ // Render the real clamped distribution, never an impossible overlapping ghost.
43400
+ this.canvasPreviewState.set(placement.primaryItem);
43401
+ this.canvasPreviewInvalidState.set(false);
43402
+ this.blockedCanvasWidgetKey = null;
43403
+ }
43404
+ return { changed, blocked: limited };
42026
43405
  }
42027
43406
  resolveCanvasTarget(page) {
42028
43407
  const kind = this.resolveDeviceKind();
@@ -42051,7 +43430,15 @@ class DynamicWidgetPageComponent {
42051
43430
  applyCanvasResize(item, interaction, colDelta, rowDelta) {
42052
43431
  const locked = item.constraints?.lockSize || interaction.startItem.constraints?.lockSize;
42053
43432
  if (locked)
42054
- return item;
43433
+ return { item, constrained: colDelta !== 0 || rowDelta !== 0 };
43434
+ if (interaction.contentHeight !== undefined) {
43435
+ const horizontal = this.applyCanvasResize(item, { ...interaction, contentHeight: undefined }, colDelta, 0);
43436
+ const handle = interaction.resizeHandle || 'south-east';
43437
+ if (!handle.includes('north') && !handle.includes('south'))
43438
+ return horizontal;
43439
+ const vertical = resizeContentHeight({ ...horizontal.item, contentSize: interaction.startItem.contentSize }, interaction.contentHeight, rowDelta, handle.includes('north'), interaction.minimumContentHeight);
43440
+ return { item: vertical.item, constrained: horizontal.constrained || vertical.constrained };
43441
+ }
42055
43442
  const handle = interaction.resizeHandle || 'south-east';
42056
43443
  const start = interaction.startItem;
42057
43444
  const constraints = this.resolveCanvasConstraints(start, interaction.columns);
@@ -42079,12 +43466,16 @@ class DynamicWidgetPageComponent {
42079
43466
  nextRow = this.clampNumber(start.row + rowDelta, minRow, maxRow);
42080
43467
  nextRowSpan = bottomEdge - nextRow + 1;
42081
43468
  }
43469
+ // Keep attempted edges until feedback is decided. Otherwise clamping here
43470
+ // makes a gesture past an own minimum/maximum look like a valid request.
43471
+ const requestedCol = start.col + (handle.includes('west') ? colDelta : 0);
43472
+ const requestedRow = start.row + (handle.includes('north') ? rowDelta : 0);
43473
+ const requestedColSpan = start.colSpan + (handle.includes('east') ? colDelta : handle.includes('west') ? -colDelta : 0);
43474
+ const requestedRowSpan = start.rowSpan + (handle.includes('south') ? rowDelta : handle.includes('north') ? -rowDelta : 0);
42082
43475
  return {
42083
- ...item,
42084
- col: nextCol,
42085
- row: nextRow,
42086
- colSpan: nextColSpan,
42087
- rowSpan: nextRowSpan,
43476
+ item: { ...item, col: nextCol, row: nextRow, colSpan: nextColSpan, rowSpan: nextRowSpan },
43477
+ constrained: nextCol !== requestedCol || nextRow !== requestedRow
43478
+ || nextColSpan !== requestedColSpan || nextRowSpan !== requestedRowSpan,
42088
43479
  };
42089
43480
  }
42090
43481
  applyCanvasKeyboardDrag(item, key, delta) {
@@ -42101,21 +43492,11 @@ class DynamicWidgetPageComponent {
42101
43492
  row: Math.max(1, item.row + delta),
42102
43493
  };
42103
43494
  }
42104
- applyCanvasKeyboardResize(item, key, delta) {
42105
- if (item.constraints?.lockSize)
42106
- return item;
42107
- const columns = this.canvas?.columns || 1;
42108
- const constraints = this.resolveCanvasConstraints(item, columns);
42109
- if (key === 'ArrowLeft' || key === 'ArrowRight') {
42110
- return {
42111
- ...item,
42112
- colSpan: this.clampNumber(item.colSpan + delta, constraints.minColSpan, Math.min(constraints.maxColSpan, columns - item.col + 1)),
42113
- };
42114
- }
42115
- return {
42116
- ...item,
42117
- rowSpan: this.clampNumber(item.rowSpan + delta, constraints.minRowSpan, constraints.maxRowSpan),
42118
- };
43495
+ applyCanvasKeyboardResize(item, key, delta, widgetKey) {
43496
+ const horizontal = key === 'ArrowLeft' || key === 'ArrowRight';
43497
+ const contentHeight = this.measureCanvasContentHeight(widgetKey);
43498
+ return this.applyCanvasResize(item, { startItem: item, columns: this.canvas?.columns || 1, resizeHandle: 'south-east', contentHeight,
43499
+ minimumContentHeight: this.measureCanvasMinimumHeight(widgetKey) }, horizontal ? delta : 0, horizontal ? 0 : contentHeight !== undefined ? delta * 16 : delta);
42119
43500
  }
42120
43501
  pageSettingsLabel() {
42121
43502
  return this.t('controls.pageSettings', 'Configurar página');
@@ -42147,6 +43528,11 @@ class DynamicWidgetPageComponent {
42147
43528
  return this.t('announcements.swapped', 'Widget trocado com {{target}}: {{widget}}', { widget: widgetKey, target: targetWidgetKey });
42148
43529
  }
42149
43530
  positionedCanvasAnnouncement(action, item) {
43531
+ const size = this.canvas?.autoRows === 'content' ? contentSize(item) : null;
43532
+ if (size)
43533
+ return this.t('announcements.contentSize', '{{action}}. Largura {{columns}} colunas, altura {{height}} pixels, recuo superior {{top}} pixels.', {
43534
+ action, columns: item.colSpan, height: size.heightPx, top: size.offsetTopPx,
43535
+ });
42150
43536
  return this.t('announcements.position', '{{action}} para coluna {{col}}, linha {{row}}, largura {{colSpan}}, altura {{rowSpan}}.', {
42151
43537
  action,
42152
43538
  col: item.col,
@@ -42164,6 +43550,8 @@ class DynamicWidgetPageComponent {
42164
43550
  const maxColSpan = Math.max(minColSpan, item.constraints?.maxColSpan || columns);
42165
43551
  const maxRowSpan = Math.max(minRowSpan, item.constraints?.maxRowSpan || Number.MAX_SAFE_INTEGER);
42166
43552
  return {
43553
+ minHeightPx: contentHeightLimits(item).min,
43554
+ maxHeightPx: contentHeightLimits(item).max,
42167
43555
  minColSpan,
42168
43556
  minRowSpan,
42169
43557
  maxColSpan,
@@ -42186,10 +43574,7 @@ class DynamicWidgetPageComponent {
42186
43574
  rowSpan,
42187
43575
  };
42188
43576
  }
42189
- resolveCanvasCollisionPolicy(canvas, interactionKind) {
42190
- if (interactionKind === 'resize') {
42191
- return 'block';
42192
- }
43577
+ resolveCanvasCollisionPolicy(canvas) {
42193
43578
  return canvas.collisionPolicy === 'swap' ? 'swap' : 'block';
42194
43579
  }
42195
43580
  resolveCanvasPlacement(widgetKey, current, candidate, canvas, collisionPolicy) {
@@ -42278,11 +43663,15 @@ class DynamicWidgetPageComponent {
42278
43663
  leftBottom >= right.row);
42279
43664
  }
42280
43665
  isSameCanvasItem(left, right) {
43666
+ const leftSize = typeof left.contentSize === 'object' ? left.contentSize : undefined;
43667
+ const rightSize = typeof right.contentSize === 'object' ? right.contentSize : undefined;
42281
43668
  return (left.col === right.col &&
42282
43669
  left.row === right.row &&
42283
43670
  left.colSpan === right.colSpan &&
42284
43671
  left.rowSpan === right.rowSpan &&
42285
- left.zIndex === right.zIndex);
43672
+ left.zIndex === right.zIndex &&
43673
+ leftSize?.heightPx === rightSize?.heightPx &&
43674
+ (leftSize?.offsetTopPx ?? 0) === (rightSize?.offsetTopPx ?? 0));
42286
43675
  }
42287
43676
  clampCanvasCol(col, span, columns) {
42288
43677
  return this.clampNumber(col, 1, Math.max(1, columns - span + 1));
@@ -42479,6 +43868,12 @@ class DynamicWidgetPageComponent {
42479
43868
  return null;
42480
43869
  return `${canvasItem.row} / span ${canvasItem.rowSpan}`;
42481
43870
  }
43871
+ widgetContentSize(widget) {
43872
+ if (this.canvas?.autoRows !== 'content')
43873
+ return null;
43874
+ const item = this.resolveRenderedCanvasItem(widget) || this.canvas.items[widget.key];
43875
+ return item ? contentSize(item) : null;
43876
+ }
42482
43877
  widgetZIndex(widget) {
42483
43878
  return this.resolveRenderedCanvasItem(widget)?.zIndex ?? null;
42484
43879
  }
@@ -42794,7 +44189,7 @@ class DynamicWidgetPageComponent {
42794
44189
  throw new Error('DynamicWidgetPageComponent no longer accepts `page.connections`. Use `composition.links` only.');
42795
44190
  }
42796
44191
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicWidgetPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
42797
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicWidgetPageComponent, isStandalone: true, selector: "praxis-dynamic-page", inputs: { page: "page", context: "context", strictValidation: "strictValidation", enableCustomization: "enableCustomization", authoringCapabilities: "authoringCapabilities", showPageSettingsButton: "showPageSettingsButton", shellEditorComponent: "shellEditorComponent", pageEditorComponent: "pageEditorComponent", autoPersist: "autoPersist", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", showWidgetAssistantButton: "showWidgetAssistantButton" }, outputs: { pageChange: "pageChange", pageAuthoringChange: "pageAuthoringChange", widgetEvent: "widgetEvent", widgetSelectionChange: "widgetSelectionChange", widgetAssistantRequested: "widgetAssistantRequested", widgetDiagnosticsChange: "widgetDiagnosticsChange" }, host: { listeners: { "window:resize": "onWindowResize()", "window:pointermove": "onCanvasPointerMove($event)", "window:pointerup": "onCanvasPointerUp($event)", "window:pointercancel": "onCanvasPointerCancel($event)" } }, providers: [providePraxisI18nConfig(DYNAMIC_WIDGET_PAGE_I18N_CONFIG)], viewQueries: [{ propertyName: "pageCanvasHost", first: true, predicate: ["pageCanvasHost"], descendants: true }, { propertyName: "widgetLoaders", predicate: ["widgetLoader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
44192
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicWidgetPageComponent, isStandalone: true, selector: "praxis-dynamic-page", inputs: { page: "page", context: "context", strictValidation: "strictValidation", enableCustomization: "enableCustomization", authoringCapabilities: "authoringCapabilities", showPageSettingsButton: "showPageSettingsButton", shellEditorComponent: "shellEditorComponent", pageEditorComponent: "pageEditorComponent", autoPersist: "autoPersist", pageIdentity: "pageIdentity", componentInstanceId: "componentInstanceId", showWidgetAssistantButton: "showWidgetAssistantButton" }, outputs: { pageChange: "pageChange", pageAuthoringChange: "pageAuthoringChange", widgetEvent: "widgetEvent", widgetSelectionChange: "widgetSelectionChange", widgetAssistantRequested: "widgetAssistantRequested", widgetDiagnosticsChange: "widgetDiagnosticsChange" }, host: { listeners: { "lostpointercapture": "onCanvasPointerCancel($event)", "window:keydown.escape": "onCanvasEscape($event)", "window:resize": "onWindowResize()", "window:pointermove": "onCanvasPointerMove($event)", "window:pointerup": "onCanvasPointerUp($event)", "window:pointercancel": "onCanvasPointerCancel($event)" } }, providers: [providePraxisI18nConfig(DYNAMIC_WIDGET_PAGE_I18N_CONFIG)], viewQueries: [{ propertyName: "pageCanvasHost", first: true, predicate: ["pageCanvasHost"], descendants: true }, { propertyName: "widgetLoaders", predicate: ["widgetLoader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
42798
44193
  <div
42799
44194
  class="pdx-page-wrapper"
42800
44195
  [class.editing]="enableCustomization"
@@ -42815,6 +44210,50 @@ class DynamicWidgetPageComponent {
42815
44210
  (click)="openPageSettings()"
42816
44211
  ></button>
42817
44212
  }
44213
+ @if (sizeEditorOrigin(); as origin) {
44214
+ @if (sizeEditorSeed(); as seed) {
44215
+ <ng-template cdkConnectedOverlay #sizeOverlay="cdkConnectedOverlay"
44216
+ [cdkConnectedOverlayOrigin]="origin" [cdkConnectedOverlayOpen]="true"
44217
+ [cdkConnectedOverlayPositions]="sizeEditorPositions" [cdkConnectedOverlayPush]="true"
44218
+ [cdkConnectedOverlayViewportMargin]="16" [cdkConnectedOverlayHasBackdrop]="true"
44219
+ cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
44220
+ (backdropClick)="closeSizeEditor(true)" (overlayKeydown)="onSizeEditorKeydown($event)"
44221
+ (attach)="focusSizeEditor(sizeOverlay.overlayRef)">
44222
+ <section role="dialog" aria-modal="true" [attr.aria-label]="sizeEditorLabel()"
44223
+ cdkTrapFocus data-testid="canvas-size-editor">
44224
+ <praxis-canvas-size-editor [width]="seed.item.colSpan" [height]="seed.height"
44225
+ [contentRows]="seed.contentRows" [widgetLabel]="sizeEditorWidgetLabel()"
44226
+ [columns]="seed.columns" [hasUndo]="hasCanvasResizeUndo(seed.key)"
44227
+ [canUndo]="canUndoCanvasResize(seed.key)" (undo)="undoCanvasResize(seed.key)"
44228
+ [scopeLabel]="sizeEditorScopeLabel()" [error]="sizeEditorError()"
44229
+ (preview)="previewCanvasSize($event)" (apply)="applyCanvasSize($event)"
44230
+ (cancel)="closeSizeEditor(true)" />
44231
+ </section>
44232
+ </ng-template>
44233
+ }
44234
+ }
44235
+ @if (resizeFeedbackOrigin(); as origin) {
44236
+ @if (selectedCanvasWidget(); as widget) {
44237
+ @if (resizeFeedback(widget.key); as feedback) {
44238
+ <ng-template cdkConnectedOverlay #feedbackOverlay="cdkConnectedOverlay"
44239
+ [cdkConnectedOverlayOrigin]="origin"
44240
+ [cdkConnectedOverlayOpen]="true"
44241
+ [cdkConnectedOverlayPositions]="resizeFeedbackPositions"
44242
+ [cdkConnectedOverlayPush]="true"
44243
+ [cdkConnectedOverlayViewportMargin]="12"
44244
+ [cdkConnectedOverlayHasBackdrop]="false"
44245
+ [cdkConnectedOverlayDisableClose]="true"
44246
+ (attach)="prepareResizeFeedbackOverlay(feedbackOverlay.overlayRef.overlayElement)">
44247
+ <div class="pdx-resize-feedback pdx-resize-feedback--floating"
44248
+ data-testid="canvas-resize-feedback" [attr.data-feedback-widget]="widget.key"
44249
+ [attr.data-resize-feedback]="feedback">
44250
+ <mat-icon aria-hidden="true">{{ feedback === 'valid' ? 'check_circle' : feedback === 'minimum' ? 'warning' : 'block' }}</mat-icon>
44251
+ <span><strong>{{ widgetContextLabel(widget) }}</strong>{{ resizeFeedbackLabel(feedback) }}</span>
44252
+ </div>
44253
+ </ng-template>
44254
+ }
44255
+ }
44256
+ }
42818
44257
  <div
42819
44258
  #pageCanvasHost
42820
44259
  class="pdx-page"
@@ -42828,6 +44267,11 @@ class DynamicWidgetPageComponent {
42828
44267
  <div
42829
44268
  class="pdx-widget pdx-widget--canvas"
42830
44269
  [attr.data-widget-key]="w.key"
44270
+ [attr.tabindex]="canCustomizeCanvas() ? 0 : null"
44271
+ [attr.role]="canCustomizeCanvas() ? 'group' : null"
44272
+ [attr.aria-label]="canCustomizeCanvas() ? canvasSelectionLabel(w.key) : null"
44273
+ (keydown)="onCanvasSelectionKeydown(w.key, $event)"
44274
+ [attr.data-resize-feedback]="resizeFeedback(w.key)"
42831
44275
  [class.pdx-widget--interactive]="canSelectWidgets()"
42832
44276
  [class.pdx-widget--selected]="
42833
44277
  canSelectWidgets() && isWidgetSelected(w.key)
@@ -42841,30 +44285,16 @@ class DynamicWidgetPageComponent {
42841
44285
  [class]="widgetClassName(w)"
42842
44286
  [style.gridColumn]="widgetGridColumn(w)"
42843
44287
  [style.gridRow]="widgetGridRow(w)"
44288
+ [style.height.px]="canvasShell.collapsed ? null : widgetContentSize(w)?.heightPx"
44289
+ [style.marginTop.px]="canvasShell.collapsed ? null : widgetContentSize(w)?.offsetTopPx"
44290
+ [style.alignSelf]="widgetContentSize(w) ? 'start' : null"
42844
44291
  [style.zIndex]="widgetZIndex(w)"
42845
44292
  (pointerdown)="selectWidget(w.key)"
42846
44293
  (click)="selectWidgetFromHostEvent(w.key, $event)"
42847
44294
  (focusin)="selectWidgetFromHostEvent(w.key, $event)"
42848
44295
  >
42849
- @if (canCustomizeCanvas()) {
42850
- @for (handle of canvasResizeHandles; track handle.id) {
42851
- <button
42852
- class="pdx-canvas-resize"
42853
- [ngClass]="handle.className"
42854
- type="button"
42855
- [attr.data-resize-handle]="handle.id"
42856
- [attr.aria-label]="resizeHandleLabel(handle.id)"
42857
- [title]="resizeHandleLabel(handle.id)"
42858
- (pointerdown)="startCanvasResize(w.key, handle.id, $event)"
42859
- (keydown)="
42860
- onCanvasResizeHandleKeydown(w.key, handle.id, $event)
42861
- "
42862
- >
42863
- <span class="pdx-canvas-resize-grip"></span>
42864
- </button>
42865
- }
42866
- }
42867
44296
  <praxis-widget-shell
44297
+ #canvasShell
42868
44298
  [shell]="widgetShellForRender(w)"
42869
44299
  [context]="mergedContext"
42870
44300
  [dragSurfaceEnabled]="canCustomizeCanvas() && isCanvasMode()"
@@ -42886,8 +44316,35 @@ class DynamicWidgetPageComponent {
42886
44316
  (widgetDiagnostic)="onWidgetDiagnostic(w.key, $event)"
42887
44317
  ></ng-container>
42888
44318
  </praxis-widget-shell>
44319
+ @if (resizeFeedback(w.key); as feedback) {
44320
+ @if (!resizeFeedbackOrigin() || selectedCanvasWidget()?.key !== w.key) {
44321
+ <div class="pdx-resize-feedback" data-testid="canvas-resize-feedback">
44322
+ <mat-icon aria-hidden="true">{{ feedback === 'valid' ? 'check_circle' : feedback === 'minimum' ? 'warning' : 'block' }}</mat-icon>
44323
+ <span>{{ resizeFeedbackLabel(feedback) }}</span>
44324
+ </div>
44325
+ }
44326
+ }
44327
+ @if (canShowCanvasEdges(w.key)) {
44328
+ <div class="pdx-canvas-edges" data-testid="canvas-edge-controls"
44329
+ role="group" [attr.aria-label]="resizeWidgetLabel()">
44330
+ @for (handle of canvasResizeHandles; track handle.id) {
44331
+ <button type="button" class="pdx-canvas-edge"
44332
+ [attr.data-canvas-edge]="handle.id"
44333
+ [attr.aria-label]="resizeHandleLabel(handle.id)"
44334
+ [matTooltip]="resizeHandleLabel(handle.id)"
44335
+ [matTooltipDisabled]="canvasInteractionActive()"
44336
+ (pointerdown)="onSelectedCanvasResizePointerDown(handle.id, $event)"
44337
+ (keydown)="onSelectedCanvasResizeKeydown(handle.id, $event)"
44338
+ (click)="$event.stopPropagation()"></button>
44339
+ }
44340
+ </div>
44341
+ }
42889
44342
  @if (shouldRenderWidgetContextOverlay(w)) {
42890
44343
  <praxis-dynamic-widget-context-toolbar
44344
+ [showSize]="canCustomizeCanvas()"
44345
+ [sizeDisabled]="isCanvasSizeLocked(w.key)"
44346
+ [sizeLabel]="sizeEditorLabel()"
44347
+ (size)="openSizeEditor(w.key)"
42891
44348
  [toolbarLabel]="widgetContextToolbarLabel(w.key)"
42892
44349
  [contextLabel]="widgetContextLabel(w)"
42893
44350
  [contextTooltip]="widgetContextTooltip(w)"
@@ -42916,7 +44373,7 @@ class DynamicWidgetPageComponent {
42916
44373
  }
42917
44374
  </div>
42918
44375
  }
42919
- @if (canvasPreviewItem()) {
44376
+ @if (canvasPreviewItem() && !hasResizeFeedback()) {
42920
44377
  <div
42921
44378
  class="pdx-canvas-snap-preview"
42922
44379
  [class.pdx-canvas-snap-preview--invalid]="canvasPreviewInvalid()"
@@ -43189,15 +44646,21 @@ class DynamicWidgetPageComponent {
43189
44646
  </div>
43190
44647
  }
43191
44648
  </div>
43192
- `, isInline: true, styles: [":host{display:block;min-width:0}.pdx-page-wrapper{position:relative;display:block;background:var(--pdx-page-bg, transparent);color:var(--pdx-page-color, inherit)}.pdx-page{display:grid;gap:var(--pdx-page-gap, 12px);grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-wrapper.editing .pdx-page--canvas{padding-block-start:var(--pdx-canvas-context-toolbar-safe-area, 22px);padding-block-end:22px;padding-inline:22px;box-sizing:border-box}.pdx-page-settings{position:sticky;top:8px;z-index:2;color:inherit;margin-bottom:6px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:6px;min-width:0;min-height:0}.pdx-widget--interactive{cursor:pointer}.pdx-widget--selected:before{content:\"\";position:absolute;inset:0;border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);border-radius:14px;pointer-events:none;z-index:1;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-horizontal: linear-gradient( 90deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-vertical: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-sw: linear-gradient( 225deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas:has(.pdx-canvas-resize:hover):after,.pdx-widget--canvas:has(.pdx-canvas-resize:focus-visible):after{opacity:.82}.pdx-widget--canvas:has(.pdx-canvas-resize--north:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-horizontal)}.pdx-widget--canvas:has(.pdx-canvas-resize--east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-vertical)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-sw)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize{position:absolute;z-index:7;border:0;padding:0;margin:0;background:transparent;touch-action:none;cursor:pointer;opacity:0;pointer-events:none;transform:scale(.94);transition:opacity .14s ease,transform .14s ease,filter .14s ease;filter:saturate(.9)}.pdx-widget--canvas:hover .pdx-canvas-resize,.pdx-widget--canvas:focus-within .pdx-canvas-resize,.pdx-widget--canvas-selected .pdx-canvas-resize{opacity:.88;pointer-events:auto;transform:scale(1);filter:saturate(1)}.pdx-widget--canvas:has(.pdx-shell.expanded) .pdx-canvas-resize,.pdx-widget--canvas:has(.pdx-shell.fullscreen) .pdx-canvas-resize{opacity:0;pointer-events:none;transform:scale(.94)}.pdx-canvas-resize-grip{display:block;width:9px;height:9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 42%,var(--md-sys-color-outline-variant) 58%);background:color-mix(in srgb,var(--md-sys-color-surface) 92%,var(--md-sys-color-primary) 8%);box-shadow:0 1px 4px color-mix(in srgb,var(--md-sys-color-shadow) 10%,transparent);transition:transform .14s ease,border-color .14s ease,background .14s ease,box-shadow .14s ease}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:color-mix(in srgb,var(--md-sys-color-primary) 58%,var(--md-sys-color-outline-variant) 42%);background:color-mix(in srgb,var(--md-sys-color-surface) 84%,var(--md-sys-color-primary) 16%);box-shadow:0 2px 7px color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);transform:scale(1.04)}.pdx-canvas-resize--north,.pdx-canvas-resize--south{left:50%;width:52px;height:44px;margin-left:-26px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north{top:-22px;cursor:ns-resize}.pdx-canvas-resize--south{bottom:-22px;cursor:ns-resize}.pdx-canvas-resize--east,.pdx-canvas-resize--west{top:50%;width:44px;height:52px;margin-top:-26px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--east{right:-22px;cursor:ew-resize}.pdx-canvas-resize--west{left:-22px;cursor:ew-resize}.pdx-canvas-resize--north-east,.pdx-canvas-resize--north-west,.pdx-canvas-resize--south-east,.pdx-canvas-resize--south-west{width:44px;height:44px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north-east{top:-22px;right:-22px;cursor:nesw-resize}.pdx-canvas-resize--north-west{top:-22px;left:-22px;cursor:nwse-resize}.pdx-canvas-resize--south-east{bottom:-22px;right:-22px;cursor:nwse-resize}.pdx-canvas-resize--south-west{bottom:-22px;left:-22px;cursor:nesw-resize}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}.pdx-canvas-resize:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:transparent;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent),var(--mat-elevation-level3)}.pdx-canvas-resize--north:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-horizontal)}.pdx-canvas-resize--east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-vertical)}.pdx-canvas-resize--north-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-sw)}.pdx-canvas-resize--north-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-west:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{transform:scale(1.08)}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "component", type: DynamicWidgetContextToolbarComponent, selector: "praxis-dynamic-widget-context-toolbar", inputs: ["toolbarLabel", "contextLabel", "contextTooltip", "showAssistant", "assistantLabel", "assistantTooltip", "showComponentSettings", "componentSettingsBusy", "componentSettingsLabel", "componentSettingsTooltip", "showShellSettings", "shellSettingsLabel", "shellSettingsTooltip", "showRemove", "moreActionsLabel", "removeLabel"], outputs: ["assistant", "componentSettings", "shellSettings", "remove"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context", "dragSurfaceEnabled", "dragSurfaceLabel"], outputs: ["action", "dragSurfacePointerDown", "dragSurfaceKeydown"] }] });
44649
+ `, isInline: true, styles: [":host{display:block;min-width:0}.pdx-page-wrapper{position:relative;display:block;background:var(--pdx-page-bg, transparent);color:var(--pdx-page-color, inherit)}.pdx-page{display:grid;gap:var(--pdx-page-gap, 12px);grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-wrapper.editing .pdx-page--canvas{padding-block-start:var(--pdx-canvas-context-toolbar-safe-area, 22px);padding-block-end:22px;padding-inline:22px;box-sizing:border-box}.pdx-page-settings{position:sticky;top:8px;z-index:2;color:inherit;margin-bottom:6px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:6px;min-width:0;min-height:0}.pdx-widget--interactive{cursor:pointer}.pdx-widget--selected:before{content:\"\";position:absolute;inset:0;border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);border-radius:14px;pointer-events:none;z-index:1;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas-selected:has(>.pdx-canvas-edges .pdx-canvas-edge:hover):after,.pdx-widget--canvas-selected:has(>.pdx-canvas-edges .pdx-canvas-edge:focus-visible):after{opacity:.82}[data-resize-feedback=valid]{--pdx-resize-feedback-color: #27864b}[data-resize-feedback=minimum]{--pdx-resize-feedback-color: #bc8a00}[data-resize-feedback=blocked]{--pdx-resize-feedback-color: var(--md-sys-color-error)}.pdx-widget--canvas[data-resize-feedback]:after{background:var(--pdx-resize-feedback-color);opacity:1;z-index:2}.pdx-resize-feedback{position:absolute;inset-inline:8px;bottom:8px;width:fit-content;max-width:calc(100% - 16px);box-sizing:border-box;display:flex;align-items:center;gap:6px;padding:4px 8px;border:2px solid var(--pdx-resize-feedback-color);border-radius:8px;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface);font-size:12px;line-height:18px;pointer-events:none;z-index:3}.pdx-resize-feedback mat-icon{flex:0 0 18px;width:18px;height:18px;font-size:18px}.pdx-resize-feedback--floating{position:static;max-width:min(320px,calc(100vw - 24px));overflow-wrap:anywhere}.pdx-resize-feedback--floating strong{display:block}.pdx-canvas-edges{position:absolute;inset:0;pointer-events:none;z-index:5}.pdx-canvas-edge{position:absolute;display:grid;place-items:center;box-sizing:border-box;width:24px;height:24px;padding:0;border:0;background:transparent;color:var(--md-sys-color-primary);pointer-events:auto;touch-action:none;transform:translate(-50%,-50%)}.pdx-canvas-edge:after{content:\"\";width:8px;height:8px;border:2px solid currentColor;border-radius:2px;background:var(--md-sys-color-surface)}.pdx-canvas-edge:hover:after,.pdx-canvas-edge:focus-visible:after{background:var(--md-sys-color-primary-container)}.pdx-canvas-edge[data-canvas-edge=north-west]{left:0;top:0;cursor:nwse-resize}.pdx-canvas-edge[data-canvas-edge=north]{left:50%;top:0;cursor:ns-resize}.pdx-canvas-edge[data-canvas-edge=north-east]{left:100%;top:0;cursor:nesw-resize}.pdx-canvas-edge[data-canvas-edge=east]{left:100%;top:50%;cursor:ew-resize}.pdx-canvas-edge[data-canvas-edge=south-east]{left:100%;top:100%;cursor:nwse-resize}.pdx-canvas-edge[data-canvas-edge=south]{left:50%;top:100%;cursor:ns-resize}.pdx-canvas-edge[data-canvas-edge=south-west]{left:0;top:100%;cursor:nesw-resize}.pdx-canvas-edge[data-canvas-edge=west]{left:0;top:50%;cursor:ew-resize}.pdx-page-wrapper:has(>.pdx-page>.pdx-widget>praxis-widget-shell>.pdx-shell.expanded)>.pdx-page>.pdx-widget>.pdx-canvas-edges,.pdx-page-wrapper:has(>.pdx-page>.pdx-widget>praxis-widget-shell>.pdx-shell.fullscreen)>.pdx-page>.pdx-widget>.pdx-canvas-edges{display:none}.pdx-widget--canvas:focus-visible,.pdx-canvas-edge:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}@media(prefers-reduced-motion:reduce){.pdx-canvas-snap-preview{animation:none}.pdx-widget--canvas:after,.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{transition:none}}@media(forced-colors:active){.pdx-widget--canvas-selected:before{border:2px solid Highlight}.pdx-widget--canvas:focus-visible,.pdx-canvas-edge:focus-visible{outline-color:Highlight}}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i4$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i5$2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: CanvasSizeEditorComponent, selector: "praxis-canvas-size-editor", inputs: ["columns", "hasUndo", "canUndo", "width", "height", "contentRows", "widgetLabel", "scopeLabel", "error"], outputs: ["undo", "preview", "apply", "cancel"] }, { kind: "component", type: DynamicWidgetContextToolbarComponent, selector: "praxis-dynamic-widget-context-toolbar", inputs: ["showSize", "sizeDisabled", "sizeLabel", "toolbarLabel", "contextLabel", "contextTooltip", "showAssistant", "assistantLabel", "assistantTooltip", "showComponentSettings", "componentSettingsBusy", "componentSettingsLabel", "componentSettingsTooltip", "showShellSettings", "shellSettingsLabel", "shellSettingsTooltip", "showRemove", "moreActionsLabel", "removeLabel"], outputs: ["size", "assistant", "componentSettings", "shellSettings", "remove"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "component", type: WidgetShellComponent, selector: "praxis-widget-shell", inputs: ["shell", "context", "dragSurfaceEnabled", "dragSurfaceLabel"], outputs: ["action", "dragSurfacePointerDown", "dragSurfaceKeydown"] }] });
43193
44650
  }
43194
44651
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicWidgetPageComponent, decorators: [{
43195
44652
  type: Component,
43196
- args: [{ selector: 'praxis-dynamic-page', standalone: true, imports: [
44653
+ args: [{ selector: 'praxis-dynamic-page', host: {
44654
+ '(lostpointercapture)': 'onCanvasPointerCancel($event)',
44655
+ '(window:keydown.escape)': 'onCanvasEscape($event)',
44656
+ }, standalone: true, imports: [
43197
44657
  CommonModule,
43198
44658
  MatIconModule,
43199
44659
  MatTooltipModule,
43200
44660
  PraxisIconButtonComponent,
44661
+ OverlayModule,
44662
+ A11yModule,
44663
+ CanvasSizeEditorComponent,
43201
44664
  DynamicWidgetContextToolbarComponent,
43202
44665
  DynamicWidgetLoaderDirective,
43203
44666
  WidgetShellComponent,
@@ -43222,6 +44685,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43222
44685
  (click)="openPageSettings()"
43223
44686
  ></button>
43224
44687
  }
44688
+ @if (sizeEditorOrigin(); as origin) {
44689
+ @if (sizeEditorSeed(); as seed) {
44690
+ <ng-template cdkConnectedOverlay #sizeOverlay="cdkConnectedOverlay"
44691
+ [cdkConnectedOverlayOrigin]="origin" [cdkConnectedOverlayOpen]="true"
44692
+ [cdkConnectedOverlayPositions]="sizeEditorPositions" [cdkConnectedOverlayPush]="true"
44693
+ [cdkConnectedOverlayViewportMargin]="16" [cdkConnectedOverlayHasBackdrop]="true"
44694
+ cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
44695
+ (backdropClick)="closeSizeEditor(true)" (overlayKeydown)="onSizeEditorKeydown($event)"
44696
+ (attach)="focusSizeEditor(sizeOverlay.overlayRef)">
44697
+ <section role="dialog" aria-modal="true" [attr.aria-label]="sizeEditorLabel()"
44698
+ cdkTrapFocus data-testid="canvas-size-editor">
44699
+ <praxis-canvas-size-editor [width]="seed.item.colSpan" [height]="seed.height"
44700
+ [contentRows]="seed.contentRows" [widgetLabel]="sizeEditorWidgetLabel()"
44701
+ [columns]="seed.columns" [hasUndo]="hasCanvasResizeUndo(seed.key)"
44702
+ [canUndo]="canUndoCanvasResize(seed.key)" (undo)="undoCanvasResize(seed.key)"
44703
+ [scopeLabel]="sizeEditorScopeLabel()" [error]="sizeEditorError()"
44704
+ (preview)="previewCanvasSize($event)" (apply)="applyCanvasSize($event)"
44705
+ (cancel)="closeSizeEditor(true)" />
44706
+ </section>
44707
+ </ng-template>
44708
+ }
44709
+ }
44710
+ @if (resizeFeedbackOrigin(); as origin) {
44711
+ @if (selectedCanvasWidget(); as widget) {
44712
+ @if (resizeFeedback(widget.key); as feedback) {
44713
+ <ng-template cdkConnectedOverlay #feedbackOverlay="cdkConnectedOverlay"
44714
+ [cdkConnectedOverlayOrigin]="origin"
44715
+ [cdkConnectedOverlayOpen]="true"
44716
+ [cdkConnectedOverlayPositions]="resizeFeedbackPositions"
44717
+ [cdkConnectedOverlayPush]="true"
44718
+ [cdkConnectedOverlayViewportMargin]="12"
44719
+ [cdkConnectedOverlayHasBackdrop]="false"
44720
+ [cdkConnectedOverlayDisableClose]="true"
44721
+ (attach)="prepareResizeFeedbackOverlay(feedbackOverlay.overlayRef.overlayElement)">
44722
+ <div class="pdx-resize-feedback pdx-resize-feedback--floating"
44723
+ data-testid="canvas-resize-feedback" [attr.data-feedback-widget]="widget.key"
44724
+ [attr.data-resize-feedback]="feedback">
44725
+ <mat-icon aria-hidden="true">{{ feedback === 'valid' ? 'check_circle' : feedback === 'minimum' ? 'warning' : 'block' }}</mat-icon>
44726
+ <span><strong>{{ widgetContextLabel(widget) }}</strong>{{ resizeFeedbackLabel(feedback) }}</span>
44727
+ </div>
44728
+ </ng-template>
44729
+ }
44730
+ }
44731
+ }
43225
44732
  <div
43226
44733
  #pageCanvasHost
43227
44734
  class="pdx-page"
@@ -43235,6 +44742,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43235
44742
  <div
43236
44743
  class="pdx-widget pdx-widget--canvas"
43237
44744
  [attr.data-widget-key]="w.key"
44745
+ [attr.tabindex]="canCustomizeCanvas() ? 0 : null"
44746
+ [attr.role]="canCustomizeCanvas() ? 'group' : null"
44747
+ [attr.aria-label]="canCustomizeCanvas() ? canvasSelectionLabel(w.key) : null"
44748
+ (keydown)="onCanvasSelectionKeydown(w.key, $event)"
44749
+ [attr.data-resize-feedback]="resizeFeedback(w.key)"
43238
44750
  [class.pdx-widget--interactive]="canSelectWidgets()"
43239
44751
  [class.pdx-widget--selected]="
43240
44752
  canSelectWidgets() && isWidgetSelected(w.key)
@@ -43248,30 +44760,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43248
44760
  [class]="widgetClassName(w)"
43249
44761
  [style.gridColumn]="widgetGridColumn(w)"
43250
44762
  [style.gridRow]="widgetGridRow(w)"
44763
+ [style.height.px]="canvasShell.collapsed ? null : widgetContentSize(w)?.heightPx"
44764
+ [style.marginTop.px]="canvasShell.collapsed ? null : widgetContentSize(w)?.offsetTopPx"
44765
+ [style.alignSelf]="widgetContentSize(w) ? 'start' : null"
43251
44766
  [style.zIndex]="widgetZIndex(w)"
43252
44767
  (pointerdown)="selectWidget(w.key)"
43253
44768
  (click)="selectWidgetFromHostEvent(w.key, $event)"
43254
44769
  (focusin)="selectWidgetFromHostEvent(w.key, $event)"
43255
44770
  >
43256
- @if (canCustomizeCanvas()) {
43257
- @for (handle of canvasResizeHandles; track handle.id) {
43258
- <button
43259
- class="pdx-canvas-resize"
43260
- [ngClass]="handle.className"
43261
- type="button"
43262
- [attr.data-resize-handle]="handle.id"
43263
- [attr.aria-label]="resizeHandleLabel(handle.id)"
43264
- [title]="resizeHandleLabel(handle.id)"
43265
- (pointerdown)="startCanvasResize(w.key, handle.id, $event)"
43266
- (keydown)="
43267
- onCanvasResizeHandleKeydown(w.key, handle.id, $event)
43268
- "
43269
- >
43270
- <span class="pdx-canvas-resize-grip"></span>
43271
- </button>
43272
- }
43273
- }
43274
44771
  <praxis-widget-shell
44772
+ #canvasShell
43275
44773
  [shell]="widgetShellForRender(w)"
43276
44774
  [context]="mergedContext"
43277
44775
  [dragSurfaceEnabled]="canCustomizeCanvas() && isCanvasMode()"
@@ -43293,8 +44791,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43293
44791
  (widgetDiagnostic)="onWidgetDiagnostic(w.key, $event)"
43294
44792
  ></ng-container>
43295
44793
  </praxis-widget-shell>
44794
+ @if (resizeFeedback(w.key); as feedback) {
44795
+ @if (!resizeFeedbackOrigin() || selectedCanvasWidget()?.key !== w.key) {
44796
+ <div class="pdx-resize-feedback" data-testid="canvas-resize-feedback">
44797
+ <mat-icon aria-hidden="true">{{ feedback === 'valid' ? 'check_circle' : feedback === 'minimum' ? 'warning' : 'block' }}</mat-icon>
44798
+ <span>{{ resizeFeedbackLabel(feedback) }}</span>
44799
+ </div>
44800
+ }
44801
+ }
44802
+ @if (canShowCanvasEdges(w.key)) {
44803
+ <div class="pdx-canvas-edges" data-testid="canvas-edge-controls"
44804
+ role="group" [attr.aria-label]="resizeWidgetLabel()">
44805
+ @for (handle of canvasResizeHandles; track handle.id) {
44806
+ <button type="button" class="pdx-canvas-edge"
44807
+ [attr.data-canvas-edge]="handle.id"
44808
+ [attr.aria-label]="resizeHandleLabel(handle.id)"
44809
+ [matTooltip]="resizeHandleLabel(handle.id)"
44810
+ [matTooltipDisabled]="canvasInteractionActive()"
44811
+ (pointerdown)="onSelectedCanvasResizePointerDown(handle.id, $event)"
44812
+ (keydown)="onSelectedCanvasResizeKeydown(handle.id, $event)"
44813
+ (click)="$event.stopPropagation()"></button>
44814
+ }
44815
+ </div>
44816
+ }
43296
44817
  @if (shouldRenderWidgetContextOverlay(w)) {
43297
44818
  <praxis-dynamic-widget-context-toolbar
44819
+ [showSize]="canCustomizeCanvas()"
44820
+ [sizeDisabled]="isCanvasSizeLocked(w.key)"
44821
+ [sizeLabel]="sizeEditorLabel()"
44822
+ (size)="openSizeEditor(w.key)"
43298
44823
  [toolbarLabel]="widgetContextToolbarLabel(w.key)"
43299
44824
  [contextLabel]="widgetContextLabel(w)"
43300
44825
  [contextTooltip]="widgetContextTooltip(w)"
@@ -43323,7 +44848,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43323
44848
  }
43324
44849
  </div>
43325
44850
  }
43326
- @if (canvasPreviewItem()) {
44851
+ @if (canvasPreviewItem() && !hasResizeFeedback()) {
43327
44852
  <div
43328
44853
  class="pdx-canvas-snap-preview"
43329
44854
  [class.pdx-canvas-snap-preview--invalid]="canvasPreviewInvalid()"
@@ -43596,7 +45121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43596
45121
  </div>
43597
45122
  }
43598
45123
  </div>
43599
- `, styles: [":host{display:block;min-width:0}.pdx-page-wrapper{position:relative;display:block;background:var(--pdx-page-bg, transparent);color:var(--pdx-page-color, inherit)}.pdx-page{display:grid;gap:var(--pdx-page-gap, 12px);grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-wrapper.editing .pdx-page--canvas{padding-block-start:var(--pdx-canvas-context-toolbar-safe-area, 22px);padding-block-end:22px;padding-inline:22px;box-sizing:border-box}.pdx-page-settings{position:sticky;top:8px;z-index:2;color:inherit;margin-bottom:6px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:6px;min-width:0;min-height:0}.pdx-widget--interactive{cursor:pointer}.pdx-widget--selected:before{content:\"\";position:absolute;inset:0;border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);border-radius:14px;pointer-events:none;z-index:1;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-horizontal: linear-gradient( 90deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-vertical: linear-gradient( 180deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-resize-gradient-diagonal-sw: linear-gradient( 225deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas:has(.pdx-canvas-resize:hover):after,.pdx-widget--canvas:has(.pdx-canvas-resize:focus-visible):after{opacity:.82}.pdx-widget--canvas:has(.pdx-canvas-resize--north:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-horizontal)}.pdx-widget--canvas:has(.pdx-canvas-resize--east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-vertical)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-east:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-west:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-sw)}.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--north-west:focus-visible),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:hover),.pdx-widget--canvas:has(.pdx-canvas-resize--south-east:focus-visible){--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize{position:absolute;z-index:7;border:0;padding:0;margin:0;background:transparent;touch-action:none;cursor:pointer;opacity:0;pointer-events:none;transform:scale(.94);transition:opacity .14s ease,transform .14s ease,filter .14s ease;filter:saturate(.9)}.pdx-widget--canvas:hover .pdx-canvas-resize,.pdx-widget--canvas:focus-within .pdx-canvas-resize,.pdx-widget--canvas-selected .pdx-canvas-resize{opacity:.88;pointer-events:auto;transform:scale(1);filter:saturate(1)}.pdx-widget--canvas:has(.pdx-shell.expanded) .pdx-canvas-resize,.pdx-widget--canvas:has(.pdx-shell.fullscreen) .pdx-canvas-resize{opacity:0;pointer-events:none;transform:scale(.94)}.pdx-canvas-resize-grip{display:block;width:9px;height:9px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 42%,var(--md-sys-color-outline-variant) 58%);background:color-mix(in srgb,var(--md-sys-color-surface) 92%,var(--md-sys-color-primary) 8%);box-shadow:0 1px 4px color-mix(in srgb,var(--md-sys-color-shadow) 10%,transparent);transition:transform .14s ease,border-color .14s ease,background .14s ease,box-shadow .14s ease}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:color-mix(in srgb,var(--md-sys-color-primary) 58%,var(--md-sys-color-outline-variant) 42%);background:color-mix(in srgb,var(--md-sys-color-surface) 84%,var(--md-sys-color-primary) 16%);box-shadow:0 2px 7px color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent);transform:scale(1.04)}.pdx-canvas-resize--north,.pdx-canvas-resize--south{left:50%;width:52px;height:44px;margin-left:-26px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north{top:-22px;cursor:ns-resize}.pdx-canvas-resize--south{bottom:-22px;cursor:ns-resize}.pdx-canvas-resize--east,.pdx-canvas-resize--west{top:50%;width:44px;height:52px;margin-top:-26px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--east{right:-22px;cursor:ew-resize}.pdx-canvas-resize--west{left:-22px;cursor:ew-resize}.pdx-canvas-resize--north-east,.pdx-canvas-resize--north-west,.pdx-canvas-resize--south-east,.pdx-canvas-resize--south-west{width:44px;height:44px;display:flex;align-items:center;justify-content:center}.pdx-canvas-resize--north-east{top:-22px;right:-22px;cursor:nesw-resize}.pdx-canvas-resize--north-west{top:-22px;left:-22px;cursor:nwse-resize}.pdx-canvas-resize--south-east{bottom:-22px;right:-22px;cursor:nwse-resize}.pdx-canvas-resize--south-west{bottom:-22px;left:-22px;cursor:nesw-resize}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}.pdx-canvas-resize:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{border-color:transparent;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent),var(--mat-elevation-level3)}.pdx-canvas-resize--north:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-horizontal)}.pdx-canvas-resize--east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-vertical)}.pdx-canvas-resize--north-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-east:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-west:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-sw)}.pdx-canvas-resize--north-west:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--north-west:focus-visible .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:hover .pdx-canvas-resize-grip,.pdx-canvas-resize--south-east:focus-visible .pdx-canvas-resize-grip{background:var(--pdx-resize-gradient-diagonal-se)}.pdx-canvas-resize:hover .pdx-canvas-resize-grip,.pdx-canvas-resize:focus-visible .pdx-canvas-resize-grip{transform:scale(1.08)}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"] }]
45124
+ `, styles: [":host{display:block;min-width:0}.pdx-page-wrapper{position:relative;display:block;background:var(--pdx-page-bg, transparent);color:var(--pdx-page-color, inherit)}.pdx-page{display:grid;gap:var(--pdx-page-gap, 12px);grid-template-columns:minmax(0,1fr)}.pdx-page--canvas{align-items:start;grid-auto-flow:dense}.pdx-page-wrapper.editing .pdx-page--canvas{padding-block-start:var(--pdx-canvas-context-toolbar-safe-area, 22px);padding-block-end:22px;padding-inline:22px;box-sizing:border-box}.pdx-page-settings{position:sticky;top:8px;z-index:2;color:inherit;margin-bottom:6px}.pdx-widget{position:relative;background:var(--pfx-surface, transparent);border-radius:6px;min-width:0;min-height:0}.pdx-widget--interactive{cursor:pointer}.pdx-widget--selected:before{content:\"\";position:absolute;inset:0;border:1px dashed color-mix(in srgb,var(--md-sys-color-primary) 34%,transparent);border-radius:14px;pointer-events:none;z-index:1;box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent),0 8px 20px color-mix(in srgb,var(--md-sys-color-primary) 6%,transparent)}.pdx-widget--canvas{align-self:stretch;--pdx-resize-gradient-diagonal-se: linear-gradient( 135deg, color-mix(in srgb, var(--md-sys-color-primary) 94%, white 6%), color-mix(in srgb, var(--md-sys-color-tertiary) 90%, white 10%), color-mix(in srgb, var(--md-sys-color-secondary) 88%, white 12%) );--pdx-active-resize-gradient: var(--pdx-resize-gradient-diagonal-se)}.pdx-widget--canvas:after{content:\"\";position:absolute;inset:0;padding:2px;border-radius:14px;background:var(--pdx-active-resize-gradient);opacity:0;pointer-events:none;transition:opacity .14s ease,filter .14s ease;filter:saturate(1.02) drop-shadow(0 0 5px color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent));-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{content:\"\";position:absolute;inset:0;border-radius:14px;pointer-events:none;z-index:1;transition:opacity .14s ease,box-shadow .14s ease,border-color .14s ease}.pdx-widget--canvas-blocked:before{border:1px solid color-mix(in srgb,var(--md-sys-color-error) 74%,transparent);box-shadow:0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 20%,transparent),0 10px 24px color-mix(in srgb,var(--md-sys-color-error) 12%,transparent)}.pdx-widget--canvas-selected:has(>.pdx-canvas-edges .pdx-canvas-edge:hover):after,.pdx-widget--canvas-selected:has(>.pdx-canvas-edges .pdx-canvas-edge:focus-visible):after{opacity:.82}[data-resize-feedback=valid]{--pdx-resize-feedback-color: #27864b}[data-resize-feedback=minimum]{--pdx-resize-feedback-color: #bc8a00}[data-resize-feedback=blocked]{--pdx-resize-feedback-color: var(--md-sys-color-error)}.pdx-widget--canvas[data-resize-feedback]:after{background:var(--pdx-resize-feedback-color);opacity:1;z-index:2}.pdx-resize-feedback{position:absolute;inset-inline:8px;bottom:8px;width:fit-content;max-width:calc(100% - 16px);box-sizing:border-box;display:flex;align-items:center;gap:6px;padding:4px 8px;border:2px solid var(--pdx-resize-feedback-color);border-radius:8px;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface);font-size:12px;line-height:18px;pointer-events:none;z-index:3}.pdx-resize-feedback mat-icon{flex:0 0 18px;width:18px;height:18px;font-size:18px}.pdx-resize-feedback--floating{position:static;max-width:min(320px,calc(100vw - 24px));overflow-wrap:anywhere}.pdx-resize-feedback--floating strong{display:block}.pdx-canvas-edges{position:absolute;inset:0;pointer-events:none;z-index:5}.pdx-canvas-edge{position:absolute;display:grid;place-items:center;box-sizing:border-box;width:24px;height:24px;padding:0;border:0;background:transparent;color:var(--md-sys-color-primary);pointer-events:auto;touch-action:none;transform:translate(-50%,-50%)}.pdx-canvas-edge:after{content:\"\";width:8px;height:8px;border:2px solid currentColor;border-radius:2px;background:var(--md-sys-color-surface)}.pdx-canvas-edge:hover:after,.pdx-canvas-edge:focus-visible:after{background:var(--md-sys-color-primary-container)}.pdx-canvas-edge[data-canvas-edge=north-west]{left:0;top:0;cursor:nwse-resize}.pdx-canvas-edge[data-canvas-edge=north]{left:50%;top:0;cursor:ns-resize}.pdx-canvas-edge[data-canvas-edge=north-east]{left:100%;top:0;cursor:nesw-resize}.pdx-canvas-edge[data-canvas-edge=east]{left:100%;top:50%;cursor:ew-resize}.pdx-canvas-edge[data-canvas-edge=south-east]{left:100%;top:100%;cursor:nwse-resize}.pdx-canvas-edge[data-canvas-edge=south]{left:50%;top:100%;cursor:ns-resize}.pdx-canvas-edge[data-canvas-edge=south-west]{left:0;top:100%;cursor:nesw-resize}.pdx-canvas-edge[data-canvas-edge=west]{left:0;top:50%;cursor:ew-resize}.pdx-page-wrapper:has(>.pdx-page>.pdx-widget>praxis-widget-shell>.pdx-shell.expanded)>.pdx-page>.pdx-widget>.pdx-canvas-edges,.pdx-page-wrapper:has(>.pdx-page>.pdx-widget>praxis-widget-shell>.pdx-shell.fullscreen)>.pdx-page>.pdx-widget>.pdx-canvas-edges{display:none}.pdx-widget--canvas:focus-visible,.pdx-canvas-edge:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-canvas-snap-preview{position:relative;align-self:stretch;border-radius:12px;pointer-events:none;z-index:0;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 10%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary) 12%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-primary) 42%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 55%,transparent);animation:pdx-snap-preview-pulse .48s ease-out infinite alternate}.pdx-canvas-snap-preview:before,.pdx-canvas-snap-preview:after{content:\"\";position:absolute;border-radius:999px;background:color-mix(in srgb,var(--md-sys-color-primary) 82%,var(--md-sys-color-tertiary) 18%);opacity:.72}.pdx-canvas-snap-preview:before{inset:0 auto 0 0;width:3px}.pdx-canvas-snap-preview:after{inset:0 0 auto;height:3px}.pdx-canvas-snap-preview--invalid{background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-error) 18%,transparent),color-mix(in srgb,var(--md-sys-color-error-container) 22%,transparent));box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--md-sys-color-error) 58%,transparent),inset 0 0 0 2px color-mix(in srgb,var(--md-sys-color-surface) 40%,transparent)}.pdx-canvas-snap-preview--invalid:before,.pdx-canvas-snap-preview--invalid:after{background:color-mix(in srgb,var(--md-sys-color-error) 86%,var(--md-sys-color-error-container) 14%)}@media(prefers-reduced-motion:reduce){.pdx-canvas-snap-preview{animation:none}.pdx-widget--canvas:after,.pdx-widget--canvas-selected:before,.pdx-widget--canvas-blocked:before{transition:none}}@media(forced-colors:active){.pdx-widget--canvas-selected:before{border:2px solid Highlight}.pdx-widget--canvas:focus-visible,.pdx-canvas-edge:focus-visible{outline-color:Highlight}}@keyframes pdx-snap-preview-pulse{0%{opacity:.72;transform:scale(.996)}to{opacity:1;transform:scale(1)}}.pdx-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pdx-group{display:grid;gap:12px;grid-column:1 / -1;padding:12px;border-radius:16px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 76%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.pdx-group--hero{padding:16px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-primary-container) 42%,transparent),color-mix(in srgb,var(--md-sys-color-surface-container-low) 86%,transparent))}.pdx-group--rail{align-self:start}.pdx-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.pdx-group-title{font-size:.95rem;font-weight:600;color:var(--md-sys-color-on-surface)}.pdx-group-content{display:grid;gap:12px}.pdx-group-content--stack{grid-template-columns:minmax(0,1fr)}.pdx-group-content--row{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.pdx-group-content--grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.pdx-group-tabs{display:grid;gap:12px}.pdx-tabs-header{display:flex;flex-wrap:wrap;gap:8px}.pdx-tab-chip{appearance:none;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface);border-radius:999px;padding:8px 12px;font:inherit;cursor:pointer}.pdx-tab-chip.active{border-color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 78%,transparent);color:var(--md-sys-color-on-primary-container)}.pdx-page-wrapper.editing .pdx-widget-settings{opacity:1}\n"] }]
43600
45125
  }], ctorParameters: () => [], propDecorators: { pageCanvasHost: [{
43601
45126
  type: ViewChild,
43602
45127
  args: ['pageCanvasHost']
@@ -44701,7 +46226,7 @@ class PraxisRelatedResourceOutletComponent {
44701
46226
  </div>
44702
46227
  }
44703
46228
  </section>
44704
- `, isInline: true, styles: [":host{display:block;min-width:0}.pdx-related-outlet{display:block;min-width:0;color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__state{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:var(--pdx-related-outlet-gap, 12px);min-height:var(--pdx-related-outlet-min-height, 64px);padding:var(--pdx-related-outlet-padding, 12px);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .16));border-radius:var(--pdx-related-outlet-radius, 8px);background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface, transparent))}.pdx-related-outlet--compact .pdx-related-outlet__state{min-height:var(--pdx-related-outlet-compact-min-height, 48px);padding:var(--pdx-related-outlet-compact-padding, 8px 10px)}.pdx-related-outlet__state--ready{border-color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__icon{display:inline-grid;place-items:center;width:32px;height:32px;color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__state--busy .pdx-related-outlet__icon{color:var(--md-sys-color-secondary, currentColor)}.pdx-related-outlet__copy{display:grid;gap:2px;min-width:0}.pdx-related-outlet__copy h3,.pdx-related-outlet__copy p{margin:0}.pdx-related-outlet__copy h3{font:var(--md-sys-typescale-title-small, 600 .95rem/1.25rem system-ui);color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__copy p{font:var(--md-sys-typescale-body-small, 400 .82rem/1.15rem system-ui);color:var(--md-sys-color-on-surface-variant, currentColor)}@media(max-width:600px){.pdx-related-outlet__state{grid-template-columns:auto minmax(0,1fr)}.pdx-related-outlet__state button{grid-column:1 / -1;justify-self:start}}\n"], dependencies: [{ kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
46229
+ `, isInline: true, styles: [":host{display:block;min-width:0}.pdx-related-outlet{display:block;min-width:0;color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__state{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:var(--pdx-related-outlet-gap, 12px);min-height:var(--pdx-related-outlet-min-height, 64px);padding:var(--pdx-related-outlet-padding, 12px);border:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .16));border-radius:var(--pdx-related-outlet-radius, 8px);background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface, transparent))}.pdx-related-outlet--compact .pdx-related-outlet__state{min-height:var(--pdx-related-outlet-compact-min-height, 48px);padding:var(--pdx-related-outlet-compact-padding, 8px 10px)}.pdx-related-outlet__state--ready{border-color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__icon{display:inline-grid;place-items:center;width:32px;height:32px;color:var(--md-sys-color-primary, currentColor)}.pdx-related-outlet__state--busy .pdx-related-outlet__icon{color:var(--md-sys-color-secondary, currentColor)}.pdx-related-outlet__copy{display:grid;gap:2px;min-width:0}.pdx-related-outlet__copy h3,.pdx-related-outlet__copy p{margin:0}.pdx-related-outlet__copy h3{font:var(--md-sys-typescale-title-small, 600 .95rem/1.25rem system-ui);color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet__copy p{font:var(--md-sys-typescale-body-small, 400 .82rem/1.15rem system-ui);color:var(--md-sys-color-on-surface-variant, currentColor)}@media(max-width:600px){.pdx-related-outlet__state{grid-template-columns:auto minmax(0,1fr)}.pdx-related-outlet__state button{grid-column:1 / -1;justify-self:start}}\n"], dependencies: [{ kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "ownerWidgetKey", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent", "widgetDiagnostic"], exportAs: ["dynamicWidgetLoader"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
44705
46230
  }
44706
46231
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisRelatedResourceOutletComponent, decorators: [{
44707
46232
  type: Component,
@@ -44964,7 +46489,7 @@ class PraxisIconPickerComponent {
44964
46489
  }
44965
46490
  </div>
44966
46491
  </div>
44967
- `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8$1.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8$1.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
46492
+ `, isInline: true, styles: [".pip-root{display:flex;flex-direction:column;min-width:340px;max-width:760px;max-height:80vh;overflow:hidden;padding:16px;color:var(--md-sys-color-on-surface)}.pip-head{display:flex;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap}.pip-search{flex:1;min-width:180px}.pip-spacer{flex:1}.pip-body{flex:1;overflow:auto;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;padding:4px}.pip-item{display:flex;gap:10px;align-items:center;justify-content:flex-start;padding:10px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);color:inherit;cursor:pointer;transition:background-color .18s ease,border-color .18s ease,box-shadow .18s ease}.pip-item:hover{background:var(--md-sys-color-surface-container)}.pip-item:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pip-item.selected{border-color:var(--md-sys-color-primary);box-shadow:0 0 0 2px var(--md-sys-color-primary)}.pip-name{font-size:12px;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.pip-hint{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin:-6px 0 8px}.pip-stats{display:flex;justify-content:space-between;gap:8px;font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:8px}.pip-shortcut{opacity:.9}.pip-empty{padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:10px;color:var(--md-sys-color-on-surface-variant);background:var(--md-sys-color-surface-container);margin-top:8px}.pip-footer{display:flex;gap:12px;align-items:center;margin-top:10px}.pip-typed{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px;color:var(--md-sys-color-on-surface)}.pip-family{display:flex;align-items:center}.pip-family .mat-mdc-chip-listbox{min-width:260px}.pip-root .mat-icon{font-family:Material Icons,Material Symbols Outlined,Material Symbols Rounded,Material Symbols Sharp!important;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24;color:currentColor}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-rounded{font-family:Material Symbols Rounded;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}.material-symbols-sharp{font-family:Material Symbols Sharp;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:\"liga\";-webkit-font-smoothing:antialiased;font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 24}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i8.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
44968
46493
  }
44969
46494
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisIconPickerComponent, decorators: [{
44970
46495
  type: Component,
@@ -45586,7 +47111,7 @@ class PraxisRelatedResourceOutletConfigEditorComponent {
45586
47111
  </span>
45587
47112
  </aside>
45588
47113
  </form>
45589
- `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet-editor{display:grid;gap:var(--pdx-spacing-lg, 24px);padding:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__header,.pdx-related-outlet-editor__boundary{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__header-icon{display:inline-grid;place-items:center;width:40px;height:40px;border-radius:var(--md-sys-shape-corner-medium, 12px);color:var(--md-sys-color-primary, currentColor);background:var(--md-sys-color-primary-container, transparent)}.pdx-related-outlet-editor__header strong,.pdx-related-outlet-editor__boundary strong,.pdx-related-outlet-editor__header small,.pdx-related-outlet-editor__boundary small{display:block}.pdx-related-outlet-editor__header strong{font:var(--md-sys-typescale-title-medium, 600 1rem/1.4 system-ui)}.pdx-related-outlet-editor__header small,.pdx-related-outlet-editor__boundary small,.pdx-related-outlet-editor__section-heading p{color:var(--md-sys-color-on-surface-variant, currentColor)}.pdx-related-outlet-editor__section{display:grid;gap:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__section-heading h3,.pdx-related-outlet-editor__section-heading p{margin:0}.pdx-related-outlet-editor__section-heading h3{font:var(--md-sys-typescale-title-small, 600 .95rem/1.3 system-ui)}.pdx-related-outlet-editor__section-heading p{margin-top:4px;font:var(--md-sys-typescale-body-small, 400 .82rem/1.35 system-ui)}.pdx-related-outlet-editor__fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__wide{grid-column:1 / -1}.pdx-related-outlet-editor__choice-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__appearance-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__appearance-cluster{display:grid;align-content:start;gap:var(--pdx-spacing-sm, 12px);min-width:0}.pdx-related-outlet-editor__appearance-cluster--wide{grid-column:1 / -1}.pdx-related-outlet-editor__appearance-cluster mat-button-toggle-group{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,132px),1fr))}.pdx-related-outlet-editor__appearance-cluster mat-button-toggle{min-height:44px}.pdx-related-outlet-editor__density-cluster{gap:var(--pdx-spacing-md, 16px)}fieldset{min-width:0;margin:0;padding:0;border:0}legend{margin-bottom:8px;font:var(--md-sys-typescale-label-large, 500 .88rem/1.2 system-ui)}mat-button-toggle-group{width:100%}mat-button-toggle{flex:1 1 0}mat-button-toggle mat-icon{margin-right:6px}.pdx-related-outlet-editor__boundary{padding:var(--pdx-spacing-sm, 12px);border:1px solid var(--md-sys-color-outline-variant, currentColor);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low, transparent)}.pdx-related-outlet-editor__boundary mat-icon{color:var(--md-sys-color-on-surface-variant, currentColor)}@media(max-width:640px){.pdx-related-outlet-editor__fields,.pdx-related-outlet-editor__choice-grid{grid-template-columns:minmax(0,1fr)}.pdx-related-outlet-editor__wide{grid-column:auto}mat-button-toggle-group{display:grid}}@media(max-width:420px){.pdx-related-outlet-editor__appearance-cluster mat-button-toggle-group{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i3$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i3$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
47114
+ `, isInline: true, styles: [":host{display:block;color:var(--md-sys-color-on-surface, currentColor)}.pdx-related-outlet-editor{display:grid;gap:var(--pdx-spacing-lg, 24px);padding:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__header,.pdx-related-outlet-editor__boundary{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__header-icon{display:inline-grid;place-items:center;width:40px;height:40px;border-radius:var(--md-sys-shape-corner-medium, 12px);color:var(--md-sys-color-primary, currentColor);background:var(--md-sys-color-primary-container, transparent)}.pdx-related-outlet-editor__header strong,.pdx-related-outlet-editor__boundary strong,.pdx-related-outlet-editor__header small,.pdx-related-outlet-editor__boundary small{display:block}.pdx-related-outlet-editor__header strong{font:var(--md-sys-typescale-title-medium, 600 1rem/1.4 system-ui)}.pdx-related-outlet-editor__header small,.pdx-related-outlet-editor__boundary small,.pdx-related-outlet-editor__section-heading p{color:var(--md-sys-color-on-surface-variant, currentColor)}.pdx-related-outlet-editor__section{display:grid;gap:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__section-heading h3,.pdx-related-outlet-editor__section-heading p{margin:0}.pdx-related-outlet-editor__section-heading h3{font:var(--md-sys-typescale-title-small, 600 .95rem/1.3 system-ui)}.pdx-related-outlet-editor__section-heading p{margin-top:4px;font:var(--md-sys-typescale-body-small, 400 .82rem/1.35 system-ui)}.pdx-related-outlet-editor__fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__wide{grid-column:1 / -1}.pdx-related-outlet-editor__choice-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--pdx-spacing-md, 16px)}.pdx-related-outlet-editor__appearance-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:var(--pdx-spacing-sm, 12px)}.pdx-related-outlet-editor__appearance-cluster{display:grid;align-content:start;gap:var(--pdx-spacing-sm, 12px);min-width:0}.pdx-related-outlet-editor__appearance-cluster--wide{grid-column:1 / -1}.pdx-related-outlet-editor__appearance-cluster mat-button-toggle-group{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,132px),1fr))}.pdx-related-outlet-editor__appearance-cluster mat-button-toggle{min-height:44px}.pdx-related-outlet-editor__density-cluster{gap:var(--pdx-spacing-md, 16px)}fieldset{min-width:0;margin:0;padding:0;border:0}legend{margin-bottom:8px;font:var(--md-sys-typescale-label-large, 500 .88rem/1.2 system-ui)}mat-button-toggle-group{width:100%}mat-button-toggle{flex:1 1 0}mat-button-toggle mat-icon{margin-right:6px}.pdx-related-outlet-editor__boundary{padding:var(--pdx-spacing-sm, 12px);border:1px solid var(--md-sys-color-outline-variant, currentColor);border-radius:var(--md-sys-shape-corner-medium, 12px);background:var(--md-sys-color-surface-container-low, transparent)}.pdx-related-outlet-editor__boundary mat-icon{color:var(--md-sys-color-on-surface-variant, currentColor)}@media(max-width:640px){.pdx-related-outlet-editor__fields,.pdx-related-outlet-editor__choice-grid{grid-template-columns:minmax(0,1fr)}.pdx-related-outlet-editor__wide{grid-column:auto}mat-button-toggle-group{display:grid}}@media(max-width:420px){.pdx-related-outlet-editor__appearance-cluster mat-button-toggle-group{grid-template-columns:minmax(0,1fr)}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i3$3.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i3$3.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
45590
47115
  }
45591
47116
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisRelatedResourceOutletConfigEditorComponent, decorators: [{
45592
47117
  type: Component,
@@ -46474,6 +47999,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
46474
47999
 
46475
48000
  class ResourceQuickConnectComponent {
46476
48001
  resourcePath = '';
48002
+ selector;
48003
+ registry = this.optionalInject(ComponentMetadataRegistry);
48004
+ discovery = this.optionalInject(ResourceDiscoveryService);
48005
+ i18n = this.optionalInject(PraxisI18nService);
48006
+ cdr = this.optionalInject(ChangeDetectorRef);
48007
+ selectorType = this.registry?.get('pdx-material-select')?.component;
48008
+ selectorInputs;
48009
+ catalogLoading = false;
48010
+ catalogError = false;
48011
+ accessor;
48012
+ tx(key) { return this.i18n?.t(`quickConnect.${key}`, undefined, key, 'resourceDiscovery') ?? key; }
48013
+ ngAfterViewChecked() {
48014
+ const instance = this.selector?.componentInstance;
48015
+ if (!instance || instance === this.accessor || !instance.registerOnChange || !instance.writeValue)
48016
+ return;
48017
+ instance.setInputMetadata?.(this.selectorInputs?.['metadata']);
48018
+ this.accessor = instance;
48019
+ this.accessor.writeValue(this.normalizedPath());
48020
+ this.accessor.registerOnChange((value) => {
48021
+ this.resourcePath = typeof value === 'string' ? value : '';
48022
+ this.updateState();
48023
+ });
48024
+ }
48025
+ async loadCatalog() {
48026
+ if (!this.discovery || !this.selectorType)
48027
+ return;
48028
+ this.catalogLoading = true;
48029
+ this.catalogError = false;
48030
+ this.cdr?.markForCheck();
48031
+ try {
48032
+ const catalog = await firstValueFrom(this.discovery.getSchemaCatalog().pipe(timeout(30000)));
48033
+ const resources = new Map();
48034
+ for (const endpoint of catalog.endpoints ?? []) {
48035
+ // Ground only declared resource-level capability endpoints; never infer user intent.
48036
+ if (endpoint.method !== 'GET' || !endpoint.resourceKey || !endpoint.path.startsWith('/')
48037
+ || endpoint.path.startsWith('//') || endpoint.path.includes('{') || !endpoint.path.endsWith('/capabilities'))
48038
+ continue;
48039
+ const value = endpoint.path.slice(0, -'/capabilities'.length);
48040
+ resources.set(value, { value, label: endpoint.resourceVisual?.title || endpoint.resourceKey });
48041
+ }
48042
+ this.selectorInputs = resources.size ? { metadata: {
48043
+ name: 'resourcePath', label: this.tx('resource'), type: 'string', controlType: 'select', searchable: true,
48044
+ selectOptions: [...resources.values()].sort((a, b) => a.label.localeCompare(b.label)),
48045
+ } } : undefined;
48046
+ }
48047
+ catch {
48048
+ this.catalogError = true;
48049
+ }
48050
+ finally {
48051
+ this.catalogLoading = false;
48052
+ this.cdr?.markForCheck();
48053
+ }
48054
+ }
48055
+ optionalInject(token) {
48056
+ try {
48057
+ return inject(token, { optional: true });
48058
+ }
48059
+ catch {
48060
+ return null;
48061
+ }
48062
+ }
46477
48063
  isDirty$ = new BehaviorSubject(false);
46478
48064
  isValid$ = new BehaviorSubject(false);
46479
48065
  isBusy$ = new BehaviorSubject(false);
@@ -46481,9 +48067,11 @@ class ResourceQuickConnectComponent {
46481
48067
  // When used standalone, consumers can bind [(resourcePath)].
46482
48068
  ngOnInit() {
46483
48069
  this.updateValidity();
48070
+ void this.loadCatalog();
46484
48071
  }
46485
48072
  ngOnChanges() {
46486
48073
  this.updateValidity();
48074
+ this.accessor?.writeValue(this.normalizedPath());
46487
48075
  }
46488
48076
  updateState() {
46489
48077
  this.isDirty$.next(true);
@@ -46519,57 +48107,54 @@ class ResourceQuickConnectComponent {
46519
48107
  return /^\/?[a-zA-Z0-9][a-zA-Z0-9/_-]*$/.test(value);
46520
48108
  }
46521
48109
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ResourceQuickConnectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
46522
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ResourceQuickConnectComponent, isStandalone: true, selector: "praxis-resource-quick-connect", inputs: { resourcePath: "resourcePath" }, usesOnChanges: true, ngImport: i0, template: `
48110
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ResourceQuickConnectComponent, isStandalone: true, selector: "praxis-resource-quick-connect", inputs: { resourcePath: "resourcePath" }, providers: [providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG)], viewQueries: [{ propertyName: "selector", first: true, predicate: NgComponentOutlet, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
46523
48111
  <div class="pdx-quick-connect">
46524
- <mat-form-field appearance="outline">
46525
- <mat-label>Rota do recurso (API)</mat-label>
46526
- <input
46527
- matInput
46528
- [(ngModel)]="resourcePath"
46529
- (ngModelChange)="updateState()"
46530
- placeholder="ex.: human-resources/departamentos"
46531
- />
46532
- <mat-icon matSuffix [praxisIcon]="'link'"></mat-icon>
46533
- @if (showInvalid()) {
46534
- <mat-error>Use apenas letras, números, /, - ou _.</mat-error>
46535
- }
46536
- </mat-form-field>
46537
- <small class="pdx-quick-connect__hint">Conecte o componente à fonte de dados (ex.: /api/&lt;rota-do-recurso&gt;)</small>
46538
- @if (normalizedPath() && normalizedPath() !== resourcePath) {
46539
- <small class="pdx-quick-connect__hint">
46540
- Rota normalizada: {{ normalizedPath() }}
46541
- </small>
48112
+ @if (catalogLoading) { <p role="status">{{ tx('loading') }}</p> }
48113
+ @if (catalogError) { <p role="status">{{ tx('unavailable') }}</p><button type="button" (click)="loadCatalog()">{{ tx('retry') }}</button> }
48114
+ @if (selectorType && selectorInputs) {
48115
+ <ng-container *ngComponentOutlet="selectorType" />
48116
+ <small>{{ tx('hint') }}</small>
46542
48117
  }
48118
+ @if (!catalogLoading && !catalogError && !selectorInputs) { <p role="status">{{ tx('empty') }}</p> }
48119
+ <details [open]="!selectorType">
48120
+ <summary>{{ tx('advanced') }}</summary>
48121
+ <mat-form-field appearance="outline">
48122
+ <mat-label>{{ tx('path') }}</mat-label>
48123
+ <input matInput [(ngModel)]="resourcePath" (ngModelChange)="updateState()" />
48124
+ <mat-icon matSuffix [praxisIcon]="'link'"></mat-icon>
48125
+ @if (showInvalid()) { <mat-error>{{ tx('invalid') }}</mat-error> }
48126
+ </mat-form-field>
48127
+ </details>
46543
48128
  </div>
46544
- `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
48129
+ `, isInline: true, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
46545
48130
  }
46546
48131
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ResourceQuickConnectComponent, decorators: [{
46547
48132
  type: Component,
46548
- args: [{ selector: 'praxis-resource-quick-connect', standalone: true, imports: [FormsModule, MatFormFieldModule, MatInputModule, MatIconModule, PraxisIconDirective], template: `
48133
+ args: [{ selector: 'praxis-resource-quick-connect', changeDetection: ChangeDetectionStrategy.OnPush, providers: [providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG)], imports: [NgComponentOutlet, FormsModule, MatFormFieldModule, MatInputModule, MatIconModule, PraxisIconDirective], template: `
46549
48134
  <div class="pdx-quick-connect">
46550
- <mat-form-field appearance="outline">
46551
- <mat-label>Rota do recurso (API)</mat-label>
46552
- <input
46553
- matInput
46554
- [(ngModel)]="resourcePath"
46555
- (ngModelChange)="updateState()"
46556
- placeholder="ex.: human-resources/departamentos"
46557
- />
46558
- <mat-icon matSuffix [praxisIcon]="'link'"></mat-icon>
46559
- @if (showInvalid()) {
46560
- <mat-error>Use apenas letras, números, /, - ou _.</mat-error>
46561
- }
46562
- </mat-form-field>
46563
- <small class="pdx-quick-connect__hint">Conecte o componente à fonte de dados (ex.: /api/&lt;rota-do-recurso&gt;)</small>
46564
- @if (normalizedPath() && normalizedPath() !== resourcePath) {
46565
- <small class="pdx-quick-connect__hint">
46566
- Rota normalizada: {{ normalizedPath() }}
46567
- </small>
48135
+ @if (catalogLoading) { <p role="status">{{ tx('loading') }}</p> }
48136
+ @if (catalogError) { <p role="status">{{ tx('unavailable') }}</p><button type="button" (click)="loadCatalog()">{{ tx('retry') }}</button> }
48137
+ @if (selectorType && selectorInputs) {
48138
+ <ng-container *ngComponentOutlet="selectorType" />
48139
+ <small>{{ tx('hint') }}</small>
46568
48140
  }
48141
+ @if (!catalogLoading && !catalogError && !selectorInputs) { <p role="status">{{ tx('empty') }}</p> }
48142
+ <details [open]="!selectorType">
48143
+ <summary>{{ tx('advanced') }}</summary>
48144
+ <mat-form-field appearance="outline">
48145
+ <mat-label>{{ tx('path') }}</mat-label>
48146
+ <input matInput [(ngModel)]="resourcePath" (ngModelChange)="updateState()" />
48147
+ <mat-icon matSuffix [praxisIcon]="'link'"></mat-icon>
48148
+ @if (showInvalid()) { <mat-error>{{ tx('invalid') }}</mat-error> }
48149
+ </mat-form-field>
48150
+ </details>
46569
48151
  </div>
46570
48152
  `, styles: [".pdx-quick-connect{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.pdx-quick-connect__hint{color:var(--md-sys-color-on-surface-variant);font-size:.9rem}\n"] }]
46571
48153
  }], propDecorators: { resourcePath: [{
46572
48154
  type: Input
48155
+ }], selector: [{
48156
+ type: ViewChild,
48157
+ args: [NgComponentOutlet]
46573
48158
  }] } });
46574
48159
 
46575
48160
  /** Optional DI token used by SchemaViewer when input is not provided. */
@@ -47486,4 +49071,4 @@ function provideHookWhitelist(allowed) {
47486
49071
  * Generated bundle index. Do not edit.
47487
49072
  */
47488
49073
 
47489
- export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, AnalyticsPresentationResolver, AnalyticsSchemaContractService, AnalyticsStatsRequestBuilderService, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, COMPONENT_METADATA_REGISTRY_I18N_CONFIG, COMPONENT_METADATA_REGISTRY_I18N_NAMESPACE, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentAuthoringManifestProjectionError, ComponentKeyService, ComponentMetadataRegistry, ComponentRuntimeProfileService, CompositionRuntimeFacade, CompositionValidatorService, ConsoleLoggerSink, CrudOperationResolutionService, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_JSON_LOGIC_OPERATORS, DEFAULT_TABLE_CONFIG, DOMAIN_CATALOG_COMPONENT_CONTEXT_PACK, DOMAIN_CATALOG_CONTEXT_HINT_SCHEMA_VERSION, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DomainCatalogService, DomainKnowledgeService, DomainRuleService, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, EnterpriseRuntimeContextService, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GenericCrudService, GlobalActionService, GlobalConfigService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NestedPortCatalogService, NestedWidgetConfigAccessor, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_ACTION_CONTROL_DEFAULTS, PRAXIS_ACTION_CONTROL_VARS, PRAXIS_COLLECTION_EXPORT_HTTP_OPTIONS, PRAXIS_COLLECTION_EXPORT_PROVIDER, PRAXIS_COLLECTION_SEARCH_DEFAULTS, PRAXIS_COLLECTION_SEARCH_VARS, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_EXPORT_SECURITY_POLICY, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_OPTIONS, PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_READY, PRAXIS_EXPORT_FORMULA_PREFIXES, PRAXIS_EXPORT_SECURITY_POLICY, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_JSON_LOGIC_OPERATORS, PRAXIS_LAYER_SCALE_DEFAULTS, PRAXIS_LAYER_SCALE_VARS, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_QUERY_FILTER_EXPRESSION_SCHEMA_VERSION, PRAXIS_RELATED_RESOURCE_OUTLET_AUTHORING_MANIFEST, PRAXIS_RELATED_RESOURCE_OUTLET_COMPONENT_METADATA, PRAXIS_RELATED_RESOURCE_OUTLET_PORTS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TABLE_DETAIL_INLINE_NODE_RESOLVERS, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, PRAXIS_TABLE_DETAIL_RESOURCE_RESOLVER, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_THEME_SURFACE_DEFAULTS, PRAXIS_THEME_SURFACE_VARS, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCollectionExportService, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisHttpCollectionExportProvider, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, PraxisIconPickerComponent, PraxisJsonLogicError, PraxisJsonLogicService, PraxisLayerScaleStyleService, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRelatedResourceOutletComponent, PraxisRelatedResourceOutletConfigEditorComponent, PraxisResourceIdentityComponent, PraxisRichTextBlockComponent, PraxisRuntimeComponentObservationRegistryService, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, RULE_PROPERTY_SCHEMA, RelatedResourceSurfaceResolverService, RemoteConfigStorage, ResourceActionOpenAdapterService, ResourceDiscoveryService, ResourceQuickConnectComponent, ResourceRecordOpenError, ResourceRecordOpenService, ResourceSurfaceOpenAdapterService, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_DRAWER_BRIDGE, SURFACE_DRAWER_CONTENT_DATA, SURFACE_DRAWER_REF, SURFACE_NAVIGATION_I18N_CONFIG, SURFACE_NAVIGATION_I18N_NAMESPACE, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceNavigationError, SurfaceOpenActionEditorComponent, SurfaceOpenMaterializerService, SurfaceOutletRegistryService, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageCompositionFactory, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, assertPraxisCollectionExportArtifact, assertPraxisRuntimeComponentObservationSerializable, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildPraxisActionControlCss, buildPraxisCollectionSearchCss, buildPraxisEffectDistinctKey, buildPraxisLayerScaleCss, buildPraxisThemeSurfaceCss, buildSchemaId, buildSchemaIdStorageKeySegment, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, canonicalJsonSha256, canonicalJsonStringify, clampRange, classifyEntityLookupResult, clonePraxisRuntimeComponentObservation, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createEmptyRichContentDocument, createFieldLayoutItem, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, domainKnowledgeTimelineToRichContentDocument, domainRuleTimelineToRichContentDocument, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, escapePraxisExportCell, evaluateFieldAccess, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getFormColumnFieldNames, getFormLayoutFieldNames, getGlobalActionCatalog, getGlobalActionPayloadActualType, getGlobalActionPayloadTypeIssue, getGlobalActionProviderEvidence, getGlobalActionUiSchema, getMissingGlobalActionPayloadKeys, getPraxisTableCellVisualizationConstraint, getPraxisTableCellVisualizationGuidance, getReferencedFieldMetadata, getRequiredGlobalActionPayloadKeys, getTextTransformer, hasMeaningfulGlobalActionPayloadValue, hasPraxisCollectionExportArtifact, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isDomainRuleSnapshotProblemResponse, isEditorialComponentMeta, isEntityLookupMultiplePayloadMode, isEntityLookupPayloadMode, isEntityLookupPayloadModeCompatible, isEntityLookupResultSelectable, isEntityLookupSinglePayloadMode, isFormLayoutItem, isGlobalActionProviderOperational, isGlobalActionRef, isInlineFilterControlType, isLookupDialogSize, isLookupFilterFieldType, isLookupFilterOperator, isPraxisI18nMessageDescriptor, isPraxisPresentationVisualizationTableSafe, isPraxisRuntimeGlobalActionEffect, isProgrammaticDateRangePreset, isRangeValidForFilter, isRequiredGlobalActionParamPayloadMissing, isRequiredGlobalActionPayloadMissing, isStaticDateRangePreset, isSurfaceNavigationError, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, markGlobalActionProviderOperational, matchFieldValidator, materializeFormLayoutFromMetadata, materializeResourceIdentity, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, migrateLegacyCompositionLink, migrateLegacyCompositionLinks, minWordsValidator, nestedPortPathIdentity, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldAccessMetadata, normalizeFieldConstraints, normalizeFieldPresentation, normalizeFormConfig, normalizeFormLayoutItems, normalizeFormMetadata, normalizeGlobalActionRef$1 as normalizeGlobalActionRef, normalizeLayoutPolicy, normalizeLookupFilterRequest, normalizePath, normalizePraxisDataQueryContext, normalizePraxisEffectPolicy, normalizePraxisPresentationVisualization, normalizePraxisQueryFilterExpression, normalizePraxisQueryFilterNode, normalizeReactiveDeterminations, normalizeResourceAvailabilityReasonCode, normalizeResourceIdentityContract, normalizeStart, normalizeSurfaceOperationContext, normalizeUnknownError, normalizeWidgetEventPath, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, projectComponentAuthoringManifest, projectGroupedCommandPartialRows, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisCollectionExportProvider, providePraxisDynamicPageMetadata, providePraxisEnterpriseRuntimeContext, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpCollectionExportProvider, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisIconDefaults, providePraxisJsonLogicOperator, providePraxisJsonLogicOperatorOverride, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRelatedResourceOutletMetadata, providePraxisRichTextBlockMetadata, providePraxisTelemetry, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, readPraxisExportValue, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, registerPraxisRuntimeComponentObservation, removeDiacritics, renderPraxisPresentationVisualizationHtml, reportTelemetryHookFactory, requiredCheckedValidator, requiredPresenceValidator, resolveBuiltinPresets, resolveColumnTypeFromFieldDefinition, resolveControlTypeAlias, resolveDateRangeShortcutPreset, resolveDateRangeShortcutPresets, resolveDefaultValuePresentationFormat, resolveEntityLookupPayloadMode, resolveFieldPresentation, resolveGroupedCommandPartialRowSpans, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisCollectionExportItems, resolvePraxisExportFields, resolvePraxisExportScope, resolvePraxisFilterCriteria, resolvePraxisI18nDocument, resolveResourceAvailabilityReasonKey, resolveResourceIdentityContract, resolveSpan, resolveTextMaskFormat, resolveTextMaskFormatFromFieldDefinition, resolveValuePresentation, resolveValuePresentationLocale, serializeEntityLookupValueForPayload, serializeOptionSourceFilterRequest, serializePraxisCollectionToCsv, serializePraxisCollectionToExcel, serializePraxisCollectionToJson, slugify, staticDateRangePresetToPreset, stripMasksHook, supportsConfigDocuments, supportsImplicitValuePresentation, syncWithServerMetadata, textSha256, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, translateResourceAvailabilityReason, translateResourceDiscoveryText, translateSurfaceNavigationRejected, translateUnavailableWorkflowMessage, trim, uniqueAsyncValidator, urlValidator, validateGlobalActionRef, validateGlobalActionRefs, withFormConfigSections, withMessage, withPraxisHttpLoading };
49074
+ export { API_CONFIG_STORAGE_OPTIONS, API_URL, ASYNC_CONFIG_STORAGE, AllowedFileTypes, AnalyticsPresentationResolver, AnalyticsSchemaContractService, AnalyticsStatsRequestBuilderService, ApiConfigStorage, ApiEndpoint, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, BUILTIN_SHELL_PRESETS, COMPONENT_METADATA_REGISTRY_I18N_CONFIG, COMPONENT_METADATA_REGISTRY_I18N_NAMESPACE, CONFIG_STORAGE, CONNECTION_STORAGE, ComponentAuthoringManifestProjectionError, ComponentKeyService, ComponentMetadataRegistry, ComponentRuntimeProfileService, CompositionRuntimeFacade, CompositionValidatorService, ConsoleLoggerSink, CrudOperationResolutionService, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, DEFAULT_JSON_LOGIC_OPERATORS, DEFAULT_TABLE_CONFIG, DOMAIN_CATALOG_COMPONENT_CONTEXT_PACK, DOMAIN_CATALOG_CONTEXT_HINT_SCHEMA_VERSION, DYNAMIC_PAGE_AI_CAPABILITIES, DYNAMIC_PAGE_COMPONENT_CONTEXT_PACK, DYNAMIC_PAGE_CONFIG_EDITOR, DYNAMIC_PAGE_SHELL_EDITOR, DefaultLoadingRenderer, DeferredAsyncConfigStorage, DomainCatalogService, DomainKnowledgeService, DomainRuleService, DynamicFormService, DynamicWidgetLoaderDirective, DynamicWidgetPageComponent, EDITORIAL_ALLOWED_CONTENT_FORMATS, EDITORIAL_COMPLIANCE_PRESETS, EDITORIAL_EXTERNAL_LINK_REL, EDITORIAL_FORM_TEMPLATE_CATALOG, EDITORIAL_HTML_ENABLED, EDITORIAL_MARKDOWN_IMAGES_ENABLED, EDITORIAL_SOLUTION_CATALOG, EDITORIAL_SOLUTION_PRESETS, EDITORIAL_THEME_PRESETS, EDITORIAL_WIDGET_CONVENTION_INPUTS, EDITORIAL_WIDGET_TAG, EMPLOYEE_ONBOARDING_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_EDITORIAL_TEMPLATE, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_SOLUTION, EMPLOYEE_ONBOARDING_GUIDED_EDITORIAL_TEMPLATE, EVENT_REGISTRATION_EDITORIAL_SOLUTION, EVENT_REGISTRATION_EDITORIAL_TEMPLATE, EmptyStateCardComponent, EnterpriseRuntimeContextService, ErrorMessageService, FIELD_METADATA_CAPABILITIES, FIELD_SELECTOR_REGISTRY_BASE, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, FIELD_SELECTOR_REGISTRY_OVERRIDES, FORM_HOOKS, FORM_HOOKS_PRESETS, FORM_HOOKS_WHITELIST, FORM_HOOK_RESOLVERS, FieldControlType, FieldDataType, FieldSelectorRegistry, FormHooksRegistry, GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_HANDLERS, GLOBAL_ACTION_UI_SCHEMAS, GLOBAL_ANALYTICS_SERVICE, GLOBAL_API_CLIENT, GLOBAL_CONFIG, GLOBAL_DIALOG_SERVICE, GLOBAL_ROUTE_GUARD_RESOLVER, GLOBAL_SURFACE_SERVICE, GLOBAL_TOAST_SERVICE, GOVERNED_COLOR_PALETTE_OPTIONS, GenericCrudService, GlobalActionService, GlobalConfigService, GovernedColorPaletteService, INLINE_FILTER_ALIAS_TOKENS, INLINE_FILTER_CONTROL_TYPES, INLINE_FILTER_CONTROL_TYPE_SET, INLINE_FILTER_CONTROL_TYPE_VALUES, INLINE_FILTER_TOKEN_TO_BASE_CONTROL_TYPE, INLINE_FILTER_TOKEN_TO_CONTROL_TYPE, IconPickerService, IconPosition, IconSize, LOGGER_LEVEL_BY_ENV, LOGGER_LEVEL_PRIORITY, LoadingOrchestrator, LocalConnectionStorage, LocalStorageAsyncAdapter, LocalStorageCacheAdapter, LocalStorageConfigService, LoggerService, LoggerThrottleTracker, LoggerWarnOnceTracker, MemoryCacheAdapter, NestedPortCatalogService, NestedWidgetConfigAccessor, NumericFormat, OVERLAY_DECIDER_DEBUG, OVERLAY_DECISION_MATRIX, ObservabilityDashboardService, OverlayDeciderService, PRAXIS_ACTION_CONTROL_DEFAULTS, PRAXIS_ACTION_CONTROL_VARS, PRAXIS_COLLECTION_EXPORT_HTTP_OPTIONS, PRAXIS_COLLECTION_EXPORT_PROVIDER, PRAXIS_COLLECTION_SEARCH_DEFAULTS, PRAXIS_COLLECTION_SEARCH_VARS, PRAXIS_CORPORATE_SENSITIVE_KEYS, PRAXIS_DEFAULT_EXPORT_SECURITY_POLICY, PRAXIS_DEFAULT_OBSERVABILITY_ALERT_RULES, PRAXIS_DYNAMIC_PAGE_COMPONENT_METADATA, PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_OPTIONS, PRAXIS_ENTERPRISE_RUNTIME_CONTEXT_READY, PRAXIS_EXPORT_FORMULA_PREFIXES, PRAXIS_EXPORT_SECURITY_POLICY, PRAXIS_FOOTER_LINKS_METADATA, PRAXIS_GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_OPTIONS, PRAXIS_GLOBAL_CONFIG_BOOTSTRAP_READY, PRAXIS_GLOBAL_CONFIG_TENANT_RESOLVER, PRAXIS_HERO_BANNER_METADATA, PRAXIS_I18N_CONFIG, PRAXIS_I18N_TRANSLATOR, PRAXIS_JSON_LOGIC_OPERATORS, PRAXIS_LAYER_SCALE_DEFAULTS, PRAXIS_LAYER_SCALE_VARS, PRAXIS_LEGAL_NOTICE_METADATA, PRAXIS_LOADING_CTX, PRAXIS_LOADING_RENDERER, PRAXIS_LOGGER_CONFIG, PRAXIS_LOGGER_SINKS, PRAXIS_OBSERVABILITY_DASHBOARD_OPTIONS, PRAXIS_QUERY_FILTER_EXPRESSION_SCHEMA_VERSION, PRAXIS_RELATED_RESOURCE_OUTLET_AUTHORING_MANIFEST, PRAXIS_RELATED_RESOURCE_OUTLET_COMPONENT_METADATA, PRAXIS_RELATED_RESOURCE_OUTLET_PORTS, PRAXIS_RICH_TEXT_BLOCK_METADATA, PRAXIS_TABLE_DETAIL_INLINE_NODE_RESOLVERS, PRAXIS_TABLE_DETAIL_INLINE_RENDERERS, PRAXIS_TABLE_DETAIL_RESOURCE_RESOLVER, PRAXIS_TELEMETRY_TRANSPORT, PRAXIS_THEME_SURFACE_DEFAULTS, PRAXIS_THEME_SURFACE_VARS, PRAXIS_USER_CONTEXT_SUMMARY_METADATA, PRIVACY_CONSENT_EDITORIAL_SOLUTION, PRIVACY_CONSENT_EDITORIAL_TEMPLATE, PraxisCollectionExportService, PraxisCore, PraxisFooterLinksComponent, PraxisGlobalErrorHandler, PraxisHeroBannerComponent, PraxisHttpCollectionExportProvider, PraxisI18nService, PraxisIconButtonComponent, PraxisIconDirective, PraxisIconPickerComponent, PraxisJsonLogicError, PraxisJsonLogicService, PraxisLayerScaleStyleService, PraxisLegalNoticeComponent, PraxisLoadingInterceptor, PraxisRelatedResourceOutletComponent, PraxisRelatedResourceOutletConfigEditorComponent, PraxisResourceIdentityComponent, PraxisRichTextBlockComponent, PraxisRuntimeComponentObservationRegistryService, PraxisSurfaceHostComponent, PraxisUserContextSummaryComponent, RESOURCE_DISCOVERY_I18N_CONFIG, RESOURCE_DISCOVERY_I18N_NAMESPACE, RULE_PROPERTY_SCHEMA, RelatedResourceSurfaceResolverService, RemoteConfigStorage, ResourceActionOpenAdapterService, ResourceDiscoveryService, ResourceQuickConnectComponent, ResourceRecordOpenError, ResourceRecordOpenService, ResourceSurfaceOpenAdapterService, SCHEMA_VIEWER_CONTEXT, SETTINGS_PANEL_BRIDGE, SETTINGS_PANEL_DATA, STEPPER_CONFIG_EDITOR, SURFACE_DRAWER_BRIDGE, SURFACE_DRAWER_CONTENT_DATA, SURFACE_DRAWER_REF, SURFACE_NAVIGATION_I18N_CONFIG, SURFACE_NAVIGATION_I18N_NAMESPACE, SURFACE_OPEN_I18N_CONFIG, SURFACE_OPEN_I18N_NAMESPACE, SURFACE_OPEN_PRESETS, SchemaMetadataClient, SchemaNormalizerService, SchemaViewerComponent, SurfaceBindingRuntimeService, SurfaceNavigationError, SurfaceOpenActionEditorComponent, SurfaceOpenMaterializerService, SurfaceOutletRegistryService, TABLE_CONFIG_EDITOR, TableConfigService, TelemetryLoggerSink, TelemetryService, ValidationPattern, WidgetPageCompositionFactory, WidgetPageStateRuntimeService, WidgetShellComponent, applyLocalCustomizations$2 as applyLocalCustomizations, applyLocalCustomizations$1 as applyLocalFormCustomizations, assertPraxisCollectionExportArtifact, assertPraxisRuntimeComponentObservationSerializable, buildAngularValidators, buildApiUrl, buildBaseColumnFromDef, buildBaseFormField, buildFormConfigFromEditorialTemplate, buildHeaders, buildPageKey, buildPraxisActionControlCss, buildPraxisCollectionSearchCss, buildPraxisEffectDistinctKey, buildPraxisLayerScaleCss, buildPraxisThemeSurfaceCss, buildSchemaId, buildSchemaIdStorageKeySegment, buildValidatorsFromValidatorOptions, cancelIfCpfInvalidHook, canonicalJsonSha256, canonicalJsonStringify, clampRange, classifyEntityLookupResult, clonePraxisRuntimeComponentObservation, cloneTableConfig, cnpjAlphaValidator, collapseWhitespace, composeHeadersWithVersion, conditionalAsyncValidator, convertFormLayoutToConfig, createCorporateLoggerConfig, createCorporateObservabilityOptions, createCpfCnpjValidator, createDefaultFormConfig, createDefaultTableConfig, createEmptyFormConfig, createEmptyRichContentDocument, createFieldLayoutItem, createPersistedPage, customAsyncValidatorFn, customValidatorFn, debounceAsyncValidator, deepMerge, domainKnowledgeTimelineToRichContentDocument, domainRuleTimelineToRichContentDocument, ensureIds, ensureNoConflictsHookFactory, ensurePageIds, escapePraxisExportCell, evaluateFieldAccess, extractNormalizedError, fetchWithETag, fileTypeValidator, fillUndefined, generateId, getDefaultFormHints, getEditorialCompliancePresetById, getEditorialFormTemplateById, getEditorialFormTemplateCatalog, getEditorialSolutionById, getEditorialSolutionCatalog, getEditorialSolutionPresetById, getEditorialThemePresetById, getEssentialConfig, getFieldMetadataCapabilities, getFormColumnFieldNames, getFormLayoutFieldNames, getGlobalActionCatalog, getGlobalActionPayloadActualType, getGlobalActionPayloadTypeIssue, getGlobalActionProviderEvidence, getGlobalActionUiSchema, getMissingGlobalActionPayloadKeys, getPraxisTableCellVisualizationConstraint, getPraxisTableCellVisualizationGuidance, getReferencedFieldMetadata, getRequiredGlobalActionPayloadKeys, getTextTransformer, hasMeaningfulGlobalActionPayloadValue, hasPraxisCollectionExportArtifact, interpolatePraxisTranslation, isAllowedEditorialContentFormat, isAllowedEditorialHref, isCssTextTransform, isDomainRuleSnapshotProblemResponse, isEditorialComponentMeta, isEntityLookupMultiplePayloadMode, isEntityLookupPayloadMode, isEntityLookupPayloadModeCompatible, isEntityLookupResultSelectable, isEntityLookupSinglePayloadMode, isFormLayoutItem, isGlobalActionProviderOperational, isGlobalActionRef, isInlineFilterControlType, isLookupDialogSize, isLookupFilterFieldType, isLookupFilterOperator, isPraxisI18nMessageDescriptor, isPraxisPresentationVisualizationTableSafe, isPraxisRuntimeGlobalActionEffect, isProgrammaticDateRangePreset, isRangeValidForFilter, isRequiredGlobalActionParamPayloadMissing, isRequiredGlobalActionPayloadMissing, isStaticDateRangePreset, isSurfaceNavigationError, isTableConfigV2, isValidFormConfig, isValidTableConfig, legacyCnpjValidator, legacyCpfValidator, logOnErrorHook, mapFieldDefinitionToMetadata, mapFieldDefinitionsToMetadata, markGlobalActionProviderOperational, matchFieldValidator, materializeFormLayoutFromMetadata, materializeGovernedPaletteColors, materializeGovernedPaletteEntries, materializeResourceIdentity, maxFileSizeValidator, mergeFieldMetadata, mergePraxisI18nConfigs, mergeTableConfigs, migrateFormLayoutRule, migrateLegacyCompositionLink, migrateLegacyCompositionLinks, minWordsValidator, nestedPortPathIdentity, normalizeControlTypeKey, normalizeControlTypeToken, normalizeEditorialLink, normalizeEnd, normalizeFieldAccessMetadata, normalizeFieldConstraints, normalizeFieldPresentation, normalizeFormConfig, normalizeFormLayoutItems, normalizeFormMetadata, normalizeGlobalActionRef$1 as normalizeGlobalActionRef, normalizeLayoutPolicy, normalizeLookupFilterRequest, normalizePath, normalizePraxisDataQueryContext, normalizePraxisEffectPolicy, normalizePraxisPresentationVisualization, normalizePraxisQueryFilterExpression, normalizePraxisQueryFilterNode, normalizeReactiveDeterminations, normalizeResourceAvailabilityReasonCode, normalizeResourceIdentityContract, normalizeStart, normalizeSurfaceOperationContext, normalizeUnknownError, normalizeWidgetEventPath, notifySuccessHook, parseJsonResponseOrEmpty, praxisLoadingInterceptorFn, prefillFromContextHook, projectComponentAuthoringManifest, projectGroupedCommandPartialRows, provideDefaultFormHooks, provideFieldSelectorRegistryBase, provideFieldSelectorRegistryOverride, provideFieldSelectorRegistryRuntime, provideFormHookPresets, provideFormHooks, provideGlobalActionCatalog, provideGlobalActionHandler, provideGlobalConfig, provideGlobalConfigReady, provideGlobalConfigSeed, provideGlobalConfigTenant, provideHookResolvers, provideHookWhitelist, provideOverlayDecisionMatrix, providePraxisAnalyticsGlobalActions, providePraxisCollectionExportProvider, providePraxisDynamicPageMetadata, providePraxisEnterpriseRuntimeContext, providePraxisFooterLinksMetadata, providePraxisGlobalActionCatalog, providePraxisGlobalActions, providePraxisGlobalConfigBootstrap, providePraxisHeroBannerMetadata, providePraxisHttpCollectionExportProvider, providePraxisHttpLoading, providePraxisI18n, providePraxisI18nConfig, providePraxisI18nTranslator, providePraxisIconDefaults, providePraxisJsonLogicOperator, providePraxisJsonLogicOperatorOverride, providePraxisLegalNoticeMetadata, providePraxisLoadingDefaults, providePraxisLogging, providePraxisRelatedResourceOutletMetadata, providePraxisRichTextBlockMetadata, providePraxisTelemetry, providePraxisToastGlobalActions, providePraxisUserContextSummaryMetadata, provideRemoteGlobalConfig, readPraxisExportValue, reconcileFilterConfig, reconcileFormConfig, reconcileTableConfig, registerPraxisRuntimeComponentObservation, removeDiacritics, renderPraxisPresentationVisualizationHtml, reportTelemetryHookFactory, requiredCheckedValidator, requiredPresenceValidator, resolveBuiltinPresets, resolveColumnTypeFromFieldDefinition, resolveControlTypeAlias, resolveDateRangeShortcutPreset, resolveDateRangeShortcutPresets, resolveDefaultValuePresentationFormat, resolveEntityLookupPayloadMode, resolveFieldPresentation, resolveGroupedCommandPartialRowSpans, resolveHidden, resolveInlineFilterControlType, resolveInlineFilterControlTypeToBaseControlType, resolveLoggerConfig, resolveObservabilityOptions, resolveOffset, resolveOrder, resolvePraxisCollectionExportItems, resolvePraxisExportFields, resolvePraxisExportScope, resolvePraxisFilterCriteria, resolvePraxisI18nDocument, resolveResourceAvailabilityReasonKey, resolveResourceIdentityContract, resolveSpan, resolveTextMaskFormat, resolveTextMaskFormatFromFieldDefinition, resolveValuePresentation, resolveValuePresentationLocale, serializeEntityLookupValueForPayload, serializeOptionSourceFilterRequest, serializePraxisCollectionToCsv, serializePraxisCollectionToExcel, serializePraxisCollectionToJson, slugify, staticDateRangePresetToPreset, stripMasksHook, supportsConfigDocuments, supportsImplicitValuePresentation, syncWithServerMetadata, textSha256, toCamel, toCapitalize, toKebab, toPascal, toSentenceCase, toSnake, toTitleCase, translateResourceAvailabilityReason, translateResourceDiscoveryText, translateSurfaceNavigationRejected, translateSurfaceOpenFailed, translateUnavailableWorkflowMessage, trim, uniqueAsyncValidator, urlValidator, validateGlobalActionRef, validateGlobalActionRefs, withFormConfigSections, withMessage, withPraxisHttpLoading };