wuic-framework-lib 1.2.1 → 1.3.2

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.
Files changed (16) hide show
  1. package/fesm2022/{wuic-framework-lib-chart-list.component-B1V8Q-Qq.mjs → wuic-framework-lib-chart-list.component-DAP-TPJR.mjs} +2 -2
  2. package/fesm2022/{wuic-framework-lib-code-editor.component-4N95qt3D.mjs → wuic-framework-lib-code-editor.component-CLMDvOWs.mjs} +59 -2
  3. package/fesm2022/{wuic-framework-lib-designer.component-pW1YK-oY.mjs → wuic-framework-lib-designer.component-DA7abiLW.mjs} +1063 -18
  4. package/fesm2022/{wuic-framework-lib-first-run-wizard.component-CaGePYLi.mjs → wuic-framework-lib-first-run-wizard.component-BLrUrfZ9.mjs} +83 -97
  5. package/fesm2022/{wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs → wuic-framework-lib-framework-docs.component-DphKhTef.mjs} +3733 -344
  6. package/fesm2022/{wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs → wuic-framework-lib-pivot-builder.component-BqUUwrJM.mjs} +5 -5
  7. package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-Dm9GTZ07.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-DZrhwMH5.mjs} +2 -2
  8. package/fesm2022/{wuic-framework-lib-report-designer.component-FiAwNkPM.mjs → wuic-framework-lib-report-designer.component-D_qbefud.mjs} +2 -2
  9. package/fesm2022/{wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs → wuic-framework-lib-report-viewer.component-CIN2fY0P.mjs} +2 -2
  10. package/fesm2022/{wuic-framework-lib-scheduler-list.component-3E5m-9Qd.mjs → wuic-framework-lib-scheduler-list.component-CdcI1Uor.mjs} +2 -2
  11. package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs → wuic-framework-lib-spreadsheet-list-sf.component-j1HTRI_P.mjs} +2 -2
  12. package/fesm2022/{wuic-framework-lib-workflow-designer.component-4RC2EkGA.mjs → wuic-framework-lib-workflow-designer.component-SE_7ZogW.mjs} +2 -2
  13. package/fesm2022/{wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs → wuic-framework-lib-wuic-framework-lib-BRujEwpR.mjs} +2252 -156
  14. package/fesm2022/wuic-framework-lib.mjs +1 -1
  15. package/package.json +1 -1
  16. package/types/wuic-framework-lib.d.ts +852 -13
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Pipe, EventEmitter, ElementRef, Renderer2, ChangeDetectorRef, HostListener, Input, Output, Inject, Directive, Component, computed, signal, untracked, inject, NgZone, numberAttribute, DOCUMENT, input, output, isDevMode, InjectionToken, Injector, contentChildren, contentChild, booleanAttribute, effect, HostBinding, ChangeDetectionStrategy } from '@angular/core';
3
- import * as i9 from 'primeng/splitter';
3
+ import * as i10 from 'primeng/splitter';
4
4
  import { SplitterModule } from 'primeng/splitter';
5
- import * as i11 from 'primeng/dragdrop';
5
+ import * as i12 from 'primeng/dragdrop';
6
6
  import { DragDropModule } from 'primeng/dragdrop';
7
7
  import { NgClass, NgStyle, NgTemplateOutlet, AsyncPipe, JsonPipe, NgFor, NgIf, NgComponentOutlet } from '@angular/common';
8
- import { W as WtoolboxService, D as DynamicGenericTemplateComponent, a as DynamicCompilerService, b as DataSourceComponent, c as DataRepeaterComponent, F as FilterBarComponent, L as LazyParametricDialogComponent, P as PagerComponent, M as MetadataProviderService, d as ParametricDialogComponent, e as MetadatiColonna, f as DataProviderService, U as UserInfoService, T as TranslationManagerService, g as MetadataEditorService, h as WorkflowRuntimeMetadataService, i as FieldEditorComponent, j as MetadataEditorComponent, A as ArchetypeConfiguratorComponent } from './wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs';
8
+ import { W as WtoolboxService, D as DynamicGenericTemplateComponent, a as DynamicCompilerService, b as DataSourceComponent, c as DataRepeaterComponent, F as FilterBarComponent, L as LazyParametricDialogComponent, P as PagerComponent, M as MetadataProviderService, d as ParametricDialogComponent, e as MetadatiColonna, f as DataProviderService, U as UserInfoService, T as TranslationManagerService, g as MetadataEditorService, C as ChatbotHostRegistryService, h as WorkflowRuntimeMetadataService, i as FieldEditorComponent, j as MetadataEditorComponent, A as ArchetypeConfiguratorComponent } from './wuic-framework-lib-wuic-framework-lib-BRujEwpR.mjs';
9
9
  import { fromEvent, BehaviorSubject, merge, filter, Observable, switchMap, take, map, takeUntil, tap, timeInterval, scan, mergeMap, of, delay, repeat, Subject, startWith, pairwise, skipWhile, combineLatest } from 'rxjs';
10
10
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
11
  import { CdkAccordionModule, CdkAccordion, CdkAccordionItem } from '@angular/cdk/accordion';
@@ -29,11 +29,11 @@ import { TreeDragDropService } from 'primeng/api';
29
29
  import { MenubarModule } from 'primeng/menubar';
30
30
  import * as i4 from 'primeng/button';
31
31
  import { ButtonModule } from 'primeng/button';
32
- import * as i17 from 'primeng/multiselect';
32
+ import * as i18 from 'primeng/multiselect';
33
33
  import { MultiSelectModule } from 'primeng/multiselect';
34
34
  import * as i2 from 'primeng/dialog';
35
35
  import { DialogModule } from 'primeng/dialog';
36
- import * as i9$1 from 'primeng/menu';
36
+ import * as i9 from 'primeng/menu';
37
37
  import { MenuModule } from 'primeng/menu';
38
38
  import * as i4$1 from 'primeng/tree';
39
39
  import { TreeModule } from 'primeng/tree';
