wuic-framework-lib 1.2.0 → 1.3.0

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-C02jeFGy.mjs} +2 -2
  2. package/fesm2022/{wuic-framework-lib-code-editor.component-4N95qt3D.mjs → wuic-framework-lib-code-editor.component-BZNLr0Jj.mjs} +59 -2
  3. package/fesm2022/{wuic-framework-lib-designer.component-pW1YK-oY.mjs → wuic-framework-lib-designer.component-N_CfbGnr.mjs} +986 -17
  4. package/fesm2022/{wuic-framework-lib-first-run-wizard.component-CaGePYLi.mjs → wuic-framework-lib-first-run-wizard.component-lW7-xMm3.mjs} +7 -61
  5. package/fesm2022/{wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs → wuic-framework-lib-framework-docs.component-CGQbmSkv.mjs} +3716 -347
  6. package/fesm2022/{wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs → wuic-framework-lib-pivot-builder.component-0gJz9RWr.mjs} +5 -5
  7. package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-Dm9GTZ07.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-C9LvmFsL.mjs} +2 -2
  8. package/fesm2022/{wuic-framework-lib-report-designer.component-FiAwNkPM.mjs → wuic-framework-lib-report-designer.component-DcpdzFjh.mjs} +2 -2
  9. package/fesm2022/{wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs → wuic-framework-lib-report-viewer.component-BHpbGpp9.mjs} +2 -2
  10. package/fesm2022/{wuic-framework-lib-scheduler-list.component-3E5m-9Qd.mjs → wuic-framework-lib-scheduler-list.component-DHoukjf-.mjs} +2 -2
  11. package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs → wuic-framework-lib-spreadsheet-list-sf.component-VsaXepUC.mjs} +2 -2
  12. package/fesm2022/{wuic-framework-lib-workflow-designer.component-4RC2EkGA.mjs → wuic-framework-lib-workflow-designer.component-epj3oKq3.mjs} +2 -2
  13. package/fesm2022/{wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs → wuic-framework-lib-wuic-framework-lib-Deudd4MY.mjs} +2186 -144
  14. package/fesm2022/wuic-framework-lib.mjs +1 -1
  15. package/package.json +1 -1
  16. package/types/wuic-framework-lib.d.ts +829 -11
