@smartbit4all/ng-client 7.3.12 → 7.3.14

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.
@@ -7290,46 +7290,32 @@ class SmartFormService {
7290
7290
  }
7291
7291
  getValueDeeply(model, key) {
7292
7292
  let splittedKey = key.split('.');
7293
- let value;
7294
- splittedKey.forEach((k) => {
7295
- if (!value) {
7296
- value = model[k];
7297
- }
7298
- else {
7299
- if (value[k] === undefined ||
7300
- value[k] === null ||
7301
- (!value && typeof value[k] !== 'boolean')) {
7302
- value = '';
7303
- return value;
7304
- }
7305
- else {
7306
- value = value[k];
7307
- }
7293
+ let value = model[splittedKey[0]];
7294
+ for (const k of splittedKey.slice(1)) {
7295
+ // A null or non-object parent has no field: the key binds nothing.
7296
+ if (value === null || typeof value !== 'object') {
7297
+ return undefined;
7308
7298
  }
7309
- });
7299
+ value = value[k] ?? '';
7300
+ }
7310
7301
  return value;
7311
7302
  }
7312
7303
  setValueDeeply(model, key, value) {
7313
7304
  let splittedKeys = key.split('.');
7314
- let deepModel = [model];
7315
- let tempModel;
7316
- for (let index = 0; index < splittedKeys.length; index++) {
7317
- tempModel = deepModel[index];
7318
- if (typeof tempModel[splittedKeys[index]] !== 'string' &&
7319
- typeof tempModel[splittedKeys[index]] !== 'boolean' &&
7320
- typeof tempModel[splittedKeys[index]] !== 'number' &&
7321
- tempModel[splittedKeys[index]] !== undefined) {
7322
- deepModel.push(tempModel[splittedKeys[index]]);
7323
- }
7324
- if (index === splittedKeys.length - 1) {
7325
- let oldValue = tempModel[splittedKeys[index]];
7326
- // if (value === '' && (oldValue === null || oldValue === undefined) then don't change value.
7327
- if (value !== '' || (oldValue !== null && oldValue !== undefined)) {
7328
- tempModel[splittedKeys[index]] = value;
7329
- }
7330
- break;
7305
+ const leafKey = splittedKeys.pop();
7306
+ let tempModel = model;
7307
+ for (const k of splittedKeys) {
7308
+ tempModel = tempModel[k];
7309
+ // A null or non-object parent has no field to write: the write is dropped.
7310
+ if (tempModel === null || typeof tempModel !== 'object') {
7311
+ return;
7331
7312
  }
7332
7313
  }
7314
+ let oldValue = tempModel[leafKey];
7315
+ // if (value === '' && (oldValue === null || oldValue === undefined) then don't change value.
7316
+ if (value !== '' || (oldValue !== null && oldValue !== undefined)) {
7317
+ tempModel[leafKey] = value;
7318
+ }
7333
7319
  }
7334
7320
  getPropertyNamesDeeply(element) {
7335
7321
  let properties = Object.keys(element);
@@ -11487,36 +11473,316 @@ function resolveViewHandler(viewHandlers, defaultComponents, view) {
11487
11473
  * Public API Surface of smart-view-context
11488
11474
  */
11489
11475
 
11476
+ /** Input types that take no text, so a key pressed on them is free for a keybinding. */
11477
+ const NON_TEXT_INPUT_TYPES = new Set([
11478
+ 'button',
11479
+ 'checkbox',
11480
+ 'radio',
11481
+ 'submit',
11482
+ 'reset',
11483
+ 'file',
11484
+ 'range',
11485
+ 'color',
11486
+ ]);
11487
+ /**
11488
+ * Whether the element takes typed text: a text-like input, a textarea, a select or anything
11489
+ * contenteditable. Keys pressed there belong to the element first (ADR-0012).
11490
+ */
11491
+ function isEditableTarget(element) {
11492
+ if (element instanceof HTMLInputElement) {
11493
+ return !NON_TEXT_INPUT_TYPES.has(element.type);
11494
+ }
11495
+ return (element instanceof HTMLTextAreaElement ||
11496
+ element instanceof HTMLSelectElement ||
11497
+ (element instanceof HTMLElement && element.isContentEditable));
11498
+ }
11499
+ /** The controls the browser activates on `Enter` or `Space`, natively or by their ARIA role. */
11500
+ const ACTIVATABLE_SELECTOR = [
11501
+ 'button',
11502
+ 'a[href]',
11503
+ 'summary',
11504
+ ...[
11505
+ 'button',
11506
+ 'link',
11507
+ 'menuitem',
11508
+ 'menuitemcheckbox',
11509
+ 'menuitemradio',
11510
+ 'option',
11511
+ 'switch',
11512
+ 'tab',
11513
+ 'checkbox',
11514
+ 'radio',
11515
+ 'treeitem',
11516
+ ].map((role) => `[role="${role}"]`),
11517
+ ].join(', ');
11518
+ /**
11519
+ * Whether the element is, or sits in, a control that `Enter` or `Space` activates: a button, a
11520
+ * link, a summary, a clickable input or an ARIA-interactive role. The browser activates it after
11521
+ * the keydown, so a binding firing on that key would steal the click (ADR-0012).
11522
+ */
11523
+ function isActivatableTarget(element) {
11524
+ if (element instanceof HTMLInputElement) {
11525
+ return NON_TEXT_INPUT_TYPES.has(element.type);
11526
+ }
11527
+ return element?.closest(ACTIVATABLE_SELECTOR) != null;
11528
+ }
11529
+
11530
+ /** The keys that edit the text of an editable element or move in it, unmodified. */
11531
+ const TEXT_KEYS = new Set([
11532
+ 'Backspace',
11533
+ 'Delete',
11534
+ 'ArrowUp',
11535
+ 'ArrowDown',
11536
+ 'ArrowLeft',
11537
+ 'ArrowRight',
11538
+ 'Home',
11539
+ 'End',
11540
+ 'PageUp',
11541
+ 'PageDown',
11542
+ 'Tab',
11543
+ ]);
11544
+ /**
11545
+ * Turns key presses into the actions the views bind them to (ADR-0012). One document listener
11546
+ * serves every view: the key goes to the innermost view holding the focus and bubbles to the
11547
+ * enclosing views until a binding fires. When nothing has the focus, it goes to the view that had
11548
+ * it last (a pushed layout re-renders the focused field, and the focus falls to the body), else
11549
+ * the top-most dialog, else the current page; while a dialog is open, the view that had the focus
11550
+ * last counts only if it is in the dialog. A dialog sits in the overlay container, outside the DOM
11551
+ * of the views below it, so the key never leaves a dialog.
11552
+ */
11553
+ class SmartKeybindingService {
11554
+ constructor() {
11555
+ this.document = inject(DOCUMENT);
11556
+ this.injector = inject(Injector);
11557
+ this.isMac = isMacPlatform();
11558
+ this.hosts = new Map();
11559
+ /** The elements of no view whose keys go to a view: a dialog's header to the page it shows. */
11560
+ this.forwards = new Map();
11561
+ const listener = (event) => this.onKeydown(event);
11562
+ const focusListener = (event) => this.onFocusin(event);
11563
+ this.document.addEventListener('keydown', listener);
11564
+ this.document.addEventListener('focusin', focusListener);
11565
+ inject(DestroyRef).onDestroy(() => {
11566
+ this.document.removeEventListener('keydown', listener);
11567
+ this.document.removeEventListener('focusin', focusListener);
11568
+ });
11569
+ }
11570
+ /** Lets the view take keys; returns the function that stops it. */
11571
+ register(entry) {
11572
+ this.hosts.set(entry.host, entry);
11573
+ return () => {
11574
+ if (this.hosts.get(entry.host) === entry) {
11575
+ this.hosts.delete(entry.host);
11576
+ }
11577
+ if (this.lastActive === entry) {
11578
+ this.lastActive = undefined;
11579
+ }
11580
+ };
11581
+ }
11582
+ /**
11583
+ * Sends the keys pressed with the focus in `from`, an element outside every view (the header
11584
+ * of a dialog), to the view whose host is `to`; returns the function that stops it.
11585
+ */
11586
+ forwardKeys(from, to) {
11587
+ this.forwards.set(from, to);
11588
+ return () => {
11589
+ if (this.forwards.get(from) === to) {
11590
+ this.forwards.delete(from);
11591
+ }
11592
+ };
11593
+ }
11594
+ onFocusin(event) {
11595
+ // The focus in an overlay of no view (a select or menu panel) keeps the view it came from.
11596
+ const view = this.closestView(event.target instanceof Element ? event.target : null);
11597
+ if (view) {
11598
+ this.lastActive = view;
11599
+ }
11600
+ }
11601
+ onKeydown(event) {
11602
+ // The Enter of an IME composition confirms the composed text; it is no command. A keydown
11603
+ // without a key (the browser's autofill fires one) is no key press either.
11604
+ if (event.repeat || event.defaultPrevented || event.isComposing || !event.key) {
11605
+ return;
11606
+ }
11607
+ const target = event.target instanceof Element ? event.target : null;
11608
+ if (isEditableTarget(target) && editsText(event, target)) {
11609
+ return;
11610
+ }
11611
+ if (isActivatableTarget(target) && activates(event)) {
11612
+ return;
11613
+ }
11614
+ for (let view = this.startView(); view; view = this.closestView(view.host.parentElement)) {
11615
+ const binding = view.bindings().find((b) => matches(b.chord, event, this.isMac));
11616
+ if (binding && view.fire(binding.uiActionCode, binding.uiActionIdentifier)) {
11617
+ event.preventDefault();
11618
+ event.stopPropagation();
11619
+ return;
11620
+ }
11621
+ }
11622
+ }
11623
+ startView() {
11624
+ const active = this.document.activeElement;
11625
+ if (active &&
11626
+ active !== this.document.body &&
11627
+ active !== this.document.documentElement &&
11628
+ this.document.contains(active)) {
11629
+ // An element in an overlay of no view (an open select or menu panel) finds none.
11630
+ const view = this.closestView(active) ?? this.forwardedView(active);
11631
+ if (view) {
11632
+ this.lastActive = view;
11633
+ }
11634
+ return view;
11635
+ }
11636
+ // Injected late: the view context is not needed before the first key, and resolving it at
11637
+ // startup would construct it ahead of the bootstrap.
11638
+ const viewContext = this.injector.get(SmartViewContextService);
11639
+ const last = this.lastActive && this.isOnScreen(this.lastActive) ? this.lastActive : undefined;
11640
+ const dialogUuid = viewContext.topDialogUuid();
11641
+ if (dialogUuid !== undefined) {
11642
+ // A view nested in the dialog keeps the key; the page behind the dialog does not get it.
11643
+ const dialog = this.viewOf(dialogUuid);
11644
+ return last && dialog?.host.contains(last.host) ? last : dialog;
11645
+ }
11646
+ return last ?? this.viewOf(viewContext.activePageUuid());
11647
+ }
11648
+ isOnScreen(view) {
11649
+ return this.hosts.get(view.host) === view && this.document.contains(view.host);
11650
+ }
11651
+ viewOf(uuid) {
11652
+ return [...this.hosts.values()].find((view) => view.uuid === uuid);
11653
+ }
11654
+ forwardedView(element) {
11655
+ for (const [from, to] of this.forwards) {
11656
+ if (from.contains(element)) {
11657
+ return this.hosts.get(to);
11658
+ }
11659
+ }
11660
+ return undefined;
11661
+ }
11662
+ closestView(element) {
11663
+ for (let node = element; node; node = node.parentElement) {
11664
+ const view = this.hosts.get(node);
11665
+ if (view) {
11666
+ return view;
11667
+ }
11668
+ }
11669
+ return undefined;
11670
+ }
11671
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
11672
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, providedIn: 'root' }); }
11673
+ }
11674
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, decorators: [{
11675
+ type: Injectable,
11676
+ args: [{ providedIn: 'root' }]
11677
+ }], ctorParameters: () => [] });
11678
+ /**
11679
+ * Whether the key, pressed in an editable element, edits or moves in its text. `Escape`, the
11680
+ * function keys and `Enter` in a single-line input do not, so they reach the bindings.
11681
+ */
11682
+ function editsText(event, target) {
11683
+ if (event.ctrlKey || event.altKey || event.metaKey) {
11684
+ return false;
11685
+ }
11686
+ if (event.key.length === 1 || TEXT_KEYS.has(event.key)) {
11687
+ return true;
11688
+ }
11689
+ return (event.key === 'Enter' &&
11690
+ (target instanceof HTMLTextAreaElement ||
11691
+ (target instanceof HTMLElement && target.isContentEditable)));
11692
+ }
11693
+ /**
11694
+ * Whether the key activates a focused button, link or ARIA control: an unmodified `Enter` or
11695
+ * `Space` (Shift alone counts as unmodified). The browser clicks the control after the keydown,
11696
+ * so the key stays with it.
11697
+ */
11698
+ function activates(event) {
11699
+ const modified = event.ctrlKey || event.altKey || event.metaKey;
11700
+ return !modified && (event.key === 'Enter' || event.key === ' ');
11701
+ }
11702
+
11703
+ /** The hardcoded toolbar ids (UiAction.toolbar) a child's action can target. */
11704
+ const TOOLBAR_DIALOG_HEADER = "toolbarDialogHeader";
11705
+ const TOOLBAR_DIALOG_FOOTER = "toolbarDialogFooter";
11706
+ /** The one place that knows where the client keeps its current window descriptor. Adjust to your model. */
11707
+ function windowDescriptorOf(client) {
11708
+ return client?.modelState?.()?.windowDescriptor;
11709
+ }
11710
+ /** The shape the toolbar was always given: { descriptor, uiAction }. */
11711
+ function toModel(action) {
11712
+ return { descriptor: action.descriptor, uiAction: action };
11713
+ }
11490
11714
  class SmartContainerDialog {
11491
11715
  constructor() {
11716
+ this.TOOLBAR_DIALOG_HEADER = TOOLBAR_DIALOG_HEADER;
11717
+ this.TOOLBAR_DIALOG_FOOTER = TOOLBAR_DIALOG_FOOTER;
11492
11718
  this.data = inject(MAT_DIALOG_DATA);
11493
- /** The client of the rendered page: the executor of the header actions. */
11719
+ this.keybindings = inject(SmartKeybindingService);
11720
+ this.destroyRef = inject(DestroyRef);
11721
+ /** The client of the rendered page: the executor of the toolbar actions. */
11494
11722
  this.executor = signal(undefined, /* @ts-ignore */
11495
11723
  ...(ngDevMode ? [{ debugName: "executor" }] : /* istanbul ignore next */ []));
11496
- // Built once: a getter returning a new array on every change detection would rebind the
11497
- // toolbar's input each time and make it re-resolve its entries.
11498
- this.uiActionModels = this.actions.map((a) => ({ descriptor: a.descriptor, uiAction: a }));
11499
- }
11500
- get title() {
11501
- return this.data.descriptor?.title ?? "";
11502
- }
11503
- get actions() {
11504
- return this.data.descriptor?.actions ?? [];
11724
+ /** The current window descriptor: starts from the dialog data, then follows the client. */
11725
+ this.descriptor = signal(this.data.descriptor, /* @ts-ignore */
11726
+ ...(ngDevMode ? [{ debugName: "descriptor" }] : /* istanbul ignore next */ []));
11727
+ this.title = computed(() => this.descriptor()?.title ?? "", /* @ts-ignore */
11728
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
11729
+ this.childActions = computed(() => {
11730
+ const models = this.executor()?.actionModels?.() ?? [];
11731
+ const actions = models.map((m) => m.uiAction).filter((a) => !!a);
11732
+ return actions;
11733
+ }, /* @ts-ignore */
11734
+ ...(ngDevMode ? [{ debugName: "childActions" }] : /* istanbul ignore next */ []));
11735
+ this.headerModels = computed(() => {
11736
+ const result = this.childActions()
11737
+ .filter((a) => a.toolbar === TOOLBAR_DIALOG_HEADER)
11738
+ .map(toModel);
11739
+ return result;
11740
+ }, /* @ts-ignore */
11741
+ ...(ngDevMode ? [{ debugName: "headerModels" }] : /* istanbul ignore next */ []));
11742
+ this.footerModels = computed(() => {
11743
+ const result = this.childActions()
11744
+ .filter((a) => a.toolbar === TOOLBAR_DIALOG_FOOTER)
11745
+ .map(toModel);
11746
+ return result;
11747
+ }, /* @ts-ignore */
11748
+ ...(ngDevMode ? [{ debugName: "footerModels" }] : /* istanbul ignore next */ []));
11505
11749
  }
11506
11750
  ngAfterViewInit() {
11507
11751
  const ref = this.vcRef.createComponent(this.data.component);
11508
- this.executor.set(ref.injector.get(SmartComponentHost, null)?.client);
11752
+ const client = ref.injector.get(SmartComponentHost, null)?.client;
11753
+ if (client) {
11754
+ // The header is outside the page's element, so its keys would reach no view.
11755
+ this.destroyRef.onDestroy(this.keybindings.forwardKeys(this.header.nativeElement, ref.location.nativeElement));
11756
+ // The window descriptor (title) can change with the page's data.
11757
+ client.dataChanged
11758
+ .pipe(takeUntilDestroyed(this.destroyRef))
11759
+ .subscribe(() => this.refreshDescriptor(client));
11760
+ }
11761
+ this.executor.set(client);
11509
11762
  ref.changeDetectorRef.detectChanges();
11763
+ if (client) {
11764
+ this.refreshDescriptor(client);
11765
+ }
11766
+ }
11767
+ refreshDescriptor(client) {
11768
+ const next = windowDescriptorOf(client);
11769
+ // Keep the descriptor from the dialog data until the client has one of its own.
11770
+ if (next && next !== this.descriptor()) {
11771
+ this.descriptor.set(next);
11772
+ }
11510
11773
  }
11511
11774
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartContainerDialog, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11512
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartContainerDialog, isStandalone: true, selector: "smart-container-dialog", viewQueries: [{ propertyName: "vcRef", first: true, predicate: ["renderComponentHere"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div class=\"dialog-container\">\r\n <div class=\"dialog-header\">\r\n <h1 class=\"dialog-title\">{{ title }}</h1>\r\n\r\n @if (actions.length && executor()) {\r\n <smart-ui-action-toolbar\r\n class=\"dialog-header-toolbar\"\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n </div>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n <ng-template #renderComponentHere></ng-template>\r\n </mat-dialog-content>\r\n</div>\r\n", styles: [".dialog-container{display:flex;flex-direction:column}.dialog-header{display:flex;align-items:center;gap:.5rem;padding:.25rem}.dialog-title{flex:1;margin:0}.dialog-header-toolbar ::ng-deep button{min-width:unset}\n"], dependencies: [{ kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }] }); }
11775
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartContainerDialog, isStandalone: true, selector: "smart-container-dialog", viewQueries: [{ propertyName: "vcRef", first: true, predicate: ["renderComponentHere"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "header", first: true, predicate: ["header"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"dialog-container\">\r\n <div class=\"dialog-header\" #header>\r\n <h1 class=\"dialog-title\">{{ title() }}</h1>\r\n\r\n @if (headerModels().length && executor()) {\r\n <smart-ui-action-toolbar\r\n class=\"dialog-header-toolbar\"\r\n [id]=\"TOOLBAR_DIALOG_HEADER\"\r\n [uiActionModels]=\"headerModels()\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n </div>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n <ng-template #renderComponentHere></ng-template>\r\n </mat-dialog-content>\r\n\r\n @if (footerModels().length && executor()) {\r\n <div class=\"dialog-footer\">\r\n <smart-ui-action-toolbar\r\n class=\"dialog-footer-toolbar\"\r\n [id]=\"TOOLBAR_DIALOG_FOOTER\"\r\n [uiActionModels]=\"footerModels()\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n }\r\n</div>", styles: [".dialog-container{display:flex;flex-direction:column;--sb4-dialog-default-padding: .5rem;--sb4-dialog-default-max-height: 85vh;--mat-dialog-content-padding: var(--sb4-dialog-default-padding)}.dialog-header{display:flex;align-items:center;gap:var(--sb4-dialog-default-padding);padding:var(--sb4-dialog-default-padding)}.mat-mdc-dialog-content{max-height:var(--sb4-dialog-default-max-height)}.dialog-title{flex:1;margin:0}.dialog-header-toolbar ::ng-deep button{min-width:unset}.dialog-footer{padding:var(--sb4-dialog-default-padding)}\n"], dependencies: [{ kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }] }); }
11513
11776
  }
11514
11777
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartContainerDialog, decorators: [{
11515
11778
  type: Component,
11516
- args: [{ selector: "smart-container-dialog", imports: [MatDialogContent, UiActionToolbarComponent], template: "<div class=\"dialog-container\">\r\n <div class=\"dialog-header\">\r\n <h1 class=\"dialog-title\">{{ title }}</h1>\r\n\r\n @if (actions.length && executor()) {\r\n <smart-ui-action-toolbar\r\n class=\"dialog-header-toolbar\"\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n </div>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n <ng-template #renderComponentHere></ng-template>\r\n </mat-dialog-content>\r\n</div>\r\n", styles: [".dialog-container{display:flex;flex-direction:column}.dialog-header{display:flex;align-items:center;gap:.5rem;padding:.25rem}.dialog-title{flex:1;margin:0}.dialog-header-toolbar ::ng-deep button{min-width:unset}\n"] }]
11779
+ args: [{ selector: "smart-container-dialog", imports: [MatDialogContent, UiActionToolbarComponent], template: "<div class=\"dialog-container\">\r\n <div class=\"dialog-header\" #header>\r\n <h1 class=\"dialog-title\">{{ title() }}</h1>\r\n\r\n @if (headerModels().length && executor()) {\r\n <smart-ui-action-toolbar\r\n class=\"dialog-header-toolbar\"\r\n [id]=\"TOOLBAR_DIALOG_HEADER\"\r\n [uiActionModels]=\"headerModels()\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n </div>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n <ng-template #renderComponentHere></ng-template>\r\n </mat-dialog-content>\r\n\r\n @if (footerModels().length && executor()) {\r\n <div class=\"dialog-footer\">\r\n <smart-ui-action-toolbar\r\n class=\"dialog-footer-toolbar\"\r\n [id]=\"TOOLBAR_DIALOG_FOOTER\"\r\n [uiActionModels]=\"footerModels()\"\r\n [executor]=\"executor()\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n }\r\n</div>", styles: [".dialog-container{display:flex;flex-direction:column;--sb4-dialog-default-padding: .5rem;--sb4-dialog-default-max-height: 85vh;--mat-dialog-content-padding: var(--sb4-dialog-default-padding)}.dialog-header{display:flex;align-items:center;gap:var(--sb4-dialog-default-padding);padding:var(--sb4-dialog-default-padding)}.mat-mdc-dialog-content{max-height:var(--sb4-dialog-default-max-height)}.dialog-title{flex:1;margin:0}.dialog-header-toolbar ::ng-deep button{min-width:unset}.dialog-footer{padding:var(--sb4-dialog-default-padding)}\n"] }]
11517
11780
  }], propDecorators: { vcRef: [{
11518
11781
  type: ViewChild,
11519
11782
  args: ["renderComponentHere", { read: ViewContainerRef, static: true }]
11783
+ }], header: [{
11784
+ type: ViewChild,
11785
+ args: ["header", { static: true }]
11520
11786
  }] } });
