wuic-framework-lib 1.3.2 → 1.7.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.
- package/fesm2022/{wuic-framework-lib-chart-list.component-DAP-TPJR.mjs → wuic-framework-lib-chart-list.component-Bl424n7x.mjs} +49 -37
- package/fesm2022/{wuic-framework-lib-code-editor.component-CLMDvOWs.mjs → wuic-framework-lib-code-editor.component-N6HMl2mf.mjs} +441 -277
- package/fesm2022/{wuic-framework-lib-designer.component-DA7abiLW.mjs → wuic-framework-lib-designer.component-Bd2fwl6H.mjs} +1948 -641
- package/fesm2022/{wuic-framework-lib-first-run-wizard.component-BLrUrfZ9.mjs → wuic-framework-lib-first-run-wizard.component-NN6cWVHL.mjs} +18 -20
- package/fesm2022/{wuic-framework-lib-framework-docs.component-DphKhTef.mjs → wuic-framework-lib-framework-docs.component-B867JAYe.mjs} +4205 -380
- package/fesm2022/{wuic-framework-lib-pivot-builder.component-BqUUwrJM.mjs → wuic-framework-lib-pivot-builder.component-DWukH0L-.mjs} +578 -66
- package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-DZrhwMH5.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-DWO1Rcgv.mjs} +2 -2
- package/fesm2022/wuic-framework-lib-report-designer.component-QcrU81m6.mjs +380 -0
- package/fesm2022/wuic-framework-lib-report-scaffold-dialog.component-U-zdOoRV.mjs +431 -0
- package/fesm2022/{wuic-framework-lib-report-viewer.component-CIN2fY0P.mjs → wuic-framework-lib-report-viewer.component-B4LhA3SL.mjs} +8 -13
- package/fesm2022/wuic-framework-lib-scene3d-designer.component-DjOfzKLo.mjs +4813 -0
- package/fesm2022/wuic-framework-lib-scene3d-light-baker-gpu-C96T9uIW.mjs +303 -0
- package/fesm2022/wuic-framework-lib-scene3d-shader-effect-DjHyCAFK.mjs +2807 -0
- package/fesm2022/wuic-framework-lib-scene3d-viewer.component-Dboz1fjl.mjs +1130 -0
- package/fesm2022/{wuic-framework-lib-scheduler-list.component-CdcI1Uor.mjs → wuic-framework-lib-scheduler-list.component--wY21R3o.mjs} +169 -53
- package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-j1HTRI_P.mjs → wuic-framework-lib-spreadsheet-list-sf.component-Ek30J8v-.mjs} +147 -106
- package/fesm2022/{wuic-framework-lib-stimulsoft-license.service-BIyxB4pO.mjs → wuic-framework-lib-stimulsoft-license.service-CsNNSFYs.mjs} +3 -3
- package/fesm2022/wuic-framework-lib-timeline-list.component-aqCH4P0G.mjs +805 -0
- package/fesm2022/wuic-framework-lib-workflow-designer.component-DMM8BeXm.mjs +9504 -0
- package/fesm2022/{wuic-framework-lib-wuic-framework-lib-BRujEwpR.mjs → wuic-framework-lib-wuic-framework-lib-dfLTqam5.mjs} +14713 -8403
- package/fesm2022/wuic-framework-lib.mjs +1 -1
- package/package.json +5 -2
- package/styles/_wuic-tokens.scss +82 -0
- package/styles/styles.scss +160 -0
- package/types/wuic-framework-lib.d.ts +5971 -3666
- package/fesm2022/wuic-framework-lib-report-designer.component-D_qbefud.mjs +0 -194
- package/fesm2022/wuic-framework-lib-workflow-designer.component-SE_7ZogW.mjs +0 -5114
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
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';
|
|
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, ViewChild } from '@angular/core';
|
|
3
3
|
import * as i10 from 'primeng/splitter';
|
|
4
4
|
import { SplitterModule } from 'primeng/splitter';
|
|
5
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
|
|
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, t as toHsbaModel, d as clampAlpha, e as toPersistedColor, f as ParametricDialogComponent, g as MetadatiColonna, h as DataProviderService, U as UserInfoService, T as TranslationManagerService, i as MetadataEditorService, C as ChatbotHostRegistryService, j as WorkflowRuntimeMetadataService, k as FieldEditorComponent, l as MetadataEditorComponent, A as ArchetypeConfiguratorComponent } from './wuic-framework-lib-wuic-framework-lib-dfLTqam5.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';
|
|
12
|
-
import * as
|
|
12
|
+
import * as i5 from 'primeng/select';
|
|
13
13
|
import { SelectModule } from 'primeng/select';
|
|
14
14
|
import * as i1 from '@angular/forms';
|
|
15
15
|
import { FormsModule } from '@angular/forms';
|
|
16
|
-
import * as
|
|
16
|
+
import * as i6 from 'primeng/colorpicker';
|
|
17
17
|
import { ColorPickerModule } from 'primeng/colorpicker';
|
|
18
|
-
import * as i2$
|
|
18
|
+
import * as i2$1 from 'primeng/autocomplete';
|
|
19
19
|
import { AutoCompleteModule } from 'primeng/autocomplete';
|
|
20
20
|
import * as i8 from '@ngx-translate/core';
|
|
21
21
|
import { TranslateModule } from '@ngx-translate/core';
|
|
@@ -33,7 +33,7 @@ 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
|
|
36
|
+
import * as i7 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';
|
|
@@ -43,7 +43,7 @@ class PropConverterPipe {
|
|
|
43
43
|
if (value) {
|
|
44
44
|
if (args.length > 0) {
|
|
45
45
|
if (args[0] === 'toArray') {
|
|
46
|
-
return Array(parseInt(value.toString())).fill(1).map((x, i) => i);
|
|
46
|
+
return new Array(Number.parseInt(value.toString())).fill(1).map((x, i) => i);
|
|
47
47
|
}
|
|
48
48
|
if (args[0] === 'fromArray') {
|
|
49
49
|
return value.length;
|
|
@@ -68,8 +68,8 @@ class NgxDraggablePoint {
|
|
|
68
68
|
x;
|
|
69
69
|
y;
|
|
70
70
|
constructor(x, y) {
|
|
71
|
-
this.x =
|
|
72
|
-
this.y =
|
|
71
|
+
this.x = x || 0;
|
|
72
|
+
this.y = y || 0;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -136,10 +136,10 @@ class NgxDraggableRect {
|
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
constructor(x, y, width, height) {
|
|
139
|
-
this.x =
|
|
140
|
-
this.y =
|
|
141
|
-
this.width =
|
|
142
|
-
this.height =
|
|
139
|
+
this.x = x || 0;
|
|
140
|
+
this.y = y || 0;
|
|
141
|
+
this.width = width || 0;
|
|
142
|
+
this.height = height || 0;
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -175,15 +175,15 @@ class NgxDraggableDomBoundsCheckEvent {
|
|
|
175
175
|
* @param isConstrained Whether the element should be constrained or not.
|
|
176
176
|
*/
|
|
177
177
|
constructor(top, right, bottom, left, elP0, translation, isConstrained) {
|
|
178
|
-
this.top =
|
|
179
|
-
this.right =
|
|
180
|
-
this.bottom =
|
|
181
|
-
this.left =
|
|
182
|
-
if (
|
|
178
|
+
this.top = top ?? false;
|
|
179
|
+
this.right = right ?? false;
|
|
180
|
+
this.bottom = bottom ?? false;
|
|
181
|
+
this.left = left ?? false;
|
|
182
|
+
if (elP0) {
|
|
183
183
|
this.constrainedCenter = elP0;
|
|
184
184
|
}
|
|
185
|
-
this.translation =
|
|
186
|
-
this.isConstrained =
|
|
185
|
+
this.translation = translation ?? new NgxDraggablePoint(0, 0);
|
|
186
|
+
this.isConstrained = isConstrained ?? false;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -217,8 +217,8 @@ class NgxDraggableDomMoveEvent {
|
|
|
217
217
|
* @param position The position of the target HTMLElement.
|
|
218
218
|
*/
|
|
219
219
|
constructor(target, position) {
|
|
220
|
-
this._target =
|
|
221
|
-
this._position =
|
|
220
|
+
this._target = target || undefined;
|
|
221
|
+
this._position = position || undefined;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -386,11 +386,11 @@ class NgxDraggableDomUtilities {
|
|
|
386
386
|
static getTransformMatrixForElement(el) {
|
|
387
387
|
// create the numerical matrix we will use
|
|
388
388
|
const matrix = [1, 0, 0, 1, 0, 0];
|
|
389
|
-
if (
|
|
389
|
+
if (globalThis) {
|
|
390
390
|
// get the computed transform style
|
|
391
|
-
let transform =
|
|
391
|
+
let transform = globalThis.getComputedStyle(el, null).getPropertyValue('transform');
|
|
392
392
|
// strip non matrix values from the string
|
|
393
|
-
transform = transform.
|
|
393
|
+
transform = transform.replaceAll('matrix', '').replaceAll('(', '').replaceAll(')', '').replaceAll(' ', '');
|
|
394
394
|
// if we have a transform set, convert the string matrix to a numerical one
|
|
395
395
|
if (transform !== 'none') {
|
|
396
396
|
// split the string based on commas
|
|
@@ -427,7 +427,7 @@ class NgxDraggableDomUtilities {
|
|
|
427
427
|
*/
|
|
428
428
|
static getTotalRotationForElement(node, rotation = 0) {
|
|
429
429
|
// if we can't calculate the computed style or we have no node to analyze, return the current calculated rotation
|
|
430
|
-
if (!node || !
|
|
430
|
+
if (!node || !globalThis) {
|
|
431
431
|
return rotation;
|
|
432
432
|
}
|
|
433
433
|
// if we have reached the body, stop processing beyond here
|
|
@@ -439,6 +439,23 @@ class NgxDraggableDomUtilities {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Stato condiviso dello snap-to-grid del designer.
|
|
444
|
+
*
|
|
445
|
+
* Sorgente di verita' NEUTRA (nessun import Angular) letta dalle direttive di
|
|
446
|
+
* drag (`NgxDraggableDomDirective`) e resize (`AngularResizableDirective`) e
|
|
447
|
+
* scritta da `DesignerComponent.toggleSnapToGrid`. Serve perche' quelle
|
|
448
|
+
* direttive sono applicate agli elementi via TEMPLATE DINAMICO generato
|
|
449
|
+
* (`DynamicDashboardTemplateComponent` riscrive `ngxDraggableDom="true"`), dove
|
|
450
|
+
* non e' pratico bindare un `[snapGridSize]` per-elemento. Un modulo neutro
|
|
451
|
+
* evita anche l'import circolare direttiva <-> componente.
|
|
452
|
+
*
|
|
453
|
+
* `gridSize` = passo in px quando lo snap e' attivo, 0 = snap off.
|
|
454
|
+
*/
|
|
455
|
+
const DesignerSnapState = {
|
|
456
|
+
gridSize: 0,
|
|
457
|
+
};
|
|
458
|
+
|
|
442
459
|
class NgxDraggableDomDirective {
|
|
443
460
|
el;
|
|
444
461
|
renderer;
|
|
@@ -510,7 +527,7 @@ class NgxDraggableDomDirective {
|
|
|
510
527
|
return null;
|
|
511
528
|
}
|
|
512
529
|
// get the bounding box of the element
|
|
513
|
-
const boundsBounds = this.bounds.getBoundingClientRect();
|
|
530
|
+
const boundsBounds = (this.bounds).getBoundingClientRect();
|
|
514
531
|
return new NgxDraggablePoint(this.scrollLeft + boundsBounds.left + (boundsBounds.width / 2), this.scrollTop + boundsBounds.top + (boundsBounds.height / 2));
|
|
515
532
|
}
|
|
516
533
|
/**
|
|
@@ -519,7 +536,7 @@ class NgxDraggableDomDirective {
|
|
|
519
536
|
* @return The current scroll position of the document in the x direction.
|
|
520
537
|
*/
|
|
521
538
|
get scrollLeft() {
|
|
522
|
-
if (
|
|
539
|
+
if (globalThis) {
|
|
523
540
|
return window.pageXOffset;
|
|
524
541
|
}
|
|
525
542
|
else if (!!document && !!document.documentElement) {
|
|
@@ -535,7 +552,7 @@ class NgxDraggableDomDirective {
|
|
|
535
552
|
* @return The current scroll position of the document in the y direction.
|
|
536
553
|
*/
|
|
537
554
|
get scrollTop() {
|
|
538
|
-
if (
|
|
555
|
+
if (globalThis) {
|
|
539
556
|
return window.pageYOffset;
|
|
540
557
|
}
|
|
541
558
|
else if (!!document && !!document.documentElement) {
|
|
@@ -637,7 +654,7 @@ class NgxDraggableDomDirective {
|
|
|
637
654
|
*/
|
|
638
655
|
onTouchStart(event) {
|
|
639
656
|
// block multiTouch events if we are configured to do so
|
|
640
|
-
if (this.ignoreMultiTouchEvents && event
|
|
657
|
+
if (this.ignoreMultiTouchEvents && event?.touches && event.touches.length > 1) {
|
|
641
658
|
return;
|
|
642
659
|
}
|
|
643
660
|
// stop all default behavior and propagation of the event so it is fully consumed by us
|
|
@@ -732,13 +749,8 @@ class NgxDraggableDomDirective {
|
|
|
732
749
|
* @param event The touch event to handle as a TouchEvent (or any solely for working around issues with Safari).
|
|
733
750
|
*/
|
|
734
751
|
onTouchEnd(event) {
|
|
735
|
-
//
|
|
736
|
-
|
|
737
|
-
// prevent the default mouse behavior on images so that browser image dragging is disabled
|
|
738
|
-
if (event.target instanceof HTMLImageElement) {
|
|
739
|
-
event.preventDefault();
|
|
740
|
-
}
|
|
741
|
-
this.putBack();
|
|
752
|
+
// same consume-and-put-back behavior as the mouse release handler
|
|
753
|
+
this.onMouseUp(event);
|
|
742
754
|
}
|
|
743
755
|
/**
|
|
744
756
|
* Event handler for when the element is moved via a touch event.
|
|
@@ -747,7 +759,7 @@ class NgxDraggableDomDirective {
|
|
|
747
759
|
*/
|
|
748
760
|
onTouchMove(event) {
|
|
749
761
|
// block multiTouch events if we are configured to do so
|
|
750
|
-
if (this.ignoreMultiTouchEvents && event
|
|
762
|
+
if (this.ignoreMultiTouchEvents && event?.touches && event.touches.length > 1) {
|
|
751
763
|
// ensure any element that is being dragged is put back
|
|
752
764
|
this.putBack(false);
|
|
753
765
|
return;
|
|
@@ -835,13 +847,23 @@ class NgxDraggableDomDirective {
|
|
|
835
847
|
// check the bounds based on the element position
|
|
836
848
|
boundsCheck = this.boundsCheck(new NgxDraggablePoint(dragPosition.x, dragPosition.y));
|
|
837
849
|
// hold the element in position if we are requested to be constrained
|
|
838
|
-
if (boundsCheck
|
|
850
|
+
if (boundsCheck?.isConstrained) {
|
|
839
851
|
// update the translation using the constrained center point and bounds center
|
|
840
852
|
translation = boundsCheck.translation;
|
|
841
853
|
}
|
|
842
854
|
}
|
|
855
|
+
// Designer snap-to-grid: arrotonda la posizione ASSOLUTA dell'elemento al
|
|
856
|
+
// multiplo di griglia, poi riporta il delta nel translation. `offsetLeft`/
|
|
857
|
+
// `offsetTop` NON includono il transform, quindi (offset + translation) e' la
|
|
858
|
+
// posizione visiva reale sul canvas (offsetParent). No-op se snap off.
|
|
859
|
+
const _snapGrid = DesignerSnapState.gridSize;
|
|
860
|
+
if (_snapGrid && _snapGrid > 0) {
|
|
861
|
+
const _dragEl = this.el.nativeElement;
|
|
862
|
+
translation.x = Math.round((_dragEl.offsetLeft + translation.x) / _snapGrid) * _snapGrid - _dragEl.offsetLeft;
|
|
863
|
+
translation.y = Math.round((_dragEl.offsetTop + translation.y) / _snapGrid) * _snapGrid - _dragEl.offsetTop;
|
|
864
|
+
}
|
|
843
865
|
// if it is possible, get the transform from the computed style and modify the matrix to maintain transform properties
|
|
844
|
-
if (
|
|
866
|
+
if (globalThis) {
|
|
845
867
|
// update the x and y values as part of the matrix
|
|
846
868
|
matrix[4] = translation.x;
|
|
847
869
|
matrix[5] = translation.y;
|
|
@@ -888,9 +910,9 @@ class NgxDraggableDomDirective {
|
|
|
888
910
|
this.oldZIndex = this.el.nativeElement.style.zIndex ? this.el.nativeElement.style.zIndex : '';
|
|
889
911
|
this.oldPosition = this.el.nativeElement.style.position ? this.el.nativeElement.style.position : '';
|
|
890
912
|
// fetch the old z-index and position from computing the style applied to the element
|
|
891
|
-
if (
|
|
892
|
-
this.oldZIndex =
|
|
893
|
-
this.oldPosition =
|
|
913
|
+
if (globalThis) {
|
|
914
|
+
this.oldZIndex = globalThis.getComputedStyle(this.el.nativeElement, null).getPropertyValue('z-index');
|
|
915
|
+
this.oldPosition = globalThis.getComputedStyle(this.el.nativeElement, null).getPropertyValue('position');
|
|
894
916
|
}
|
|
895
917
|
// check if old position is draggable
|
|
896
918
|
if (this.oldPosition && (this.oldPosition === 'absolute' ||
|
|
@@ -927,7 +949,7 @@ class NgxDraggableDomDirective {
|
|
|
927
949
|
// compute the current rotation of all parent nodes
|
|
928
950
|
this.computedRotation = NgxDraggableDomUtilities.getTotalRotationForElement(this.el.nativeElement.parentElement);
|
|
929
951
|
// normalize the start position for the rotation
|
|
930
|
-
this.startPosition = NgxDraggableMath.rotatePoint(this.startPosition,
|
|
952
|
+
this.startPosition = NgxDraggableMath.rotatePoint(this.startPosition, boundsCenter ?? new NgxDraggablePoint(0, 0), -this.computedRotation);
|
|
931
953
|
// get the current transformation matrix and extract the current translation
|
|
932
954
|
matrix = NgxDraggableDomUtilities.getTransformMatrixForElement(this.el.nativeElement);
|
|
933
955
|
translation.x = matrix[4];
|
|
@@ -936,7 +958,7 @@ class NgxDraggableDomDirective {
|
|
|
936
958
|
this.startPosition.x -= translation.x;
|
|
937
959
|
this.startPosition.y -= translation.y;
|
|
938
960
|
// reapply the rotation to the start position
|
|
939
|
-
this.startPosition = NgxDraggableMath.rotatePoint(this.startPosition,
|
|
961
|
+
this.startPosition = NgxDraggableMath.rotatePoint(this.startPosition, boundsCenter ?? new NgxDraggablePoint(0, 0), this.computedRotation);
|
|
940
962
|
// determine the clientX and clientY position based on the event type
|
|
941
963
|
const clientX = (event instanceof MouseEvent) ? event.clientX : event.changedTouches[0]?.clientX;
|
|
942
964
|
const clientY = (event instanceof MouseEvent) ? event.clientY : event.changedTouches[0]?.clientY;
|
|
@@ -994,7 +1016,7 @@ class NgxDraggableDomDirective {
|
|
|
994
1016
|
if (this.bounds) {
|
|
995
1017
|
// get the current center point of the element
|
|
996
1018
|
const elCenter = this.elCenter;
|
|
997
|
-
if (
|
|
1019
|
+
if (elCenter) {
|
|
998
1020
|
// check the bounds based on the element position
|
|
999
1021
|
const boundsCheck = this.boundsCheck(elCenter);
|
|
1000
1022
|
// emit the edge event so consumers know the current state of the position
|
|
@@ -1116,7 +1138,7 @@ class NgxDraggableDomDirective {
|
|
|
1116
1138
|
if (isBLOutside && elBL.x >= (checkBounds.left + checkBounds.width) &&
|
|
1117
1139
|
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0) > greatestConstrainDistance) {
|
|
1118
1140
|
constrainPoint = new NgxDraggablePoint(elBL.x, elBL.y);
|
|
1119
|
-
|
|
1141
|
+
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0);
|
|
1120
1142
|
}
|
|
1121
1143
|
// calculate the displacement
|
|
1122
1144
|
displaceX = checkBounds.left + checkBounds.width - (constrainPoint ? constrainPoint.x : 0);
|
|
@@ -1141,7 +1163,7 @@ class NgxDraggableDomDirective {
|
|
|
1141
1163
|
if (isBLOutside && elBL.x <= checkBounds.left &&
|
|
1142
1164
|
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0) > greatestConstrainDistance) {
|
|
1143
1165
|
constrainPoint = new NgxDraggablePoint(elBL.x, elBL.y);
|
|
1144
|
-
|
|
1166
|
+
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0);
|
|
1145
1167
|
}
|
|
1146
1168
|
// calculate the displacement
|
|
1147
1169
|
displaceX = checkBounds.left - (constrainPoint ? constrainPoint.x : 0);
|
|
@@ -1169,7 +1191,7 @@ class NgxDraggableDomDirective {
|
|
|
1169
1191
|
if (isBLOutside && elBL.y >= (checkBounds.top + checkBounds.height) &&
|
|
1170
1192
|
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0) > greatestConstrainDistance) {
|
|
1171
1193
|
constrainPoint = new NgxDraggablePoint(elBL.x, elBL.y);
|
|
1172
|
-
|
|
1194
|
+
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0);
|
|
1173
1195
|
}
|
|
1174
1196
|
// calculate the displacement
|
|
1175
1197
|
displaceY = checkBounds.top + checkBounds.height - (constrainPoint ? constrainPoint.y : 0);
|
|
@@ -1194,7 +1216,7 @@ class NgxDraggableDomDirective {
|
|
|
1194
1216
|
if (isBLOutside && elBL.y <= checkBounds.top &&
|
|
1195
1217
|
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0) > greatestConstrainDistance) {
|
|
1196
1218
|
constrainPoint = new NgxDraggablePoint(elBL.x, elBL.y);
|
|
1197
|
-
|
|
1219
|
+
NgxDraggableMath.getDistanceBetweenPoints(elBL, boundsP0);
|
|
1198
1220
|
}
|
|
1199
1221
|
// calculate the displacement
|
|
1200
1222
|
displaceY = checkBounds.top - (constrainPoint ? constrainPoint.y : 0);
|
|
@@ -1318,7 +1340,7 @@ class ResizeHandle {
|
|
|
1318
1340
|
this._handle.removeEventListener('mousedown', this._onResize);
|
|
1319
1341
|
this._handle.removeEventListener('touchstart', this._onResize);
|
|
1320
1342
|
if (this.parent && !this.existHandle) {
|
|
1321
|
-
this.
|
|
1343
|
+
this._handle.remove();
|
|
1322
1344
|
}
|
|
1323
1345
|
this._handle = null;
|
|
1324
1346
|
this._onResize = null;
|
|
@@ -1352,9 +1374,9 @@ class Position {
|
|
|
1352
1374
|
* Fix issue: Multiple phone draggables at the same time
|
|
1353
1375
|
* https://github.com/xieziyu/angular2-draggable/issues/128
|
|
1354
1376
|
*/
|
|
1355
|
-
for (
|
|
1356
|
-
if (
|
|
1357
|
-
return new Position(
|
|
1377
|
+
for (const touch of e.changedTouches) {
|
|
1378
|
+
if (touch.target === el) {
|
|
1379
|
+
return new Position(touch.clientX, touch.clientY);
|
|
1358
1380
|
}
|
|
1359
1381
|
}
|
|
1360
1382
|
}
|
|
@@ -1368,13 +1390,13 @@ class Position {
|
|
|
1368
1390
|
}
|
|
1369
1391
|
static getCurrent(el) {
|
|
1370
1392
|
let pos = new Position(0, 0);
|
|
1371
|
-
if (
|
|
1372
|
-
const computed =
|
|
1393
|
+
if (globalThis) {
|
|
1394
|
+
const computed = globalThis.getComputedStyle(el);
|
|
1373
1395
|
if (computed) {
|
|
1374
|
-
let x = parseInt(computed.getPropertyValue('left'), 10);
|
|
1375
|
-
let y = parseInt(computed.getPropertyValue('top'), 10);
|
|
1376
|
-
pos.x = isNaN(x) ? 0 : x;
|
|
1377
|
-
pos.y = isNaN(y) ? 0 : y;
|
|
1396
|
+
let x = Number.parseInt(computed.getPropertyValue('left'), 10);
|
|
1397
|
+
let y = Number.parseInt(computed.getPropertyValue('top'), 10);
|
|
1398
|
+
pos.x = Number.isNaN(Number(x)) ? 0 : x;
|
|
1399
|
+
pos.y = Number.isNaN(Number(y)) ? 0 : y;
|
|
1378
1400
|
}
|
|
1379
1401
|
return pos;
|
|
1380
1402
|
}
|
|
@@ -1428,11 +1450,11 @@ class Size {
|
|
|
1428
1450
|
}
|
|
1429
1451
|
static getCurrent(el) {
|
|
1430
1452
|
let size = new Size(0, 0);
|
|
1431
|
-
if (
|
|
1432
|
-
const computed =
|
|
1453
|
+
if (globalThis) {
|
|
1454
|
+
const computed = globalThis.getComputedStyle(el);
|
|
1433
1455
|
if (computed) {
|
|
1434
|
-
size.width = parseInt(computed.getPropertyValue('width'), 10);
|
|
1435
|
-
size.height = parseInt(computed.getPropertyValue('height'), 10);
|
|
1456
|
+
size.width = Number.parseInt(computed.getPropertyValue('width'), 10);
|
|
1457
|
+
size.height = Number.parseInt(computed.getPropertyValue('height'), 10);
|
|
1436
1458
|
}
|
|
1437
1459
|
return size;
|
|
1438
1460
|
}
|
|
@@ -1479,7 +1501,7 @@ class HelperBlock {
|
|
|
1479
1501
|
}
|
|
1480
1502
|
remove() {
|
|
1481
1503
|
if (this.parent && this._added) {
|
|
1482
|
-
this.
|
|
1504
|
+
this._helper.remove();
|
|
1483
1505
|
this._added = false;
|
|
1484
1506
|
}
|
|
1485
1507
|
}
|
|
@@ -1666,7 +1688,7 @@ class AngularResizableDirective {
|
|
|
1666
1688
|
}
|
|
1667
1689
|
else {
|
|
1668
1690
|
let r = Number(this.rzAspectRatio);
|
|
1669
|
-
this._aspectRatio = isNaN(r) ? 0 : r;
|
|
1691
|
+
this._aspectRatio = Number.isNaN(Number(r)) ? 0 : r;
|
|
1670
1692
|
}
|
|
1671
1693
|
}
|
|
1672
1694
|
/** Use it to update containment */
|
|
@@ -1698,7 +1720,7 @@ class AngularResizableDirective {
|
|
|
1698
1720
|
tmpHandleTypes = ['n', 'e', 's', 'w', 'ne', 'se', 'nw', 'sw'];
|
|
1699
1721
|
}
|
|
1700
1722
|
else {
|
|
1701
|
-
tmpHandleTypes = this.rzHandles.
|
|
1723
|
+
tmpHandleTypes = this.rzHandles.replaceAll(' ', '').toLowerCase().split(',');
|
|
1702
1724
|
}
|
|
1703
1725
|
for (let type of tmpHandleTypes) {
|
|
1704
1726
|
// default handle theme: ng-resizable-$type.
|
|
@@ -1725,7 +1747,7 @@ class AngularResizableDirective {
|
|
|
1725
1747
|
createHandleByType(type, css) {
|
|
1726
1748
|
const _el = this.el.nativeElement;
|
|
1727
1749
|
const _h = this.rzHandleDoms[type] ? this.rzHandleDoms[type].nativeElement : null;
|
|
1728
|
-
if (
|
|
1750
|
+
if (!/^(se|sw|ne|nw|n|e|s|w)$/.exec(type)) {
|
|
1729
1751
|
console.error('Invalid handle type:', type);
|
|
1730
1752
|
return null;
|
|
1731
1753
|
}
|
|
@@ -1759,7 +1781,7 @@ class AngularResizableDirective {
|
|
|
1759
1781
|
this.draggingSub.add(fromEvent(document, 'touchmove', { passive: false }).subscribe(event => this.onMouseMove(event)));
|
|
1760
1782
|
this.draggingSub.add(fromEvent(document, 'mouseup', { passive: false }).subscribe(() => this.onMouseLeave()));
|
|
1761
1783
|
// fix for issue #164
|
|
1762
|
-
let isIEOrEdge = /msie\s|trident\//i.test(
|
|
1784
|
+
let isIEOrEdge = /msie\s|trident\//i.test(globalThis.navigator.userAgent);
|
|
1763
1785
|
if (!isIEOrEdge) {
|
|
1764
1786
|
this.draggingSub.add(fromEvent(document, 'mouseleave', { passive: false }).subscribe(() => this.onMouseLeave()));
|
|
1765
1787
|
}
|
|
@@ -1835,10 +1857,10 @@ class AngularResizableDirective {
|
|
|
1835
1857
|
}
|
|
1836
1858
|
updateDirection() {
|
|
1837
1859
|
this._direction = {
|
|
1838
|
-
n:
|
|
1839
|
-
s:
|
|
1840
|
-
w:
|
|
1841
|
-
e:
|
|
1860
|
+
n: /n/.test(this._handleResizing.type),
|
|
1861
|
+
s: /s/.test(this._handleResizing.type),
|
|
1862
|
+
w: /w/.test(this._handleResizing.type),
|
|
1863
|
+
e: /e/.test(this._handleResizing.type),
|
|
1842
1864
|
};
|
|
1843
1865
|
this._directionChanged = { ...this._direction };
|
|
1844
1866
|
// if aspect ration should be preserved:
|
|
@@ -2002,24 +2024,24 @@ class AngularResizableDirective {
|
|
|
2002
2024
|
}
|
|
2003
2025
|
getBounding() {
|
|
2004
2026
|
const el = this._containment;
|
|
2005
|
-
const computed =
|
|
2027
|
+
const computed = globalThis.getComputedStyle(el);
|
|
2006
2028
|
if (computed) {
|
|
2007
2029
|
let p = computed.getPropertyValue('position');
|
|
2008
|
-
const nativeEl =
|
|
2030
|
+
const nativeEl = globalThis.getComputedStyle(this.el.nativeElement);
|
|
2009
2031
|
let transforms = nativeEl
|
|
2010
2032
|
.getPropertyValue('transform')
|
|
2011
|
-
.
|
|
2033
|
+
.replaceAll(/[^-\d,]/g, '')
|
|
2012
2034
|
.split(',');
|
|
2013
2035
|
this._bounding = {};
|
|
2014
2036
|
this._bounding.width = el.clientWidth;
|
|
2015
2037
|
this._bounding.height = el.clientHeight;
|
|
2016
|
-
this._bounding.pr = parseInt(computed.getPropertyValue('padding-right'), 10);
|
|
2017
|
-
this._bounding.pb = parseInt(computed.getPropertyValue('padding-bottom'), 10);
|
|
2038
|
+
this._bounding.pr = Number.parseInt(computed.getPropertyValue('padding-right'), 10);
|
|
2039
|
+
this._bounding.pb = Number.parseInt(computed.getPropertyValue('padding-bottom'), 10);
|
|
2018
2040
|
this._bounding.deltaL = this.el.nativeElement.offsetLeft - this._currPos.x;
|
|
2019
2041
|
this._bounding.deltaT = this.el.nativeElement.offsetTop - this._currPos.y;
|
|
2020
2042
|
if (transforms.length >= 6) {
|
|
2021
|
-
this._bounding.translateX = parseInt(transforms[4], 10);
|
|
2022
|
-
this._bounding.translateY = parseInt(transforms[5], 10);
|
|
2043
|
+
this._bounding.translateX = Number.parseInt(transforms[4], 10);
|
|
2044
|
+
this._bounding.translateY = Number.parseInt(transforms[5], 10);
|
|
2023
2045
|
}
|
|
2024
2046
|
else {
|
|
2025
2047
|
this._bounding.translateX = 0;
|
|
@@ -2032,7 +2054,7 @@ class AngularResizableDirective {
|
|
|
2032
2054
|
}
|
|
2033
2055
|
}
|
|
2034
2056
|
resetBounding() {
|
|
2035
|
-
if (this._bounding
|
|
2057
|
+
if (this._bounding?.position === 'static') {
|
|
2036
2058
|
this.renderer.setStyle(this._containment, 'position', 'relative');
|
|
2037
2059
|
}
|
|
2038
2060
|
this._bounding = null;
|
|
@@ -2048,6 +2070,12 @@ class AngularResizableDirective {
|
|
|
2048
2070
|
this._gridSize = { x: this.rzGrid[0], y: this.rzGrid[1] };
|
|
2049
2071
|
}
|
|
2050
2072
|
}
|
|
2073
|
+
else if (DesignerSnapState.gridSize && DesignerSnapState.gridSize > 0) {
|
|
2074
|
+
// Designer snap-to-grid (stato condiviso): usa il passo griglia quando lo
|
|
2075
|
+
// snap e' attivo e nessun `rzGrid` esplicito e' bindato (il designer
|
|
2076
|
+
// applica la direttiva via template dinamico, senza binding per-elemento).
|
|
2077
|
+
this._gridSize = { x: DesignerSnapState.gridSize, y: DesignerSnapState.gridSize };
|
|
2078
|
+
}
|
|
2051
2079
|
}
|
|
2052
2080
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AngularResizableDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
|
2053
2081
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: AngularResizableDirective, isStandalone: true, selector: "[ngResizable]", inputs: { ngResizable: "ngResizable", rzHandles: "rzHandles", rzHandleDoms: "rzHandleDoms", rzAspectRatio: "rzAspectRatio", rzContainment: "rzContainment", rzGrid: "rzGrid", rzMinWidth: "rzMinWidth", rzMinHeight: "rzMinHeight", rzMaxWidth: "rzMaxWidth", rzMaxHeight: "rzMaxHeight", rzScale: "rzScale", preventDefaultEvent: "preventDefaultEvent" }, outputs: { rzStart: "rzStart", rzResizing: "rzResizing", rzStop: "rzStop" }, usesOnChanges: true, ngImport: i0 });
|
|
@@ -2104,7 +2132,7 @@ class DataBoundDirective {
|
|
|
2104
2132
|
this.cd = cd;
|
|
2105
2133
|
}
|
|
2106
2134
|
async ngOnChanges(changes) {
|
|
2107
|
-
if (changes['databound']
|
|
2135
|
+
if (changes['databound']?.currentValue) {
|
|
2108
2136
|
const source = changes['databound'].currentValue;
|
|
2109
2137
|
if (!(source instanceof BehaviorSubject)) {
|
|
2110
2138
|
return;
|
|
@@ -2125,17 +2153,17 @@ class DataBoundDirective {
|
|
|
2125
2153
|
});
|
|
2126
2154
|
});
|
|
2127
2155
|
}
|
|
2128
|
-
else if (changes['bindingFunction']
|
|
2156
|
+
else if (changes['bindingFunction']?.currentValue && this.ds) {
|
|
2129
2157
|
new Function('resultInfo, metaInfo, inputs, wtoolbox', this.bindingFunction)(this.ds.resultInfo, this.ds.metaInfo, this.inputs, WtoolboxService);
|
|
2130
2158
|
this.cd.detectChanges();
|
|
2131
2159
|
}
|
|
2132
|
-
else if (changes['itemTemplate']
|
|
2160
|
+
else if (changes['itemTemplate']?.currentValue && changes['itemTemplate'].previousValue != changes['itemTemplate'].currentValue) {
|
|
2133
2161
|
this.inputs.itemTemplateComponent = DynamicGenericTemplateComponent.getComponentFromTemplate(this.inputs.itemTemplate || '', '[data-bound] itemTemplate (host-supplied)', String(this.ds?.metaInfo?.tableMetadata?.md_route_name || ''));
|
|
2134
2162
|
if (this.ds) {
|
|
2135
2163
|
await this.ds.fetchData();
|
|
2136
2164
|
}
|
|
2137
2165
|
}
|
|
2138
|
-
else if (changes['clickCallback']
|
|
2166
|
+
else if (changes['clickCallback']?.currentValue) {
|
|
2139
2167
|
this.inputs._wtoolbox = WtoolboxService;
|
|
2140
2168
|
this.inputs.clickCallback__fn = new Function('resultInfo, metaInfo, inputs, event, wtoolbox', this.clickCallback);
|
|
2141
2169
|
this.cd.detectChanges();
|
|
@@ -2202,11 +2230,11 @@ class TabViewWrapperComponent {
|
|
|
2202
2230
|
});
|
|
2203
2231
|
}
|
|
2204
2232
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TabViewWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2205
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TabViewWrapperComponent, isStandalone: true, selector: "p-tabView", inputs: { tabs: "tabs", activeIndex: "activeIndex" }, ngImport: i0, template: "<div data-pc-name=\"tabview\" class=\"p-tabview p-component\">\n <div class=\"p-tabview-nav-container\">\n <div class=\"p-tabview-nav-content\" data-pc-section=\"navcontent\">\n <ul role=\"tablist\" class=\"p-tabview-nav\" data-pc-section=\"nav\">\n @for (tab of tabs; track tab; let i = $index) {\n <li role=\"presentation\" data-p-disabled=\"false\"\n [ngClass]=\"{'p-highlight':
|
|
2233
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TabViewWrapperComponent, isStandalone: true, selector: "p-tabView", inputs: { tabs: "tabs", activeIndex: "activeIndex" }, ngImport: i0, template: "<div data-pc-name=\"tabview\" class=\"p-tabview p-component\">\n <div class=\"p-tabview-nav-container\">\n <div class=\"p-tabview-nav-content\" data-pc-section=\"navcontent\">\n <ul role=\"tablist\" class=\"p-tabview-nav\" data-pc-section=\"nav\">\n @for (tab of tabs; track tab; let i = $index) {\n <li role=\"presentation\" data-p-disabled=\"false\"\n [ngClass]=\"{'p-highlight': tab.inputs?.selected}\">\n <button type=\"button\" role=\"tab\" pripple=\"\" class=\"p-element p-ripple p-tabview-nav-link\" (click)=\"setIndex(i)\"\n [attr.aria-selected]=\"tab.inputs?.selected\" [attr.tabindex]=\"i\" aria-disabled=\"false\"\n [attr.data-pc-index]=\"i\" data-pc-section=\"headeraction\">\n <span class=\"p-tabview-title\">\n {{tab.inputs.header}}\n </span>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n <div class=\"p-tabview-panels\">\n <ng-content></ng-content>\n </div>\n</div>", styles: [".p-tabview.p-component{height:100%}.p-tabview .p-tabview-nav-content{scroll-padding-inline:3rem}.p-tabview-nav-content{overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;scrollbar-width:none;overscroll-behavior:contain auto}.p-tabview-nav{display:inline-flex;min-width:100%;margin:0;padding:0;list-style-type:none;flex:1 1 auto}.p-tabview .p-tabview-nav li .p-tabview-nav-link{border-width:0 0 2px 0;padding:1.25rem;font-weight:700;border-top-right-radius:6px;border-top-left-radius:6px;transition:box-shadow .2s;margin:0 0 -2px}.p-tabview-nav-link{appearance:none;background:transparent;border:none;font:inherit;color:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;display:flex;align-items:center;position:relative;text-decoration:none;overflow:hidden;width:100%}.p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link{border-color:var(--p-primary-color, #3B82F6);color:var(--p-primary-color, #3B82F6);background-color:transparent}.p-tabview-panels{height:calc(100% - 60px);overflow:auto}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
2206
2234
|
}
|
|
2207
2235
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TabViewWrapperComponent, decorators: [{
|
|
2208
2236
|
type: Component,
|
|
2209
|
-
args: [{ selector: 'p-tabView', imports: [NgClass], template: "<div data-pc-name=\"tabview\" class=\"p-tabview p-component\">\n <div class=\"p-tabview-nav-container\">\n <div class=\"p-tabview-nav-content\" data-pc-section=\"navcontent\">\n <ul role=\"tablist\" class=\"p-tabview-nav\" data-pc-section=\"nav\">\n @for (tab of tabs; track tab; let i = $index) {\n <li role=\"presentation\" data-p-disabled=\"false\"\n [ngClass]=\"{'p-highlight':
|
|
2237
|
+
args: [{ selector: 'p-tabView', imports: [NgClass], template: "<div data-pc-name=\"tabview\" class=\"p-tabview p-component\">\n <div class=\"p-tabview-nav-container\">\n <div class=\"p-tabview-nav-content\" data-pc-section=\"navcontent\">\n <ul role=\"tablist\" class=\"p-tabview-nav\" data-pc-section=\"nav\">\n @for (tab of tabs; track tab; let i = $index) {\n <li role=\"presentation\" data-p-disabled=\"false\"\n [ngClass]=\"{'p-highlight': tab.inputs?.selected}\">\n <button type=\"button\" role=\"tab\" pripple=\"\" class=\"p-element p-ripple p-tabview-nav-link\" (click)=\"setIndex(i)\"\n [attr.aria-selected]=\"tab.inputs?.selected\" [attr.tabindex]=\"i\" aria-disabled=\"false\"\n [attr.data-pc-index]=\"i\" data-pc-section=\"headeraction\">\n <span class=\"p-tabview-title\">\n {{tab.inputs.header}}\n </span>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n <div class=\"p-tabview-panels\">\n <ng-content></ng-content>\n </div>\n</div>", styles: [".p-tabview.p-component{height:100%}.p-tabview .p-tabview-nav-content{scroll-padding-inline:3rem}.p-tabview-nav-content{overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;scrollbar-width:none;overscroll-behavior:contain auto}.p-tabview-nav{display:inline-flex;min-width:100%;margin:0;padding:0;list-style-type:none;flex:1 1 auto}.p-tabview .p-tabview-nav li .p-tabview-nav-link{border-width:0 0 2px 0;padding:1.25rem;font-weight:700;border-top-right-radius:6px;border-top-left-radius:6px;transition:box-shadow .2s;margin:0 0 -2px}.p-tabview-nav-link{appearance:none;background:transparent;border:none;font:inherit;color:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;display:flex;align-items:center;position:relative;text-decoration:none;overflow:hidden;width:100%}.p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link{border-color:var(--p-primary-color, #3B82F6);color:var(--p-primary-color, #3B82F6);background-color:transparent}.p-tabview-panels{height:calc(100% - 60px);overflow:auto}\n"] }]
|
|
2210
2238
|
}], propDecorators: { tabs: [{
|
|
2211
2239
|
type: Input
|
|
2212
2240
|
}], activeIndex: [{
|
|
@@ -2218,7 +2246,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2218
2246
|
*/
|
|
2219
2247
|
function getPointFromEvent(event) {
|
|
2220
2248
|
// NOTE: In firefox TouchEvent is only defined for touch capable devices
|
|
2221
|
-
const isTouchEvent = (e) =>
|
|
2249
|
+
const isTouchEvent = (e) => globalThis.TouchEvent && event instanceof TouchEvent;
|
|
2222
2250
|
if (isTouchEvent(event)) {
|
|
2223
2251
|
if (event.changedTouches.length === 0) {
|
|
2224
2252
|
return undefined;
|
|
@@ -3112,17 +3140,17 @@ class SplitComponent {
|
|
|
3112
3140
|
else {
|
|
3113
3141
|
const percentSize = (tempAreasPixelSizes[index] / dragStartContext.totalAreasPixelSize) * 100;
|
|
3114
3142
|
// Fix javascript only working with float numbers which are inaccurate compared to decimals
|
|
3115
|
-
area._internalSize.set(parseFloat(percentSize.toFixed(10)));
|
|
3143
|
+
area._internalSize.set(Number.parseFloat(percentSize.toFixed(10)));
|
|
3116
3144
|
}
|
|
3117
3145
|
});
|
|
3118
3146
|
this.dragProgressSubject.next(this.createDragInteractionEvent(this.draggedGutterIndex()));
|
|
3119
3147
|
}
|
|
3120
3148
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SplitComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3121
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SplitComponent, isStandalone: true, selector: "p-splitter", inputs: { gutterSize: { classPropertyName: "gutterSize", publicName: "gutterSize", isSignal: true, isRequired: false, transformFunction: null }, gutterStep: { classPropertyName: "gutterStep", publicName: "gutterStep", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, gutterClickDeltaPx: { classPropertyName: "gutterClickDeltaPx", publicName: "gutterClickDeltaPx", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, gutterAriaLabel: { classPropertyName: "gutterAriaLabel", publicName: "gutterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, restrictMove: { classPropertyName: "restrictMove", publicName: "restrictMove", isSignal: true, isRequired: false, transformFunction: null }, useTransition: { classPropertyName: "useTransition", publicName: "useTransition", isSignal: true, isRequired: false, transformFunction: null }, gutterDblClickDuration: { classPropertyName: "gutterDblClickDuration", publicName: "gutterDblClickDuration", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { gutterClick: "gutterClick", gutterDblClick: "gutterDblClick", dragStart: "dragStart", dragEnd: "dragEnd", transitionEnd: "transitionEnd" }, host: { properties: { "class": "this.hostClassesBinding", "dir": "this.hostDirBinding" } }, queries: [{ propertyName: "_areas", predicate: SPLIT_AREA_CONTRACT, isSignal: true }, { propertyName: "customGutter", first: true, predicate: SplitGutterDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-content></ng-content>\n@for (area of _areas(); track area) {\n @if (!$last) {\n <div\n #gutter\n class=\"as-split-gutter\"\n role=\"separator\"\n tabindex=\"0\"\n [attr.aria-label]=\"gutterAriaLabel()\"\n [attr.aria-orientation]=\"direction()\"\n [attr.aria-valuemin]=\"getAriaValue(area.minSize())\"\n [attr.aria-valuemax]=\"getAriaValue(area.maxSize())\"\n [attr.aria-valuenow]=\"getAriaValue(area._internalSize())\"\n [attr.aria-valuetext]=\"getAriaAreaSizeText(area)\"\n [ngStyle]=\"getGutterGridStyle($index + 1)\"\n [class.as-dragged]=\"draggedGutterIndex() === $index\"\n asSplitCustomEventsBehavior\n [asSplitCustomMultiClickThreshold]=\"gutterDblClickDuration()\"\n [asSplitCustomClickDeltaInPx]=\"gutterClickDeltaPx()\"\n (asSplitCustomClick)=\"gutterClicked($index)\"\n (asSplitCustomDblClick)=\"gutterDoubleClicked($index)\"\n (asSplitCustomMouseDown)=\"gutterMouseDown($event, gutter, $index, $index, $index + 1)\"\n (asSplitCustomKeyDown)=\"gutterKeyDown($event, $index, $index, $index + 1)\"\n >\n @if (customGutter()?.template) {\n <ng-container *asSplitGutterDynamicInjector=\"$index + 1; let injector\">\n <ng-container\n *ngTemplateOutlet=\"\n customGutter().template;\n context: {\n areaBefore: area,\n areaAfter: _areas()[$index + 1],\n gutterNum: $index + 1,\n first: $first,\n last: $index === _areas().length - 2,\n isDragged: draggedGutterIndex() === $index\n };\n injector: injector\n \"\n ></ng-container>\n </ng-container>\n } @else {\n <div class=\"as-split-gutter-icon\"></div>\n }\n </div>\n }\n}\n", styles: ["@property --as-gutter-background-color{syntax: \"<color>\"; inherits: true; initial-value: #eeeeee;}@property --as-gutter-icon-horizontal{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-gutter-icon-vertical{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC);}@property --as-gutter-icon-disabled{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-transition-duration{syntax: \"<time>\"; inherits: true; initial-value: .3s;}@property --as-gutter-disabled-cursor{syntax: \"*\"; inherits: true; initial-value: default;}:host{--_as-gutter-background-color: var(--as-gutter-background-color, #eeeeee);--_as-gutter-icon-horizontal: var(--as-gutter-icon-horizontal, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-gutter-icon-vertical: var(--as-gutter-icon-vertical, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC));--_as-gutter-icon-disabled: var(--as-gutter-icon-disabled, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-transition-duration: var(--as-transition-duration, .3s);--_as-gutter-disabled-cursor: var(--as-gutter-disabled-cursor, default)
|
|
3149
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SplitComponent, isStandalone: true, selector: "p-splitter", inputs: { gutterSize: { classPropertyName: "gutterSize", publicName: "gutterSize", isSignal: true, isRequired: false, transformFunction: null }, gutterStep: { classPropertyName: "gutterStep", publicName: "gutterStep", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, gutterClickDeltaPx: { classPropertyName: "gutterClickDeltaPx", publicName: "gutterClickDeltaPx", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, gutterAriaLabel: { classPropertyName: "gutterAriaLabel", publicName: "gutterAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, restrictMove: { classPropertyName: "restrictMove", publicName: "restrictMove", isSignal: true, isRequired: false, transformFunction: null }, useTransition: { classPropertyName: "useTransition", publicName: "useTransition", isSignal: true, isRequired: false, transformFunction: null }, gutterDblClickDuration: { classPropertyName: "gutterDblClickDuration", publicName: "gutterDblClickDuration", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { gutterClick: "gutterClick", gutterDblClick: "gutterDblClick", dragStart: "dragStart", dragEnd: "dragEnd", transitionEnd: "transitionEnd" }, host: { properties: { "class": "this.hostClassesBinding", "dir": "this.hostDirBinding" } }, queries: [{ propertyName: "_areas", predicate: SPLIT_AREA_CONTRACT, isSignal: true }, { propertyName: "customGutter", first: true, predicate: SplitGutterDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-content></ng-content>\n@for (area of _areas(); track area) {\n @if (!$last) {\n <div\n #gutter\n class=\"as-split-gutter\"\n role=\"separator\"\n tabindex=\"0\"\n [attr.aria-label]=\"gutterAriaLabel()\"\n [attr.aria-orientation]=\"direction()\"\n [attr.aria-valuemin]=\"getAriaValue(area.minSize())\"\n [attr.aria-valuemax]=\"getAriaValue(area.maxSize())\"\n [attr.aria-valuenow]=\"getAriaValue(area._internalSize())\"\n [attr.aria-valuetext]=\"getAriaAreaSizeText(area)\"\n [ngStyle]=\"getGutterGridStyle($index + 1)\"\n [class.as-dragged]=\"draggedGutterIndex() === $index\"\n asSplitCustomEventsBehavior\n [asSplitCustomMultiClickThreshold]=\"gutterDblClickDuration()\"\n [asSplitCustomClickDeltaInPx]=\"gutterClickDeltaPx()\"\n (asSplitCustomClick)=\"gutterClicked($index)\"\n (asSplitCustomDblClick)=\"gutterDoubleClicked($index)\"\n (asSplitCustomMouseDown)=\"gutterMouseDown($event, gutter, $index, $index, $index + 1)\"\n (asSplitCustomKeyDown)=\"gutterKeyDown($event, $index, $index, $index + 1)\"\n >\n @if (customGutter()?.template) {\n <ng-container *asSplitGutterDynamicInjector=\"$index + 1; let injector\">\n <ng-container\n *ngTemplateOutlet=\"\n customGutter().template;\n context: {\n areaBefore: area,\n areaAfter: _areas()[$index + 1],\n gutterNum: $index + 1,\n first: $first,\n last: $index === _areas().length - 2,\n isDragged: draggedGutterIndex() === $index\n };\n injector: injector\n \"\n ></ng-container>\n </ng-container>\n } @else {\n <div class=\"as-split-gutter-icon\"></div>\n }\n </div>\n }\n}\n", styles: ["@property --as-gutter-background-color{syntax: \"<color>\"; inherits: true; initial-value: #eeeeee;}@property --as-gutter-icon-horizontal{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-gutter-icon-vertical{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC);}@property --as-gutter-icon-disabled{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-transition-duration{syntax: \"<time>\"; inherits: true; initial-value: .3s;}@property --as-gutter-disabled-cursor{syntax: \"*\"; inherits: true; initial-value: default;}:host{--_as-gutter-background-color: var(--as-gutter-background-color, #eeeeee);--_as-gutter-icon-horizontal: var(--as-gutter-icon-horizontal, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-gutter-icon-vertical: var(--as-gutter-icon-vertical, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC));--_as-gutter-icon-disabled: var(--as-gutter-icon-disabled, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-transition-duration: var(--as-transition-duration, .3s);--_as-gutter-disabled-cursor: var(--as-gutter-disabled-cursor, default);display:grid;overflow:hidden;height:100%;width:100%}:host(.as-transition){transition:grid-template var(--_as-transition-duration)}.as-split-gutter{background-color:var(--_as-gutter-background-color);display:flex;align-items:center;justify-content:center;touch-action:none}:host(.as-horizontal)>.as-split-gutter{cursor:col-resize;height:100%}:host(.as-vertical)>.as-split-gutter{cursor:row-resize;width:100%}:host(.as-disabled)>.as-split-gutter{cursor:var(--_as-gutter-disabled-cursor)}.as-split-gutter-icon{width:100%;height:100%;background-position:center center;background-repeat:no-repeat}:host(.as-horizontal)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-horizontal)}:host(.as-vertical)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-vertical)}:host(.as-disabled)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-disabled)}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: SplitCustomEventsBehaviorDirective, selector: "[asSplitCustomEventsBehavior]", inputs: ["asSplitCustomMultiClickThreshold", "asSplitCustomClickDeltaInPx"], outputs: ["asSplitCustomMouseDown", "asSplitCustomClick", "asSplitCustomDblClick", "asSplitCustomKeyDown"] }, { kind: "directive", type: SplitGutterDynamicInjectorDirective, selector: "[asSplitGutterDynamicInjector]", inputs: ["asSplitGutterDynamicInjector"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3122
3150
|
}
|
|
3123
3151
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SplitComponent, decorators: [{
|
|
3124
3152
|
type: Component,
|
|
3125
|
-
args: [{ selector: 'p-splitter', imports: [NgStyle, SplitCustomEventsBehaviorDirective, SplitGutterDynamicInjectorDirective, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\n@for (area of _areas(); track area) {\n @if (!$last) {\n <div\n #gutter\n class=\"as-split-gutter\"\n role=\"separator\"\n tabindex=\"0\"\n [attr.aria-label]=\"gutterAriaLabel()\"\n [attr.aria-orientation]=\"direction()\"\n [attr.aria-valuemin]=\"getAriaValue(area.minSize())\"\n [attr.aria-valuemax]=\"getAriaValue(area.maxSize())\"\n [attr.aria-valuenow]=\"getAriaValue(area._internalSize())\"\n [attr.aria-valuetext]=\"getAriaAreaSizeText(area)\"\n [ngStyle]=\"getGutterGridStyle($index + 1)\"\n [class.as-dragged]=\"draggedGutterIndex() === $index\"\n asSplitCustomEventsBehavior\n [asSplitCustomMultiClickThreshold]=\"gutterDblClickDuration()\"\n [asSplitCustomClickDeltaInPx]=\"gutterClickDeltaPx()\"\n (asSplitCustomClick)=\"gutterClicked($index)\"\n (asSplitCustomDblClick)=\"gutterDoubleClicked($index)\"\n (asSplitCustomMouseDown)=\"gutterMouseDown($event, gutter, $index, $index, $index + 1)\"\n (asSplitCustomKeyDown)=\"gutterKeyDown($event, $index, $index, $index + 1)\"\n >\n @if (customGutter()?.template) {\n <ng-container *asSplitGutterDynamicInjector=\"$index + 1; let injector\">\n <ng-container\n *ngTemplateOutlet=\"\n customGutter().template;\n context: {\n areaBefore: area,\n areaAfter: _areas()[$index + 1],\n gutterNum: $index + 1,\n first: $first,\n last: $index === _areas().length - 2,\n isDragged: draggedGutterIndex() === $index\n };\n injector: injector\n \"\n ></ng-container>\n </ng-container>\n } @else {\n <div class=\"as-split-gutter-icon\"></div>\n }\n </div>\n }\n}\n", styles: ["@property --as-gutter-background-color{syntax: \"<color>\"; inherits: true; initial-value: #eeeeee;}@property --as-gutter-icon-horizontal{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-gutter-icon-vertical{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC);}@property --as-gutter-icon-disabled{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-transition-duration{syntax: \"<time>\"; inherits: true; initial-value: .3s;}@property --as-gutter-disabled-cursor{syntax: \"*\"; inherits: true; initial-value: default;}:host{--_as-gutter-background-color: var(--as-gutter-background-color, #eeeeee);--_as-gutter-icon-horizontal: var(--as-gutter-icon-horizontal, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-gutter-icon-vertical: var(--as-gutter-icon-vertical, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC));--_as-gutter-icon-disabled: var(--as-gutter-icon-disabled, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-transition-duration: var(--as-transition-duration, .3s);--_as-gutter-disabled-cursor: var(--as-gutter-disabled-cursor, default)
|
|
3153
|
+
args: [{ selector: 'p-splitter', imports: [NgStyle, SplitCustomEventsBehaviorDirective, SplitGutterDynamicInjectorDirective, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\n@for (area of _areas(); track area) {\n @if (!$last) {\n <div\n #gutter\n class=\"as-split-gutter\"\n role=\"separator\"\n tabindex=\"0\"\n [attr.aria-label]=\"gutterAriaLabel()\"\n [attr.aria-orientation]=\"direction()\"\n [attr.aria-valuemin]=\"getAriaValue(area.minSize())\"\n [attr.aria-valuemax]=\"getAriaValue(area.maxSize())\"\n [attr.aria-valuenow]=\"getAriaValue(area._internalSize())\"\n [attr.aria-valuetext]=\"getAriaAreaSizeText(area)\"\n [ngStyle]=\"getGutterGridStyle($index + 1)\"\n [class.as-dragged]=\"draggedGutterIndex() === $index\"\n asSplitCustomEventsBehavior\n [asSplitCustomMultiClickThreshold]=\"gutterDblClickDuration()\"\n [asSplitCustomClickDeltaInPx]=\"gutterClickDeltaPx()\"\n (asSplitCustomClick)=\"gutterClicked($index)\"\n (asSplitCustomDblClick)=\"gutterDoubleClicked($index)\"\n (asSplitCustomMouseDown)=\"gutterMouseDown($event, gutter, $index, $index, $index + 1)\"\n (asSplitCustomKeyDown)=\"gutterKeyDown($event, $index, $index, $index + 1)\"\n >\n @if (customGutter()?.template) {\n <ng-container *asSplitGutterDynamicInjector=\"$index + 1; let injector\">\n <ng-container\n *ngTemplateOutlet=\"\n customGutter().template;\n context: {\n areaBefore: area,\n areaAfter: _areas()[$index + 1],\n gutterNum: $index + 1,\n first: $first,\n last: $index === _areas().length - 2,\n isDragged: draggedGutterIndex() === $index\n };\n injector: injector\n \"\n ></ng-container>\n </ng-container>\n } @else {\n <div class=\"as-split-gutter-icon\"></div>\n }\n </div>\n }\n}\n", styles: ["@property --as-gutter-background-color{syntax: \"<color>\"; inherits: true; initial-value: #eeeeee;}@property --as-gutter-icon-horizontal{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-gutter-icon-vertical{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC);}@property --as-gutter-icon-disabled{syntax: \"<url>\"; inherits: true; initial-value: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==);}@property --as-transition-duration{syntax: \"<time>\"; inherits: true; initial-value: .3s;}@property --as-gutter-disabled-cursor{syntax: \"*\"; inherits: true; initial-value: default;}:host{--_as-gutter-background-color: var(--as-gutter-background-color, #eeeeee);--_as-gutter-icon-horizontal: var(--as-gutter-icon-horizontal, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-gutter-icon-vertical: var(--as-gutter-icon-vertical, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAMAAABl/6zIAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAABRJREFUeAFjYGRkwIMJSeMHlBkOABP7AEGzSuPKAAAAAElFTkSuQmCC));--_as-gutter-icon-disabled: var(--as-gutter-icon-disabled, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==));--_as-transition-duration: var(--as-transition-duration, .3s);--_as-gutter-disabled-cursor: var(--as-gutter-disabled-cursor, default);display:grid;overflow:hidden;height:100%;width:100%}:host(.as-transition){transition:grid-template var(--_as-transition-duration)}.as-split-gutter{background-color:var(--_as-gutter-background-color);display:flex;align-items:center;justify-content:center;touch-action:none}:host(.as-horizontal)>.as-split-gutter{cursor:col-resize;height:100%}:host(.as-vertical)>.as-split-gutter{cursor:row-resize;width:100%}:host(.as-disabled)>.as-split-gutter{cursor:var(--_as-gutter-disabled-cursor)}.as-split-gutter-icon{width:100%;height:100%;background-position:center center;background-repeat:no-repeat}:host(.as-horizontal)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-horizontal)}:host(.as-vertical)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-vertical)}:host(.as-disabled)>.as-split-gutter>.as-split-gutter-icon{background-image:var(--_as-gutter-icon-disabled)}\n"] }]
|
|
3126
3154
|
}], ctorParameters: () => [], propDecorators: { _areas: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SPLIT_AREA_CONTRACT), { isSignal: true }] }], customGutter: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SplitGutterDirective), { isSignal: true }] }], gutterSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterSize", required: false }] }], gutterStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterStep", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], gutterClickDeltaPx: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterClickDeltaPx", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], gutterAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterAriaLabel", required: false }] }], restrictMove: [{ type: i0.Input, args: [{ isSignal: true, alias: "restrictMove", required: false }] }], useTransition: [{ type: i0.Input, args: [{ isSignal: true, alias: "useTransition", required: false }] }], gutterDblClickDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "gutterDblClickDuration", required: false }] }], gutterClick: [{ type: i0.Output, args: ["gutterClick"] }], gutterDblClick: [{ type: i0.Output, args: ["gutterDblClick"] }], dragStart: [{ type: i0.Output, args: ["dragStart"] }], dragEnd: [{ type: i0.Output, args: ["dragEnd"] }], transitionEnd: [{ type: i0.Output, args: ["transitionEnd"] }], hostClassesBinding: [{
|
|
3127
3155
|
type: HostBinding,
|
|
3128
3156
|
args: ['class']
|
|
@@ -3188,7 +3216,7 @@ class SplitAreaComponent {
|
|
|
3188
3216
|
/**
|
|
3189
3217
|
* Indice corrente per index, usato per posizionamento o navigazione nel componente.
|
|
3190
3218
|
*/
|
|
3191
|
-
index = computed(() => this.split._areas().
|
|
3219
|
+
index = computed(() => this.split._areas().indexOf(this), ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
|
|
3192
3220
|
/**
|
|
3193
3221
|
* Proprieta di stato del componente per grid area num, usata dalla logica interna e dal template.
|
|
3194
3222
|
*/
|
|
@@ -3352,6 +3380,23 @@ class DynamicDashboardTemplateComponent {
|
|
|
3352
3380
|
* does not trigger a spurious commit.
|
|
3353
3381
|
*/
|
|
3354
3382
|
static lastDesignerGestureTimestamp = 0;
|
|
3383
|
+
/**
|
|
3384
|
+
* Dimensione della griglia di snap (px) per drag/resize nel designer.
|
|
3385
|
+
* 0 = snap disattivato (comportamento libero, default). Impostato staticamente
|
|
3386
|
+
* da DesignerComponent quando l'utente attiva "snap alla griglia": statico per
|
|
3387
|
+
* lo stesso motivo di lastDesignerGestureTimestamp (ogni elemento ha la propria
|
|
3388
|
+
* istanza di template). onMoving/onResizing arrotondano posizione/dimensione al
|
|
3389
|
+
* multiplo piu' vicino quando > 0.
|
|
3390
|
+
*/
|
|
3391
|
+
static snapGridSize = 0;
|
|
3392
|
+
/** Arrotonda un valore al multiplo di `snapGridSize` (no-op se snap off). */
|
|
3393
|
+
static snap(value) {
|
|
3394
|
+
const g = DynamicDashboardTemplateComponent.snapGridSize;
|
|
3395
|
+
if (!g || g <= 0) {
|
|
3396
|
+
return value;
|
|
3397
|
+
}
|
|
3398
|
+
return Math.round(value / g) * g;
|
|
3399
|
+
}
|
|
3355
3400
|
/**
|
|
3356
3401
|
* Input dal componente padre per inputs; usata nella configurazione e nel rendering del componente.
|
|
3357
3402
|
*/
|
|
@@ -3437,7 +3482,7 @@ class DynamicDashboardTemplateComponent {
|
|
|
3437
3482
|
let cache = [];
|
|
3438
3483
|
let str = JSON.stringify(obj, function (key, value) {
|
|
3439
3484
|
if (typeof value === "object" && value !== null) {
|
|
3440
|
-
if (cache.
|
|
3485
|
+
if (cache.includes(value)) {
|
|
3441
3486
|
// Circular reference found, discard key
|
|
3442
3487
|
return;
|
|
3443
3488
|
}
|
|
@@ -3456,12 +3501,8 @@ class DynamicDashboardTemplateComponent {
|
|
|
3456
3501
|
* @param parentElement Parametro utilizzato dal metodo nel flusso elaborativo.
|
|
3457
3502
|
*/
|
|
3458
3503
|
onResizing(event, element, parentElement) {
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
else {
|
|
3462
|
-
}
|
|
3463
|
-
element.inputs.width = event.size.width + 'px';
|
|
3464
|
-
element.inputs.height = event.size.height + 'px';
|
|
3504
|
+
element.inputs.width = DynamicDashboardTemplateComponent.snap(event.size.width) + 'px';
|
|
3505
|
+
element.inputs.height = DynamicDashboardTemplateComponent.snap(event.size.height) + 'px';
|
|
3465
3506
|
// Mark "designer mutation in progress" so the next window:mouseup in
|
|
3466
3507
|
// DesignerComponent commits a history snapshot. See the field doc.
|
|
3467
3508
|
DynamicDashboardTemplateComponent.lastDesignerGestureTimestamp = Date.now();
|
|
@@ -3472,13 +3513,9 @@ class DynamicDashboardTemplateComponent {
|
|
|
3472
3513
|
* @param element Parametro utilizzato dal metodo nel flusso elaborativo.
|
|
3473
3514
|
*/
|
|
3474
3515
|
onMoving($event, element) {
|
|
3475
|
-
// debugger;
|
|
3476
|
-
// element.inputs.left = $event.position.x + 'px';
|
|
3477
|
-
// element.inputs.top = $event.position.y + 'px';
|
|
3478
|
-
// element.inputs.position = 'absolute';
|
|
3479
3516
|
let scaleX = 1, skewY = 0, skewX = 0, scaleY = 1;
|
|
3480
3517
|
if (element.inputs.transform) {
|
|
3481
|
-
let matrix = element.inputs.transform.replace('matrix(', '').replace(')', '').split(',').map(x => parseFloat(x));
|
|
3518
|
+
let matrix = element.inputs.transform.replace('matrix(', '').replace(')', '').split(',').map(x => Number.parseFloat(x));
|
|
3482
3519
|
if (matrix.length >= 1) {
|
|
3483
3520
|
scaleX = matrix[0];
|
|
3484
3521
|
}
|
|
@@ -3492,6 +3529,11 @@ class DynamicDashboardTemplateComponent {
|
|
|
3492
3529
|
scaleY = matrix[3];
|
|
3493
3530
|
}
|
|
3494
3531
|
}
|
|
3532
|
+
// La direttiva ngx-draggable-dom snappa gia' la traslazione alla griglia
|
|
3533
|
+
// ASSOLUTA (offsetLeft/Top + delta), quindi $event.position e' gia' allineato.
|
|
3534
|
+
// NON ri-snappare con DynamicDashboardTemplateComponent.snap(): e' un
|
|
3535
|
+
// arrotondamento grid-STEP che, applicato a una traslazione gia' assoluta,
|
|
3536
|
+
// la disallineava (l'offset visto sui box trascinati).
|
|
3495
3537
|
element.inputs.transform = `matrix(${scaleX}, ${skewY}, ${skewX}, ${scaleY}, ${$event.position.x}, ${$event.position.y})`;
|
|
3496
3538
|
// Mark "designer mutation in progress" so the next window:mouseup in
|
|
3497
3539
|
// DesignerComponent commits a history snapshot. See the field doc.
|
|
@@ -3624,7 +3666,7 @@ class DynamicDashboardTemplateComponent {
|
|
|
3624
3666
|
* Abilita drag/drop solo in contesto designer.
|
|
3625
3667
|
*/
|
|
3626
3668
|
isDesignerMode() {
|
|
3627
|
-
const hash = String(
|
|
3669
|
+
const hash = String(globalThis?.location?.hash || '').toLowerCase();
|
|
3628
3670
|
return hash.includes('/designer');
|
|
3629
3671
|
}
|
|
3630
3672
|
/**
|
|
@@ -3640,9 +3682,9 @@ class DynamicDashboardTemplateComponent {
|
|
|
3640
3682
|
*/
|
|
3641
3683
|
static getComponentFromTemplate(template, route, templateField) {
|
|
3642
3684
|
const normalizedTemplate = String(template || '')
|
|
3643
|
-
.
|
|
3644
|
-
.
|
|
3645
|
-
.
|
|
3685
|
+
.replaceAll(/wuic-parametric-dialog(?!-lazy)/g, 'wuic-parametric-dialog-lazy')
|
|
3686
|
+
.replaceAll(/ngxDraggableDom\s*=\s*"true"/g, '[ngxDraggableDom]="isDesignerMode()"')
|
|
3687
|
+
.replaceAll(/ngxDraggableDom\s*=\s*'true'/g, '[ngxDraggableDom]="isDesignerMode()"');
|
|
3646
3688
|
return DynamicCompilerService.compile({
|
|
3647
3689
|
template: normalizedTemplate,
|
|
3648
3690
|
baseClass: DynamicDashboardTemplateComponent,
|
|
@@ -3724,6 +3766,36 @@ class DashboardComponent {
|
|
|
3724
3766
|
* Input dal componente padre per dashboard elements; usata nella configurazione e nel rendering del componente.
|
|
3725
3767
|
*/
|
|
3726
3768
|
dashboardElements;
|
|
3769
|
+
/**
|
|
3770
|
+
* Chrome "card" opzionale per i widget top-level (surface + bordo + radius
|
|
3771
|
+
* + shadow dai token --wuic-*). Opt-in dal boardcontent wrapper-object
|
|
3772
|
+
* (`{"widgetChrome": true, "dashboardElements": [...]}`): default false per
|
|
3773
|
+
* non alterare l'aspetto delle dashboard esistenti. Parser tollerante
|
|
3774
|
+
* boolean-like come le altre config archetype.
|
|
3775
|
+
*/
|
|
3776
|
+
widgetChrome = false;
|
|
3777
|
+
get hasWidgetChrome() {
|
|
3778
|
+
const v = this.widgetChrome;
|
|
3779
|
+
return v === true || v === 1 || String(v).trim().toLowerCase() === 'true' || String(v).trim() === '1';
|
|
3780
|
+
}
|
|
3781
|
+
/**
|
|
3782
|
+
* Layout assoluto vs flusso normale: il "modo" con cui la dashboard e' stata
|
|
3783
|
+
* SALVATA e' codificato per-elemento nel boardcontent (`inputs.position`).
|
|
3784
|
+
* Se almeno un figlio diretto e' `absolute`/`fixed`, questo host deve diventare
|
|
3785
|
+
* il contesto di posizionamento dei figli (altrimenti risalgono al viewport e
|
|
3786
|
+
* finiscono sopra il menu fixed). In flusso normale la classe NON viene
|
|
3787
|
+
* applicata -> nessun cambiamento di layout, retrocompatibile.
|
|
3788
|
+
*
|
|
3789
|
+
* Vale sia top-level (host = area dashboard sotto il menu) sia annidato
|
|
3790
|
+
* (host = dentro il container `position:relative` gia' salvato dal designer):
|
|
3791
|
+
* in entrambi i casi il figlio absolute risolve left/top rispetto a questo box.
|
|
3792
|
+
*/
|
|
3793
|
+
get hasAbsoluteLayout() {
|
|
3794
|
+
return (this.dashboardElements || []).some((el) => {
|
|
3795
|
+
const pos = String(el?.inputs?.position || '').trim().toLowerCase();
|
|
3796
|
+
return pos === 'absolute' || pos === 'fixed';
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3727
3799
|
/**
|
|
3728
3800
|
* function Object() { [native code] }
|
|
3729
3801
|
*/
|
|
@@ -3755,13 +3827,21 @@ class DashboardComponent {
|
|
|
3755
3827
|
}
|
|
3756
3828
|
;
|
|
3757
3829
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3758
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DashboardComponent, isStandalone: true, selector: "wuic-dashboard", inputs: { dashboardElements: "dashboardElements" }, ngImport: i0, template: "@for (dashboardElement of dashboardElements; track dashboardElement.uniqueName) {\r\n<ng-container *ngComponentOutlet=\"getComponent(dashboardElement); inputs: getComponentInputs(dashboardElement)\"></ng-container>\r\n}\r\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
|
|
3830
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DashboardComponent, isStandalone: true, selector: "wuic-dashboard", inputs: { dashboardElements: "dashboardElements", widgetChrome: "widgetChrome" }, host: { properties: { "class.wuic-dashboard-widget-chrome": "this.hasWidgetChrome", "class.wuic-dashboard-absolute-layout": "this.hasAbsoluteLayout" } }, ngImport: i0, template: "@for (dashboardElement of dashboardElements; track dashboardElement.uniqueName) {\r\n<ng-container *ngComponentOutlet=\"getComponent(dashboardElement); inputs: getComponentInputs(dashboardElement)\"></ng-container>\r\n}\r\n", styles: [":host(.wuic-dashboard-absolute-layout){position:relative;display:block;width:100%;height:100%}:host(.wuic-dashboard-widget-chrome){display:block;padding:var(--wuic-space-3, 16px)}:host(.wuic-dashboard-widget-chrome)>:not(wuic-data-source):not(.datasource-descriptor){display:block;background:var(--wuic-surface, #ffffff);border:1px solid var(--wuic-border-soft, #e6ebf2);border-radius:var(--wuic-radius-lg, 10px);box-shadow:var(--wuic-shadow-1, 0 1px 3px rgba(15, 23, 42, .08));padding:var(--wuic-space-3, 16px);margin-bottom:var(--wuic-space-3, 16px)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }] });
|
|
3759
3831
|
}
|
|
3760
3832
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DashboardComponent, decorators: [{
|
|
3761
3833
|
type: Component,
|
|
3762
|
-
args: [{ selector: 'wuic-dashboard', imports: [NgComponentOutlet], template: "@for (dashboardElement of dashboardElements; track dashboardElement.uniqueName) {\r\n<ng-container *ngComponentOutlet=\"getComponent(dashboardElement); inputs: getComponentInputs(dashboardElement)\"></ng-container>\r\n}\r\n" }]
|
|
3834
|
+
args: [{ selector: 'wuic-dashboard', imports: [NgComponentOutlet], template: "@for (dashboardElement of dashboardElements; track dashboardElement.uniqueName) {\r\n<ng-container *ngComponentOutlet=\"getComponent(dashboardElement); inputs: getComponentInputs(dashboardElement)\"></ng-container>\r\n}\r\n", styles: [":host(.wuic-dashboard-absolute-layout){position:relative;display:block;width:100%;height:100%}:host(.wuic-dashboard-widget-chrome){display:block;padding:var(--wuic-space-3, 16px)}:host(.wuic-dashboard-widget-chrome)>:not(wuic-data-source):not(.datasource-descriptor){display:block;background:var(--wuic-surface, #ffffff);border:1px solid var(--wuic-border-soft, #e6ebf2);border-radius:var(--wuic-radius-lg, 10px);box-shadow:var(--wuic-shadow-1, 0 1px 3px rgba(15, 23, 42, .08));padding:var(--wuic-space-3, 16px);margin-bottom:var(--wuic-space-3, 16px)}\n"] }]
|
|
3763
3835
|
}], ctorParameters: () => [], propDecorators: { dashboardElements: [{
|
|
3764
3836
|
type: Input
|
|
3837
|
+
}], widgetChrome: [{
|
|
3838
|
+
type: Input
|
|
3839
|
+
}], hasWidgetChrome: [{
|
|
3840
|
+
type: HostBinding,
|
|
3841
|
+
args: ['class.wuic-dashboard-widget-chrome']
|
|
3842
|
+
}], hasAbsoluteLayout: [{
|
|
3843
|
+
type: HostBinding,
|
|
3844
|
+
args: ['class.wuic-dashboard-absolute-layout']
|
|
3765
3845
|
}] } });
|
|
3766
3846
|
|
|
3767
3847
|
class CssSheetEditorComponent {
|
|
@@ -3829,8 +3909,8 @@ class CssSheetEditorComponent {
|
|
|
3829
3909
|
if (!this.valore) {
|
|
3830
3910
|
this.valore = '#FFFFFF';
|
|
3831
3911
|
}
|
|
3832
|
-
this.pickerValue =
|
|
3833
|
-
this.alphaValue =
|
|
3912
|
+
this.pickerValue = toHsbaModel(this.valore);
|
|
3913
|
+
this.alphaValue = clampAlpha(this.pickerValue?.a);
|
|
3834
3914
|
}
|
|
3835
3915
|
/**
|
|
3836
3916
|
* Gestisce i cambiamenti degli input aggiornando lo stato derivato e le dipendenze del componente.
|
|
@@ -4038,11 +4118,11 @@ class CssSheetEditorComponent {
|
|
|
4038
4118
|
onPendingColorChanged(value) {
|
|
4039
4119
|
const raw = value;
|
|
4040
4120
|
const hsba = {
|
|
4041
|
-
...(raw
|
|
4042
|
-
a:
|
|
4121
|
+
...(raw),
|
|
4122
|
+
a: clampAlpha(raw?.a ?? this.alphaValue)
|
|
4043
4123
|
};
|
|
4044
|
-
this.alphaValue =
|
|
4045
|
-
const newValue =
|
|
4124
|
+
this.alphaValue = clampAlpha(hsba?.a);
|
|
4125
|
+
const newValue = toPersistedColor(raw, this.alphaValue);
|
|
4046
4126
|
this.pickerValue = raw;
|
|
4047
4127
|
this.pendingStyleValue = this.valore = newValue;
|
|
4048
4128
|
}
|
|
@@ -4144,7 +4224,7 @@ class CssSheetEditorComponent {
|
|
|
4144
4224
|
* @returns Stringa risultante calcolata da `normalizePath` per chiavi/label o valori testuali.
|
|
4145
4225
|
*/
|
|
4146
4226
|
normalizePath(path) {
|
|
4147
|
-
return String(path || '').trim().
|
|
4227
|
+
return String(path || '').trim().replaceAll('\\', '/');
|
|
4148
4228
|
}
|
|
4149
4229
|
/**
|
|
4150
4230
|
* Costruisce il payload/struttura risultante in `buildAssetSheetPath` partendo dai dati correnti.
|
|
@@ -4160,11 +4240,11 @@ class CssSheetEditorComponent {
|
|
|
4160
4240
|
if (normalizedName.includes('/') || normalizedName.includes('\\')) {
|
|
4161
4241
|
return '';
|
|
4162
4242
|
}
|
|
4163
|
-
normalizedName = normalizedName.
|
|
4243
|
+
normalizedName = normalizedName.replaceAll(/\s+/g, '-');
|
|
4164
4244
|
if (!normalizedName.toLowerCase().endsWith('.css')) {
|
|
4165
4245
|
normalizedName += '.css';
|
|
4166
4246
|
}
|
|
4167
|
-
const folder = String(this.assetsFolder || '').trim().
|
|
4247
|
+
const folder = String(this.assetsFolder || '').trim().replaceAll('\\', '/').replaceAll(/^\/+|\/+$/g, '');
|
|
4168
4248
|
if (!folder) {
|
|
4169
4249
|
return '';
|
|
4170
4250
|
}
|
|
@@ -4208,167 +4288,16 @@ class CssSheetEditorComponent {
|
|
|
4208
4288
|
return found || null;
|
|
4209
4289
|
}
|
|
4210
4290
|
async onAlphaChange(rawAlpha) {
|
|
4211
|
-
this.alphaValue =
|
|
4291
|
+
this.alphaValue = clampAlpha(rawAlpha);
|
|
4212
4292
|
this.pickerValue = {
|
|
4213
|
-
...(this.pickerValue
|
|
4293
|
+
...(this.pickerValue),
|
|
4214
4294
|
a: this.alphaValue
|
|
4215
4295
|
};
|
|
4216
|
-
const newValue =
|
|
4296
|
+
const newValue = toPersistedColor(this.pickerValue, this.alphaValue);
|
|
4217
4297
|
this.pendingStyleValue = this.valore = newValue;
|
|
4218
4298
|
}
|
|
4219
|
-
toPersistedColor(raw) {
|
|
4220
|
-
if (raw === undefined || raw === null) {
|
|
4221
|
-
return '';
|
|
4222
|
-
}
|
|
4223
|
-
if (typeof raw === 'string') {
|
|
4224
|
-
return raw.trim();
|
|
4225
|
-
}
|
|
4226
|
-
const h = Number(raw?.h);
|
|
4227
|
-
const s = Number(raw?.s);
|
|
4228
|
-
const b = Number(raw?.b);
|
|
4229
|
-
let a = Number(raw?.a);
|
|
4230
|
-
if (!Number.isFinite(h) || !Number.isFinite(s) || !Number.isFinite(b)) {
|
|
4231
|
-
return String(raw);
|
|
4232
|
-
}
|
|
4233
|
-
if (!Number.isFinite(a)) {
|
|
4234
|
-
a = this.alphaValue;
|
|
4235
|
-
}
|
|
4236
|
-
a = this.clampAlpha(a);
|
|
4237
|
-
return this.hsbaToRgbaString(h, s, b, a);
|
|
4238
|
-
}
|
|
4239
|
-
toHsbaModel(value) {
|
|
4240
|
-
const fallback = { h: 0, s: 0, b: 100, a: 1 };
|
|
4241
|
-
if (value === undefined || value === null) {
|
|
4242
|
-
return fallback;
|
|
4243
|
-
}
|
|
4244
|
-
if (typeof value === 'object' && value?.h !== undefined && value?.s !== undefined && value?.b !== undefined) {
|
|
4245
|
-
return {
|
|
4246
|
-
h: Number(value.h) || 0,
|
|
4247
|
-
s: Number(value.s) || 0,
|
|
4248
|
-
b: Number(value.b) || 0,
|
|
4249
|
-
a: Number.isFinite(Number(value.a)) ? Number(value.a) : 1
|
|
4250
|
-
};
|
|
4251
|
-
}
|
|
4252
|
-
const str = String(value).trim();
|
|
4253
|
-
if (!str) {
|
|
4254
|
-
return fallback;
|
|
4255
|
-
}
|
|
4256
|
-
const rgbMatch = str.match(/^rgba?\(([^)]+)\)$/i);
|
|
4257
|
-
if (rgbMatch) {
|
|
4258
|
-
const parts = rgbMatch[1].split(',').map((x) => x.trim());
|
|
4259
|
-
const r = Number(parts[0]);
|
|
4260
|
-
const g = Number(parts[1]);
|
|
4261
|
-
const b = Number(parts[2]);
|
|
4262
|
-
const a = parts.length > 3 ? Number(parts[3]) : 1;
|
|
4263
|
-
if (Number.isFinite(r) && Number.isFinite(g) && Number.isFinite(b)) {
|
|
4264
|
-
return this.rgbaToHsbaModel(r, g, b, Number.isFinite(a) ? a : 1);
|
|
4265
|
-
}
|
|
4266
|
-
return fallback;
|
|
4267
|
-
}
|
|
4268
|
-
const hex = str.replace('#', '');
|
|
4269
|
-
if (/^[0-9a-f]{3,8}$/i.test(hex)) {
|
|
4270
|
-
const normalized = hex.length === 3
|
|
4271
|
-
? hex.split('').map((x) => `${x}${x}`).join('') + 'ff'
|
|
4272
|
-
: hex.length === 4
|
|
4273
|
-
? hex.split('').map((x) => `${x}${x}`).join('')
|
|
4274
|
-
: hex.length === 6
|
|
4275
|
-
? `${hex}ff`
|
|
4276
|
-
: hex;
|
|
4277
|
-
const r = parseInt(normalized.slice(0, 2), 16);
|
|
4278
|
-
const g = parseInt(normalized.slice(2, 4), 16);
|
|
4279
|
-
const b = parseInt(normalized.slice(4, 6), 16);
|
|
4280
|
-
const a = parseInt(normalized.slice(6, 8), 16) / 255;
|
|
4281
|
-
return this.rgbaToHsbaModel(r, g, b, a);
|
|
4282
|
-
}
|
|
4283
|
-
return fallback;
|
|
4284
|
-
}
|
|
4285
|
-
rgbaToHsbaModel(rRaw, gRaw, bRaw, aRaw) {
|
|
4286
|
-
const r = Math.max(0, Math.min(255, rRaw)) / 255;
|
|
4287
|
-
const g = Math.max(0, Math.min(255, gRaw)) / 255;
|
|
4288
|
-
const b = Math.max(0, Math.min(255, bRaw)) / 255;
|
|
4289
|
-
const a = Math.max(0, Math.min(1, aRaw));
|
|
4290
|
-
const max = Math.max(r, g, b);
|
|
4291
|
-
const min = Math.min(r, g, b);
|
|
4292
|
-
const delta = max - min;
|
|
4293
|
-
let h = 0;
|
|
4294
|
-
if (delta !== 0) {
|
|
4295
|
-
if (max === r) {
|
|
4296
|
-
h = 60 * (((g - b) / delta) % 6);
|
|
4297
|
-
}
|
|
4298
|
-
else if (max === g) {
|
|
4299
|
-
h = 60 * (((b - r) / delta) + 2);
|
|
4300
|
-
}
|
|
4301
|
-
else {
|
|
4302
|
-
h = 60 * (((r - g) / delta) + 4);
|
|
4303
|
-
}
|
|
4304
|
-
}
|
|
4305
|
-
if (h < 0) {
|
|
4306
|
-
h += 360;
|
|
4307
|
-
}
|
|
4308
|
-
const s = max === 0 ? 0 : (delta / max) * 100;
|
|
4309
|
-
const v = max * 100;
|
|
4310
|
-
return {
|
|
4311
|
-
h: Math.round(h),
|
|
4312
|
-
s: Math.round(s),
|
|
4313
|
-
b: Math.round(v),
|
|
4314
|
-
a: Number(a.toFixed(3))
|
|
4315
|
-
};
|
|
4316
|
-
}
|
|
4317
|
-
hsbaToRgbaString(hRaw, sRaw, bRaw, aRaw) {
|
|
4318
|
-
const h = ((hRaw % 360) + 360) % 360;
|
|
4319
|
-
const s = Math.max(0, Math.min(100, sRaw)) / 100;
|
|
4320
|
-
const v = Math.max(0, Math.min(100, bRaw)) / 100;
|
|
4321
|
-
const a = Math.max(0, Math.min(1, aRaw));
|
|
4322
|
-
const c = v * s;
|
|
4323
|
-
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
4324
|
-
const m = v - c;
|
|
4325
|
-
let r1 = 0;
|
|
4326
|
-
let g1 = 0;
|
|
4327
|
-
let b1 = 0;
|
|
4328
|
-
if (h < 60) {
|
|
4329
|
-
r1 = c;
|
|
4330
|
-
g1 = x;
|
|
4331
|
-
b1 = 0;
|
|
4332
|
-
}
|
|
4333
|
-
else if (h < 120) {
|
|
4334
|
-
r1 = x;
|
|
4335
|
-
g1 = c;
|
|
4336
|
-
b1 = 0;
|
|
4337
|
-
}
|
|
4338
|
-
else if (h < 180) {
|
|
4339
|
-
r1 = 0;
|
|
4340
|
-
g1 = c;
|
|
4341
|
-
b1 = x;
|
|
4342
|
-
}
|
|
4343
|
-
else if (h < 240) {
|
|
4344
|
-
r1 = 0;
|
|
4345
|
-
g1 = x;
|
|
4346
|
-
b1 = c;
|
|
4347
|
-
}
|
|
4348
|
-
else if (h < 300) {
|
|
4349
|
-
r1 = x;
|
|
4350
|
-
g1 = 0;
|
|
4351
|
-
b1 = c;
|
|
4352
|
-
}
|
|
4353
|
-
else {
|
|
4354
|
-
r1 = c;
|
|
4355
|
-
g1 = 0;
|
|
4356
|
-
b1 = x;
|
|
4357
|
-
}
|
|
4358
|
-
const r = Math.round((r1 + m) * 255);
|
|
4359
|
-
const g = Math.round((g1 + m) * 255);
|
|
4360
|
-
const b = Math.round((b1 + m) * 255);
|
|
4361
|
-
return `rgba(${r}, ${g}, ${b}, ${Number(a.toFixed(3))})`;
|
|
4362
|
-
}
|
|
4363
|
-
clampAlpha(value) {
|
|
4364
|
-
const parsed = Number(value);
|
|
4365
|
-
if (!Number.isFinite(parsed)) {
|
|
4366
|
-
return 1;
|
|
4367
|
-
}
|
|
4368
|
-
return Math.max(0, Math.min(1, parsed));
|
|
4369
|
-
}
|
|
4370
4299
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CssSheetEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4371
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CssSheetEditorComponent, isStandalone: true, selector: "wuic-css-sheet-editor", inputs: { visible: "visible", sheets: "sheets", selectedSheetPath: "selectedSheetPath", styleProps: "styleProps", assetsFolder: "assetsFolder" }, outputs: { visibleChange: "visibleChange", applyEditor: "applyEditor" }, usesOnChanges: true, ngImport: i0, template: "<p-dialog [header]=\"'CSS Editor'\" [(visible)]=\"visible\" (visibleChange)=\"visibleChange.emit($event)\" [modal]=\"true\"\n [closable]=\"true\" [style]=\"{ width: '1100px', maxWidth: '98vw' }\">\n <div class=\"css-editor-layout\">\n <div class=\"css-editor-col\">\n <div class=\"css-editor-row\">\n <label>File CSS</label>\n <p-select [options]=\"draftSheets\" optionLabel=\"SheetPath\" optionValue=\"SheetPath\"\n [(ngModel)]=\"selectedSheetPathDraft\" (ngModelChange)=\"onSelectedSheetChanged()\" appendTo=\"body\"\n [style]=\"{ width: '100%' }\">\n </p-select>\n </div>\n <div class=\"css-editor-row css-editor-inline\">\n <input type=\"text\" [(ngModel)]=\"newSheetName\" pattern=\"^[^/\\\\]+$\"\n [placeholder]=\"'Nuovo file in assets/' + (assetsFolder || '<dash_route>') + ' (solo nome, es. dashboard-custom.css)'\" />\n <p-button label=\"Crea file\" (onClick)=\"addSheet()\" [disabled]=\"!assetsFolder\"></p-button>\n </div>\n @if (!assetsFolder) {\n <div class=\"css-editor-row\">\n <small>Salva prima la dashboard per definire il folder (dash_route).</small>\n </div>\n }\n <div class=\"css-editor-row\">\n <p-button severity=\"danger\" label=\"Rimuovi file selezionato\" (onClick)=\"removeSelectedSheet()\"\n [disabled]=\"!currentSheet\"></p-button>\n </div>\n </div>\n\n @if (currentSheet) {\n <div class=\"css-editor-col\">\n <div class=\"css-editor-row\">\n <label>Classi</label>\n <p-select [options]=\"currentSheet?.classes || []\" optionLabel=\"SelectorString\" optionValue=\"SelectorString\"\n [(ngModel)]=\"selectedClassSelector\" (ngModelChange)=\"onSelectedClassChanged()\" appendTo=\"body\"\n [style]=\"{ width: '100%' }\">\n </p-select>\n </div>\n <div class=\"css-editor-row css-editor-inline\">\n <input type=\"text\" [(ngModel)]=\"newClassName\" placeholder=\"Nuova classe (es. card-title)\" />\n <p-button label=\"Crea classe\" (onClick)=\"addClassToSelectedSheet()\" [disabled]=\"!currentSheet\"></p-button>\n </div>\n <div class=\"css-editor-row\">\n <p-button severity=\"danger\" label=\"Rimuovi classe selezionata\" (onClick)=\"removeSelectedClass()\"\n [disabled]=\"!currentClass\"></p-button>\n </div>\n </div>\n }\n </div>\n\n @if (currentClass) {\n <div class=\"css-editor-styles\">\n <div class=\"css-editor-row css-style-row\" style=\"margin-bottom: 8px;\">\n <p-select [options]=\"styleProps\" optionLabel=\"key\" optionValue=\"key\" [(ngModel)]=\"pendingStyleKey\"\n (ngModelChange)=\"onPendingStyleKeyChanged()\" appendTo=\"body\" [style]=\"{ width: '260px' }\">\n </p-select>\n\n @if (isPropertyDictionary(pendingStyleKey)) {\n <p-select [options]=\"getPropertyValues(pendingStyleKey)\" [(ngModel)]=\"pendingStyleValue\" appendTo=\"body\"\n [style]=\"{ width: '320px' }\">\n </p-select>\n } @else if (isPropertyColor(pendingStyleKey)) {\n\n <div class=\"css-style-color-input\">\n <div class=\"color-editor-root\" [ngStyle]=\"{ '--wuic-color-editor-preview': valore || 'transparent' }\">\n <p-colorPicker format=\"hsb\" [ngModel]=\"pickerValue\" (ngModelChange)=\"onPendingColorChanged($event)\"\n appendTo=\"body\">\n </p-colorPicker>\n </div>\n <input type=\"text\" [(ngModel)]=\"pendingStyleValue\" style=\"width: 252px;\" />\n </div>\n <div class=\"color-editor-alpha-row\">\n <label class=\"color-editor-alpha-label\">Alpha</label>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" [ngModel]=\"alphaValue\"\n (ngModelChange)=\"onAlphaChange($event)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\" [ngModel]=\"alphaValue\"\n (ngModelChange)=\"onAlphaChange($event)\" />\n </div>\n\n } @else {\n <input type=\"text\" [(ngModel)]=\"pendingStyleValue\" style=\"width: 320px;\" />\n }\n\n <p-button label=\"Aggiungi propriet\u00E0 corrente\" (onClick)=\"addStyleToSelectedClass()\"\n [disabled]=\"!currentClass || !pendingStyleKey\"></p-button>\n </div>\n\n @for (style of currentClass.Styles; track style; let i = $index) {\n <div class=\"css-style-row\">\n <div class=\"css-style-readonly-key\">{{ style.Key }}</div>\n <div class=\"css-style-readonly-value\">{{ style.Value }}</div>\n\n <p-button icon=\"pi pi-trash\" severity=\"danger\" [text]=\"true\" (onClick)=\"removeStyle(i)\"></p-button>\n </div>\n }\n </div>\n }\n\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Chiudi\" severity=\"secondary\" (onClick)=\"close()\"></p-button>\n </ng-template>\n</p-dialog>", styles: [".css-editor-layout{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}.css-editor-col{border:1px solid #d0d0d0;border-radius:6px;padding:12px}.css-editor-row{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}.css-editor-inline{flex-direction:row;align-items:center}.css-editor-inline input{flex:1}.css-editor-styles{border:1px solid #d0d0d0;border-radius:6px;padding:12px;max-height:420px;overflow:auto}.css-style-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.css-style-readonly-key{width:260px;min-height:32px;display:flex;align-items:center;padding:0 8px;border:1px solid #d0d0d0;border-radius:4px;background:#f9f9f9;font-family:monospace}.css-style-readonly-value{width:320px;min-height:32px;display:flex;align-items:center;padding:0 8px;border:1px solid #d0d0d0;border-radius:4px;background:#f9f9f9;font-family:monospace}.css-style-color-input{display:flex;align-items:center;gap:8px;width:320px}@media(max-width:1024px){.css-editor-layout{grid-template-columns:1fr}.css-style-row{flex-wrap:wrap}}.color-editor-root{--wuic-color-editor-preview: transparent;border:solid 1px}:host ::ng-deep .color-editor-root .p-colorpicker-preview{background-image:linear-gradient(var(--wuic-color-editor-preview),var(--wuic-color-editor-preview)),linear-gradient(45deg,#d5d9e2 25%,transparent 25%),linear-gradient(-45deg,#d5d9e2 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#d5d9e2 75%),linear-gradient(-45deg,transparent 75%,#d5d9e2 75%)!important;background-size:10px 10px!important;background-position:0 0,0 5px,5px -5px,-5px 0!important;background-color:transparent!important}.color-editor-alpha-row{display:flex;align-items:center;gap:.5rem;margin-top:.5rem}.color-editor-alpha-label{min-width:2.8rem;font-size:.85rem}.color-editor-alpha-row input[type=range]{flex:1 1 auto}.color-editor-alpha-row input[type=number]{width:4.5rem}\n"], dependencies: [{ 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { 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: 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: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: 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: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
4300
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CssSheetEditorComponent, isStandalone: true, selector: "wuic-css-sheet-editor", inputs: { visible: "visible", sheets: "sheets", selectedSheetPath: "selectedSheetPath", styleProps: "styleProps", assetsFolder: "assetsFolder" }, outputs: { visibleChange: "visibleChange", applyEditor: "applyEditor" }, usesOnChanges: true, ngImport: i0, template: "<p-dialog [header]=\"'CSS Editor'\" [(visible)]=\"visible\" (visibleChange)=\"visibleChange.emit($event)\" [modal]=\"true\"\n [closable]=\"true\" [style]=\"{ width: '1100px', maxWidth: '98vw' }\">\n <div class=\"css-editor-layout\">\n <div class=\"css-editor-col\">\n <div class=\"css-editor-row\">\n <label>File CSS</label>\n <p-select [options]=\"draftSheets\" optionLabel=\"SheetPath\" optionValue=\"SheetPath\"\n [(ngModel)]=\"selectedSheetPathDraft\" (ngModelChange)=\"onSelectedSheetChanged()\" appendTo=\"body\"\n [style]=\"{ width: '100%' }\">\n </p-select>\n </div>\n <div class=\"css-editor-row css-editor-inline\">\n <input type=\"text\" [(ngModel)]=\"newSheetName\" pattern=\"^[^/\\\\]+$\"\n [placeholder]=\"'Nuovo file in assets/' + (assetsFolder || '<dash_route>') + ' (solo nome, es. dashboard-custom.css)'\" />\n <p-button label=\"Crea file\" (onClick)=\"addSheet()\" [disabled]=\"!assetsFolder\"></p-button>\n </div>\n @if (!assetsFolder) {\n <div class=\"css-editor-row\">\n <small>Salva prima la dashboard per definire il folder (dash_route).</small>\n </div>\n }\n <div class=\"css-editor-row\">\n <p-button severity=\"danger\" label=\"Rimuovi file selezionato\" (onClick)=\"removeSelectedSheet()\"\n [disabled]=\"!currentSheet\"></p-button>\n </div>\n </div>\n\n @if (currentSheet) {\n <div class=\"css-editor-col\">\n <div class=\"css-editor-row\">\n <label>Classi</label>\n <p-select [options]=\"currentSheet?.classes || []\" optionLabel=\"SelectorString\" optionValue=\"SelectorString\"\n [(ngModel)]=\"selectedClassSelector\" (ngModelChange)=\"onSelectedClassChanged()\" appendTo=\"body\"\n [style]=\"{ width: '100%' }\">\n </p-select>\n </div>\n <div class=\"css-editor-row css-editor-inline\">\n <input type=\"text\" [(ngModel)]=\"newClassName\" placeholder=\"Nuova classe (es. card-title)\" />\n <p-button label=\"Crea classe\" (onClick)=\"addClassToSelectedSheet()\" [disabled]=\"!currentSheet\"></p-button>\n </div>\n <div class=\"css-editor-row\">\n <p-button severity=\"danger\" label=\"Rimuovi classe selezionata\" (onClick)=\"removeSelectedClass()\"\n [disabled]=\"!currentClass\"></p-button>\n </div>\n </div>\n }\n </div>\n\n @if (currentClass) {\n <div class=\"css-editor-styles\">\n <div class=\"css-editor-row css-style-row\" style=\"margin-bottom: 8px;\">\n <p-select [options]=\"styleProps\" optionLabel=\"key\" optionValue=\"key\" [(ngModel)]=\"pendingStyleKey\"\n (ngModelChange)=\"onPendingStyleKeyChanged()\" appendTo=\"body\" [style]=\"{ width: '260px' }\">\n </p-select>\n\n @if (isPropertyDictionary(pendingStyleKey)) {\n <p-select [options]=\"getPropertyValues(pendingStyleKey)\" [(ngModel)]=\"pendingStyleValue\" appendTo=\"body\"\n [style]=\"{ width: '320px' }\">\n </p-select>\n } @else if (isPropertyColor(pendingStyleKey)) {\n\n <div class=\"css-style-color-input\">\n <div class=\"color-editor-root\" [ngStyle]=\"{ '--wuic-color-editor-preview': valore || 'transparent' }\">\n <p-colorPicker format=\"hsb\" [ngModel]=\"pickerValue\" (ngModelChange)=\"onPendingColorChanged($event)\"\n appendTo=\"body\">\n </p-colorPicker>\n </div>\n <input type=\"text\" [(ngModel)]=\"pendingStyleValue\" style=\"width: 252px;\" />\n </div>\n <div class=\"color-editor-alpha-row\">\n <label class=\"color-editor-alpha-label\">Alpha</label>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" [ngModel]=\"alphaValue\"\n (ngModelChange)=\"onAlphaChange($event)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\" [ngModel]=\"alphaValue\"\n (ngModelChange)=\"onAlphaChange($event)\" />\n </div>\n\n } @else {\n <input type=\"text\" [(ngModel)]=\"pendingStyleValue\" style=\"width: 320px;\" />\n }\n\n <p-button label=\"Aggiungi propriet\u00E0 corrente\" (onClick)=\"addStyleToSelectedClass()\"\n [disabled]=\"!currentClass || !pendingStyleKey\"></p-button>\n </div>\n\n @for (style of currentClass.Styles; track style; let i = $index) {\n <div class=\"css-style-row\">\n <div class=\"css-style-readonly-key\">{{ style.Key }}</div>\n <div class=\"css-style-readonly-value\">{{ style.Value }}</div>\n\n <p-button icon=\"pi pi-trash\" severity=\"danger\" [text]=\"true\" (onClick)=\"removeStyle(i)\"></p-button>\n </div>\n }\n </div>\n }\n\n <ng-template pTemplate=\"footer\">\n <p-button label=\"Chiudi\" severity=\"secondary\" (onClick)=\"close()\"></p-button>\n </ng-template>\n</p-dialog>", styles: [".css-editor-layout{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}.css-editor-col{border:1px solid #d0d0d0;border-radius:6px;padding:12px}.css-editor-row{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}.css-editor-inline{flex-direction:row;align-items:center}.css-editor-inline input{flex:1}.css-editor-styles{border:1px solid #d0d0d0;border-radius:6px;padding:12px;max-height:420px;overflow:auto}.css-style-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.css-style-readonly-key{width:260px;min-height:32px;display:flex;align-items:center;padding:0 8px;border:1px solid #d0d0d0;border-radius:4px;background:#f9f9f9;font-family:monospace}.css-style-readonly-value{width:320px;min-height:32px;display:flex;align-items:center;padding:0 8px;border:1px solid #d0d0d0;border-radius:4px;background:#f9f9f9;font-family:monospace}.css-style-color-input{display:flex;align-items:center;gap:8px;width:320px}@media(max-width:1024px){.css-editor-layout{grid-template-columns:1fr}.css-style-row{flex-wrap:wrap}}.color-editor-root{--wuic-color-editor-preview: transparent;border:solid 1px}:host ::ng-deep .color-editor-root .p-colorpicker-preview{background-image:linear-gradient(var(--wuic-color-editor-preview),var(--wuic-color-editor-preview)),linear-gradient(45deg,#d5d9e2 25%,transparent 25%),linear-gradient(-45deg,#d5d9e2 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#d5d9e2 75%),linear-gradient(-45deg,transparent 75%,#d5d9e2 75%)!important;background-size:10px 10px!important;background-position:0 0,0 5px,5px -5px,-5px 0!important;background-color:transparent!important}.color-editor-alpha-row{display:flex;align-items:center;gap:.5rem;margin-top:.5rem}.color-editor-alpha-label{min-width:2.8rem;font-size:.85rem}.color-editor-alpha-row input[type=range]{flex:1 1 auto}.color-editor-alpha-row input[type=number]{width:4.5rem}\n"], dependencies: [{ 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { 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: 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: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { 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: SelectModule }, { kind: "component", type: i5.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: i6.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: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
4372
4301
|
}
|
|
4373
4302
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CssSheetEditorComponent, decorators: [{
|
|
4374
4303
|
type: Component,
|
|
@@ -4479,6 +4408,8 @@ class DesignerComponent {
|
|
|
4479
4408
|
reopenDashboardDialogVisible = false;
|
|
4480
4409
|
reopenDialogSelectedDashboardRoute = null;
|
|
4481
4410
|
noEdit;
|
|
4411
|
+
/** Chrome card dei widget top-level (flag board-level dal boardcontent wrapper-object). */
|
|
4412
|
+
dashboardWidgetChrome = false;
|
|
4482
4413
|
currentDashboardRoute = '';
|
|
4483
4414
|
currentDashboardDescription = '';
|
|
4484
4415
|
showArchetypeConfigurator = false;
|
|
@@ -4527,79 +4458,12 @@ class DesignerComponent {
|
|
|
4527
4458
|
this.metadataEditorSrv = metadataEditorSrv;
|
|
4528
4459
|
this.chatbotHostRegistry = chatbotHostRegistry;
|
|
4529
4460
|
this.workflowRuntimeMetadata = workflowRuntimeMetadata;
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
if (this.ctxElement) {
|
|
4536
|
-
this.selectDashboardItem(this.ctxElement);
|
|
4537
|
-
this.cd.detectChanges();
|
|
4538
|
-
}
|
|
4539
|
-
}
|
|
4540
|
-
},
|
|
4541
|
-
{
|
|
4542
|
-
label: this.t('designer.remove', 'Remove'),
|
|
4543
|
-
icon: 'pi pi-trash',
|
|
4544
|
-
command: (menuItem) => {
|
|
4545
|
-
if (this.ctxElement) {
|
|
4546
|
-
this.removeElementByName(this.ctxElement.uniqueName);
|
|
4547
|
-
}
|
|
4548
|
-
}
|
|
4549
|
-
}
|
|
4550
|
-
];
|
|
4461
|
+
// Context menu elemento: UNICA sorgente per tutti e tre i punti di accesso
|
|
4462
|
+
// (canvas / gerarchia / footer breadcrumb) -> `buildElementContextMenuItems`.
|
|
4463
|
+
// I tre handler `show*ContextMenu` ripopolano il rispettivo array con
|
|
4464
|
+
// l'elemento risolto al click, cosi' le voci sono sempre identiche.
|
|
4465
|
+
this.defaultCtxItems = this.buildElementContextMenuItems(null);
|
|
4551
4466
|
this.ctxItems.next(this.defaultCtxItems);
|
|
4552
|
-
this.footerCtxItems = [
|
|
4553
|
-
{
|
|
4554
|
-
label: this.t('designer.edit_properties', 'Edit properties'),
|
|
4555
|
-
icon: 'pi pi-cog',
|
|
4556
|
-
command: () => {
|
|
4557
|
-
if (this.footerCtxElement) {
|
|
4558
|
-
this.selectDashboardItem(this.footerCtxElement);
|
|
4559
|
-
this.cd.detectChanges();
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
},
|
|
4563
|
-
{
|
|
4564
|
-
label: this.t('designer.rename', 'Rename'),
|
|
4565
|
-
icon: 'pi pi-pencil',
|
|
4566
|
-
command: () => {
|
|
4567
|
-
this.renameFooterContextItem();
|
|
4568
|
-
}
|
|
4569
|
-
},
|
|
4570
|
-
{
|
|
4571
|
-
label: this.t('designer.remove', 'Remove'),
|
|
4572
|
-
icon: 'pi pi-trash',
|
|
4573
|
-
command: () => {
|
|
4574
|
-
const uniqueName = String(this.footerCtxElement?.uniqueName || '').trim();
|
|
4575
|
-
if (uniqueName) {
|
|
4576
|
-
this.removeElementByName(uniqueName);
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
}
|
|
4580
|
-
];
|
|
4581
|
-
this.hierarchyCtxItems = [
|
|
4582
|
-
{
|
|
4583
|
-
label: this.t('designer.edit_properties', 'Edit properties'),
|
|
4584
|
-
icon: 'pi pi-cog',
|
|
4585
|
-
command: () => {
|
|
4586
|
-
if (this.hierarchyCtxElement) {
|
|
4587
|
-
this.selectDashboardItem(this.hierarchyCtxElement);
|
|
4588
|
-
this.cd.detectChanges();
|
|
4589
|
-
}
|
|
4590
|
-
}
|
|
4591
|
-
},
|
|
4592
|
-
{
|
|
4593
|
-
label: this.t('designer.remove', 'Remove'),
|
|
4594
|
-
icon: 'pi pi-trash',
|
|
4595
|
-
command: () => {
|
|
4596
|
-
const uniqueName = String(this.hierarchyCtxElement?.uniqueName || '').trim();
|
|
4597
|
-
if (uniqueName) {
|
|
4598
|
-
this.removeElementByName(uniqueName);
|
|
4599
|
-
}
|
|
4600
|
-
}
|
|
4601
|
-
}
|
|
4602
|
-
];
|
|
4603
4467
|
this.htmlStyleString = '[ngClass]="0.cssClass" [ngStyle]="{width: 0.width, maxWidth: 0.maxWidth, height: 0.height, maxHeight: 0.maxHeight, minWidth: 0.minWidth, minHeight: 0.minHeight, position: 0.position, left: 0.left, top: 0.top, float: 0.float, display: 0.display, boxSizing: 0.boxSizing, backgroundColor: 0.backgroundColor, color: 0.color, borderRadius: 0.borderRadius, borderBottomStyle: 0.borderBottomStyle, borderBottomWidth: 0.borderBottomWidth, borderBottomColor: 0.borderBottomColor, borderLeftStyle: 0.borderLeftStyle, borderLeftWidth: 0.borderLeftWidth, borderLeftColor: 0.borderLeftColor, borderRightStyle: 0.borderRightStyle, borderRightWidth: 0.borderRightWidth, borderRightColor: 0.borderRightColor, borderTopStyle: 0.borderTopStyle, borderTopWidth: 0.borderTopWidth, borderTopColor: 0.borderTopColor, paddingTop: 0.paddingTop, paddingRight: 0.paddingRight, paddingBottom: 0.paddingBottom, paddingLeft: 0.paddingLeft, marginTop: 0.marginTop, marginRight: 0.marginRight, marginBottom: 0.marginBottom, marginLeft: 0.marginLeft, fontSize: 0.fontSize, fontFamily: 0.fontFamily, fontWeight: 0.fontWeight, fontStyle: 0.fontStyle, textDecoration: 0.textDecoration, textAlign: 0.textAlign, verticalAlign: 0.verticalAlign, lineHeight: 0.lineHeight, letterSpacing: 0.letterSpacing, wordSpacing: 0.wordSpacing, textTransform: 0.textTransform, textIndent: 0.textIndent, whiteSpace: 0.whiteSpace, wordWrap: 0.wordWrap, wordBreak: 0.wordBreak, overflow: 0.overflow, overflowX: 0.overflowX, overflowY: 0.overflowY, zIndex: 0.zIndex, cursor: 0.cursor, visibility: 0.visibility, opacity: 0.opacity, filter: 0.filter, transform: 0.transform, transformOrigin: 0.transformOrigin, transition: 0.transition, boxShadow: 0.boxShadow, textShadow: 0.textShadow, backgroundPosition: 0.backgroundPosition, backgroundRepeat: 0.backgroundRepeat, backgroundAttachment: 0.backgroundAttachment, backgroundSize: 0.backgroundSize, backgroundImage: 0.backgroundImage, backgroundClip: 0.backgroundClip, backgroundOrigin: 0.backgroundOrigin, backgroundBlendMode: 0.backgroundBlendMode, clip: 0.clip, clipPath: 0.clipPath, clipRule: 0.clipRule, mask: 0.mask, maskType: 0.maskType, maskImage: 0.maskImage, maskMode: 0.maskMode, maskSize: 0.maskSize, maskRepeat: 0.maskRepeat, maskPosition: 0.maskPosition, maskClip: 0.maskClip, maskOrigin: 0.maskOrigin, maskComposite: 0.maskComposite, maskBorder: 0.maskBorder, maskBorderSource: 0.maskBorderSource, maskBorderSlice: 0.maskBorderSlice, maskBorderWidth: 0.maskBorderWidth, maskBorderOutset: 0.maskBorderOutset, maskBorderRepeat: 0.maskBorderRepeat, maskBorderMode: 0.maskBorderMode, maskBorderClip: 0.maskBorderClip, maskBorderOrigin: 0.maskBorderOrigin, maskBorder, tableLayout: 0.tableLayout, borderCollapse: 0.borderCollapse, borderSpacing: 0.borderSpacing, captionSide: 0.captionSide, emptyCells: 0.emptyCells, tableCaption: 0.tableCaption, tableBorder: 0.tableBorder, tableBorderSpacing: 0.tableBorderSpacing, tableBorderCollapse: 0.tableBorderCollapse, listStyle: 0.listStyle}"';
|
|
4604
4468
|
this.htmlInputProperties = {
|
|
4605
4469
|
animation: { type: 'string' },
|
|
@@ -4908,7 +4772,6 @@ class DesignerComponent {
|
|
|
4908
4772
|
size: 50,
|
|
4909
4773
|
areas: 2,
|
|
4910
4774
|
resize: (event, splitter) => {
|
|
4911
|
-
// let indx = event.gutterNum;
|
|
4912
4775
|
splitter.nestedComponents.forEach((area, i) => {
|
|
4913
4776
|
area.inputs.size = event.sizes[i];
|
|
4914
4777
|
});
|
|
@@ -4967,29 +4830,18 @@ class DesignerComponent {
|
|
|
4967
4830
|
delete this.accordionAreaInputs['cssClass'];
|
|
4968
4831
|
this.accordionAreaInputs['height'] = null;
|
|
4969
4832
|
this.accordionAreaInputs['display'] = 'block';
|
|
4970
|
-
// this.accordionAreaInputs['borderStyle'] = 'none';
|
|
4971
4833
|
this.accordionAreaInputs['borderTopStyle'] = 'none';
|
|
4972
4834
|
this.accordionAreaInputs['borderLeftStyle'] = 'none';
|
|
4973
4835
|
this.accordionAreaInputs['borderRightStyle'] = 'none';
|
|
4974
4836
|
this.accordionAreaInputs['borderBottomStyle'] = 'solid';
|
|
4975
4837
|
this.accordionAreaInputs['borderBottomWidth'] = '1px';
|
|
4976
4838
|
this.accordionAreaInputs['borderBottomColor'] = '#e2e8f0';
|
|
4977
|
-
// let divCtx = [
|
|
4978
|
-
// {
|
|
4979
|
-
// label: 'Add DIV',
|
|
4980
|
-
// icon: 'pi pi-plus',
|
|
4981
|
-
// command: (menuItem) => {
|
|
4982
|
-
// debugger;
|
|
4983
|
-
// }
|
|
4984
|
-
// } as any
|
|
4985
|
-
// ];
|
|
4986
|
-
// divCtx.push(...this.defaultCtxItems);
|
|
4987
4839
|
this.availableTools = [
|
|
4988
4840
|
{
|
|
4989
4841
|
group: 'HTML',
|
|
4990
4842
|
toolId: 1,
|
|
4991
4843
|
name: 'TABLE',
|
|
4992
|
-
tag: `<table [attr.id]="uniqueName" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.
|
|
4844
|
+
tag: `<table [attr.id]="uniqueName" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><tr *ngFor="let nestedTR of nestedComponents" [attr.id]="nestedTR.uniqueName" ${this.htmlStyleString.replaceAll('0', 'nestedTR.inputs')}><td *ngFor="let nestedTD of nestedTR.nestedComponents" [attr.id]="nestedTD.uniqueName" [attr.colspan]="nestedTD.inputs?.colSpan || null" [attr.rowspan]="nestedTD.inputs?.rowSpan || null" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, nestedTD)" ${this.htmlStyleString.replaceAll('0', 'nestedTD.inputs')}><wuic-dashboard [dashboardElements]="nestedTD.nestedComponents"></wuic-dashboard></td></tr></table>`,
|
|
4993
4845
|
icon: 'pi pi-table',
|
|
4994
4846
|
inputProps: tableProps,
|
|
4995
4847
|
inputs: tableFullInputs,
|
|
@@ -5004,7 +4856,7 @@ class DesignerComponent {
|
|
|
5004
4856
|
tag: ``,
|
|
5005
4857
|
icon: 'pi pi-bars',
|
|
5006
4858
|
inputProps: this.htmlInputProperties,
|
|
5007
|
-
inputs:
|
|
4859
|
+
inputs: { ...tableFullInputs, cssClass: [] },
|
|
5008
4860
|
nestedComponents: []
|
|
5009
4861
|
},
|
|
5010
4862
|
{
|
|
@@ -5014,21 +4866,24 @@ class DesignerComponent {
|
|
|
5014
4866
|
name: 'TD',
|
|
5015
4867
|
tag: ``,
|
|
5016
4868
|
icon: 'pi pi-bars',
|
|
5017
|
-
inputProps:
|
|
4869
|
+
inputProps: {
|
|
4870
|
+
...this.htmlInputProperties,
|
|
5018
4871
|
colSpan: { type: 'string', propertyCaption: 'Col span' },
|
|
5019
4872
|
rowSpan: { type: 'string', propertyCaption: 'Row span' }
|
|
5020
|
-
}
|
|
5021
|
-
inputs:
|
|
4873
|
+
},
|
|
4874
|
+
inputs: {
|
|
4875
|
+
...tableFullInputs,
|
|
5022
4876
|
colSpan: '',
|
|
5023
|
-
rowSpan: ''
|
|
5024
|
-
|
|
4877
|
+
rowSpan: '',
|
|
4878
|
+
cssClass: []
|
|
4879
|
+
},
|
|
5025
4880
|
nestedComponents: []
|
|
5026
4881
|
},
|
|
5027
4882
|
{
|
|
5028
4883
|
group: 'HTML',
|
|
5029
4884
|
toolId: 2,
|
|
5030
4885
|
name: 'DIV',
|
|
5031
|
-
tag: `<div [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4886
|
+
tag: `<div [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><wuic-dashboard [dashboardElements]="nestedComponents"></wuic-dashboard></div>`,
|
|
5032
4887
|
icon: 'pi pi-stop',
|
|
5033
4888
|
inputProps: this.htmlInputProperties,
|
|
5034
4889
|
inputs: this.htmlInputs,
|
|
@@ -5039,7 +4894,7 @@ class DesignerComponent {
|
|
|
5039
4894
|
group: 'HTML',
|
|
5040
4895
|
toolId: 3,
|
|
5041
4896
|
name: 'SPAN',
|
|
5042
|
-
tag: `<span [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4897
|
+
tag: `<span [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</span>`,
|
|
5043
4898
|
icon: 'pi pi-language',
|
|
5044
4899
|
inputProps: this.bindableHtmlProps,
|
|
5045
4900
|
inputs: this.bindableHtmlInputs
|
|
@@ -5048,25 +4903,27 @@ class DesignerComponent {
|
|
|
5048
4903
|
group: 'HTML',
|
|
5049
4904
|
toolId: 17,
|
|
5050
4905
|
name: 'LABEL',
|
|
5051
|
-
tag: `<label [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4906
|
+
tag: `<label [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{getLabelDisplayValue(inputs)}}</label>`,
|
|
5052
4907
|
icon: 'pi pi-tag',
|
|
5053
|
-
inputProps:
|
|
4908
|
+
inputProps: {
|
|
4909
|
+
...this.bindableHtmlProps,
|
|
5054
4910
|
displayField: { type: 'dictionary', values: [''], propertyCaption: 'Display Field' },
|
|
5055
4911
|
displayFormula: { type: 'button', propertyCaption: 'Display Formula', hideCaption: true, callback: this.openLabelDisplayFormulaEditor.bind(this) }
|
|
5056
|
-
}
|
|
5057
|
-
inputs:
|
|
4912
|
+
},
|
|
4913
|
+
inputs: {
|
|
4914
|
+
...this.bindableHtmlInputs,
|
|
5058
4915
|
datasource: null,
|
|
5059
4916
|
componentRef: null,
|
|
5060
4917
|
innerText: 'Label',
|
|
5061
4918
|
displayField: '',
|
|
5062
4919
|
displayFormula: ''
|
|
5063
|
-
}
|
|
4920
|
+
}
|
|
5064
4921
|
},
|
|
5065
4922
|
{
|
|
5066
4923
|
group: 'HTML',
|
|
5067
4924
|
toolId: 4,
|
|
5068
4925
|
name: 'Hx',
|
|
5069
|
-
tag: `<h1 *ngIf="inputs.headerType == 'h1'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4926
|
+
tag: `<h1 *ngIf="inputs.headerType == 'h1'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h1><h2 *ngIf="inputs.headerType == 'h2'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h2><h3 *ngIf="inputs.headerType == 'h3'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h3><h4 *ngIf="inputs.headerType == 'h4'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h4><h5 *ngIf="inputs.headerType == 'h5'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h5><h6 *ngIf="inputs.headerType == 'h6'" [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</h6>`,
|
|
5070
4927
|
icon: 'pi pi-chevron-up',
|
|
5071
4928
|
inputProps: this.headerProps,
|
|
5072
4929
|
inputs: this.headerInputs
|
|
@@ -5075,7 +4932,7 @@ class DesignerComponent {
|
|
|
5075
4932
|
group: 'HTML',
|
|
5076
4933
|
toolId: 5,
|
|
5077
4934
|
name: 'ANCHOR',
|
|
5078
|
-
tag: `<a [attr.id]="uniqueName" [href]="inputs.href" [target]="inputs.target" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4935
|
+
tag: `<a [attr.id]="uniqueName" [href]="inputs.href" [target]="inputs.target" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>{{inputs.innerText}}</a>`,
|
|
5079
4936
|
icon: 'pi pi-link',
|
|
5080
4937
|
inputProps: this.anchorProps,
|
|
5081
4938
|
inputs: this.anchorInputs
|
|
@@ -5084,7 +4941,7 @@ class DesignerComponent {
|
|
|
5084
4941
|
group: 'HTML',
|
|
5085
4942
|
toolId: 6,
|
|
5086
4943
|
name: 'IMG',
|
|
5087
|
-
tag: `<img [attr.id]="uniqueName" [src]="inputs.src" [alt]="inputs.alt" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4944
|
+
tag: `<img [attr.id]="uniqueName" [src]="inputs.src" [alt]="inputs.alt" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')} />`,
|
|
5088
4945
|
icon: 'pi pi-image',
|
|
5089
4946
|
inputProps: this.imgProps,
|
|
5090
4947
|
inputs: this.imgInputs
|
|
@@ -5093,26 +4950,51 @@ class DesignerComponent {
|
|
|
5093
4950
|
group: 'HTML',
|
|
5094
4951
|
toolId: 18,
|
|
5095
4952
|
name: 'IFRAME',
|
|
5096
|
-
tag: `<iframe [attr.id]="uniqueName" [attr.src]="inputs.src" [attr.frameborder]="inputs.frameBorder" [attr.allowfullscreen]="inputs.allowFullscreen ? '' : null" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4953
|
+
tag: `<iframe [attr.id]="uniqueName" [attr.src]="inputs.src" [attr.frameborder]="inputs.frameBorder" [attr.allowfullscreen]="inputs.allowFullscreen ? '' : null" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}></iframe>`,
|
|
5097
4954
|
icon: 'pi pi-window-maximize',
|
|
5098
|
-
inputProps:
|
|
4955
|
+
inputProps: {
|
|
4956
|
+
...this.htmlInputProperties,
|
|
5099
4957
|
src: { type: 'string', propertyCaption: 'Source URL' },
|
|
5100
4958
|
frameBorder: { type: 'dictionary', values: ['0', '1'], propertyCaption: 'Frame Border' },
|
|
5101
4959
|
allowFullscreen: { type: 'boolean', propertyCaption: 'Allow Fullscreen' }
|
|
5102
|
-
}
|
|
5103
|
-
inputs:
|
|
4960
|
+
},
|
|
4961
|
+
inputs: {
|
|
4962
|
+
...this.htmlInputs,
|
|
5104
4963
|
src: '',
|
|
5105
4964
|
frameBorder: '0',
|
|
5106
4965
|
allowFullscreen: false,
|
|
5107
4966
|
width: '480px',
|
|
5108
4967
|
height: '280px'
|
|
5109
|
-
}
|
|
4968
|
+
}
|
|
4969
|
+
},
|
|
4970
|
+
{
|
|
4971
|
+
group: 'HTML',
|
|
4972
|
+
toolId: 130,
|
|
4973
|
+
name: 'IMAGE',
|
|
4974
|
+
tag: `<wuic-image-wrapper-lazy [attr.id]="uniqueName" [src]="inputs.src" [alt]="inputs.alt" [preview]="inputs.preview" [previewImageSrc]="inputs.previewImageSrc" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}></wuic-image-wrapper-lazy>`,
|
|
4975
|
+
icon: 'pi pi-images',
|
|
4976
|
+
inputProps: {
|
|
4977
|
+
...this.htmlInputProperties,
|
|
4978
|
+
src: { type: 'string', propertyCaption: 'Source URL' },
|
|
4979
|
+
previewImageSrc: { type: 'string', propertyCaption: 'Preview (full-size) URL' },
|
|
4980
|
+
alt: { type: 'string', propertyCaption: 'Alt text' },
|
|
4981
|
+
preview: { type: 'boolean', propertyCaption: 'Enable lightbox preview' }
|
|
4982
|
+
},
|
|
4983
|
+
inputs: {
|
|
4984
|
+
...this.htmlInputs,
|
|
4985
|
+
src: '',
|
|
4986
|
+
previewImageSrc: '',
|
|
4987
|
+
alt: '',
|
|
4988
|
+
preview: true,
|
|
4989
|
+
width: '240px',
|
|
4990
|
+
height: '160px'
|
|
4991
|
+
}
|
|
5110
4992
|
},
|
|
5111
4993
|
{
|
|
5112
4994
|
group: 'HTML',
|
|
5113
4995
|
toolId: 7,
|
|
5114
4996
|
name: 'UL',
|
|
5115
|
-
tag: `<ul [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [itemTemplate]="inputs.itemTemplate" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
4997
|
+
tag: `<ul [attr.id]="uniqueName" [databound]="inputs.datasource?.component" [itemTemplate]="inputs.itemTemplate" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>
|
|
5116
4998
|
<li *ngFor="let li of (inputs.datasource?.component?.value?.resultInfo?.dato || (inputs.optionsCsv || '').split(','))">
|
|
5117
4999
|
<ng-container *ngIf="inputs.displayFormula; else ulItemTemplate">{{getUlItemDisplayValue(inputs, li)}}</ng-container>
|
|
5118
5000
|
<ng-template #ulItemTemplate>
|
|
@@ -5122,28 +5004,30 @@ class DesignerComponent {
|
|
|
5122
5004
|
</ul>`,
|
|
5123
5005
|
icon: 'pi pi-list',
|
|
5124
5006
|
inputProps: (() => {
|
|
5125
|
-
const ulProps =
|
|
5007
|
+
const ulProps = {
|
|
5008
|
+
...this.htmlItemTemplateProps,
|
|
5126
5009
|
optionsCsv: { type: 'string', propertyCaption: 'Options CSV' },
|
|
5127
5010
|
valueField: { type: 'dictionary', values: [''], propertyCaption: 'Value Field' },
|
|
5128
5011
|
textField: { type: 'dictionary', values: [''], propertyCaption: 'Text Field' },
|
|
5129
5012
|
displayFormula: { type: 'button', propertyCaption: 'Display Formula', hideCaption: true, callback: this.openUlDisplayFormulaEditor.bind(this) }
|
|
5130
|
-
}
|
|
5013
|
+
};
|
|
5131
5014
|
delete ulProps['bindingFunction'];
|
|
5132
5015
|
return ulProps;
|
|
5133
5016
|
})(),
|
|
5134
|
-
inputs:
|
|
5017
|
+
inputs: {
|
|
5018
|
+
...this.bindableHtmlInputs,
|
|
5135
5019
|
optionsCsv: 'Item 1,Item 2,Item 3',
|
|
5136
5020
|
valueField: '',
|
|
5137
5021
|
textField: '',
|
|
5138
5022
|
displayFormula: ''
|
|
5139
|
-
}
|
|
5023
|
+
},
|
|
5140
5024
|
nestedComponents: []
|
|
5141
5025
|
},
|
|
5142
5026
|
{
|
|
5143
5027
|
group: 'HTML',
|
|
5144
5028
|
toolId: 8,
|
|
5145
5029
|
name: 'BUTTON',
|
|
5146
|
-
tag: `<button [attr.id]="uniqueName" (click)="inputs.clickCallback__fn(inputs.datasource?.component?.value?.resultInfo, inputs.datasource?.component?.value?.metaInfo, inputs, $event, inputs._wtoolbox)" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [itemTemplate]="inputs.itemTemplate" [clickCallback]="inputs.clickCallback" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5030
|
+
tag: `<button [attr.id]="uniqueName" (click)="inputs.clickCallback__fn(inputs.datasource?.component?.value?.resultInfo, inputs.datasource?.component?.value?.metaInfo, inputs, $event, inputs._wtoolbox)" [databound]="inputs.datasource?.component" [bindingFunction]="inputs.bindingFunction" [itemTemplate]="inputs.itemTemplate" [clickCallback]="inputs.clickCallback" [inputs]="inputs" ngxDraggableDom="true" ngResizable [rzHandles] = "'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><ng-container *ngComponentOutlet="inputs.itemTemplateComponent; inputs: { rowData: inputs.templateData, metaInfo: inputs.datasource?.component?.value?.metaInfo, datasource: inputs.datasource?.component?.value }"></ng-container></button>`,
|
|
5147
5031
|
icon: 'pi pi-arrow-circle-down',
|
|
5148
5032
|
inputProps: this.htmlButtonProps,
|
|
5149
5033
|
inputs: this.bindableHtmlInputs
|
|
@@ -5152,58 +5036,64 @@ class DesignerComponent {
|
|
|
5152
5036
|
group: 'HTML',
|
|
5153
5037
|
toolId: 9,
|
|
5154
5038
|
name: 'INPUT',
|
|
5155
|
-
tag: `<input [attr.id]="uniqueName" [type]="inputs.inputType || 'text'" [placeholder]="inputs.placeholder" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5039
|
+
tag: `<input [attr.id]="uniqueName" [type]="inputs.inputType || 'text'" [placeholder]="inputs.placeholder" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')} />`,
|
|
5156
5040
|
icon: 'pi pi-pencil',
|
|
5157
|
-
inputProps:
|
|
5041
|
+
inputProps: {
|
|
5042
|
+
...this.htmlInputProperties,
|
|
5158
5043
|
inputType: { type: 'dictionary', propertyCaption: 'Type', values: ['text', 'number', 'email', 'password', 'search', 'url'] },
|
|
5159
5044
|
placeholder: { type: 'string', propertyCaption: 'Placeholder' },
|
|
5160
5045
|
value: { type: 'string', propertyCaption: 'Value' },
|
|
5161
5046
|
readonly: { type: 'boolean', propertyCaption: 'Readonly' },
|
|
5162
5047
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5163
|
-
}
|
|
5164
|
-
inputs:
|
|
5048
|
+
},
|
|
5049
|
+
inputs: {
|
|
5050
|
+
...this.htmlInputs,
|
|
5165
5051
|
inputType: 'text',
|
|
5166
5052
|
placeholder: '',
|
|
5167
5053
|
value: '',
|
|
5168
5054
|
readonly: false,
|
|
5169
5055
|
disabled: false,
|
|
5170
5056
|
height: '32px'
|
|
5171
|
-
}
|
|
5057
|
+
}
|
|
5172
5058
|
},
|
|
5173
5059
|
{
|
|
5174
5060
|
group: 'HTML',
|
|
5175
5061
|
toolId: 10,
|
|
5176
5062
|
name: 'TEXTAREA',
|
|
5177
|
-
tag: `<textarea [attr.id]="uniqueName" [placeholder]="inputs.placeholder" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [rows]="inputs.rows" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5063
|
+
tag: `<textarea [attr.id]="uniqueName" [placeholder]="inputs.placeholder" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [rows]="inputs.rows" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}></textarea>`,
|
|
5178
5064
|
icon: 'pi pi-align-left',
|
|
5179
|
-
inputProps:
|
|
5065
|
+
inputProps: {
|
|
5066
|
+
...this.htmlInputProperties,
|
|
5180
5067
|
placeholder: { type: 'string', propertyCaption: 'Placeholder' },
|
|
5181
5068
|
value: { type: 'txt_area', propertyCaption: 'Value' },
|
|
5182
5069
|
rows: { type: 'string', propertyCaption: 'Rows' },
|
|
5183
5070
|
readonly: { type: 'boolean', propertyCaption: 'Readonly' },
|
|
5184
5071
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5185
|
-
}
|
|
5186
|
-
inputs:
|
|
5072
|
+
},
|
|
5073
|
+
inputs: {
|
|
5074
|
+
...this.htmlInputs,
|
|
5187
5075
|
placeholder: '',
|
|
5188
5076
|
value: '',
|
|
5189
5077
|
rows: '4',
|
|
5190
5078
|
readonly: false,
|
|
5191
5079
|
disabled: false,
|
|
5192
5080
|
height: '96px'
|
|
5193
|
-
}
|
|
5081
|
+
}
|
|
5194
5082
|
},
|
|
5195
5083
|
{
|
|
5196
5084
|
group: 'HTML',
|
|
5197
5085
|
toolId: 11,
|
|
5198
5086
|
name: 'CHECKBOX',
|
|
5199
|
-
tag: `<label [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5087
|
+
tag: `<label [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><input type="checkbox" [checked]="inputs.checked" (change)="inputs.checked=$any($event.target).checked" [disabled]="inputs.disabled" /> {{inputs.label}}</label>`,
|
|
5200
5088
|
icon: 'pi pi-check-square',
|
|
5201
|
-
inputProps:
|
|
5089
|
+
inputProps: {
|
|
5090
|
+
...this.htmlInputProperties,
|
|
5202
5091
|
label: { type: 'string', propertyCaption: 'Label' },
|
|
5203
5092
|
checked: { type: 'boolean', propertyCaption: 'Checked' },
|
|
5204
5093
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5205
|
-
}
|
|
5206
|
-
inputs:
|
|
5094
|
+
},
|
|
5095
|
+
inputs: {
|
|
5096
|
+
...this.htmlInputs,
|
|
5207
5097
|
label: 'Checkbox',
|
|
5208
5098
|
checked: false,
|
|
5209
5099
|
disabled: false,
|
|
@@ -5214,16 +5104,17 @@ class DesignerComponent {
|
|
|
5214
5104
|
borderRightStyle: 'none',
|
|
5215
5105
|
borderBottomStyle: 'none',
|
|
5216
5106
|
borderLeftStyle: 'none'
|
|
5217
|
-
}
|
|
5107
|
+
}
|
|
5218
5108
|
},
|
|
5219
5109
|
{
|
|
5220
5110
|
group: 'HTML',
|
|
5221
5111
|
toolId: 12,
|
|
5222
5112
|
name: 'SEPARATOR',
|
|
5223
|
-
tag: `<hr [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5113
|
+
tag: `<hr [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')} />`,
|
|
5224
5114
|
icon: 'pi pi-minus',
|
|
5225
5115
|
inputProps: this.htmlInputProperties,
|
|
5226
|
-
inputs:
|
|
5116
|
+
inputs: {
|
|
5117
|
+
...this.htmlInputs,
|
|
5227
5118
|
width: '100%',
|
|
5228
5119
|
height: '2px',
|
|
5229
5120
|
// borderStyle: 'none',
|
|
@@ -5233,16 +5124,17 @@ class DesignerComponent {
|
|
|
5233
5124
|
borderRightStyle: 'none',
|
|
5234
5125
|
borderBottomStyle: 'none',
|
|
5235
5126
|
borderLeftStyle: 'none'
|
|
5236
|
-
}
|
|
5127
|
+
}
|
|
5237
5128
|
},
|
|
5238
5129
|
{
|
|
5239
5130
|
group: 'HTML',
|
|
5240
5131
|
toolId: 19,
|
|
5241
5132
|
name: 'HR',
|
|
5242
|
-
tag: `<hr [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5133
|
+
tag: `<hr [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')} />`,
|
|
5243
5134
|
icon: 'pi pi-minus',
|
|
5244
5135
|
inputProps: this.htmlInputProperties,
|
|
5245
|
-
inputs:
|
|
5136
|
+
inputs: {
|
|
5137
|
+
...this.htmlInputs,
|
|
5246
5138
|
width: '100%',
|
|
5247
5139
|
height: '2px',
|
|
5248
5140
|
// borderStyle: 'none',
|
|
@@ -5252,19 +5144,21 @@ class DesignerComponent {
|
|
|
5252
5144
|
borderRightStyle: 'none',
|
|
5253
5145
|
borderBottomStyle: 'none',
|
|
5254
5146
|
borderLeftStyle: 'none'
|
|
5255
|
-
}
|
|
5147
|
+
}
|
|
5256
5148
|
},
|
|
5257
5149
|
{
|
|
5258
5150
|
group: 'HTML',
|
|
5259
5151
|
toolId: 13,
|
|
5260
5152
|
name: 'KPI',
|
|
5261
|
-
tag: `<div [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5153
|
+
tag: `<div [attr.id]="uniqueName" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><div style="font-size:12px; opacity:.75;">{{inputs.caption}}</div><div style="font-size:28px; font-weight:700; line-height:1.2;">{{inputs.value}}</div></div>`,
|
|
5262
5154
|
icon: 'pi pi-chart-line',
|
|
5263
|
-
inputProps:
|
|
5155
|
+
inputProps: {
|
|
5156
|
+
...this.htmlInputProperties,
|
|
5264
5157
|
caption: { type: 'string', propertyCaption: 'Caption' },
|
|
5265
5158
|
value: { type: 'string', propertyCaption: 'Value' }
|
|
5266
|
-
}
|
|
5267
|
-
inputs:
|
|
5159
|
+
},
|
|
5160
|
+
inputs: {
|
|
5161
|
+
...this.htmlInputs,
|
|
5268
5162
|
caption: 'KPI',
|
|
5269
5163
|
value: '0',
|
|
5270
5164
|
width: '180px',
|
|
@@ -5277,42 +5171,47 @@ class DesignerComponent {
|
|
|
5277
5171
|
// borderStyle: 'solid',
|
|
5278
5172
|
// borderWidth: '1px',
|
|
5279
5173
|
// borderColor: '#d1d5db'
|
|
5280
|
-
}
|
|
5174
|
+
}
|
|
5281
5175
|
},
|
|
5282
5176
|
{
|
|
5283
5177
|
group: 'HTML',
|
|
5284
5178
|
toolId: 14,
|
|
5285
5179
|
name: 'DATE',
|
|
5286
|
-
tag: `<input [attr.id]="uniqueName" type="date" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5180
|
+
tag: `<input [attr.id]="uniqueName" type="date" [readonly]="inputs.readonly" [disabled]="inputs.disabled" [value]="inputs.value" (input)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')} />`,
|
|
5287
5181
|
icon: 'pi pi-calendar',
|
|
5288
|
-
inputProps:
|
|
5182
|
+
inputProps: {
|
|
5183
|
+
...this.htmlInputProperties,
|
|
5289
5184
|
value: { type: 'string', propertyCaption: 'Value (yyyy-mm-dd)' },
|
|
5290
5185
|
readonly: { type: 'boolean', propertyCaption: 'Readonly' },
|
|
5291
5186
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5292
|
-
}
|
|
5293
|
-
inputs:
|
|
5187
|
+
},
|
|
5188
|
+
inputs: {
|
|
5189
|
+
...this.htmlInputs,
|
|
5294
5190
|
value: '',
|
|
5295
5191
|
readonly: false,
|
|
5296
5192
|
disabled: false,
|
|
5297
5193
|
width: '180px',
|
|
5298
5194
|
height: '32px'
|
|
5299
|
-
}
|
|
5195
|
+
}
|
|
5300
5196
|
},
|
|
5301
5197
|
{
|
|
5302
5198
|
group: 'HTML',
|
|
5303
5199
|
toolId: 15,
|
|
5304
5200
|
name: 'SELECT',
|
|
5305
|
-
tag: `<select [attr.id]="uniqueName" [disabled]="inputs.disabled" [value]="inputs.value" (change)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5201
|
+
tag: `<select [attr.id]="uniqueName" [disabled]="inputs.disabled" [value]="inputs.value" (change)="inputs.value=$any($event.target).value" ngxDraggableDom="true" ngResizable [rzHandles]="'e'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><option *ngFor="let opt of (inputs.datasource?.component?.value?.resultInfo?.dato || (inputs.optionsCsv || '').split(','))" [value]="inputs.datasource?.component?.value?.resultInfo?.dato ? (opt?.[inputs.valueField] ?? '') : opt.trim()">{{getSelectOptionLabel(inputs, opt)}}</option></select>`,
|
|
5306
5202
|
icon: 'pi pi-list',
|
|
5307
|
-
inputProps:
|
|
5203
|
+
inputProps: {
|
|
5204
|
+
...this.htmlInputProperties,
|
|
5205
|
+
...this.dsProperties,
|
|
5308
5206
|
optionsCsv: { type: 'string', propertyCaption: 'Options CSV' },
|
|
5309
5207
|
valueField: { type: 'dictionary', values: [''], propertyCaption: 'Value Field' },
|
|
5310
5208
|
textField: { type: 'dictionary', values: [''], propertyCaption: 'Text Field' },
|
|
5311
5209
|
displayFormula: { type: 'button', propertyCaption: 'Display Formula', hideCaption: true, callback: this.openSelectDisplayFormulaEditor.bind(this) },
|
|
5312
5210
|
value: { type: 'string', propertyCaption: 'Selected Value' },
|
|
5313
5211
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5314
|
-
}
|
|
5315
|
-
inputs:
|
|
5212
|
+
},
|
|
5213
|
+
inputs: {
|
|
5214
|
+
...this.htmlInputs,
|
|
5316
5215
|
datasource: null,
|
|
5317
5216
|
componentRef: null,
|
|
5318
5217
|
optionsCsv: 'Option 1,Option 2,Option 3',
|
|
@@ -5323,15 +5222,17 @@ class DesignerComponent {
|
|
|
5323
5222
|
disabled: false,
|
|
5324
5223
|
width: '180px',
|
|
5325
5224
|
height: '32px'
|
|
5326
|
-
}
|
|
5225
|
+
}
|
|
5327
5226
|
},
|
|
5328
5227
|
{
|
|
5329
5228
|
group: 'HTML',
|
|
5330
5229
|
toolId: 16,
|
|
5331
5230
|
name: 'MULTISELECT',
|
|
5332
|
-
tag: `<select [attr.id]="uniqueName" multiple="true" [disabled]="inputs.disabled" [size]="inputs.size || 4" (change)="inputs.values = [].slice.call($any($event.target).selectedOptions || []).map(x => x.value)" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.
|
|
5231
|
+
tag: `<select [attr.id]="uniqueName" multiple="true" [disabled]="inputs.disabled" [size]="inputs.size || 4" (change)="inputs.values = [].slice.call($any($event.target).selectedOptions || []).map(x => x.value)" ngxDraggableDom="true" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" (moved)="onMoving($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}><option *ngFor="let opt of (inputs.datasource?.component?.value?.resultInfo?.dato || (inputs.optionsCsv || '').split(','))" [value]="inputs.datasource?.component?.value?.resultInfo?.dato ? (opt?.[inputs.valueField] ?? '') : opt.trim()" [selected]="(inputs.values || []).includes((inputs.datasource?.component?.value?.resultInfo?.dato ? (opt?.[inputs.valueField] ?? '') : opt.trim()) + '')">{{getSelectOptionLabel(inputs, opt)}}</option></select>`,
|
|
5333
5232
|
icon: 'pi pi-check-square',
|
|
5334
|
-
inputProps:
|
|
5233
|
+
inputProps: {
|
|
5234
|
+
...this.htmlInputProperties,
|
|
5235
|
+
...this.dsProperties,
|
|
5335
5236
|
optionsCsv: { type: 'string', propertyCaption: 'Options CSV' },
|
|
5336
5237
|
valueField: { type: 'dictionary', values: [''], propertyCaption: 'Value Field' },
|
|
5337
5238
|
textField: { type: 'dictionary', values: [''], propertyCaption: 'Text Field' },
|
|
@@ -5339,8 +5240,9 @@ class DesignerComponent {
|
|
|
5339
5240
|
values: { type: 'txt_area', propertyCaption: 'Selected Values (array)' },
|
|
5340
5241
|
size: { type: 'string', propertyCaption: 'Visible Rows' },
|
|
5341
5242
|
disabled: { type: 'boolean', propertyCaption: 'Disabled' }
|
|
5342
|
-
}
|
|
5343
|
-
inputs:
|
|
5243
|
+
},
|
|
5244
|
+
inputs: {
|
|
5245
|
+
...this.htmlInputs,
|
|
5344
5246
|
datasource: null,
|
|
5345
5247
|
componentRef: null,
|
|
5346
5248
|
optionsCsv: 'Option 1,Option 2,Option 3',
|
|
@@ -5352,7 +5254,7 @@ class DesignerComponent {
|
|
|
5352
5254
|
disabled: false,
|
|
5353
5255
|
width: '200px',
|
|
5354
5256
|
height: '96px'
|
|
5355
|
-
}
|
|
5257
|
+
}
|
|
5356
5258
|
},
|
|
5357
5259
|
{
|
|
5358
5260
|
group: 'DATA',
|
|
@@ -5427,7 +5329,7 @@ class DesignerComponent {
|
|
|
5427
5329
|
name: 'FILTERBAR',
|
|
5428
5330
|
tag: `<wuic-filter-bar [attr.id]="uniqueName" [datasource]="inputs.datasource?.component"></wuic-filter-bar>`,
|
|
5429
5331
|
icon: 'pi pi-filter',
|
|
5430
|
-
inputProps:
|
|
5332
|
+
inputProps: { ...this.dsProperties },
|
|
5431
5333
|
inputs: {
|
|
5432
5334
|
datasource: null,
|
|
5433
5335
|
componentRef: null
|
|
@@ -5439,10 +5341,11 @@ class DesignerComponent {
|
|
|
5439
5341
|
name: 'PAGER',
|
|
5440
5342
|
tag: `<wuic-pager [attr.id]="uniqueName" [datasource]="inputs.datasource?.component" [pageSize]="inputs.pageSize" [currentPage]="inputs.currentPage"></wuic-pager>`,
|
|
5441
5343
|
icon: 'pi pi-angle-double-right',
|
|
5442
|
-
inputProps:
|
|
5344
|
+
inputProps: {
|
|
5345
|
+
...this.dsProperties,
|
|
5443
5346
|
pageSize: { type: 'string', propertyCaption: 'Page Size' },
|
|
5444
5347
|
currentPage: { type: 'string', propertyCaption: 'Current Page' }
|
|
5445
|
-
}
|
|
5348
|
+
},
|
|
5446
5349
|
inputs: {
|
|
5447
5350
|
datasource: null,
|
|
5448
5351
|
componentRef: null,
|
|
@@ -5454,12 +5357,12 @@ class DesignerComponent {
|
|
|
5454
5357
|
group: 'CONTAINER',
|
|
5455
5358
|
toolId: 1000,
|
|
5456
5359
|
name: 'TABVIEW',
|
|
5457
|
-
tag: `<p-tabView [attr.id]="uniqueName" [tabs]="nestedComponents" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.replace('[ngStyle]', '[style]').
|
|
5360
|
+
tag: `<p-tabView [attr.id]="uniqueName" [tabs]="nestedComponents" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.replace('[ngStyle]', '[style]').replaceAll('0', 'inputs')}>
|
|
5458
5361
|
<wuic-dashboard [dashboardElements]="nestedComponents"></wuic-dashboard>
|
|
5459
5362
|
</p-tabView>`,
|
|
5460
5363
|
icon: 'pi pi-folder',
|
|
5461
5364
|
inputProps: this.htmlInputProperties,
|
|
5462
|
-
inputs:
|
|
5365
|
+
inputs: { ...this.htmlInputs, cssClass: [] },
|
|
5463
5366
|
nestedComponents: [],
|
|
5464
5367
|
allowedChildren: ['TABPANEL']
|
|
5465
5368
|
},
|
|
@@ -5476,14 +5379,17 @@ class DesignerComponent {
|
|
|
5476
5379
|
selected: { type: 'boolean', propertyCaption: 'Selected', hide: true }
|
|
5477
5380
|
},
|
|
5478
5381
|
inputs: { header: '<header>' },
|
|
5479
|
-
nestedComponents: []
|
|
5382
|
+
nestedComponents: [],
|
|
5383
|
+
// Appena droppato dentro un TABVIEW il tab deve diventare quello attivo,
|
|
5384
|
+
// altrimenti la sua area non e' visibile e non ci si possono droppare figli.
|
|
5385
|
+
onDrop: this.dropTabPanel.bind(this)
|
|
5480
5386
|
},
|
|
5481
5387
|
{
|
|
5482
5388
|
group: 'CONTAINER',
|
|
5483
5389
|
toolId: 1002,
|
|
5484
5390
|
name: 'SPLITTER',
|
|
5485
|
-
tag: `<p-splitter [attr.id]="uniqueName" [direction]="inputs.direction" (dragEnd)="inputs.resize?.($event, this)" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.
|
|
5486
|
-
<p-splitter-area *ngFor="let nestedArea of nestedComponents" [attr.id]="nestedArea.uniqueName" [size]="nestedArea.inputs.size" ${this.htmlStyleString.
|
|
5391
|
+
tag: `<p-splitter [attr.id]="uniqueName" [direction]="inputs.direction" (dragEnd)="inputs.resize?.($event, this)" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>
|
|
5392
|
+
<p-splitter-area *ngFor="let nestedArea of nestedComponents" [attr.id]="nestedArea.uniqueName" [size]="nestedArea.inputs.size" ${this.htmlStyleString.replaceAll('0', 'nestedArea.inputs')}>
|
|
5487
5393
|
<wuic-dashboard [dashboardElements]="nestedArea.nestedComponents"></wuic-dashboard>
|
|
5488
5394
|
</p-splitter-area>
|
|
5489
5395
|
</p-splitter>`,
|
|
@@ -5510,9 +5416,9 @@ class DesignerComponent {
|
|
|
5510
5416
|
group: 'CONTAINER',
|
|
5511
5417
|
toolId: 1004,
|
|
5512
5418
|
name: 'ACCORDION',
|
|
5513
|
-
tag: ` <cdk-accordion class="wuic-accordion-tab" [attr.id]="uniqueName" (dragEnd)="inputs.resize?.($event, this)" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.
|
|
5419
|
+
tag: ` <cdk-accordion class="wuic-accordion-tab" [attr.id]="uniqueName" (dragEnd)="inputs.resize?.($event, this)" ngxDraggableDom="true" (moved)="onMoving($event, this)" ngResizable [rzHandles]="'se'" (rzResizing)="onResizing($event, this)" ${this.htmlStyleString.replaceAll('0', 'inputs')}>
|
|
5514
5420
|
<cdk-accordion-item *ngFor="let nestedItem of nestedComponents" [attr.id]="nestedItem.uniqueName"
|
|
5515
|
-
${this.htmlStyleString.
|
|
5421
|
+
${this.htmlStyleString.replaceAll('0', 'nestedItem.inputs')}>
|
|
5516
5422
|
<div class="wuic-accordion-header">
|
|
5517
5423
|
<a class="wuic-accordion-header-link" (click)="inputs.toggle?.($event, this, nestedItem)">{{nestedItem.inputs.header}}</a>
|
|
5518
5424
|
</div>
|
|
@@ -5551,7 +5457,7 @@ class DesignerComponent {
|
|
|
5551
5457
|
* oppure ricarica la lista dashboard salvate.
|
|
5552
5458
|
*/
|
|
5553
5459
|
async ngOnInit() {
|
|
5554
|
-
this.lastKnownHashRoute = String(
|
|
5460
|
+
this.lastKnownHashRoute = String(globalThis?.location?.hash || '');
|
|
5555
5461
|
this.refreshAvailableProjectCssClasses();
|
|
5556
5462
|
// Registra handler chatbot per kind 'designer_inject': il chatbot
|
|
5557
5463
|
// popover (globale, vedi rag-chatbot.component.ts onApplyAction)
|
|
@@ -5702,7 +5608,10 @@ class DesignerComponent {
|
|
|
5702
5608
|
let topParent = null;
|
|
5703
5609
|
const declared = (node && typeof node.parent_unique_name === 'string') ? node.parent_unique_name.trim() : '';
|
|
5704
5610
|
if (declared) {
|
|
5705
|
-
if (
|
|
5611
|
+
if (this.isRootPlaceholder(declared)) {
|
|
5612
|
+
topParent = null; // alias esplicito del caso 3: va al ROOT
|
|
5613
|
+
}
|
|
5614
|
+
else if (declared.startsWith('<') && declared.endsWith('>')) {
|
|
5706
5615
|
if (lastTopLevelInjected) {
|
|
5707
5616
|
const resolved = this.resolveLayoutPlaceholder(declared, lastTopLevelInjected);
|
|
5708
5617
|
if (resolved)
|
|
@@ -5722,7 +5631,18 @@ class DesignerComponent {
|
|
|
5722
5631
|
// Aggiorna il last top-level per i prossimi placeholder. NB: usiamo
|
|
5723
5632
|
// findLastInjectedNode per pescare l'istanza vera (con onDrop appena
|
|
5724
5633
|
// eseguito = TR/TD/AREA gia' popolati), non il template clonato.
|
|
5725
|
-
|
|
5634
|
+
// FIX 2026-06-21 (designer-canvas-verify [2], layout 3-colonne): aggiorna
|
|
5635
|
+
// SOLO se il nodo iniettato e' un CONTAINER che auto-crea celle/aree
|
|
5636
|
+
// referenziabili via placeholder (TABLE/SPLITTER/ACCORDION/TABVIEW). I
|
|
5637
|
+
// leaf (DATASOURCE/DATAREPEATER/LABEL/...) NON devono sostituirlo: altrimenti
|
|
5638
|
+
// i placeholder <TD-N>/<AREA-N> dei nodi SUCCESSIVI risolverebbero contro
|
|
5639
|
+
// il leaf invece del container -> errore "Placeholder '<TD-0>' non
|
|
5640
|
+
// risolvibile dentro DATASOURCE__6" e i DATAREPEATER restano 0/3. Cosi'
|
|
5641
|
+
// tutti i nodi di un layout multi-cella risolvono contro lo stesso container.
|
|
5642
|
+
const PLACEHOLDER_CONTAINERS = ['TABLE', 'SPLITTER', 'ACCORDION', 'TABVIEW'];
|
|
5643
|
+
if (PLACEHOLDER_CONTAINERS.includes(String(node.tool_name || '').toUpperCase())) {
|
|
5644
|
+
lastTopLevelInjected = this.findLastInjectedNode(String(node.tool_name || ''), topParent) || lastTopLevelInjected;
|
|
5645
|
+
}
|
|
5726
5646
|
}
|
|
5727
5647
|
const rationale = (action?.['rationale'] || '').toString().slice(0, 100);
|
|
5728
5648
|
return { ok: true, message: `Iniettati ${totalInjected} elementi nel designer${rationale ? ': ' + rationale : ''}.` };
|
|
@@ -5768,13 +5688,13 @@ class DesignerComponent {
|
|
|
5768
5688
|
// (es. backgroundColor non e' in inputs di default ma e' in
|
|
5769
5689
|
// htmlInputProperties -> inputProps). Coerente col property panel
|
|
5770
5690
|
// che legge inputProps per costruire l'UI.
|
|
5771
|
-
if (!target.inputProps || !Object.
|
|
5691
|
+
if (!target.inputProps || !Object.hasOwn(target.inputProps, propName)) {
|
|
5772
5692
|
return { ok: false, message: `set_property: proprieta' '${propName}' non esposta su ${target.name} (${targetName})` };
|
|
5773
5693
|
}
|
|
5774
5694
|
// Inizializza inputs[propName] se non esiste, cosi' setValue() canonico
|
|
5775
5695
|
// (o l'assign fallback) lo trova.
|
|
5776
|
-
if (target.inputs && !Object.
|
|
5777
|
-
target.inputs[propName] = undefined;
|
|
5696
|
+
if (target.inputs && !Object.hasOwn(target.inputs, propName)) {
|
|
5697
|
+
(target.inputs)[propName] = undefined;
|
|
5778
5698
|
}
|
|
5779
5699
|
// Trova la toolProp matching (chiave 'key' === propName) per delegare a setValue
|
|
5780
5700
|
// canonico - applica converter, conditional, hooks specifici (datasource/select).
|
|
@@ -5791,8 +5711,8 @@ class DesignerComponent {
|
|
|
5791
5711
|
// Fallback: la prop non e' nelle toolProps (raro - inputProps inconsistenti).
|
|
5792
5712
|
// Assign diretto su inputs[prop], distinguendo BehaviorSubject vs primitivo.
|
|
5793
5713
|
const cur = target.inputs[propName];
|
|
5794
|
-
if (cur && typeof cur.next === 'function' && typeof cur.subscribe === 'function') {
|
|
5795
|
-
cur.next(newValue);
|
|
5714
|
+
if (cur && typeof (cur).next === 'function' && typeof (cur).subscribe === 'function') {
|
|
5715
|
+
(cur).next(newValue);
|
|
5796
5716
|
}
|
|
5797
5717
|
else {
|
|
5798
5718
|
target.inputs[propName] = newValue;
|
|
@@ -6030,16 +5950,16 @@ class DesignerComponent {
|
|
|
6030
5950
|
continue;
|
|
6031
5951
|
}
|
|
6032
5952
|
// BehaviorSubject: per datasource estrai uniqueName dell'oggetto wrappato
|
|
6033
|
-
if (v && typeof v.subscribe === 'function') {
|
|
6034
|
-
const inner = v.value;
|
|
5953
|
+
if (v && typeof (v).subscribe === 'function') {
|
|
5954
|
+
const inner = (v).value;
|
|
6035
5955
|
const wrappedName = inner?.uniqueName || inner?.component?.uniqueName;
|
|
6036
5956
|
if (wrappedName)
|
|
6037
5957
|
parts.push(`${key}=${wrappedName}`);
|
|
6038
5958
|
continue;
|
|
6039
5959
|
}
|
|
6040
5960
|
// Oggetto datasource (dropped-component-list, sync)
|
|
6041
|
-
if (v && typeof v === 'object' && v.uniqueName) {
|
|
6042
|
-
parts.push(`${key}=${v.uniqueName}`);
|
|
5961
|
+
if (v && typeof v === 'object' && (v).uniqueName) {
|
|
5962
|
+
parts.push(`${key}=${(v).uniqueName}`);
|
|
6043
5963
|
continue;
|
|
6044
5964
|
}
|
|
6045
5965
|
// Primitivi: includi solo se DIVERSI dal default del template
|
|
@@ -6246,7 +6166,7 @@ class DesignerComponent {
|
|
|
6246
6166
|
if (template.inputs) {
|
|
6247
6167
|
// Rimuovi le chiavi che node.inputs aveva aggiunto e non c'erano nel backup
|
|
6248
6168
|
for (const k of Object.keys(template.inputs)) {
|
|
6249
|
-
if (!Object.
|
|
6169
|
+
if (!Object.hasOwn(inputsBackup, k)) {
|
|
6250
6170
|
delete template.inputs[k];
|
|
6251
6171
|
}
|
|
6252
6172
|
}
|
|
@@ -6276,7 +6196,7 @@ class DesignerComponent {
|
|
|
6276
6196
|
const rowPct = `${Math.floor(10000 / childCount) / 100}%`;
|
|
6277
6197
|
for (const tr of justInjected.nestedComponents) {
|
|
6278
6198
|
if (tr?.inputs)
|
|
6279
|
-
tr.inputs['height'] = rowPct;
|
|
6199
|
+
(tr.inputs)['height'] = rowPct;
|
|
6280
6200
|
}
|
|
6281
6201
|
}
|
|
6282
6202
|
else if (toolName === 'SPLITTER') {
|
|
@@ -6284,7 +6204,7 @@ class DesignerComponent {
|
|
|
6284
6204
|
const sizePct = Math.floor(10000 / childCount) / 100;
|
|
6285
6205
|
for (const area of justInjected.nestedComponents) {
|
|
6286
6206
|
if (area?.inputs)
|
|
6287
|
-
area.inputs['size'] = sizePct;
|
|
6207
|
+
(area.inputs)['size'] = sizePct;
|
|
6288
6208
|
}
|
|
6289
6209
|
}
|
|
6290
6210
|
}
|
|
@@ -6303,7 +6223,7 @@ class DesignerComponent {
|
|
|
6303
6223
|
// 2. il target referenziato (es. DATASOURCE_15) deve esistere gia'
|
|
6304
6224
|
// in flattenedDashboardElements (= iniettato in nodo precedente
|
|
6305
6225
|
// del layout o gia' nel canvas).
|
|
6306
|
-
if (justInjected
|
|
6226
|
+
if (justInjected?.inputs && droppedBindingKeys.length > 0 && node.inputs) {
|
|
6307
6227
|
for (const key of droppedBindingKeys) {
|
|
6308
6228
|
const propDef = template.inputProps?.[key];
|
|
6309
6229
|
if (!propDef)
|
|
@@ -6314,7 +6234,7 @@ class DesignerComponent {
|
|
|
6314
6234
|
// Se gia' oggetto (LLM mandasse direttamente {uniqueName,component})
|
|
6315
6235
|
// o BehaviorSubject -> pass-through. Solo stringhe vengono risolte.
|
|
6316
6236
|
if (typeof raw !== 'string') {
|
|
6317
|
-
justInjected.inputs[key] = raw;
|
|
6237
|
+
(justInjected.inputs)[key] = raw;
|
|
6318
6238
|
continue;
|
|
6319
6239
|
}
|
|
6320
6240
|
const resolved = (raw.startsWith('<') && raw.endsWith('>'))
|
|
@@ -6381,7 +6301,7 @@ class DesignerComponent {
|
|
|
6381
6301
|
}
|
|
6382
6302
|
catch { /* subs collection non disponibile, sub continuera' a vivere col designer */ }
|
|
6383
6303
|
}
|
|
6384
|
-
justInjected.inputs[key] = {
|
|
6304
|
+
(justInjected.inputs)[key] = {
|
|
6385
6305
|
uniqueName: refElement.uniqueName,
|
|
6386
6306
|
[asyncPath]: compBs
|
|
6387
6307
|
};
|
|
@@ -6395,8 +6315,8 @@ class DesignerComponent {
|
|
|
6395
6315
|
// non sa quali colonne mostrare e la grid resta vuota anche con
|
|
6396
6316
|
// datasource bindato correttamente.
|
|
6397
6317
|
if (justInjected && toolName === 'DATAREPEATER' && justInjected.inputs) {
|
|
6398
|
-
const ds = justInjected.inputs['datasource'];
|
|
6399
|
-
const action = this.getRepeaterAction(justInjected) || justInjected.inputs['action'] || 'list';
|
|
6318
|
+
const ds = (justInjected.inputs)['datasource'];
|
|
6319
|
+
const action = this.getRepeaterAction(justInjected) || (justInjected.inputs)['action'] || 'list';
|
|
6400
6320
|
if (ds) {
|
|
6401
6321
|
try {
|
|
6402
6322
|
this.propertyTreeBuilder(action, justInjected.inputs);
|
|
@@ -6443,7 +6363,10 @@ class DesignerComponent {
|
|
|
6443
6363
|
let childParent = injectedUniqueName;
|
|
6444
6364
|
const declared = (ch && typeof ch.parent_unique_name === 'string') ? ch.parent_unique_name.trim() : '';
|
|
6445
6365
|
if (declared) {
|
|
6446
|
-
if (
|
|
6366
|
+
if (this.isRootPlaceholder(declared)) {
|
|
6367
|
+
childParent = null; // alias esplicito del ROOT anche in ricorsione
|
|
6368
|
+
}
|
|
6369
|
+
else if (declared.startsWith('<') && declared.endsWith('>')) {
|
|
6447
6370
|
// Placeholder semantico: risolvi dentro justInjected
|
|
6448
6371
|
const resolved = this.resolveLayoutPlaceholder(declared, justInjected);
|
|
6449
6372
|
if (resolved)
|
|
@@ -6479,10 +6402,24 @@ class DesignerComponent {
|
|
|
6479
6402
|
* Ritorna l'uniqueName del nodo risolto, o `null` se il pattern non matcha
|
|
6480
6403
|
* o l'index e' out-of-bounds (es. <TD-3> in tabella 2x2).
|
|
6481
6404
|
*/
|
|
6405
|
+
/**
|
|
6406
|
+
* `<ROOT>` e' l'alias ESPLICITO del caso 3 ("null/missing: va al ROOT"), non un
|
|
6407
|
+
* placeholder container-relative. Senza questo riconoscimento finiva nel ramo
|
|
6408
|
+
* angle-bracket e `resolveLayoutPlaceholder` lo scartava (`'ROOT'.split('-')`
|
|
6409
|
+
* da un solo elemento -> `null`), quindi era IRRISOLVIBILE anche con un
|
|
6410
|
+
* container iniettato prima: un `<ROOT>` faceva abortire l'intero inject.
|
|
6411
|
+
* Caso reale: "aggiungi un bottone Esporta nel canvas" su canvas vuoto.
|
|
6412
|
+
* Nota: se il token arrivasse fino a `drop()` il nodo verrebbe piazzato
|
|
6413
|
+
* correttamente al root (destination = dashboardElements), quindi non e' un
|
|
6414
|
+
* vincolo del canvas — era solo questo ramo a trasformarlo in un rifiuto.
|
|
6415
|
+
*/
|
|
6416
|
+
isRootPlaceholder(declared) {
|
|
6417
|
+
return /^<\s*ROOT\s*>$/i.test((declared || '').trim());
|
|
6418
|
+
}
|
|
6482
6419
|
resolveLayoutPlaceholder(placeholder, parentNode) {
|
|
6483
6420
|
if (!placeholder || !parentNode)
|
|
6484
6421
|
return null;
|
|
6485
|
-
const inner = placeholder.
|
|
6422
|
+
const inner = placeholder.replaceAll(/^<|>$/g, '').trim();
|
|
6486
6423
|
const parts = inner.split('-');
|
|
6487
6424
|
if (parts.length < 2)
|
|
6488
6425
|
return null;
|
|
@@ -6491,7 +6428,7 @@ class DesignerComponent {
|
|
|
6491
6428
|
if (tag === 'TD') {
|
|
6492
6429
|
// <TD-N> singolo index OR <TD-R-C> riga/colonna
|
|
6493
6430
|
if (parts.length === 2) {
|
|
6494
|
-
const n = parseInt(parts[1], 10);
|
|
6431
|
+
const n = Number.parseInt(parts[1], 10);
|
|
6495
6432
|
if (!Number.isFinite(n))
|
|
6496
6433
|
return null;
|
|
6497
6434
|
// Row-major lookup: TR[i].TD[j]
|
|
@@ -6506,8 +6443,8 @@ class DesignerComponent {
|
|
|
6506
6443
|
return null;
|
|
6507
6444
|
}
|
|
6508
6445
|
if (parts.length === 3) {
|
|
6509
|
-
const r = parseInt(parts[1], 10);
|
|
6510
|
-
const c = parseInt(parts[2], 10);
|
|
6446
|
+
const r = Number.parseInt(parts[1], 10);
|
|
6447
|
+
const c = Number.parseInt(parts[2], 10);
|
|
6511
6448
|
if (!Number.isFinite(r) || !Number.isFinite(c))
|
|
6512
6449
|
return null;
|
|
6513
6450
|
const tr = nested[r];
|
|
@@ -6519,25 +6456,25 @@ class DesignerComponent {
|
|
|
6519
6456
|
return null;
|
|
6520
6457
|
}
|
|
6521
6458
|
if (tag === 'TR') {
|
|
6522
|
-
const n = parseInt(parts[1], 10);
|
|
6459
|
+
const n = Number.parseInt(parts[1], 10);
|
|
6523
6460
|
if (!Number.isFinite(n))
|
|
6524
6461
|
return null;
|
|
6525
6462
|
return nested[n]?.uniqueName || null;
|
|
6526
6463
|
}
|
|
6527
6464
|
if (tag === 'SPLITTER' && parts[1].toUpperCase() === 'AREA') {
|
|
6528
|
-
const n = parseInt(parts[2], 10);
|
|
6465
|
+
const n = Number.parseInt(parts[2], 10);
|
|
6529
6466
|
if (!Number.isFinite(n))
|
|
6530
6467
|
return null;
|
|
6531
6468
|
return nested[n]?.uniqueName || null;
|
|
6532
6469
|
}
|
|
6533
6470
|
if (tag === 'ACCORDION' && parts[1].toUpperCase() === 'AREA') {
|
|
6534
|
-
const n = parseInt(parts[2], 10);
|
|
6471
|
+
const n = Number.parseInt(parts[2], 10);
|
|
6535
6472
|
if (!Number.isFinite(n))
|
|
6536
6473
|
return null;
|
|
6537
6474
|
return nested[n]?.uniqueName || null;
|
|
6538
6475
|
}
|
|
6539
6476
|
if (tag === 'TABPANEL') {
|
|
6540
|
-
const n = parseInt(parts[1], 10);
|
|
6477
|
+
const n = Number.parseInt(parts[1], 10);
|
|
6541
6478
|
if (!Number.isFinite(n))
|
|
6542
6479
|
return null;
|
|
6543
6480
|
return nested[n]?.uniqueName || null;
|
|
@@ -6579,12 +6516,12 @@ class DesignerComponent {
|
|
|
6579
6516
|
const trim = placeholder.trim();
|
|
6580
6517
|
if (!trim.startsWith('<') || !trim.endsWith('>'))
|
|
6581
6518
|
return null;
|
|
6582
|
-
const inner = trim.
|
|
6583
|
-
const m =
|
|
6519
|
+
const inner = trim.replaceAll(/^<|>$/g, '').trim();
|
|
6520
|
+
const m = /^([A-Za-z][A-Za-z0-9_-]*)-(\d+)$/.exec(inner);
|
|
6584
6521
|
if (!m)
|
|
6585
6522
|
return null;
|
|
6586
6523
|
const targetName = m[1].toUpperCase();
|
|
6587
|
-
const targetIdx = parseInt(m[2], 10);
|
|
6524
|
+
const targetIdx = Number.parseInt(m[2], 10);
|
|
6588
6525
|
if (!Number.isFinite(targetIdx) || targetIdx < 0)
|
|
6589
6526
|
return null;
|
|
6590
6527
|
const matching = (this.flattenedDashboardElements || [])
|
|
@@ -6624,7 +6561,7 @@ class DesignerComponent {
|
|
|
6624
6561
|
*/
|
|
6625
6562
|
_designerGestureMouseUp;
|
|
6626
6563
|
async onWindowHashChange() {
|
|
6627
|
-
const nextHash = String(
|
|
6564
|
+
const nextHash = String(globalThis?.location?.hash || '');
|
|
6628
6565
|
const previousHash = this.lastKnownHashRoute;
|
|
6629
6566
|
if (this.suppressNextHashGuard) {
|
|
6630
6567
|
this.lastKnownHashRoute = nextHash;
|
|
@@ -6643,10 +6580,10 @@ class DesignerComponent {
|
|
|
6643
6580
|
return;
|
|
6644
6581
|
}
|
|
6645
6582
|
this.suppressNextHashGuard = true;
|
|
6646
|
-
|
|
6583
|
+
globalThis.location.hash = previousHash || '#/';
|
|
6647
6584
|
setTimeout(() => {
|
|
6648
6585
|
this.suppressNextHashGuard = false;
|
|
6649
|
-
this.lastKnownHashRoute = String(
|
|
6586
|
+
this.lastKnownHashRoute = String(globalThis?.location?.hash || '');
|
|
6650
6587
|
}, 0);
|
|
6651
6588
|
}
|
|
6652
6589
|
onWindowBeforeUnload(event) {
|
|
@@ -6757,14 +6694,14 @@ class DesignerComponent {
|
|
|
6757
6694
|
}
|
|
6758
6695
|
try {
|
|
6759
6696
|
const urlObj = new URL(href, document.baseURI);
|
|
6760
|
-
const cleanedPath = String(urlObj.pathname || '').
|
|
6697
|
+
const cleanedPath = String(urlObj.pathname || '').replaceAll('\\', '/');
|
|
6761
6698
|
const assetsIdx = cleanedPath.toLowerCase().indexOf('/assets/');
|
|
6762
6699
|
if (assetsIdx >= 0) {
|
|
6763
6700
|
// Keep relative assets path (e.g. assets/my-dash/file.css) to avoid collisions.
|
|
6764
6701
|
return cleanedPath.substring(assetsIdx + 1);
|
|
6765
6702
|
}
|
|
6766
6703
|
const parts = cleanedPath.split('/');
|
|
6767
|
-
return parts
|
|
6704
|
+
return parts.at(-1) || cleanedPath;
|
|
6768
6705
|
}
|
|
6769
6706
|
catch {
|
|
6770
6707
|
return href;
|
|
@@ -7079,7 +7016,7 @@ class DesignerComponent {
|
|
|
7079
7016
|
* Normalizza un path CSS in formato slash `/` e trim.
|
|
7080
7017
|
*/
|
|
7081
7018
|
normalizeSheetPath(path) {
|
|
7082
|
-
return String(path || '').trim().
|
|
7019
|
+
return String(path || '').trim().replaceAll('\\', '/');
|
|
7083
7020
|
}
|
|
7084
7021
|
/**
|
|
7085
7022
|
* Normalizza il payload foglio CSS (path, selector, class names, key/value style)
|
|
@@ -7141,7 +7078,7 @@ class DesignerComponent {
|
|
|
7141
7078
|
return WtoolboxService.appSettings?.file_path;
|
|
7142
7079
|
}
|
|
7143
7080
|
catch {
|
|
7144
|
-
return
|
|
7081
|
+
return globalThis.location.origin;
|
|
7145
7082
|
}
|
|
7146
7083
|
};
|
|
7147
7084
|
const resolveBackendStaticBaseUrl = () => {
|
|
@@ -7150,7 +7087,7 @@ class DesignerComponent {
|
|
|
7150
7087
|
if (!raw) {
|
|
7151
7088
|
return '';
|
|
7152
7089
|
}
|
|
7153
|
-
const url = new URL(raw,
|
|
7090
|
+
const url = new URL(raw, globalThis.location.origin);
|
|
7154
7091
|
const apiIndex = url.pathname.toLowerCase().indexOf('/api/');
|
|
7155
7092
|
let staticPath = apiIndex >= 0 ? url.pathname.substring(0, apiIndex) : url.pathname;
|
|
7156
7093
|
if (!staticPath.endsWith('/')) {
|
|
@@ -7163,7 +7100,7 @@ class DesignerComponent {
|
|
|
7163
7100
|
}
|
|
7164
7101
|
};
|
|
7165
7102
|
const buildCssHref = (baseUrl, relativePath) => {
|
|
7166
|
-
const baseUrlObj = new URL(baseUrl ||
|
|
7103
|
+
const baseUrlObj = new URL(baseUrl || globalThis.location.origin, globalThis.location.origin);
|
|
7167
7104
|
const basePath = baseUrlObj.pathname.toLowerCase();
|
|
7168
7105
|
let resolvedRelativePath = relativePath;
|
|
7169
7106
|
if (basePath.endsWith('/assets/') && resolvedRelativePath.toLowerCase().startsWith('assets/')) {
|
|
@@ -7375,6 +7312,8 @@ class DesignerComponent {
|
|
|
7375
7312
|
this.applyHistorySnapshot(target, false);
|
|
7376
7313
|
}
|
|
7377
7314
|
catch (err) {
|
|
7315
|
+
// Snapshot apply failed — best-effort: keep the designer alive, the
|
|
7316
|
+
// history stacks are already consistent (target popped, previous pushed).
|
|
7378
7317
|
}
|
|
7379
7318
|
}
|
|
7380
7319
|
/**
|
|
@@ -7454,11 +7393,53 @@ class DesignerComponent {
|
|
|
7454
7393
|
if (this.isTypingTarget(event.target)) {
|
|
7455
7394
|
return;
|
|
7456
7395
|
}
|
|
7396
|
+
const rawKey = String(event.key || '');
|
|
7457
7397
|
const ctrlOrMeta = !!event.ctrlKey || !!event.metaKey;
|
|
7398
|
+
// ---- Scorciatoie SENZA modificatore (feature B) ----
|
|
7458
7399
|
if (!ctrlOrMeta) {
|
|
7400
|
+
// Delete/Backspace: rimuove l'elemento selezionato (o TUTTI i multi-selezionati).
|
|
7401
|
+
if (rawKey === 'Delete' || rawKey === 'Backspace') {
|
|
7402
|
+
if (this.selectedTools.length > 1) {
|
|
7403
|
+
event.preventDefault();
|
|
7404
|
+
const names = this.selectedTools.map((t) => t.uniqueName);
|
|
7405
|
+
this.clearMultiSelection();
|
|
7406
|
+
names.forEach((n) => this.removeElementByName(n));
|
|
7407
|
+
this.commitHistoryIfChanged();
|
|
7408
|
+
return;
|
|
7409
|
+
}
|
|
7410
|
+
if (this.tool?.uniqueName) {
|
|
7411
|
+
event.preventDefault();
|
|
7412
|
+
this.removeElementByName(this.tool.uniqueName);
|
|
7413
|
+
this.commitHistoryIfChanged();
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7416
|
+
}
|
|
7417
|
+
// Frecce: nudge di posizione dell'elemento selezionato (passo = gridSize se
|
|
7418
|
+
// snap attivo, altrimenti 1px; con Shift un passo piu' grande).
|
|
7419
|
+
const nudges = {
|
|
7420
|
+
ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1]
|
|
7421
|
+
};
|
|
7422
|
+
if (nudges[rawKey] && (this.tool || this.selectedTools.length > 1)) {
|
|
7423
|
+
event.preventDefault();
|
|
7424
|
+
const step = (this.snapToGridEnabled ? this.gridSize : 1) * (event.shiftKey ? 5 : 1);
|
|
7425
|
+
const ux = nudges[rawKey][0] * step, uy = nudges[rawKey][1] * step;
|
|
7426
|
+
if (this.selectedTools.length > 1) {
|
|
7427
|
+
this.nudgeElements(this.selectedTools, ux, uy);
|
|
7428
|
+
}
|
|
7429
|
+
else {
|
|
7430
|
+
this.nudgeSelectedElement(ux, uy);
|
|
7431
|
+
}
|
|
7432
|
+
return;
|
|
7433
|
+
}
|
|
7434
|
+
return;
|
|
7435
|
+
}
|
|
7436
|
+
const key = rawKey.toLowerCase();
|
|
7437
|
+
// Ctrl/Cmd+S: salva (previene il dialog di salvataggio del browser).
|
|
7438
|
+
if (key === 's') {
|
|
7439
|
+
event.preventDefault();
|
|
7440
|
+
void this.saveDashboard();
|
|
7459
7441
|
return;
|
|
7460
7442
|
}
|
|
7461
|
-
const key = String(event.key || '').toLowerCase();
|
|
7462
7443
|
if (key === 'z' && event.shiftKey) {
|
|
7463
7444
|
event.preventDefault();
|
|
7464
7445
|
this.redo();
|
|
@@ -7472,6 +7453,28 @@ class DesignerComponent {
|
|
|
7472
7453
|
if (key === 'y') {
|
|
7473
7454
|
event.preventDefault();
|
|
7474
7455
|
this.redo();
|
|
7456
|
+
return;
|
|
7457
|
+
}
|
|
7458
|
+
if (key === 'd') {
|
|
7459
|
+
event.preventDefault();
|
|
7460
|
+
void this.duplicateDesignerElement(this.tool);
|
|
7461
|
+
return;
|
|
7462
|
+
}
|
|
7463
|
+
if (key === 'c') {
|
|
7464
|
+
// Non intercettare Ctrl+C se l'utente sta copiando una selezione di testo.
|
|
7465
|
+
const textSelection = (globalThis.window !== undefined && globalThis.getSelection)
|
|
7466
|
+
? String(globalThis.getSelection() || '')
|
|
7467
|
+
: '';
|
|
7468
|
+
if (textSelection) {
|
|
7469
|
+
return;
|
|
7470
|
+
}
|
|
7471
|
+
event.preventDefault();
|
|
7472
|
+
this.copyDesignerElement(this.tool);
|
|
7473
|
+
return;
|
|
7474
|
+
}
|
|
7475
|
+
if (key === 'v') {
|
|
7476
|
+
event.preventDefault();
|
|
7477
|
+
void this.pasteDesignerElement();
|
|
7475
7478
|
}
|
|
7476
7479
|
}
|
|
7477
7480
|
/**
|
|
@@ -7502,9 +7505,7 @@ class DesignerComponent {
|
|
|
7502
7505
|
* via touch su designer.
|
|
7503
7506
|
*/
|
|
7504
7507
|
onWindowTouchend() {
|
|
7505
|
-
|
|
7506
|
-
this.commitHistoryAfterDesignerGestureIfNeeded();
|
|
7507
|
-
}
|
|
7508
|
+
this.onWindowMouseup();
|
|
7508
7509
|
}
|
|
7509
7510
|
/**
|
|
7510
7511
|
* Se l'utente ha appena terminato un drag/resize di un elemento del designer
|
|
@@ -7697,12 +7698,49 @@ class DesignerComponent {
|
|
|
7697
7698
|
{
|
|
7698
7699
|
separator: true
|
|
7699
7700
|
},
|
|
7701
|
+
{
|
|
7702
|
+
label: this.t('designer.menu_export_json', 'Esporta JSON'),
|
|
7703
|
+
icon: 'pi pi-download',
|
|
7704
|
+
command: () => this.exportDashboardJson()
|
|
7705
|
+
},
|
|
7706
|
+
{
|
|
7707
|
+
label: this.t('designer.menu_import_json', 'Importa JSON'),
|
|
7708
|
+
icon: 'pi pi-upload',
|
|
7709
|
+
command: () => this.importDashboardJson()
|
|
7710
|
+
},
|
|
7711
|
+
{
|
|
7712
|
+
label: this.t('designer.menu_save_preset', 'Salva come preset'),
|
|
7713
|
+
icon: 'pi pi-bookmark',
|
|
7714
|
+
command: () => this.saveDashboardAsPreset()
|
|
7715
|
+
},
|
|
7716
|
+
{
|
|
7717
|
+
label: this.t('designer.menu_apply_preset', 'Applica preset'),
|
|
7718
|
+
icon: 'pi pi-bookmark-fill',
|
|
7719
|
+
command: () => this.applyDashboardPreset()
|
|
7720
|
+
},
|
|
7721
|
+
{
|
|
7722
|
+
separator: true
|
|
7723
|
+
},
|
|
7700
7724
|
{
|
|
7701
7725
|
label: this.hideDatasourcesInCanvas
|
|
7702
7726
|
? this.t('designer.menu_show_datasources', 'Mostra datasource nel canvas')
|
|
7703
7727
|
: this.t('designer.menu_hide_datasources', 'Nascondi datasource nel canvas'),
|
|
7704
7728
|
icon: this.hideDatasourcesInCanvas ? 'pi pi-eye' : 'pi pi-eye-slash',
|
|
7705
7729
|
command: () => this.toggleHideDatasourcesInCanvas()
|
|
7730
|
+
},
|
|
7731
|
+
{
|
|
7732
|
+
label: this.snapToGridEnabled
|
|
7733
|
+
? this.t('designer.menu_snap_off', 'Disattiva snap alla griglia')
|
|
7734
|
+
: this.t('designer.menu_snap_on', 'Attiva snap alla griglia'),
|
|
7735
|
+
icon: this.snapToGridEnabled ? 'pi pi-th-large' : 'pi pi-table',
|
|
7736
|
+
command: () => this.toggleSnapToGrid()
|
|
7737
|
+
},
|
|
7738
|
+
{
|
|
7739
|
+
label: this.absoluteFlowEnabled
|
|
7740
|
+
? this.t('designer.menu_absflow_off', 'Disattiva flusso assoluto')
|
|
7741
|
+
: this.t('designer.menu_absflow_on', 'Attiva flusso assoluto'),
|
|
7742
|
+
icon: 'pi pi-arrows-alt',
|
|
7743
|
+
command: () => this.toggleAbsoluteFlow()
|
|
7706
7744
|
}
|
|
7707
7745
|
];
|
|
7708
7746
|
}
|
|
@@ -7710,10 +7748,521 @@ class DesignerComponent {
|
|
|
7710
7748
|
this.hideDatasourcesInCanvas = !this.hideDatasourcesInCanvas;
|
|
7711
7749
|
this.refreshGraphActionsMenuItems();
|
|
7712
7750
|
}
|
|
7713
|
-
|
|
7714
|
-
|
|
7751
|
+
/**
|
|
7752
|
+
* Flusso "assoluto" (default OFF = flusso normale come sempre). Quando ON, ogni
|
|
7753
|
+
* elemento droppato nasce `position: absolute` con `left/top` dalla posizione
|
|
7754
|
+
* del drop -> non partecipa al flusso, quindi ridimensionarne uno NON sposta gli
|
|
7755
|
+
* altri. E' per-elemento (chi nasce con position:absolute segue il nuovo modello,
|
|
7756
|
+
* gli elementi salvati senza restano a flusso) -> retrocompatibile senza flag globale.
|
|
7757
|
+
*/
|
|
7758
|
+
absoluteFlowEnabled = false;
|
|
7759
|
+
toggleAbsoluteFlow() {
|
|
7760
|
+
const turningOn = !this.absoluteFlowEnabled;
|
|
7761
|
+
// Se attivo il flusso assoluto con elementi gia' nel canvas, quelli restano a
|
|
7762
|
+
// flusso: notifico che vanno riposizionati manualmente.
|
|
7763
|
+
if (turningOn && (this.dashboardElements?.length || 0) > 0) {
|
|
7764
|
+
WtoolboxService.messageNotificationService.add({
|
|
7765
|
+
severity: 'warn',
|
|
7766
|
+
summary: this.t('designer.absflow.title', 'Flusso assoluto'),
|
|
7767
|
+
detail: this.t('designer.absflow.existing_manual', 'Gli elementi gia\' presenti restano nel flusso attuale: vanno riposizionati a mano (position/left/top) per adottare il layout assoluto.')
|
|
7768
|
+
});
|
|
7769
|
+
}
|
|
7770
|
+
this.absoluteFlowEnabled = turningOn;
|
|
7771
|
+
this.refreshGraphActionsMenuItems();
|
|
7772
|
+
this.cd.detectChanges();
|
|
7715
7773
|
}
|
|
7716
|
-
|
|
7774
|
+
/**
|
|
7775
|
+
* Posizione del drop relativa a un elemento di riferimento (il canvas per i drop
|
|
7776
|
+
* top-level, oppure il container quando droppo dentro un contenitore), snappata
|
|
7777
|
+
* alla griglia se lo snap e' attivo.
|
|
7778
|
+
*/
|
|
7779
|
+
getDropPositionRelativeTo(event, refEl) {
|
|
7780
|
+
const rect = refEl ? refEl.getBoundingClientRect() : null;
|
|
7781
|
+
const oe = (event?.originalEvent) ? event.originalEvent : event;
|
|
7782
|
+
const fallbackCx = (typeof event?.clientX === 'number') ? event.clientX : null;
|
|
7783
|
+
const fallbackCy = (typeof event?.clientY === 'number') ? event.clientY : null;
|
|
7784
|
+
const cx = (typeof oe?.clientX === 'number') ? oe.clientX : fallbackCx;
|
|
7785
|
+
const cy = (typeof oe?.clientY === 'number') ? oe.clientY : fallbackCy;
|
|
7786
|
+
if (!rect || cx === null || cy === null) {
|
|
7787
|
+
return { left: 0, top: 0 };
|
|
7788
|
+
}
|
|
7789
|
+
// Coordinate del cursore relative al box del riferimento (client rect gia'
|
|
7790
|
+
// include l'eventuale bordo/scroll del riferimento: sottraggo il suo scroll).
|
|
7791
|
+
let left = cx - rect.left + (refEl ? refEl.scrollLeft : 0);
|
|
7792
|
+
let top = cy - rect.top + (refEl ? refEl.scrollTop : 0);
|
|
7793
|
+
const g = DesignerSnapState.gridSize;
|
|
7794
|
+
if (g && g > 0) {
|
|
7795
|
+
left = Math.round(left / g) * g;
|
|
7796
|
+
top = Math.round(top / g) * g;
|
|
7797
|
+
}
|
|
7798
|
+
return { left: Math.max(0, Math.round(left)), top: Math.max(0, Math.round(top)) };
|
|
7799
|
+
}
|
|
7800
|
+
/** Snap alla griglia per drag/resize (feature B). Off di default. */
|
|
7801
|
+
snapToGridEnabled = false;
|
|
7802
|
+
/** Passo della griglia di snap in px. */
|
|
7803
|
+
gridSize = 10;
|
|
7804
|
+
/**
|
|
7805
|
+
* Attiva/disattiva lo snap alla griglia: propaga la dimensione griglia alle
|
|
7806
|
+
* istanze DynamicDashboardTemplateComponent (che arrotondano posizione/dimensione
|
|
7807
|
+
* in onMoving/onResizing) e mostra un overlay griglia sul canvas.
|
|
7808
|
+
*/
|
|
7809
|
+
toggleSnapToGrid() {
|
|
7810
|
+
this.snapToGridEnabled = !this.snapToGridEnabled;
|
|
7811
|
+
DynamicDashboardTemplateComponent.snapGridSize = this.snapToGridEnabled ? this.gridSize : 0;
|
|
7812
|
+
// Alimenta lo stato condiviso letto dalle direttive drag/resize (lo snap
|
|
7813
|
+
// effettivo su move/resize avviene li', non solo nel bookkeeping onMoving).
|
|
7814
|
+
DesignerSnapState.gridSize = this.snapToGridEnabled ? this.gridSize : 0;
|
|
7815
|
+
// Attivando lo snap, allinea SUBITO alla griglia anche gli elementi gia'
|
|
7816
|
+
// presenti (droppati/spostati prima di attivarlo).
|
|
7817
|
+
if (this.snapToGridEnabled) {
|
|
7818
|
+
this.snapAllExistingElementsToGrid();
|
|
7819
|
+
}
|
|
7820
|
+
this.refreshGraphActionsMenuItems();
|
|
7821
|
+
this.cd.detectChanges();
|
|
7822
|
+
}
|
|
7823
|
+
/** Snappa alla griglia la posizione assoluta di tutti gli elementi top-level presenti. */
|
|
7824
|
+
snapAllExistingElementsToGrid() {
|
|
7825
|
+
const g = DesignerSnapState.gridSize;
|
|
7826
|
+
if (!g || g <= 0) {
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7829
|
+
setTimeout(() => {
|
|
7830
|
+
let changed = false;
|
|
7831
|
+
for (const tool of (this.dashboardElements || [])) {
|
|
7832
|
+
if (this.snapElementAbsoluteToGrid(tool, g)) {
|
|
7833
|
+
changed = true;
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
if (changed) {
|
|
7837
|
+
this.commitHistoryIfChanged();
|
|
7838
|
+
this.cd.detectChanges();
|
|
7839
|
+
}
|
|
7840
|
+
}, 0);
|
|
7841
|
+
}
|
|
7842
|
+
/**
|
|
7843
|
+
* Allinea la posizione VISIVA assoluta di un elemento al multiplo di griglia
|
|
7844
|
+
* agendo sulla traslazione del suo `transform` (offsetLeft/Top escludono il
|
|
7845
|
+
* transform -> assoluto = offset + traslazione). Ritorna true se ha modificato.
|
|
7846
|
+
*/
|
|
7847
|
+
snapElementAbsoluteToGrid(tool, g) {
|
|
7848
|
+
const dom = tool?.uniqueName ? document.getElementById(tool.uniqueName) : null;
|
|
7849
|
+
if (!dom || !tool.inputs) {
|
|
7850
|
+
return false;
|
|
7851
|
+
}
|
|
7852
|
+
let sx = 1, sy = 0, kx = 0, ky = 1, tx = 0, ty = 0;
|
|
7853
|
+
const m = String(tool.inputs['transform'] || '').match(/matrix\(([^)]+)\)/);
|
|
7854
|
+
if (m) {
|
|
7855
|
+
const p = m[1].split(',').map((x) => Number.parseFloat(x));
|
|
7856
|
+
if (p.length >= 6 && p.every((n) => !Number.isNaN(Number(n)))) {
|
|
7857
|
+
[sx, sy, kx, ky, tx, ty] = p;
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7860
|
+
const newTx = Math.round((dom.offsetLeft + tx) / g) * g - dom.offsetLeft;
|
|
7861
|
+
const newTy = Math.round((dom.offsetTop + ty) / g) * g - dom.offsetTop;
|
|
7862
|
+
if (newTx === tx && newTy === ty) {
|
|
7863
|
+
return false;
|
|
7864
|
+
}
|
|
7865
|
+
tool.inputs['transform'] = `matrix(${sx}, ${sy}, ${kx}, ${ky}, ${newTx}, ${newTy})`;
|
|
7866
|
+
return true;
|
|
7867
|
+
}
|
|
7868
|
+
/**
|
|
7869
|
+
* Sposta l'elemento selezionato di (dx, dy) px agendo sulla translation della
|
|
7870
|
+
* matrice `transform` (stesso campo che usa onMoving). Usato dalle frecce.
|
|
7871
|
+
*/
|
|
7872
|
+
nudgeSelectedElement(dx, dy) {
|
|
7873
|
+
const el = this.tool;
|
|
7874
|
+
if (!el?.inputs) {
|
|
7875
|
+
return;
|
|
7876
|
+
}
|
|
7877
|
+
let scaleX = 1, skewY = 0, skewX = 0, scaleY = 1, tx = 0, ty = 0;
|
|
7878
|
+
const cur = String(el.inputs['transform'] || '');
|
|
7879
|
+
const m = /matrix\(([^)]+)\)/.exec(cur);
|
|
7880
|
+
if (m) {
|
|
7881
|
+
const parts = m[1].split(',').map((x) => Number.parseFloat(x));
|
|
7882
|
+
if (parts.length >= 6 && parts.every((n) => !Number.isNaN(Number(n)))) {
|
|
7883
|
+
[scaleX, skewY, skewX, scaleY, tx, ty] = parts;
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
el.inputs['transform'] = `matrix(${scaleX}, ${skewY}, ${skewX}, ${scaleY}, ${tx + dx}, ${ty + dy})`;
|
|
7887
|
+
this.commitHistoryIfChanged();
|
|
7888
|
+
this.cd.detectChanges();
|
|
7889
|
+
}
|
|
7890
|
+
// ===================== Feature F: preset/template riusabili =====================
|
|
7891
|
+
// Libreria di preset lato client (localStorage): mappa nome -> boardcontent JSON.
|
|
7892
|
+
// Riusa buildSerializedDashboardElements (salva) e loadDashboard (applica), gli
|
|
7893
|
+
// stessi choke-point di save/import. Nessuno schema change.
|
|
7894
|
+
static PRESETS_STORAGE_KEY = 'wuic.designer.presets';
|
|
7895
|
+
readDesignerPresets() {
|
|
7896
|
+
try {
|
|
7897
|
+
const raw = localStorage.getItem(DesignerComponent.PRESETS_STORAGE_KEY);
|
|
7898
|
+
const parsed = raw ? JSON.parse(raw) : {};
|
|
7899
|
+
return (parsed && typeof parsed === 'object') ? parsed : {};
|
|
7900
|
+
}
|
|
7901
|
+
catch {
|
|
7902
|
+
return {};
|
|
7903
|
+
}
|
|
7904
|
+
}
|
|
7905
|
+
writeDesignerPresets(presets) {
|
|
7906
|
+
try {
|
|
7907
|
+
localStorage.setItem(DesignerComponent.PRESETS_STORAGE_KEY, JSON.stringify(presets));
|
|
7908
|
+
}
|
|
7909
|
+
catch { /* quota / storage disabilitato */ }
|
|
7910
|
+
}
|
|
7911
|
+
/** Salva il layout corrente come preset nominato nella libreria locale. */
|
|
7912
|
+
async saveDashboardAsPreset() {
|
|
7913
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.preset.save_title', 'Salva come preset'), [{ name: 'presetName', caption: this.t('designer.preset.name_caption', 'Nome preset'), type: 'string', value: '', required: true }], null, '400px', '230px');
|
|
7914
|
+
const name = String(resp?.presetName?.value || '').trim();
|
|
7915
|
+
if (!name) {
|
|
7916
|
+
return;
|
|
7917
|
+
}
|
|
7918
|
+
const presets = this.readDesignerPresets();
|
|
7919
|
+
presets[name] = this.buildSerializedDashboardElements();
|
|
7920
|
+
this.writeDesignerPresets(presets);
|
|
7921
|
+
WtoolboxService.messageNotificationService.add({
|
|
7922
|
+
severity: 'success',
|
|
7923
|
+
summary: this.t('success', 'Success'),
|
|
7924
|
+
detail: this.t('designer.preset.saved', 'Preset salvato')
|
|
7925
|
+
});
|
|
7926
|
+
}
|
|
7927
|
+
/** Applica un preset della libreria al canvas (sostituisce il layout corrente). */
|
|
7928
|
+
async applyDashboardPreset() {
|
|
7929
|
+
const presets = this.readDesignerPresets();
|
|
7930
|
+
const names = Object.keys(presets);
|
|
7931
|
+
if (names.length === 0) {
|
|
7932
|
+
WtoolboxService.messageNotificationService.add({
|
|
7933
|
+
severity: 'info',
|
|
7934
|
+
summary: this.t('info', 'Info'),
|
|
7935
|
+
detail: this.t('designer.preset.none', 'Nessun preset salvato')
|
|
7936
|
+
});
|
|
7937
|
+
return;
|
|
7938
|
+
}
|
|
7939
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.preset.apply_title', 'Applica preset'), [{
|
|
7940
|
+
name: 'preset',
|
|
7941
|
+
caption: this.t('designer.preset.pick_caption', 'Preset'),
|
|
7942
|
+
type: 'dictionary',
|
|
7943
|
+
required: true,
|
|
7944
|
+
dictionaryData: names.map((n) => ({ label: n, value: n }))
|
|
7945
|
+
}], null, '400px', '230px');
|
|
7946
|
+
const chosen = String(resp?.preset?.value || '').trim();
|
|
7947
|
+
if (!chosen || !presets[chosen]) {
|
|
7948
|
+
return;
|
|
7949
|
+
}
|
|
7950
|
+
if ((this.flattenedDashboardElements || []).length > 0) {
|
|
7951
|
+
const ok = await WtoolboxService.confirm({
|
|
7952
|
+
header: this.t('warning', 'Warning'),
|
|
7953
|
+
message: this.t('designer.preset.apply_confirm', 'Applicare il preset sostituira\' il canvas corrente. Continuare?'),
|
|
7954
|
+
acceptLabel: this.t('confirm', 'Confirm'),
|
|
7955
|
+
rejectLabel: this.t('cancel', 'Cancel')
|
|
7956
|
+
});
|
|
7957
|
+
if (!ok) {
|
|
7958
|
+
return;
|
|
7959
|
+
}
|
|
7960
|
+
}
|
|
7961
|
+
try {
|
|
7962
|
+
await this.loadDashboard({
|
|
7963
|
+
boardcontent: presets[chosen],
|
|
7964
|
+
board_route: this.currentDashboardRoute || '',
|
|
7965
|
+
board_des: this.currentDashboardDescription || ''
|
|
7966
|
+
}, true);
|
|
7967
|
+
WtoolboxService.messageNotificationService.add({
|
|
7968
|
+
severity: 'success',
|
|
7969
|
+
summary: this.t('success', 'Success'),
|
|
7970
|
+
detail: this.t('designer.preset.applied', 'Preset applicato')
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
catch (error) {
|
|
7974
|
+
console.error('Failed to apply designer preset', error);
|
|
7975
|
+
WtoolboxService.errorHandler?.handleError?.(error);
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
/** Clipboard interno per copy/paste di elementi del designer (inject-node). */
|
|
7979
|
+
designerClipboard = null;
|
|
7980
|
+
/**
|
|
7981
|
+
* Converte un elemento del canvas nella shape `node` accettata da
|
|
7982
|
+
* {@link injectLayoutNode} (`{ tool_name, inputs }`), estraendo i soli input
|
|
7983
|
+
* SCALARI (string/number/boolean/array) + i binding datasource (ridotti al
|
|
7984
|
+
* loro `uniqueName`, che il path canonico ri-idrata post-drop). Function e
|
|
7985
|
+
* oggetti complessi vengono scartati: verranno ricostruiti dal template della
|
|
7986
|
+
* palette al drop. Ritorna null se l'elemento non e' copiabile.
|
|
7987
|
+
*/
|
|
7988
|
+
buildInjectNodeFromElement(el) {
|
|
7989
|
+
const name = String(el?.name || '').trim();
|
|
7990
|
+
if (!name) {
|
|
7991
|
+
return null;
|
|
7992
|
+
}
|
|
7993
|
+
const inputs = {};
|
|
7994
|
+
const rawInputs = el?.inputs || {};
|
|
7995
|
+
for (const key of Object.keys(rawInputs)) {
|
|
7996
|
+
let v = rawInputs[key];
|
|
7997
|
+
if (v instanceof BehaviorSubject) {
|
|
7998
|
+
v = v.value;
|
|
7999
|
+
}
|
|
8000
|
+
if (typeof v === 'function') {
|
|
8001
|
+
continue;
|
|
8002
|
+
}
|
|
8003
|
+
// Binding datasource (dropped-component): passa il solo uniqueName stringa
|
|
8004
|
+
if (v && typeof v === 'object' && !Array.isArray(v) && 'uniqueName' in v && 'component' in v) {
|
|
8005
|
+
const un = String(v.uniqueName || '').trim();
|
|
8006
|
+
if (un) {
|
|
8007
|
+
inputs[key] = un;
|
|
8008
|
+
}
|
|
8009
|
+
continue;
|
|
8010
|
+
}
|
|
8011
|
+
// Scarta gli altri oggetti complessi non serializzabili come scalari
|
|
8012
|
+
if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
8013
|
+
continue;
|
|
8014
|
+
}
|
|
8015
|
+
inputs[key] = v;
|
|
8016
|
+
}
|
|
8017
|
+
return { tool_name: name, inputs };
|
|
8018
|
+
}
|
|
8019
|
+
/** Trova l'uniqueName del parent di un elemento nel tree, o null se e' a root. */
|
|
8020
|
+
findParentUniqueName(target) {
|
|
8021
|
+
const targetUn = String(target?.uniqueName || '').trim();
|
|
8022
|
+
if (!targetUn) {
|
|
8023
|
+
return null;
|
|
8024
|
+
}
|
|
8025
|
+
const walk = (nodes) => {
|
|
8026
|
+
for (const n of nodes || []) {
|
|
8027
|
+
const kids = n?.nestedComponents || [];
|
|
8028
|
+
if (kids.some((k) => String(k?.uniqueName || '') === targetUn)) {
|
|
8029
|
+
return String(n?.uniqueName || '') || null;
|
|
8030
|
+
}
|
|
8031
|
+
const deeper = walk(kids);
|
|
8032
|
+
if (deeper !== undefined) {
|
|
8033
|
+
return deeper;
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
return undefined;
|
|
8037
|
+
};
|
|
8038
|
+
const res = walk(this.dashboardElements || []);
|
|
8039
|
+
return res ?? null;
|
|
8040
|
+
}
|
|
8041
|
+
/**
|
|
8042
|
+
* Copia l'elemento selezionato (o `ctxElement`) nel clipboard interno.
|
|
8043
|
+
* I contenitori con figli non sono ancora supportati (richiedono il mapping
|
|
8044
|
+
* dei placeholder strutturali): mostra un avviso e non copia.
|
|
8045
|
+
*/
|
|
8046
|
+
copyDesignerElement(el) {
|
|
8047
|
+
const target = el || this.tool || this.ctxElement;
|
|
8048
|
+
if (!target) {
|
|
8049
|
+
return;
|
|
8050
|
+
}
|
|
8051
|
+
if (Array.isArray(target.nestedComponents) && target.nestedComponents.length > 0) {
|
|
8052
|
+
WtoolboxService.messageNotificationService.add({
|
|
8053
|
+
severity: 'info',
|
|
8054
|
+
summary: this.t('info', 'Info'),
|
|
8055
|
+
detail: this.t('designer.clipboard.container_unsupported', 'La copia di contenitori con figli non e\' ancora supportata')
|
|
8056
|
+
});
|
|
8057
|
+
return;
|
|
8058
|
+
}
|
|
8059
|
+
// Gli elementi con binding datasource "live" + rendering per-riga
|
|
8060
|
+
// (DATAREPEATER/SELECT/MULTISELECT/UL) non si duplicano ancora in modo
|
|
8061
|
+
// affidabile: il clone via injectLayoutNode ricrea un list-grid il cui
|
|
8062
|
+
// selection model non e' inizializzato ad array -> IsSelectedRowPipe fallisce
|
|
8063
|
+
// (`items.find is not a function`). Fino a quando la re-idratazione del
|
|
8064
|
+
// binding non e' gestita, escludiamo questi tipi con un avviso chiaro.
|
|
8065
|
+
const DATASOURCE_BOUND = new Set(['DATAREPEATER', 'SELECT', 'MULTISELECT', 'UL']);
|
|
8066
|
+
if (DATASOURCE_BOUND.has(String(target.name || ''))) {
|
|
8067
|
+
WtoolboxService.messageNotificationService.add({
|
|
8068
|
+
severity: 'info',
|
|
8069
|
+
summary: this.t('info', 'Info'),
|
|
8070
|
+
detail: this.t('designer.clipboard.datasource_bound_unsupported', 'La duplicazione di componenti legati a un datasource non e\' ancora supportata')
|
|
8071
|
+
});
|
|
8072
|
+
return;
|
|
8073
|
+
}
|
|
8074
|
+
const node = this.buildInjectNodeFromElement(target);
|
|
8075
|
+
if (!node) {
|
|
8076
|
+
return;
|
|
8077
|
+
}
|
|
8078
|
+
this.designerClipboard = node;
|
|
8079
|
+
WtoolboxService.messageNotificationService.add({
|
|
8080
|
+
severity: 'success',
|
|
8081
|
+
summary: this.t('success', 'Success'),
|
|
8082
|
+
detail: this.t('designer.clipboard.copied', 'Elemento copiato')
|
|
8083
|
+
});
|
|
8084
|
+
}
|
|
8085
|
+
/**
|
|
8086
|
+
* Incolla l'elemento dal clipboard. Se `parentUniqueName` e' passato (usato da
|
|
8087
|
+
* `duplicateDesignerElement` per creare un sibling) inietta li'; altrimenti
|
|
8088
|
+
* dentro l'elemento selezionato se e' un contenitore, altrimenti a root.
|
|
8089
|
+
* Riusa il path canonico {@link injectLayoutNode} (drop() reale) per garantire
|
|
8090
|
+
* uniqueName freschi, hydration e history coerenti.
|
|
8091
|
+
*/
|
|
8092
|
+
async pasteDesignerElement(parentUniqueName) {
|
|
8093
|
+
if (!this.designerClipboard) {
|
|
8094
|
+
return;
|
|
8095
|
+
}
|
|
8096
|
+
let parent = parentUniqueName ?? null;
|
|
8097
|
+
if (parent === null && this.tool && Array.isArray(this.tool.nestedComponents)) {
|
|
8098
|
+
parent = String(this.tool.uniqueName || '') || null;
|
|
8099
|
+
}
|
|
8100
|
+
try {
|
|
8101
|
+
await this.injectLayoutNode(JSON.parse(JSON.stringify(this.designerClipboard)), parent);
|
|
8102
|
+
this.cd.detectChanges();
|
|
8103
|
+
WtoolboxService.messageNotificationService.add({
|
|
8104
|
+
severity: 'success',
|
|
8105
|
+
summary: this.t('success', 'Success'),
|
|
8106
|
+
detail: this.t('designer.clipboard.pasted', 'Elemento incollato')
|
|
8107
|
+
});
|
|
8108
|
+
}
|
|
8109
|
+
catch (error) {
|
|
8110
|
+
console.error('Failed to paste designer element', error);
|
|
8111
|
+
WtoolboxService.errorHandler?.handleError?.(error);
|
|
8112
|
+
}
|
|
8113
|
+
}
|
|
8114
|
+
/**
|
|
8115
|
+
* Duplica un elemento: lo copia e lo incolla come sibling (stesso parent).
|
|
8116
|
+
*/
|
|
8117
|
+
async duplicateDesignerElement(el) {
|
|
8118
|
+
const target = el || this.tool || this.ctxElement;
|
|
8119
|
+
if (!target) {
|
|
8120
|
+
return;
|
|
8121
|
+
}
|
|
8122
|
+
this.copyDesignerElement(target);
|
|
8123
|
+
if (!this.designerClipboard) {
|
|
8124
|
+
// caso contenitore: copyDesignerElement ha gia' avvisato
|
|
8125
|
+
return;
|
|
8126
|
+
}
|
|
8127
|
+
const parentUn = this.findParentUniqueName(target);
|
|
8128
|
+
await this.pasteDesignerElement(parentUn);
|
|
8129
|
+
}
|
|
8130
|
+
/**
|
|
8131
|
+
* Esporta il boardcontent della dashboard corrente come file JSON scaricabile.
|
|
8132
|
+
* Riusa lo stesso choke-point di serializzazione del salvataggio
|
|
8133
|
+
* (`buildSerializedDashboardElements`), quindi il file prodotto e' identico al
|
|
8134
|
+
* `boardcontent` persistito su `dom_board` ed e' re-importabile qui o su un
|
|
8135
|
+
* altro ambiente (import/export tra ambienti).
|
|
8136
|
+
*/
|
|
8137
|
+
exportDashboardJson() {
|
|
8138
|
+
try {
|
|
8139
|
+
const raw = this.buildSerializedDashboardElements();
|
|
8140
|
+
let pretty = raw;
|
|
8141
|
+
try {
|
|
8142
|
+
pretty = JSON.stringify(JSON.parse(raw), null, 2);
|
|
8143
|
+
}
|
|
8144
|
+
catch { /* keep raw */ }
|
|
8145
|
+
const routeSlug = String(this.currentDashboardRoute || 'dashboard')
|
|
8146
|
+
.replaceAll(/[^a-zA-Z0-9._-]+/g, '_')
|
|
8147
|
+
.replaceAll(/^_+|_+$/g, '') || 'dashboard';
|
|
8148
|
+
const fileName = `${routeSlug}.dashboard.json`;
|
|
8149
|
+
const blob = new Blob([pretty], { type: 'application/json' });
|
|
8150
|
+
const url = URL.createObjectURL(blob);
|
|
8151
|
+
const anchor = document.createElement('a');
|
|
8152
|
+
anchor.href = url;
|
|
8153
|
+
anchor.download = fileName;
|
|
8154
|
+
document.body.appendChild(anchor);
|
|
8155
|
+
anchor.click();
|
|
8156
|
+
anchor.remove();
|
|
8157
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
8158
|
+
WtoolboxService.messageNotificationService.add({
|
|
8159
|
+
severity: 'success',
|
|
8160
|
+
summary: this.t('success', 'Success'),
|
|
8161
|
+
detail: this.t('designer.export_json_success', 'Dashboard JSON exported')
|
|
8162
|
+
});
|
|
8163
|
+
}
|
|
8164
|
+
catch (error) {
|
|
8165
|
+
console.error('Failed to export dashboard JSON', error);
|
|
8166
|
+
WtoolboxService.errorHandler?.handleError?.(error);
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
8169
|
+
/**
|
|
8170
|
+
* Importa un boardcontent da un file JSON precedentemente esportato.
|
|
8171
|
+
* Apre un file picker nativo (input[type=file] creato al volo, nessun prompt
|
|
8172
|
+
* nativo del browser), valida il JSON, chiede conferma di sovrascrittura del
|
|
8173
|
+
* canvas corrente e infine ricostruisce la dashboard riusando `loadDashboard`
|
|
8174
|
+
* (con `skipServerFetch=true`), lo stesso path usato per aprire una dashboard
|
|
8175
|
+
* salvata — cosi' l'import passa da idratazione/backfill/rebind canonici.
|
|
8176
|
+
*/
|
|
8177
|
+
importDashboardJson() {
|
|
8178
|
+
const input = document.createElement('input');
|
|
8179
|
+
input.type = 'file';
|
|
8180
|
+
input.accept = 'application/json,.json';
|
|
8181
|
+
input.style.display = 'none';
|
|
8182
|
+
input.addEventListener('change', () => {
|
|
8183
|
+
const file = input.files?.[0];
|
|
8184
|
+
input.remove();
|
|
8185
|
+
if (!file) {
|
|
8186
|
+
return;
|
|
8187
|
+
}
|
|
8188
|
+
const reader = new FileReader();
|
|
8189
|
+
reader.onload = () => {
|
|
8190
|
+
const text = String(reader.result || '');
|
|
8191
|
+
void this.applyImportedDashboardJson(text);
|
|
8192
|
+
};
|
|
8193
|
+
reader.onerror = () => {
|
|
8194
|
+
WtoolboxService.messageNotificationService.add({
|
|
8195
|
+
severity: 'error',
|
|
8196
|
+
summary: this.t('error', 'Error'),
|
|
8197
|
+
detail: this.t('designer.import_json_read_error', 'Could not read the selected file')
|
|
8198
|
+
});
|
|
8199
|
+
};
|
|
8200
|
+
reader.readAsText(file);
|
|
8201
|
+
});
|
|
8202
|
+
document.body.appendChild(input);
|
|
8203
|
+
input.click();
|
|
8204
|
+
}
|
|
8205
|
+
/**
|
|
8206
|
+
* Valida e applica un boardcontent importato. Estratta da `importDashboardJson`
|
|
8207
|
+
* per essere riusabile (es. incolla da clipboard / test) e testabile.
|
|
8208
|
+
*/
|
|
8209
|
+
async applyImportedDashboardJson(text) {
|
|
8210
|
+
let parsed;
|
|
8211
|
+
try {
|
|
8212
|
+
parsed = JSON.parse(text);
|
|
8213
|
+
}
|
|
8214
|
+
catch {
|
|
8215
|
+
WtoolboxService.messageNotificationService.add({
|
|
8216
|
+
severity: 'error',
|
|
8217
|
+
summary: this.t('error', 'Error'),
|
|
8218
|
+
detail: this.t('designer.import_json_invalid', 'The file does not contain valid JSON')
|
|
8219
|
+
});
|
|
8220
|
+
return;
|
|
8221
|
+
}
|
|
8222
|
+
// Accetta sia l'array di elementi top-level sia il wrapper {elements|dashboardElements}
|
|
8223
|
+
const dashboardElements = Array.isArray(parsed?.dashboardElements) ? parsed.dashboardElements : null;
|
|
8224
|
+
const wrappedElements = Array.isArray(parsed?.elements) ? parsed.elements : dashboardElements;
|
|
8225
|
+
const elements = Array.isArray(parsed) ? parsed : wrappedElements;
|
|
8226
|
+
if (!elements) {
|
|
8227
|
+
WtoolboxService.messageNotificationService.add({
|
|
8228
|
+
severity: 'error',
|
|
8229
|
+
summary: this.t('error', 'Error'),
|
|
8230
|
+
detail: this.t('designer.import_json_unrecognized', 'The JSON is not a recognizable dashboard layout')
|
|
8231
|
+
});
|
|
8232
|
+
return;
|
|
8233
|
+
}
|
|
8234
|
+
if ((this.flattenedDashboardElements || []).length > 0) {
|
|
8235
|
+
const ok = await WtoolboxService.confirm({
|
|
8236
|
+
header: this.t('warning', 'Warning'),
|
|
8237
|
+
message: this.t('designer.import_json_confirm', 'Importing will replace the current canvas. Continue?'),
|
|
8238
|
+
acceptLabel: this.t('confirm', 'Confirm'),
|
|
8239
|
+
rejectLabel: this.t('cancel', 'Cancel')
|
|
8240
|
+
});
|
|
8241
|
+
if (!ok) {
|
|
8242
|
+
return;
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
try {
|
|
8246
|
+
await this.loadDashboard({
|
|
8247
|
+
boardcontent: JSON.stringify(elements),
|
|
8248
|
+
board_route: this.currentDashboardRoute || '',
|
|
8249
|
+
board_des: this.currentDashboardDescription || ''
|
|
8250
|
+
}, true);
|
|
8251
|
+
WtoolboxService.messageNotificationService.add({
|
|
8252
|
+
severity: 'success',
|
|
8253
|
+
summary: this.t('success', 'Success'),
|
|
8254
|
+
detail: this.t('designer.import_json_success', 'Dashboard JSON imported')
|
|
8255
|
+
});
|
|
8256
|
+
}
|
|
8257
|
+
catch (error) {
|
|
8258
|
+
console.error('Failed to import dashboard JSON', error);
|
|
8259
|
+
WtoolboxService.errorHandler?.handleError?.(error);
|
|
8260
|
+
}
|
|
8261
|
+
}
|
|
8262
|
+
get droppedDashboardItems() {
|
|
8263
|
+
return (this.flattenedDashboardElements || []).filter((item) => !!item?.uniqueName);
|
|
8264
|
+
}
|
|
8265
|
+
get hierarchyTreeNodes() {
|
|
7717
8266
|
return this.hierarchyTreeNodesState;
|
|
7718
8267
|
}
|
|
7719
8268
|
get canUndo() {
|
|
@@ -7747,11 +8296,561 @@ class DesignerComponent {
|
|
|
7747
8296
|
this.tool = item;
|
|
7748
8297
|
this.syncHierarchySelectionToCurrentTool();
|
|
7749
8298
|
}
|
|
8299
|
+
// ===================== Multi-selezione (rubber-band) =====================
|
|
8300
|
+
/** Canvas host (per le coordinate del rettangolo di selezione). */
|
|
8301
|
+
canvasRef;
|
|
8302
|
+
/** Elementi top-level multi-selezionati. `tool` (attivo nel property panel) e' null quando != 1. */
|
|
8303
|
+
selectedTools = [];
|
|
8304
|
+
/** Overlay del rettangolo di selezione (rubber-band). */
|
|
8305
|
+
rubberBandVisible = false;
|
|
8306
|
+
rubberBandStyle = { left: '0px', top: '0px', width: '0px', height: '0px' };
|
|
8307
|
+
rubber = null;
|
|
8308
|
+
canvasRectAtStart = null;
|
|
8309
|
+
boundRubberMove = (e) => this.onRubberMove(e);
|
|
8310
|
+
boundRubberUp = (e) => this.onRubberUp(e);
|
|
8311
|
+
/**
|
|
8312
|
+
* Mousedown sul canvas: avvia la selezione a rettangolo SOLO se parte da area
|
|
8313
|
+
* vuota (nessun elemento sotto il puntatore). Sugli elementi il mousedown e'
|
|
8314
|
+
* consumato dalla direttiva di drag (`stopImmediatePropagation`), quindi qui
|
|
8315
|
+
* arriva solo il click su spazio libero — coerente col vincolo richiesto.
|
|
8316
|
+
*/
|
|
8317
|
+
onCanvasMouseDown(event) {
|
|
8318
|
+
if (this.noEdit || !event || event.button !== 0) {
|
|
8319
|
+
return;
|
|
8320
|
+
}
|
|
8321
|
+
const canvasEl = this.canvasRef?.nativeElement;
|
|
8322
|
+
if (!canvasEl || !this.isEmptyCanvasTarget(event.target, canvasEl)) {
|
|
8323
|
+
return;
|
|
8324
|
+
}
|
|
8325
|
+
event.preventDefault();
|
|
8326
|
+
this.clearMultiSelection();
|
|
8327
|
+
this.tool = null;
|
|
8328
|
+
this.canvasRectAtStart = canvasEl.getBoundingClientRect();
|
|
8329
|
+
this.rubber = { startClientX: event.clientX, startClientY: event.clientY };
|
|
8330
|
+
this.rubberBandVisible = true;
|
|
8331
|
+
this.updateRubberBandStyle(event.clientX, event.clientY);
|
|
8332
|
+
globalThis.addEventListener('mousemove', this.boundRubberMove);
|
|
8333
|
+
globalThis.addEventListener('mouseup', this.boundRubberUp);
|
|
8334
|
+
this.cd.detectChanges();
|
|
8335
|
+
}
|
|
8336
|
+
/** true se il target risale fino al canvas senza attraversare un elemento designer (id con '__'). */
|
|
8337
|
+
isEmptyCanvasTarget(target, canvasEl) {
|
|
8338
|
+
let node = target;
|
|
8339
|
+
while (node && node !== canvasEl) {
|
|
8340
|
+
const id = node.getAttribute ? (node.getAttribute('id') || '') : '';
|
|
8341
|
+
if (id.includes('__')) {
|
|
8342
|
+
return false;
|
|
8343
|
+
}
|
|
8344
|
+
node = node.parentElement;
|
|
8345
|
+
}
|
|
8346
|
+
return node === canvasEl;
|
|
8347
|
+
}
|
|
8348
|
+
onRubberMove(event) {
|
|
8349
|
+
if (!this.rubber) {
|
|
8350
|
+
return;
|
|
8351
|
+
}
|
|
8352
|
+
this.updateRubberBandStyle(event.clientX, event.clientY);
|
|
8353
|
+
this.cd.detectChanges();
|
|
8354
|
+
}
|
|
8355
|
+
updateRubberBandStyle(curX, curY) {
|
|
8356
|
+
if (!this.rubber || !this.canvasRectAtStart) {
|
|
8357
|
+
return;
|
|
8358
|
+
}
|
|
8359
|
+
const canvasEl = this.canvasRef?.nativeElement;
|
|
8360
|
+
const r = this.canvasRectAtStart;
|
|
8361
|
+
const sL = canvasEl ? canvasEl.scrollLeft : 0;
|
|
8362
|
+
const sT = canvasEl ? canvasEl.scrollTop : 0;
|
|
8363
|
+
const x1 = Math.min(this.rubber.startClientX, curX), x2 = Math.max(this.rubber.startClientX, curX);
|
|
8364
|
+
const y1 = Math.min(this.rubber.startClientY, curY), y2 = Math.max(this.rubber.startClientY, curY);
|
|
8365
|
+
this.rubberBandStyle = {
|
|
8366
|
+
left: (x1 - r.left + sL) + 'px',
|
|
8367
|
+
top: (y1 - r.top + sT) + 'px',
|
|
8368
|
+
width: (x2 - x1) + 'px',
|
|
8369
|
+
height: (y2 - y1) + 'px',
|
|
8370
|
+
};
|
|
8371
|
+
}
|
|
8372
|
+
onRubberUp(event) {
|
|
8373
|
+
globalThis.removeEventListener('mousemove', this.boundRubberMove);
|
|
8374
|
+
globalThis.removeEventListener('mouseup', this.boundRubberUp);
|
|
8375
|
+
const rubber = this.rubber;
|
|
8376
|
+
this.rubber = null;
|
|
8377
|
+
this.rubberBandVisible = false;
|
|
8378
|
+
if (!rubber) {
|
|
8379
|
+
this.cd.detectChanges();
|
|
8380
|
+
return;
|
|
8381
|
+
}
|
|
8382
|
+
const x1 = Math.min(rubber.startClientX, event.clientX), x2 = Math.max(rubber.startClientX, event.clientX);
|
|
8383
|
+
const y1 = Math.min(rubber.startClientY, event.clientY), y2 = Math.max(rubber.startClientY, event.clientY);
|
|
8384
|
+
const selected = [];
|
|
8385
|
+
// Ignora i "click" (rettangolo minuscolo): niente selezione, solo deselect.
|
|
8386
|
+
if (Math.abs(x2 - x1) >= 3 && Math.abs(y2 - y1) >= 3) {
|
|
8387
|
+
// Hit-test dei soli elementi TOP-LEVEL (spostare/eliminare un top-level porta i figli).
|
|
8388
|
+
for (const el of (this.dashboardElements || [])) {
|
|
8389
|
+
const dom = el?.uniqueName ? document.getElementById(el.uniqueName) : null;
|
|
8390
|
+
if (!dom) {
|
|
8391
|
+
continue;
|
|
8392
|
+
}
|
|
8393
|
+
const b = dom.getBoundingClientRect();
|
|
8394
|
+
if (b.left < x2 && b.right > x1 && b.top < y2 && b.bottom > y1) {
|
|
8395
|
+
selected.push(el);
|
|
8396
|
+
}
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
this.setMultiSelection(selected);
|
|
8400
|
+
this.cd.detectChanges();
|
|
8401
|
+
}
|
|
8402
|
+
/** Imposta la multi-selezione: `tool` resta valorizzato solo con esattamente 1 elemento. */
|
|
8403
|
+
setMultiSelection(selected) {
|
|
8404
|
+
this.clearHighlight();
|
|
8405
|
+
this.selectedTools = selected || [];
|
|
8406
|
+
this.tool = this.selectedTools.length === 1 ? this.selectedTools[0] : null;
|
|
8407
|
+
this.applyHighlight();
|
|
8408
|
+
}
|
|
8409
|
+
clearMultiSelection() {
|
|
8410
|
+
this.clearHighlight();
|
|
8411
|
+
this.selectedTools = [];
|
|
8412
|
+
}
|
|
8413
|
+
applyHighlight() {
|
|
8414
|
+
for (const el of (this.selectedTools || [])) {
|
|
8415
|
+
const dom = el?.uniqueName ? document.getElementById(el.uniqueName) : null;
|
|
8416
|
+
if (dom) {
|
|
8417
|
+
dom.classList.add('designer-multi-selected');
|
|
8418
|
+
}
|
|
8419
|
+
}
|
|
8420
|
+
}
|
|
8421
|
+
clearHighlight() {
|
|
8422
|
+
for (const el of (this.selectedTools || [])) {
|
|
8423
|
+
const dom = el?.uniqueName ? document.getElementById(el.uniqueName) : null;
|
|
8424
|
+
if (dom) {
|
|
8425
|
+
dom.classList.remove('designer-multi-selected');
|
|
8426
|
+
}
|
|
8427
|
+
}
|
|
8428
|
+
}
|
|
8429
|
+
/** Applica un delta di traslazione (px) a un insieme di elementi (multi-move via frecce). */
|
|
8430
|
+
nudgeElements(tools, dx, dy) {
|
|
8431
|
+
for (const el of (tools || [])) {
|
|
8432
|
+
if (!el?.inputs) {
|
|
8433
|
+
continue;
|
|
8434
|
+
}
|
|
8435
|
+
let scaleX = 1, skewY = 0, skewX = 0, scaleY = 1, tx = 0, ty = 0;
|
|
8436
|
+
const m = String(el.inputs['transform'] || '').match(/matrix\(([^)]+)\)/);
|
|
8437
|
+
if (m) {
|
|
8438
|
+
const p = m[1].split(',').map((x) => Number.parseFloat(x));
|
|
8439
|
+
if (p.length >= 6 && p.every((n) => !Number.isNaN(Number(n)))) {
|
|
8440
|
+
[scaleX, skewY, skewX, scaleY, tx, ty] = p;
|
|
8441
|
+
}
|
|
8442
|
+
}
|
|
8443
|
+
el.inputs['transform'] = `matrix(${scaleX}, ${skewY}, ${skewX}, ${scaleY}, ${tx + dx}, ${ty + dy})`;
|
|
8444
|
+
}
|
|
8445
|
+
this.commitHistoryIfChanged();
|
|
8446
|
+
this.cd.detectChanges();
|
|
8447
|
+
}
|
|
8448
|
+
// ============ Import dashboard/preset DENTRO un elemento (context menu) ============
|
|
8449
|
+
/** true se il tool e' un container che accetta figli arbitrari (nestedComponents + nessun allowedChildren). */
|
|
8450
|
+
isFreeContainer(tool) {
|
|
8451
|
+
return !!tool && Array.isArray(tool.nestedComponents) && !(tool.allowedChildren);
|
|
8452
|
+
}
|
|
8453
|
+
/** Voci di context menu per importare dashboard/preset dentro un container. */
|
|
8454
|
+
buildImportContextItems(el) {
|
|
8455
|
+
return [
|
|
8456
|
+
{ separator: true },
|
|
8457
|
+
{ label: this.t('designer.ctx_import_dashboard', 'Importa dashboard qui'), icon: 'pi pi-download', command: () => this.importDashboardIntoElement(el) },
|
|
8458
|
+
{ label: this.t('designer.ctx_import_preset', 'Importa preset qui'), icon: 'pi pi-bookmark', command: () => this.importPresetIntoElement(el) },
|
|
8459
|
+
];
|
|
8460
|
+
}
|
|
8461
|
+
/**
|
|
8462
|
+
* UNICA sorgente del context menu di un elemento, condivisa dai tre punti di
|
|
8463
|
+
* accesso (canvas `showCtx`, gerarchia `showHierarchyNodeContextMenu`, footer
|
|
8464
|
+
* breadcrumb `showFooterContextMenu`). Le `command` operano sull'elemento
|
|
8465
|
+
* passato (risolto al click), non su una variabile globale, quindi il menu si
|
|
8466
|
+
* ricostruisce per-elemento con voci identiche ovunque. Il caller canvas
|
|
8467
|
+
* antepone eventuali voci field-editor (specifiche del click su un campo).
|
|
8468
|
+
*/
|
|
8469
|
+
buildElementContextMenuItems(el) {
|
|
8470
|
+
const items = [
|
|
8471
|
+
{
|
|
8472
|
+
label: this.t('designer.edit_properties', 'Edit properties'),
|
|
8473
|
+
icon: 'pi pi-cog',
|
|
8474
|
+
command: () => { if (el) {
|
|
8475
|
+
this.selectDashboardItem(el);
|
|
8476
|
+
this.cd.detectChanges();
|
|
8477
|
+
} }
|
|
8478
|
+
},
|
|
8479
|
+
{
|
|
8480
|
+
label: this.t('designer.duplicate', 'Duplica'),
|
|
8481
|
+
icon: 'pi pi-clone',
|
|
8482
|
+
command: () => { if (el) {
|
|
8483
|
+
void this.duplicateDesignerElement(el);
|
|
8484
|
+
} }
|
|
8485
|
+
},
|
|
8486
|
+
{
|
|
8487
|
+
label: this.t('designer.rename', 'Rename'),
|
|
8488
|
+
icon: 'pi pi-pencil',
|
|
8489
|
+
command: () => { if (el) {
|
|
8490
|
+
void this.renameDesignerElement(el);
|
|
8491
|
+
} }
|
|
8492
|
+
},
|
|
8493
|
+
{
|
|
8494
|
+
label: this.t('designer.remove', 'Remove'),
|
|
8495
|
+
icon: 'pi pi-trash',
|
|
8496
|
+
command: () => { const n = String(el?.uniqueName || '').trim(); if (n) {
|
|
8497
|
+
this.removeElementByName(n);
|
|
8498
|
+
} }
|
|
8499
|
+
}
|
|
8500
|
+
];
|
|
8501
|
+
// Sposta in nuovo Tab: SOLO se l'elemento e' dentro un TABPANEL di un TABVIEW
|
|
8502
|
+
// (migrazione elemento -> nuovo TABPANEL del TABVIEW padre).
|
|
8503
|
+
if (this.isInsideTabPanel(el)) {
|
|
8504
|
+
items.push({
|
|
8505
|
+
label: this.t('designer.ctx_move_to_new_tab', 'Sposta in nuovo Tab'),
|
|
8506
|
+
icon: 'pi pi-folder-open',
|
|
8507
|
+
command: () => { void this.moveElementToNewTab(el); }
|
|
8508
|
+
});
|
|
8509
|
+
// Sposta in altro tab: solo se il TABVIEW ha piu' di un tab (scelta esclusa il corrente).
|
|
8510
|
+
if (this.hasOtherTabs(el)) {
|
|
8511
|
+
items.push({
|
|
8512
|
+
label: this.t('designer.ctx_move_to_existing_tab_element', 'Sposta in altro tab'),
|
|
8513
|
+
icon: 'pi pi-arrow-right-arrow-left',
|
|
8514
|
+
command: () => { void this.moveElementToExistingTab(el); }
|
|
8515
|
+
});
|
|
8516
|
+
}
|
|
8517
|
+
}
|
|
8518
|
+
// Import dashboard/preset: solo per container liberi (nestedComponents senza allowedChildren).
|
|
8519
|
+
if (this.isFreeContainer(el)) {
|
|
8520
|
+
items.push(...this.buildImportContextItems(el));
|
|
8521
|
+
}
|
|
8522
|
+
return items;
|
|
8523
|
+
}
|
|
8524
|
+
/**
|
|
8525
|
+
* Risolve il contesto tab di un elemento: il TABPANEL che lo contiene e il
|
|
8526
|
+
* TABVIEW che contiene quel pannello. null se l'elemento non e' dentro un tab.
|
|
8527
|
+
*/
|
|
8528
|
+
resolveTabContext(el) {
|
|
8529
|
+
if (!el) {
|
|
8530
|
+
return null;
|
|
8531
|
+
}
|
|
8532
|
+
const panelUn = this.findParentUniqueName(el);
|
|
8533
|
+
const panel = panelUn ? this.getElementByName(panelUn) : null;
|
|
8534
|
+
if (!panel || String(panel.name || '').toUpperCase() !== 'TABPANEL') {
|
|
8535
|
+
return null;
|
|
8536
|
+
}
|
|
8537
|
+
const tabViewUn = this.findParentUniqueName(panel);
|
|
8538
|
+
const tabView = tabViewUn ? this.getElementByName(tabViewUn) : null;
|
|
8539
|
+
if (!tabView || String(tabView.name || '').toUpperCase() !== 'TABVIEW') {
|
|
8540
|
+
return null;
|
|
8541
|
+
}
|
|
8542
|
+
return { panel, tabView };
|
|
8543
|
+
}
|
|
8544
|
+
/** true se l'elemento e' dentro un TABPANEL di un TABVIEW (eleggibile a "Sposta in nuovo Tab"). */
|
|
8545
|
+
isInsideTabPanel(el) {
|
|
8546
|
+
return !!this.resolveTabContext(el);
|
|
8547
|
+
}
|
|
8548
|
+
/** true se il TABVIEW che contiene l'elemento ha piu' di un tab (eleggibile a "Sposta in altro tab"). */
|
|
8549
|
+
hasOtherTabs(el) {
|
|
8550
|
+
const ctx = this.resolveTabContext(el);
|
|
8551
|
+
return !!ctx && (ctx.tabView.nestedComponents?.length || 0) > 1;
|
|
8552
|
+
}
|
|
8553
|
+
/**
|
|
8554
|
+
* Sposta l'elemento in un NUOVO tab (TABPANEL) del TABVIEW che lo contiene:
|
|
8555
|
+
* crea un TABPANEL fresco (clonando il template di palette, uniqueName/componentId
|
|
8556
|
+
* nuovi), rimuove l'elemento dal tab corrente e ci mette lo STESSO oggetto (move,
|
|
8557
|
+
* non clone -> binding datasource/stato preservati), poi seleziona il nuovo tab.
|
|
8558
|
+
*/
|
|
8559
|
+
async moveElementToNewTab(el) {
|
|
8560
|
+
if (!el) {
|
|
8561
|
+
return;
|
|
8562
|
+
}
|
|
8563
|
+
const ctx = this.resolveTabContext(el);
|
|
8564
|
+
if (!ctx) {
|
|
8565
|
+
return;
|
|
8566
|
+
}
|
|
8567
|
+
const { panel, tabView } = ctx;
|
|
8568
|
+
// Nome del nuovo tab (default: displayName/name dell'elemento).
|
|
8569
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.ctx_move_to_new_tab', 'Sposta in nuovo Tab'), [{
|
|
8570
|
+
name: 'tabName',
|
|
8571
|
+
caption: this.t('designer.tab_name_caption', 'Nome Tab'),
|
|
8572
|
+
type: 'text',
|
|
8573
|
+
required: true,
|
|
8574
|
+
value: String(el.displayName || el.name || 'Tab').trim()
|
|
8575
|
+
}], '440px', '320px');
|
|
8576
|
+
if (!resp) {
|
|
8577
|
+
return;
|
|
8578
|
+
}
|
|
8579
|
+
const tabName = String(resp?.tabName?.value || '').trim();
|
|
8580
|
+
if (!tabName) {
|
|
8581
|
+
return;
|
|
8582
|
+
}
|
|
8583
|
+
// Nuovo TABPANEL dalla stessa shape del drop (template + id freschi).
|
|
8584
|
+
const template = (this.availableTools || []).find((t) => String(t?.name || '').toUpperCase() === 'TABPANEL');
|
|
8585
|
+
if (!template) {
|
|
8586
|
+
return;
|
|
8587
|
+
}
|
|
8588
|
+
const newPanel = JSON.parse(JSON.stringify(template));
|
|
8589
|
+
newPanel.componentId = this.getNewID();
|
|
8590
|
+
newPanel.uniqueName = newPanel.name + '__' + newPanel.componentId;
|
|
8591
|
+
newPanel.inputs = newPanel.inputs || {};
|
|
8592
|
+
newPanel.inputs['header'] = tabName;
|
|
8593
|
+
newPanel.nestedComponents = [];
|
|
8594
|
+
// Move: rimuovi dall'attuale tab e inserisci lo stesso oggetto nel nuovo.
|
|
8595
|
+
panel.nestedComponents = (panel.nestedComponents || []).filter((k) => String(k?.uniqueName || '') !== String(el.uniqueName || ''));
|
|
8596
|
+
newPanel.nestedComponents.push(el);
|
|
8597
|
+
// Aggancia il nuovo tab al TABVIEW e selezionalo (deseleziona gli altri).
|
|
8598
|
+
tabView.nestedComponents = tabView.nestedComponents || [];
|
|
8599
|
+
tabView.nestedComponents.push(newPanel);
|
|
8600
|
+
this.selectTabPanelExclusive(newPanel);
|
|
8601
|
+
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
8602
|
+
this.commitHistoryIfChanged();
|
|
8603
|
+
this.cd.detectChanges();
|
|
8604
|
+
WtoolboxService.messageNotificationService.add({
|
|
8605
|
+
severity: 'success',
|
|
8606
|
+
summary: this.t('success', 'Success'),
|
|
8607
|
+
detail: this.t('designer.moved_to_new_tab', 'Elemento spostato nel nuovo tab')
|
|
8608
|
+
});
|
|
8609
|
+
}
|
|
8610
|
+
/**
|
|
8611
|
+
* Sposta l'elemento in un ALTRO tab (TABPANEL) gia' esistente dello stesso
|
|
8612
|
+
* TABVIEW: picker dei tab escluso il corrente, poi move dello STESSO oggetto
|
|
8613
|
+
* (binding/stato preservati) nel tab scelto, che viene selezionato.
|
|
8614
|
+
*/
|
|
8615
|
+
async moveElementToExistingTab(el) {
|
|
8616
|
+
if (!el) {
|
|
8617
|
+
return;
|
|
8618
|
+
}
|
|
8619
|
+
const ctx = this.resolveTabContext(el);
|
|
8620
|
+
if (!ctx) {
|
|
8621
|
+
return;
|
|
8622
|
+
}
|
|
8623
|
+
const { panel, tabView } = ctx;
|
|
8624
|
+
// Tab candidati: tutti i TABPANEL del TABVIEW tranne quello corrente.
|
|
8625
|
+
const others = (tabView.nestedComponents || []).filter((p) => String(p?.uniqueName || '') !== String(panel.uniqueName || ''));
|
|
8626
|
+
if (!others.length) {
|
|
8627
|
+
return;
|
|
8628
|
+
}
|
|
8629
|
+
const choices = others.map((p, idx) => {
|
|
8630
|
+
const label = String(p?.inputs?.header || p?.displayName || p?.uniqueName || '').trim() || ('Tab ' + (idx + 1));
|
|
8631
|
+
return { label, value: String(p?.uniqueName || '') };
|
|
8632
|
+
});
|
|
8633
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.ctx_move_to_existing_tab_element', 'Sposta in altro tab'), [{
|
|
8634
|
+
name: 'targetTab',
|
|
8635
|
+
caption: this.t('designer.target_tab_caption', 'Tab di destinazione'),
|
|
8636
|
+
type: 'dictionary',
|
|
8637
|
+
required: true,
|
|
8638
|
+
value: choices[0].value,
|
|
8639
|
+
dictionaryData: choices
|
|
8640
|
+
}], '440px', '320px');
|
|
8641
|
+
if (!resp) {
|
|
8642
|
+
return;
|
|
8643
|
+
}
|
|
8644
|
+
const targetUn = String(resp?.targetTab?.value || '').trim();
|
|
8645
|
+
if (!targetUn) {
|
|
8646
|
+
return;
|
|
8647
|
+
}
|
|
8648
|
+
const targetPanel = (tabView.nestedComponents || []).find((p) => String(p?.uniqueName || '') === targetUn);
|
|
8649
|
+
if (!targetPanel) {
|
|
8650
|
+
return;
|
|
8651
|
+
}
|
|
8652
|
+
// Move: rimuovi dal tab corrente e inserisci lo stesso oggetto nel target.
|
|
8653
|
+
panel.nestedComponents = (panel.nestedComponents || []).filter((k) => String(k?.uniqueName || '') !== String(el.uniqueName || ''));
|
|
8654
|
+
targetPanel.nestedComponents = Array.isArray(targetPanel.nestedComponents) ? targetPanel.nestedComponents : [];
|
|
8655
|
+
targetPanel.nestedComponents.push(el);
|
|
8656
|
+
// Seleziona il tab di destinazione.
|
|
8657
|
+
this.selectTabPanelExclusive(targetPanel);
|
|
8658
|
+
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
8659
|
+
this.commitHistoryIfChanged();
|
|
8660
|
+
this.cd.detectChanges();
|
|
8661
|
+
WtoolboxService.messageNotificationService.add({
|
|
8662
|
+
severity: 'success',
|
|
8663
|
+
summary: this.t('success', 'Success'),
|
|
8664
|
+
detail: this.t('designer.moved_to_tab', 'Elemento spostato nel tab')
|
|
8665
|
+
});
|
|
8666
|
+
}
|
|
8667
|
+
/**
|
|
8668
|
+
* onDrop del TABPANEL: appena droppato dentro un TABVIEW lo rende il tab attivo,
|
|
8669
|
+
* cosi' la sua area diventa visibile e droppabile. Il pannello a questo punto e'
|
|
8670
|
+
* gia' stato inserito in `tabView.nestedComponents` dal drop() (push prima di onDrop).
|
|
8671
|
+
*/
|
|
8672
|
+
dropTabPanel(currentTool) {
|
|
8673
|
+
this.selectTabPanelExclusive(currentTool);
|
|
8674
|
+
this.cd.detectChanges();
|
|
8675
|
+
}
|
|
8676
|
+
/**
|
|
8677
|
+
* Seleziona in modo esclusivo un TABPANEL nel TABVIEW che lo contiene
|
|
8678
|
+
* (`inputs.selected = true` sul pannello, `false` sugli altri). No-op se il
|
|
8679
|
+
* parent non e' un TABVIEW. Stesso meccanismo usato da `activateTabPanelForHover`.
|
|
8680
|
+
*/
|
|
8681
|
+
selectTabPanelExclusive(panel) {
|
|
8682
|
+
const tabViewUn = this.findParentUniqueName(panel);
|
|
8683
|
+
const tabView = tabViewUn ? this.getElementByName(tabViewUn) : null;
|
|
8684
|
+
if (!tabView || String(tabView.name || '').toUpperCase() !== 'TABVIEW' || !Array.isArray(tabView.nestedComponents)) {
|
|
8685
|
+
return;
|
|
8686
|
+
}
|
|
8687
|
+
tabView.nestedComponents.forEach((p) => {
|
|
8688
|
+
if (!p?.inputs) {
|
|
8689
|
+
p.inputs = {};
|
|
8690
|
+
}
|
|
8691
|
+
p.inputs.selected = String(p?.uniqueName || '') === String(panel?.uniqueName || '');
|
|
8692
|
+
});
|
|
8693
|
+
}
|
|
8694
|
+
/** Importa una dashboard salvata DENTRO l'elemento (nei suoi nestedComponents). */
|
|
8695
|
+
async importDashboardIntoElement(target) {
|
|
8696
|
+
if (!this.isFreeContainer(target)) {
|
|
8697
|
+
return;
|
|
8698
|
+
}
|
|
8699
|
+
const options = (this.savedDashboardOptions || []).map((o) => ({ label: o.label, value: o.value }));
|
|
8700
|
+
if (!options.length) {
|
|
8701
|
+
WtoolboxService.messageNotificationService.add({ severity: 'info', summary: this.t('designer.import.title', 'Import'), detail: this.t('designer.import.no_dashboards', 'Nessuna dashboard salvata') });
|
|
8702
|
+
return;
|
|
8703
|
+
}
|
|
8704
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.import.pick_dashboard', 'Importa dashboard qui'), [{ name: 'dash', caption: this.t('designer.import.dashboard', 'Dashboard'), type: 'dictionary', dictionaryData: options, value: '', required: true }], null, '440px', '230px');
|
|
8705
|
+
const route = String(resp?.dash?.value ?? '').trim();
|
|
8706
|
+
if (!route) {
|
|
8707
|
+
return;
|
|
8708
|
+
}
|
|
8709
|
+
let boardcontent = null;
|
|
8710
|
+
try {
|
|
8711
|
+
const result = await this.dataSrv.loadDashboard({ user_id: this.userInfo.getuserInfo().user_id, dashRoute: route });
|
|
8712
|
+
const fallbackRows = result ? [result] : [];
|
|
8713
|
+
const resultRows = Array.isArray(result?.results) ? result.results : fallbackRows;
|
|
8714
|
+
const rows = Array.isArray(result) ? result : resultRows;
|
|
8715
|
+
boardcontent = rows?.[0]?.boardcontent ?? null;
|
|
8716
|
+
}
|
|
8717
|
+
catch {
|
|
8718
|
+
boardcontent = null;
|
|
8719
|
+
}
|
|
8720
|
+
await this.importBoardcontentIntoElement(target, boardcontent);
|
|
8721
|
+
}
|
|
8722
|
+
/** Importa un PRESET locale DENTRO l'elemento (nei suoi nestedComponents). */
|
|
8723
|
+
async importPresetIntoElement(target) {
|
|
8724
|
+
if (!this.isFreeContainer(target)) {
|
|
8725
|
+
return;
|
|
8726
|
+
}
|
|
8727
|
+
const presets = this.readDesignerPresets();
|
|
8728
|
+
const names = Object.keys(presets || {});
|
|
8729
|
+
if (!names.length) {
|
|
8730
|
+
WtoolboxService.messageNotificationService.add({ severity: 'info', summary: this.t('designer.import.title', 'Import'), detail: this.t('designer.import.no_presets', 'Nessun preset salvato') });
|
|
8731
|
+
return;
|
|
8732
|
+
}
|
|
8733
|
+
const resp = await WtoolboxService.promptDialog(this.t('designer.import.pick_preset', 'Importa preset qui'), [{ name: 'preset', caption: this.t('designer.import.preset', 'Preset'), type: 'dictionary', dictionaryData: names.map((n) => ({ label: n, value: n })), value: '', required: true }], null, '440px', '230px');
|
|
8734
|
+
const name = String(resp?.preset?.value ?? '').trim();
|
|
8735
|
+
if (!name || !presets[name]) {
|
|
8736
|
+
return;
|
|
8737
|
+
}
|
|
8738
|
+
await this.importBoardcontentIntoElement(target, presets[name]);
|
|
8739
|
+
}
|
|
8740
|
+
/**
|
|
8741
|
+
* Importa un boardcontent (dashboard salvata o preset) DENTRO un container:
|
|
8742
|
+
* rimappa uniqueName/componentId con id freschi (niente collisioni) e i riferimenti
|
|
8743
|
+
* che li puntano (datasource, parentDatasource, dropped-component...), poi riusa la
|
|
8744
|
+
* rehydration completa di loadDashboard (datasource inclusi) in modalita' insert-target.
|
|
8745
|
+
*/
|
|
8746
|
+
async importBoardcontentIntoElement(target, boardContentRaw) {
|
|
8747
|
+
if (!target || !boardContentRaw) {
|
|
8748
|
+
WtoolboxService.messageNotificationService.add({ severity: 'warn', summary: this.t('designer.import.title', 'Import'), detail: this.t('designer.import.empty', 'Contenuto vuoto o non valido') });
|
|
8749
|
+
return;
|
|
8750
|
+
}
|
|
8751
|
+
let imported;
|
|
8752
|
+
try {
|
|
8753
|
+
imported = JSON.parse(boardContentRaw);
|
|
8754
|
+
}
|
|
8755
|
+
catch {
|
|
8756
|
+
imported = null;
|
|
8757
|
+
}
|
|
8758
|
+
if (!Array.isArray(imported)) {
|
|
8759
|
+
if (Array.isArray(imported?.dashboardElements)) {
|
|
8760
|
+
imported = imported.dashboardElements;
|
|
8761
|
+
}
|
|
8762
|
+
else if (Array.isArray(imported?.elements)) {
|
|
8763
|
+
imported = imported.elements;
|
|
8764
|
+
}
|
|
8765
|
+
else {
|
|
8766
|
+
imported = [];
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
if (!imported.length) {
|
|
8770
|
+
WtoolboxService.messageNotificationService.add({ severity: 'warn', summary: this.t('designer.import.title', 'Import'), detail: this.t('designer.import.empty', 'Contenuto vuoto o non valido') });
|
|
8771
|
+
return;
|
|
8772
|
+
}
|
|
8773
|
+
// 1. Rimappa gli ID con id freschi (getNewID -> uniqueName = name__id) e traccia old->new.
|
|
8774
|
+
const nameMap = new Map();
|
|
8775
|
+
const idMap = new Map();
|
|
8776
|
+
this.remapImportedIds(imported, nameMap, idMap);
|
|
8777
|
+
// 2. Aggiorna i riferimenti agli elementi (datasource ecc.) coi nuovi uniqueName/id.
|
|
8778
|
+
this.remapImportedReferences(imported, nameMap, idMap);
|
|
8779
|
+
// 3. Riusa la rehydration completa di loadDashboard in modalita' insert-target.
|
|
8780
|
+
await this.loadDashboard({ boardcontent: JSON.stringify(imported), board_route: '' }, true, target);
|
|
8781
|
+
WtoolboxService.messageNotificationService.add({ severity: 'success', summary: this.t('designer.import.title', 'Import'), detail: this.t('designer.import.done', 'Contenuto importato nell\'elemento') });
|
|
8782
|
+
}
|
|
8783
|
+
/** Rimappa ricorsivamente componentId/uniqueName degli elementi importati con id freschi. */
|
|
8784
|
+
remapImportedIds(nodes, nameMap, idMap) {
|
|
8785
|
+
for (const el of (nodes || [])) {
|
|
8786
|
+
if (!el || typeof el !== 'object') {
|
|
8787
|
+
continue;
|
|
8788
|
+
}
|
|
8789
|
+
const oldName = String(el.uniqueName || '');
|
|
8790
|
+
const oldId = Number(el.componentId);
|
|
8791
|
+
const newId = this.getNewID();
|
|
8792
|
+
const newName = String(el.name || 'ITEM') + '__' + newId;
|
|
8793
|
+
if (oldName) {
|
|
8794
|
+
nameMap.set(oldName, newName);
|
|
8795
|
+
}
|
|
8796
|
+
if (!Number.isNaN(Number(oldId))) {
|
|
8797
|
+
idMap.set(oldId, newId);
|
|
8798
|
+
}
|
|
8799
|
+
el.componentId = newId;
|
|
8800
|
+
el.uniqueName = newName;
|
|
8801
|
+
if (Array.isArray(el.nestedComponents)) {
|
|
8802
|
+
this.remapImportedIds(el.nestedComponents, nameMap, idMap);
|
|
8803
|
+
}
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
/** Aggiorna i riferimenti (oggetti con uniqueName/id) dentro gli inputs, coi nuovi valori. */
|
|
8807
|
+
remapImportedReferences(nodes, nameMap, idMap) {
|
|
8808
|
+
const SKIP = new Set(['nestedComponents', 'inputProps', 'toolProps', 'metaInfo', 'componentRef', 'propertyTree']);
|
|
8809
|
+
const remap = (v, depth) => {
|
|
8810
|
+
if (!v || typeof v !== 'object' || depth > 6) {
|
|
8811
|
+
return;
|
|
8812
|
+
}
|
|
8813
|
+
if (Array.isArray(v)) {
|
|
8814
|
+
for (const it of v) {
|
|
8815
|
+
remap(it, depth + 1);
|
|
8816
|
+
}
|
|
8817
|
+
return;
|
|
8818
|
+
}
|
|
8819
|
+
if (typeof v.uniqueName === 'string' && nameMap.has(v.uniqueName)) {
|
|
8820
|
+
v.uniqueName = nameMap.get(v.uniqueName);
|
|
8821
|
+
if (typeof v.id === 'number' && idMap.has(v.id)) {
|
|
8822
|
+
v.id = idMap.get(v.id);
|
|
8823
|
+
}
|
|
8824
|
+
if (typeof v.componentId === 'number' && idMap.has(v.componentId)) {
|
|
8825
|
+
v.componentId = idMap.get(v.componentId);
|
|
8826
|
+
}
|
|
8827
|
+
}
|
|
8828
|
+
for (const k of Object.keys(v)) {
|
|
8829
|
+
if (SKIP.has(k)) {
|
|
8830
|
+
continue;
|
|
8831
|
+
}
|
|
8832
|
+
remap(v[k], depth + 1);
|
|
8833
|
+
}
|
|
8834
|
+
};
|
|
8835
|
+
const walk = (list) => {
|
|
8836
|
+
for (const el of (list || [])) {
|
|
8837
|
+
if (!el) {
|
|
8838
|
+
continue;
|
|
8839
|
+
}
|
|
8840
|
+
remap(el.inputs, 0);
|
|
8841
|
+
if (Array.isArray(el.nestedComponents)) {
|
|
8842
|
+
walk(el.nestedComponents);
|
|
8843
|
+
}
|
|
8844
|
+
}
|
|
8845
|
+
};
|
|
8846
|
+
walk(nodes);
|
|
8847
|
+
}
|
|
7750
8848
|
showHierarchyNodeContextMenu(event, node, menu) {
|
|
7751
8849
|
event.preventDefault();
|
|
7752
8850
|
event.stopPropagation();
|
|
7753
8851
|
const uniqueName = String(node?.key || '').trim();
|
|
7754
8852
|
this.hierarchyCtxElement = uniqueName ? this.getElementByName(uniqueName) : null;
|
|
8853
|
+
this.hierarchyCtxItems = this.buildElementContextMenuItems(this.hierarchyCtxElement);
|
|
7755
8854
|
menu.show(event);
|
|
7756
8855
|
}
|
|
7757
8856
|
onHierarchyNodeSelect(event) {
|
|
@@ -8007,13 +9106,14 @@ class DesignerComponent {
|
|
|
8007
9106
|
event.preventDefault();
|
|
8008
9107
|
event.stopPropagation();
|
|
8009
9108
|
this.footerCtxElement = item;
|
|
9109
|
+
this.footerCtxItems = this.buildElementContextMenuItems(item);
|
|
8010
9110
|
menu.show(event);
|
|
8011
9111
|
}
|
|
8012
9112
|
/**
|
|
8013
|
-
* Rinomina
|
|
9113
|
+
* Rinomina un elemento aggiornando `displayName`. Usato dal context menu
|
|
9114
|
+
* unificato (canvas/gerarchia/footer) via `buildElementContextMenuItems`.
|
|
8014
9115
|
*/
|
|
8015
|
-
async
|
|
8016
|
-
const item = this.footerCtxElement;
|
|
9116
|
+
async renameDesignerElement(item) {
|
|
8017
9117
|
if (!item) {
|
|
8018
9118
|
return;
|
|
8019
9119
|
}
|
|
@@ -8087,7 +9187,7 @@ class DesignerComponent {
|
|
|
8087
9187
|
if (value instanceof BehaviorSubject) {
|
|
8088
9188
|
return unwrapEntry(value.value);
|
|
8089
9189
|
}
|
|
8090
|
-
if (value && typeof value === 'object' && Object.
|
|
9190
|
+
if (value && typeof value === 'object' && Object.hasOwn(value, 'value')) {
|
|
8091
9191
|
return unwrapEntry(value.value);
|
|
8092
9192
|
}
|
|
8093
9193
|
if (Array.isArray(value)) {
|
|
@@ -8151,7 +9251,7 @@ class DesignerComponent {
|
|
|
8151
9251
|
if (idValue === undefined || idValue === null || idValue === '') {
|
|
8152
9252
|
if (!isDeleted && allowMissingIdInsert) {
|
|
8153
9253
|
const temporaryId = nextTemporaryNumericId(nextList, idKey);
|
|
8154
|
-
const rowWithId =
|
|
9254
|
+
const rowWithId = { ...originalRow, ...row, [idKey]: temporaryId };
|
|
8155
9255
|
nextList.push(rowWithId);
|
|
8156
9256
|
}
|
|
8157
9257
|
return nextList;
|
|
@@ -8164,10 +9264,10 @@ class DesignerComponent {
|
|
|
8164
9264
|
return nextList;
|
|
8165
9265
|
}
|
|
8166
9266
|
if (idx >= 0) {
|
|
8167
|
-
nextList[idx] =
|
|
9267
|
+
nextList[idx] = { ...nextList[idx], ...row };
|
|
8168
9268
|
}
|
|
8169
9269
|
else {
|
|
8170
|
-
nextList.push(
|
|
9270
|
+
nextList.push({ ...originalRow, ...row });
|
|
8171
9271
|
}
|
|
8172
9272
|
return nextList;
|
|
8173
9273
|
};
|
|
@@ -8199,7 +9299,7 @@ class DesignerComponent {
|
|
|
8199
9299
|
else if (normalizedEditorKey === 'CG_Id') {
|
|
8200
9300
|
const tableMetadata = hostDatasource?.metaInfo?.tableMetadata;
|
|
8201
9301
|
if (tableMetadata) {
|
|
8202
|
-
const normalizedConditionGroup =
|
|
9302
|
+
const normalizedConditionGroup = { ...normalizedOriginal, ...normalizedData };
|
|
8203
9303
|
if (normalizedConditionGroup.CG_Id === undefined || normalizedConditionGroup.CG_Id === null || normalizedConditionGroup.CG_Id === '') {
|
|
8204
9304
|
const fallbackGroupId = normalizedConditionGroup.__guid ?? normalizedOriginal?.__guid;
|
|
8205
9305
|
if (fallbackGroupId !== undefined && fallbackGroupId !== null && String(fallbackGroupId) !== '') {
|
|
@@ -8224,7 +9324,11 @@ class DesignerComponent {
|
|
|
8224
9324
|
const linkedGroup = hasGroupId
|
|
8225
9325
|
? conditionGroups.find((row) => String(row?.CG_Id ?? row?.FK_CG_Id ?? row?.__guid ?? '') === String(rawGroupId))
|
|
8226
9326
|
: null;
|
|
8227
|
-
const normalizedConditionItem =
|
|
9327
|
+
const normalizedConditionItem = {
|
|
9328
|
+
...(linkedGroup || {}),
|
|
9329
|
+
...(normalizedOriginal || {}),
|
|
9330
|
+
...(normalizedData || {})
|
|
9331
|
+
};
|
|
8228
9332
|
if (hasGroupId) {
|
|
8229
9333
|
normalizedConditionItem.CG_Id = rawGroupId;
|
|
8230
9334
|
}
|
|
@@ -8252,7 +9356,7 @@ class DesignerComponent {
|
|
|
8252
9356
|
WtoolboxService.errorHandler?.handleError(new Error('FK_CG_Id obbligatorio: impossibile salvare Condition Action Group senza gruppo condizione.'));
|
|
8253
9357
|
return;
|
|
8254
9358
|
}
|
|
8255
|
-
const normalizedActionGroup =
|
|
9359
|
+
const normalizedActionGroup = { ...normalizedOriginal, ...normalizedData };
|
|
8256
9360
|
if (hasGroupId) {
|
|
8257
9361
|
normalizedActionGroup.FK_CG_Id = rawGroupId;
|
|
8258
9362
|
}
|
|
@@ -8292,7 +9396,7 @@ class DesignerComponent {
|
|
|
8292
9396
|
WtoolboxService.errorHandler?.handleError(new Error('FK_CAG_Id obbligatorio: impossibile salvare Condition Action Item senza gruppo azione condizione.'));
|
|
8293
9397
|
return;
|
|
8294
9398
|
}
|
|
8295
|
-
const normalizedActionItem =
|
|
9399
|
+
const normalizedActionItem = { ...normalizedOriginal, ...normalizedData };
|
|
8296
9400
|
if (hasActionGroupId) {
|
|
8297
9401
|
normalizedActionItem.FK_CAG_Id = rawActionGroupId;
|
|
8298
9402
|
if (normalizedActionItem.CAG_Id === undefined || normalizedActionItem.CAG_Id === null || normalizedActionItem.CAG_Id === '') {
|
|
@@ -8355,7 +9459,7 @@ class DesignerComponent {
|
|
|
8355
9459
|
publishDatasource(this['datasourceTabelle']);
|
|
8356
9460
|
if (hostDatasource?.resultInfo?.current) {
|
|
8357
9461
|
Object.keys(hostDatasource.resultInfo.current).forEach((field) => {
|
|
8358
|
-
if (hostTableMetadata && Object.
|
|
9462
|
+
if (hostTableMetadata && Object.hasOwn(hostTableMetadata, field) && hostDatasource.resultInfo.current[field]?.next) {
|
|
8359
9463
|
hostDatasource.resultInfo.current[field].next(hostTableMetadata[field]);
|
|
8360
9464
|
}
|
|
8361
9465
|
});
|
|
@@ -8489,12 +9593,9 @@ class DesignerComponent {
|
|
|
8489
9593
|
const fieldCtxItems = (contextField && contextDs)
|
|
8490
9594
|
? this.buildFieldEditorContextMenuItems(contextField, contextDs)
|
|
8491
9595
|
: [];
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
else {
|
|
8496
|
-
this.ctxItems.next([...fieldCtxItems, ...this.defaultCtxItems]);
|
|
8497
|
-
}
|
|
9596
|
+
// Menu elemento dalla sorgente unificata (edit/duplica/rinomina/rimuovi +
|
|
9597
|
+
// import per i container); il canvas antepone le voci field-editor.
|
|
9598
|
+
this.ctxItems.next([...fieldCtxItems, ...this.buildElementContextMenuItems(el)]);
|
|
8498
9599
|
if (el) {
|
|
8499
9600
|
this.ctxElement = el;
|
|
8500
9601
|
}
|
|
@@ -8575,18 +9676,21 @@ class DesignerComponent {
|
|
|
8575
9676
|
if (!fieldEditorHost) {
|
|
8576
9677
|
return null;
|
|
8577
9678
|
}
|
|
8578
|
-
const normalize = (v) => String(v || '')
|
|
8579
|
-
.replace('*', '')
|
|
8580
|
-
.replace(/\s+/g, ' ')
|
|
8581
|
-
.trim()
|
|
8582
|
-
.toLowerCase();
|
|
8583
9679
|
return {
|
|
8584
9680
|
dataFieldId: Number(fieldEditorHost.getAttribute('data-field-id') || 0),
|
|
8585
9681
|
dataFieldName: String(fieldEditorHost.getAttribute('data-field-name') || '').trim(),
|
|
8586
9682
|
forAttr: String(fieldEditorHost.querySelector('label.caption')?.getAttribute('for') || '').trim(),
|
|
8587
|
-
labelText:
|
|
9683
|
+
labelText: this.normalizeFieldEditorLabel(fieldEditorHost.querySelector('label.caption')?.textContent || '')
|
|
8588
9684
|
};
|
|
8589
9685
|
}
|
|
9686
|
+
/** Normalizzazione condivisa delle label field-editor (no asterisco, spazi collassati, lowercase). */
|
|
9687
|
+
normalizeFieldEditorLabel(v) {
|
|
9688
|
+
return String(v || '')
|
|
9689
|
+
.replace('*', '')
|
|
9690
|
+
.replaceAll(/\s+/g, ' ')
|
|
9691
|
+
.trim()
|
|
9692
|
+
.toLowerCase();
|
|
9693
|
+
}
|
|
8590
9694
|
matchContextFieldColumn(cols, hints) {
|
|
8591
9695
|
if (!Array.isArray(cols) || !hints) {
|
|
8592
9696
|
return null;
|
|
@@ -8612,12 +9716,7 @@ class DesignerComponent {
|
|
|
8612
9716
|
}
|
|
8613
9717
|
}
|
|
8614
9718
|
if (hints.labelText) {
|
|
8615
|
-
const
|
|
8616
|
-
.replace('*', '')
|
|
8617
|
-
.replace(/\s+/g, ' ')
|
|
8618
|
-
.trim()
|
|
8619
|
-
.toLowerCase();
|
|
8620
|
-
const byLabel = cols.find((c) => normalize(c?.mc_display_string_in_edit) === hints.labelText);
|
|
9719
|
+
const byLabel = cols.find((c) => this.normalizeFieldEditorLabel(c?.mc_display_string_in_edit) === hints.labelText);
|
|
8621
9720
|
if (byLabel) {
|
|
8622
9721
|
return byLabel;
|
|
8623
9722
|
}
|
|
@@ -9307,6 +10406,20 @@ class DesignerComponent {
|
|
|
9307
10406
|
applyArchetypeConfigToTool(tool, action, nextConfig) {
|
|
9308
10407
|
if (!tool || !action || !nextConfig)
|
|
9309
10408
|
return;
|
|
10409
|
+
// Chart AGGREGATO: con groupInfo/aggregates il backend ritorna il valore per-gruppo nella
|
|
10410
|
+
// colonna `<field>_<AGG>` (es. LatestRecordedPopulation_AVG), NON nel campo grezzo (null nelle
|
|
10411
|
+
// righe gruppo). Il modello/configuratore emette il campo grezzo come dataField + l'aggregato in
|
|
10412
|
+
// `aggregates`: qui colleghiamo i due riscrivendo il dataField dei datasets sul nome aggregato,
|
|
10413
|
+
// cosi' il chart plotta i valori per gruppo (suffix uppercase, coerente con _Metadati_methods).
|
|
10414
|
+
if (Array.isArray((nextConfig).aggregates) && (nextConfig).aggregates.length && (nextConfig).dataOptions?.datasets) {
|
|
10415
|
+
for (const dset of (nextConfig).dataOptions.datasets) {
|
|
10416
|
+
const agg = (nextConfig).aggregates.find((a) => a?.field === dset?.dataField);
|
|
10417
|
+
const sfx = agg?.aggregate ? '_' + String(agg.aggregate).toUpperCase() : '';
|
|
10418
|
+
if (sfx && dset?.dataField && !String(dset.dataField).toUpperCase().endsWith(sfx)) {
|
|
10419
|
+
dset.dataField = dset.dataField + sfx;
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
}
|
|
9310
10423
|
const customKey = 'customProps_' + action;
|
|
9311
10424
|
tool.inputs[customKey] = this.toBehaviorSubjectsObject(nextConfig);
|
|
9312
10425
|
const metaInfo = this.getDesignerDatasourceMetaInfo(tool);
|
|
@@ -9314,11 +10427,30 @@ class DesignerComponent {
|
|
|
9314
10427
|
const extraProps = this.ensureMergedExtraProps(metaInfo);
|
|
9315
10428
|
extraProps.archetypes = extraProps.archetypes || {};
|
|
9316
10429
|
const currentArchetype = extraProps.archetypes[action] || {};
|
|
9317
|
-
extraProps.archetypes[action] =
|
|
10430
|
+
extraProps.archetypes[action] = { ...currentArchetype, ...nextConfig };
|
|
10431
|
+
// AGGREGAZIONE chart: groupInfo/aggregates sono config DATASOURCE-level (extraProps
|
|
10432
|
+
// top-level, lette in data-source.component initMetadata:1914), non archetype-level. Se
|
|
10433
|
+
// l'archetype_config li porta (es. chart 'popolazione media per provincia'), sollevali a
|
|
10434
|
+
// extraProps top-level cosi' il datasource raggruppa/aggrega lato server nel fetch.
|
|
10435
|
+
if (Array.isArray((nextConfig).groupInfo))
|
|
10436
|
+
extraProps.groupInfo = (nextConfig).groupInfo;
|
|
10437
|
+
if (Array.isArray((nextConfig).aggregates))
|
|
10438
|
+
extraProps.aggregates = (nextConfig).aggregates;
|
|
9318
10439
|
metaInfo.tableMetadata.extraProps = extraProps;
|
|
9319
10440
|
metaInfo.tableMetadata.md_props_bag = JSON.stringify(extraProps);
|
|
9320
10441
|
}
|
|
9321
10442
|
const ds = tool.inputs?.['datasource']?.component?.value;
|
|
10443
|
+
// Il datasource parsa extraProps.groupInfo/aggregates SOLO all'init metadata: qui (config
|
|
10444
|
+
// applicato a runtime dall'injection/configuratore) li settiamo a mano sull'istanza PRIMA
|
|
10445
|
+
// del fetch, cosi' la fetchData successiva raggruppa/aggrega lato server (un punto per gruppo).
|
|
10446
|
+
if (ds) {
|
|
10447
|
+
if (Array.isArray((nextConfig).groupInfo)) {
|
|
10448
|
+
ds.groupInfo = (nextConfig).groupInfo.map((x) => ({ field: x?.field, groupFormula: x?.groupFormula, dir: x?.dir, groupCount: x?.groupCount }));
|
|
10449
|
+
}
|
|
10450
|
+
if (Array.isArray((nextConfig).aggregates)) {
|
|
10451
|
+
ds.aggregationInfo = (nextConfig).aggregates.map((x) => ({ field: x?.field, aggregate: x?.aggregate }));
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
9322
10454
|
if (ds?.fetchData) {
|
|
9323
10455
|
ds.fetchData();
|
|
9324
10456
|
}
|
|
@@ -9385,7 +10517,7 @@ class DesignerComponent {
|
|
|
9385
10517
|
if (!raw) {
|
|
9386
10518
|
return null;
|
|
9387
10519
|
}
|
|
9388
|
-
const rgbaMatch =
|
|
10520
|
+
const rgbaMatch = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d*\.?\d+)\s*\)$/i.exec(raw);
|
|
9389
10521
|
if (rgbaMatch) {
|
|
9390
10522
|
return {
|
|
9391
10523
|
r: this.clampRgbChannel(rgbaMatch[1]),
|
|
@@ -9394,7 +10526,7 @@ class DesignerComponent {
|
|
|
9394
10526
|
a: this.clampColorAlpha(rgbaMatch[4])
|
|
9395
10527
|
};
|
|
9396
10528
|
}
|
|
9397
|
-
const rgbMatch =
|
|
10529
|
+
const rgbMatch = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i.exec(raw);
|
|
9398
10530
|
if (rgbMatch) {
|
|
9399
10531
|
return {
|
|
9400
10532
|
r: this.clampRgbChannel(rgbMatch[1]),
|
|
@@ -9406,26 +10538,26 @@ class DesignerComponent {
|
|
|
9406
10538
|
const hex = raw.replace('#', '');
|
|
9407
10539
|
if (/^[0-9a-f]{3}$/i.test(hex)) {
|
|
9408
10540
|
return {
|
|
9409
|
-
r: parseInt(hex[0] + hex[0], 16),
|
|
9410
|
-
g: parseInt(hex[1] + hex[1], 16),
|
|
9411
|
-
b: parseInt(hex[2] + hex[2], 16),
|
|
10541
|
+
r: Number.parseInt(hex[0] + hex[0], 16),
|
|
10542
|
+
g: Number.parseInt(hex[1] + hex[1], 16),
|
|
10543
|
+
b: Number.parseInt(hex[2] + hex[2], 16),
|
|
9412
10544
|
a: 1
|
|
9413
10545
|
};
|
|
9414
10546
|
}
|
|
9415
10547
|
if (/^[0-9a-f]{6}$/i.test(hex)) {
|
|
9416
10548
|
return {
|
|
9417
|
-
r: parseInt(hex.slice(0, 2), 16),
|
|
9418
|
-
g: parseInt(hex.slice(2, 4), 16),
|
|
9419
|
-
b: parseInt(hex.slice(4, 6), 16),
|
|
10549
|
+
r: Number.parseInt(hex.slice(0, 2), 16),
|
|
10550
|
+
g: Number.parseInt(hex.slice(2, 4), 16),
|
|
10551
|
+
b: Number.parseInt(hex.slice(4, 6), 16),
|
|
9420
10552
|
a: 1
|
|
9421
10553
|
};
|
|
9422
10554
|
}
|
|
9423
10555
|
if (/^[0-9a-f]{8}$/i.test(hex)) {
|
|
9424
10556
|
return {
|
|
9425
|
-
r: parseInt(hex.slice(0, 2), 16),
|
|
9426
|
-
g: parseInt(hex.slice(2, 4), 16),
|
|
9427
|
-
b: parseInt(hex.slice(4, 6), 16),
|
|
9428
|
-
a: this.clampColorAlpha(parseInt(hex.slice(6, 8), 16) / 255)
|
|
10557
|
+
r: Number.parseInt(hex.slice(0, 2), 16),
|
|
10558
|
+
g: Number.parseInt(hex.slice(2, 4), 16),
|
|
10559
|
+
b: Number.parseInt(hex.slice(4, 6), 16),
|
|
10560
|
+
a: this.clampColorAlpha(Number.parseInt(hex.slice(6, 8), 16) / 255)
|
|
9429
10561
|
};
|
|
9430
10562
|
}
|
|
9431
10563
|
return null;
|
|
@@ -9436,14 +10568,14 @@ class DesignerComponent {
|
|
|
9436
10568
|
let g = 255;
|
|
9437
10569
|
let b = 255;
|
|
9438
10570
|
if (/^[0-9a-f]{3}$/i.test(hex)) {
|
|
9439
|
-
r = parseInt(hex[0] + hex[0], 16);
|
|
9440
|
-
g = parseInt(hex[1] + hex[1], 16);
|
|
9441
|
-
b = parseInt(hex[2] + hex[2], 16);
|
|
10571
|
+
r = Number.parseInt(hex[0] + hex[0], 16);
|
|
10572
|
+
g = Number.parseInt(hex[1] + hex[1], 16);
|
|
10573
|
+
b = Number.parseInt(hex[2] + hex[2], 16);
|
|
9442
10574
|
}
|
|
9443
10575
|
else if (/^[0-9a-f]{6}$/i.test(hex)) {
|
|
9444
|
-
r = parseInt(hex.slice(0, 2), 16);
|
|
9445
|
-
g = parseInt(hex.slice(2, 4), 16);
|
|
9446
|
-
b = parseInt(hex.slice(4, 6), 16);
|
|
10576
|
+
r = Number.parseInt(hex.slice(0, 2), 16);
|
|
10577
|
+
g = Number.parseInt(hex.slice(2, 4), 16);
|
|
10578
|
+
b = Number.parseInt(hex.slice(4, 6), 16);
|
|
9447
10579
|
}
|
|
9448
10580
|
const alpha = this.clampColorAlpha(alphaRaw);
|
|
9449
10581
|
return `rgba(${r}, ${g}, ${b}, ${Number(alpha.toFixed(2))})`;
|
|
@@ -9536,7 +10668,6 @@ class DesignerComponent {
|
|
|
9536
10668
|
}
|
|
9537
10669
|
if (!prevAction) {
|
|
9538
10670
|
prevAction = createAction(prevActionCaption, prevActionCallback, prevActiondisableCallback, 'wizard-prev-btn', 1);
|
|
9539
|
-
needPublish = true;
|
|
9540
10671
|
}
|
|
9541
10672
|
else {
|
|
9542
10673
|
prevAction.button_image = 'wizard-prev-btn';
|
|
@@ -9544,20 +10675,25 @@ class DesignerComponent {
|
|
|
9544
10675
|
}
|
|
9545
10676
|
if (!nextAction) {
|
|
9546
10677
|
nextAction = createAction(nextActionCaption, nextActionCallback, nextActiondisableCallback, 'wizard-next-btn', 3);
|
|
9547
|
-
needPublish = true;
|
|
9548
10678
|
}
|
|
9549
10679
|
else {
|
|
9550
10680
|
nextAction.button_image = 'wizard-next-btn';
|
|
9551
10681
|
nextAction.ordine = 3;
|
|
9552
10682
|
}
|
|
9553
|
-
// if (needPublish) {
|
|
9554
10683
|
this.publishDesignerDatasourceState(hostDatasource);
|
|
9555
10684
|
this.commitHistoryIfChanged();
|
|
9556
10685
|
// }
|
|
9557
10686
|
if (!nextAction) {
|
|
9558
10687
|
return;
|
|
9559
10688
|
}
|
|
9560
|
-
|
|
10689
|
+
let actionToEdit = nextAction;
|
|
10690
|
+
if (target === 'end') {
|
|
10691
|
+
actionToEdit = completeAction;
|
|
10692
|
+
}
|
|
10693
|
+
else if (target === 'prev') {
|
|
10694
|
+
actionToEdit = prevAction;
|
|
10695
|
+
}
|
|
10696
|
+
await this.openTableActionEditorDialog(actionToEdit, hostDatasource);
|
|
9561
10697
|
}
|
|
9562
10698
|
/**
|
|
9563
10699
|
* Restituisce un Id temporaneo negativo non usato per nuove table action in-memory.
|
|
@@ -9731,7 +10867,7 @@ class DesignerComponent {
|
|
|
9731
10867
|
async openLabelDisplayFormulaEditor(inputs, tool) {
|
|
9732
10868
|
const displayField = String(inputs?.displayField || '').trim();
|
|
9733
10869
|
const defaultFormula = displayField
|
|
9734
|
-
? `return dataItem?.['${displayField.
|
|
10870
|
+
? `return dataItem?.['${displayField.replaceAll('\'', "\\'")}'] ?? '';`
|
|
9735
10871
|
: "return dataItem?.[inputs?.displayField] ?? '';";
|
|
9736
10872
|
await this.openSelectDisplayFormulaEditor(inputs, tool, defaultFormula);
|
|
9737
10873
|
}
|
|
@@ -9787,11 +10923,10 @@ class DesignerComponent {
|
|
|
9787
10923
|
const masterRoute = this.getDatasourceRouteName(masterDatasource || {});
|
|
9788
10924
|
const detailRoute = this.getDatasourceRouteName(detailDatasource || {});
|
|
9789
10925
|
const masterPrimaryKey = this.getDatasourcePrimaryKey(masterDatasource || {});
|
|
9790
|
-
const
|
|
9791
|
-
?
|
|
9792
|
-
? `dataItem?.${masterPrimaryKey}`
|
|
9793
|
-
: 'dataItem?.id')
|
|
10926
|
+
const masterPkExpression = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(masterPrimaryKey || '')
|
|
10927
|
+
? `dataItem?.${masterPrimaryKey}`
|
|
9794
10928
|
: 'dataItem?.id';
|
|
10929
|
+
const masterPkValueExpression = masterPrimaryKey ? masterPkExpression : 'dataItem?.id';
|
|
9795
10930
|
const propsBag = {
|
|
9796
10931
|
customEditorConfig: {
|
|
9797
10932
|
editorOptions: {
|
|
@@ -9821,7 +10956,7 @@ class DesignerComponent {
|
|
|
9821
10956
|
: null;
|
|
9822
10957
|
const detailFkField = fkFromLookup || fkFromNameMatch || (detailColumns.length ? detailColumns[0] : '');
|
|
9823
10958
|
const defaultFormula = detailFkField
|
|
9824
|
-
? `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\nsetFilter('${detailFkField.
|
|
10959
|
+
? `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\nsetFilter('${detailFkField.replaceAll('\'', "\\'")}', ${masterPkValueExpression}, 'eq');`
|
|
9825
10960
|
: `// Detail columns (use one as first argument of setFilter):\n${detailColumnsHint}\n// Example:\n// setFilter('detail_fk', ${masterPkValueExpression}, 'eq');`;
|
|
9826
10961
|
const result = await WtoolboxService.promptDialog('Master-Detail Filter Formula', [
|
|
9827
10962
|
{
|
|
@@ -9858,7 +10993,7 @@ class DesignerComponent {
|
|
|
9858
10993
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(col)) {
|
|
9859
10994
|
return ` ${col}: any;`;
|
|
9860
10995
|
}
|
|
9861
|
-
return ` '${col.
|
|
10996
|
+
return ` '${col.replaceAll('\'', "\\'")}': any;`;
|
|
9862
10997
|
}).join('\n');
|
|
9863
10998
|
};
|
|
9864
10999
|
const detailCommentRows = detailColumns.length
|
|
@@ -9906,7 +11041,7 @@ ${detailCommentRows}
|
|
|
9906
11041
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
9907
11042
|
return ` ${key}: any;`;
|
|
9908
11043
|
}
|
|
9909
|
-
return ` '${key.
|
|
11044
|
+
return ` '${key.replaceAll('\'', "\\'")}': any;`;
|
|
9910
11045
|
}).join('\n')
|
|
9911
11046
|
: ' [key: string]: any;';
|
|
9912
11047
|
const routeComment = routeName ? `route: ${routeName}` : 'route: n/a';
|
|
@@ -9998,8 +11133,8 @@ Display formula context:
|
|
|
9998
11133
|
propsFromBag = {};
|
|
9999
11134
|
}
|
|
10000
11135
|
const current = metaInfo?.tableMetadata?.extraProps || {};
|
|
10001
|
-
const merged =
|
|
10002
|
-
merged.archetypes =
|
|
11136
|
+
const merged = { ...propsFromBag, ...current };
|
|
11137
|
+
merged.archetypes = { ...propsFromBag?.archetypes, ...current?.archetypes };
|
|
10003
11138
|
return merged;
|
|
10004
11139
|
}
|
|
10005
11140
|
/**
|
|
@@ -10035,8 +11170,8 @@ Display formula context:
|
|
|
10035
11170
|
propsFromBag = {};
|
|
10036
11171
|
}
|
|
10037
11172
|
const current = tableMetadata.extraProps || {};
|
|
10038
|
-
const merged =
|
|
10039
|
-
merged.archetypes =
|
|
11173
|
+
const merged = { ...propsFromBag, ...current };
|
|
11174
|
+
merged.archetypes = { ...propsFromBag?.archetypes, ...current?.archetypes };
|
|
10040
11175
|
tableMetadata.extraProps = merged;
|
|
10041
11176
|
}
|
|
10042
11177
|
/**
|
|
@@ -10051,8 +11186,8 @@ Display formula context:
|
|
|
10051
11186
|
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
|
10052
11187
|
const normalizeDynamicScript = (source) => String(source || '')
|
|
10053
11188
|
.replace(/^\uFEFF/, '')
|
|
10054
|
-
.
|
|
10055
|
-
.
|
|
11189
|
+
.replaceAll(/\u0000/g, '')
|
|
11190
|
+
.replaceAll(/[\u2028\u2029]/g, '\n');
|
|
10056
11191
|
actions.forEach((action) => {
|
|
10057
11192
|
if (!action || typeof action !== 'object') {
|
|
10058
11193
|
return;
|
|
@@ -10062,7 +11197,7 @@ Display formula context:
|
|
|
10062
11197
|
try {
|
|
10063
11198
|
const body = WtoolboxService.buildAsyncBody(actionScript);
|
|
10064
11199
|
action.action_callback__fn = actionScript
|
|
10065
|
-
? new AsyncFunction('datasource, metaInfo, record, event, wtoolbox', body)
|
|
11200
|
+
? (new AsyncFunction('datasource, metaInfo, record, event, wtoolbox', body))
|
|
10066
11201
|
: async () => { };
|
|
10067
11202
|
}
|
|
10068
11203
|
catch {
|
|
@@ -10205,10 +11340,6 @@ Display formula context:
|
|
|
10205
11340
|
}
|
|
10206
11341
|
}
|
|
10207
11342
|
else if (deltaRows < 0) {
|
|
10208
|
-
for (let i = tool.nestedComponents.length; i >= Math.abs(deltaRows); i--) {
|
|
10209
|
-
let trTool = tool.nestedComponents[i - 1];
|
|
10210
|
-
// trTool.nestedComponents = [];
|
|
10211
|
-
}
|
|
10212
11343
|
tool.nestedComponents.splice(deltaRows, Math.abs(deltaRows));
|
|
10213
11344
|
}
|
|
10214
11345
|
else if (deltaCols > 0) {
|
|
@@ -10267,14 +11398,10 @@ Display formula context:
|
|
|
10267
11398
|
}
|
|
10268
11399
|
}
|
|
10269
11400
|
else if (deltaAreas < 0) {
|
|
10270
|
-
for (let i = tool.nestedComponents.length; i >= Math.abs(deltaAreas); i--) {
|
|
10271
|
-
let trTool = tool.nestedComponents[i - 1];
|
|
10272
|
-
// trTool.nestedComponents = [];
|
|
10273
|
-
}
|
|
10274
11401
|
tool.nestedComponents.splice(deltaAreas, Math.abs(deltaAreas));
|
|
10275
11402
|
}
|
|
10276
|
-
for (
|
|
10277
|
-
|
|
11403
|
+
for (const nestedComponent of tool.nestedComponents) {
|
|
11404
|
+
nestedComponent.inputs['size'] = 100 / tool.nestedComponents.length;
|
|
10278
11405
|
}
|
|
10279
11406
|
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
10280
11407
|
obj[prop] = newValue;
|
|
@@ -10304,14 +11431,10 @@ Display formula context:
|
|
|
10304
11431
|
}
|
|
10305
11432
|
}
|
|
10306
11433
|
else if (deltaItems < 0) {
|
|
10307
|
-
for (let i = tool.nestedComponents.length; i >= Math.abs(deltaItems); i--) {
|
|
10308
|
-
let trTool = tool.nestedComponents[i - 1];
|
|
10309
|
-
// trTool.nestedComponents = [];
|
|
10310
|
-
}
|
|
10311
11434
|
tool.nestedComponents.splice(deltaItems, Math.abs(deltaItems));
|
|
10312
11435
|
}
|
|
10313
|
-
for (
|
|
10314
|
-
|
|
11436
|
+
for (const nestedComponent of tool.nestedComponents) {
|
|
11437
|
+
nestedComponent.inputs['size'] = 100 / tool.nestedComponents.length;
|
|
10315
11438
|
}
|
|
10316
11439
|
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
10317
11440
|
obj[prop] = newValue;
|
|
@@ -10325,7 +11448,8 @@ Display formula context:
|
|
|
10325
11448
|
if (currentTool.name == 'TABLE') {
|
|
10326
11449
|
let tr = this.availableTools.find(x => x.name == 'TR');
|
|
10327
11450
|
let td = this.availableTools.find(x => x.name == 'TD');
|
|
10328
|
-
let table
|
|
11451
|
+
let table;
|
|
11452
|
+
this.availableTools.find(x => x.name == 'TABLE');
|
|
10329
11453
|
for (let i = 1; i <= currentTool.inputs['rows']; i++) {
|
|
10330
11454
|
let tr1 = JSON.parse(JSON.stringify(tr));
|
|
10331
11455
|
tr1.inputs.width = undefined;
|
|
@@ -10473,16 +11597,14 @@ Display formula context:
|
|
|
10473
11597
|
}
|
|
10474
11598
|
});
|
|
10475
11599
|
}
|
|
11600
|
+
else if (currentTool.inputProps[prop].type == 'dropped-component') {
|
|
11601
|
+
currentTool.inputs[prop] = { component: new BehaviorSubject(currentTool.inputs[prop]), id: currentTool.componentId, name: currentTool.name, uniqueName: currentTool.uniqueName };
|
|
11602
|
+
}
|
|
11603
|
+
else if (currentTool.inputProps[prop].type == 'function') {
|
|
11604
|
+
currentTool.inputs[prop] = this.draggedPayload.inputs[prop].bind(currentTool.inputs);
|
|
11605
|
+
}
|
|
10476
11606
|
else {
|
|
10477
|
-
|
|
10478
|
-
currentTool.inputs[prop] = { component: new BehaviorSubject(currentTool.inputs[prop]), id: currentTool.componentId, name: currentTool.name, uniqueName: currentTool.uniqueName };
|
|
10479
|
-
}
|
|
10480
|
-
else if (currentTool.inputProps[prop].type == 'function') {
|
|
10481
|
-
currentTool.inputs[prop] = this.draggedPayload.inputs[prop].bind(currentTool.inputs);
|
|
10482
|
-
}
|
|
10483
|
-
else {
|
|
10484
|
-
currentTool.inputs[prop] = currentTool.inputs[prop];
|
|
10485
|
-
}
|
|
11607
|
+
currentTool.inputs[prop] = currentTool.inputs[prop];
|
|
10486
11608
|
}
|
|
10487
11609
|
if (this.draggedPayload.inputProps[prop].conditional) {
|
|
10488
11610
|
currentTool.inputProps[prop].conditional = this.draggedPayload.inputProps[prop].conditional;
|
|
@@ -10512,6 +11634,7 @@ Display formula context:
|
|
|
10512
11634
|
});
|
|
10513
11635
|
}
|
|
10514
11636
|
let destination = this.dashboardElements;
|
|
11637
|
+
let parentContainerTool = null;
|
|
10515
11638
|
if (event.toElement) {
|
|
10516
11639
|
let id = event.toElement.getAttribute("id");
|
|
10517
11640
|
let parentEl = event.toElement.parentElement;
|
|
@@ -10519,14 +11642,14 @@ Display formula context:
|
|
|
10519
11642
|
id = parentEl.getAttribute("id");
|
|
10520
11643
|
parentEl = parentEl.parentElement;
|
|
10521
11644
|
}
|
|
10522
|
-
if (id
|
|
10523
|
-
let componentId = parseInt(id.split("__")[1]);
|
|
11645
|
+
if (id?.includes("__")) {
|
|
11646
|
+
let componentId = Number.parseInt(id.split("__")[1]);
|
|
10524
11647
|
let match = null;
|
|
10525
|
-
// let parent = this.dashboardElements;
|
|
10526
11648
|
match = (this.flattenedDashboardElements || []).find(x => x.componentId == componentId);
|
|
10527
11649
|
if (match) {
|
|
10528
11650
|
if (!match.allowedChildren || match.allowedChildren.includes(currentTool.name)) {
|
|
10529
11651
|
destination = match?.nestedComponents;
|
|
11652
|
+
parentContainerTool = match;
|
|
10530
11653
|
}
|
|
10531
11654
|
else {
|
|
10532
11655
|
WtoolboxService.messageNotificationService.add({
|
|
@@ -10552,11 +11675,68 @@ Display formula context:
|
|
|
10552
11675
|
currentTool.onDrop(currentTool);
|
|
10553
11676
|
}
|
|
10554
11677
|
this.tool = currentTool;
|
|
11678
|
+
// Flusso assoluto (feature, default OFF): l'elemento droppato nasce
|
|
11679
|
+
// position:absolute con left/top dalla posizione del drop -> fuori dal flusso,
|
|
11680
|
+
// quindi ridimensionarne uno non sposta gli altri.
|
|
11681
|
+
if (this.absoluteFlowEnabled) {
|
|
11682
|
+
// Riferimento di posizionamento: il container se droppo dentro uno, altrimenti il canvas.
|
|
11683
|
+
let refEl = this.canvasRef?.nativeElement || null;
|
|
11684
|
+
if (parentContainerTool) {
|
|
11685
|
+
const containerDom = document.getElementById(parentContainerTool.uniqueName);
|
|
11686
|
+
if (containerDom) {
|
|
11687
|
+
refEl = containerDom;
|
|
11688
|
+
// Il container diventa il contesto di posizionamento del figlio absolute
|
|
11689
|
+
// (se non e' gia' posizionato): cosi' left/top del figlio sono relativi a lui.
|
|
11690
|
+
const curPos = String(parentContainerTool.inputs?.['position'] || '').trim();
|
|
11691
|
+
if (!curPos || curPos === 'static') {
|
|
11692
|
+
parentContainerTool.inputs['position'] = 'relative';
|
|
11693
|
+
}
|
|
11694
|
+
}
|
|
11695
|
+
}
|
|
11696
|
+
const pos = this.getDropPositionRelativeTo(event, refEl);
|
|
11697
|
+
currentTool.inputs['position'] = 'absolute';
|
|
11698
|
+
currentTool.inputs['left'] = pos.left + 'px';
|
|
11699
|
+
currentTool.inputs['top'] = pos.top + 'px';
|
|
11700
|
+
}
|
|
10555
11701
|
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
11702
|
+
// Snap-on-drop SOLO in flusso normale: in modalita' assoluta la posizione e'
|
|
11703
|
+
// gia' settata (left/top) e snappata sopra.
|
|
11704
|
+
if (!this.absoluteFlowEnabled) {
|
|
11705
|
+
this.snapDroppedElementToGrid(currentTool);
|
|
11706
|
+
}
|
|
10556
11707
|
this.commitHistoryIfChanged();
|
|
10557
11708
|
this.draggedPayload = null;
|
|
10558
11709
|
}
|
|
10559
11710
|
}
|
|
11711
|
+
/**
|
|
11712
|
+
* Snap-on-drop: se lo snap alla griglia e' attivo, allinea la posizione
|
|
11713
|
+
* dell'elemento appena droppato al multiplo di griglia scrivendo un
|
|
11714
|
+
* `inputs.transform` di traslazione (stessa proprieta' usata da `onMoving`,
|
|
11715
|
+
* quindi persiste su save/reload). Deferred perche' l'elemento deve essere
|
|
11716
|
+
* nel DOM per leggere `offsetLeft/offsetTop` (posizione di flusso, senza
|
|
11717
|
+
* transform). No-op se snap off o gia' allineato.
|
|
11718
|
+
*/
|
|
11719
|
+
snapDroppedElementToGrid(tool) {
|
|
11720
|
+
const g = DesignerSnapState.gridSize;
|
|
11721
|
+
if (!g || g <= 0 || !tool?.uniqueName) {
|
|
11722
|
+
return;
|
|
11723
|
+
}
|
|
11724
|
+
setTimeout(() => {
|
|
11725
|
+
const el = document.getElementById(tool.uniqueName);
|
|
11726
|
+
if (!el) {
|
|
11727
|
+
return;
|
|
11728
|
+
}
|
|
11729
|
+
const dx = Math.round(el.offsetLeft / g) * g - el.offsetLeft;
|
|
11730
|
+
const dy = Math.round(el.offsetTop / g) * g - el.offsetTop;
|
|
11731
|
+
if (dx === 0 && dy === 0) {
|
|
11732
|
+
return;
|
|
11733
|
+
}
|
|
11734
|
+
tool.inputs = tool.inputs || {};
|
|
11735
|
+
tool.inputs['transform'] = `matrix(1, 0, 0, 1, ${dx}, ${dy})`;
|
|
11736
|
+
this.cd.detectChanges();
|
|
11737
|
+
this.commitHistoryIfChanged();
|
|
11738
|
+
}, 0);
|
|
11739
|
+
}
|
|
10560
11740
|
/**
|
|
10561
11741
|
* Genera un nuovo identificativo incrementale per `componentId`.
|
|
10562
11742
|
*/
|
|
@@ -10600,12 +11780,15 @@ Display formula context:
|
|
|
10600
11780
|
const savedState = this.propertySectionStateByTool[toolKey] || {};
|
|
10601
11781
|
return sectionOrder
|
|
10602
11782
|
.filter((key) => grouped[key].length > 0)
|
|
10603
|
-
.map((key) =>
|
|
10604
|
-
key: key
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
11783
|
+
.map((key) => {
|
|
11784
|
+
const savedOpen = savedState[key] !== undefined ? !!savedState[key] : openByDefault[key];
|
|
11785
|
+
return {
|
|
11786
|
+
key: key,
|
|
11787
|
+
label: labels[key],
|
|
11788
|
+
open: (propFilter && propFilter.length >= 3 && grouped[key].length) ? true : savedOpen,
|
|
11789
|
+
props: grouped[key]
|
|
11790
|
+
};
|
|
11791
|
+
});
|
|
10609
11792
|
}
|
|
10610
11793
|
clearPropFilter() {
|
|
10611
11794
|
this.propFilter = "";
|
|
@@ -10648,7 +11831,7 @@ Display formula context:
|
|
|
10648
11831
|
return this.getRepeaterAction(tool) !== 'wizard';
|
|
10649
11832
|
}
|
|
10650
11833
|
if (propFilter && propFilter.length >= 3) {
|
|
10651
|
-
return toolProp.key.toLocaleLowerCase().
|
|
11834
|
+
return !toolProp.key.toLocaleLowerCase().includes(propFilter.toLocaleLowerCase());
|
|
10652
11835
|
}
|
|
10653
11836
|
return false;
|
|
10654
11837
|
}
|
|
@@ -10764,7 +11947,7 @@ Display formula context:
|
|
|
10764
11947
|
return value;
|
|
10765
11948
|
}
|
|
10766
11949
|
mergeToolPropDefinition(baseDef, currentDef) {
|
|
10767
|
-
const merged = { ...(baseDef
|
|
11950
|
+
const merged = { ...(baseDef), ...(currentDef) };
|
|
10768
11951
|
if (!merged.converter && baseDef?.converter) {
|
|
10769
11952
|
merged.converter = baseDef.converter;
|
|
10770
11953
|
}
|
|
@@ -10790,7 +11973,7 @@ Display formula context:
|
|
|
10790
11973
|
if (template?.inputProps) {
|
|
10791
11974
|
Object.keys(template.inputProps).forEach((key) => {
|
|
10792
11975
|
element.inputProps[key] = this.mergeToolPropDefinition(template.inputProps[key], element.inputProps[key]);
|
|
10793
|
-
if (element.inputs[key] === undefined && template.inputs
|
|
11976
|
+
if (element.inputs[key] === undefined && template.inputs?.[key] !== undefined) {
|
|
10794
11977
|
element.inputs[key] = this.cloneInputDefaultValue(template.inputs[key]);
|
|
10795
11978
|
}
|
|
10796
11979
|
});
|
|
@@ -10845,7 +12028,80 @@ Display formula context:
|
|
|
10845
12028
|
* - `elements` (JSON) -> `boardcontent` (TEXT)
|
|
10846
12029
|
* e aggiorna anche i fogli collegati in `dom_board_sheet`.
|
|
10847
12030
|
*/
|
|
12031
|
+
/**
|
|
12032
|
+
* Lint pre-save del boardcontent: individua problemi di configurazione che
|
|
12033
|
+
* altrimenti emergerebbero solo a runtime. Metodo puro (nessun side-effect),
|
|
12034
|
+
* riusabile e testabile. Rileva:
|
|
12035
|
+
* - uniqueName duplicati (rompono selezione/rimozione/hierarchy tree, key=uniqueName);
|
|
12036
|
+
* - binding datasource orfani (DATAREPEATER/SELECT/MULTISELECT/UL che puntano a
|
|
12037
|
+
* un DATASOURCE.uniqueName inesistente nel layout);
|
|
12038
|
+
* - DATASOURCE senza route impostata.
|
|
12039
|
+
*/
|
|
12040
|
+
computeDashboardLintIssues() {
|
|
12041
|
+
const issues = [];
|
|
12042
|
+
const flat = this.flattenComponentTree(this.dashboardElements) || [];
|
|
12043
|
+
// 1. uniqueName duplicati
|
|
12044
|
+
const seen = new Set();
|
|
12045
|
+
const dup = new Set();
|
|
12046
|
+
for (const el of flat) {
|
|
12047
|
+
const un = String(el?.uniqueName || '').trim();
|
|
12048
|
+
if (!un) {
|
|
12049
|
+
continue;
|
|
12050
|
+
}
|
|
12051
|
+
if (seen.has(un)) {
|
|
12052
|
+
dup.add(un);
|
|
12053
|
+
}
|
|
12054
|
+
seen.add(un);
|
|
12055
|
+
}
|
|
12056
|
+
for (const un of dup) {
|
|
12057
|
+
issues.push(this.trslSrv.format(this.t('designer.lint.duplicate_unique_name', "Nome elemento duplicato: '{0}'"), un));
|
|
12058
|
+
}
|
|
12059
|
+
// 2. uniqueName dei DATASOURCE disponibili
|
|
12060
|
+
const dsNames = new Set();
|
|
12061
|
+
for (const el of flat) {
|
|
12062
|
+
if (el?.name === 'DATASOURCE') {
|
|
12063
|
+
const un = String(el?.uniqueName || '').trim();
|
|
12064
|
+
if (un) {
|
|
12065
|
+
dsNames.add(un);
|
|
12066
|
+
}
|
|
12067
|
+
}
|
|
12068
|
+
}
|
|
12069
|
+
// 3. binding orfani + DATASOURCE senza route
|
|
12070
|
+
for (const el of flat) {
|
|
12071
|
+
const name = String(el?.name || '');
|
|
12072
|
+
if (name === 'DATAREPEATER' || name === 'SELECT' || name === 'MULTISELECT' || name === 'UL') {
|
|
12073
|
+
const ds = el?.inputs?.['datasource'];
|
|
12074
|
+
const dsUn = ds && typeof ds === 'object' ? String(ds.uniqueName || '').trim() : '';
|
|
12075
|
+
if (dsUn && !dsNames.has(dsUn)) {
|
|
12076
|
+
issues.push(this.trslSrv.format(this.t('designer.lint.orphan_datasource_binding', "Il componente '{0}' punta a un datasource inesistente ('{1}')"), String(el?.uniqueName || el?.name || ''), dsUn));
|
|
12077
|
+
}
|
|
12078
|
+
}
|
|
12079
|
+
if (name === 'DATASOURCE') {
|
|
12080
|
+
const routeVal = el?.inputs?.['route'];
|
|
12081
|
+
const routeStr = routeVal instanceof BehaviorSubject ? routeVal.value : routeVal;
|
|
12082
|
+
if (!String(routeStr || '').trim()) {
|
|
12083
|
+
issues.push(this.trslSrv.format(this.t('designer.lint.datasource_without_route', "Il datasource '{0}' non ha una route impostata"), String(el?.uniqueName || 'DATASOURCE')));
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12086
|
+
}
|
|
12087
|
+
return issues;
|
|
12088
|
+
}
|
|
10848
12089
|
async saveDashboard() {
|
|
12090
|
+
// Lint pre-save non bloccante: se ci sono problemi, elencali e chiedi conferma.
|
|
12091
|
+
const lintIssues = this.computeDashboardLintIssues();
|
|
12092
|
+
if (lintIssues.length > 0) {
|
|
12093
|
+
const proceed = await WtoolboxService.confirm({
|
|
12094
|
+
header: this.t('designer.lint.title', 'Problemi nel layout'),
|
|
12095
|
+
message: this.t('designer.lint.intro', 'Il layout presenta alcuni problemi:')
|
|
12096
|
+
+ ' ' + lintIssues.map((i) => '• ' + i).join(' ')
|
|
12097
|
+
+ ' — ' + this.t('designer.lint.confirm_save_anyway', 'Salvare comunque?'),
|
|
12098
|
+
acceptLabel: this.t('confirm', 'Confirm'),
|
|
12099
|
+
rejectLabel: this.t('cancel', 'Cancel')
|
|
12100
|
+
});
|
|
12101
|
+
if (!proceed) {
|
|
12102
|
+
return;
|
|
12103
|
+
}
|
|
12104
|
+
}
|
|
10849
12105
|
const route = String(this.currentDashboardRoute || '').trim();
|
|
10850
12106
|
if (route) {
|
|
10851
12107
|
const payload = {
|
|
@@ -10931,14 +12187,18 @@ Display formula context:
|
|
|
10931
12187
|
* e ricostruisce lo stato runtime del designer partendo da `dom_board.boardcontent`.
|
|
10932
12188
|
* Supporta route in forme alias (`board_route`, `boardroute`, `dashRoute`).
|
|
10933
12189
|
*/
|
|
10934
|
-
async loadDashboard(dashboard, skipServerFetch = false) {
|
|
12190
|
+
async loadDashboard(dashboard, skipServerFetch = false, insertTarget = null) {
|
|
10935
12191
|
const dashRoute = String(dashboard?.board_route ??
|
|
10936
12192
|
dashboard?.boardroute ??
|
|
10937
12193
|
dashboard?.boardRoute ??
|
|
10938
12194
|
dashboard?.dashRoute ??
|
|
10939
12195
|
dashboard ??
|
|
10940
12196
|
'').trim();
|
|
10941
|
-
|
|
12197
|
+
// Import/preset (feature C/F) passano boardcontent con route eventualmente
|
|
12198
|
+
// vuota (board non ancora salvata). NON fare reset in quel caso: il reset va
|
|
12199
|
+
// fatto solo quando NON c'e' ne' route ne' contenuto (nuovo grafo / clear).
|
|
12200
|
+
const providedBoardcontent = dashboard?.boardcontent ?? dashboard?.boardContent ?? null;
|
|
12201
|
+
if (!dashRoute && !providedBoardcontent) {
|
|
10942
12202
|
this.dashboardElements = [];
|
|
10943
12203
|
this.flattenedDashboardElements = [];
|
|
10944
12204
|
this.currentDashboardRoute = '';
|
|
@@ -10952,8 +12212,10 @@ Display formula context:
|
|
|
10952
12212
|
this.cd.detectChanges();
|
|
10953
12213
|
return;
|
|
10954
12214
|
}
|
|
10955
|
-
|
|
10956
|
-
|
|
12215
|
+
if (!insertTarget) {
|
|
12216
|
+
this.currentDashboardRoute = dashRoute;
|
|
12217
|
+
this.refreshGraphActionsMenuItems();
|
|
12218
|
+
}
|
|
10957
12219
|
let resultRows = [];
|
|
10958
12220
|
let result = null;
|
|
10959
12221
|
if (!skipServerFetch) {
|
|
@@ -10962,18 +12224,20 @@ Display formula context:
|
|
|
10962
12224
|
dashRoute: dashRoute
|
|
10963
12225
|
};
|
|
10964
12226
|
result = await this.dataSrv.loadDashboard(payload);
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
}
|
|
10969
|
-
|
|
10970
|
-
dashboard?.
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
12227
|
+
const singleRows = result ? [result] : [];
|
|
12228
|
+
const wrappedRows = Array.isArray((result)?.results) ? (result).results : singleRows;
|
|
12229
|
+
resultRows = Array.isArray(result) ? result : wrappedRows;
|
|
12230
|
+
}
|
|
12231
|
+
if (!insertTarget) {
|
|
12232
|
+
this.currentDashboardDescription = String(dashboard?.board_des ??
|
|
12233
|
+
dashboard?.boarddes ??
|
|
12234
|
+
resultRows?.[0]?.board_des ??
|
|
12235
|
+
resultRows?.[0]?.boarddes ??
|
|
12236
|
+
this.currentDashboardDescription ??
|
|
12237
|
+
dashRoute).trim();
|
|
12238
|
+
const boardSheetPaths = this.extractDashboardSheetPaths(resultRows, dashboard);
|
|
12239
|
+
await this.loadDashboardCssSheets(boardSheetPaths);
|
|
12240
|
+
}
|
|
10977
12241
|
const boardContentRaw = resultRows?.[0]?.boardcontent ?? dashboard?.boardcontent ?? dashboard?.boardContent ?? null;
|
|
10978
12242
|
if (boardContentRaw) {
|
|
10979
12243
|
let loadedDashboard = [];
|
|
@@ -10984,14 +12248,29 @@ Display formula context:
|
|
|
10984
12248
|
loadedDashboard = [];
|
|
10985
12249
|
}
|
|
10986
12250
|
if (!Array.isArray(loadedDashboard)) {
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
12251
|
+
// Forma wrapper-object: puo' trasportare flag board-level come
|
|
12252
|
+
// `widgetChrome` (chrome card dei widget, vedi <wuic-dashboard>).
|
|
12253
|
+
if (!insertTarget) {
|
|
12254
|
+
const chrome = loadedDashboard?.widgetChrome;
|
|
12255
|
+
this.dashboardWidgetChrome = chrome === true || String(chrome ?? '').trim().toLowerCase() === 'true' || String(chrome ?? '').trim() === '1';
|
|
12256
|
+
}
|
|
12257
|
+
if (Array.isArray(loadedDashboard?.dashboardElements)) {
|
|
12258
|
+
loadedDashboard = loadedDashboard.dashboardElements;
|
|
12259
|
+
}
|
|
12260
|
+
else if (Array.isArray(loadedDashboard?.elements)) {
|
|
12261
|
+
loadedDashboard = loadedDashboard.elements;
|
|
12262
|
+
}
|
|
12263
|
+
else {
|
|
12264
|
+
loadedDashboard = [];
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
else if (!insertTarget) {
|
|
12268
|
+
this.dashboardWidgetChrome = false;
|
|
10992
12269
|
}
|
|
10993
12270
|
this.mergeMissingToolPropsRecursive(loadedDashboard);
|
|
10994
|
-
|
|
12271
|
+
if (!insertTarget) {
|
|
12272
|
+
this.dashboardComponents = [];
|
|
12273
|
+
}
|
|
10995
12274
|
let dsSubs = [];
|
|
10996
12275
|
let dsElements = [];
|
|
10997
12276
|
let dsElementCount = (loadedDashboard || []).filter(x => x?.name == 'DATASOURCE').length;
|
|
@@ -11044,18 +12323,16 @@ Display formula context:
|
|
|
11044
12323
|
uniqueName: element.uniqueName
|
|
11045
12324
|
});
|
|
11046
12325
|
}
|
|
12326
|
+
else if (element.inputProps[prop].asyncPath) {
|
|
12327
|
+
const path = element.inputProps[prop].asyncPath;
|
|
12328
|
+
const rawValue = element.inputs[prop] ?? {};
|
|
12329
|
+
element.inputs[prop] = new BehaviorSubject({
|
|
12330
|
+
...rawValue,
|
|
12331
|
+
[path]: new BehaviorSubject(rawValue?.[path] ?? null)
|
|
12332
|
+
});
|
|
12333
|
+
}
|
|
11047
12334
|
else {
|
|
11048
|
-
|
|
11049
|
-
const path = element.inputProps[prop].asyncPath;
|
|
11050
|
-
const rawValue = element.inputs[prop] ?? {};
|
|
11051
|
-
element.inputs[prop] = new BehaviorSubject({
|
|
11052
|
-
...rawValue,
|
|
11053
|
-
[path]: new BehaviorSubject(rawValue?.[path] ?? null)
|
|
11054
|
-
});
|
|
11055
|
-
}
|
|
11056
|
-
else {
|
|
11057
|
-
element.inputs[prop] = new BehaviorSubject(element.inputs[prop]);
|
|
11058
|
-
}
|
|
12335
|
+
element.inputs[prop] = new BehaviorSubject(element.inputs[prop]);
|
|
11059
12336
|
}
|
|
11060
12337
|
element.inputs[prop].subscribe((v) => {
|
|
11061
12338
|
if (v?.component) {
|
|
@@ -11089,7 +12366,7 @@ Display formula context:
|
|
|
11089
12366
|
}
|
|
11090
12367
|
}
|
|
11091
12368
|
}
|
|
11092
|
-
if (!dsElements.
|
|
12369
|
+
if (!dsElements.some(x => x.uniqueName == v.uniqueName)) {
|
|
11093
12370
|
dsSubs.push(v.component.fetchInfo$);
|
|
11094
12371
|
dsElements.push(dsElement);
|
|
11095
12372
|
if (dsElements.length == dsElementCount) {
|
|
@@ -11133,18 +12410,34 @@ Display formula context:
|
|
|
11133
12410
|
});
|
|
11134
12411
|
// Ensure a new reference for template control-flow and force refresh when
|
|
11135
12412
|
// async loading happens outside Angular zone.
|
|
11136
|
-
|
|
11137
|
-
|
|
12413
|
+
if (insertTarget) {
|
|
12414
|
+
// Import IN-elemento: inserisci gli elementi importati nei nestedComponents
|
|
12415
|
+
// del target invece di sostituire l'intero canvas.
|
|
12416
|
+
insertTarget.nestedComponents = Array.isArray(insertTarget.nestedComponents) ? insertTarget.nestedComponents : [];
|
|
12417
|
+
insertTarget.nestedComponents.push(...(Array.isArray(loadedDashboard) ? loadedDashboard : []));
|
|
12418
|
+
this.dashboardElements = Array.isArray(this.dashboardElements) ? [...this.dashboardElements] : [];
|
|
12419
|
+
this.flattenedDashboardElements = this.flattenComponentTree(this.dashboardElements);
|
|
12420
|
+
}
|
|
12421
|
+
else {
|
|
12422
|
+
this.dashboardElements = Array.isArray(loadedDashboard) ? [...loadedDashboard] : [];
|
|
12423
|
+
this.flattenedDashboardElements = flattened;
|
|
12424
|
+
}
|
|
11138
12425
|
this.cd.detectChanges();
|
|
11139
12426
|
}
|
|
11140
|
-
else {
|
|
12427
|
+
else if (!insertTarget) {
|
|
11141
12428
|
this.dashboardElements = [];
|
|
11142
12429
|
this.flattenedDashboardElements = [];
|
|
11143
12430
|
this.currentDashboardRoute = '';
|
|
11144
12431
|
this.cd.detectChanges();
|
|
11145
12432
|
console.warn('[Designer] No dashboard content found for route:', dashRoute, result);
|
|
11146
12433
|
}
|
|
11147
|
-
|
|
12434
|
+
if (insertTarget) {
|
|
12435
|
+
// Import in-elemento: NON resettare la history (stiamo aggiungendo), committa.
|
|
12436
|
+
this.commitHistoryIfChanged();
|
|
12437
|
+
}
|
|
12438
|
+
else {
|
|
12439
|
+
this.resetHistory();
|
|
12440
|
+
}
|
|
11148
12441
|
this.refreshGraphActionsMenuItems();
|
|
11149
12442
|
this.maxId = (this.flattenedDashboardElements || []).reduce((max, item) => Math.max(max, Number(item?.componentId || 0)), 0);
|
|
11150
12443
|
}
|
|
@@ -11258,11 +12551,15 @@ Display formula context:
|
|
|
11258
12551
|
loadedDashboard = [];
|
|
11259
12552
|
}
|
|
11260
12553
|
if (!Array.isArray(loadedDashboard)) {
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
12554
|
+
if (Array.isArray(loadedDashboard?.dashboardElements)) {
|
|
12555
|
+
loadedDashboard = loadedDashboard.dashboardElements;
|
|
12556
|
+
}
|
|
12557
|
+
else if (Array.isArray(loadedDashboard?.elements)) {
|
|
12558
|
+
loadedDashboard = loadedDashboard.elements;
|
|
12559
|
+
}
|
|
12560
|
+
else {
|
|
12561
|
+
loadedDashboard = [];
|
|
12562
|
+
}
|
|
11266
12563
|
}
|
|
11267
12564
|
this.mergeMissingToolPropsRecursive(loadedDashboard);
|
|
11268
12565
|
this.dashboardComponents = [];
|
|
@@ -11317,7 +12614,14 @@ Display formula context:
|
|
|
11317
12614
|
if (!propDef.async && propDef.asyncPath && (propDef.type === 'dropped-component-list' || propDef.type === 'dropped-component')) {
|
|
11318
12615
|
const path = propDef.asyncPath;
|
|
11319
12616
|
const rawValue = element.inputs[prop];
|
|
11320
|
-
|
|
12617
|
+
let rawValueKind = 'object';
|
|
12618
|
+
if (rawValue === null) {
|
|
12619
|
+
rawValueKind = 'NULL';
|
|
12620
|
+
}
|
|
12621
|
+
else if (rawValue === undefined) {
|
|
12622
|
+
rawValueKind = 'UNDEF';
|
|
12623
|
+
}
|
|
12624
|
+
console.log(`[HYDRATE-v9] ${element.name}.${prop}: rawValue=${rawValueKind} uniqueName=${rawValue?.uniqueName} keys=${rawValue ? Object.keys(rawValue) : '[]'}`);
|
|
11321
12625
|
if (rawValue && typeof rawValue === 'object' && !(rawValue instanceof BehaviorSubject)) {
|
|
11322
12626
|
// Only hydrate the component BS — do NOT invent a uniqueName if the
|
|
11323
12627
|
// snapshot didn't have one. That would create a false binding on undo.
|
|
@@ -11380,7 +12684,7 @@ Display formula context:
|
|
|
11380
12684
|
}
|
|
11381
12685
|
}
|
|
11382
12686
|
}
|
|
11383
|
-
if (!dsElements.
|
|
12687
|
+
if (!dsElements.some(de => de.uniqueName == v.uniqueName)) {
|
|
11384
12688
|
dsSubs.push(v.component.fetchInfo$);
|
|
11385
12689
|
dsElements.push(dsElement);
|
|
11386
12690
|
if (dsElements.length == dsElementCount) {
|
|
@@ -11881,10 +13185,10 @@ Display formula context:
|
|
|
11881
13185
|
*/
|
|
11882
13186
|
openCurrentDashboardInNewTab() {
|
|
11883
13187
|
const route = String(this.currentDashboardRoute || '').trim();
|
|
11884
|
-
if (!route ||
|
|
13188
|
+
if (!route || globalThis.window === undefined) {
|
|
11885
13189
|
return;
|
|
11886
13190
|
}
|
|
11887
|
-
const base =
|
|
13191
|
+
const base = globalThis.location.href.split('#')[0];
|
|
11888
13192
|
const url = `${base}#/${encodeURIComponent(route)}/dashboard`;
|
|
11889
13193
|
window.open(url, '_blank');
|
|
11890
13194
|
}
|
|
@@ -11904,11 +13208,11 @@ Display formula context:
|
|
|
11904
13208
|
// return customProps;
|
|
11905
13209
|
}
|
|
11906
13210
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, deps: [{ token: i1$1.ActivatedRoute }, { token: DataProviderService }, { token: MetadataProviderService }, { token: UserInfoService }, { token: i0.ChangeDetectorRef }, { token: TranslationManagerService }, { token: MetadataEditorService }, { token: ChatbotHostRegistryService }, { token: WorkflowRuntimeMetadataService }], target: i0.ɵɵFactoryTarget.Component });
|
|
11907
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DesignerComponent, isStandalone: true, selector: "wuic-designer", host: { listeners: { "window:hashchange": "onWindowHashChange()", "window:beforeunload": "onWindowBeforeUnload($event)", "window:keydown": "onWindowKeydown($event)", "window:mouseup": "onWindowMouseup()", "window:touchend": "onWindowTouchend()", "window:keyup": "onWindowKeyup()", "window:change": "onWindowChange()" } }, providers: [TreeDragDropService], ngImport: i0, template: "<!-- <button pButton type=\"button\" (click)=\"loadDashboard()\" class=\"mr-2\">{{'load' | translate}}</button> -->\n@if (!noEdit) {\n<div class=\"designer-layout\">\n <div class=\"designer-toolbar\">\n <div class=\"designer-current-dashboard\">{{ currentDashboardTitle }}</div>\n\n <div class=\"designer-toolbar-spacer\"></div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"undo()\" [disabled]=\"!canUndo\">{{ 'designer.undo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"redo()\" [disabled]=\"!canRedo\">{{ 'designer.redo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"refreshGraphActionsMenuItems(); graphActionsMenu.toggle($event)\">{{ 'designer.actions_menu' | translate }}</button>\n <p-menu #graphActionsMenu [popup]=\"true\" [model]=\"graphActionsMenuItems\" appendTo=\"body\"></p-menu>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"openCurrentDashboardInNewTab()\" [disabled]=\"!currentDashboardRoute\">{{\n 'designer.open_current_dashboard' | translate }}</button>\n </div>\n </div>\n <div class=\"designer-main\">\n <p-splitter [style]=\"{ height: '100%' }\" [panelSizes]=\"[15, 85]\" styleClass=\"designer-main-splitter\">\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[35, 65]\"\n [minSizes]=\"[20, 30]\" styleClass=\"designer-left-splitter\">\n <ng-template pTemplate>\n <div class=\"col flex designer-toolbox palette-panel\">\n <ul class=\"tool-groups\">\n @for (toolGroup of availableToolGroups; track toolGroup.group) {\n <li class=\"tool-group-item\">\n <div class=\"tool-group-title\">{{ toolGroup.group }}</div>\n <ul class=\"tool-group-list\">\n @for (tool of toolGroup.tools; track tool) {\n <li pDraggable (onDragStart)=\"dragStart(tool)\" (onDragEnd)=\"dragEnd()\" (onDrag)=\"onDrag($event)\"\n (dblclick)=\"dropToolAtRoot(tool)\">\n <i [ngClass]=\"tool.icon\" [title]=\"tool.name\"></i>\n </li>\n }\n </ul>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[68, 32]\"\n [minSizes]=\"[35, 20]\" styleClass=\"designer-left-bottom-splitter\">\n <ng-template pTemplate>\n <div class=\"properties-panel\">\n <p-select [style]=\"{width: '100%'}\" [options]=\"flattenedDashboardElements\" [(ngModel)]=\"tool\"\n optionLabel=\"uniqueName\" dataKey=\"componentId\" appendTo=\"body\" />\n <div class=\"designer-toolbox\">\n\n <span style=\"width: 100%;\">\n <input type=\"text\" style=\"width: calc(100% - 30px);\" [(ngModel)]=\"propFilter\"\n [placeholder]=\"'prop.filter' | translate\" />\n <button style=\"width: 25px\" (click)=\"clearPropFilter()\">X</button>\n </span>\n\n @for (section of getToolPropertySections(tool, propFilter); track section.key) {\n <details class=\"prop-section\" [open]=\"section.open\"\n (toggle)=\"onPropertySectionToggle(tool, section.key, $event)\">\n <summary class=\"prop-section-title\">{{ section.label }}</summary>\n <ul class=\"props\">\n @for (toolProp of section.props; track toolProp.key) {\n <li>\n @if (!toolProp.value.hideCaption) {\n <span>{{ toolProp.value?.propertyCaption ||\n toolProp.key[0].toUpperCase() + toolProp.key.slice(1)\n }}</span>\n }\n <br />\n @if (toolProp.value.type == 'button') {\n <p-button (click)=\"toolProp.value.callback(tool.inputs, toolProp.key, null, tool)\"\n class=\"k-button\" [style]=\"{width: '100%'}\">\n {{toolProp.value.propertyCaption}}\n </p-button>\n }\n @if (toolProp.value.type == 'metaEditor' &&\n tool.inputs['componentRef']?.value?.component) {\n <wuic-metadata-editor [hardcodedDatasource]=\"tool.inputs['componentRef']?.value?.component\"\n [saveCallback]=\"metaEditorSave\"></wuic-metadata-editor>\n }\n @if (toolProp.value.type == 'boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"tool.inputs[toolProp.key]\" />\n }\n @if (toolProp.value.type == 'numberToArray') {\n <input style=\"width: 100%;\" type=\"number\" pInputText [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'txt_area') {\n <textarea style=\"width: 100%;\" [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\"></textarea>\n }\n @if (toolProp.value.type == 'string') {\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'color') {\n <p-colorPicker [ngModel]=\"getDesignerColorPickerHex(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorPickerHexChange($event, tool, toolProp)\" appendTo=\"body\" />\n <div class=\"designer-color-alpha-row\">\n <span class=\"designer-color-alpha-label\">{{ 'designer.alpha' | translate }}</span>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n </div>\n <input type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'dictionary' && !toolProp.value.async) {\n @if (toolProp.key == 'cssFile') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"availableProjectCssFiles\"\n [ngModel]=\"tool.inputs[toolProp.key]\" (ngModelChange)=\"onCssFileChanged($event, tool)\" />\n } @else if (toolProp.key == 'cssClass') {\n <p-multiSelect [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getCssClassOptionsForTool(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"onCssClassChanged($event, tool)\" [filter]=\"true\" [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"38\" scrollHeight=\"260px\" [showToggleAll]=\"false\" display=\"chip\"\n [maxSelectedLabels]=\"3\" [selectedItemsLabel]=\"'{0} selected'\">\n </p-multiSelect>\n } @else if ((tool.name == 'SELECT' || tool.name == 'MULTISELECT' || tool.name == 'UL') &&\n (toolProp.key == 'valueField' || toolProp.key == 'textField')) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else if (tool.name == 'LABEL' && toolProp.key == 'displayField') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n }\n @if (toolProp.value.type == 'dictionary' && toolProp.value.async) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (tool.name == 'DATAREPEATER' && toolProp.key == 'action' &&\n canOpenArchetypeConfigurator(tool)) {\n <button style=\"margin-top: 8px;\" (click)=\"openArchetypeConfigurator(tool)\">\n {{ 'designer.configure' | translate }} {{ (tool.inputs['action'] | async) || '' }}\n </button>\n }\n @if (toolProp.value.type == 'dropped-component-list') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [placeholder]=\"'select.component' | translate\" [options]=\"elementFilter(toolProp)\"\n [ngModel]=\"getDroppedComponentListModel(tool, toolProp)\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" optionLabel=\"uniqueName\"\n dataKey=\"uniqueName\" />\n }\n @if (toolProp.value.type == 'autocomplete') {\n <p-autoComplete [style]=\"{width: '100%'}\" appendTo=\"body\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\" [forceSelection]=\"true\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" [suggestions]=\"tool.suggestions\"\n [optionLabel]=\"toolProp.value.displayField\" [optionValue]=\"toolProp.value.valueField\"\n (completeMethod)=\"search($event, tool, toolProp)\" [dropdown]=\"true\" dropdownMode=\"current\"\n [multiple]=\"toolProp.value.multiple\" />\n }\n @if (toolProp.value.type == 'propertyTree' && shouldShowPropertyTree(tool)) {\n <ul>\n @for (item of $any(tool.inputs[toolProp.key] | async); track item) {\n <li style=\"padding: 0; margin: 0;\">\n @if (item.col && tool.inputs['customProps_' + (tool.inputs['action'] | async)])\n {\n <wuic-field-editor [field]=\"item.col\"\n [record]=\"tool.inputs['customProps_' + (tool.inputs['action'] | async)]\"\n [metaInfo]=\"tool.inputs['datasource']?.component?.value?.metaInfo\"\n [triggerProp]=\"tool.inputs['action']\"></wuic-field-editor>\n }\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n </details>\n }\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"hierarchy-panel\">\n <div class=\"hierarchy-panel-title\">{{ 'designer.canvas_hierarchy' | translate }}</div>\n <div class=\"hierarchy-tree-wrap\">\n <p-tree [value]=\"hierarchyTreeNodes\" [draggableNodes]=\"true\" [droppableNodes]=\"true\"\n selectionMode=\"single\" [(selection)]=\"hierarchySelection\"\n (onNodeSelect)=\"onHierarchyNodeSelect($event)\" draggableScope=\"designer-hierarchy\"\n droppableScope=\"designer-hierarchy\" (onNodeDrop)=\"onHierarchyNodeDrop($event)\">\n <ng-template let-node pTemplate=\"default\">\n <span class=\"hierarchy-node-label\" [class.active]=\"isHierarchyNodeSelected(node)\"\n (mouseenter)=\"onHierarchyNodeMouseEnter(node)\" (mouseleave)=\"onHierarchyNodeMouseLeave()\"\n (contextmenu)=\"showHierarchyNodeContextMenu($event, node, hierarchyCtxMenu)\">\n {{ node.label }}\n </span>\n </ng-template>\n </p-tree>\n </div>\n <p-contextMenu #hierarchyCtxMenu [model]=\"hierarchyCtxItems\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </ng-template>\n </p-splitter>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"col dashboard-design\" [class.designer-hide-datasources]=\"hideDatasourcesInCanvas\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n <p-contextMenu [target]=\"canvas\" [model]=\"ctxItems | async\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </div>\n <div class=\"designer-footer\">\n <div class=\"designer-footer-label\">{{ 'designer.items' | translate }}:</div>\n <div class=\"designer-footer-list\">\n @for (item of droppedDashboardItems; track item.uniqueName) {\n <button type=\"button\" class=\"designer-footer-item\" [class.active]=\"tool?.uniqueName === item?.uniqueName\"\n [attr.data-designer-unique-name]=\"item?.uniqueName\" (mouseenter)=\"setFooterHoverHighlight(item)\"\n (mouseleave)=\"clearFooterHoverHighlight()\" (click)=\"selectDashboardItem(item)\"\n (contextmenu)=\"showFooterContextMenu($event, item, footerCtxMenu)\">\n {{ getDroppedDashboardItemLabel(item) }}\n </button>\n }\n </div>\n <p-contextMenu #footerCtxMenu [model]=\"footerCtxItems\" appendTo=\"body\" />\n </div>\n</div>\n}\n\n@if (noEdit) {\n<wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n}\n\n<wuic-archetype-configurator [visible]=\"showArchetypeConfigurator\"\n (visibleChange)=\"onArchetypeConfigVisibleChange($event)\" [archetype]=\"getActiveArchetypeConfig()\"\n [metaInfo]=\"getArchetypeConfigMetaInfoForTool(archetypeConfigTool)\"\n [value]=\"getArchetypeConfigValueForTool(archetypeConfigTool)\" (applyConfig)=\"onArchetypeConfigApply($event)\">\n</wuic-archetype-configurator>\n\n<wuic-css-sheet-editor [visible]=\"showCssSheetEditor\" (visibleChange)=\"onCssSheetEditorVisibleChange($event)\"\n [sheets]=\"dashboardCssSheets\" [selectedSheetPath]=\"selectedCssEditorSheetPath\" [assetsFolder]=\"currentDashboardRoute\"\n [styleProps]=\"getCssEditorStyleProps()\" (applyEditor)=\"onCssSheetEditorApply($event)\">\n</wuic-css-sheet-editor>\n\n<p-dialog [header]=\"'designer.reopen_dashboard' | translate\" [(visible)]=\"reopenDashboardDialogVisible\" [modal]=\"true\"\n [closable]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [appendTo]=\"'body'\" [style]=\"{ width: '34rem' }\"\n (onHide)=\"cancelReopenDashboardDialog()\">\n <div class=\"route-popup-form\">\n <label for=\"designer-reopen-select\">{{ 'designer.existing_dashboards' | translate }}</label>\n <p-select inputId=\"designer-reopen-select\" [options]=\"savedDashboardOptions\" optionLabel=\"label\" optionValue=\"value\"\n [(ngModel)]=\"reopenDialogSelectedDashboardRoute\" [filter]=\"true\" [showClear]=\"true\" [style]=\"{ width: '100%' }\"\n appendTo=\"body\" />\n\n <div class=\"route-popup-actions\">\n <button type=\"button\" class=\"route-popup-btn\" (click)=\"cancelReopenDashboardDialog()\">{{ 'cancel' | translate\n }}</button>\n <button type=\"button\" class=\"route-popup-btn route-popup-btn-primary\" (click)=\"confirmReopenDashboardDialog()\"\n [disabled]=\"!reopenDialogSelectedDashboardRoute\">{{ 'designer.reopen' | translate }}</button>\n </div>\n </div>\n</p-dialog>", styles: [":host{display:block;width:100%;height:100%;min-width:0}.designer-layout{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%;min-height:0;width:100%;min-width:0}.designer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;border:1px solid inherit;border-radius:10px;background:transparent;padding:10px 12px;margin-bottom:8px}.designer-current-dashboard{min-width:220px;font-size:.86rem;font-weight:700;color:inherit}.designer-toolbar-spacer{flex:1 1 auto;min-width:16px}.designer-toolbar-menu-group{display:inline-flex}.designer-toolbar-menu-group button{height:32px;border:1px solid #2f67d8;border-radius:6px;background:#2f67d8;color:#fff;padding:0 10px;font-size:.82rem;cursor:pointer}.designer-toolbar-menu-group button[disabled]{opacity:.6;cursor:not-allowed}.designer-main{min-height:0;min-width:0;width:100%;overflow:hidden;direction:ltr}.designer-main-splitter{height:100%;width:100%;min-width:0;direction:ltr}.designer-toolbox{font-size:3rem}.designer-toolbox ul{list-style-type:none;padding:0;margin:0;width:100%}.designer-toolbox li{float:left;margin-left:5px;margin-top:5px}.designer-toolbox .tool-groups{display:block}.designer-toolbox .tool-group-item{float:none;margin-left:0;margin-top:10px;clear:both}.designer-toolbox .tool-group-title{font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;opacity:.7;margin:0 0 4px 6px}.designer-toolbox .tool-group-list{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.designer-toolbox .tool-group-list li{float:none;margin-left:0;margin-top:0}.designer-toolbox .props{font-size:1rem}.designer-toolbox .props li{float:none;padding-bottom:15px}.designer-toolbox .prop-section{width:100%;border:1px solid inherit;border-radius:6px;margin-top:8px;background:transparent;padding:6px 8px 2px}.designer-toolbox .prop-section-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;cursor:pointer;color:inherit;margin-bottom:6px}.palette-panel{height:100%;overflow:auto;width:100%}.designer-color-alpha-row{display:flex;align-items:center;gap:8px;margin-top:6px;margin-bottom:6px}.designer-color-alpha-label{font-size:.8rem;min-width:40px}.designer-color-alpha-row input[type=range]{flex:1 1 auto;min-width:90px}.designer-color-alpha-row input[type=number]{width:72px}.properties-panel{height:100%;overflow:hidden;width:100%;display:flex;flex-direction:column;min-height:0;padding-right:10px;padding-left:10px;padding-top:10px}.properties-panel>.designer-toolbox{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable}:host ::ng-deep .designer-left-bottom-splitter .p-splitterpanel{min-height:0!important;overflow:hidden!important}.hierarchy-panel{height:100%;width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;padding:6px}.hierarchy-panel-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#4b5563;margin:0 0 6px}.hierarchy-tree-wrap{flex:1 1 auto;min-height:0;border:1px solid inherit;border-radius:6px;background:transparent;overflow:auto;padding:4px}:host ::ng-deep .hierarchy-tree-wrap .p-tree{border:none;padding:0;height:100%;background:transparent}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content{border:1px solid transparent;border-radius:4px;background:transparent!important;color:inherit!important;padding:0!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight{border-color:transparent!important;background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content:hover{background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label{display:inline-block;border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label:hover{border-color:#94a3b8}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label.active{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight .hierarchy-node-label{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}.designer-hierarchy-node{cursor:pointer;border:1px solid transparent;border-radius:4px;padding:2px 6px;font-size:.82rem;line-height:1.25rem}.designer-hierarchy-node-label{cursor:pointer;display:inline-block;width:100%}.designer-hierarchy-node.active{border-color:#3b82f6;background:#eff6ff;color:#1d4ed8}.designer-footer-item.designer-tree-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px}.dashboard-design{height:100%;min-height:0;min-width:0;width:100%;overflow:auto;direction:ltr}:host ::ng-deep .dashboard-design.designer-hide-datasources .datasource-descriptor{display:none!important}.current-dashboard-title{font-weight:600;margin-right:12px}.designer-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid inherit;background:transparent;min-height:36px;flex:0 0 auto;width:100%;overflow:auto}.designer-footer-label{font-size:.85rem;font-weight:600;color:inherit;white-space:nowrap}.designer-footer-list{display:flex;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;width:100%}.designer-footer-item{border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer;white-space:nowrap}.designer-footer-item.active{border-color:#3b82f6;background:transparent;color:#1d4ed8}:host ::ng-deep .designer-footer-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px;box-shadow:inset 0 0 0 1px #3b82f6!important}.route-popup-form{display:flex;flex-direction:column;gap:8px}.route-popup-form label{font-size:.85rem;font-weight:600;color:#2f3f58}.route-popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.route-popup-btn{border:1px solid inherit;background:transparent;color:inherit;border-radius:6px;padding:7px 12px;font-size:.85rem;cursor:pointer}.route-popup-btn-primary{border-color:#2f67d8;background:#2f67d8;color:#fff}.p-colorpicker{border:1px solid black;border-radius:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SplitterModule }, { kind: "component", type: i10.Splitter, selector: "p-splitter", inputs: ["styleClass", "panelStyleClass", "panelStyle", "stateStorage", "stateKey", "layout", "gutterSize", "step", "minSizes", "panelSizes"], outputs: ["onResizeEnd", "onResizeStart"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i12.Draggable, selector: "[pDraggable]", inputs: ["pDraggable", "dragEffect", "dragHandle", "pDraggableDisabled"], outputs: ["onDragStart", "onDragEnd", "onDrag"] }, { kind: "directive", type: i12.Droppable, selector: "[pDroppable]", inputs: ["pDroppable", "pDroppableDisabled", "dropEffect"], outputs: ["onDragEnter", "onDragLeave", "onDrop"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i6.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ColorPickerModule }, { kind: "component", type: i2$1.ColorPicker, selector: "p-colorPicker, p-colorpicker, p-color-picker", inputs: ["styleClass", "showTransitionOptions", "hideTransitionOptions", "inline", "format", "tabindex", "inputId", "autoZIndex", "autofocus", "defaultColor", "appendTo", "overlayOptions", "motionOptions"], outputs: ["onChange", "onShow", "onHide"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i2$2.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "component", type: DashboardComponent, selector: "wuic-dashboard", inputs: ["dashboardElements"] }, { kind: "component", type: FieldEditorComponent, selector: "wuic-field-editor", inputs: ["datasource", "record", "field", "metaInfo", "readOnly", "isFilter", "hideLabel", "forceShowLabel", "operator", "nestedIndex", "triggerProp", "tabIndex", "onInlineCellValueChange"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: MetadataEditorComponent, selector: "wuic-metadata-editor", inputs: ["datasource", "hardcodedDatasource", "saveCallback", "hideReports", "hideRelatedTableActions", "hideRelatedColumnActions"] }, { kind: "ngmodule", type: MenubarModule }, { kind: "component", type: ArchetypeConfiguratorComponent, selector: "wuic-archetype-configurator", inputs: ["visible", "archetype", "metaInfo", "value"], outputs: ["visibleChange", "applyConfig"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i18.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "component", type: CssSheetEditorComponent, selector: "wuic-css-sheet-editor", inputs: ["visible", "sheets", "selectedSheetPath", "styleProps", "assetsFolder"], outputs: ["visibleChange", "applyEditor"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i9.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: TreeModule }, { kind: "component", type: i4$1.Tree, selector: "p-tree", inputs: ["value", "selectionMode", "loadingMode", "selection", "styleClass", "contextMenu", "contextMenuSelectionMode", "contextMenuSelection", "draggableScope", "droppableScope", "draggableNodes", "droppableNodes", "metaKeySelection", "propagateSelectionUp", "propagateSelectionDown", "loading", "loadingIcon", "emptyMessage", "ariaLabel", "togglerAriaLabel", "ariaLabelledBy", "validateDrop", "filter", "filterInputAutoFocus", "filterBy", "filterMode", "filterOptions", "filterPlaceholder", "filteredNodes", "filterLocale", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "indentation", "_templateMap", "trackBy", "highlightOnSelect"], outputs: ["selectionChange", "contextMenuSelectionChange", "onNodeSelect", "onNodeUnselect", "onNodeExpand", "onNodeCollapse", "onNodeContextMenuSelect", "onNodeDoubleClick", "onNodeDrop", "onLazyLoad", "onScroll", "onScrollIndexChange", "onFilter"] }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
|
|
13211
|
+
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], viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true }], ngImport: i0, template: "@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\" data-testid=\"designer-actions-menu-btn\" (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 <!-- updateOn:blur come il txt_area sopra: senza, setValue() scatta\n A OGNI CARATTERE e buildSerializedDashboardElements ri-serializza\n l'intero tree (history da centinaia di KB per tasto, P-D1). -->\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\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\"\n [class.designer-snap-grid]=\"snapToGridEnabled\" [style.--wuic-grid-size.px]=\"gridSize\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (mousedown)=\"onCanvasMouseDown($event)\"\n (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n @if (rubberBandVisible) {\n <div class=\"designer-selection-rect\" [style.left]=\"rubberBandStyle.left\" [style.top]=\"rubberBandStyle.top\"\n [style.width]=\"rubberBandStyle.width\" [style.height]=\"rubberBandStyle.height\"></div>\n }\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\" [widgetChrome]=\"dashboardWidgetChrome\"></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;position:relative}.designer-selection-rect{position:absolute;border:1px dashed #3b82f6;background:#3b82f61f;pointer-events:none;z-index:10000}:host ::ng-deep .designer-multi-selected{outline:2px solid #3b82f6!important;outline-offset:1px}: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}.dashboard-design.designer-snap-grid{background-image:linear-gradient(to right,rgba(128,128,128,.16) 1px,transparent 1px),linear-gradient(to bottom,rgba(128,128,128,.16) 1px,transparent 1px);background-size:var(--wuic-grid-size, 10px) var(--wuic-grid-size, 10px)}\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: i5.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: i6.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$1.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", "widgetChrome"] }, { 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: i7.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" }] });
|
|
11908
13212
|
}
|
|
11909
13213
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DesignerComponent, decorators: [{
|
|
11910
13214
|
type: Component,
|
|
11911
|
-
args: [{ selector: 'wuic-designer', imports: [NgClass, TranslateModule, AsyncPipe, FormsModule, SplitterModule, DragDropModule, SelectModule, ColorPickerModule, AutoCompleteModule, DashboardComponent, FieldEditorComponent, ContextMenuModule, MetadataEditorComponent, MenubarModule, ArchetypeConfiguratorComponent, ButtonModule, MultiSelectModule, CssSheetEditorComponent, MenuModule, DialogModule, TreeModule], providers: [TreeDragDropService], template: "<!-- <button pButton type=\"button\" (click)=\"loadDashboard()\" class=\"mr-2\">{{'load' | translate}}</button> -->\n@if (!noEdit) {\n<div class=\"designer-layout\">\n <div class=\"designer-toolbar\">\n <div class=\"designer-current-dashboard\">{{ currentDashboardTitle }}</div>\n\n <div class=\"designer-toolbar-spacer\"></div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"undo()\" [disabled]=\"!canUndo\">{{ 'designer.undo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"redo()\" [disabled]=\"!canRedo\">{{ 'designer.redo' | translate }}</button>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"refreshGraphActionsMenuItems(); graphActionsMenu.toggle($event)\">{{ 'designer.actions_menu' | translate }}</button>\n <p-menu #graphActionsMenu [popup]=\"true\" [model]=\"graphActionsMenuItems\" appendTo=\"body\"></p-menu>\n </div>\n\n <div class=\"designer-toolbar-menu-group\">\n <button type=\"button\" (click)=\"openCurrentDashboardInNewTab()\" [disabled]=\"!currentDashboardRoute\">{{\n 'designer.open_current_dashboard' | translate }}</button>\n </div>\n </div>\n <div class=\"designer-main\">\n <p-splitter [style]=\"{ height: '100%' }\" [panelSizes]=\"[15, 85]\" styleClass=\"designer-main-splitter\">\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[35, 65]\"\n [minSizes]=\"[20, 30]\" styleClass=\"designer-left-splitter\">\n <ng-template pTemplate>\n <div class=\"col flex designer-toolbox palette-panel\">\n <ul class=\"tool-groups\">\n @for (toolGroup of availableToolGroups; track toolGroup.group) {\n <li class=\"tool-group-item\">\n <div class=\"tool-group-title\">{{ toolGroup.group }}</div>\n <ul class=\"tool-group-list\">\n @for (tool of toolGroup.tools; track tool) {\n <li pDraggable (onDragStart)=\"dragStart(tool)\" (onDragEnd)=\"dragEnd()\" (onDrag)=\"onDrag($event)\"\n (dblclick)=\"dropToolAtRoot(tool)\">\n <i [ngClass]=\"tool.icon\" [title]=\"tool.name\"></i>\n </li>\n }\n </ul>\n </li>\n }\n </ul>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <p-splitter layout=\"vertical\" [style]=\"{ height: '100%', width: '100%' }\" [panelSizes]=\"[68, 32]\"\n [minSizes]=\"[35, 20]\" styleClass=\"designer-left-bottom-splitter\">\n <ng-template pTemplate>\n <div class=\"properties-panel\">\n <p-select [style]=\"{width: '100%'}\" [options]=\"flattenedDashboardElements\" [(ngModel)]=\"tool\"\n optionLabel=\"uniqueName\" dataKey=\"componentId\" appendTo=\"body\" />\n <div class=\"designer-toolbox\">\n\n <span style=\"width: 100%;\">\n <input type=\"text\" style=\"width: calc(100% - 30px);\" [(ngModel)]=\"propFilter\"\n [placeholder]=\"'prop.filter' | translate\" />\n <button style=\"width: 25px\" (click)=\"clearPropFilter()\">X</button>\n </span>\n\n @for (section of getToolPropertySections(tool, propFilter); track section.key) {\n <details class=\"prop-section\" [open]=\"section.open\"\n (toggle)=\"onPropertySectionToggle(tool, section.key, $event)\">\n <summary class=\"prop-section-title\">{{ section.label }}</summary>\n <ul class=\"props\">\n @for (toolProp of section.props; track toolProp.key) {\n <li>\n @if (!toolProp.value.hideCaption) {\n <span>{{ toolProp.value?.propertyCaption ||\n toolProp.key[0].toUpperCase() + toolProp.key.slice(1)\n }}</span>\n }\n <br />\n @if (toolProp.value.type == 'button') {\n <p-button (click)=\"toolProp.value.callback(tool.inputs, toolProp.key, null, tool)\"\n class=\"k-button\" [style]=\"{width: '100%'}\">\n {{toolProp.value.propertyCaption}}\n </p-button>\n }\n @if (toolProp.value.type == 'metaEditor' &&\n tool.inputs['componentRef']?.value?.component) {\n <wuic-metadata-editor [hardcodedDatasource]=\"tool.inputs['componentRef']?.value?.component\"\n [saveCallback]=\"metaEditorSave\"></wuic-metadata-editor>\n }\n @if (toolProp.value.type == 'boolean') {\n <input type=\"checkbox\" [(ngModel)]=\"tool.inputs[toolProp.key]\" />\n }\n @if (toolProp.value.type == 'numberToArray') {\n <input style=\"width: 100%;\" type=\"number\" pInputText [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'txt_area') {\n <textarea style=\"width: 100%;\" [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\"></textarea>\n }\n @if (toolProp.value.type == 'string') {\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'color') {\n <p-colorPicker [ngModel]=\"getDesignerColorPickerHex(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorPickerHexChange($event, tool, toolProp)\" appendTo=\"body\" />\n <div class=\"designer-color-alpha-row\">\n <span class=\"designer-color-alpha-label\">{{ 'designer.alpha' | translate }}</span>\n <input type=\"range\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n <input type=\"number\" min=\"0\" max=\"1\" step=\"0.01\"\n [ngModel]=\"getDesignerColorAlpha(tool, toolProp)\"\n (ngModelChange)=\"onDesignerColorAlphaChange($event, tool, toolProp)\" />\n </div>\n <input type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (toolProp.value.type == 'dictionary' && !toolProp.value.async) {\n @if (toolProp.key == 'cssFile') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"availableProjectCssFiles\"\n [ngModel]=\"tool.inputs[toolProp.key]\" (ngModelChange)=\"onCssFileChanged($event, tool)\" />\n } @else if (toolProp.key == 'cssClass') {\n <p-multiSelect [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getCssClassOptionsForTool(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"onCssClassChanged($event, tool)\" [filter]=\"true\" [virtualScroll]=\"true\"\n [virtualScrollItemSize]=\"38\" scrollHeight=\"260px\" [showToggleAll]=\"false\" display=\"chip\"\n [maxSelectedLabels]=\"3\" [selectedItemsLabel]=\"'{0} selected'\">\n </p-multiSelect>\n } @else if ((tool.name == 'SELECT' || tool.name == 'MULTISELECT' || tool.name == 'UL') &&\n (toolProp.key == 'valueField' || toolProp.key == 'textField')) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else if (tool.name == 'LABEL' && toolProp.key == 'displayField') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [options]=\"getDatasourceColumnOptions(tool)\" [ngModel]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n } @else {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [(ngModel)]=\"tool.inputs[toolProp.key]\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n }\n @if (toolProp.value.type == 'dictionary' && toolProp.value.async) {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\" [options]=\"toolProp.value.values\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" />\n }\n @if (tool.name == 'DATAREPEATER' && toolProp.key == 'action' &&\n canOpenArchetypeConfigurator(tool)) {\n <button style=\"margin-top: 8px;\" (click)=\"openArchetypeConfigurator(tool)\">\n {{ 'designer.configure' | translate }} {{ (tool.inputs['action'] | async) || '' }}\n </button>\n }\n @if (toolProp.value.type == 'dropped-component-list') {\n <p-select [style]=\"{width: '100%'}\" appendTo=\"body\"\n [placeholder]=\"'select.component' | translate\" [options]=\"elementFilter(toolProp)\"\n [ngModel]=\"getDroppedComponentListModel(tool, toolProp)\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" optionLabel=\"uniqueName\"\n dataKey=\"uniqueName\" />\n }\n @if (toolProp.value.type == 'autocomplete') {\n <p-autoComplete [style]=\"{width: '100%'}\" appendTo=\"body\"\n [ngModel]=\"tool.inputs[toolProp.key] | async\" [forceSelection]=\"true\"\n (ngModelChange)=\"setValue($event, tool, toolProp)\" [suggestions]=\"tool.suggestions\"\n [optionLabel]=\"toolProp.value.displayField\" [optionValue]=\"toolProp.value.valueField\"\n (completeMethod)=\"search($event, tool, toolProp)\" [dropdown]=\"true\" dropdownMode=\"current\"\n [multiple]=\"toolProp.value.multiple\" />\n }\n @if (toolProp.value.type == 'propertyTree' && shouldShowPropertyTree(tool)) {\n <ul>\n @for (item of $any(tool.inputs[toolProp.key] | async); track item) {\n <li style=\"padding: 0; margin: 0;\">\n @if (item.col && tool.inputs['customProps_' + (tool.inputs['action'] | async)])\n {\n <wuic-field-editor [field]=\"item.col\"\n [record]=\"tool.inputs['customProps_' + (tool.inputs['action'] | async)]\"\n [metaInfo]=\"tool.inputs['datasource']?.component?.value?.metaInfo\"\n [triggerProp]=\"tool.inputs['action']\"></wuic-field-editor>\n }\n </li>\n }\n </ul>\n }\n </li>\n }\n </ul>\n </details>\n }\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"hierarchy-panel\">\n <div class=\"hierarchy-panel-title\">{{ 'designer.canvas_hierarchy' | translate }}</div>\n <div class=\"hierarchy-tree-wrap\">\n <p-tree [value]=\"hierarchyTreeNodes\" [draggableNodes]=\"true\" [droppableNodes]=\"true\"\n selectionMode=\"single\" [(selection)]=\"hierarchySelection\"\n (onNodeSelect)=\"onHierarchyNodeSelect($event)\" draggableScope=\"designer-hierarchy\"\n droppableScope=\"designer-hierarchy\" (onNodeDrop)=\"onHierarchyNodeDrop($event)\">\n <ng-template let-node pTemplate=\"default\">\n <span class=\"hierarchy-node-label\" [class.active]=\"isHierarchyNodeSelected(node)\"\n (mouseenter)=\"onHierarchyNodeMouseEnter(node)\" (mouseleave)=\"onHierarchyNodeMouseLeave()\"\n (contextmenu)=\"showHierarchyNodeContextMenu($event, node, hierarchyCtxMenu)\">\n {{ node.label }}\n </span>\n </ng-template>\n </p-tree>\n </div>\n <p-contextMenu #hierarchyCtxMenu [model]=\"hierarchyCtxItems\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </ng-template>\n </p-splitter>\n </ng-template>\n <ng-template pTemplate>\n <div class=\"col dashboard-design\" [class.designer-hide-datasources]=\"hideDatasourcesInCanvas\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n <p-contextMenu [target]=\"canvas\" [model]=\"ctxItems | async\" appendTo=\"body\" />\n </div>\n </ng-template>\n </p-splitter>\n </div>\n <div class=\"designer-footer\">\n <div class=\"designer-footer-label\">{{ 'designer.items' | translate }}:</div>\n <div class=\"designer-footer-list\">\n @for (item of droppedDashboardItems; track item.uniqueName) {\n <button type=\"button\" class=\"designer-footer-item\" [class.active]=\"tool?.uniqueName === item?.uniqueName\"\n [attr.data-designer-unique-name]=\"item?.uniqueName\" (mouseenter)=\"setFooterHoverHighlight(item)\"\n (mouseleave)=\"clearFooterHoverHighlight()\" (click)=\"selectDashboardItem(item)\"\n (contextmenu)=\"showFooterContextMenu($event, item, footerCtxMenu)\">\n {{ getDroppedDashboardItemLabel(item) }}\n </button>\n }\n </div>\n <p-contextMenu #footerCtxMenu [model]=\"footerCtxItems\" appendTo=\"body\" />\n </div>\n</div>\n}\n\n@if (noEdit) {\n<wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n}\n\n<wuic-archetype-configurator [visible]=\"showArchetypeConfigurator\"\n (visibleChange)=\"onArchetypeConfigVisibleChange($event)\" [archetype]=\"getActiveArchetypeConfig()\"\n [metaInfo]=\"getArchetypeConfigMetaInfoForTool(archetypeConfigTool)\"\n [value]=\"getArchetypeConfigValueForTool(archetypeConfigTool)\" (applyConfig)=\"onArchetypeConfigApply($event)\">\n</wuic-archetype-configurator>\n\n<wuic-css-sheet-editor [visible]=\"showCssSheetEditor\" (visibleChange)=\"onCssSheetEditorVisibleChange($event)\"\n [sheets]=\"dashboardCssSheets\" [selectedSheetPath]=\"selectedCssEditorSheetPath\" [assetsFolder]=\"currentDashboardRoute\"\n [styleProps]=\"getCssEditorStyleProps()\" (applyEditor)=\"onCssSheetEditorApply($event)\">\n</wuic-css-sheet-editor>\n\n<p-dialog [header]=\"'designer.reopen_dashboard' | translate\" [(visible)]=\"reopenDashboardDialogVisible\" [modal]=\"true\"\n [closable]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [appendTo]=\"'body'\" [style]=\"{ width: '34rem' }\"\n (onHide)=\"cancelReopenDashboardDialog()\">\n <div class=\"route-popup-form\">\n <label for=\"designer-reopen-select\">{{ 'designer.existing_dashboards' | translate }}</label>\n <p-select inputId=\"designer-reopen-select\" [options]=\"savedDashboardOptions\" optionLabel=\"label\" optionValue=\"value\"\n [(ngModel)]=\"reopenDialogSelectedDashboardRoute\" [filter]=\"true\" [showClear]=\"true\" [style]=\"{ width: '100%' }\"\n appendTo=\"body\" />\n\n <div class=\"route-popup-actions\">\n <button type=\"button\" class=\"route-popup-btn\" (click)=\"cancelReopenDashboardDialog()\">{{ 'cancel' | translate\n }}</button>\n <button type=\"button\" class=\"route-popup-btn route-popup-btn-primary\" (click)=\"confirmReopenDashboardDialog()\"\n [disabled]=\"!reopenDialogSelectedDashboardRoute\">{{ 'designer.reopen' | translate }}</button>\n </div>\n </div>\n</p-dialog>", styles: [":host{display:block;width:100%;height:100%;min-width:0}.designer-layout{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%;min-height:0;width:100%;min-width:0}.designer-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;border:1px solid inherit;border-radius:10px;background:transparent;padding:10px 12px;margin-bottom:8px}.designer-current-dashboard{min-width:220px;font-size:.86rem;font-weight:700;color:inherit}.designer-toolbar-spacer{flex:1 1 auto;min-width:16px}.designer-toolbar-menu-group{display:inline-flex}.designer-toolbar-menu-group button{height:32px;border:1px solid #2f67d8;border-radius:6px;background:#2f67d8;color:#fff;padding:0 10px;font-size:.82rem;cursor:pointer}.designer-toolbar-menu-group button[disabled]{opacity:.6;cursor:not-allowed}.designer-main{min-height:0;min-width:0;width:100%;overflow:hidden;direction:ltr}.designer-main-splitter{height:100%;width:100%;min-width:0;direction:ltr}.designer-toolbox{font-size:3rem}.designer-toolbox ul{list-style-type:none;padding:0;margin:0;width:100%}.designer-toolbox li{float:left;margin-left:5px;margin-top:5px}.designer-toolbox .tool-groups{display:block}.designer-toolbox .tool-group-item{float:none;margin-left:0;margin-top:10px;clear:both}.designer-toolbox .tool-group-title{font-size:.8rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;opacity:.7;margin:0 0 4px 6px}.designer-toolbox .tool-group-list{display:flex;flex-wrap:wrap;align-items:flex-start;gap:4px}.designer-toolbox .tool-group-list li{float:none;margin-left:0;margin-top:0}.designer-toolbox .props{font-size:1rem}.designer-toolbox .props li{float:none;padding-bottom:15px}.designer-toolbox .prop-section{width:100%;border:1px solid inherit;border-radius:6px;margin-top:8px;background:transparent;padding:6px 8px 2px}.designer-toolbox .prop-section-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;cursor:pointer;color:inherit;margin-bottom:6px}.palette-panel{height:100%;overflow:auto;width:100%}.designer-color-alpha-row{display:flex;align-items:center;gap:8px;margin-top:6px;margin-bottom:6px}.designer-color-alpha-label{font-size:.8rem;min-width:40px}.designer-color-alpha-row input[type=range]{flex:1 1 auto;min-width:90px}.designer-color-alpha-row input[type=number]{width:72px}.properties-panel{height:100%;overflow:hidden;width:100%;display:flex;flex-direction:column;min-height:0;padding-right:10px;padding-left:10px;padding-top:10px}.properties-panel>.designer-toolbox{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable}:host ::ng-deep .designer-left-bottom-splitter .p-splitterpanel{min-height:0!important;overflow:hidden!important}.hierarchy-panel{height:100%;width:100%;min-height:0;display:flex;flex-direction:column;overflow:hidden;padding:6px}.hierarchy-panel-title{font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:#4b5563;margin:0 0 6px}.hierarchy-tree-wrap{flex:1 1 auto;min-height:0;border:1px solid inherit;border-radius:6px;background:transparent;overflow:auto;padding:4px}:host ::ng-deep .hierarchy-tree-wrap .p-tree{border:none;padding:0;height:100%;background:transparent}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content{border:1px solid transparent;border-radius:4px;background:transparent!important;color:inherit!important;padding:0!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight{border-color:transparent!important;background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content:hover{background:transparent!important;color:inherit!important}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label{display:inline-block;border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label:hover{border-color:#94a3b8}:host ::ng-deep .hierarchy-tree-wrap .hierarchy-node-label.active{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}:host ::ng-deep .hierarchy-tree-wrap .p-tree-node-content.p-highlight .hierarchy-node-label{border-color:#3b82f6!important;background:#eff6ff!important;color:#1d4ed8!important}.designer-hierarchy-node{cursor:pointer;border:1px solid transparent;border-radius:4px;padding:2px 6px;font-size:.82rem;line-height:1.25rem}.designer-hierarchy-node-label{cursor:pointer;display:inline-block;width:100%}.designer-hierarchy-node.active{border-color:#3b82f6;background:#eff6ff;color:#1d4ed8}.designer-footer-item.designer-tree-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px}.dashboard-design{height:100%;min-height:0;min-width:0;width:100%;overflow:auto;direction:ltr}:host ::ng-deep .dashboard-design.designer-hide-datasources .datasource-descriptor{display:none!important}.current-dashboard-title{font-weight:600;margin-right:12px}.designer-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid inherit;background:transparent;min-height:36px;flex:0 0 auto;width:100%;overflow:auto}.designer-footer-label{font-size:.85rem;font-weight:600;color:inherit;white-space:nowrap}.designer-footer-list{display:flex;align-items:center;gap:6px;overflow-x:auto;overflow-y:hidden;white-space:nowrap;width:100%}.designer-footer-item{border:1px solid inherit;background:transparent;color:inherit;border-radius:4px;padding:2px 8px;font-size:.8rem;line-height:1.25rem;cursor:pointer;white-space:nowrap}.designer-footer-item.active{border-color:#3b82f6;background:transparent;color:#1d4ed8}:host ::ng-deep .designer-footer-hover-target{border-color:#3b82f6!important;outline:1px solid #3b82f6!important;outline-offset:-1px;box-shadow:inset 0 0 0 1px #3b82f6!important}.route-popup-form{display:flex;flex-direction:column;gap:8px}.route-popup-form label{font-size:.85rem;font-weight:600;color:#2f3f58}.route-popup-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}.route-popup-btn{border:1px solid inherit;background:transparent;color:inherit;border-radius:6px;padding:7px 12px;font-size:.85rem;cursor:pointer}.route-popup-btn-primary{border-color:#2f67d8;background:#2f67d8;color:#fff}.p-colorpicker{border:1px solid black;border-radius:4px}\n"] }]
|
|
13215
|
+
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: "@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\" data-testid=\"designer-actions-menu-btn\" (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 <!-- updateOn:blur come il txt_area sopra: senza, setValue() scatta\n A OGNI CARATTERE e buildSerializedDashboardElements ri-serializza\n l'intero tree (history da centinaia di KB per tasto, P-D1). -->\n <input style=\"width: 100%;\" type=\"text\" pInputText [(ngModel)]=\"tool.inputs[toolProp.key]\"\n [ngModelOptions]=\"{ updateOn: 'blur' }\"\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\"\n [class.designer-snap-grid]=\"snapToGridEnabled\" [style.--wuic-grid-size.px]=\"gridSize\" pDroppable\n (onDrop)=\"drop($event)\" #canvas (mousedown)=\"onCanvasMouseDown($event)\"\n (contextmenu)=\"showCtx($event, this)\">\n <wuic-dashboard [dashboardElements]=\"dashboardElements\"></wuic-dashboard>\n @if (rubberBandVisible) {\n <div class=\"designer-selection-rect\" [style.left]=\"rubberBandStyle.left\" [style.top]=\"rubberBandStyle.top\"\n [style.width]=\"rubberBandStyle.width\" [style.height]=\"rubberBandStyle.height\"></div>\n }\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\" [widgetChrome]=\"dashboardWidgetChrome\"></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;position:relative}.designer-selection-rect{position:absolute;border:1px dashed #3b82f6;background:#3b82f61f;pointer-events:none;z-index:10000}:host ::ng-deep .designer-multi-selected{outline:2px solid #3b82f6!important;outline-offset:1px}: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}.dashboard-design.designer-snap-grid{background-image:linear-gradient(to right,rgba(128,128,128,.16) 1px,transparent 1px),linear-gradient(to bottom,rgba(128,128,128,.16) 1px,transparent 1px);background-size:var(--wuic-grid-size, 10px) var(--wuic-grid-size, 10px)}\n"] }]
|
|
11912
13216
|
}], ctorParameters: () => [{ type: i1$1.ActivatedRoute }, { type: DataProviderService }, { type: MetadataProviderService }, { type: UserInfoService }, { type: i0.ChangeDetectorRef }, { type: TranslationManagerService }, { type: MetadataEditorService }, { type: ChatbotHostRegistryService }, { type: WorkflowRuntimeMetadataService }], propDecorators: { onWindowHashChange: [{
|
|
11913
13217
|
type: HostListener,
|
|
11914
13218
|
args: ['window:hashchange']
|
|
@@ -11930,7 +13234,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
11930
13234
|
}], onWindowChange: [{
|
|
11931
13235
|
type: HostListener,
|
|
11932
13236
|
args: ['window:change']
|
|
13237
|
+
}], canvasRef: [{
|
|
13238
|
+
type: ViewChild,
|
|
13239
|
+
args: ['canvas']
|
|
11933
13240
|
}] } });
|
|
11934
13241
|
|
|
11935
13242
|
export { DesignerComponent };
|
|
11936
|
-
//# sourceMappingURL=wuic-framework-lib-designer.component-
|
|
13243
|
+
//# sourceMappingURL=wuic-framework-lib-designer.component-Bd2fwl6H.mjs.map
|