@@ -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-Deudd4MY.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,922 @@ 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
+ async injectLayoutNode(node, parentUniqueName) {
6096
+ const toolName = String(node?.tool_name || '').trim();
6097
+ if (!toolName)
6098
+ throw new Error('Nodo senza tool_name');
6099
+ const template = this.availableTools.find(t => t.name === toolName);
6100
+ if (!template) {
6101
+ throw new Error(`Tool '${toolName}' non trovato in availableTools (controlla docs/pages/_internal/designer-tool-catalog.md)`);
6102
+ }
6103
+ if (template.hide) {
6104
+ throw new Error(`Tool '${toolName}' ha hide=true: non puo' essere proposto come top-level. Sara' creato automaticamente dal parent (es. TABLE -> TR/TD).`);
6105
+ }
6106
+ // CRITICAL: il drop() canonico (riga 5847+) recovera le function references
6107
+ // (onDrop, ctxItems, inputProps[*].conditional/converter/callback) leggendole
6108
+ // da `this.draggedPayload`. Se passiamo un clone via JSON.parse(JSON.stringify)
6109
+ // tutte le function vengono perse -> dropTable/dropSplitter/dropAccordion NON
6110
+ // vengono mai chiamati -> niente TR/TD auto-generati per TABLE/SPLITTER/ACCORDION.
6111
+ //
6112
+ // Pattern corretto: usare il template ORIGINALE (con function vive) come
6113
+ // draggedPayload. Override temporaneo degli inputs + restore in finally,
6114
+ // cosi' inject ripetuti non inquinano i defaults della palette.
6115
+ const inputsBackup = template.inputs ? { ...template.inputs } : {};
6116
+ // Override degli inputs SCALARI (string/number/boolean/array) PRIMA
6117
+ // del drop(). I binding di tipo `dropped-component-list` /
6118
+ // `dropped-component` vanno invece applicati POST-drop sull'istanza
6119
+ // appena iniettata, perche':
6120
+ // 1. il drop() canonico fa `JSON.parse(JSON.stringify(template))`
6121
+ // sul currentTool -> qualsiasi BehaviorSubject infilato in
6122
+ // template.inputs verrebbe serializzato come `{}` (perde valore
6123
+ // e prototype) -> hydration distrutta;
6124
+ // 2. pre-drop l'oggetto target potrebbe non esistere ancora
6125
+ // (es. caso edge in cui il LLM riferisce un componente che
6126
+ // verra' creato in un nodo successivo del layout - non
6127
+ // supportato, ma diagnosticabile post-drop con messaggio chiaro).
6128
+ //
6129
+ // Estraiamo qui le keys "drop-binding" (per applicarle dopo) e teniamo
6130
+ // SOLO gli scalari per il pre-drop Object.assign.
6131
+ const droppedBindingKeys = [];
6132
+ if (node.inputs && typeof node.inputs === 'object' && template.inputProps) {
6133
+ for (const key of Object.keys(node.inputs)) {
6134
+ const propDef = template.inputProps[key];
6135
+ if (propDef && (propDef.type === 'dropped-component-list' || propDef.type === 'dropped-component')) {
6136
+ droppedBindingKeys.push(key);
6137
+ }
6138
+ }
6139
+ }
6140
+ if (node.inputs && typeof node.inputs === 'object' && template.inputs) {
6141
+ for (const key of Object.keys(node.inputs)) {
6142
+ if (droppedBindingKeys.includes(key))
6143
+ continue;
6144
+ template.inputs[key] = node.inputs[key];
6145
+ }
6146
+ }
6147
+ // DEFAULTS auto: se il tool e' un CONTAINER block-level (TABLE/DIV/
6148
+ // SPLITTER/ACCORDION/TABVIEW/TABPANEL) e l'utente non ha specificato
6149
+ // width/height in node.inputs, settali a '100%' cosi' riempie lo slot
6150
+ // ospitante (TD/SPLITTER-AREA/canvas). Coerente con l'aspettativa
6151
+ // utente: "crea TABLE 2x2" -> occupa il canvas, non un quadratino.
6152
+ //
6153
+ // ESCLUSI (inline-level / fit-content tools): LABEL/SPAN/Hx/ANCHOR/
6154
+ // INPUT/BUTTON/CHECKBOX/DATE/SELECT/MULTISELECT/IMG/IFRAME/UL/KPI/
6155
+ // SEPARATOR/HR/TEXTAREA. Forzando width:100% su questi tool si
6156
+ // rompe il layout del parent (es. LABEL width:100% dentro un TD
6157
+ // costringe il browser a espandere il TD perche' table-layout:auto
6158
+ // non sa la dimensione finale -> squilibrio colonne).
6159
+ //
6160
+ // Sono OUT anche per def:
6161
+ // - tool senza width/height in inputProps (DATASOURCE, DATAREPEATER,
6162
+ // FILTERBAR, PAGER, TR, TD, SPLITTER-AREA, ACCORDION-AREA).
6163
+ // - tool dove l'utente ha specificato esplicitamente width/height.
6164
+ const BLOCK_CONTAINER_TOOLS = new Set([
6165
+ 'TABLE', 'DIV', 'SPLITTER', 'ACCORDION', 'TABVIEW', 'TABPANEL'
6166
+ ]);
6167
+ const explicitInputs = node.inputs || {};
6168
+ if (template.inputs && template.inputProps && BLOCK_CONTAINER_TOOLS.has(toolName)) {
6169
+ if ('width' in template.inputProps && !('width' in explicitInputs)) {
6170
+ template.inputs['width'] = '100%';
6171
+ }
6172
+ if ('height' in template.inputProps && !('height' in explicitInputs)) {
6173
+ template.inputs['height'] = '100%';
6174
+ }
6175
+ }
6176
+ // DEFAULT B: per TABLE forziamo `table-layout: fixed` se non specificato
6177
+ // dall'utente. In modalita' fixed le colonne sono equipartite a priori
6178
+ // (cols * (width / cols)) e NON dipendono dal contenuto. Cosi' iniettare
6179
+ // contenuti di dimensioni diverse nelle celle (LABEL "ciao" + altro)
6180
+ // non causa squilibrio. Default storico del framework era 'auto' ma per
6181
+ // le table create dal RAG-chatbot 'fixed' e' molto piu' robusto.
6182
+ if (toolName === 'TABLE' && template.inputs
6183
+ && template.inputProps && 'tableLayout' in template.inputProps
6184
+ && !('tableLayout' in explicitInputs)) {
6185
+ template.inputs['tableLayout'] = 'fixed';
6186
+ }
6187
+ try {
6188
+ // Stash il template ORIGINALE (preserva function references).
6189
+ // Il drop() interno fara' un JSON.parse(JSON.stringify(draggedPayload))
6190
+ // per il currentTool, e poi ripristinera' currentTool.onDrop =
6191
+ // draggedPayload.onDrop dalla nostra istanza viva.
6192
+ this.draggedPayload = template;
6193
+ const fakeEvent = { toElement: null };
6194
+ if (parentUniqueName) {
6195
+ const fakeEl = document.createElement('div');
6196
+ fakeEl.setAttribute('id', parentUniqueName);
6197
+ fakeEvent.toElement = fakeEl;
6198
+ }
6199
+ this.drop(fakeEvent);
6200
+ }
6201
+ finally {
6202
+ // Restore SEMPRE, anche su exception in drop(), cosi' il template della
6203
+ // palette torna ai defaults originali (rows=2, cols=2, route=null, ecc.)
6204
+ if (template.inputs) {
6205
+ // Rimuovi le chiavi che node.inputs aveva aggiunto e non c'erano nel backup
6206
+ for (const k of Object.keys(template.inputs)) {
6207
+ if (!Object.prototype.hasOwnProperty.call(inputsBackup, k)) {
6208
+ delete template.inputs[k];
6209
+ }
6210
+ }
6211
+ // Ripristina i valori delle chiavi originali
6212
+ Object.assign(template.inputs, inputsBackup);
6213
+ }
6214
+ }
6215
+ // L'ultimo nodo pushed e' quello appena iniettato. Recuperalo dal flatten
6216
+ // tree (aggiornato da drop()) per ottenere l'uniqueName generato.
6217
+ const justInjected = this.findLastInjectedNode(toolName, parentUniqueName);
6218
+ const injectedUniqueName = justInjected?.uniqueName;
6219
+ // EQUIPARTITION VERTICALE per TABLE/SPLITTER/ACCORDION iniettati via RAG.
6220
+ // `table-layout: fixed` equipartisce solo le colonne (asse orizzontale);
6221
+ // le righe TR (senza height esplicita) restano content-driven -> una
6222
+ // riga con contenuto alto si espande, schiacciando le altre.
6223
+ //
6224
+ // Fix: dopo dropTable/dropSplitter/dropAccordion settiamo height/size
6225
+ // percentuale equa sui figli auto-create.
6226
+ // - TABLE: ogni TR riceve height = 100/rows %
6227
+ // - SPLITTER: ogni SPLITTER-AREA riceve size = 100/areas
6228
+ // (gia' fatto da dropSplitter con size=50 hardcoded, qui rigiamo
6229
+ // per essere coerenti con N != 2)
6230
+ // - ACCORDION: items non hanno height (sono collassabili), skip
6231
+ if (justInjected && Array.isArray(justInjected.nestedComponents) && justInjected.nestedComponents.length > 0) {
6232
+ const childCount = justInjected.nestedComponents.length;
6233
+ if (toolName === 'TABLE') {
6234
+ const rowPct = `${Math.floor(10000 / childCount) / 100}%`;
6235
+ for (const tr of justInjected.nestedComponents) {
6236
+ if (tr?.inputs)
6237
+ tr.inputs['height'] = rowPct;
6238
+ }
6239
+ }
6240
+ else if (toolName === 'SPLITTER') {
6241
+ // SPLITTER-AREA.size e' un number (% o px in base a inputs.unit)
6242
+ const sizePct = Math.floor(10000 / childCount) / 100;
6243
+ for (const area of justInjected.nestedComponents) {
6244
+ if (area?.inputs)
6245
+ area.inputs['size'] = sizePct;
6246
+ }
6247
+ }
6248
+ }
6249
+ // HYDRATION POST-DROP dei binding `dropped-component-list` /
6250
+ // `dropped-component`. Il LLM passa una STRINGA (uniqueName diretto
6251
+ // o placeholder `<DATASOURCE-N>`); il template runtime aspetta un
6252
+ // OGGETTO `{uniqueName, [asyncPath]: BehaviorSubject(liveComp)}`
6253
+ // (stessa shape costruita da setValue() canonico riga ~4972).
6254
+ //
6255
+ // Senza hydration: `inputs.datasource = "DATASOURCE__15"` (stringa),
6256
+ // il tag `[datasource]="inputs.datasource?.component"` legge undefined,
6257
+ // DATAREPEATER renderizza vuoto = grid senza dati e senza colonne.
6258
+ //
6259
+ // POST-drop perche':
6260
+ // 1. `drop()` clona via JSON un eventuale BehaviorSubject pre-drop;
6261
+ // 2. il target referenziato (es. DATASOURCE_15) deve esistere gia'
6262
+ // in flattenedDashboardElements (= iniettato in nodo precedente
6263
+ // del layout o gia' nel canvas).
6264
+ if (justInjected && justInjected.inputs && droppedBindingKeys.length > 0 && node.inputs) {
6265
+ for (const key of droppedBindingKeys) {
6266
+ const propDef = template.inputProps?.[key];
6267
+ if (!propDef)
6268
+ continue;
6269
+ const raw = node.inputs[key];
6270
+ if (raw === null || raw === undefined)
6271
+ continue;
6272
+ // Se gia' oggetto (LLM mandasse direttamente {uniqueName,component})
6273
+ // o BehaviorSubject -> pass-through. Solo stringhe vengono risolte.
6274
+ if (typeof raw !== 'string') {
6275
+ justInjected.inputs[key] = raw;
6276
+ continue;
6277
+ }
6278
+ const resolved = (raw.startsWith('<') && raw.endsWith('>'))
6279
+ ? this.resolveCrossTreePlaceholder(raw)
6280
+ : raw.trim();
6281
+ if (!resolved) {
6282
+ throw new Error(`Binding '${toolName}.${key}': placeholder '${raw}' non risolvibile. `
6283
+ + `Pattern atteso: '<DATASOURCE-N>' (N=indice 0-based del DATASOURCE iniettato). `
6284
+ + `Assicurati che il componente target sia stato iniettato PRIMA nel layout[].`);
6285
+ }
6286
+ const refElement = (this.flattenedDashboardElements || []).find((n) => n?.uniqueName === resolved);
6287
+ if (!refElement) {
6288
+ throw new Error(`Binding '${toolName}.${key}': componente '${resolved}' non presente nel canvas`);
6289
+ }
6290
+ const filter = propDef.filter;
6291
+ if (filter && refElement.name && String(refElement.name).toUpperCase() !== String(filter).toUpperCase()) {
6292
+ throw new Error(`Binding '${toolName}.${key}': atteso ${filter}, trovato ${refElement.name} (${resolved})`);
6293
+ }
6294
+ // Estrai snapshot del liveComp dal componentRef del DATASOURCE
6295
+ // referenziato. Stessa logica di setValue() canonico riga 4977-4982.
6296
+ //
6297
+ // CRITICAL ASYNC: in scenario "inject programmatico" (chatbot RAG),
6298
+ // il DATASOURCE e il DATAREPEATER vengono droppati nello stesso
6299
+ // tick. Il componente Angular <wuic-data-source> NON e' ancora
6300
+ // materializzato (ngAfterViewInit non e' partito) -> il suo
6301
+ // `componentRef.next({component: this, ...})` (riga 838 di
6302
+ // data-source.component.ts) succede SOLO al prossimo CD tick.
6303
+ //
6304
+ // Quindi al momento dell'hydration `compRef.value?.component` e' null.
6305
+ // Se ci fermassimo a snapshot, creeremmo `BehaviorSubject(null)` e
6306
+ // il DATAREPEATER resterebbe vuoto FOREVER (il BS interno del repeater
6307
+ // non sa che deve ri-leggere quando il DATASOURCE finisce l'init).
6308
+ //
6309
+ // Fix: subscribe al compRef BS originale del DATASOURCE e propagare
6310
+ // al compBs locale del repeater quando emette. Cosi' appena il
6311
+ // DataSourceComponent si materializza, il binding si chiude e la
6312
+ // grid renderizza i dati.
6313
+ const compRef = refElement.inputs?.componentRef;
6314
+ const liveComp = (compRef instanceof BehaviorSubject)
6315
+ ? (compRef.value?.component ?? compRef.value)
6316
+ : (compRef?.value?.component ?? compRef?.component ?? compRef ?? null);
6317
+ const asyncPath = propDef.asyncPath || 'component';
6318
+ const compBs = new BehaviorSubject(liveComp);
6319
+ if (compRef instanceof BehaviorSubject) {
6320
+ // Subscribe sul compRef del DATASOURCE: ogni volta che il payload
6321
+ // viene emesso (es. quando il componente Angular fa next con
6322
+ // {component: this} in ngAfterViewInit), propaghiamo al BS del
6323
+ // repeater. La sottoscrizione vive finche' il designer e' aperto;
6324
+ // viene cleaned-up via `this.subs.add(...)` se la subs collection
6325
+ // esiste, altrimenti scope al componente lifecycle.
6326
+ const sub = compRef.subscribe((emitted) => {
6327
+ const newComp = (emitted && typeof emitted === 'object')
6328
+ ? (emitted.component ?? emitted)
6329
+ : null;
6330
+ if (newComp && newComp !== compBs.value) {
6331
+ compBs.next(newComp);
6332
+ }
6333
+ });
6334
+ try {
6335
+ const selfAny = this;
6336
+ if (selfAny.subs && typeof selfAny.subs.add === 'function') {
6337
+ selfAny.subs.add(sub);
6338
+ }
6339
+ }
6340
+ catch { /* subs collection non disponibile, sub continuera' a vivere col designer */ }
6341
+ }
6342
+ justInjected.inputs[key] = {
6343
+ uniqueName: refElement.uniqueName,
6344
+ [asyncPath]: compBs
6345
+ };
6346
+ }
6347
+ }
6348
+ // SIDE-EFFECT DATAREPEATER: dopo l'inject e l'hydration di
6349
+ // `inputs.datasource`, il property panel canonico (setValue riga ~4997)
6350
+ // chiama `propertyTreeBuilder(action, inputs)` per popolare la lista
6351
+ // colonne del repeater (action='list' -> grid). Replichiamo qui per
6352
+ // chiudere il binding end-to-end: senza questo, l'archetipo 'list'
6353
+ // non sa quali colonne mostrare e la grid resta vuota anche con
6354
+ // datasource bindato correttamente.
6355
+ if (justInjected && toolName === 'DATAREPEATER' && justInjected.inputs) {
6356
+ const ds = justInjected.inputs['datasource'];
6357
+ const action = this.getRepeaterAction(justInjected) || justInjected.inputs['action'] || 'list';
6358
+ if (ds) {
6359
+ try {
6360
+ this.propertyTreeBuilder(action, justInjected.inputs);
6361
+ }
6362
+ catch (err) {
6363
+ try {
6364
+ console.warn('[injectLayoutNode] propertyTreeBuilder failed for DATAREPEATER', err);
6365
+ }
6366
+ catch { /* ignore */ }
6367
+ }
6368
+ }
6369
+ }
6370
+ let count = 1;
6371
+ // Recursion sui children. Tre casi per il parent del child:
6372
+ // 1. `ch.parent_unique_name` e' un uniqueName REALE (es. "TD__126")
6373
+ // => usalo direttamente, il drop() lo risolvera' via componentId.
6374
+ // 2. `ch.parent_unique_name` e' un PLACEHOLDER semantico (es. "<TD-0>",
6375
+ // "<SPLITTER-AREA-1>") => risolvilo dentro `justInjected.nestedComponents`
6376
+ // (i nodi auto-create da dropTable/dropSplitter/dropAccordion).
6377
+ // 3. Manca o null => default = il parent appena iniettato (LABEL dentro
6378
+ // DIV, TABPANEL dentro TABVIEW, ecc.).
6379
+ const children = Array.isArray(node.children) ? node.children : [];
6380
+ if (children.length && injectedUniqueName) {
6381
+ for (const ch of children) {
6382
+ let childParent = injectedUniqueName;
6383
+ const declared = (ch && typeof ch.parent_unique_name === 'string') ? ch.parent_unique_name.trim() : '';
6384
+ if (declared) {
6385
+ if (declared.startsWith('<') && declared.endsWith('>')) {
6386
+ // Placeholder semantico: risolvi dentro justInjected
6387
+ const resolved = this.resolveLayoutPlaceholder(declared, justInjected);
6388
+ if (resolved)
6389
+ childParent = resolved;
6390
+ else
6391
+ throw new Error(`Placeholder '${declared}' non risolvibile dentro ${justInjected?.uniqueName} (verifica struttura: rows/cols/areas/items match)`);
6392
+ }
6393
+ else {
6394
+ // uniqueName reale (es. TD__126): pass-through al drop()
6395
+ childParent = declared;
6396
+ }
6397
+ }
6398
+ count += await this.injectLayoutNode(ch, childParent);
6399
+ }
6400
+ }
6401
+ return count;
6402
+ }
6403
+ /**
6404
+ * Risolve i placeholder semantici che il LLM usa per riferirsi a nodi
6405
+ * auto-generati dai custom onDrop (TABLE crea TR/TD, SPLITTER crea
6406
+ * SPLITTER-AREA, ACCORDION crea ACCORDION-AREA). Lookup dentro il parent
6407
+ * appena iniettato.
6408
+ *
6409
+ * Pattern supportati (case-insensitive sui tag):
6410
+ * - `<TD-N>` - N-esima cella in row-major order (TR[0].TD[0],
6411
+ * TR[0].TD[1], ..., TR[1].TD[0], ...). N zero-based.
6412
+ * - `<TD-R-C>` - cella a riga R colonna C (zero-based).
6413
+ * - `<TR-N>` - N-esima riga.
6414
+ * - `<SPLITTER-AREA-N>` - N-esima area dello splitter.
6415
+ * - `<ACCORDION-AREA-N>` - N-esimo item dell'accordion.
6416
+ * - `<TABPANEL-N>` - N-esimo tab del tabview.
6417
+ *
6418
+ * Ritorna l'uniqueName del nodo risolto, o `null` se il pattern non matcha
6419
+ * o l'index e' out-of-bounds (es. <TD-3> in tabella 2x2).
6420
+ */
6421
+ resolveLayoutPlaceholder(placeholder, parentNode) {
6422
+ if (!placeholder || !parentNode)
6423
+ return null;
6424
+ const inner = placeholder.replace(/^<|>$/g, '').trim();
6425
+ const parts = inner.split('-');
6426
+ if (parts.length < 2)
6427
+ return null;
6428
+ const tag = parts[0].toUpperCase();
6429
+ const nested = parentNode.nestedComponents || [];
6430
+ if (tag === 'TD') {
6431
+ // <TD-N> singolo index OR <TD-R-C> riga/colonna
6432
+ if (parts.length === 2) {
6433
+ const n = parseInt(parts[1], 10);
6434
+ if (!Number.isFinite(n))
6435
+ return null;
6436
+ // Row-major lookup: TR[i].TD[j]
6437
+ let idx = 0;
6438
+ for (const tr of nested) {
6439
+ for (const td of (tr.nestedComponents || [])) {
6440
+ if (idx === n)
6441
+ return td.uniqueName || null;
6442
+ idx++;
6443
+ }
6444
+ }
6445
+ return null;
6446
+ }
6447
+ if (parts.length === 3) {
6448
+ const r = parseInt(parts[1], 10);
6449
+ const c = parseInt(parts[2], 10);
6450
+ if (!Number.isFinite(r) || !Number.isFinite(c))
6451
+ return null;
6452
+ const tr = nested[r];
6453
+ if (!tr)
6454
+ return null;
6455
+ const td = (tr.nestedComponents || [])[c];
6456
+ return td?.uniqueName || null;
6457
+ }
6458
+ return null;
6459
+ }
6460
+ if (tag === 'TR') {
6461
+ const n = parseInt(parts[1], 10);
6462
+ if (!Number.isFinite(n))
6463
+ return null;
6464
+ return nested[n]?.uniqueName || null;
6465
+ }
6466
+ if (tag === 'SPLITTER' && parts[1].toUpperCase() === 'AREA') {
6467
+ const n = parseInt(parts[2], 10);
6468
+ if (!Number.isFinite(n))
6469
+ return null;
6470
+ return nested[n]?.uniqueName || null;
6471
+ }
6472
+ if (tag === 'ACCORDION' && parts[1].toUpperCase() === 'AREA') {
6473
+ const n = parseInt(parts[2], 10);
6474
+ if (!Number.isFinite(n))
6475
+ return null;
6476
+ return nested[n]?.uniqueName || null;
6477
+ }
6478
+ if (tag === 'TABPANEL') {
6479
+ const n = parseInt(parts[1], 10);
6480
+ if (!Number.isFinite(n))
6481
+ return null;
6482
+ return nested[n]?.uniqueName || null;
6483
+ }
6484
+ return null;
6485
+ }
6486
+ /**
6487
+ * Risolve placeholder CROSS-TREE per binding di tipo `dropped-component-list`
6488
+ * (es. DATAREPEATER.inputs.datasource = "<DATASOURCE-0>" -> "DATASOURCE__15").
6489
+ *
6490
+ * Diversamente da `resolveLayoutPlaceholder` (lookup dentro
6491
+ * nestedComponents del parent), qui cerchiamo nel `flattenedDashboardElements`
6492
+ * globale, perche' il LLM puo' riferirsi a componenti che vivono in tutto
6493
+ * il canvas (es. DATASOURCE in un TD, DATAREPEATER in un TD diverso).
6494
+ *
6495
+ * Pattern supportato: `<NAME-N>` dove `NAME` matcha `tool.name`
6496
+ * (DATASOURCE, DATAREPEATER, FILTERBAR, PAGER, ...) e `N` e' l'index
6497
+ * 0-based dei tool con quel nome nell'ordine in cui appaiono in
6498
+ * `flattenedDashboardElements` (= ordine di inject).
6499
+ *
6500
+ * Ritorna l'uniqueName risolto, o `null` se non trova match.
6501
+ *
6502
+ * Esempi:
6503
+ * - "<DATASOURCE-0>" -> primo DATASOURCE iniettato (es. "DATASOURCE__3")
6504
+ * - "<DATASOURCE-1>" -> secondo DATASOURCE (master-detail)
6505
+ * - "<DATAREPEATER-0>" -> primo DATAREPEATER
6506
+ *
6507
+ * Casi degeneri:
6508
+ * - placeholder non riconosciuto -> null
6509
+ * - N out-of-bounds (es. <DATASOURCE-3> con solo 2 DS) -> null
6510
+ *
6511
+ * Usato in `injectLayoutNode` per risolvere binding del tipo
6512
+ * `inputs.datasource = "<DATASOURCE-0>"` PRIMA di costruire l'oggetto
6513
+ * wrapper `{uniqueName, component: BS}`.
6514
+ */
6515
+ resolveCrossTreePlaceholder(placeholder) {
6516
+ if (!placeholder || typeof placeholder !== 'string')
6517
+ return null;
6518
+ const trim = placeholder.trim();
6519
+ if (!trim.startsWith('<') || !trim.endsWith('>'))
6520
+ return null;
6521
+ const inner = trim.replace(/^<|>$/g, '').trim();
6522
+ const m = inner.match(/^([A-Za-z][A-Za-z0-9_-]*)-(\d+)$/);
6523
+ if (!m)
6524
+ return null;
6525
+ const targetName = m[1].toUpperCase();
6526
+ const targetIdx = parseInt(m[2], 10);
6527
+ if (!Number.isFinite(targetIdx) || targetIdx < 0)
6528
+ return null;
6529
+ const matching = (this.flattenedDashboardElements || [])
6530
+ .filter((n) => n?.name && String(n.name).toUpperCase() === targetName);
6531
+ if (targetIdx >= matching.length)
6532
+ return null;
6533
+ return matching[targetIdx]?.uniqueName || null;
6534
+ }
6535
+ /**
6536
+ * Best-effort lookup dell'ultimo nodo iniettato. Strategia:
6537
+ * - se parentUniqueName fornito: cerca dentro il nestedComponents del
6538
+ * parent il piu' recente con quel toolName;
6539
+ * - altrimenti (root): cerca in dashboardElements il piu' recente con
6540
+ * quel toolName.
6541
+ *
6542
+ * "piu' recente" = ultimo elemento dell'array (drop() fa push() in append).
6543
+ */
6544
+ findLastInjectedNode(toolName, parentUniqueName) {
6545
+ let scope;
6546
+ if (parentUniqueName) {
6547
+ const parent = (this.flattenedDashboardElements || []).find((n) => n?.uniqueName === parentUniqueName);
6548
+ scope = parent?.nestedComponents || [];
6549
+ }
6550
+ else {
6551
+ scope = this.dashboardElements || [];
6552
+ }
6553
+ for (let i = scope.length - 1; i >= 0; i--) {
6554
+ if (scope[i]?.name === toolName)
6555
+ return scope[i];
6556
+ }
6557
+ return undefined;
5625
6558
  }