11521
11787
 
11522
11788
  class SmartViewContextDialogService {
@@ -11543,12 +11809,12 @@ class SmartViewContextDialogService {
11543
11809
  return dialogRef;
11544
11810
  }
11545
11811
  openComponentAsDialog(component, viewName, uuid, dialogDescriptor) {
11546
- return this.openContainerDialog(component, viewName, dialogDescriptor);
11812
+ return this.openContainerDialog(component, viewName, dialogDescriptor, undefined, uuid);
11547
11813
  }
11548
- openContainerDialog(component, viewName, dialogDescriptor, config) {
11814
+ openContainerDialog(component, viewName, dialogDescriptor, config, uuid) {
11549
11815
  return this.dialog.open(SmartContainerDialog, {
11550
11816
  ...config,
11551
- data: { component, descriptor: dialogDescriptor, viewName },
11817
+ data: { component, descriptor: dialogDescriptor, viewName, uuid },
11552
11818
  closeOnNavigation: false,
11553
11819
  });
11554
11820
  }
@@ -11622,60 +11888,6 @@ function findExchangeSlots(ops) {
11622
11888
  return new Set([...opening].filter((key) => closing.has(key)));
11623
11889
  }
11624
11890
 
11625
- /** Input types that take no text, so a key pressed on them is free for a keybinding. */
11626
- const NON_TEXT_INPUT_TYPES = new Set([
11627
- 'button',
11628
- 'checkbox',
11629
- 'radio',
11630
- 'submit',
11631
- 'reset',
11632
- 'file',
11633
- 'range',
11634
- 'color',
11635
- ]);
11636
- /**
11637
- * Whether the element takes typed text: a text-like input, a textarea, a select or anything
11638
- * contenteditable. Keys pressed there belong to the element first (ADR-0012).
11639
- */
11640
- function isEditableTarget(element) {
11641
- if (element instanceof HTMLInputElement) {
11642
- return !NON_TEXT_INPUT_TYPES.has(element.type);
11643
- }
11644
- return (element instanceof HTMLTextAreaElement ||
11645
- element instanceof HTMLSelectElement ||
11646
- (element instanceof HTMLElement && element.isContentEditable));
11647
- }
11648
- /** The controls the browser activates on `Enter` or `Space`, natively or by their ARIA role. */
11649
- const ACTIVATABLE_SELECTOR = [
11650
- 'button',
11651
- 'a[href]',
11652
- 'summary',
11653
- ...[
11654
- 'button',
11655
- 'link',
11656
- 'menuitem',
11657
- 'menuitemcheckbox',
11658
- 'menuitemradio',
11659
- 'option',
11660
- 'switch',
11661
- 'tab',
11662
- 'checkbox',
11663
- 'radio',
11664
- 'treeitem',
11665
- ].map((role) => `[role="${role}"]`),
11666
- ].join(', ');
11667
- /**
11668
- * Whether the element is, or sits in, a control that `Enter` or `Space` activates: a button, a
11669
- * link, a summary, a clickable input or an ARIA-interactive role. The browser activates it after
11670
- * the keydown, so a binding firing on that key would steal the click (ADR-0012).
11671
- */
11672
- function isActivatableTarget(element) {
11673
- if (element instanceof HTMLInputElement) {
11674
- return NON_TEXT_INPUT_TYPES.has(element.type);
11675
- }
11676
- return element?.closest(ACTIVATABLE_SELECTOR) != null;
11677
- }
11678
-
11679
11891
  const DefaultUiActionCode_CLOSE = "DEFAULT_CLOSE";
11680
11892
  const DIALOG_DISABLE_CLOSE = new InjectionToken("dialogDisableClose");
11681
11893
  /** How long the old component of an exchanged slot waits for the model of the new view. */
@@ -15568,156 +15780,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
15568
15780
  type: Injectable
15569
15781
  }] });