@@ -4415,6 +4415,7 @@ class DesignerComponent {
4415
4415
  cd;
4416
4416
  trslSrv;
4417
4417
  metadataEditorSrv;
4418
+ chatbotHostRegistry;
4418
4419
  workflowRuntimeMetadata;
4419
4420
  draggedPayload;
4420
4421
  selected;
@@ -4516,7 +4517,7 @@ class DesignerComponent {
4516
4517
  * e binding ai converter/callback usati dall'editor proprieta.
4517
4518
  * Non carica dati remoti: il bootstrap runtime avviene in `ngOnInit`.
4518
4519
  */
4519
- constructor(route, dataSrv, metaSrv, userInfo, cd, trslSrv, metadataEditorSrv, workflowRuntimeMetadata = {}) {
4520
+ constructor(route, dataSrv, metaSrv, userInfo, cd, trslSrv, metadataEditorSrv, chatbotHostRegistry, workflowRuntimeMetadata = {}) {
4520
4521
  this.route = route;
4521
4522
  this.dataSrv = dataSrv;
4522
4523
  this.metaSrv = metaSrv;
@@ -4524,6 +4525,7 @@ class DesignerComponent {
4524
4525
  this.cd = cd;
4525
4526
  this.trslSrv = trslSrv;
4526
4527
  this.metadataEditorSrv = metadataEditorSrv;
4528
+ this.chatbotHostRegistry = chatbotHostRegistry;
4527
4529
  this.workflowRuntimeMetadata = workflowRuntimeMetadata;
4528
4530
  this.defaultCtxItems = [
4529
4531
  {
@@ -5551,6 +5553,21 @@ class DesignerComponent {
5551
5553
  async ngOnInit() {
5552
5554
  this.lastKnownHashRoute = String(window?.location?.hash || '');
5553
5555
  this.refreshAvailableProjectCssClasses();
5556
+ // Registra handler chatbot per kind 'designer_inject': il chatbot
5557
+ // popover (globale, vedi rag-chatbot.component.ts onApplyAction)
5558
+ // intercetta il kind e delega a questo handler invece di chiamare
5559
+ // /api/Rag/ApplyAction. unregister in ngOnDestroy per evitare retain.
5560
+ this.chatbotHostRegistry.register('designer_inject', (action) => this.onChatbotProposedDesignerAction(action));
5561
+ // Registra state provider: il chatbot (in buildRouteContext) chiama
5562
+ // questo provider per ottenere uno snapshot compatto del canvas e
5563
+ // iniettarlo nel system prompt del LLM. Cosi' il LLM puo' proporre
5564
+ // set_property / remove referenziando uniqueName reali (TD__126 ecc.).
5565
+ this.chatbotHostRegistry.registerStateProvider('designer_inject', () => this.buildCompactDashboardStateDump());
5566
+ // NB: il fetch della lista route disponibili NON va fatto qui (al mount
5567
+ // del designer). Sarebbe una chiamata HTTP per ogni apertura del designer
5568
+ // anche quando l'utente non usa mai il chatbot. Lo facciamo LAZY dentro
5569
+ // `buildCompactDashboardStateDump` al primo prompt del chatbot
5570
+ // (state provider invocato solo quando il chatbot prepara il route_context).
5554
5571
  let menus = [];
5555
5572
  menus = [
5556
5573
  {
@@ -5622,6 +5639,983 @@ class DesignerComponent {
5622
5639
  this.clearHierarchyHoverHighlight();
5623
5640
  this.clearFooterHoverHighlight();
5624
5641
  this.setDesignerEditModeClass(false);
5642
+ // Deregistra handler chatbot: il prossimo mount (es. navigation
5643
+ // back-and-forth a /designer) registrera' una nuova istanza.
5644
+ try {
5645
+ this.chatbotHostRegistry.unregister('designer_inject');
5646
+ }
5647
+ catch { /* ignore */ }
5648
+ try {
5649
+ this.chatbotHostRegistry.unregisterStateProvider('designer_inject');
5650
+ }
5651
+ catch { /* ignore */ }
5652
+ }
5653
+ // ===========================================================================
5654
+ // CHATBOT HOST HANDLER: kind 'designer_inject' (client-only)
5655
+ // ===========================================================================
5656
+ //
5657
+ // Riceve un payload `propose_designer_inject` dal LLM, lo deserializza e
5658
+ // simula drag&drop iterativo sui `dashboardElements` usando le API
5659
+ // canoniche `dropToolAtRoot()` / `drop()` esistenti.
5660
+ //
5661
+ // Per i nodi nested (`children[]`): dopo aver iniettato il parent, leggiamo
5662
+ // l'`uniqueName` generato dal sistema (TOOL__<componentId>) e lo passiamo
5663
+ // come parent_unique_name ai figli. Il `drop()` risolve il parent via
5664
+ // `flattenedDashboardElements.find(componentId)`.
5665
+ //
5666
+ // Per TABLE/SPLITTER/ACCORDION (custom onDrop): il framework crea
5667
+ // automaticamente i nodi figli TR/TD / SPLITTER-AREA / ACCORDION-AREA
5668
+ // basandosi su inputs.rows/cols/areas/items. Il LLM NON deve includerli
5669
+ // nel children[] (vedi docs/pages/_internal/designer-tool-catalog.md
5670
+ // constraint #2).
5671
+ //
5672
+ // Per TABVIEW: il LLM deve invece includere espliciti TABPANEL nel
5673
+ // children[] (allowedChildren) - non sono auto-create.
5674
+ //
5675
+ // commitHistoryIfChanged() viene gia' invocato da drop() -> undo/redo
5676
+ // gratis. Nessun side-effect su backend metadata (kind client-only).
5677
+ async onChatbotProposedDesignerAction(action) {
5678
+ if (this.noEdit) {
5679
+ return { ok: false, message: 'Designer in modalita read-only' };
5680
+ }
5681
+ // Discrimina su action_type. Backward-compat: se assente, assume 'inject'
5682
+ // (lo schema precedente non aveva action_type).
5683
+ const actionType = String(action?.['action_type'] || 'inject').toLowerCase();
5684
+ try {
5685
+ if (actionType === 'inject') {
5686
+ const layout = Array.isArray(action?.['layout']) ? action['layout'] : [];
5687
+ if (!layout.length)
5688
+ return { ok: false, message: 'Layout vuoto: nessun tool proposto dal LLM' };
5689
+ let totalInjected = 0;
5690
+ // Top-level: il LLM puo' specificare `parent_unique_name` per innestare
5691
+ // dentro un componente gia' presente nel canvas. Tre casi:
5692
+ // 1) uniqueName reale gia' presente (es. "TD__126") => pass-through;
5693
+ // 2) placeholder semantico (es. "<TD-0>") riferito al nodo top-level
5694
+ // iniettato POCO PRIMA in questo stesso layout: resolve dentro
5695
+ // l'ultimo top-level injected (necessario quando il LLM
5696
+ // preferisce mettere TABLE + LABEL come 2 top-level sibling,
5697
+ // con LABEL.parent_unique_name="<TD-0>" puntando alla TABLE
5698
+ // appena create);
5699
+ // 3) null/missing: va al ROOT (dashboardElements).
5700
+ let lastTopLevelInjected = null;
5701
+ for (const node of layout) {
5702
+ let topParent = null;
5703
+ const declared = (node && typeof node.parent_unique_name === 'string') ? node.parent_unique_name.trim() : '';
5704
+ if (declared) {
5705
+ if (declared.startsWith('<') && declared.endsWith('>')) {
5706
+ if (lastTopLevelInjected) {
5707
+ const resolved = this.resolveLayoutPlaceholder(declared, lastTopLevelInjected);
5708
+ if (resolved)
5709
+ topParent = resolved;
5710
+ else
5711
+ throw new Error(`Placeholder '${declared}' non risolvibile dentro ${lastTopLevelInjected?.uniqueName} (verifica struttura: rows/cols/areas/items match)`);
5712
+ }
5713
+ else {
5714
+ throw new Error(`Placeholder '${declared}' per nodo top-level senza nodo precedente da cui risolvere (deve esserci un TABLE/SPLITTER/ACCORDION/TABVIEW iniettato prima)`);
5715
+ }
5716
+ }
5717
+ else {
5718
+ topParent = declared; // uniqueName reale, pass-through al drop()
5719
+ }
5720
+ }
5721
+ totalInjected += await this.injectLayoutNode(node, topParent);
5722
+ // Aggiorna il last top-level per i prossimi placeholder. NB: usiamo
5723
+ // findLastInjectedNode per pescare l'istanza vera (con onDrop appena
5724
+ // eseguito = TR/TD/AREA gia' popolati), non il template clonato.
5725
+ lastTopLevelInjected = this.findLastInjectedNode(String(node.tool_name || ''), topParent) || lastTopLevelInjected;
5726
+ }
5727
+ const rationale = (action?.['rationale'] || '').toString().slice(0, 100);
5728
+ return { ok: true, message: `Iniettati ${totalInjected} elementi nel designer${rationale ? ': ' + rationale : ''}.` };
5729
+ }
5730
+ if (actionType === 'set_property') {
5731
+ return this.applySetPropertyAction(action);
5732
+ }
5733
+ if (actionType === 'remove') {
5734
+ return this.applyRemoveAction(action);
5735
+ }
5736
+ return { ok: false, message: `action_type sconosciuto: '${actionType}' (atteso 'inject', 'set_property' o 'remove')` };
5737
+ }
5738
+ catch (err) {
5739
+ return { ok: false, message: err?.message || String(err) };
5740
+ }
5741
+ }
5742
+ /**
5743
+ * Applica `set_property`: modifica UN valore di proprieta' su un componente
5744
+ * gia' presente nel canvas. Lookup via uniqueName in flattenedDashboardElements,
5745
+ * assign del valore (BehaviorSubject .next() per async, plain assign per sync),
5746
+ * commitHistoryIfChanged() per undo/redo.
5747
+ *
5748
+ * NB: per coerenza con il property panel runtime, simuliamo il flow di
5749
+ * setValue() canonico (riga 4306) quando possibile: usiamo la toolProp
5750
+ * dell'inputProps target per propagare converter/conditional, in modo che
5751
+ * effetti collaterali (es. borderColor che setta border-{top,right,bottom,left}-color)
5752
+ * siano applicati correttamente.
5753
+ */
5754
+ applySetPropertyAction(action) {
5755
+ const targetName = String(action?.['target_unique_name'] || '').trim();
5756
+ const propName = String(action?.['prop_name'] || '').trim();
5757
+ const newValue = action?.['value'];
5758
+ if (!targetName)
5759
+ return { ok: false, message: "set_property: 'target_unique_name' mancante" };
5760
+ if (!propName)
5761
+ return { ok: false, message: "set_property: 'prop_name' mancante" };
5762
+ const target = (this.flattenedDashboardElements || []).find((n) => n?.uniqueName === targetName);
5763
+ if (!target)
5764
+ return { ok: false, message: `set_property: componente '${targetName}' non trovato nel canvas. Verifica DESIGNER STATE.` };
5765
+ // La prop deve essere esposta in `inputProps` (schema delle proprieta'
5766
+ // editabili del tool, dichiarate al boot). NON controllare `inputs`:
5767
+ // gli inputs partono spesso vuoti/parziali e si popolano solo on-edit
5768
+ // (es. backgroundColor non e' in inputs di default ma e' in
5769
+ // htmlInputProperties -> inputProps). Coerente col property panel
5770
+ // che legge inputProps per costruire l'UI.
5771
+ if (!target.inputProps || !Object.prototype.hasOwnProperty.call(target.inputProps, propName)) {
5772
+ return { ok: false, message: `set_property: proprieta' '${propName}' non esposta su ${target.name} (${targetName})` };
5773
+ }
5774
+ // Inizializza inputs[propName] se non esiste, cosi' setValue() canonico
5775
+ // (o l'assign fallback) lo trova.
5776
+ if (target.inputs && !Object.prototype.hasOwnProperty.call(target.inputs, propName)) {
5777
+ target.inputs[propName] = undefined;
5778
+ }
5779
+ // Trova la toolProp matching (chiave 'key' === propName) per delegare a setValue
5780
+ // canonico - applica converter, conditional, hooks specifici (datasource/select).
5781
+ const toolProp = Array.isArray(target.toolProps)
5782
+ ? target.toolProps.find((tp) => tp?.key === propName)
5783
+ : null;
5784
+ try {
5785
+ if (toolProp) {
5786
+ // Path canonico: setValue() applica converter (es. borderColor -> 4
5787
+ // border-*-color), conditional, e gestisce BehaviorSubject.
5788
+ this.setValue(newValue, target, toolProp);
5789
+ }
5790
+ else {
5791
+ // Fallback: la prop non e' nelle toolProps (raro - inputProps inconsistenti).
5792
+ // Assign diretto su inputs[prop], distinguendo BehaviorSubject vs primitivo.
5793
+ const cur = target.inputs[propName];
5794
+ if (cur && typeof cur.next === 'function' && typeof cur.subscribe === 'function') {
5795
+ cur.next(newValue);
5796
+ }
5797
+ else {
5798
+ target.inputs[propName] = newValue;
5799
+ }
5800
+ this.commitHistoryIfChanged();
5801
+ }
5802
+ const rationale = (action?.['rationale'] || '').toString().slice(0, 100);
5803
+ return { ok: true, message: `Aggiornato ${target.name}.${propName} = ${this.formatPropValueForToast(newValue)}${rationale ? ': ' + rationale : ''}` };
5804
+ }
5805
+ catch (err) {
5806
+ return { ok: false, message: `set_property fallito: ${err?.message || String(err)}` };
5807
+ }
5808
+ }
5809
+ /**
5810
+ * Applica `remove`: rimuove un nodo dal tree dashboardElements (e tutti i
5811
+ * suoi nestedComponents per via della rimozione dal parent).
5812
+ *
5813
+ * Strategia: walk ricorsivo dei dashboardElements + ogni nestedComponents[]
5814
+ * cercando l'array che contiene un elemento con uniqueName==target. splice.
5815
+ * Poi rebuilda flatten + commitHistoryIfChanged.
5816
+ */
5817
+ applyRemoveAction(action) {
5818
+ const targetName = String(action?.['target_unique_name'] || '').trim();
5819
+ if (!targetName)
5820
+ return { ok: false, message: "remove: 'target_unique_name' mancante" };
5821
+ const removed = this.removeNodeByUniqueName(this.dashboardElements, targetName);
5822
+ if (!removed) {
5823
+ return { ok: false, message: `remove: componente '${targetName}' non trovato nel canvas` };
5824
+ }
5825
+ try {
5826
+ this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
5827
+ }
5828
+ catch { /* best-effort */ }
5829
+ try {
5830
+ this.commitHistoryIfChanged();
5831
+ }
5832
+ catch { /* best-effort */ }
5833
+ const rationale = (action?.['rationale'] || '').toString().slice(0, 100);
5834
+ return { ok: true, message: `Rimosso ${targetName}${rationale ? ': ' + rationale : ''}` };
5835
+ }
5836
+ /** Walk ricorsivo: rimuove la prima occorrenza del nodo con uniqueName==target. */
5837
+ removeNodeByUniqueName(scope, targetName) {
5838
+ if (!Array.isArray(scope))
5839
+ return false;
5840
+ for (let i = 0; i < scope.length; i++) {
5841
+ const node = scope[i];
5842
+ if (node?.uniqueName === targetName) {
5843
+ scope.splice(i, 1);
5844
+ return true;
5845
+ }
5846
+ if (Array.isArray(node?.nestedComponents)) {
5847
+ if (this.removeNodeByUniqueName(node.nestedComponents, targetName))
5848
+ return true;
5849
+ }
5850
+ }
5851
+ return false;
5852
+ }
5853
+ /**
5854
+ * Best-effort format del value per il toast (max 40 char).
5855
+ * Solo per UX feedback - non per persistenza.
5856
+ */
5857
+ formatPropValueForToast(v) {
5858
+ try {
5859
+ if (v === null || v === undefined)
5860
+ return String(v);
5861
+ if (typeof v === 'string')
5862
+ return v.length > 40 ? v.slice(0, 37) + '...' : v;
5863
+ if (typeof v === 'number' || typeof v === 'boolean')
5864
+ return String(v);
5865
+ const json = JSON.stringify(v);
5866
+ return json.length > 40 ? json.slice(0, 37) + '...' : json;
5867
+ }
5868
+ catch {
5869
+ return '?';
5870
+ }
5871
+ }
5872
+ /**
5873
+ * Dump compatto dello state del designer per iniezione nel system prompt
5874
+ * del LLM (via ChatbotHostRegistryService state provider, vedi
5875
+ * rag-chatbot.component.ts buildHostStateContext()).
5876
+ *
5877
+ * Formato tree indentato con uniqueName + nome + inputs significativi
5878
+ * (skippa CSS standard ereditate da htmlInputProperties per non gonfiare
5879
+ * il context). Max ~50 nodi (canvas tipico) per stare entro un budget di
5880
+ * ~2-3K token.
5881
+ *
5882
+ * Esempio output:
5883
+ * TABLE__123 (rows=2, cols=2)
5884
+ * TR__124
5885
+ * TD__125
5886
+ * TD__126
5887
+ * TR__127
5888
+ * TD__128
5889
+ * TD__129
5890
+ * DATASOURCE__130 (route="cities", autoload=true)
5891
+ * DATAREPEATER__131 (action="list", datasource=DATASOURCE__130)
5892
+ *
5893
+ * Ritorna null se il canvas e' vuoto.
5894
+ */
5895
+ buildCompactDashboardStateDump() {
5896
+ // Sezione 1: stato canvas designer (tree compatto dei dashboardElements).
5897
+ const root = this.dashboardElements || [];
5898
+ const canvasLines = [];
5899
+ const MAX_NODES = 60;
5900
+ let count = 0;
5901
+ const walk = (nodes, depth) => {
5902
+ if (!Array.isArray(nodes))
5903
+ return;
5904
+ for (const n of nodes) {
5905
+ if (count >= MAX_NODES)
5906
+ return;
5907
+ count++;
5908
+ const indent = ' '.repeat(depth);
5909
+ const uname = String(n?.uniqueName || '?');
5910
+ const inputsSummary = this.summarizeInputsForDump(n);
5911
+ canvasLines.push(`${indent}${uname}${inputsSummary ? ' (' + inputsSummary + ')' : ''}`);
5912
+ if (Array.isArray(n?.nestedComponents) && n.nestedComponents.length > 0) {
5913
+ walk(n.nestedComponents, depth + 1);
5914
+ }
5915
+ }
5916
+ };
5917
+ walk(root, 0);
5918
+ if (count >= MAX_NODES) {
5919
+ canvasLines.push(`... (canvas con ${this.flattenedDashboardElements?.length || '?'} nodi totali, mostrati i primi ${MAX_NODES})`);
5920
+ }
5921
+ // Sezione 2: whitelist route metadata disponibili.
5922
+ //
5923
+ // Lazy-fetch: la prima volta che il chatbot invoca lo state provider
5924
+ // (= primo prompt utente al chatbot dopo apertura designer), triggera
5925
+ // il fetch fire-and-forget. La cache viene popolata in background; al
5926
+ // SECONDO prompt sara' disponibile. Al PRIMO prompt il LLM vede
5927
+ // 'caricamento in corso' e risponde "riprova tra un secondo".
5928
+ //
5929
+ // Questo approccio evita la chiamata HTTP al mount del designer
5930
+ // (sprecata se l'utente non usa mai il chatbot) e isola eventuali
5931
+ // errori (es. 404, utente non admin) dall'apertura del designer.
5932
+ const metaSrvCtor = this.metaSrv?.constructor;
5933
+ const routesCache = metaSrvCtor?._availableRoutesCache;
5934
+ const fetchInflight = metaSrvCtor?._availableRoutesInflight;
5935
+ if (routesCache === null && !fetchInflight) {
5936
+ // Trigger fire-and-forget: non aspettiamo, la promise popolera' la
5937
+ // cache; questo dump corrente restituira' 'caricamento' al LLM.
5938
+ try {
5939
+ void this.metaSrv.getAvailableRoutesAsync();
5940
+ }
5941
+ catch { /* ignore */ }
5942
+ }
5943
+ let routesSection;
5944
+ if (Array.isArray(routesCache) && routesCache.length > 0) {
5945
+ // Compatta: <route_name> (<display_string>) per ogni route. Limit
5946
+ // 300 entries per evitare context overflow su install con molte route.
5947
+ const MAX_ROUTES = 300;
5948
+ const slice = routesCache.slice(0, MAX_ROUTES);
5949
+ const routeLines = slice
5950
+ .map(r => ` - ${r.md_route_name}${r.md_display_string ? ' ("' + r.md_display_string + '")' : ''}`)
5951
+ .join('\n');
5952
+ const trunc = routesCache.length > MAX_ROUTES
5953
+ ? `\n ... (${routesCache.length - MAX_ROUTES} altre route non mostrate)` : '';
5954
+ routesSection = `\n\nROUTE METADATA DISPONIBILI (per DATASOURCE.inputs.route):\n${routeLines}${trunc}\n`
5955
+ + `REGOLA: in DATASOURCE.inputs.route usa ESCLUSIVAMENTE uno dei valori `
5956
+ + `md_route_name sopra (la prima colonna, non il display string). Se `
5957
+ + `l'utente menziona un termine non esatto (es. "provincie" invece di `
5958
+ + `"provinces", "fornitori" invece di "suppliers"), scegli il FUZZY `
5959
+ + `MATCH semantico piu' probabile dalla whitelist. Se NESSUN match e' `
5960
+ + `ragionevole (la route non esiste proprio), NON inventare: rispondi `
5961
+ + `con un messaggio testuale (senza tool_use) che chiede all'utente di `
5962
+ + `scegliere tra 2-3 candidati piu' vicini dalla whitelist.`;
5963
+ }
5964
+ else if (Array.isArray(routesCache) && routesCache.length === 0) {
5965
+ // Cache popolata ma vuota (utente non admin, o nessuna route non-system).
5966
+ routesSection = `\n\nROUTE METADATA DISPONIBILI: nessuna (utente senza `
5967
+ + `accesso o nessuna route business). NON proporre DATASOURCE con `
5968
+ + `inputs.route valorizzato finche' l'utente non specifica esplicitamente.`;
5969
+ }
5970
+ else {
5971
+ // Cache ancora in caricamento (race con primo prompt subito dopo mount).
5972
+ routesSection = `\n\nROUTE METADATA DISPONIBILI: <caricamento in corso>. `
5973
+ + `Se l'utente menziona una route specifica, NON proporre il DATASOURCE `
5974
+ + `con quella route alla cieca; rispondi che la whitelist sta caricando `
5975
+ + `e chiedi di riprovare tra un secondo.`;
5976
+ }
5977
+ if (canvasLines.length === 0 && !routesCache)
5978
+ return null;
5979
+ const canvasSection = canvasLines.length > 0
5980
+ ? canvasLines.join('\n')
5981
+ : '(canvas vuoto)';
5982
+ return canvasSection + routesSection;
5983
+ }
5984
+ /**
5985
+ * Compatta i `inputs` di un nodo per il dump. Skip:
5986
+ * - proprieta' CSS standard (htmlInputProperties: backgroundColor,
5987
+ * fontSize, ecc.) per evitare di gonfiare il context con
5988
+ * proprieta' che il LLM puo' inferire dal tool_name + catalog.
5989
+ * - BehaviorSubject/oggetti complessi: solo serializzazione minima
5990
+ * (uniqueName se e' un dropped-component).
5991
+ * - undefined/null/stringhe vuote.
5992
+ *
5993
+ * Mostra solo le proprieta' "semantiche": rows/cols/areas/items per
5994
+ * container, route/action per datasource, caption/value/innerText per
5995
+ * leaf, header per tab/accordion area.
5996
+ */
5997
+ summarizeInputsForDump(node) {
5998
+ if (!node?.inputs)
5999
+ return '';
6000
+ // Strategia: confronta ogni inputs[key] del nodo con il DEFAULT del template
6001
+ // (availableTools.find(t.name === node.name).inputs[key]). Se uguale, skip.
6002
+ // Cosi' il dump espone SOLO le proprieta' esplicitamente modificate
6003
+ // dall'utente (manualmente nel property panel o via set_property RAG),
6004
+ // dando al LLM un focus chirurgico sui "tratti distintivi" del componente
6005
+ // per riferimenti deittici ("riquadro blu", "kpi grande", "testo rosso").
6006
+ //
6007
+ // Skip aggiuntivi:
6008
+ // - chiavi tecniche runtime (componentId/uniqueName/toolProps/onDrop/ctxItems...)
6009
+ // - BehaviorSubject del datasource: estrai solo uniqueName interno
6010
+ // - oggetti complessi non-datasource (callback bound, props bag): json
6011
+ // troncato a 40 char per non gonfiare
6012
+ // - inputs[propName] = undefined a default (es. width/height resettati
6013
+ // da dropTable per TR/TD)
6014
+ const tplDefaults = this.getTemplateDefaultsFor(node?.name);
6015
+ const technicalKeys = new Set([
6016
+ 'componentId', 'uniqueName', 'toolProps', 'onDrop', 'ctxItems',
6017
+ 'componentRef', 'parentRecord', 'parentMetaInfo', 'metaEditor',
6018
+ ]);
6019
+ const parts = [];
6020
+ for (const key of Object.keys(node.inputs)) {
6021
+ if (technicalKeys.has(key))
6022
+ continue;
6023
+ const v = node.inputs[key];
6024
+ const def = tplDefaults ? tplDefaults[key] : undefined;
6025
+ if (v === null || v === undefined || v === '') {
6026
+ if (def === null || def === undefined || def === '')
6027
+ continue;
6028
+ // L'utente ha esplicitamente cancellato un valore non-default
6029
+ parts.push(`${key}=null`);
6030
+ continue;
6031
+ }
6032
+ // BehaviorSubject: per datasource estrai uniqueName dell'oggetto wrappato
6033
+ if (v && typeof v.subscribe === 'function') {
6034
+ const inner = v.value;
6035
+ const wrappedName = inner?.uniqueName || inner?.component?.uniqueName;
6036
+ if (wrappedName)
6037
+ parts.push(`${key}=${wrappedName}`);
6038
+ continue;
6039
+ }
6040
+ // Oggetto datasource (dropped-component-list, sync)
6041
+ if (v && typeof v === 'object' && v.uniqueName) {
6042
+ parts.push(`${key}=${v.uniqueName}`);
6043
+ continue;
6044
+ }
6045
+ // Primitivi: includi solo se DIVERSI dal default del template
6046
+ if (typeof v === 'boolean' || typeof v === 'number') {
6047
+ if (v === def)
6048
+ continue;
6049
+ parts.push(`${key}=${v}`);
6050
+ continue;
6051
+ }
6052
+ if (typeof v === 'string') {
6053
+ if (v === def)
6054
+ continue;
6055
+ const trimmed = v.length > 40 ? v.slice(0, 37) + '...' : v;
6056
+ parts.push(`${key}="${trimmed}"`);
6057
+ continue;
6058
+ }
6059
+ // Funzioni: skip silenzioso (sono callback bound, non utili al LLM)
6060
+ if (typeof v === 'function')
6061
+ continue;
6062
+ // Altri oggetti: serializza compatto (es. {a:1,b:2}) e diff vs default
6063
+ try {
6064
+ const json = JSON.stringify(v);
6065
+ const defJson = def !== undefined ? JSON.stringify(def) : undefined;
6066
+ if (json === defJson)
6067
+ continue;
6068
+ const trimmed = json.length > 40 ? json.slice(0, 37) + '...' : json;
6069
+ parts.push(`${key}=${trimmed}`);
6070
+ }
6071
+ catch { /* ignore */ }
6072
+ }
6073
+ return parts.join(', ');
6074
+ }
6075
+ /**
6076
+ * Best-effort lookup dei defaults dell'inputs di un tool dal `availableTools`
6077
+ * catalog. Usato dal dump per fare diff vs defaults. Ritorna `undefined` se
6078
+ * tool name non trovato (compaiono solo i tool custom registrati via
6079
+ * MetadataProviderService.customDesignerTools).
6080
+ */
6081
+ getTemplateDefaultsFor(toolName) {
6082
+ if (!toolName)
6083
+ return undefined;
6084
+ const tpl = (this.availableTools || []).find(t => t?.name === toolName);
6085
+ return tpl?.inputs;
6086
+ }
6087
+ /**
6088
+ * Inietta UN nodo del layout proposto. Ricorsivo per children[].
6089
+ *
6090
+ * Pattern: clona template da availableTools, override inputs, simula drop
6091
+ * (programmatico via this.drop({toElement: fakeEl con id=parentUniqueName})).
6092
+ *
6093
+ * Ritorna il numero di elementi creati (1 + somma dei children).
6094
+ */
6095
+ /**
6096
+ * Inietta la config di archetipo (`md_props_bag.archetypes.<action>`) nel
6097
+ * `metaInfo.tableMetadata` del DATASOURCE bindato a un DATAREPEATER appena
6098
+ * droppato. NESSUNA chiamata server: muta solo lo stato in-memory del
6099
+ * datasource (md_props_bag + extraProps.archetypes) e ri-emette `fetchInfo$`
6100
+ * cosi' il DATAREPEATER reagisce e renderizza l'archetipo configurato. La
6101
+ * config viene poi serializzata insieme al datasource quando l'utente salva
6102
+ * la dashboard (boardcontent), coerente con AGENTS regola 7 (md_props_bag e'
6103
+ * la fonte di verita', extraProps e' la proiezione runtime).
6104
+ * @param dsBinding il binding gia' idratato (`{uniqueName, component: BehaviorSubject<DataSourceComponent>}`)
6105
+ * @param action l'archetipo (chart/map/scheduler/kanban/tree/carousel/spreadsheet/...)
6106
+ * @param config oggetto config = contenuto di archetypes.<action>
6107
+ */
6108
+ /**
6109
+ * Applica `archetype_config` (dal nodo designer_inject) a un DATAREPEATER tool
6110
+ * APPENA droppato, posticipando finche' il DataSourceComponent bindato e'
6111
+ * materializzato (`fetchData` disponibile). Poi delega ad
6112
+ * {@link applyArchetypeConfigToTool} — il path proven del configuratore.
6113
+ */
6114
+ deferApplyArchetypeConfig(tool, action, config) {
6115
+ if (!tool || !action || !config || typeof config !== 'object')
6116
+ return;
6117
+ let tries = 0;
6118
+ const attempt = () => {
6119
+ const ds = tool.inputs?.['datasource']?.component?.value;
6120
+ if (ds && typeof ds.fetchData === 'function') {
6121
+ try {
6122
+ this.applyArchetypeConfigToTool(tool, action, config);
6123
+ }
6124
+ catch (e) {
6125
+ try {
6126
+ console.warn('[deferApplyArchetypeConfig] apply failed', e);
6127
+ }
6128
+ catch { /* */ }
6129
+ }
6130
+ return;
6131
+ }
6132
+ if (tries++ < 60)
6133
+ setTimeout(attempt, 200); // ~12s max per la materializzazione del ds
6134
+ };
6135
+ attempt();
6136
+ }
6137
+ async injectLayoutNode(node, parentUniqueName) {
6138
+ const toolName = String(node?.tool_name || '').trim();
6139
+ if (!toolName)
6140
+ throw new Error('Nodo senza tool_name');
6141
+ const template = this.availableTools.find(t => t.name === toolName);
6142
+ if (!template) {
6143
+ throw new Error(`Tool '${toolName}' non trovato in availableTools (controlla docs/pages/_internal/designer-tool-catalog.md)`);
6144
+ }
6145
+ if (template.hide) {
6146
+ throw new Error(`Tool '${toolName}' ha hide=true: non puo' essere proposto come top-level. Sara' creato automaticamente dal parent (es. TABLE -> TR/TD).`);
6147
+ }
6148
+ // CRITICAL: il drop() canonico (riga 5847+) recovera le function references
6149
+ // (onDrop, ctxItems, inputProps[*].conditional/converter/callback) leggendole
6150
+ // da `this.draggedPayload`. Se passiamo un clone via JSON.parse(JSON.stringify)
6151
+ // tutte le function vengono perse -> dropTable/dropSplitter/dropAccordion NON
6152
+ // vengono mai chiamati -> niente TR/TD auto-generati per TABLE/SPLITTER/ACCORDION.
6153
+ //
6154
+ // Pattern corretto: usare il template ORIGINALE (con function vive) come
6155
+ // draggedPayload. Override temporaneo degli inputs + restore in finally,
6156
+ // cosi' inject ripetuti non inquinano i defaults della palette.
6157
+ const inputsBackup = template.inputs ? { ...template.inputs } : {};
6158
+ // Override degli inputs SCALARI (string/number/boolean/array) PRIMA
6159
+ // del drop(). I binding di tipo `dropped-component-list` /
6160
+ // `dropped-component` vanno invece applicati POST-drop sull'istanza
6161
+ // appena iniettata, perche':
6162
+ // 1. il drop() canonico fa `JSON.parse(JSON.stringify(template))`
6163
+ // sul currentTool -> qualsiasi BehaviorSubject infilato in
6164
+ // template.inputs verrebbe serializzato come `{}` (perde valore
6165
+ // e prototype) -> hydration distrutta;
6166
+ // 2. pre-drop l'oggetto target potrebbe non esistere ancora
6167
+ // (es. caso edge in cui il LLM riferisce un componente che
6168
+ // verra' creato in un nodo successivo del layout - non
6169
+ // supportato, ma diagnosticabile post-drop con messaggio chiaro).
6170
+ //
6171
+ // Estraiamo qui le keys "drop-binding" (per applicarle dopo) e teniamo
6172
+ // SOLO gli scalari per il pre-drop Object.assign.
6173
+ const droppedBindingKeys = [];
6174
+ if (node.inputs && typeof node.inputs === 'object' && template.inputProps) {
6175
+ for (const key of Object.keys(node.inputs)) {
6176
+ const propDef = template.inputProps[key];
6177
+ if (propDef && (propDef.type === 'dropped-component-list' || propDef.type === 'dropped-component')) {
6178
+ droppedBindingKeys.push(key);
6179
+ }
6180
+ }
6181
+ }
6182
+ if (node.inputs && typeof node.inputs === 'object' && template.inputs) {
6183
+ for (const key of Object.keys(node.inputs)) {
6184
+ if (droppedBindingKeys.includes(key))
6185
+ continue;
6186
+ template.inputs[key] = node.inputs[key];
6187
+ }
6188
+ }
6189
+ // DEFAULTS auto: se il tool e' un CONTAINER block-level (TABLE/DIV/
6190
+ // SPLITTER/ACCORDION/TABVIEW/TABPANEL) e l'utente non ha specificato
6191
+ // width/height in node.inputs, settali a '100%' cosi' riempie lo slot
6192
+ // ospitante (TD/SPLITTER-AREA/canvas). Coerente con l'aspettativa
6193
+ // utente: "crea TABLE 2x2" -> occupa il canvas, non un quadratino.
6194
+ //
6195
+ // ESCLUSI (inline-level / fit-content tools): LABEL/SPAN/Hx/ANCHOR/
6196
+ // INPUT/BUTTON/CHECKBOX/DATE/SELECT/MULTISELECT/IMG/IFRAME/UL/KPI/
6197
+ // SEPARATOR/HR/TEXTAREA. Forzando width:100% su questi tool si
6198
+ // rompe il layout del parent (es. LABEL width:100% dentro un TD
6199
+ // costringe il browser a espandere il TD perche' table-layout:auto
6200
+ // non sa la dimensione finale -> squilibrio colonne).
6201
+ //
6202
+ // Sono OUT anche per def:
6203
+ // - tool senza width/height in inputProps (DATASOURCE, DATAREPEATER,
6204
+ // FILTERBAR, PAGER, TR, TD, SPLITTER-AREA, ACCORDION-AREA).
6205
+ // - tool dove l'utente ha specificato esplicitamente width/height.
6206
+ const BLOCK_CONTAINER_TOOLS = new Set([
6207
+ 'TABLE', 'DIV', 'SPLITTER', 'ACCORDION', 'TABVIEW', 'TABPANEL'
6208
+ ]);
6209
+ const explicitInputs = node.inputs || {};
6210
+ if (template.inputs && template.inputProps && BLOCK_CONTAINER_TOOLS.has(toolName)) {
6211
+ if ('width' in template.inputProps && !('width' in explicitInputs)) {
6212
+ template.inputs['width'] = '100%';
6213
+ }
6214
+ if ('height' in template.inputProps && !('height' in explicitInputs)) {
6215
+ template.inputs['height'] = '100%';
6216
+ }
6217
+ }
6218
+ // DEFAULT B: per TABLE forziamo `table-layout: fixed` se non specificato
6219
+ // dall'utente. In modalita' fixed le colonne sono equipartite a priori
6220
+ // (cols * (width / cols)) e NON dipendono dal contenuto. Cosi' iniettare
6221
+ // contenuti di dimensioni diverse nelle celle (LABEL "ciao" + altro)
6222
+ // non causa squilibrio. Default storico del framework era 'auto' ma per
6223
+ // le table create dal RAG-chatbot 'fixed' e' molto piu' robusto.
6224
+ if (toolName === 'TABLE' && template.inputs
6225
+ && template.inputProps && 'tableLayout' in template.inputProps
6226
+ && !('tableLayout' in explicitInputs)) {
6227
+ template.inputs['tableLayout'] = 'fixed';
6228
+ }
6229
+ try {
6230
+ // Stash il template ORIGINALE (preserva function references).
6231
+ // Il drop() interno fara' un JSON.parse(JSON.stringify(draggedPayload))
6232
+ // per il currentTool, e poi ripristinera' currentTool.onDrop =
6233
+ // draggedPayload.onDrop dalla nostra istanza viva.
6234
+ this.draggedPayload = template;
6235
+ const fakeEvent = { toElement: null };
6236
+ if (parentUniqueName) {
6237
+ const fakeEl = document.createElement('div');
6238
+ fakeEl.setAttribute('id', parentUniqueName);
6239
+ fakeEvent.toElement = fakeEl;
6240
+ }
6241
+ this.drop(fakeEvent);
6242
+ }
6243
+ finally {
6244
+ // Restore SEMPRE, anche su exception in drop(), cosi' il template della
6245
+ // palette torna ai defaults originali (rows=2, cols=2, route=null, ecc.)
6246
+ if (template.inputs) {
6247
+ // Rimuovi le chiavi che node.inputs aveva aggiunto e non c'erano nel backup
6248
+ for (const k of Object.keys(template.inputs)) {
6249
+ if (!Object.prototype.hasOwnProperty.call(inputsBackup, k)) {
6250
+ delete template.inputs[k];
6251
+ }
6252
+ }
6253
+ // Ripristina i valori delle chiavi originali
6254
+ Object.assign(template.inputs, inputsBackup);
6255
+ }
6256
+ }
6257
+ // L'ultimo nodo pushed e' quello appena iniettato. Recuperalo dal flatten
6258
+ // tree (aggiornato da drop()) per ottenere l'uniqueName generato.
6259
+ const justInjected = this.findLastInjectedNode(toolName, parentUniqueName);
6260
+ const injectedUniqueName = justInjected?.uniqueName;
6261
+ // EQUIPARTITION VERTICALE per TABLE/SPLITTER/ACCORDION iniettati via RAG.
6262
+ // `table-layout: fixed` equipartisce solo le colonne (asse orizzontale);
6263
+ // le righe TR (senza height esplicita) restano content-driven -> una
6264
+ // riga con contenuto alto si espande, schiacciando le altre.
6265
+ //
6266
+ // Fix: dopo dropTable/dropSplitter/dropAccordion settiamo height/size
6267
+ // percentuale equa sui figli auto-create.
6268
+ // - TABLE: ogni TR riceve height = 100/rows %
6269
+ // - SPLITTER: ogni SPLITTER-AREA riceve size = 100/areas
6270
+ // (gia' fatto da dropSplitter con size=50 hardcoded, qui rigiamo
6271
+ // per essere coerenti con N != 2)
6272
+ // - ACCORDION: items non hanno height (sono collassabili), skip
6273
+ if (justInjected && Array.isArray(justInjected.nestedComponents) && justInjected.nestedComponents.length > 0) {
6274
+ const childCount = justInjected.nestedComponents.length;
6275
+ if (toolName === 'TABLE') {
6276
+ const rowPct = `${Math.floor(10000 / childCount) / 100}%`;
6277
+ for (const tr of justInjected.nestedComponents) {
6278
+ if (tr?.inputs)
6279
+ tr.inputs['height'] = rowPct;
6280
+ }
6281
+ }
6282
+ else if (toolName === 'SPLITTER') {
6283
+ // SPLITTER-AREA.size e' un number (% o px in base a inputs.unit)
6284
+ const sizePct = Math.floor(10000 / childCount) / 100;
6285
+ for (const area of justInjected.nestedComponents) {
6286
+ if (area?.inputs)
6287
+ area.inputs['size'] = sizePct;
6288
+ }
6289
+ }
6290
+ }
6291
+ // HYDRATION POST-DROP dei binding `dropped-component-list` /
6292
+ // `dropped-component`. Il LLM passa una STRINGA (uniqueName diretto
6293
+ // o placeholder `<DATASOURCE-N>`); il template runtime aspetta un
6294
+ // OGGETTO `{uniqueName, [asyncPath]: BehaviorSubject(liveComp)}`
6295
+ // (stessa shape costruita da setValue() canonico riga ~4972).
6296
+ //
6297
+ // Senza hydration: `inputs.datasource = "DATASOURCE__15"` (stringa),
6298
+ // il tag `[datasource]="inputs.datasource?.component"` legge undefined,
6299
+ // DATAREPEATER renderizza vuoto = grid senza dati e senza colonne.
6300
+ //
6301
+ // POST-drop perche':
6302
+ // 1. `drop()` clona via JSON un eventuale BehaviorSubject pre-drop;
6303
+ // 2. il target referenziato (es. DATASOURCE_15) deve esistere gia'
6304
+ // in flattenedDashboardElements (= iniettato in nodo precedente
6305
+ // del layout o gia' nel canvas).
6306
+ if (justInjected && justInjected.inputs && droppedBindingKeys.length > 0 && node.inputs) {
6307
+ for (const key of droppedBindingKeys) {
6308
+ const propDef = template.inputProps?.[key];
6309
+ if (!propDef)
6310
+ continue;
6311
+ const raw = node.inputs[key];
6312
+ if (raw === null || raw === undefined)
6313
+ continue;
6314
+ // Se gia' oggetto (LLM mandasse direttamente {uniqueName,component})
6315
+ // o BehaviorSubject -> pass-through. Solo stringhe vengono risolte.
6316
+ if (typeof raw !== 'string') {
6317
+ justInjected.inputs[key] = raw;
6318
+ continue;
6319
+ }
6320
+ const resolved = (raw.startsWith('<') && raw.endsWith('>'))
6321
+ ? this.resolveCrossTreePlaceholder(raw)
6322
+ : raw.trim();
6323
+ if (!resolved) {
6324
+ throw new Error(`Binding '${toolName}.${key}': placeholder '${raw}' non risolvibile. `
6325
+ + `Pattern atteso: '<DATASOURCE-N>' (N=indice 0-based del DATASOURCE iniettato). `
6326
+ + `Assicurati che il componente target sia stato iniettato PRIMA nel layout[].`);
6327
+ }
6328
+ const refElement = (this.flattenedDashboardElements || []).find((n) => n?.uniqueName === resolved);
6329
+ if (!refElement) {
6330
+ throw new Error(`Binding '${toolName}.${key}': componente '${resolved}' non presente nel canvas`);
6331
+ }
6332
+ const filter = propDef.filter;
6333
+ if (filter && refElement.name && String(refElement.name).toUpperCase() !== String(filter).toUpperCase()) {
6334
+ throw new Error(`Binding '${toolName}.${key}': atteso ${filter}, trovato ${refElement.name} (${resolved})`);
6335
+ }
6336
+ // Estrai snapshot del liveComp dal componentRef del DATASOURCE
6337
+ // referenziato. Stessa logica di setValue() canonico riga 4977-4982.
6338
+ //
6339
+ // CRITICAL ASYNC: in scenario "inject programmatico" (chatbot RAG),
6340
+ // il DATASOURCE e il DATAREPEATER vengono droppati nello stesso
6341
+ // tick. Il componente Angular <wuic-data-source> NON e' ancora
6342
+ // materializzato (ngAfterViewInit non e' partito) -> il suo
6343
+ // `componentRef.next({component: this, ...})` (riga 838 di
6344
+ // data-source.component.ts) succede SOLO al prossimo CD tick.
6345
+ //
6346
+ // Quindi al momento dell'hydration `compRef.value?.component` e' null.
6347
+ // Se ci fermassimo a snapshot, creeremmo `BehaviorSubject(null)` e
6348
+ // il DATAREPEATER resterebbe vuoto FOREVER (il BS interno del repeater
6349
+ // non sa che deve ri-leggere quando il DATASOURCE finisce l'init).
6350
+ //
6351
+ // Fix: subscribe al compRef BS originale del DATASOURCE e propagare
6352
+ // al compBs locale del repeater quando emette. Cosi' appena il
6353
+ // DataSourceComponent si materializza, il binding si chiude e la
6354
+ // grid renderizza i dati.
6355
+ const compRef = refElement.inputs?.componentRef;
6356
+ const liveComp = (compRef instanceof BehaviorSubject)
6357
+ ? (compRef.value?.component ?? compRef.value)
6358
+ : (compRef?.value?.component ?? compRef?.component ?? compRef ?? null);
6359
+ const asyncPath = propDef.asyncPath || 'component';
6360
+ const compBs = new BehaviorSubject(liveComp);
6361
+ if (compRef instanceof BehaviorSubject) {
6362
+ // Subscribe sul compRef del DATASOURCE: ogni volta che il payload
6363
+ // viene emesso (es. quando il componente Angular fa next con
6364
+ // {component: this} in ngAfterViewInit), propaghiamo al BS del
6365
+ // repeater. La sottoscrizione vive finche' il designer e' aperto;
6366
+ // viene cleaned-up via `this.subs.add(...)` se la subs collection
6367
+ // esiste, altrimenti scope al componente lifecycle.
6368
+ const sub = compRef.subscribe((emitted) => {
6369
+ const newComp = (emitted && typeof emitted === 'object')
6370
+ ? (emitted.component ?? emitted)
6371
+ : null;
6372
+ if (newComp && newComp !== compBs.value) {
6373
+ compBs.next(newComp);
6374
+ }
6375
+ });
6376
+ try {
6377
+ const selfAny = this;
6378
+ if (selfAny.subs && typeof selfAny.subs.add === 'function') {
6379
+ selfAny.subs.add(sub);
6380
+ }
6381
+ }
6382
+ catch { /* subs collection non disponibile, sub continuera' a vivere col designer */ }
6383
+ }
6384
+ justInjected.inputs[key] = {
6385
+ uniqueName: refElement.uniqueName,
6386
+ [asyncPath]: compBs
6387
+ };
6388
+ }
6389
+ }
6390
+ // SIDE-EFFECT DATAREPEATER: dopo l'inject e l'hydration di
6391
+ // `inputs.datasource`, il property panel canonico (setValue riga ~4997)
6392
+ // chiama `propertyTreeBuilder(action, inputs)` per popolare la lista
6393
+ // colonne del repeater (action='list' -> grid). Replichiamo qui per
6394
+ // chiudere il binding end-to-end: senza questo, l'archetipo 'list'
6395
+ // non sa quali colonne mostrare e la grid resta vuota anche con
6396
+ // datasource bindato correttamente.
6397
+ if (justInjected && toolName === 'DATAREPEATER' && justInjected.inputs) {
6398
+ const ds = justInjected.inputs['datasource'];
6399
+ const action = this.getRepeaterAction(justInjected) || justInjected.inputs['action'] || 'list';
6400
+ if (ds) {
6401
+ try {
6402
+ this.propertyTreeBuilder(action, justInjected.inputs);
6403
+ }
6404
+ catch (err) {
6405
+ try {
6406
+ console.warn('[injectLayoutNode] propertyTreeBuilder failed for DATAREPEATER', err);
6407
+ }
6408
+ catch { /* ignore */ }
6409
+ }
6410
+ // CONFIG ARCHETIPO INLINE: il nodo puo' portare `archetype_config`
6411
+ // (= md_props_bag.archetypes.<action> JSON, es. chart {type,options,
6412
+ // dataOptions}, map {zoom,center,...}, scheduler {fromField,...},
6413
+ // kanban {statusField,statusColumns,...}). NESSUNA chiamata server: la
6414
+ // config viene scritta nel metaInfo.tableMetadata del DATASOURCE bindato
6415
+ // (md_props_bag + extraProps.archetypes); il DATAREPEATER reagisce via
6416
+ // fetchInfo$ e renderizza l'archetipo configurato. Persiste alla
6417
+ // serializzazione del datasource quando si salva la dashboard.
6418
+ const archCfg = (node && (node.archetype_config || node.archetypeConfig))
6419
+ || (node?.inputs && (node.inputs.archetype_config || node.inputs.archetypeConfig))
6420
+ || null;
6421
+ if (archCfg && typeof archCfg === 'object') {
6422
+ // Defer: il DataSourceComponent bindato (e l'archetipo renderizzato) si
6423
+ // materializzano async dopo il drop. Aspetta che il ds sia pronto
6424
+ // (fetchData disponibile), poi applica via il path PROVEN del configuratore
6425
+ // (`applyArchetypeConfigToTool`: customProps_<action> + metaInfo + ds.fetchData
6426
+ // + propertyTreeBuilder) — lo STESSO usato da "Configura chart".
6427
+ this.deferApplyArchetypeConfig(justInjected, String(action), archCfg);
6428
+ }
6429
+ }
6430
+ }
6431
+ let count = 1;
6432
+ // Recursion sui children. Tre casi per il parent del child:
6433
+ // 1. `ch.parent_unique_name` e' un uniqueName REALE (es. "TD__126")
6434
+ // => usalo direttamente, il drop() lo risolvera' via componentId.
6435
+ // 2. `ch.parent_unique_name` e' un PLACEHOLDER semantico (es. "<TD-0>",
6436
+ // "<SPLITTER-AREA-1>") => risolvilo dentro `justInjected.nestedComponents`
6437
+ // (i nodi auto-create da dropTable/dropSplitter/dropAccordion).
6438
+ // 3. Manca o null => default = il parent appena iniettato (LABEL dentro
6439
+ // DIV, TABPANEL dentro TABVIEW, ecc.).
6440
+ const children = Array.isArray(node.children) ? node.children : [];
6441
+ if (children.length && injectedUniqueName) {
6442
+ for (const ch of children) {
6443
+ let childParent = injectedUniqueName;
6444
+ const declared = (ch && typeof ch.parent_unique_name === 'string') ? ch.parent_unique_name.trim() : '';
6445
+ if (declared) {
6446
+ if (declared.startsWith('<') && declared.endsWith('>')) {
6447
+ // Placeholder semantico: risolvi dentro justInjected
6448
+ const resolved = this.resolveLayoutPlaceholder(declared, justInjected);
6449
+ if (resolved)
6450
+ childParent = resolved;
6451
+ else
6452
+ throw new Error(`Placeholder '${declared}' non risolvibile dentro ${justInjected?.uniqueName} (verifica struttura: rows/cols/areas/items match)`);
6453
+ }
6454
+ else {
6455
+ // uniqueName reale (es. TD__126): pass-through al drop()
6456
+ childParent = declared;
6457
+ }
6458
+ }
6459
+ count += await this.injectLayoutNode(ch, childParent);
6460
+ }
6461
+ }
6462
+ return count;
6463
+ }
6464
+ /**
6465
+ * Risolve i placeholder semantici che il LLM usa per riferirsi a nodi
6466
+ * auto-generati dai custom onDrop (TABLE crea TR/TD, SPLITTER crea
6467
+ * SPLITTER-AREA, ACCORDION crea ACCORDION-AREA). Lookup dentro il parent
6468
+ * appena iniettato.
6469
+ *
6470
+ * Pattern supportati (case-insensitive sui tag):
6471
+ * - `<TD-N>` - N-esima cella in row-major order (TR[0].TD[0],
6472
+ * TR[0].TD[1], ..., TR[1].TD[0], ...). N zero-based.
6473
+ * - `<TD-R-C>` - cella a riga R colonna C (zero-based).
6474
+ * - `<TR-N>` - N-esima riga.
6475
+ * - `<SPLITTER-AREA-N>` - N-esima area dello splitter.
6476
+ * - `<ACCORDION-AREA-N>` - N-esimo item dell'accordion.
6477
+ * - `<TABPANEL-N>` - N-esimo tab del tabview.
6478
+ *
6479
+ * Ritorna l'uniqueName del nodo risolto, o `null` se il pattern non matcha
6480
+ * o l'index e' out-of-bounds (es. <TD-3> in tabella 2x2).
6481
+ */
6482
+ resolveLayoutPlaceholder(placeholder, parentNode) {
6483
+ if (!placeholder || !parentNode)
6484
+ return null;
6485
+ const inner = placeholder.replace(/^<|>$/g, '').trim();
6486
+ const parts = inner.split('-');
6487
+ if (parts.length < 2)
6488
+ return null;
6489
+ const tag = parts[0].toUpperCase();
6490
+ const nested = parentNode.nestedComponents || [];
6491
+ if (tag === 'TD') {
6492
+ // <TD-N> singolo index OR <TD-R-C> riga/colonna
6493
+ if (parts.length === 2) {
6494
+ const n = parseInt(parts[1], 10);
6495
+ if (!Number.isFinite(n))
6496
+ return null;
6497
+ // Row-major lookup: TR[i].TD[j]
6498
+ let idx = 0;
6499
+ for (const tr of nested) {
6500
+ for (const td of (tr.nestedComponents || [])) {
6501
+ if (idx === n)
6502
+ return td.uniqueName || null;
6503
+ idx++;
6504
+ }
6505
+ }
6506
+ return null;
6507
+ }
6508
+ if (parts.length === 3) {
6509
+ const r = parseInt(parts[1], 10);
6510
+ const c = parseInt(parts[2], 10);
6511
+ if (!Number.isFinite(r) || !Number.isFinite(c))
6512
+ return null;
6513
+ const tr = nested[r];
6514
+ if (!tr)
6515
+ return null;
6516
+ const td = (tr.nestedComponents || [])[c];
6517
+ return td?.uniqueName || null;
6518
+ }
6519
+ return null;
6520
+ }
6521
+ if (tag === 'TR') {
6522
+ const n = parseInt(parts[1], 10);
6523
+ if (!Number.isFinite(n))
6524
+ return null;
6525
+ return nested[n]?.uniqueName || null;
6526
+ }
6527
+ if (tag === 'SPLITTER' && parts[1].toUpperCase() === 'AREA') {
6528
+ const n = parseInt(parts[2], 10);
6529
+ if (!Number.isFinite(n))
6530
+ return null;
6531
+ return nested[n]?.uniqueName || null;
6532
+ }
6533
+ if (tag === 'ACCORDION' && parts[1].toUpperCase() === 'AREA') {
6534
+ const n = parseInt(parts[2], 10);
6535
+ if (!Number.isFinite(n))
6536
+ return null;
6537
+ return nested[n]?.uniqueName || null;
6538
+ }
6539
+ if (tag === 'TABPANEL') {
6540
+ const n = parseInt(parts[1], 10);
6541
+ if (!Number.isFinite(n))
6542
+ return null;
6543
+ return nested[n]?.uniqueName || null;
6544
+ }
6545
+ return null;
6546
+ }
6547
+ /**
6548
+ * Risolve placeholder CROSS-TREE per binding di tipo `dropped-component-list`
6549
+ * (es. DATAREPEATER.inputs.datasource = "<DATASOURCE-0>" -> "DATASOURCE__15").
6550
+ *
6551
+ * Diversamente da `resolveLayoutPlaceholder` (lookup dentro
6552
+ * nestedComponents del parent), qui cerchiamo nel `flattenedDashboardElements`
6553
+ * globale, perche' il LLM puo' riferirsi a componenti che vivono in tutto
6554
+ * il canvas (es. DATASOURCE in un TD, DATAREPEATER in un TD diverso).
6555
+ *
6556
+ * Pattern supportato: `<NAME-N>` dove `NAME` matcha `tool.name`
6557
+ * (DATASOURCE, DATAREPEATER, FILTERBAR, PAGER, ...) e `N` e' l'index
6558
+ * 0-based dei tool con quel nome nell'ordine in cui appaiono in
6559
+ * `flattenedDashboardElements` (= ordine di inject).
6560
+ *
6561
+ * Ritorna l'uniqueName risolto, o `null` se non trova match.
6562
+ *
6563
+ * Esempi:
6564
+ * - "<DATASOURCE-0>" -> primo DATASOURCE iniettato (es. "DATASOURCE__3")
6565
+ * - "<DATASOURCE-1>" -> secondo DATASOURCE (master-detail)
6566
+ * - "<DATAREPEATER-0>" -> primo DATAREPEATER
6567
+ *
6568
+ * Casi degeneri:
6569
+ * - placeholder non riconosciuto -> null
6570
+ * - N out-of-bounds (es. <DATASOURCE-3> con solo 2 DS) -> null
6571
+ *
6572
+ * Usato in `injectLayoutNode` per risolvere binding del tipo
6573
+ * `inputs.datasource = "<DATASOURCE-0>"` PRIMA di costruire l'oggetto
6574
+ * wrapper `{uniqueName, component: BS}`.
6575
+ */
6576
+ resolveCrossTreePlaceholder(placeholder) {
6577
+ if (!placeholder || typeof placeholder !== 'string')
6578
+ return null;
6579
+ const trim = placeholder.trim();
6580
+ if (!trim.startsWith('<') || !trim.endsWith('>'))
6581
+ return null;
6582
+ const inner = trim.replace(/^<|>$/g, '').trim();
6583
+ const m = inner.match(/^([A-Za-z][A-Za-z0-9_-]*)-(\d+)$/);
6584
+ if (!m)
6585
+ return null;
6586
+ const targetName = m[1].toUpperCase();
6587
+ const targetIdx = parseInt(m[2], 10);
6588
+ if (!Number.isFinite(targetIdx) || targetIdx < 0)
6589
+ return null;
6590
+ const matching = (this.flattenedDashboardElements || [])
6591
+ .filter((n) => n?.name && String(n.name).toUpperCase() === targetName);
6592
+ if (targetIdx >= matching.length)
6593
+ return null;
6594
+ return matching[targetIdx]?.uniqueName || null;
6595
+ }
6596
+ /**
6597
+ * Best-effort lookup dell'ultimo nodo iniettato. Strategia:
6598
+ * - se parentUniqueName fornito: cerca dentro il nestedComponents del
6599
+ * parent il piu' recente con quel toolName;
6600
+ * - altrimenti (root): cerca in dashboardElements il piu' recente con
6601
+ * quel toolName.
6602
+ *
6603
+ * "piu' recente" = ultimo elemento dell'array (drop() fa push() in append).
6604
+ */
6605
+ findLastInjectedNode(toolName, parentUniqueName) {
6606
+ let scope;
6607
+ if (parentUniqueName) {
6608
+ const parent = (this.flattenedDashboardElements || []).find((n) => n?.uniqueName === parentUniqueName);
6609
+ scope = parent?.nestedComponents || [];
6610
+ }
6611
+ else {
6612
+ scope = this.dashboardElements || [];
6613
+ }
6614
+ for (let i = scope.length - 1; i >= 0; i--) {
6615
+ if (scope[i]?.name === toolName)
6616
+ return scope[i];
6617
+ }
6618
+ return undefined;
5625
6619
  }
5626
6620
  /**
5627
6621
  * Handler capture-phase per mouseup/touchend su document. Riferimento
@@ -8297,6 +9291,22 @@ class DesignerComponent {
8297
9291
  this.closeArchetypeConfigurator();
8298
9292
  return;
8299
9293
  }
9294
+ this.applyArchetypeConfigToTool(tool, action, nextConfig);
9295
+ this.closeArchetypeConfigurator();
9296
+ }
9297
+ /**
9298
+ * Core riusabile di {@link onArchetypeConfigApply}: applica la config di
9299
+ * archetipo a un DATAREPEATER tool. Scrive `customProps_<action>` sulle
9300
+ * proprietà del tool (= ciò che il chatbot designer_inject persiste e l'archetipo
9301
+ * legge), aggiorna `metaInfo.tableMetadata.extraProps`+`md_props_bag`, e RIGENERA
9302
+ * il render chiamando `ds.fetchData()` (re-emit `fetchInfo$` → l'archetipo
9303
+ * ri-legge type/opzioni) + `propertyTreeBuilder`. NESSUNA chiamata server.
9304
+ * Usato sia dal configuratore UI ("Configura chart") sia dall'injection del
9305
+ * chatbot (drop di un DATAREPEATER con `archetype_config`).
9306
+ */
9307
+ applyArchetypeConfigToTool(tool, action, nextConfig) {
9308
+ if (!tool || !action || !nextConfig)
9309
+ return;
8300
9310
  const customKey = 'customProps_' + action;
8301
9311
  tool.inputs[customKey] = this.toBehaviorSubjectsObject(nextConfig);
8302
9312
  const metaInfo = this.getDesignerDatasourceMetaInfo(tool);
@@ -8313,7 +9323,6 @@ class DesignerComponent {
8313
9323
  ds.fetchData();
8314
9324
  }
8315
9325
  this.propertyTreeBuilder(action, tool.inputs);
8316
- this.closeArchetypeConfigurator();
8317
9326
  }
8318
9327
  /**
8319
9328
  * Espone i metadati datasource al popup configuratore archetype.
@@ -8795,8 +9804,24 @@ class DesignerComponent {
8795
9804
  const detailColumnsHint = detailColumns.length
8796
9805
  ? detailColumns.map((col) => `// - ${col}`).join('\n')
8797
9806
  : '// - (no detail columns available yet)';
8798
- const defaultFormula = detailColumns.length
8799
- ? `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\nsetFilter('${detailColumns[0].replace(/'/g, "\\'")}', ${masterPkValueExpression}, 'eq');`
9807
+ // Risoluzione FK del detail verso il master (allineata a
9808
+ // `injectMasterDetailLookupFilter` runtime — cosi' la formula proposta
9809
+ // produce lo stesso risultato del fallback automatico).
9810
+ // Cascata:
9811
+ // 1) FK lookup canonica via metadata (mc_ui_lookup_entity_name == masterRoute);
9812
+ // 2) colonna del detail che ha lo stesso nome della PK del master
9813
+ // (convention WUIC: `cities.StateProvinceID` punta a `stateprovinces.StateProvinceID`);
9814
+ // 3) backward-compat: prima colonna del detail (vecchio comportamento,
9815
+ // produce filtro sbagliato ma evita undefined).
9816
+ const fkFromLookup = detailDatasource
9817
+ ? this.findDetailFkColumnToMasterRoute(detailDatasource, masterRoute)
9818
+ : null;
9819
+ const fkFromNameMatch = !fkFromLookup && masterPrimaryKey && detailColumns.includes(masterPrimaryKey)
9820
+ ? masterPrimaryKey
9821
+ : null;
9822
+ const detailFkField = fkFromLookup || fkFromNameMatch || (detailColumns.length ? detailColumns[0] : '');
9823
+ const defaultFormula = detailFkField
9824
+ ? `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\nsetFilter('${detailFkField.replace(/'/g, "\\'")}', ${masterPkValueExpression}, 'eq');`
8800
9825
  : `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\n// Example:\n// setFilter('detail_fk', ${masterPkValueExpression}, 'eq');`;
8801
9826
  const result = await WtoolboxService.promptDialog('Master-Detail Filter Formula', [
8802
9827
  {
@@ -10791,12 +11816,25 @@ Display formula context:
10791
11816
  return true;
10792
11817
  }
10793
11818
  /**
10794
- * Fallback master-detail basato su colonne lookup: applica filtro `eq` sulla colonna che punta alla route master.
10795
- */
10796
- injectMasterDetailLookupFilter(detailDatasource, masterRoute, masterRowId) {
11819
+ * Helper condiviso: trova nella metadata del DETAIL la colonna lookup che
11820
+ * punta alla route MASTER. Usato sia da `injectMasterDetailLookupFilter`
11821
+ * (runtime fallback) sia da `openMasterDetailFilterFormulaEditor` (default
11822
+ * formula in UI editor) — cosi' i 2 path producono lo stesso risultato e
11823
+ * non c'e' drift tra "filtro automatico" e "formula proposta nel dialog".
11824
+ *
11825
+ * Logica:
11826
+ * 1) cerca una colonna `mc_ui_column_type=='lookupbyid'` con
11827
+ * `mc_ui_lookup_entity_name==<masterRoute>` OPPURE `mc_ui_grid_route==<masterRoute>`.
11828
+ * Questo e' il match canonico WUIC (FK lookup definita nei metadata).
11829
+ * 2) ritorna `null` se non c'e' match (il chiamante decide il fallback —
11830
+ * `injectMasterDetailLookupFilter` ritorna `false`,
11831
+ * `openMasterDetailFilterFormulaEditor` cade su `detailColumns[0]` per
11832
+ * backward-compat).
11833
+ */
11834
+ findDetailFkColumnToMasterRoute(detailDatasource, masterRoute) {
10797
11835
  const normalizedMasterRoute = String(masterRoute || '').trim().toLowerCase();
10798
11836
  if (!normalizedMasterRoute || !detailDatasource?.metaInfo?.columnMetadata?.length) {
10799
- return false;
11837
+ return null;
10800
11838
  }
10801
11839
  const lookupColumn = detailDatasource.metaInfo.columnMetadata.find((column) => {
10802
11840
  if (!column || String(column.mc_ui_column_type || '').toLowerCase() !== 'lookupbyid') {
@@ -10806,7 +11844,14 @@ Display formula context:
10806
11844
  const gridRoute = String(column.mc_ui_grid_route || '').trim().toLowerCase();
10807
11845
  return lookupRoute === normalizedMasterRoute || gridRoute === normalizedMasterRoute;
10808
11846
  });
10809
- const lookupField = lookupColumn?.mc_nome_colonna;
11847
+ const lookupField = String(lookupColumn?.mc_nome_colonna || '').trim();
11848
+ return lookupField || null;
11849
+ }
11850
+ /**
11851
+ * Fallback master-detail basato su colonne lookup: applica filtro `eq` sulla colonna che punta alla route master.
11852
+ */
11853
+ injectMasterDetailLookupFilter(detailDatasource, masterRoute, masterRowId) {
11854
+ const lookupField = this.findDetailFkColumnToMasterRoute(detailDatasource, masterRoute);
10810
11855
  if (!lookupField) {
10811
11856
  return false;
10812
11857
  }
@@ -10858,13 +11903,13 @@ Display formula context:
10858
11903
  });
10859
11904
  // return customProps;
10860
11905
  }
10861
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: DataProviderService }, { token: MetadataProviderService }, { token: UserInfoService }, { token: i0.ChangeDetectorRef }, { token: TranslationManagerService }, { token: MetadataEditorService }, { token: WorkflowRuntimeMetadataService }], target: i0.ɵɵFactoryTarget.Component });
10862
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DesignerComponent, isStandalone: true, selector: "wuic-designer", host: { listeners: { "window:hashchange": "onWindowHashChange()", "window:beforeunload": "onWindowBeforeUnload($event)", "window:keydown": "onWindowKeydown($event)", "window:mouseup": "onWindowMouseup()", "window:touchend": "onWindowTouchend()", "window:keyup": "onWindowKeyup()", "window:change": "onWindowChange()" } }, providers: [TreeDragDropService], ngImport: i0, template: "<!-- <button pButton type=\"button\" (click)=\"loadDashboard()\" class=\"mr-2\">{{'load' | translate}}</button> -->\n@if (!noEdit) {\n<div class=\"designer-layout\">\n <div class=\"designer-toolbar\">\n <div class=\"designer-current-dashboard\">{{ currentDashboardTitle }}</div>\n\n <div class=\"designer-toolbar-spacer\"></div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"undo()\" [disabled]=\"!canUndo\">{{ 'designer.undo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"redo()\" [disabled]=\"!canRedo\">{{ 'designer.redo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"refreshGraphActionsMenuItems(); graphActionsMenu.toggle($event)\">{{ 'designer.actions_menu' | translate }}</button>\n <p-menu #graphActionsMenu [popup]=\"true\" [model]=\"graphActionsMenuItems\" appendTo=\"body\"></p-menu>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"openCurrentDashboardInNewTab()\" [disabled]=\"!currentDashboardRoute\">{{\n 'designer.open_current_dashboard' | translate }}</button>\n </div>\n </div>\n <div class=\"designer-main\">\n <p-splitter [style]=\"{ height: '100%' }\" [panelSizes]=\"[15, 85]\" styleClass=\"designer-main-splitter\">\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[35, 65]\"\n [minSizes]=\"[20, 30]\" styleClass=\"designer-left-splitter\">\n <ng-template pTemplate>\n <div class=\"col flex designer-toolbox palette-panel\">\n <ul class=\"tool-groups\">\n @for (toolGroup of availableToolGroups; track toolGroup.group) {\n <li class=\"tool-group-item\">\n <div class=\"tool-group-title\">{{ toolGroup.group }}</div>\n <ul class=\"tool-group-list\">\n @for (tool of toolGroup.tools; track tool) {\n <li pDraggable (onDragStart)=\"dragStart(tool)\" (onDragEnd)=\"dragEnd()\" (onDrag)=\"onDrag($event)\"\n (dblclick)=\"dropToolAtRoot(tool)\">\n <i [ngClass]=\"tool.icon\" [title]=\"tool.name\"></i>\n </li>\n }\n </ul>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[68, 32]\"\n [minSizes]=\"[35, 20]\" styleClass=\"designer-left-bottom-splitter\">\n <ng-template pTemplate>\n <div class=\"properties-panel\">\n <p-select [style]=\"{width: '100%'}\" [options]=\"flattenedDashboardElements\" [(ngModel)]=\"tool\"\n optionLabel=\"uniqueName\" dataKey=\"componentId\" appendTo=\"body\" />\n <div class=\"designer-toolbox\">\n\n <span style=\"width: 100%;\">\n <input type=\"text\" style=\"width: calc(100% - 30px);\" [(ngModel)]=\"propFilter\"\n [placeholder]=\"'prop.filter' | translate\" />\n <button style=\"width: 25px\" (click)=\"clearPropFilter()\">X</button>\n </span>\n\n @for (section of getToolPropertySections(tool, propFilter); track section.key) {\n <details class=\"prop-section\" [open]=\"section.open\"\n (toggle)=\"onPropertySectionToggle(tool, section.key, $event)\">\n <summary class=\"prop-section-title\">{{ section.label }}</summary>\n <ul class=\"props\">\n @for (toolProp of section.props; track toolProp.key) {\n <li>\n @if (!toolProp.value.hideCaption) {\n <span>{{ toolProp.value?.propertyCaption ||\n toolProp.key[0].toUpperCase() + toolProp.key.slice(1)\n }}</span>\n }\n <br />\n @if (toolProp.value.type == 'button') {\n <p-button (click)=\"toolProp.value.callback(tool.inputs, toolProp.key, null, tool)\"\n class=\"k-button\" [style]=\"{width: '100%'}\">\n {{toolProp.value.propertyCaption}}\n </p-button>\n }\n @if (toolProp.value.type == 'metaEditor' &&\n tool.inputs['componentRef']?.value?.component) {\n <wuic-metadata-editor [hardcodedDatasource]=\"tool.inputs['componentRef']?.value?.component\"\n [saveCallback]=\"metaEditorSave\"></wuic-metadata-editor>\n }\n @if (toolProp.value.type == 'boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"tool.inputs[toolProp.key]\" />\n }\n @if (toolProp.value.type == 'numberToArray') {\n <input style=\"width: 100%;\" type=\"number\" pInputText [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'txt_area') {\n <textarea style=\"width: 100%;\" [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\"></textarea>\n }\n @if (toolProp.value.type == 'string') {\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'color') {\n <p-colorPicker [ngModel]=\"getDesignerColorPickerHex(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorPickerHexChange($event, tool, toolProp)\" appendTo=\"body\" />\n <div class=\"designer-color-alpha-row\">\n <span class=\"designer-color-alpha-label\">{{ 'designer.alpha' | translate }}</span>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n </div>\n <input type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'dictionary' && !toolProp.value.async) {\n @if (toolProp.key == 'cssFile') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"availableProjectCssFiles\"\n [ngModel]=\"tool.inputs[toolProp.key]\" (ngModelChange)=\"onCssFileChanged($event, tool)\" />\n } @else if (toolProp.key == 'cssClass') {\n <p-multiSelect [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getCssClassOptionsForTool(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"onCssClassChanged($event, tool)\" [filter]=\"true\" [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"38\" scrollHeight=\"260px\" [showToggleAll]=\"false\" display=\"chip\"\n [maxSelectedLabels]=\"3\" [selectedItemsLabel]=\"'{0} selected'\">\n </p-multiSelect>\n } @else if ((tool.name == 'SELECT' || tool.name == 'MULTISELECT' || tool.name == 'UL') &&\n (toolProp.key == 'valueField' || toolProp.key == 'textField')) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else if (tool.name == 'LABEL' && toolProp.key == 'displayField') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n }\n @if (toolProp.value.type == 'dictionary' && toolProp.value.async) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (tool.name == 'DATAREPEATER' && toolProp.key == 'action' &&\n canOpenArchetypeConfigurator(tool)) {\n <button style=\"margin-top: 8px;\" (click)=\"openArchetypeConfigurator(tool)\">\n {{ 'designer.configure' | translate }} {{ (tool.inputs['action'] | async) || '' }}\n </button>\n }\n @if (toolProp.value.type == 'dropped-component-list') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [placeholder]=\"'select.component' | translate\" [options]=\"elementFilter(toolProp)\"\n [ngModel]=\"getDroppedComponentListModel(tool, toolProp)\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" optionLabel=\"uniqueName\"\n dataKey=\"uniqueName\" />\n }\n @if (toolProp.value.type == 'autocomplete') {\n <p-autoComplete [style]=\"{width: '100%'}\" appendTo=\"body\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\" [forceSelection]=\"true\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" [suggestions]=\"tool.suggestions\"\n [optionLabel]=\"toolProp.value.displayField\" [optionValue]=\"toolProp.value.valueField\"\n (completeMethod)=\"search($event, tool, toolProp)\" [dropdown]=\"true\" dropdownMode=\"current\"\n [multiple]=\"toolProp.value.multiple\" />\n }\n @if (toolProp.value.type == 'propertyTree' && shouldShowPropertyTree(tool)) {\n <ul>\n @for (item of $any(tool.inputs[toolProp.key] | async); track item) {\n <li style=\"padding: 0; margin: 0;\">\n @if (item.col && tool.inputs['customProps_' + (tool.inputs['action'] | async)])\n {\n <wuic-field-editor [field]=\"item.col\"\n [record]=\"tool.inputs['customProps_' + (tool.inputs['action'] | async)]\"\n [metaInfo]=\"tool.inputs['datasource']?.component?.value?.metaInfo\"\n [triggerProp]=\"tool.inputs['action']\"></wuic-field-editor>\n }\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n </details>\n }\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"hierarchy-panel\">\n <div class=\"hierarchy-panel-title\">{{ 'designer.canvas_hierarchy' | translate }}</div>\n <div class=\"hierarchy-tree-wrap\">\n <p-tree [value]=\"hierarchyTreeNodes\" [draggableNodes]=\"true\" [droppableNodes]=\"true\"\n selectionMode=\"single\" [(selection)]=\"hierarchySelection\"\n (onNodeSelect)=\"onHierarchyNodeSelect($event)\" draggableScope=\"designer-hierarchy\"\n droppableScope=\"designer-hierarchy\" (onNodeDrop)=\"onHierarchyNodeDrop($event)\">\n <ng-template let-node pTemplate=\"default\">\n <span class=\"hierarchy-node-label\" [class.active]=\"isHierarchyNodeSelected(node)\"\n (mouseenter)=\"onHierarchyNodeMouseEnter(node)\" (mouseleave)=\"onHierarchyNodeMouseLeave()\"\n (contextmenu)=\"showHierarchyNodeContextMenu($event, node, hierarchyCtxMenu)\">\n {{ node.label }}\n </span>\n </ng-template>\n </p-tree>\n </div>\n <p-contextMenu #hierarchyCtxMenu [model]=\"hierarchyCtxItems\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </ng-template>\n </p-splitter>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"col dashboard-design\" [class.designer-hide-datasources]=\"hideDatasourcesInCanvas\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n <p-contextMenu [target]=\"canvas\" [model]=\"ctxItems | async\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </div>\n <div class=\"designer-footer\">\n <div class=\"designer-footer-label\">{{ 'designer.items' | translate }}:</div>\n <div class=\"designer-footer-list\">\n @for (item of droppedDashboardItems; track item.uniqueName) {\n <button type=\"button\" class=\"designer-footer-item\" [class.active]=\"tool?.uniqueName === item?.uniqueName\"\n [attr.data-designer-unique-name]=\"item?.uniqueName\" (mouseenter)=\"setFooterHoverHighlight(item)\"\n (mouseleave)=\"clearFooterHoverHighlight()\" (click)=\"selectDashboardItem(item)\"\n (contextmenu)=\"showFooterContextMenu($event, item, footerCtxMenu)\">\n {{ getDroppedDashboardItemLabel(item) }}\n </button>\n }\n </div>\n <p-contextMenu #footerCtxMenu [model]=\"footerCtxItems\" appendTo=\"body\" />\n </div>\n</div>\n}\n\n@if (noEdit) {\n<wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n}\n\n<wuic-archetype-configurator [visible]=\"showArchetypeConfigurator\"\n (visibleChange)=\"onArchetypeConfigVisibleChange($event)\" [archetype]=\"getActiveArchetypeConfig()\"\n [metaInfo]=\"getArchetypeConfigMetaInfoForTool(archetypeConfigTool)\"\n [value]=\"getArchetypeConfigValueForTool(archetypeConfigTool)\" (applyConfig)=\"onArchetypeConfigApply($event)\">\n</wuic-archetype-configurator>\n\n<wuic-css-sheet-editor [visible]=\"showCssSheetEditor\" (visibleChange)=\"onCssSheetEditorVisibleChange($event)\"\n [sheets]=\"dashboardCssSheets\" [selectedSheetPath]=\"selectedCssEditorSheetPath\" [assetsFolder]=\"currentDashboardRoute\"\n [styleProps]=\"getCssEditorStyleProps()\" (applyEditor)=\"onCssSheetEditorApply($event)\">\n</wuic-css-sheet-editor>\n\n<p-dialog [header]=\"'designer.reopen_dashboard' | translate\" [(visible)]=\"reopenDashboardDialogVisible\" [modal]=\"true\"\n [closable]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [appendTo]=\"'body'\" [style]=\"{ width: '34rem' }\"\n (onHide)=\"cancelReopenDashboardDialog()\">\n <div class=\"route-popup-form\">\n <label for=\"designer-reopen-select\">{{ 'designer.existing_dashboards' | translate }}</label>\n <p-select inputId=\"designer-reopen-select\" [options]=\"savedDashboardOptions\" optionLabel=\"label\" optionValue=\"value\"\n [(ngModel)]=\"reopenDialogSelectedDashboardRoute\" [filter]=\"true\" [showClear]=\"true\" [style]=\"{ width: '100%' }\"\n appendTo=\"body\" />\n\n <div class=\"route-popup-actions\">\n <button type=\"button\" class=\"route-popup-btn\" (click)=\"cancelReopenDashboardDialog()\">{{ 'cancel' | translate\n }}</button>\n <button type=\"button\" class=\"route-popup-btn route-popup-btn-primary\" (click)=\"confirmReopenDashboardDialog()\"\n [disabled]=\"!reopenDialogSelectedDashboardRoute\">{{ 'designer.reopen' | translate }}</button>\n </div>\n </div>\n</p-dialog>", styles: [":host{display:block;width:100%;height:100%;min-width:0}.designer-layout{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%;min-height:0;width:100%;min-width:0}.designer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;border:1px solid inherit;border-radius:10px;background:transparent;padding:10px 12px;margin-bottom:8px}.designer-current-dashboard{min-width:220px;font-size:.86rem;font-weight:700;color:inherit}.designer-toolbar-spacer{flex:1 1 auto;min-width:16px}.designer-toolbar-menu-group{display:inline-flex}.designer-toolbar-menu-group button{height:32px;border:1px solid #2f67d8;border-radius:6px;background:#2f67d8;color:#fff;padding:0 10px;font-size:.82rem;cursor:pointer}.designer-toolbar-menu-group button[disabled]{opacity:.6;cursor:not-allowed}.designer-main{min-height:0;min-width:0;width:100%;overflow:hidden;direction:ltr}.designer-main-splitter{height:100%;width:100%;min-width:0;direction:ltr}.designer-toolbox{font-size:3rem}.designer-toolbox ul{list-style-type:none;padding:0;margin:0;width:100%}.designer-toolbox li{float:left;margin-left:5px;margin-top:5px}.designer-toolbox .tool-groups{display:block}.designer-toolbox .tool-group-item{float:none;margin-left:0;margin-top:10px;clear:both}.designer-toolbox .tool-group-title{font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;opacity:.7;margin:0 0 4px 6px}.designer-toolbox .tool-group-list{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.designer-toolbox .tool-group-list li{float:none;margin-left:0;margin-top:0}.designer-toolbox .props{font-size:1rem}.designer-toolbox .props li{float:none;padding-bottom:15px}.designer-toolbox .prop-section{width:100%;border:1px solid inherit;border-radius:6px;margin-top:8px;background:transparent;padding:6px 8px 2px}.designer-toolbox .prop-section-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;cursor:pointer;color:inherit;margin-bottom:6px}.palette-panel{height:100%;overflow:auto;width:100%}.designer-color-alpha-row{display:flex;align-items:center;gap:8px;margin-top:6px;margin-bottom:6px}.designer-color-alpha-label{font-size:.8rem;min-width:40px}.designer-color-alpha-row input[type=range]{flex:1 1 auto;min-width:90px}.designer-color-alpha-row input[type=number]{width:72px}.properties-panel{height:100%;overflow:hidden;width:100%;display:flex;flex-direction:column;min-height:0;padding-right:10px;padding-left:10px;padding-top:10px}.properties-panel>.designer-toolbox{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable}:host ::ng-deep .designer-left-bottom-splitter .p-splitterpanel{min-height:0!important;overflow:hidden!important}.hierarchy-panel{height:100%;width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;padding:6px}.hierarchy-panel-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#4b5563;margin:0 0 6px}.hierarchy-tree-wrap{flex:1 1 auto;min-height:0;border:1px solid inherit;border-radius:6px;background:transparent;overflow:auto;padding:4px}:host ::ng-deep .hierarchy-tree-wrap .p-tree{border:none;padding:0;height:100%;background:transparent}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content{border:1px solid transparent;border-radius:4px;background:transparent!important;color:inherit!important;padding:0!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight{border-color:transparent!important;background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content:hover{background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label{display:inline-block;border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label:hover{border-color:#94a3b8}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label.active{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight .hierarchy-node-label{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}.designer-hierarchy-node{cursor:pointer;border:1px solid transparent;border-radius:4px;padding:2px 6px;font-size:.82rem;line-height:1.25rem}.designer-hierarchy-node-label{cursor:pointer;display:inline-block;width:100%}.designer-hierarchy-node.active{border-color:#3b82f6;background:#eff6ff;color:#1d4ed8}.designer-footer-item.designer-tree-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px}.dashboard-design{height:100%;min-height:0;min-width:0;width:100%;overflow:auto;direction:ltr}:host ::ng-deep .dashboard-design.designer-hide-datasources .datasource-descriptor{display:none!important}.current-dashboard-title{font-weight:600;margin-right:12px}.designer-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid inherit;background:transparent;min-height:36px;flex:0 0 auto;width:100%;overflow:auto}.designer-footer-label{font-size:.85rem;font-weight:600;color:inherit;white-space:nowrap}.designer-footer-list{display:flex;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;width:100%}.designer-footer-item{border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer;white-space:nowrap}.designer-footer-item.active{border-color:#3b82f6;background:transparent;color:#1d4ed8}:host ::ng-deep .designer-footer-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px;box-shadow:inset 0 0 0 1px #3b82f6!important}.route-popup-form{display:flex;flex-direction:column;gap:8px}.route-popup-form label{font-size:.85rem;font-weight:600;color:#2f3f58}.route-popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.route-popup-btn{border:1px solid inherit;background:transparent;color:inherit;border-radius:6px;padding:7px 12px;font-size:.85rem;cursor:pointer}.route-popup-btn-primary{border-color:#2f67d8;background:#2f67d8;color:#fff}.p-colorpicker{border:1px solid black;border-radius:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i9.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i11.Draggable, selector: "[pDraggable]", inputs: ["pDraggable", "dragEffect", "dragHandle", "pDraggableDisabled"], outputs: ["onDragStart", "onDragEnd", "onDrag"] }, { kind: "directive", type: i11.Droppable, selector: "[pDroppable]", inputs: ["pDroppable", "pDroppableDisabled", "dropEffect"], outputs: ["onDragEnter", "onDragLeave", "onDrop"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i6.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ColorPickerModule }, { kind: "component", type: i2$1.ColorPicker, selector: "p-colorPicker, p-colorpicker, p-color-picker", inputs: ["styleClass", "showTransitionOptions", "hideTransitionOptions", "inline", "format", "tabindex", "inputId", "autoZIndex", "autofocus", "defaultColor", "appendTo", "overlayOptions", "motionOptions"], outputs: ["onChange", "onShow", "onHide"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i2$2.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "component", type: DashboardComponent, selector: "wuic-dashboard", inputs: ["dashboardElements"] }, { kind: "component", type: FieldEditorComponent, selector: "wuic-field-editor", inputs: ["datasource", "record", "field", "metaInfo", "readOnly", "isFilter", "hideLabel", "forceShowLabel", "operator", "nestedIndex", "triggerProp", "tabIndex", "onInlineCellValueChange"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: MetadataEditorComponent, selector: "wuic-metadata-editor", inputs: ["datasource", "hardcodedDatasource", "saveCallback", "hideReports", "hideRelatedTableActions", "hideRelatedColumnActions"] }, { kind: "ngmodule", type: MenubarModule }, { kind: "component", type: ArchetypeConfiguratorComponent, selector: "wuic-archetype-configurator", inputs: ["visible", "archetype", "metaInfo", "value"], outputs: ["visibleChange", "applyConfig"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i17.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: CssSheetEditorComponent, selector: "wuic-css-sheet-editor", inputs: ["visible", "sheets", "selectedSheetPath", "styleProps", "assetsFolder"], outputs: ["visibleChange", "applyEditor"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i9$1.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TreeModule }, { kind: "component", type: i4$1.Tree, selector: "p-tree", inputs: ["value", "selectionMode", "loadingMode", "selection", "styleClass", "contextMenu", "contextMenuSelectionMode", "contextMenuSelection", "draggableScope", "droppableScope", "draggableNodes", "droppableNodes", "metaKeySelection", "propagateSelectionUp", "propagateSelectionDown", "loading", "loadingIcon", "emptyMessage", "ariaLabel", "togglerAriaLabel", "ariaLabelledBy", "validateDrop", "filter", "filterInputAutoFocus", "filterBy", "filterMode", "filterOptions", "filterPlaceholder", "filteredNodes", "filterLocale", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "indentation", "_templateMap", "trackBy", "highlightOnSelect"], outputs: ["selectionChange", "contextMenuSelectionChange", "onNodeSelect", "onNodeUnselect", "onNodeExpand", "onNodeCollapse", "onNodeContextMenuSelect", "onNodeDoubleClick", "onNodeDrop", "onLazyLoad", "onScroll", "onScrollIndexChange", "onFilter"] }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
11906
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: DataProviderService }, { token: MetadataProviderService }, { token: UserInfoService }, { token: i0.ChangeDetectorRef }, { token: TranslationManagerService }, { token: MetadataEditorService }, { token: ChatbotHostRegistryService }, { token: WorkflowRuntimeMetadataService }], target: i0.ɵɵFactoryTarget.Component });
11907
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DesignerComponent, isStandalone: true, selector: "wuic-designer", host: { listeners: { "window:hashchange": "onWindowHashChange()", "window:beforeunload": "onWindowBeforeUnload($event)", "window:keydown": "onWindowKeydown($event)", "window:mouseup": "onWindowMouseup()", "window:touchend": "onWindowTouchend()", "window:keyup": "onWindowKeyup()", "window:change": "onWindowChange()" } }, providers: [TreeDragDropService], ngImport: i0, template: "<!-- <button pButton type=\"button\" (click)=\"loadDashboard()\" class=\"mr-2\">{{'load' | translate}}</button> -->\n@if (!noEdit) {\n<div class=\"designer-layout\">\n <div class=\"designer-toolbar\">\n <div class=\"designer-current-dashboard\">{{ currentDashboardTitle }}</div>\n\n <div class=\"designer-toolbar-spacer\"></div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"undo()\" [disabled]=\"!canUndo\">{{ 'designer.undo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"redo()\" [disabled]=\"!canRedo\">{{ 'designer.redo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"refreshGraphActionsMenuItems(); graphActionsMenu.toggle($event)\">{{ 'designer.actions_menu' | translate }}</button>\n <p-menu #graphActionsMenu [popup]=\"true\" [model]=\"graphActionsMenuItems\" appendTo=\"body\"></p-menu>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"openCurrentDashboardInNewTab()\" [disabled]=\"!currentDashboardRoute\">{{\n 'designer.open_current_dashboard' | translate }}</button>\n </div>\n </div>\n <div class=\"designer-main\">\n <p-splitter [style]=\"{ height: '100%' }\" [panelSizes]=\"[15, 85]\" styleClass=\"designer-main-splitter\">\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[35, 65]\"\n [minSizes]=\"[20, 30]\" styleClass=\"designer-left-splitter\">\n <ng-template pTemplate>\n <div class=\"col flex designer-toolbox palette-panel\">\n <ul class=\"tool-groups\">\n @for (toolGroup of availableToolGroups; track toolGroup.group) {\n <li class=\"tool-group-item\">\n <div class=\"tool-group-title\">{{ toolGroup.group }}</div>\n <ul class=\"tool-group-list\">\n @for (tool of toolGroup.tools; track tool) {\n <li pDraggable (onDragStart)=\"dragStart(tool)\" (onDragEnd)=\"dragEnd()\" (onDrag)=\"onDrag($event)\"\n (dblclick)=\"dropToolAtRoot(tool)\">\n <i [ngClass]=\"tool.icon\" [title]=\"tool.name\"></i>\n </li>\n }\n </ul>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[68, 32]\"\n [minSizes]=\"[35, 20]\" styleClass=\"designer-left-bottom-splitter\">\n <ng-template pTemplate>\n <div class=\"properties-panel\">\n <p-select [style]=\"{width: '100%'}\" [options]=\"flattenedDashboardElements\" [(ngModel)]=\"tool\"\n optionLabel=\"uniqueName\" dataKey=\"componentId\" appendTo=\"body\" />\n <div class=\"designer-toolbox\">\n\n <span style=\"width: 100%;\">\n <input type=\"text\" style=\"width: calc(100% - 30px);\" [(ngModel)]=\"propFilter\"\n [placeholder]=\"'prop.filter' | translate\" />\n <button style=\"width: 25px\" (click)=\"clearPropFilter()\">X</button>\n </span>\n\n @for (section of getToolPropertySections(tool, propFilter); track section.key) {\n <details class=\"prop-section\" [open]=\"section.open\"\n (toggle)=\"onPropertySectionToggle(tool, section.key, $event)\">\n <summary class=\"prop-section-title\">{{ section.label }}</summary>\n <ul class=\"props\">\n @for (toolProp of section.props; track toolProp.key) {\n <li>\n @if (!toolProp.value.hideCaption) {\n <span>{{ toolProp.value?.propertyCaption ||\n toolProp.key[0].toUpperCase() + toolProp.key.slice(1)\n }}</span>\n }\n <br />\n @if (toolProp.value.type == 'button') {\n <p-button (click)=\"toolProp.value.callback(tool.inputs, toolProp.key, null, tool)\"\n class=\"k-button\" [style]=\"{width: '100%'}\">\n {{toolProp.value.propertyCaption}}\n </p-button>\n }\n @if (toolProp.value.type == 'metaEditor' &&\n tool.inputs['componentRef']?.value?.component) {\n <wuic-metadata-editor [hardcodedDatasource]=\"tool.inputs['componentRef']?.value?.component\"\n [saveCallback]=\"metaEditorSave\"></wuic-metadata-editor>\n }\n @if (toolProp.value.type == 'boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"tool.inputs[toolProp.key]\" />\n }\n @if (toolProp.value.type == 'numberToArray') {\n <input style=\"width: 100%;\" type=\"number\" pInputText [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'txt_area') {\n <textarea style=\"width: 100%;\" [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\"></textarea>\n }\n @if (toolProp.value.type == 'string') {\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'color') {\n <p-colorPicker [ngModel]=\"getDesignerColorPickerHex(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorPickerHexChange($event, tool, toolProp)\" appendTo=\"body\" />\n <div class=\"designer-color-alpha-row\">\n <span class=\"designer-color-alpha-label\">{{ 'designer.alpha' | translate }}</span>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n </div>\n <input type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'dictionary' && !toolProp.value.async) {\n @if (toolProp.key == 'cssFile') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"availableProjectCssFiles\"\n [ngModel]=\"tool.inputs[toolProp.key]\" (ngModelChange)=\"onCssFileChanged($event, tool)\" />\n } @else if (toolProp.key == 'cssClass') {\n <p-multiSelect [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getCssClassOptionsForTool(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"onCssClassChanged($event, tool)\" [filter]=\"true\" [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"38\" scrollHeight=\"260px\" [showToggleAll]=\"false\" display=\"chip\"\n [maxSelectedLabels]=\"3\" [selectedItemsLabel]=\"'{0} selected'\">\n </p-multiSelect>\n } @else if ((tool.name == 'SELECT' || tool.name == 'MULTISELECT' || tool.name == 'UL') &&\n (toolProp.key == 'valueField' || toolProp.key == 'textField')) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else if (tool.name == 'LABEL' && toolProp.key == 'displayField') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n }\n @if (toolProp.value.type == 'dictionary' && toolProp.value.async) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (tool.name == 'DATAREPEATER' && toolProp.key == 'action' &&\n canOpenArchetypeConfigurator(tool)) {\n <button style=\"margin-top: 8px;\" (click)=\"openArchetypeConfigurator(tool)\">\n {{ 'designer.configure' | translate }} {{ (tool.inputs['action'] | async) || '' }}\n </button>\n }\n @if (toolProp.value.type == 'dropped-component-list') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [placeholder]=\"'select.component' | translate\" [options]=\"elementFilter(toolProp)\"\n [ngModel]=\"getDroppedComponentListModel(tool, toolProp)\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" optionLabel=\"uniqueName\"\n dataKey=\"uniqueName\" />\n }\n @if (toolProp.value.type == 'autocomplete') {\n <p-autoComplete [style]=\"{width: '100%'}\" appendTo=\"body\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\" [forceSelection]=\"true\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" [suggestions]=\"tool.suggestions\"\n [optionLabel]=\"toolProp.value.displayField\" [optionValue]=\"toolProp.value.valueField\"\n (completeMethod)=\"search($event, tool, toolProp)\" [dropdown]=\"true\" dropdownMode=\"current\"\n [multiple]=\"toolProp.value.multiple\" />\n }\n @if (toolProp.value.type == 'propertyTree' && shouldShowPropertyTree(tool)) {\n <ul>\n @for (item of $any(tool.inputs[toolProp.key] | async); track item) {\n <li style=\"padding: 0; margin: 0;\">\n @if (item.col && tool.inputs['customProps_' + (tool.inputs['action'] | async)])\n {\n <wuic-field-editor [field]=\"item.col\"\n [record]=\"tool.inputs['customProps_' + (tool.inputs['action'] | async)]\"\n [metaInfo]=\"tool.inputs['datasource']?.component?.value?.metaInfo\"\n [triggerProp]=\"tool.inputs['action']\"></wuic-field-editor>\n }\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n </details>\n }\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"hierarchy-panel\">\n <div class=\"hierarchy-panel-title\">{{ 'designer.canvas_hierarchy' | translate }}</div>\n <div class=\"hierarchy-tree-wrap\">\n <p-tree [value]=\"hierarchyTreeNodes\" [draggableNodes]=\"true\" [droppableNodes]=\"true\"\n selectionMode=\"single\" [(selection)]=\"hierarchySelection\"\n (onNodeSelect)=\"onHierarchyNodeSelect($event)\" draggableScope=\"designer-hierarchy\"\n droppableScope=\"designer-hierarchy\" (onNodeDrop)=\"onHierarchyNodeDrop($event)\">\n <ng-template let-node pTemplate=\"default\">\n <span class=\"hierarchy-node-label\" [class.active]=\"isHierarchyNodeSelected(node)\"\n (mouseenter)=\"onHierarchyNodeMouseEnter(node)\" (mouseleave)=\"onHierarchyNodeMouseLeave()\"\n (contextmenu)=\"showHierarchyNodeContextMenu($event, node, hierarchyCtxMenu)\">\n {{ node.label }}\n </span>\n </ng-template>\n </p-tree>\n </div>\n <p-contextMenu #hierarchyCtxMenu [model]=\"hierarchyCtxItems\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </ng-template>\n </p-splitter>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"col dashboard-design\" [class.designer-hide-datasources]=\"hideDatasourcesInCanvas\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n <p-contextMenu [target]=\"canvas\" [model]=\"ctxItems | async\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </div>\n <div class=\"designer-footer\">\n <div class=\"designer-footer-label\">{{ 'designer.items' | translate }}:</div>\n <div class=\"designer-footer-list\">\n @for (item of droppedDashboardItems; track item.uniqueName) {\n <button type=\"button\" class=\"designer-footer-item\" [class.active]=\"tool?.uniqueName === item?.uniqueName\"\n [attr.data-designer-unique-name]=\"item?.uniqueName\" (mouseenter)=\"setFooterHoverHighlight(item)\"\n (mouseleave)=\"clearFooterHoverHighlight()\" (click)=\"selectDashboardItem(item)\"\n (contextmenu)=\"showFooterContextMenu($event, item, footerCtxMenu)\">\n {{ getDroppedDashboardItemLabel(item) }}\n </button>\n }\n </div>\n <p-contextMenu #footerCtxMenu [model]=\"footerCtxItems\" appendTo=\"body\" />\n </div>\n</div>\n}\n\n@if (noEdit) {\n<wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n}\n\n<wuic-archetype-configurator [visible]=\"showArchetypeConfigurator\"\n (visibleChange)=\"onArchetypeConfigVisibleChange($event)\" [archetype]=\"getActiveArchetypeConfig()\"\n [metaInfo]=\"getArchetypeConfigMetaInfoForTool(archetypeConfigTool)\"\n [value]=\"getArchetypeConfigValueForTool(archetypeConfigTool)\" (applyConfig)=\"onArchetypeConfigApply($event)\">\n</wuic-archetype-configurator>\n\n<wuic-css-sheet-editor [visible]=\"showCssSheetEditor\" (visibleChange)=\"onCssSheetEditorVisibleChange($event)\"\n [sheets]=\"dashboardCssSheets\" [selectedSheetPath]=\"selectedCssEditorSheetPath\" [assetsFolder]=\"currentDashboardRoute\"\n [styleProps]=\"getCssEditorStyleProps()\" (applyEditor)=\"onCssSheetEditorApply($event)\">\n</wuic-css-sheet-editor>\n\n<p-dialog [header]=\"'designer.reopen_dashboard' | translate\" [(visible)]=\"reopenDashboardDialogVisible\" [modal]=\"true\"\n [closable]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [appendTo]=\"'body'\" [style]=\"{ width: '34rem' }\"\n (onHide)=\"cancelReopenDashboardDialog()\">\n <div class=\"route-popup-form\">\n <label for=\"designer-reopen-select\">{{ 'designer.existing_dashboards' | translate }}</label>\n <p-select inputId=\"designer-reopen-select\" [options]=\"savedDashboardOptions\" optionLabel=\"label\" optionValue=\"value\"\n [(ngModel)]=\"reopenDialogSelectedDashboardRoute\" [filter]=\"true\" [showClear]=\"true\" [style]=\"{ width: '100%' }\"\n appendTo=\"body\" />\n\n <div class=\"route-popup-actions\">\n <button type=\"button\" class=\"route-popup-btn\" (click)=\"cancelReopenDashboardDialog()\">{{ 'cancel' | translate\n }}</button>\n <button type=\"button\" class=\"route-popup-btn route-popup-btn-primary\" (click)=\"confirmReopenDashboardDialog()\"\n [disabled]=\"!reopenDialogSelectedDashboardRoute\">{{ 'designer.reopen' | translate }}</button>\n </div>\n </div>\n</p-dialog>", styles: [":host{display:block;width:100%;height:100%;min-width:0}.designer-layout{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%;min-height:0;width:100%;min-width:0}.designer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;border:1px solid inherit;border-radius:10px;background:transparent;padding:10px 12px;margin-bottom:8px}.designer-current-dashboard{min-width:220px;font-size:.86rem;font-weight:700;color:inherit}.designer-toolbar-spacer{flex:1 1 auto;min-width:16px}.designer-toolbar-menu-group{display:inline-flex}.designer-toolbar-menu-group button{height:32px;border:1px solid #2f67d8;border-radius:6px;background:#2f67d8;color:#fff;padding:0 10px;font-size:.82rem;cursor:pointer}.designer-toolbar-menu-group button[disabled]{opacity:.6;cursor:not-allowed}.designer-main{min-height:0;min-width:0;width:100%;overflow:hidden;direction:ltr}.designer-main-splitter{height:100%;width:100%;min-width:0;direction:ltr}.designer-toolbox{font-size:3rem}.designer-toolbox ul{list-style-type:none;padding:0;margin:0;width:100%}.designer-toolbox li{float:left;margin-left:5px;margin-top:5px}.designer-toolbox .tool-groups{display:block}.designer-toolbox .tool-group-item{float:none;margin-left:0;margin-top:10px;clear:both}.designer-toolbox .tool-group-title{font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;opacity:.7;margin:0 0 4px 6px}.designer-toolbox .tool-group-list{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.designer-toolbox .tool-group-list li{float:none;margin-left:0;margin-top:0}.designer-toolbox .props{font-size:1rem}.designer-toolbox .props li{float:none;padding-bottom:15px}.designer-toolbox .prop-section{width:100%;border:1px solid inherit;border-radius:6px;margin-top:8px;background:transparent;padding:6px 8px 2px}.designer-toolbox .prop-section-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;cursor:pointer;color:inherit;margin-bottom:6px}.palette-panel{height:100%;overflow:auto;width:100%}.designer-color-alpha-row{display:flex;align-items:center;gap:8px;margin-top:6px;margin-bottom:6px}.designer-color-alpha-label{font-size:.8rem;min-width:40px}.designer-color-alpha-row input[type=range]{flex:1 1 auto;min-width:90px}.designer-color-alpha-row input[type=number]{width:72px}.properties-panel{height:100%;overflow:hidden;width:100%;display:flex;flex-direction:column;min-height:0;padding-right:10px;padding-left:10px;padding-top:10px}.properties-panel>.designer-toolbox{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable}:host ::ng-deep .designer-left-bottom-splitter .p-splitterpanel{min-height:0!important;overflow:hidden!important}.hierarchy-panel{height:100%;width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;padding:6px}.hierarchy-panel-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#4b5563;margin:0 0 6px}.hierarchy-tree-wrap{flex:1 1 auto;min-height:0;border:1px solid inherit;border-radius:6px;background:transparent;overflow:auto;padding:4px}:host ::ng-deep .hierarchy-tree-wrap .p-tree{border:none;padding:0;height:100%;background:transparent}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content{border:1px solid transparent;border-radius:4px;background:transparent!important;color:inherit!important;padding:0!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight{border-color:transparent!important;background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content:hover{background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label{display:inline-block;border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label:hover{border-color:#94a3b8}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label.active{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight .hierarchy-node-label{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}.designer-hierarchy-node{cursor:pointer;border:1px solid transparent;border-radius:4px;padding:2px 6px;font-size:.82rem;line-height:1.25rem}.designer-hierarchy-node-label{cursor:pointer;display:inline-block;width:100%}.designer-hierarchy-node.active{border-color:#3b82f6;background:#eff6ff;color:#1d4ed8}.designer-footer-item.designer-tree-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px}.dashboard-design{height:100%;min-height:0;min-width:0;width:100%;overflow:auto;direction:ltr}:host ::ng-deep .dashboard-design.designer-hide-datasources .datasource-descriptor{display:none!important}.current-dashboard-title{font-weight:600;margin-right:12px}.designer-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid inherit;background:transparent;min-height:36px;flex:0 0 auto;width:100%;overflow:auto}.designer-footer-label{font-size:.85rem;font-weight:600;color:inherit;white-space:nowrap}.designer-footer-list{display:flex;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;width:100%}.designer-footer-item{border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer;white-space:nowrap}.designer-footer-item.active{border-color:#3b82f6;background:transparent;color:#1d4ed8}:host ::ng-deep .designer-footer-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px;box-shadow:inset 0 0 0 1px #3b82f6!important}.route-popup-form{display:flex;flex-direction:column;gap:8px}.route-popup-form label{font-size:.85rem;font-weight:600;color:#2f3f58}.route-popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.route-popup-btn{border:1px solid inherit;background:transparent;color:inherit;border-radius:6px;padding:7px 12px;font-size:.85rem;cursor:pointer}.route-popup-btn-primary{border-color:#2f67d8;background:#2f67d8;color:#fff}.p-colorpicker{border:1px solid black;border-radius:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i10.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i12.Draggable, selector: "[pDraggable]", inputs: ["pDraggable", "dragEffect", "dragHandle", "pDraggableDisabled"], outputs: ["onDragStart", "onDragEnd", "onDrag"] }, { kind: "directive", type: i12.Droppable, selector: "[pDroppable]", inputs: ["pDroppable", "pDroppableDisabled", "dropEffect"], outputs: ["onDragEnter", "onDragLeave", "onDrop"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i6.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ColorPickerModule }, { kind: "component", type: i2$1.ColorPicker, selector: "p-colorPicker, p-colorpicker, p-color-picker", inputs: ["styleClass", "showTransitionOptions", "hideTransitionOptions", "inline", "format", "tabindex", "inputId", "autoZIndex", "autofocus", "defaultColor", "appendTo", "overlayOptions", "motionOptions"], outputs: ["onChange", "onShow", "onHide"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i2$2.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "component", type: DashboardComponent, selector: "wuic-dashboard", inputs: ["dashboardElements"] }, { kind: "component", type: FieldEditorComponent, selector: "wuic-field-editor", inputs: ["datasource", "record", "field", "metaInfo", "readOnly", "isFilter", "hideLabel", "forceShowLabel", "operator", "nestedIndex", "triggerProp", "tabIndex", "onInlineCellValueChange"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: MetadataEditorComponent, selector: "wuic-metadata-editor", inputs: ["datasource", "hardcodedDatasource", "saveCallback", "hideReports", "hideRelatedTableActions", "hideRelatedColumnActions"] }, { kind: "ngmodule", type: MenubarModule }, { kind: "component", type: ArchetypeConfiguratorComponent, selector: "wuic-archetype-configurator", inputs: ["visible", "archetype", "metaInfo", "value"], outputs: ["visibleChange", "applyConfig"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i18.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: CssSheetEditorComponent, selector: "wuic-css-sheet-editor", inputs: ["visible", "sheets", "selectedSheetPath", "styleProps", "assetsFolder"], outputs: ["visibleChange", "applyEditor"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i9.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TreeModule }, { kind: "component", type: i4$1.Tree, selector: "p-tree", inputs: ["value", "selectionMode", "loadingMode", "selection", "styleClass", "contextMenu", "contextMenuSelectionMode", "contextMenuSelection", "draggableScope", "droppableScope", "draggableNodes", "droppableNodes", "metaKeySelection", "propagateSelectionUp", "propagateSelectionDown", "loading", "loadingIcon", "emptyMessage", "ariaLabel", "togglerAriaLabel", "ariaLabelledBy", "validateDrop", "filter", "filterInputAutoFocus", "filterBy", "filterMode", "filterOptions", "filterPlaceholder", "filteredNodes", "filterLocale", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "indentation", "_templateMap", "trackBy", "highlightOnSelect"], outputs: ["selectionChange", "contextMenuSelectionChange", "onNodeSelect", "onNodeUnselect", "onNodeExpand", "onNodeCollapse", "onNodeContextMenuSelect", "onNodeDoubleClick", "onNodeDrop", "onLazyLoad", "onScroll", "onScrollIndexChange", "onFilter"] }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
10863
11908
  }
10864
11909
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, decorators: [{
10865
11910
  type: Component,
10866
11911
  args: [{ selector: 'wuic-designer', imports: [NgClass, TranslateModule, AsyncPipe, FormsModule, SplitterModule, DragDropModule, SelectModule, ColorPickerModule, AutoCompleteModule, DashboardComponent, FieldEditorComponent, ContextMenuModule, MetadataEditorComponent, MenubarModule, ArchetypeConfiguratorComponent, ButtonModule, MultiSelectModule, CssSheetEditorComponent, MenuModule, DialogModule, TreeModule], providers: [TreeDragDropService], template: "<!-- <button pButton type=\"button\" (click)=\"loadDashboard()\" class=\"mr-2\">{{'load' | translate}}</button> -->\n@if (!noEdit) {\n<div class=\"designer-layout\">\n <div class=\"designer-toolbar\">\n <div class=\"designer-current-dashboard\">{{ currentDashboardTitle }}</div>\n\n <div class=\"designer-toolbar-spacer\"></div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"undo()\" [disabled]=\"!canUndo\">{{ 'designer.undo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"redo()\" [disabled]=\"!canRedo\">{{ 'designer.redo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"refreshGraphActionsMenuItems(); graphActionsMenu.toggle($event)\">{{ 'designer.actions_menu' | translate }}</button>\n <p-menu #graphActionsMenu [popup]=\"true\" [model]=\"graphActionsMenuItems\" appendTo=\"body\"></p-menu>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"openCurrentDashboardInNewTab()\" [disabled]=\"!currentDashboardRoute\">{{\n 'designer.open_current_dashboard' | translate }}</button>\n </div>\n </div>\n <div class=\"designer-main\">\n <p-splitter [style]=\"{ height: '100%' }\" [panelSizes]=\"[15, 85]\" styleClass=\"designer-main-splitter\">\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[35, 65]\"\n [minSizes]=\"[20, 30]\" styleClass=\"designer-left-splitter\">\n <ng-template pTemplate>\n <div class=\"col flex designer-toolbox palette-panel\">\n <ul class=\"tool-groups\">\n @for (toolGroup of availableToolGroups; track toolGroup.group) {\n <li class=\"tool-group-item\">\n <div class=\"tool-group-title\">{{ toolGroup.group }}</div>\n <ul class=\"tool-group-list\">\n @for (tool of toolGroup.tools; track tool) {\n <li pDraggable (onDragStart)=\"dragStart(tool)\" (onDragEnd)=\"dragEnd()\" (onDrag)=\"onDrag($event)\"\n (dblclick)=\"dropToolAtRoot(tool)\">\n <i [ngClass]=\"tool.icon\" [title]=\"tool.name\"></i>\n </li>\n }\n </ul>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[68, 32]\"\n [minSizes]=\"[35, 20]\" styleClass=\"designer-left-bottom-splitter\">\n <ng-template pTemplate>\n <div class=\"properties-panel\">\n <p-select [style]=\"{width: '100%'}\" [options]=\"flattenedDashboardElements\" [(ngModel)]=\"tool\"\n optionLabel=\"uniqueName\" dataKey=\"componentId\" appendTo=\"body\" />\n <div class=\"designer-toolbox\">\n\n <span style=\"width: 100%;\">\n <input type=\"text\" style=\"width: calc(100% - 30px);\" [(ngModel)]=\"propFilter\"\n [placeholder]=\"'prop.filter' | translate\" />\n <button style=\"width: 25px\" (click)=\"clearPropFilter()\">X</button>\n </span>\n\n @for (section of getToolPropertySections(tool, propFilter); track section.key) {\n <details class=\"prop-section\" [open]=\"section.open\"\n (toggle)=\"onPropertySectionToggle(tool, section.key, $event)\">\n <summary class=\"prop-section-title\">{{ section.label }}</summary>\n <ul class=\"props\">\n @for (toolProp of section.props; track toolProp.key) {\n <li>\n @if (!toolProp.value.hideCaption) {\n <span>{{ toolProp.value?.propertyCaption ||\n toolProp.key[0].toUpperCase() + toolProp.key.slice(1)\n }}</span>\n }\n <br />\n @if (toolProp.value.type == 'button') {\n <p-button (click)=\"toolProp.value.callback(tool.inputs, toolProp.key, null, tool)\"\n class=\"k-button\" [style]=\"{width: '100%'}\">\n {{toolProp.value.propertyCaption}}\n </p-button>\n }\n @if (toolProp.value.type == 'metaEditor' &&\n tool.inputs['componentRef']?.value?.component) {\n <wuic-metadata-editor [hardcodedDatasource]=\"tool.inputs['componentRef']?.value?.component\"\n [saveCallback]=\"metaEditorSave\"></wuic-metadata-editor>\n }\n @if (toolProp.value.type == 'boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"tool.inputs[toolProp.key]\" />\n }\n @if (toolProp.value.type == 'numberToArray') {\n <input style=\"width: 100%;\" type=\"number\" pInputText [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'txt_area') {\n <textarea style=\"width: 100%;\" [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\"></textarea>\n }\n @if (toolProp.value.type == 'string') {\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'color') {\n <p-colorPicker [ngModel]=\"getDesignerColorPickerHex(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorPickerHexChange($event, tool, toolProp)\" appendTo=\"body\" />\n <div class=\"designer-color-alpha-row\">\n <span class=\"designer-color-alpha-label\">{{ 'designer.alpha' | translate }}</span>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n </div>\n <input type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'dictionary' && !toolProp.value.async) {\n @if (toolProp.key == 'cssFile') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"availableProjectCssFiles\"\n [ngModel]=\"tool.inputs[toolProp.key]\" (ngModelChange)=\"onCssFileChanged($event, tool)\" />\n } @else if (toolProp.key == 'cssClass') {\n <p-multiSelect [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getCssClassOptionsForTool(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"onCssClassChanged($event, tool)\" [filter]=\"true\" [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"38\" scrollHeight=\"260px\" [showToggleAll]=\"false\" display=\"chip\"\n [maxSelectedLabels]=\"3\" [selectedItemsLabel]=\"'{0} selected'\">\n </p-multiSelect>\n } @else if ((tool.name == 'SELECT' || tool.name == 'MULTISELECT' || tool.name == 'UL') &&\n (toolProp.key == 'valueField' || toolProp.key == 'textField')) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else if (tool.name == 'LABEL' && toolProp.key == 'displayField') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n }\n @if (toolProp.value.type == 'dictionary' && toolProp.value.async) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (tool.name == 'DATAREPEATER' && toolProp.key == 'action' &&\n canOpenArchetypeConfigurator(tool)) {\n <button style=\"margin-top: 8px;\" (click)=\"openArchetypeConfigurator(tool)\">\n {{ 'designer.configure' | translate }} {{ (tool.inputs['action'] | async) || '' }}\n </button>\n }\n @if (toolProp.value.type == 'dropped-component-list') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [placeholder]=\"'select.component' | translate\" [options]=\"elementFilter(toolProp)\"\n [ngModel]=\"getDroppedComponentListModel(tool, toolProp)\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" optionLabel=\"uniqueName\"\n dataKey=\"uniqueName\" />\n }\n @if (toolProp.value.type == 'autocomplete') {\n <p-autoComplete [style]=\"{width: '100%'}\" appendTo=\"body\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\" [forceSelection]=\"true\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" [suggestions]=\"tool.suggestions\"\n [optionLabel]=\"toolProp.value.displayField\" [optionValue]=\"toolProp.value.valueField\"\n (completeMethod)=\"search($event, tool, toolProp)\" [dropdown]=\"true\" dropdownMode=\"current\"\n [multiple]=\"toolProp.value.multiple\" />\n }\n @if (toolProp.value.type == 'propertyTree' && shouldShowPropertyTree(tool)) {\n <ul>\n @for (item of $any(tool.inputs[toolProp.key] | async); track item) {\n <li style=\"padding: 0; margin: 0;\">\n @if (item.col && tool.inputs['customProps_' + (tool.inputs['action'] | async)])\n {\n <wuic-field-editor [field]=\"item.col\"\n [record]=\"tool.inputs['customProps_' + (tool.inputs['action'] | async)]\"\n [metaInfo]=\"tool.inputs['datasource']?.component?.value?.metaInfo\"\n [triggerProp]=\"tool.inputs['action']\"></wuic-field-editor>\n }\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n </details>\n }\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"hierarchy-panel\">\n <div class=\"hierarchy-panel-title\">{{ 'designer.canvas_hierarchy' | translate }}</div>\n <div class=\"hierarchy-tree-wrap\">\n <p-tree [value]=\"hierarchyTreeNodes\" [draggableNodes]=\"true\" [droppableNodes]=\"true\"\n selectionMode=\"single\" [(selection)]=\"hierarchySelection\"\n (onNodeSelect)=\"onHierarchyNodeSelect($event)\" draggableScope=\"designer-hierarchy\"\n droppableScope=\"designer-hierarchy\" (onNodeDrop)=\"onHierarchyNodeDrop($event)\">\n <ng-template let-node pTemplate=\"default\">\n <span class=\"hierarchy-node-label\" [class.active]=\"isHierarchyNodeSelected(node)\"\n (mouseenter)=\"onHierarchyNodeMouseEnter(node)\" (mouseleave)=\"onHierarchyNodeMouseLeave()\"\n (contextmenu)=\"showHierarchyNodeContextMenu($event, node, hierarchyCtxMenu)\">\n {{ node.label }}\n </span>\n </ng-template>\n </p-tree>\n </div>\n <p-contextMenu #hierarchyCtxMenu [model]=\"hierarchyCtxItems\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </ng-template>\n </p-splitter>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"col dashboard-design\" [class.designer-hide-datasources]=\"hideDatasourcesInCanvas\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n <p-contextMenu [target]=\"canvas\" [model]=\"ctxItems | async\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </div>\n <div class=\"designer-footer\">\n <div class=\"designer-footer-label\">{{ 'designer.items' | translate }}:</div>\n <div class=\"designer-footer-list\">\n @for (item of droppedDashboardItems; track item.uniqueName) {\n <button type=\"button\" class=\"designer-footer-item\" [class.active]=\"tool?.uniqueName === item?.uniqueName\"\n [attr.data-designer-unique-name]=\"item?.uniqueName\" (mouseenter)=\"setFooterHoverHighlight(item)\"\n (mouseleave)=\"clearFooterHoverHighlight()\" (click)=\"selectDashboardItem(item)\"\n (contextmenu)=\"showFooterContextMenu($event, item, footerCtxMenu)\">\n {{ getDroppedDashboardItemLabel(item) }}\n </button>\n }\n </div>\n <p-contextMenu #footerCtxMenu [model]=\"footerCtxItems\" appendTo=\"body\" />\n </div>\n</div>\n}\n\n@if (noEdit) {\n<wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n}\n\n<wuic-archetype-configurator [visible]=\"showArchetypeConfigurator\"\n (visibleChange)=\"onArchetypeConfigVisibleChange($event)\" [archetype]=\"getActiveArchetypeConfig()\"\n [metaInfo]=\"getArchetypeConfigMetaInfoForTool(archetypeConfigTool)\"\n [value]=\"getArchetypeConfigValueForTool(archetypeConfigTool)\" (applyConfig)=\"onArchetypeConfigApply($event)\">\n</wuic-archetype-configurator>\n\n<wuic-css-sheet-editor [visible]=\"showCssSheetEditor\" (visibleChange)=\"onCssSheetEditorVisibleChange($event)\"\n [sheets]=\"dashboardCssSheets\" [selectedSheetPath]=\"selectedCssEditorSheetPath\" [assetsFolder]=\"currentDashboardRoute\"\n [styleProps]=\"getCssEditorStyleProps()\" (applyEditor)=\"onCssSheetEditorApply($event)\">\n</wuic-css-sheet-editor>\n\n<p-dialog [header]=\"'designer.reopen_dashboard' | translate\" [(visible)]=\"reopenDashboardDialogVisible\" [modal]=\"true\"\n [closable]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [appendTo]=\"'body'\" [style]=\"{ width: '34rem' }\"\n (onHide)=\"cancelReopenDashboardDialog()\">\n <div class=\"route-popup-form\">\n <label for=\"designer-reopen-select\">{{ 'designer.existing_dashboards' | translate }}</label>\n <p-select inputId=\"designer-reopen-select\" [options]=\"savedDashboardOptions\" optionLabel=\"label\" optionValue=\"value\"\n [(ngModel)]=\"reopenDialogSelectedDashboardRoute\" [filter]=\"true\" [showClear]=\"true\" [style]=\"{ width: '100%' }\"\n appendTo=\"body\" />\n\n <div class=\"route-popup-actions\">\n <button type=\"button\" class=\"route-popup-btn\" (click)=\"cancelReopenDashboardDialog()\">{{ 'cancel' | translate\n }}</button>\n <button type=\"button\" class=\"route-popup-btn route-popup-btn-primary\" (click)=\"confirmReopenDashboardDialog()\"\n [disabled]=\"!reopenDialogSelectedDashboardRoute\">{{ 'designer.reopen' | translate }}</button>\n </div>\n </div>\n</p-dialog>", styles: [":host{display:block;width:100%;height:100%;min-width:0}.designer-layout{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%;min-height:0;width:100%;min-width:0}.designer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;border:1px solid inherit;border-radius:10px;background:transparent;padding:10px 12px;margin-bottom:8px}.designer-current-dashboard{min-width:220px;font-size:.86rem;font-weight:700;color:inherit}.designer-toolbar-spacer{flex:1 1 auto;min-width:16px}.designer-toolbar-menu-group{display:inline-flex}.designer-toolbar-menu-group button{height:32px;border:1px solid #2f67d8;border-radius:6px;background:#2f67d8;color:#fff;padding:0 10px;font-size:.82rem;cursor:pointer}.designer-toolbar-menu-group button[disabled]{opacity:.6;cursor:not-allowed}.designer-main{min-height:0;min-width:0;width:100%;overflow:hidden;direction:ltr}.designer-main-splitter{height:100%;width:100%;min-width:0;direction:ltr}.designer-toolbox{font-size:3rem}.designer-toolbox ul{list-style-type:none;padding:0;margin:0;width:100%}.designer-toolbox li{float:left;margin-left:5px;margin-top:5px}.designer-toolbox .tool-groups{display:block}.designer-toolbox .tool-group-item{float:none;margin-left:0;margin-top:10px;clear:both}.designer-toolbox .tool-group-title{font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;opacity:.7;margin:0 0 4px 6px}.designer-toolbox .tool-group-list{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.designer-toolbox .tool-group-list li{float:none;margin-left:0;margin-top:0}.designer-toolbox .props{font-size:1rem}.designer-toolbox .props li{float:none;padding-bottom:15px}.designer-toolbox .prop-section{width:100%;border:1px solid inherit;border-radius:6px;margin-top:8px;background:transparent;padding:6px 8px 2px}.designer-toolbox .prop-section-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;cursor:pointer;color:inherit;margin-bottom:6px}.palette-panel{height:100%;overflow:auto;width:100%}.designer-color-alpha-row{display:flex;align-items:center;gap:8px;margin-top:6px;margin-bottom:6px}.designer-color-alpha-label{font-size:.8rem;min-width:40px}.designer-color-alpha-row input[type=range]{flex:1 1 auto;min-width:90px}.designer-color-alpha-row input[type=number]{width:72px}.properties-panel{height:100%;overflow:hidden;width:100%;display:flex;flex-direction:column;min-height:0;padding-right:10px;padding-left:10px;padding-top:10px}.properties-panel>.designer-toolbox{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable}:host ::ng-deep .designer-left-bottom-splitter .p-splitterpanel{min-height:0!important;overflow:hidden!important}.hierarchy-panel{height:100%;width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;padding:6px}.hierarchy-panel-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#4b5563;margin:0 0 6px}.hierarchy-tree-wrap{flex:1 1 auto;min-height:0;border:1px solid inherit;border-radius:6px;background:transparent;overflow:auto;padding:4px}:host ::ng-deep .hierarchy-tree-wrap .p-tree{border:none;padding:0;height:100%;background:transparent}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content{border:1px solid transparent;border-radius:4px;background:transparent!important;color:inherit!important;padding:0!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight{border-color:transparent!important;background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content:hover{background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label{display:inline-block;border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label:hover{border-color:#94a3b8}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label.active{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight .hierarchy-node-label{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}.designer-hierarchy-node{cursor:pointer;border:1px solid transparent;border-radius:4px;padding:2px 6px;font-size:.82rem;line-height:1.25rem}.designer-hierarchy-node-label{cursor:pointer;display:inline-block;width:100%}.designer-hierarchy-node.active{border-color:#3b82f6;background:#eff6ff;color:#1d4ed8}.designer-footer-item.designer-tree-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px}.dashboard-design{height:100%;min-height:0;min-width:0;width:100%;overflow:auto;direction:ltr}:host ::ng-deep .dashboard-design.designer-hide-datasources .datasource-descriptor{display:none!important}.current-dashboard-title{font-weight:600;margin-right:12px}.designer-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid inherit;background:transparent;min-height:36px;flex:0 0 auto;width:100%;overflow:auto}.designer-footer-label{font-size:.85rem;font-weight:600;color:inherit;white-space:nowrap}.designer-footer-list{display:flex;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;width:100%}.designer-footer-item{border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer;white-space:nowrap}.designer-footer-item.active{border-color:#3b82f6;background:transparent;color:#1d4ed8}:host ::ng-deep .designer-footer-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px;box-shadow:inset 0 0 0 1px #3b82f6!important}.route-popup-form{display:flex;flex-direction:column;gap:8px}.route-popup-form label{font-size:.85rem;font-weight:600;color:#2f3f58}.route-popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.route-popup-btn{border:1px solid inherit;background:transparent;color:inherit;border-radius:6px;padding:7px 12px;font-size:.85rem;cursor:pointer}.route-popup-btn-primary{border-color:#2f67d8;background:#2f67d8;color:#fff}.p-colorpicker{border:1px solid black;border-radius:4px}\n"] }]
10867
- }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: DataProviderService }, { type: MetadataProviderService }, { type: UserInfoService }, { type: i0.ChangeDetectorRef }, { type: TranslationManagerService }, { type: MetadataEditorService }, { type: WorkflowRuntimeMetadataService }], propDecorators: { onWindowHashChange: [{
11912
+ }], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: DataProviderService }, { type: MetadataProviderService }, { type: UserInfoService }, { type: i0.ChangeDetectorRef }, { type: TranslationManagerService }, { type: MetadataEditorService }, { type: ChatbotHostRegistryService }, { type: WorkflowRuntimeMetadataService }], propDecorators: { onWindowHashChange: [{
10868
11913
  type: HostListener,
10869
11914
  args: ['window:hashchange']
10870
11915
  }], onWindowBeforeUnload: [{
@@ -10888,4 +11933,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10888
11933
  }] } });
10889
11934
 
10890
11935
  export { DesignerComponent };
10891
- //# sourceMappingURL=wuic-framework-lib-designer.component-pW1YK-oY.mjs.map
11936
+ //# sourceMappingURL=wuic-framework-lib-designer.component-DA7abiLW.mjs.map