5626
6559
  /**
5627
6560
  * Handler capture-phase per mouseup/touchend su document. Riferimento
@@ -8795,8 +9728,24 @@ class DesignerComponent {
8795
9728
  const detailColumnsHint = detailColumns.length
8796
9729
  ? detailColumns.map((col) => `// - ${col}`).join('\n')
8797
9730
  : '// - (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');`
9731
+ // Risoluzione FK del detail verso il master (allineata a
9732
+ // `injectMasterDetailLookupFilter` runtime — cosi' la formula proposta
9733
+ // produce lo stesso risultato del fallback automatico).
9734
+ // Cascata:
9735
+ // 1) FK lookup canonica via metadata (mc_ui_lookup_entity_name == masterRoute);
9736
+ // 2) colonna del detail che ha lo stesso nome della PK del master
9737
+ // (convention WUIC: `cities.StateProvinceID` punta a `stateprovinces.StateProvinceID`);
9738
+ // 3) backward-compat: prima colonna del detail (vecchio comportamento,
9739
+ // produce filtro sbagliato ma evita undefined).
9740
+ const fkFromLookup = detailDatasource
9741
+ ? this.findDetailFkColumnToMasterRoute(detailDatasource, masterRoute)
9742
+ : null;
9743
+ const fkFromNameMatch = !fkFromLookup && masterPrimaryKey && detailColumns.includes(masterPrimaryKey)
9744
+ ? masterPrimaryKey
9745
+ : null;
9746
+ const detailFkField = fkFromLookup || fkFromNameMatch || (detailColumns.length ? detailColumns[0] : '');
9747
+ const defaultFormula = detailFkField
9748
+ ? `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\nsetFilter('${detailFkField.replace(/'/g, "\\'")}', ${masterPkValueExpression}, 'eq');`
8800
9749
  : `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\n// Example:\n// setFilter('detail_fk', ${masterPkValueExpression}, 'eq');`;
8801
9750
  const result = await WtoolboxService.promptDialog('Master-Detail Filter Formula', [
8802
9751
  {
@@ -10791,12 +11740,25 @@ Display formula context:
10791
11740
  return true;
10792
11741
  }
10793
11742
  /**
10794
- * Fallback master-detail basato su colonne lookup: applica filtro `eq` sulla colonna che punta alla route master.
10795
- */
10796
- injectMasterDetailLookupFilter(detailDatasource, masterRoute, masterRowId) {
11743
+ * Helper condiviso: trova nella metadata del DETAIL la colonna lookup che
11744
+ * punta alla route MASTER. Usato sia da `injectMasterDetailLookupFilter`
11745
+ * (runtime fallback) sia da `openMasterDetailFilterFormulaEditor` (default
11746
+ * formula in UI editor) — cosi' i 2 path producono lo stesso risultato e
11747
+ * non c'e' drift tra "filtro automatico" e "formula proposta nel dialog".
11748
+ *
11749
+ * Logica:
11750
+ * 1) cerca una colonna `mc_ui_column_type=='lookupbyid'` con
11751
+ * `mc_ui_lookup_entity_name==<masterRoute>` OPPURE `mc_ui_grid_route==<masterRoute>`.
11752
+ * Questo e' il match canonico WUIC (FK lookup definita nei metadata).
11753
+ * 2) ritorna `null` se non c'e' match (il chiamante decide il fallback —
11754
+ * `injectMasterDetailLookupFilter` ritorna `false`,
11755
+ * `openMasterDetailFilterFormulaEditor` cade su `detailColumns[0]` per
11756
+ * backward-compat).
11757
+ */
11758
+ findDetailFkColumnToMasterRoute(detailDatasource, masterRoute) {
10797
11759
  const normalizedMasterRoute = String(masterRoute || '').trim().toLowerCase();
10798
11760
  if (!normalizedMasterRoute || !detailDatasource?.metaInfo?.columnMetadata?.length) {
10799
- return false;
11761
+ return null;
10800
11762
  }
10801
11763
  const lookupColumn = detailDatasource.metaInfo.columnMetadata.find((column) => {
10802
11764
  if (!column || String(column.mc_ui_column_type || '').toLowerCase() !== 'lookupbyid') {
@@ -10806,7 +11768,14 @@ Display formula context:
10806
11768
  const gridRoute = String(column.mc_ui_grid_route || '').trim().toLowerCase();
10807
11769
  return lookupRoute === normalizedMasterRoute || gridRoute === normalizedMasterRoute;
10808
11770
  });
10809
- const lookupField = lookupColumn?.mc_nome_colonna;
11771
+ const lookupField = String(lookupColumn?.mc_nome_colonna || '').trim();
11772
+ return lookupField || null;
11773
+ }
11774
+ /**
11775
+ * Fallback master-detail basato su colonne lookup: applica filtro `eq` sulla colonna che punta alla route master.
11776
+ */
11777
+ injectMasterDetailLookupFilter(detailDatasource, masterRoute, masterRowId) {
11778
+ const lookupField = this.findDetailFkColumnToMasterRoute(detailDatasource, masterRoute);
10810
11779
  if (!lookupField) {
10811
11780
  return false;
10812
11781
  }
@@ -10858,13 +11827,13 @@ Display formula context:
10858
11827
  });
10859
11828
  // return customProps;
10860
11829
  }
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" }] });
11830
+ 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 });
11831
+ 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
11832
  }
10864
11833
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, decorators: [{
10865
11834
  type: Component,
10866
11835
  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: [{
11836
+ }], 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
11837
  type: HostListener,
10869
11838
  args: ['window:hashchange']
10870
11839
  }], onWindowBeforeUnload: [{
@@ -10888,4 +11857,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10888
11857
  }] } });
10889
11858
 
10890
11859
  export { DesignerComponent };
10891
- //# sourceMappingURL=wuic-framework-lib-designer.component-pW1YK-oY.mjs.map
11860
+ //# sourceMappingURL=wuic-framework-lib-designer.component-N_CfbGnr.mjs.map