15570
15782
 
15571
- /** The keys that edit the text of an editable element or move in it, unmodified. */
15572
- const TEXT_KEYS = new Set([
15573
- 'Backspace',
15574
- 'Delete',
15575
- 'ArrowUp',
15576
- 'ArrowDown',
15577
- 'ArrowLeft',
15578
- 'ArrowRight',
15579
- 'Home',
15580
- 'End',
15581
- 'PageUp',
15582
- 'PageDown',
15583
- 'Tab',
15584
- ]);
15585
- /**
15586
- * Turns key presses into the actions the views bind them to (ADR-0012). One document listener
15587
- * serves every view: the key goes to the innermost view holding the focus and bubbles to the
15588
- * enclosing views until a binding fires. When nothing has the focus, it goes to the view that had
15589
- * it last (a pushed layout re-renders the focused field, and the focus falls to the body), else
15590
- * the top-most dialog, else the current page; while a dialog is open, the view that had the focus
15591
- * last counts only if it is in the dialog. A dialog sits in the overlay container, outside the DOM
15592
- * of the views below it, so the key never leaves a dialog.
15593
- */
15594
- class SmartKeybindingService {
15595
- constructor() {
15596
- this.document = inject(DOCUMENT);
15597
- this.injector = inject(Injector);
15598
- this.isMac = isMacPlatform();
15599
- this.hosts = new Map();
15600
- const listener = (event) => this.onKeydown(event);
15601
- const focusListener = (event) => this.onFocusin(event);
15602
- this.document.addEventListener('keydown', listener);
15603
- this.document.addEventListener('focusin', focusListener);
15604
- inject(DestroyRef).onDestroy(() => {
15605
- this.document.removeEventListener('keydown', listener);
15606
- this.document.removeEventListener('focusin', focusListener);
15607
- });
15608
- }
15609
- /** Lets the view take keys; returns the function that stops it. */
15610
- register(entry) {
15611
- this.hosts.set(entry.host, entry);
15612
- return () => {
15613
- if (this.hosts.get(entry.host) === entry) {
15614
- this.hosts.delete(entry.host);
15615
- }
15616
- if (this.lastActive === entry) {
15617
- this.lastActive = undefined;
15618
- }
15619
- };
15620
- }
15621
- onFocusin(event) {
15622
- // The focus in an overlay of no view (a select or menu panel) keeps the view it came from.
15623
- const view = this.closestView(event.target instanceof Element ? event.target : null);
15624
- if (view) {
15625
- this.lastActive = view;
15626
- }
15627
- }
15628
- onKeydown(event) {
15629
- // The Enter of an IME composition confirms the composed text; it is no command.
15630
- if (event.repeat || event.defaultPrevented || event.isComposing) {
15631
- return;
15632
- }
15633
- const target = event.target instanceof Element ? event.target : null;
15634
- if (isEditableTarget(target) && editsText(event, target)) {
15635
- return;
15636
- }
15637
- if (isActivatableTarget(target) && activates(event)) {
15638
- return;
15639
- }
15640
- for (let view = this.startView(); view; view = this.closestView(view.host.parentElement)) {
15641
- const binding = view.bindings().find((b) => matches(b.chord, event, this.isMac));
15642
- if (binding && view.fire(binding.uiActionCode, binding.uiActionIdentifier)) {
15643
- event.preventDefault();
15644
- event.stopPropagation();
15645
- return;
15646
- }
15647
- }
15648
- }
15649
- startView() {
15650
- const active = this.document.activeElement;
15651
- if (active &&
15652
- active !== this.document.body &&
15653
- active !== this.document.documentElement &&
15654
- this.document.contains(active)) {
15655
- // An element in an overlay of no view (an open select or menu panel) finds none.
15656
- const view = this.closestView(active);
15657
- if (view) {
15658
- this.lastActive = view;
15659
- }
15660
- return view;
15661
- }
15662
- // Injected late: the view context is not needed before the first key, and resolving it at
15663
- // startup would construct it ahead of the bootstrap.
15664
- const viewContext = this.injector.get(SmartViewContextService);
15665
- const last = this.lastActive && this.isOnScreen(this.lastActive) ? this.lastActive : undefined;
15666
- const dialogUuid = viewContext.topDialogUuid();
15667
- if (dialogUuid !== undefined) {
15668
- // A view nested in the dialog keeps the key; the page behind the dialog does not get it.
15669
- const dialog = this.viewOf(dialogUuid);
15670
- return last && dialog?.host.contains(last.host) ? last : dialog;
15671
- }
15672
- return last ?? this.viewOf(viewContext.activePageUuid());
15673
- }
15674
- isOnScreen(view) {
15675
- return this.hosts.get(view.host) === view && this.document.contains(view.host);
15676
- }
15677
- viewOf(uuid) {
15678
- return [...this.hosts.values()].find((view) => view.uuid === uuid);
15679
- }
15680
- closestView(element) {
15681
- for (let node = element; node; node = node.parentElement) {
15682
- const view = this.hosts.get(node);
15683
- if (view) {
15684
- return view;
15685
- }
15686
- }
15687
- return undefined;
15688
- }
15689
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
15690
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, providedIn: 'root' }); }
15691
- }
15692
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartKeybindingService, decorators: [{
15693
- type: Injectable,
15694
- args: [{ providedIn: 'root' }]
15695
- }], ctorParameters: () => [] });
15696
- /**
15697
- * Whether the key, pressed in an editable element, edits or moves in its text. `Escape`, the
15698
- * function keys and `Enter` in a single-line input do not, so they reach the bindings.
15699
- */
15700
- function editsText(event, target) {
15701
- if (event.ctrlKey || event.altKey || event.metaKey) {
15702
- return false;
15703
- }
15704
- if (event.key.length === 1 || TEXT_KEYS.has(event.key)) {
15705
- return true;
15706
- }
15707
- return (event.key === 'Enter' &&
15708
- (target instanceof HTMLTextAreaElement ||
15709
- (target instanceof HTMLElement && target.isContentEditable)));
15710
- }
15711
- /**
15712
- * Whether the key activates a focused button, link or ARIA control: an unmodified `Enter` or
15713
- * `Space` (Shift alone counts as unmodified). The browser clicks the control after the keydown,
15714
- * so the key stays with it.
15715
- */
15716
- function activates(event) {
15717
- const modified = event.ctrlKey || event.altKey || event.metaKey;
15718
- return !modified && (event.key === 'Enter' || event.key === ' ');
15719
- }
15720
-
15721
15783
  class SmartformLayoutDefinitionService {
15722
15784
  static { this.DEFAULT_QUILL_MODULES = {
15723
15785
  toolbar: {
@@ -16748,11 +16810,11 @@ class SmartTreeComponent {
16748
16810
  return `${cssClass}-${plusProperty}`;
16749
16811
  }
16750
16812
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16751
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartTreeComponent, isStandalone: true, selector: "smart-tree", inputs: { treeStyle: { classPropertyName: "treeStyle", publicName: "treeStyle", isSignal: true, isRequired: false, transformFunction: null }, treeService: { classPropertyName: "treeService", publicName: "treeService", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-testid": "this.testId" } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }, { propertyName: "trigger", predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<div class=\"smartTreeContainer\">\r\n @if (uiActionModels.length) {\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n @if (treeData) {\r\n <mat-tree\r\n #tree\r\n [dataSource]=\"dataSource\"\r\n [treeControl]=\"treeControl\"\r\n [trackBy]=\"trackByIdentifier\"\r\n class=\"sm-tree\"\r\n >\r\n <mat-nested-tree-node\r\n *matTreeNodeDef=\"let node; when: hasChild\"\r\n matTreeNodeToggle\r\n [ngClass]=\"getClassesForTreeNode(node)\"\r\n [ngStyle]=\"getNodeStyle(node)\"\r\n >\r\n <div\r\n [ngStyle]=\"getNodePadding(node)\"\r\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\r\n class=\"mat-tree-node sm-tree-node\"\r\n (click)=\"onNodeClick($event, node)\"\r\n >\r\n <button mat-icon-button matTreeNodeToggle [attr.aria-label]=\"'Toggle ' + node.name\">\r\n <mat-icon class=\"mat-icon-rtl-mirror\" (click)=\"onOpenNode($event, node)\">\r\n @if (hasChild(node.level, node)) {\r\n <div>\r\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\r\n </div>\r\n }\r\n </mat-icon>\r\n </button>\r\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\r\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\r\n <div class=\"sm-tree-row-caption\" [attr.title]=\"node.shortDescription || null\">\r\n {{ node.caption }}\r\n </div>\r\n <div class=\"sm-shortDescription-spacer\"></div>\r\n <div class=\"sm-tree-row-shortDescription\">\r\n {{ node.shortDescription }}\r\n </div>\r\n <div class=\"sm-shortDescription-button-spacer\"></div>\r\n @if (node.button) {\r\n <div class=\"sm-tree-node-button\">\r\n @switch (node.button.type) {\r\n @case (smartTreeNodeButtonType.ICON) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-icon-button\r\n >\r\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\r\n </button>\r\n }\r\n @case (smartTreeNodeButtonType.MENU) {\r\n <div>\r\n <button\r\n mat-button\r\n [matMenuTriggerFor]=\"menu\"\r\n (click)=\"customButtonClicked($event, node.button, true)\"\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon\r\n >\r\n }{{ node.button.label }}\r\n </button>\r\n <mat-menu #menu=\"matMenu\">\r\n @for (\r\n button of node.button.menuItemButtons;\r\n track button.code ?? $index\r\n ) {\r\n <button\r\n (click)=\"customButtonClicked($event, button, true)\"\r\n mat-menu-item\r\n [attr.data-testid]=\"button.code ?? null\"\r\n >\r\n @if (button.icon) {\r\n <smart-icon [icon]=\"button.icon\"></smart-icon\r\n >\r\n }{{ button.label }}\r\n </button>\r\n }\r\n </mat-menu>\r\n </div>\r\n }\r\n @case (smartTreeNodeButtonType.NORMAL) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-raised-button\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon>\r\n }\r\n {{ node.button.icon }}\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div\r\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\r\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\r\n role=\"group\"\r\n >\r\n <ng-container matTreeNodeOutlet></ng-container>\r\n </div>\r\n </mat-nested-tree-node>\r\n </mat-tree>\r\n }\r\n @if (!treeData) {\r\n <div class=\"tree-loading\">\r\n <mat-spinner diameter=\"24\" color=\"primary\"></mat-spinner>\r\n </div>\r\n }\r\n </div>\r\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}.sm-tree-node-selected{background-color:var(--tree-active-background, var(--accent-color, #20b6a8));color:var(--tree-active-color, #ffffff)}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}.sm-tree-row.sm-tree-branch-new{color:var(--success-color, #16a34a)}.sm-tree-row.sm-tree-branch-new .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-modified{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-modified .sm-tree-row-caption{font-weight:600}.sm-tree-row.sm-tree-branch-deleted{color:var(--color-text-muted, #68778e)}.sm-tree-row.sm-tree-branch-deleted .sm-tree-row-caption{text-decoration:line-through}.sm-tree-row.sm-tree-branch-foreign,.sm-tree-row.sm-tree-branch-own-other-view-context{border-left:3px solid;padding-left:6px}.sm-tree-row.sm-tree-branch-foreign{border-left-color:var(--warning-color, #d97706)}.sm-tree-row.sm-tree-branch-own-other-view-context{border-left-color:var(--info-color, #64748b)}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-caption,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-shortDescription,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-shortDescription{display:none}.sm-tree-node-selected .sm-tree-row,.sm-tree-node-selected .sm-tree-row-caption,.sm-tree-node-selected .sm-tree-row-shortDescription{color:inherit}.tree-loading{display:flex;justify-content:center;align-items:center;min-height:200px;width:100%}\n"], dependencies: [{ kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }, { kind: "component", type: MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "directive", type: MatNestedTreeNode, selector: "mat-nested-tree-node", inputs: ["matNestedTreeNode", "disabled", "tabIndex"], outputs: ["activation", "expandedChange"], exportAs: ["matNestedTreeNode"] }, { kind: "directive", type: MatTreeNodeToggle, selector: "[matTreeNodeToggle]", inputs: ["matTreeNodeToggleRecursive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SmartIconComponent, selector: "smart-icon", inputs: ["icon", "color", "imageResource"] }, { kind: "component", type: 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: "directive", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: 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: MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
16813
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartTreeComponent, isStandalone: true, selector: "smart-tree", inputs: { treeStyle: { classPropertyName: "treeStyle", publicName: "treeStyle", isSignal: true, isRequired: false, transformFunction: null }, treeService: { classPropertyName: "treeService", publicName: "treeService", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-testid": "this.testId" } }, viewQueries: [{ propertyName: "tree", first: true, predicate: ["tree"], descendants: true }, { propertyName: "trigger", predicate: MatMenuTrigger, descendants: true }], ngImport: i0, template: "<div class=\"smartTreeContainer\">\r\n @if (uiActionModels.length) {\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n @if (treeData) {\r\n <mat-tree\r\n #tree\r\n [dataSource]=\"dataSource\"\r\n [treeControl]=\"treeControl\"\r\n [trackBy]=\"trackByIdentifier\"\r\n class=\"sm-tree\"\r\n >\r\n <mat-nested-tree-node\r\n *matTreeNodeDef=\"let node; when: hasChild\"\r\n matTreeNodeToggle\r\n [ngClass]=\"getClassesForTreeNode(node)\"\r\n [ngStyle]=\"getNodeStyle(node)\"\r\n >\r\n <div\r\n [ngStyle]=\"getNodePadding(node)\"\r\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\r\n class=\"mat-tree-node sm-tree-node\"\r\n (click)=\"onNodeClick($event, node)\"\r\n >\r\n <button mat-icon-button matTreeNodeToggle [attr.aria-label]=\"'Toggle ' + node.name\">\r\n <mat-icon class=\"mat-icon-rtl-mirror\" (click)=\"onOpenNode($event, node)\">\r\n @if (hasChild(node.level, node)) {\r\n <div>\r\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\r\n </div>\r\n }\r\n </mat-icon>\r\n </button>\r\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\r\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\r\n <div class=\"sm-tree-row-caption\" [attr.title]=\"node.shortDescription || null\">\r\n {{ node.caption }}\r\n </div>\r\n <div class=\"sm-shortDescription-spacer\"></div>\r\n <div class=\"sm-tree-row-shortDescription\">\r\n {{ node.shortDescription }}\r\n </div>\r\n <div class=\"sm-shortDescription-button-spacer\"></div>\r\n @if (node.button) {\r\n <div class=\"sm-tree-node-button\">\r\n @switch (node.button.type) {\r\n @case (smartTreeNodeButtonType.ICON) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-icon-button\r\n >\r\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\r\n </button>\r\n }\r\n @case (smartTreeNodeButtonType.MENU) {\r\n <div>\r\n <button\r\n mat-button\r\n [matMenuTriggerFor]=\"menu\"\r\n (click)=\"customButtonClicked($event, node.button, true)\"\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon\r\n >\r\n }{{ node.button.label }}\r\n </button>\r\n <mat-menu #menu=\"matMenu\">\r\n @for (\r\n button of node.button.menuItemButtons;\r\n track button.code ?? $index\r\n ) {\r\n <button\r\n (click)=\"customButtonClicked($event, button, true)\"\r\n mat-menu-item\r\n [attr.data-testid]=\"button.code ?? null\"\r\n >\r\n @if (button.icon) {\r\n <smart-icon [icon]=\"button.icon\"></smart-icon\r\n >\r\n }{{ button.label }}\r\n </button>\r\n }\r\n </mat-menu>\r\n </div>\r\n }\r\n @case (smartTreeNodeButtonType.NORMAL) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-raised-button\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon>\r\n }\r\n {{ node.button.icon }}\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div\r\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\r\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\r\n role=\"group\"\r\n >\r\n <ng-container matTreeNodeOutlet></ng-container>\r\n </div>\r\n </mat-nested-tree-node>\r\n </mat-tree>\r\n }\r\n @if (!treeData) {\r\n <div class=\"tree-loading\">\r\n <mat-spinner diameter=\"24\" color=\"primary\"></mat-spinner>\r\n </div>\r\n }\r\n </div>\r\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}.sm-tree-node-selected{background-color:var(--tree-active-background, var(--accent-color, #20b6a8));color:var(--tree-active-color, #ffffff)}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}.sm-tree-row.sm-tree-branch-new,.sm-tree-row.sm-tree-branch-new *{color:var(--success-color, #16a34a)}.sm-tree-row.sm-tree-branch-new .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-modified,.sm-tree-row.sm-tree-branch-modified *{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-modified .sm-tree-row-caption{font-weight:600}.sm-tree-row.sm-tree-branch-deleted,.sm-tree-row.sm-tree-branch-deleted *{color:var(--color-text-muted, #68778e)}.sm-tree-row.sm-tree-branch-deleted .sm-tree-row-caption{text-decoration:line-through}.sm-tree-row.sm-tree-branch-contains-change,.sm-tree-row.sm-tree-branch-contains-change *{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-foreign,.sm-tree-row.sm-tree-branch-own-other-view-context{border-left:3px solid;padding-left:6px}.sm-tree-row.sm-tree-branch-foreign{border-left-color:var(--warning-color, #d97706)}.sm-tree-row.sm-tree-branch-own-other-view-context{border-left-color:var(--info-color, #64748b)}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-caption,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-shortDescription,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-shortDescription{display:none}.sm-tree-node-selected .sm-tree-row,.sm-tree-node-selected .sm-tree-row-caption,.sm-tree-node-selected .sm-tree-row-shortDescription{color:inherit}.tree-loading{display:flex;justify-content:center;align-items:center;min-height:200px;width:100%}\n"], dependencies: [{ kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }, { kind: "component", type: MatTree, selector: "mat-tree", exportAs: ["matTree"] }, { kind: "directive", type: MatTreeNodeDef, selector: "[matTreeNodeDef]", inputs: ["matTreeNodeDefWhen", "matTreeNode"] }, { kind: "directive", type: MatNestedTreeNode, selector: "mat-nested-tree-node", inputs: ["matNestedTreeNode", "disabled", "tabIndex"], outputs: ["activation", "expandedChange"], exportAs: ["matNestedTreeNode"] }, { kind: "directive", type: MatTreeNodeToggle, selector: "[matTreeNodeToggle]", inputs: ["matTreeNodeToggleRecursive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SmartIconComponent, selector: "smart-icon", inputs: ["icon", "color", "imageResource"] }, { kind: "component", type: 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: "directive", type: MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: 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: MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: MatTreeNodeOutlet, selector: "[matTreeNodeOutlet]" }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
16752
16814
  }
16753
16815
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartTreeComponent, decorators: [{
16754
16816
  type: Component,
16755
- args: [{ selector: 'smart-tree', imports: [UiActionToolbarComponent, MatTree, MatTreeNodeDef, MatNestedTreeNode, MatTreeNodeToggle, NgClass, NgStyle, MatIconButton, MatIcon, SmartIconComponent, MatButton, MatMenuTrigger, MatMenu, MatMenuItem, MatTreeNodeOutlet, MatProgressSpinner], template: "<div class=\"smartTreeContainer\">\r\n @if (uiActionModels.length) {\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n @if (treeData) {\r\n <mat-tree\r\n #tree\r\n [dataSource]=\"dataSource\"\r\n [treeControl]=\"treeControl\"\r\n [trackBy]=\"trackByIdentifier\"\r\n class=\"sm-tree\"\r\n >\r\n <mat-nested-tree-node\r\n *matTreeNodeDef=\"let node; when: hasChild\"\r\n matTreeNodeToggle\r\n [ngClass]=\"getClassesForTreeNode(node)\"\r\n [ngStyle]=\"getNodeStyle(node)\"\r\n >\r\n <div\r\n [ngStyle]=\"getNodePadding(node)\"\r\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\r\n class=\"mat-tree-node sm-tree-node\"\r\n (click)=\"onNodeClick($event, node)\"\r\n >\r\n <button mat-icon-button matTreeNodeToggle [attr.aria-label]=\"'Toggle ' + node.name\">\r\n <mat-icon class=\"mat-icon-rtl-mirror\" (click)=\"onOpenNode($event, node)\">\r\n @if (hasChild(node.level, node)) {\r\n <div>\r\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\r\n </div>\r\n }\r\n </mat-icon>\r\n </button>\r\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\r\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\r\n <div class=\"sm-tree-row-caption\" [attr.title]=\"node.shortDescription || null\">\r\n {{ node.caption }}\r\n </div>\r\n <div class=\"sm-shortDescription-spacer\"></div>\r\n <div class=\"sm-tree-row-shortDescription\">\r\n {{ node.shortDescription }}\r\n </div>\r\n <div class=\"sm-shortDescription-button-spacer\"></div>\r\n @if (node.button) {\r\n <div class=\"sm-tree-node-button\">\r\n @switch (node.button.type) {\r\n @case (smartTreeNodeButtonType.ICON) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-icon-button\r\n >\r\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\r\n </button>\r\n }\r\n @case (smartTreeNodeButtonType.MENU) {\r\n <div>\r\n <button\r\n mat-button\r\n [matMenuTriggerFor]=\"menu\"\r\n (click)=\"customButtonClicked($event, node.button, true)\"\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon\r\n >\r\n }{{ node.button.label }}\r\n </button>\r\n <mat-menu #menu=\"matMenu\">\r\n @for (\r\n button of node.button.menuItemButtons;\r\n track button.code ?? $index\r\n ) {\r\n <button\r\n (click)=\"customButtonClicked($event, button, true)\"\r\n mat-menu-item\r\n [attr.data-testid]=\"button.code ?? null\"\r\n >\r\n @if (button.icon) {\r\n <smart-icon [icon]=\"button.icon\"></smart-icon\r\n >\r\n }{{ button.label }}\r\n </button>\r\n }\r\n </mat-menu>\r\n </div>\r\n }\r\n @case (smartTreeNodeButtonType.NORMAL) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-raised-button\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon>\r\n }\r\n {{ node.button.icon }}\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div\r\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\r\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\r\n role=\"group\"\r\n >\r\n <ng-container matTreeNodeOutlet></ng-container>\r\n </div>\r\n </mat-nested-tree-node>\r\n </mat-tree>\r\n }\r\n @if (!treeData) {\r\n <div class=\"tree-loading\">\r\n <mat-spinner diameter=\"24\" color=\"primary\"></mat-spinner>\r\n </div>\r\n }\r\n </div>\r\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}.sm-tree-node-selected{background-color:var(--tree-active-background, var(--accent-color, #20b6a8));color:var(--tree-active-color, #ffffff)}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}.sm-tree-row.sm-tree-branch-new{color:var(--success-color, #16a34a)}.sm-tree-row.sm-tree-branch-new .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-modified{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-modified .sm-tree-row-caption{font-weight:600}.sm-tree-row.sm-tree-branch-deleted{color:var(--color-text-muted, #68778e)}.sm-tree-row.sm-tree-branch-deleted .sm-tree-row-caption{text-decoration:line-through}.sm-tree-row.sm-tree-branch-foreign,.sm-tree-row.sm-tree-branch-own-other-view-context{border-left:3px solid;padding-left:6px}.sm-tree-row.sm-tree-branch-foreign{border-left-color:var(--warning-color, #d97706)}.sm-tree-row.sm-tree-branch-own-other-view-context{border-left-color:var(--info-color, #64748b)}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-caption,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-shortDescription,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-shortDescription{display:none}.sm-tree-node-selected .sm-tree-row,.sm-tree-node-selected .sm-tree-row-caption,.sm-tree-node-selected .sm-tree-row-shortDescription{color:inherit}.tree-loading{display:flex;justify-content:center;align-items:center;min-height:200px;width:100%}\n"] }]
16817
+ args: [{ selector: 'smart-tree', imports: [UiActionToolbarComponent, MatTree, MatTreeNodeDef, MatNestedTreeNode, MatTreeNodeToggle, NgClass, NgStyle, MatIconButton, MatIcon, SmartIconComponent, MatButton, MatMenuTrigger, MatMenu, MatMenuItem, MatTreeNodeOutlet, MatProgressSpinner], template: "<div class=\"smartTreeContainer\">\r\n @if (uiActionModels.length) {\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"executor\"\r\n ></smart-ui-action-toolbar>\r\n }\r\n @if (treeData) {\r\n <mat-tree\r\n #tree\r\n [dataSource]=\"dataSource\"\r\n [treeControl]=\"treeControl\"\r\n [trackBy]=\"trackByIdentifier\"\r\n class=\"sm-tree\"\r\n >\r\n <mat-nested-tree-node\r\n *matTreeNodeDef=\"let node; when: hasChild\"\r\n matTreeNodeToggle\r\n [ngClass]=\"getClassesForTreeNode(node)\"\r\n [ngStyle]=\"getNodeStyle(node)\"\r\n >\r\n <div\r\n [ngStyle]=\"getNodePadding(node)\"\r\n [ngClass]=\"getInnerClassesForTreeNode(node)\"\r\n class=\"mat-tree-node sm-tree-node\"\r\n (click)=\"onNodeClick($event, node)\"\r\n >\r\n <button mat-icon-button matTreeNodeToggle [attr.aria-label]=\"'Toggle ' + node.name\">\r\n <mat-icon class=\"mat-icon-rtl-mirror\" (click)=\"onOpenNode($event, node)\">\r\n @if (hasChild(node.level, node)) {\r\n <div>\r\n {{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}\r\n </div>\r\n }\r\n </mat-icon>\r\n </button>\r\n <smart-icon [icon]=\"node.icon\"> </smart-icon>\r\n <div class=\"sm-tree-row\" [ngClass]=\"node.classes\">\r\n <div class=\"sm-tree-row-caption\" [attr.title]=\"node.shortDescription || null\">\r\n {{ node.caption }}\r\n </div>\r\n <div class=\"sm-shortDescription-spacer\"></div>\r\n <div class=\"sm-tree-row-shortDescription\">\r\n {{ node.shortDescription }}\r\n </div>\r\n <div class=\"sm-shortDescription-button-spacer\"></div>\r\n @if (node.button) {\r\n <div class=\"sm-tree-node-button\">\r\n @switch (node.button.type) {\r\n @case (smartTreeNodeButtonType.ICON) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-icon-button\r\n >\r\n <smart-icon title=\"{{ node.button.icon }}\" [icon]=\"node.button.icon\"></smart-icon>\r\n </button>\r\n }\r\n @case (smartTreeNodeButtonType.MENU) {\r\n <div>\r\n <button\r\n mat-button\r\n [matMenuTriggerFor]=\"menu\"\r\n (click)=\"customButtonClicked($event, node.button, true)\"\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon\r\n >\r\n }{{ node.button.label }}\r\n </button>\r\n <mat-menu #menu=\"matMenu\">\r\n @for (\r\n button of node.button.menuItemButtons;\r\n track button.code ?? $index\r\n ) {\r\n <button\r\n (click)=\"customButtonClicked($event, button, true)\"\r\n mat-menu-item\r\n [attr.data-testid]=\"button.code ?? null\"\r\n >\r\n @if (button.icon) {\r\n <smart-icon [icon]=\"button.icon\"></smart-icon\r\n >\r\n }{{ button.label }}\r\n </button>\r\n }\r\n </mat-menu>\r\n </div>\r\n }\r\n @case (smartTreeNodeButtonType.NORMAL) {\r\n <button\r\n (click)=\"customButtonClicked($event, node.button)\"\r\n mat-raised-button\r\n >\r\n @if (node.button.icon) {\r\n <smart-icon [icon]=\"node.button.icon\"></smart-icon>\r\n }\r\n {{ node.button.icon }}\r\n </button>\r\n }\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div\r\n [class.sm-tree-invisible]=\"!treeControl.isExpanded(node)\"\r\n [ngClass]=\"getClassesForTreeNodeChildren(node)\"\r\n role=\"group\"\r\n >\r\n <ng-container matTreeNodeOutlet></ng-container>\r\n </div>\r\n </mat-nested-tree-node>\r\n </mat-tree>\r\n }\r\n @if (!treeData) {\r\n <div class=\"tree-loading\">\r\n <mat-spinner diameter=\"24\" color=\"primary\"></mat-spinner>\r\n </div>\r\n }\r\n </div>\r\n", styles: [".smartTreeContainer{display:flex;flex-direction:column;gap:.5rem}.sm-tree-invisible{display:none}.sm-tree ul,.sm-tree li{margin-top:0;margin-bottom:0;list-style-type:none}.sm-tree div[role=group]>.mat-tree-node{padding-left:40px}.sm-tee-node{padding-left:40px}.sm-tree-node-name{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tree-node-name-row{padding-left:15px;padding-top:15px;display:flex;flex-direction:row;justify-content:space-between}.sm-tree-node-name-col{padding-left:15px;padding-top:15px;display:flex;flex-direction:column}.sm-tee-node-id{font-weight:lighter}.mat-tree-node:hover{cursor:pointer}.sm-tree-node-selected{background-color:var(--tree-active-background, var(--accent-color, #20b6a8));color:var(--tree-active-color, #ffffff)}::ng-deep .mat-icon-rtl-mirror{display:flex;flex-direction:row}.sm-tree-row{display:flex;flex-direction:row;flex:1;align-items:center}.sm-shortDescription-spacer{flex:1}.sm-tree-row.sm-tree-branch-new,.sm-tree-row.sm-tree-branch-new *{color:var(--success-color, #16a34a)}.sm-tree-row.sm-tree-branch-new .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-modified,.sm-tree-row.sm-tree-branch-modified *{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-modified .sm-tree-row-caption{font-weight:600}.sm-tree-row.sm-tree-branch-deleted,.sm-tree-row.sm-tree-branch-deleted *{color:var(--color-text-muted, #68778e)}.sm-tree-row.sm-tree-branch-deleted .sm-tree-row-caption{text-decoration:line-through}.sm-tree-row.sm-tree-branch-contains-change,.sm-tree-row.sm-tree-branch-contains-change *{color:var(--accent-color, #20b6a8)}.sm-tree-row.sm-tree-branch-foreign,.sm-tree-row.sm-tree-branch-own-other-view-context{border-left:3px solid;padding-left:6px}.sm-tree-row.sm-tree-branch-foreign{border-left-color:var(--warning-color, #d97706)}.sm-tree-row.sm-tree-branch-own-other-view-context{border-left-color:var(--info-color, #64748b)}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-caption,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-caption{font-style:italic}.sm-tree-row.sm-tree-branch-foreign .sm-tree-row-shortDescription,.sm-tree-row.sm-tree-branch-own-other-view-context .sm-tree-row-shortDescription{display:none}.sm-tree-node-selected .sm-tree-row,.sm-tree-node-selected .sm-tree-row-caption,.sm-tree-node-selected .sm-tree-row-shortDescription{color:inherit}.tree-loading{display:flex;justify-content:center;align-items:center;min-height:200px;width:100%}\n"] }]
16756
16818
  }], ctorParameters: () => [], propDecorators: { trigger: [{
16757
16819
  type: ViewChildren,
16758
16820
  args: [MatMenuTrigger]
@@ -18646,6 +18708,11 @@ class SmartComponentApiClient {
18646
18708
  this.dataChanged = new Subject();
18647
18709
  this.inmediateDataChangeKeys = [];
18648
18710
  this.blurDataChangeKeys = [];
18711
+ /**
18712
+ * The actions of the window descriptor of the dialog showing this view (its header), set by
18713
+ * the dialog. The backend binds keys to them on this view, so the keys fire them here.
18714
+ */
18715
+ this.windowActions = [];
18649
18716
  // check and calculate pageName
18650
18717
  if (!this.pageName && !this._componentName) {
18651
18718
  throw new Error('pageName or componentName must be specified!');
@@ -18707,10 +18774,18 @@ class SmartComponentApiClient {
18707
18774
  fire: (code, identifier) => this.fireBoundAction(code, identifier),
18708
18775
  });
18709
18776
  }
18710
- /** The action a binding fires: the one of the code and identifier, or the first of the code. */
18777
+ /**
18778
+ * The action a binding fires: the one of the code and identifier, or the first of the code,
18779
+ * among the actions of the view, then among the header actions of the dialog showing it.
18780
+ */
18711
18781
  findBoundAction(code, identifier) {
18712
- return this.actionModels().find((entry) => entry.uiAction.code === code &&
18713
- (identifier == null || entry.uiAction.identifier === identifier));
18782
+ const isBound = (uiAction) => uiAction.code === code && (identifier == null || uiAction.identifier === identifier);
18783
+ const entry = this.actionModels().find((model) => isBound(model.uiAction));
18784
+ if (entry) {
18785
+ return entry;
18786
+ }
18787
+ const windowAction = this.windowActions.find(isBound);
18788
+ return windowAction ? { serviceToUse: this, uiAction: windowAction } : undefined;
18714
18789
  }
18715
18790
  fireBoundAction(code, identifier) {
18716
18791
  const entry = this.findBoundAction(code, identifier);
@@ -18787,8 +18862,8 @@ class SmartComponentApiClient {
18787
18862
  async load() {
18788
18863
  this.componentModelLoaded = false;
18789
18864
  // Slot-hosted views (NORMAL/EMBEDDED) get their uuid injected at creation. DIALOG views
18790
- // are NOT slot-hosted, so they still resolve their uuid via the pageName -> getUuidOfPage
18791
- // shim (set by openView's DIALOG branch). Part of the retained page-uuid shim (#28870).
18865
+ // are not slot-hosted: they read it from the dialog data, and fall back to the
18866
+ // pageName -> getUuidOfPage shim only when the dialog was opened without one.
18792
18867
  const uuid = this.uuid ?? this.readDialogDataUuid() ?? this.viewContext.getUuidOfPage(this.pageName);
18793
18868
  // console.log('[Embedded] load()', {
18794
18869
  // pageName: this.pageName,
@@ -25420,11 +25495,11 @@ class SmartFilterEditorContentComponent {
25420
25495
  });
25421
25496
  }
25422
25497
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartFilterEditorContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25423
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartFilterEditorContentComponent, isStandalone: true, selector: "smart-filter-expression-editor", inputs: { service: "service" }, viewQueries: [{ propertyName: "vcRef", first: true, predicate: ["form"], descendants: true, read: ViewContainerRef }, { propertyName: "simpleFilterVcRef", first: true, predicate: ["simpleFilterFromChild"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<div class=\"smart-filter-editor-container\" *ngIf=\"service.model\">\r\n @if (service.model.model?.label) {\r\n <h3 class=\"smart-filter-editor-title\">{{ service.model.model?.label }}</h3>\r\n }\r\n\r\n <div *ngIf=\"service.model.type === type.SIMPLE; then simpleFilter; else complexFilter\"></div>\r\n\r\n <ng-template #simpleFilter>\r\n <div class=\"smart-filter-editor-expressionsContainer\">\r\n <ng-template #simpleFilterFromChild></ng-template>\r\n <!-- #simpleFilterFromChild [smartForm]=\"simpleFilterForm\"></smartform> -->\r\n </div>\r\n </ng-template>\r\n <ng-template #complexFilter>\r\n <div class=\"smart-filter-editor-uiActionsBar\" *ngIf=\"!service.model?.readOnly\">\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"service\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n service.model?.model?.workplaceList?.filters?.length;\r\n then withParameters;\r\n else withoutParameters\r\n \"\r\n ></div>\r\n <ng-template #withoutParameters>\r\n <div class=\"smart-filter-editor-withoutParametersContainer\">\r\n <span class=\"mat-body\"> M\u00E9g nincs megadva param\u00E9ter </span>\r\n </div>\r\n </ng-template>\r\n <ng-template #withParameters>\r\n <div class=\"smart-filter-editor-withParametersContainer\">\r\n <div\r\n class=\"smart-filter-editor-expressionsContainer\"\r\n *ngIf=\"service.model?.model?.workplaceList?.filters?.length\"\r\n (click)=\"unSelectItems($event)\"\r\n >\r\n <app-expression-items\r\n [items]=\"(service.model?.model?.workplaceList)!.filters\"\r\n [service]=\"service\"\r\n ></app-expression-items>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n !useAdvancedFilter ||\r\n service.model?.selectedField?.expressionData?.currentOperation === 'EXPRESSION'\r\n \"\r\n class=\"smart-filter-editor-expressionEditorContainer\"\r\n >\r\n <!-- <smartform #form [smartForm]=\"smartForm\"></smartform> -->\r\n <ng-template #form></ng-template>\r\n <div *ngIf=\"smartForm\">\r\n <button (click)=\"remove()\" mat-raised-button color=\"primary\">T\u00F6rl\u00E9s</button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </ng-template>\r\n</div>\r\n", styles: [".smart-filter-editor-container{display:flex;flex-direction:column;overflow:auto;position:relative}.targetGroupContentContainer h3{margin:0;font-weight:600}.smart-filter-editor-uiActionsBar{padding:.5rem;border-radius:.25rem;background-color:var(--container-color);display:flex;flex-direction:row;gap:1rem}.smart-filter-editor-withoutParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.withoutParametersContainer .mat-body{font-weight:600;text-align:center}.smart-filter-editor-withParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.smart-filter-editor-expressionEditorContainer{display:flex;flex-direction:column;gap:.75rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }, { kind: "component", type: SmartFilterExpressionItemsComponent, selector: "app-expression-items", inputs: ["items", "service"] }, { kind: "component", type: 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"] }] }); }
25498
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: SmartFilterEditorContentComponent, isStandalone: true, selector: "smart-filter-expression-editor", inputs: { service: "service" }, viewQueries: [{ propertyName: "vcRef", first: true, predicate: ["form"], descendants: true, read: ViewContainerRef }, { propertyName: "simpleFilterVcRef", first: true, predicate: ["simpleFilterFromChild"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<div class=\"smart-filter-editor-container\" *ngIf=\"service.model\">\r\n @if (service.model.model?.label) {\r\n <h3 class=\"smart-filter-editor-title\">{{ service.model.model?.label }}</h3>\r\n }\r\n\r\n <div *ngIf=\"service.model.type === type.SIMPLE; then simpleFilter; else complexFilter\"></div>\r\n\r\n <ng-template #simpleFilter>\r\n <div class=\"smart-filter-editor-expressionsContainer\">\r\n <ng-template #simpleFilterFromChild></ng-template>\r\n <!-- #simpleFilterFromChild [smartForm]=\"simpleFilterForm\"></smartform> -->\r\n </div>\r\n </ng-template>\r\n <ng-template #complexFilter>\r\n <div class=\"smart-filter-editor-uiActionsBar\" *ngIf=\"!service.model?.readOnly\">\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"service\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n service.model?.model?.workplaceList?.filters?.length;\r\n then withParameters;\r\n else withoutParameters\r\n \"\r\n ></div>\r\n <ng-template #withoutParameters>\r\n <div class=\"smart-filter-editor-withoutParametersContainer\">\r\n <span class=\"mat-body\"> M\u00E9g nincs megadva param\u00E9ter </span>\r\n </div>\r\n </ng-template>\r\n <ng-template #withParameters>\r\n <div class=\"smart-filter-editor-withParametersContainer\">\r\n <div\r\n class=\"smart-filter-editor-expressionsContainer\"\r\n *ngIf=\"service.model?.model?.workplaceList?.filters?.length\"\r\n (click)=\"unSelectItems($event)\"\r\n >\r\n <app-expression-items\r\n [items]=\"(service.model?.model?.workplaceList)!.filters\"\r\n [service]=\"service\"\r\n ></app-expression-items>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n !useAdvancedFilter ||\r\n service.model?.selectedField?.expressionData?.currentOperation === 'EXPRESSION'\r\n \"\r\n class=\"smart-filter-editor-expressionEditorContainer\"\r\n >\r\n <!-- <smartform #form [smartForm]=\"smartForm\"></smartform> -->\r\n <ng-template #form></ng-template>\r\n <div *ngIf=\"smartForm\">\r\n <button (click)=\"remove()\" mat-raised-button color=\"primary\">T\u00F6rl\u00E9s</button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </ng-template>\r\n</div>\r\n", styles: [".smart-filter-editor-container{display:flex;flex-direction:column;overflow:auto;position:relative}.targetGroupContentContainer h3{margin:0;font-weight:600}.smart-filter-editor-uiActionsBar{padding:.5rem;border-radius:.25rem;background-color:var(--container-color);display:flex;flex-direction:row;gap:1rem}.smart-filter-editor-withoutParametersContainer{display:flex;flex-direction:column;padding:.5rem;background-color:var(--container-color)}.withoutParametersContainer .mat-body{font-weight:600;text-align:center}.smart-filter-editor-withParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.smart-filter-editor-expressionEditorContainer{display:flex;flex-direction:column;gap:.75rem}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: UiActionToolbarComponent, selector: "smart-ui-action-toolbar", inputs: ["uiActionModels", "uiActionDescriptorService", "id", "executor", "widgetId", "nodeId", "actionParams", "scrollOnWrap", "toolbarPropertes"], outputs: ["actionFired"] }, { kind: "component", type: SmartFilterExpressionItemsComponent, selector: "app-expression-items", inputs: ["items", "service"] }, { kind: "component", type: 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"] }] }); }
25424
25499
  }
25425
25500
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: SmartFilterEditorContentComponent, decorators: [{
25426
25501
  type: Component,
25427
- args: [{ selector: 'smart-filter-expression-editor', imports: [NgIf, UiActionToolbarComponent, SmartFilterExpressionItemsComponent, MatButton], template: "<div class=\"smart-filter-editor-container\" *ngIf=\"service.model\">\r\n @if (service.model.model?.label) {\r\n <h3 class=\"smart-filter-editor-title\">{{ service.model.model?.label }}</h3>\r\n }\r\n\r\n <div *ngIf=\"service.model.type === type.SIMPLE; then simpleFilter; else complexFilter\"></div>\r\n\r\n <ng-template #simpleFilter>\r\n <div class=\"smart-filter-editor-expressionsContainer\">\r\n <ng-template #simpleFilterFromChild></ng-template>\r\n <!-- #simpleFilterFromChild [smartForm]=\"simpleFilterForm\"></smartform> -->\r\n </div>\r\n </ng-template>\r\n <ng-template #complexFilter>\r\n <div class=\"smart-filter-editor-uiActionsBar\" *ngIf=\"!service.model?.readOnly\">\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"service\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n service.model?.model?.workplaceList?.filters?.length;\r\n then withParameters;\r\n else withoutParameters\r\n \"\r\n ></div>\r\n <ng-template #withoutParameters>\r\n <div class=\"smart-filter-editor-withoutParametersContainer\">\r\n <span class=\"mat-body\"> M\u00E9g nincs megadva param\u00E9ter </span>\r\n </div>\r\n </ng-template>\r\n <ng-template #withParameters>\r\n <div class=\"smart-filter-editor-withParametersContainer\">\r\n <div\r\n class=\"smart-filter-editor-expressionsContainer\"\r\n *ngIf=\"service.model?.model?.workplaceList?.filters?.length\"\r\n (click)=\"unSelectItems($event)\"\r\n >\r\n <app-expression-items\r\n [items]=\"(service.model?.model?.workplaceList)!.filters\"\r\n [service]=\"service\"\r\n ></app-expression-items>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n !useAdvancedFilter ||\r\n service.model?.selectedField?.expressionData?.currentOperation === 'EXPRESSION'\r\n \"\r\n class=\"smart-filter-editor-expressionEditorContainer\"\r\n >\r\n <!-- <smartform #form [smartForm]=\"smartForm\"></smartform> -->\r\n <ng-template #form></ng-template>\r\n <div *ngIf=\"smartForm\">\r\n <button (click)=\"remove()\" mat-raised-button color=\"primary\">T\u00F6rl\u00E9s</button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </ng-template>\r\n</div>\r\n", styles: [".smart-filter-editor-container{display:flex;flex-direction:column;overflow:auto;position:relative}.targetGroupContentContainer h3{margin:0;font-weight:600}.smart-filter-editor-uiActionsBar{padding:.5rem;border-radius:.25rem;background-color:var(--container-color);display:flex;flex-direction:row;gap:1rem}.smart-filter-editor-withoutParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.withoutParametersContainer .mat-body{font-weight:600;text-align:center}.smart-filter-editor-withParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.smart-filter-editor-expressionEditorContainer{display:flex;flex-direction:column;gap:.75rem}\n"] }]
25502
+ args: [{ selector: 'smart-filter-expression-editor', imports: [NgIf, UiActionToolbarComponent, SmartFilterExpressionItemsComponent, MatButton], template: "<div class=\"smart-filter-editor-container\" *ngIf=\"service.model\">\r\n @if (service.model.model?.label) {\r\n <h3 class=\"smart-filter-editor-title\">{{ service.model.model?.label }}</h3>\r\n }\r\n\r\n <div *ngIf=\"service.model.type === type.SIMPLE; then simpleFilter; else complexFilter\"></div>\r\n\r\n <ng-template #simpleFilter>\r\n <div class=\"smart-filter-editor-expressionsContainer\">\r\n <ng-template #simpleFilterFromChild></ng-template>\r\n <!-- #simpleFilterFromChild [smartForm]=\"simpleFilterForm\"></smartform> -->\r\n </div>\r\n </ng-template>\r\n <ng-template #complexFilter>\r\n <div class=\"smart-filter-editor-uiActionsBar\" *ngIf=\"!service.model?.readOnly\">\r\n <smart-ui-action-toolbar\r\n [uiActionModels]=\"uiActionModels\"\r\n [executor]=\"service\"\r\n ></smart-ui-action-toolbar>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n service.model?.model?.workplaceList?.filters?.length;\r\n then withParameters;\r\n else withoutParameters\r\n \"\r\n ></div>\r\n <ng-template #withoutParameters>\r\n <div class=\"smart-filter-editor-withoutParametersContainer\">\r\n <span class=\"mat-body\"> M\u00E9g nincs megadva param\u00E9ter </span>\r\n </div>\r\n </ng-template>\r\n <ng-template #withParameters>\r\n <div class=\"smart-filter-editor-withParametersContainer\">\r\n <div\r\n class=\"smart-filter-editor-expressionsContainer\"\r\n *ngIf=\"service.model?.model?.workplaceList?.filters?.length\"\r\n (click)=\"unSelectItems($event)\"\r\n >\r\n <app-expression-items\r\n [items]=\"(service.model?.model?.workplaceList)!.filters\"\r\n [service]=\"service\"\r\n ></app-expression-items>\r\n </div>\r\n <div\r\n *ngIf=\"\r\n !useAdvancedFilter ||\r\n service.model?.selectedField?.expressionData?.currentOperation === 'EXPRESSION'\r\n \"\r\n class=\"smart-filter-editor-expressionEditorContainer\"\r\n >\r\n <!-- <smartform #form [smartForm]=\"smartForm\"></smartform> -->\r\n <ng-template #form></ng-template>\r\n <div *ngIf=\"smartForm\">\r\n <button (click)=\"remove()\" mat-raised-button color=\"primary\">T\u00F6rl\u00E9s</button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </ng-template>\r\n</div>\r\n", styles: [".smart-filter-editor-container{display:flex;flex-direction:column;overflow:auto;position:relative}.targetGroupContentContainer h3{margin:0;font-weight:600}.smart-filter-editor-uiActionsBar{padding:.5rem;border-radius:.25rem;background-color:var(--container-color);display:flex;flex-direction:row;gap:1rem}.smart-filter-editor-withoutParametersContainer{display:flex;flex-direction:column;padding:.5rem;background-color:var(--container-color)}.withoutParametersContainer .mat-body{font-weight:600;text-align:center}.smart-filter-editor-withParametersContainer{display:flex;flex-direction:column;background-color:var(--container-color)}.smart-filter-editor-expressionEditorContainer{display:flex;flex-direction:column;gap:.75rem}\n"] }]
25428
25503
  }], ctorParameters: () => [], propDecorators: { service: [{
25429
25504
  type: Input
25430
25505
  }], vcRef: [{