@zentto/report-designer 1.0.0 → 1.2.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.
|
@@ -105,7 +105,10 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
105
105
|
this._zoom = 1.5; // Default 150% for comfortable editing
|
|
106
106
|
this._showPageSetup = false;
|
|
107
107
|
this._showBandMenu = false;
|
|
108
|
+
this._mobileLeftOpen = false;
|
|
109
|
+
this._mobileRightOpen = false;
|
|
108
110
|
this._guides = { x: [], y: [] };
|
|
111
|
+
this._collapsedSections = new Set();
|
|
109
112
|
this._pageSetupDraft = null;
|
|
110
113
|
this._clipboard = [];
|
|
111
114
|
this._saveTimer = null;
|
|
@@ -131,6 +134,14 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
131
134
|
get _unit() {
|
|
132
135
|
return this._layout.pageSize.unit;
|
|
133
136
|
}
|
|
137
|
+
_toggleSection(section) {
|
|
138
|
+
const next = new Set(this._collapsedSections);
|
|
139
|
+
if (next.has(section))
|
|
140
|
+
next.delete(section);
|
|
141
|
+
else
|
|
142
|
+
next.add(section);
|
|
143
|
+
this._collapsedSections = next;
|
|
144
|
+
}
|
|
134
145
|
// Helper: single selected element id (for property panel, backward compat)
|
|
135
146
|
get _selectedElementId() {
|
|
136
147
|
if (this._selectedElementIds.size === 1) {
|
|
@@ -177,6 +188,8 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
177
188
|
padding: 6px 12px;
|
|
178
189
|
background: var(--zrd-panel-bg);
|
|
179
190
|
border-bottom: 1px solid var(--zrd-border);
|
|
191
|
+
flex-wrap: wrap;
|
|
192
|
+
min-height: 36px;
|
|
180
193
|
}
|
|
181
194
|
.top-toolbar button {
|
|
182
195
|
background: none;
|
|
@@ -288,10 +301,12 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
288
301
|
background: #d0d0d0;
|
|
289
302
|
display: flex;
|
|
290
303
|
justify-content: center;
|
|
304
|
+
-webkit-overflow-scrolling: touch;
|
|
291
305
|
}
|
|
292
306
|
.canvas-scroll-container {
|
|
293
307
|
transform-origin: top center;
|
|
294
308
|
transition: transform 0.1s ease;
|
|
309
|
+
min-width: min-content;
|
|
295
310
|
}
|
|
296
311
|
.canvas-wrapper {
|
|
297
312
|
position: relative;
|
|
@@ -384,6 +399,7 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
384
399
|
cursor: pointer;
|
|
385
400
|
user-select: none;
|
|
386
401
|
gap: 6px;
|
|
402
|
+
overflow: hidden;
|
|
387
403
|
}
|
|
388
404
|
.band-header:hover { filter: brightness(0.95); }
|
|
389
405
|
.band-header.selected { box-shadow: inset 0 0 0 2px var(--zrd-accent); color: var(--zrd-accent); }
|
|
@@ -391,6 +407,10 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
391
407
|
display: flex;
|
|
392
408
|
align-items: center;
|
|
393
409
|
gap: 6px;
|
|
410
|
+
overflow: hidden;
|
|
411
|
+
white-space: nowrap;
|
|
412
|
+
text-overflow: ellipsis;
|
|
413
|
+
min-width: 0;
|
|
394
414
|
}
|
|
395
415
|
.band-type-icon {
|
|
396
416
|
font-size: 13px;
|
|
@@ -505,79 +525,306 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
505
525
|
box-sizing: border-box;
|
|
506
526
|
}
|
|
507
527
|
|
|
508
|
-
/* ─── Right Panel (Properties) ─── */
|
|
528
|
+
/* ─── Right Panel (Figma-style Properties) ─── */
|
|
509
529
|
.right-panel {
|
|
510
530
|
background: var(--zrd-panel-bg);
|
|
511
531
|
border-left: 1px solid var(--zrd-border);
|
|
512
532
|
overflow-y: auto;
|
|
513
|
-
|
|
533
|
+
overflow-x: hidden;
|
|
534
|
+
scrollbar-width: thin;
|
|
535
|
+
scrollbar-color: #ccc transparent;
|
|
514
536
|
}
|
|
537
|
+
.right-panel::-webkit-scrollbar { width: 6px; }
|
|
538
|
+
.right-panel::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; }
|
|
539
|
+
|
|
515
540
|
.prop-section {
|
|
516
|
-
|
|
541
|
+
border-bottom: 1px solid var(--zrd-border);
|
|
542
|
+
padding: 10px 12px;
|
|
543
|
+
}
|
|
544
|
+
.prop-section:last-child { border-bottom: none; }
|
|
545
|
+
|
|
546
|
+
.prop-section-header {
|
|
547
|
+
display: flex;
|
|
548
|
+
align-items: center;
|
|
549
|
+
justify-content: space-between;
|
|
550
|
+
cursor: pointer;
|
|
551
|
+
user-select: none;
|
|
552
|
+
margin-bottom: 8px;
|
|
517
553
|
}
|
|
518
|
-
.prop-section h4 {
|
|
554
|
+
.prop-section-header h4 {
|
|
519
555
|
font-size: 11px;
|
|
556
|
+
font-weight: 600;
|
|
520
557
|
text-transform: uppercase;
|
|
558
|
+
letter-spacing: 0.5px;
|
|
559
|
+
color: var(--zrd-text);
|
|
560
|
+
margin: 0;
|
|
561
|
+
}
|
|
562
|
+
.prop-section-header .collapse-icon {
|
|
563
|
+
font-size: 10px;
|
|
521
564
|
color: var(--zrd-text-muted);
|
|
522
|
-
|
|
565
|
+
transition: transform 0.15s;
|
|
566
|
+
}
|
|
567
|
+
.prop-section-header .collapse-icon.collapsed {
|
|
568
|
+
transform: rotate(-90deg);
|
|
569
|
+
}
|
|
570
|
+
.prop-section-body {
|
|
571
|
+
display: grid;
|
|
572
|
+
gap: 4px;
|
|
523
573
|
}
|
|
574
|
+
.prop-section-body.collapsed {
|
|
575
|
+
display: none;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* ─── Property Rows ─── */
|
|
524
579
|
.prop-row {
|
|
525
|
-
display:
|
|
580
|
+
display: grid;
|
|
581
|
+
grid-template-columns: 68px 1fr;
|
|
526
582
|
align-items: center;
|
|
527
|
-
gap:
|
|
528
|
-
|
|
583
|
+
gap: 6px;
|
|
584
|
+
min-height: 28px;
|
|
585
|
+
}
|
|
586
|
+
.prop-row-full {
|
|
587
|
+
grid-template-columns: 1fr;
|
|
529
588
|
}
|
|
530
589
|
.prop-label {
|
|
531
|
-
width: 70px;
|
|
532
590
|
font-size: 11px;
|
|
533
591
|
color: var(--zrd-text-muted);
|
|
534
|
-
|
|
592
|
+
white-space: nowrap;
|
|
593
|
+
overflow: hidden;
|
|
594
|
+
text-overflow: ellipsis;
|
|
595
|
+
padding-left: 2px;
|
|
535
596
|
}
|
|
597
|
+
|
|
598
|
+
/* ─── Inputs (Figma-style) ─── */
|
|
536
599
|
.prop-input {
|
|
537
|
-
|
|
538
|
-
border: 1px solid
|
|
539
|
-
border-radius:
|
|
540
|
-
padding:
|
|
600
|
+
width: 100%;
|
|
601
|
+
border: 1px solid transparent;
|
|
602
|
+
border-radius: 4px;
|
|
603
|
+
padding: 4px 8px;
|
|
541
604
|
font-size: 12px;
|
|
542
|
-
background:
|
|
605
|
+
background: var(--zrd-bg);
|
|
543
606
|
color: var(--zrd-text);
|
|
607
|
+
transition: border-color 0.15s, background 0.15s;
|
|
608
|
+
outline: none;
|
|
609
|
+
min-width: 0;
|
|
544
610
|
}
|
|
545
|
-
.prop-input:
|
|
546
|
-
|
|
547
|
-
|
|
611
|
+
.prop-input:hover {
|
|
612
|
+
border-color: var(--zrd-border);
|
|
613
|
+
}
|
|
614
|
+
.prop-input:focus {
|
|
615
|
+
border-color: var(--zrd-accent);
|
|
616
|
+
background: white;
|
|
617
|
+
}
|
|
618
|
+
select.prop-input {
|
|
619
|
+
padding: 3px 6px;
|
|
620
|
+
cursor: pointer;
|
|
621
|
+
appearance: none;
|
|
622
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E");
|
|
623
|
+
background-repeat: no-repeat;
|
|
624
|
+
background-position: right 6px center;
|
|
625
|
+
padding-right: 20px;
|
|
626
|
+
}
|
|
627
|
+
textarea.prop-input {
|
|
628
|
+
min-height: 52px;
|
|
629
|
+
resize: vertical;
|
|
630
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
631
|
+
font-size: 11px;
|
|
632
|
+
line-height: 1.5;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* ─── Compact number inputs (X, Y, W, H grid) ─── */
|
|
636
|
+
.prop-grid-2x2 {
|
|
637
|
+
display: grid;
|
|
638
|
+
grid-template-columns: 1fr 1fr;
|
|
639
|
+
gap: 4px;
|
|
640
|
+
}
|
|
641
|
+
.prop-grid-item {
|
|
642
|
+
display: flex;
|
|
643
|
+
align-items: center;
|
|
644
|
+
background: var(--zrd-bg);
|
|
645
|
+
border: 1px solid transparent;
|
|
646
|
+
border-radius: 4px;
|
|
647
|
+
padding: 2px 6px;
|
|
648
|
+
gap: 4px;
|
|
649
|
+
transition: border-color 0.15s;
|
|
650
|
+
}
|
|
651
|
+
.prop-grid-item:hover { border-color: var(--zrd-border); }
|
|
652
|
+
.prop-grid-item:focus-within { border-color: var(--zrd-accent); }
|
|
653
|
+
.prop-grid-item .prop-grid-label {
|
|
548
654
|
font-size: 10px;
|
|
549
|
-
color: var(--zrd-
|
|
550
|
-
|
|
655
|
+
color: var(--zrd-accent);
|
|
656
|
+
font-weight: 600;
|
|
657
|
+
width: 12px;
|
|
658
|
+
text-align: center;
|
|
551
659
|
flex-shrink: 0;
|
|
552
660
|
}
|
|
661
|
+
.prop-grid-item input {
|
|
662
|
+
border: none;
|
|
663
|
+
background: transparent;
|
|
664
|
+
width: 100%;
|
|
665
|
+
font-size: 12px;
|
|
666
|
+
color: var(--zrd-text);
|
|
667
|
+
outline: none;
|
|
668
|
+
padding: 2px 0;
|
|
669
|
+
min-width: 0;
|
|
670
|
+
}
|
|
553
671
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
672
|
+
/* ─── Color picker (Figma-style swatch + input) ─── */
|
|
673
|
+
.prop-color-row {
|
|
674
|
+
display: flex;
|
|
675
|
+
align-items: center;
|
|
676
|
+
gap: 6px;
|
|
677
|
+
}
|
|
678
|
+
.prop-color-swatch {
|
|
679
|
+
width: 24px;
|
|
680
|
+
height: 24px;
|
|
681
|
+
border-radius: 4px;
|
|
682
|
+
border: 1px solid var(--zrd-border);
|
|
683
|
+
cursor: pointer;
|
|
684
|
+
flex-shrink: 0;
|
|
685
|
+
position: relative;
|
|
686
|
+
overflow: hidden;
|
|
687
|
+
}
|
|
688
|
+
.prop-color-swatch input[type="color"] {
|
|
689
|
+
position: absolute;
|
|
690
|
+
inset: -4px;
|
|
691
|
+
width: calc(100% + 8px);
|
|
692
|
+
height: calc(100% + 8px);
|
|
693
|
+
cursor: pointer;
|
|
557
694
|
border: none;
|
|
695
|
+
padding: 0;
|
|
696
|
+
}
|
|
697
|
+
.prop-color-hex {
|
|
698
|
+
border: 1px solid transparent;
|
|
558
699
|
border-radius: 4px;
|
|
559
|
-
padding: 4px
|
|
700
|
+
padding: 4px 8px;
|
|
701
|
+
font-size: 11px;
|
|
702
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
703
|
+
background: var(--zrd-bg);
|
|
704
|
+
color: var(--zrd-text);
|
|
705
|
+
width: 80px;
|
|
706
|
+
outline: none;
|
|
707
|
+
}
|
|
708
|
+
.prop-color-hex:hover { border-color: var(--zrd-border); }
|
|
709
|
+
.prop-color-hex:focus { border-color: var(--zrd-accent); }
|
|
710
|
+
|
|
711
|
+
/* ─── Toggle switches ─── */
|
|
712
|
+
.prop-toggle {
|
|
713
|
+
display: flex;
|
|
714
|
+
align-items: center;
|
|
715
|
+
gap: 8px;
|
|
716
|
+
}
|
|
717
|
+
.prop-switch {
|
|
718
|
+
position: relative;
|
|
719
|
+
width: 32px;
|
|
720
|
+
height: 18px;
|
|
721
|
+
border-radius: 9px;
|
|
722
|
+
background: #ccc;
|
|
723
|
+
cursor: pointer;
|
|
724
|
+
transition: background 0.2s;
|
|
725
|
+
flex-shrink: 0;
|
|
726
|
+
}
|
|
727
|
+
.prop-switch.active { background: var(--zrd-accent); }
|
|
728
|
+
.prop-switch::after {
|
|
729
|
+
content: '';
|
|
730
|
+
position: absolute;
|
|
731
|
+
top: 2px;
|
|
732
|
+
left: 2px;
|
|
733
|
+
width: 14px;
|
|
734
|
+
height: 14px;
|
|
735
|
+
border-radius: 50%;
|
|
736
|
+
background: white;
|
|
737
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
|
738
|
+
transition: transform 0.2s;
|
|
739
|
+
}
|
|
740
|
+
.prop-switch.active::after { transform: translateX(14px); }
|
|
741
|
+
|
|
742
|
+
/* ─── Segmented buttons (alignment, etc.) ─── */
|
|
743
|
+
.prop-segmented {
|
|
744
|
+
display: flex;
|
|
745
|
+
border: 1px solid var(--zrd-border);
|
|
746
|
+
border-radius: 4px;
|
|
747
|
+
overflow: hidden;
|
|
748
|
+
}
|
|
749
|
+
.prop-seg-btn {
|
|
750
|
+
flex: 1;
|
|
751
|
+
padding: 4px 0;
|
|
752
|
+
background: var(--zrd-bg);
|
|
753
|
+
border: none;
|
|
754
|
+
border-right: 1px solid var(--zrd-border);
|
|
755
|
+
cursor: pointer;
|
|
756
|
+
font-size: 12px;
|
|
757
|
+
color: var(--zrd-text-muted);
|
|
758
|
+
transition: background 0.15s;
|
|
759
|
+
text-align: center;
|
|
760
|
+
}
|
|
761
|
+
.prop-seg-btn:last-child { border-right: none; }
|
|
762
|
+
.prop-seg-btn:hover { background: var(--zrd-accent-light); }
|
|
763
|
+
.prop-seg-btn.active { background: var(--zrd-accent); color: white; }
|
|
764
|
+
|
|
765
|
+
/* ─── Font size + family row ─── */
|
|
766
|
+
.prop-font-row {
|
|
767
|
+
display: grid;
|
|
768
|
+
grid-template-columns: 1fr 60px;
|
|
769
|
+
gap: 4px;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/* ─── Delete button ─── */
|
|
773
|
+
.btn-danger {
|
|
774
|
+
background: none;
|
|
775
|
+
color: var(--zrd-danger);
|
|
776
|
+
border: 1px solid var(--zrd-danger);
|
|
777
|
+
border-radius: 4px;
|
|
778
|
+
padding: 6px 10px;
|
|
560
779
|
cursor: pointer;
|
|
561
780
|
font-size: 12px;
|
|
562
781
|
width: 100%;
|
|
782
|
+
transition: background 0.15s;
|
|
563
783
|
}
|
|
564
|
-
.btn-danger:hover {
|
|
784
|
+
.btn-danger:hover { background: var(--zrd-danger); color: white; }
|
|
565
785
|
|
|
786
|
+
/* ─── Z-order buttons ─── */
|
|
787
|
+
.z-order-buttons {
|
|
788
|
+
display: flex;
|
|
789
|
+
gap: 2px;
|
|
790
|
+
}
|
|
566
791
|
.btn-small {
|
|
567
|
-
background:
|
|
568
|
-
border: 1px solid
|
|
569
|
-
border-radius:
|
|
570
|
-
padding:
|
|
792
|
+
background: var(--zrd-bg);
|
|
793
|
+
border: 1px solid transparent;
|
|
794
|
+
border-radius: 4px;
|
|
795
|
+
padding: 4px 8px;
|
|
571
796
|
cursor: pointer;
|
|
572
797
|
font-size: 11px;
|
|
573
798
|
color: var(--zrd-text);
|
|
799
|
+
transition: all 0.15s;
|
|
574
800
|
}
|
|
575
|
-
.btn-small:hover { background: var(--zrd-accent-light); }
|
|
801
|
+
.btn-small:hover { background: var(--zrd-accent-light); border-color: var(--zrd-border); }
|
|
576
802
|
|
|
577
|
-
.
|
|
578
|
-
|
|
803
|
+
.prop-unit {
|
|
804
|
+
font-size: 10px;
|
|
805
|
+
color: var(--zrd-text-muted);
|
|
806
|
+
width: 20px;
|
|
807
|
+
flex-shrink: 0;
|
|
808
|
+
text-align: center;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/* ─── Section-specific colors ─── */
|
|
812
|
+
.prop-section[data-section="position"] .prop-section-header h4 { color: var(--zrd-accent); }
|
|
813
|
+
.prop-section[data-section="style"] .prop-section-header h4 { color: #7c3aed; }
|
|
814
|
+
.prop-section[data-section="visibility"] .prop-section-header h4 { color: #0d9488; }
|
|
815
|
+
.prop-section[data-section="data"] .prop-section-header h4 { color: #ea580c; }
|
|
816
|
+
|
|
817
|
+
/* ─── Element type badge ─── */
|
|
818
|
+
.element-type-badge {
|
|
819
|
+
display: inline-flex;
|
|
820
|
+
align-items: center;
|
|
579
821
|
gap: 4px;
|
|
580
|
-
|
|
822
|
+
padding: 3px 10px;
|
|
823
|
+
border-radius: 10px;
|
|
824
|
+
font-size: 11px;
|
|
825
|
+
font-weight: 600;
|
|
826
|
+
text-transform: uppercase;
|
|
827
|
+
letter-spacing: 0.3px;
|
|
581
828
|
}
|
|
582
829
|
|
|
583
830
|
/* ─── Data Source Panel ─── */
|
|
@@ -607,6 +854,8 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
607
854
|
z-index: 1000;
|
|
608
855
|
min-width: 160px;
|
|
609
856
|
padding: 4px 0;
|
|
857
|
+
max-height: calc(100vh - 40px);
|
|
858
|
+
overflow-y: auto;
|
|
610
859
|
}
|
|
611
860
|
.context-menu-item {
|
|
612
861
|
padding: 6px 12px;
|
|
@@ -695,6 +944,7 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
695
944
|
border-bottom: 1px solid var(--zrd-border);
|
|
696
945
|
font-size: 12px;
|
|
697
946
|
flex-wrap: wrap;
|
|
947
|
+
min-height: 32px;
|
|
698
948
|
}
|
|
699
949
|
.page-setup-group {
|
|
700
950
|
display: flex;
|
|
@@ -778,6 +1028,160 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
778
1028
|
font-size: 10px;
|
|
779
1029
|
font-family: monospace;
|
|
780
1030
|
}
|
|
1031
|
+
|
|
1032
|
+
/* ─── Mobile Panel Toggle Buttons ─── */
|
|
1033
|
+
.mobile-panel-toggles {
|
|
1034
|
+
display: none;
|
|
1035
|
+
gap: 8px;
|
|
1036
|
+
padding: 8px;
|
|
1037
|
+
}
|
|
1038
|
+
@media (max-width: 768px) {
|
|
1039
|
+
.mobile-panel-toggles {
|
|
1040
|
+
display: flex;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
.mobile-toggle-btn {
|
|
1044
|
+
padding: 6px 12px;
|
|
1045
|
+
border: 1px solid var(--zrd-border);
|
|
1046
|
+
border-radius: 4px;
|
|
1047
|
+
background: var(--zrd-panel-bg);
|
|
1048
|
+
cursor: pointer;
|
|
1049
|
+
font-size: 12px;
|
|
1050
|
+
color: var(--zrd-text);
|
|
1051
|
+
}
|
|
1052
|
+
.mobile-toggle-btn.active {
|
|
1053
|
+
background: var(--zrd-accent);
|
|
1054
|
+
color: white;
|
|
1055
|
+
border-color: var(--zrd-accent);
|
|
1056
|
+
}
|
|
1057
|
+
.mobile-overlay {
|
|
1058
|
+
position: fixed;
|
|
1059
|
+
inset: 0;
|
|
1060
|
+
background: rgba(0,0,0,0.3);
|
|
1061
|
+
z-index: 499;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/* ─── Responsive: Grid Layout ─── */
|
|
1065
|
+
@media (max-width: 1024px) {
|
|
1066
|
+
.designer {
|
|
1067
|
+
grid-template-columns: 180px 1fr 220px;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
@media (max-width: 768px) {
|
|
1071
|
+
.designer {
|
|
1072
|
+
grid-template-columns: 1fr;
|
|
1073
|
+
grid-template-rows: auto auto 1fr auto;
|
|
1074
|
+
}
|
|
1075
|
+
.left-panel {
|
|
1076
|
+
display: none;
|
|
1077
|
+
position: fixed;
|
|
1078
|
+
left: 0;
|
|
1079
|
+
top: 0;
|
|
1080
|
+
bottom: 0;
|
|
1081
|
+
width: 260px;
|
|
1082
|
+
z-index: 500;
|
|
1083
|
+
box-shadow: 4px 0 20px rgba(0,0,0,0.2);
|
|
1084
|
+
}
|
|
1085
|
+
.left-panel.mobile-open {
|
|
1086
|
+
display: block;
|
|
1087
|
+
}
|
|
1088
|
+
.right-panel {
|
|
1089
|
+
display: none;
|
|
1090
|
+
position: fixed;
|
|
1091
|
+
right: 0;
|
|
1092
|
+
top: 0;
|
|
1093
|
+
bottom: 0;
|
|
1094
|
+
width: 280px;
|
|
1095
|
+
z-index: 500;
|
|
1096
|
+
box-shadow: -4px 0 20px rgba(0,0,0,0.2);
|
|
1097
|
+
}
|
|
1098
|
+
.right-panel.mobile-open {
|
|
1099
|
+
display: block;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/* ─── Responsive: Top Toolbar ─── */
|
|
1104
|
+
@media (max-width: 768px) {
|
|
1105
|
+
.top-toolbar {
|
|
1106
|
+
padding: 4px 8px;
|
|
1107
|
+
gap: 4px;
|
|
1108
|
+
}
|
|
1109
|
+
.top-toolbar button {
|
|
1110
|
+
padding: 3px 6px;
|
|
1111
|
+
font-size: 11px;
|
|
1112
|
+
}
|
|
1113
|
+
.top-toolbar .report-name {
|
|
1114
|
+
font-size: 12px;
|
|
1115
|
+
max-width: 120px;
|
|
1116
|
+
overflow: hidden;
|
|
1117
|
+
text-overflow: ellipsis;
|
|
1118
|
+
white-space: nowrap;
|
|
1119
|
+
}
|
|
1120
|
+
.zoom-controls {
|
|
1121
|
+
border-left: none;
|
|
1122
|
+
padding-left: 0;
|
|
1123
|
+
margin-left: 0;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* ─── Responsive: Page Setup Bar ─── */
|
|
1128
|
+
@media (max-width: 900px) {
|
|
1129
|
+
.page-setup-bar {
|
|
1130
|
+
gap: 6px;
|
|
1131
|
+
padding: 4px 8px;
|
|
1132
|
+
}
|
|
1133
|
+
.page-setup-input {
|
|
1134
|
+
width: 36px;
|
|
1135
|
+
}
|
|
1136
|
+
.page-setup-select {
|
|
1137
|
+
max-width: 140px;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
@media (max-width: 600px) {
|
|
1141
|
+
.page-setup-bar {
|
|
1142
|
+
font-size: 11px;
|
|
1143
|
+
}
|
|
1144
|
+
.page-setup-label {
|
|
1145
|
+
display: none;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/* ─── Responsive: Toolbox Touch Targets ─── */
|
|
1150
|
+
@media (max-width: 768px) {
|
|
1151
|
+
.toolbox-item {
|
|
1152
|
+
padding: 12px 10px;
|
|
1153
|
+
min-height: 44px;
|
|
1154
|
+
}
|
|
1155
|
+
.toolbox-icon {
|
|
1156
|
+
width: 32px;
|
|
1157
|
+
height: 32px;
|
|
1158
|
+
font-size: 13px;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/* ─── Responsive: Properties Panel ─── */
|
|
1163
|
+
@media (max-width: 768px) {
|
|
1164
|
+
.prop-label {
|
|
1165
|
+
width: 60px;
|
|
1166
|
+
font-size: 10px;
|
|
1167
|
+
}
|
|
1168
|
+
.prop-input {
|
|
1169
|
+
font-size: 11px;
|
|
1170
|
+
padding: 4px 4px;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/* ─── Responsive: Dialog ─── */
|
|
1175
|
+
@media (max-width: 600px) {
|
|
1176
|
+
.dialog {
|
|
1177
|
+
min-width: auto;
|
|
1178
|
+
max-width: calc(100vw - 32px);
|
|
1179
|
+
margin: 16px;
|
|
1180
|
+
}
|
|
1181
|
+
.dialog-body {
|
|
1182
|
+
padding: 12px;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
781
1185
|
`; }
|
|
782
1186
|
// ─── Lifecycle ──────────────────────────────────────────────────
|
|
783
1187
|
connectedCallback() {
|
|
@@ -1375,7 +1779,11 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
1375
1779
|
${this._renderTopToolbar()}
|
|
1376
1780
|
${this._renderPageSetupBar()}
|
|
1377
1781
|
|
|
1378
|
-
|
|
1782
|
+
${this._mobileLeftOpen || this._mobileRightOpen ? html `
|
|
1783
|
+
<div class="mobile-overlay" @click=${() => { this._mobileLeftOpen = false; this._mobileRightOpen = false; }}></div>
|
|
1784
|
+
` : nothing}
|
|
1785
|
+
|
|
1786
|
+
<div class="left-panel ${this._mobileLeftOpen ? 'mobile-open' : ''}">
|
|
1379
1787
|
<div class="panel-tabs">
|
|
1380
1788
|
<div class="panel-tab ${this._activePanel === 'toolbox' ? 'active' : ''}"
|
|
1381
1789
|
@click=${() => this._activePanel = 'toolbox'}>Toolbox</div>
|
|
@@ -1398,6 +1806,16 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
1398
1806
|
this.requestUpdate();
|
|
1399
1807
|
}
|
|
1400
1808
|
}}>
|
|
1809
|
+
<div class="mobile-panel-toggles">
|
|
1810
|
+
<button class="mobile-toggle-btn ${this._mobileLeftOpen ? 'active' : ''}"
|
|
1811
|
+
@click=${(e) => { e.stopPropagation(); this._mobileLeftOpen = !this._mobileLeftOpen; this._mobileRightOpen = false; }}>
|
|
1812
|
+
Toolbox
|
|
1813
|
+
</button>
|
|
1814
|
+
<button class="mobile-toggle-btn ${this._mobileRightOpen ? 'active' : ''}"
|
|
1815
|
+
@click=${(e) => { e.stopPropagation(); this._mobileRightOpen = !this._mobileRightOpen; this._mobileLeftOpen = false; }}>
|
|
1816
|
+
Properties
|
|
1817
|
+
</button>
|
|
1818
|
+
</div>
|
|
1401
1819
|
<div class="canvas-scroll-container" style="transform:scale(${this._zoom});padding:20px;">
|
|
1402
1820
|
<div class="canvas-wrapper" style="width:${pageWidthPx}px;">
|
|
1403
1821
|
${this._renderRuler(pageWidthPx)}
|
|
@@ -1415,7 +1833,7 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
1415
1833
|
</div>
|
|
1416
1834
|
</div>
|
|
1417
1835
|
|
|
1418
|
-
<div class="right-panel">
|
|
1836
|
+
<div class="right-panel ${this._mobileRightOpen ? 'mobile-open' : ''}">
|
|
1419
1837
|
${this._renderPropertiesPanel()}
|
|
1420
1838
|
</div>
|
|
1421
1839
|
</div>
|
|
@@ -2221,87 +2639,121 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
2221
2639
|
_renderPropertiesPanel() {
|
|
2222
2640
|
const el = this._getSelectedElement();
|
|
2223
2641
|
const unitLabel = this._unit;
|
|
2224
|
-
|
|
2642
|
+
const collapsed = this._collapsedSections;
|
|
2643
|
+
const sectionHeader = (key, title) => html `
|
|
2644
|
+
<div class="prop-section-header" @click=${() => this._toggleSection(key)}>
|
|
2645
|
+
<h4>${title}</h4>
|
|
2646
|
+
<span class="collapse-icon ${collapsed.has(key) ? 'collapsed' : ''}">\u25BE</span>
|
|
2647
|
+
</div>
|
|
2648
|
+
`;
|
|
2649
|
+
// ── Multiple selection ──
|
|
2225
2650
|
if (this._selectedElementIds.size > 1) {
|
|
2226
2651
|
return html `
|
|
2227
2652
|
<div class="prop-section">
|
|
2228
|
-
<
|
|
2229
|
-
|
|
2230
|
-
|
|
2653
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;">
|
|
2654
|
+
<span class="element-type-badge" style="background:#fff3e0;color:#e65100;">\u2750 ${this._selectedElementIds.size} selected</span>
|
|
2655
|
+
</div>
|
|
2656
|
+
<p style="font-size:11px;color:var(--zrd-text-muted);">Use Ctrl+C/V to copy/paste, Del to delete.</p>
|
|
2231
2657
|
</div>
|
|
2232
|
-
<div class="prop-section"
|
|
2658
|
+
<div class="prop-section">
|
|
2233
2659
|
<button class="btn-danger" @click=${this._deleteSelectedElements}>Delete All Selected</button>
|
|
2234
2660
|
</div>
|
|
2235
2661
|
`;
|
|
2236
2662
|
}
|
|
2237
|
-
// Band properties
|
|
2663
|
+
// ── Band properties ──
|
|
2238
2664
|
if (!el && this._selectedBandId) {
|
|
2239
2665
|
const band = this._layout.bands.find(b => b.id === this._selectedBandId);
|
|
2240
2666
|
if (!band)
|
|
2241
2667
|
return html `<div style="padding:12px;color:var(--zrd-text-muted)">Select an element</div>`;
|
|
2242
2668
|
return html `
|
|
2243
2669
|
<div class="prop-section">
|
|
2244
|
-
<
|
|
2245
|
-
|
|
2670
|
+
<div style="display:flex;align-items:center;gap:8px;">
|
|
2671
|
+
<span style="font-size:16px;">${BAND_ICONS[band.type]}</span>
|
|
2672
|
+
<div>
|
|
2673
|
+
<div style="font-weight:600;font-size:13px;">${BAND_LABELS[band.type]}</div>
|
|
2674
|
+
<div style="font-size:10px;color:var(--zrd-text-muted);">${band.elements.length} elements</div>
|
|
2675
|
+
</div>
|
|
2676
|
+
</div>
|
|
2246
2677
|
</div>
|
|
2247
2678
|
|
|
2248
|
-
<div class="prop-section">
|
|
2249
|
-
|
|
2250
|
-
<div class="prop-
|
|
2251
|
-
<
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2679
|
+
<div class="prop-section" data-section="position">
|
|
2680
|
+
${sectionHeader('band-dim', 'Dimensions')}
|
|
2681
|
+
<div class="prop-section-body ${collapsed.has('band-dim') ? 'collapsed' : ''}">
|
|
2682
|
+
<div class="prop-row">
|
|
2683
|
+
<span class="prop-label">Height</span>
|
|
2684
|
+
<div style="display:flex;align-items:center;gap:4px;">
|
|
2685
|
+
<input class="prop-input" type="number" step="0.5" .value=${String(band.height)}
|
|
2686
|
+
@change=${(e) => this._updateBandHeight(band.id, Number(e.target.value))} />
|
|
2687
|
+
<span class="prop-unit">${unitLabel}</span>
|
|
2688
|
+
</div>
|
|
2689
|
+
</div>
|
|
2690
|
+
<div class="prop-row">
|
|
2691
|
+
<span class="prop-label">Background</span>
|
|
2692
|
+
<div class="prop-color-row">
|
|
2693
|
+
<div class="prop-color-swatch" style="background:${band.backgroundColor || '#ffffff'}">
|
|
2694
|
+
<input type="color" .value=${band.backgroundColor || '#ffffff'}
|
|
2695
|
+
@change=${(e) => { band.backgroundColor = e.target.value; this._commitChange(); }} />
|
|
2696
|
+
</div>
|
|
2697
|
+
<input class="prop-color-hex" .value=${band.backgroundColor || '#ffffff'}
|
|
2698
|
+
@change=${(e) => { band.backgroundColor = e.target.value; this._commitChange(); }} />
|
|
2699
|
+
</div>
|
|
2700
|
+
</div>
|
|
2260
2701
|
</div>
|
|
2261
2702
|
</div>
|
|
2262
2703
|
|
|
2263
2704
|
${band.type === 'detail' || band.type === 'groupHeader' || band.type === 'groupFooter' ? html `
|
|
2264
|
-
<div class="prop-section">
|
|
2265
|
-
|
|
2266
|
-
<div class="prop-
|
|
2267
|
-
<span class="prop-label">DataSource</span>
|
|
2268
|
-
<select class="prop-input" .value=${band.dataSource || ''}
|
|
2269
|
-
@change=${(e) => { band.dataSource = e.target.value; this._commitChange(); }}>
|
|
2270
|
-
<option value="">-- none --</option>
|
|
2271
|
-
${this._layout.dataSources.map(ds => html `<option value="${ds.id}">${ds.name}</option>`)}
|
|
2272
|
-
</select>
|
|
2273
|
-
</div>
|
|
2274
|
-
${band.type === 'groupHeader' || band.type === 'groupFooter' ? html `
|
|
2705
|
+
<div class="prop-section" data-section="data">
|
|
2706
|
+
${sectionHeader('band-data', 'Data')}
|
|
2707
|
+
<div class="prop-section-body ${collapsed.has('band-data') ? 'collapsed' : ''}">
|
|
2275
2708
|
<div class="prop-row">
|
|
2276
|
-
<span class="prop-label">
|
|
2277
|
-
<
|
|
2278
|
-
|
|
2279
|
-
|
|
2709
|
+
<span class="prop-label">DataSource</span>
|
|
2710
|
+
<select class="prop-input" .value=${band.dataSource || ''}
|
|
2711
|
+
@change=${(e) => { band.dataSource = e.target.value; this._commitChange(); }}>
|
|
2712
|
+
<option value="">-- none --</option>
|
|
2713
|
+
${this._layout.dataSources.map(ds => html `<option value="${ds.id}">${ds.name}</option>`)}
|
|
2714
|
+
</select>
|
|
2280
2715
|
</div>
|
|
2281
|
-
|
|
2716
|
+
${band.type === 'groupHeader' || band.type === 'groupFooter' ? html `
|
|
2717
|
+
<div class="prop-row">
|
|
2718
|
+
<span class="prop-label">Group By</span>
|
|
2719
|
+
<input class="prop-input" .value=${band.groupField || ''}
|
|
2720
|
+
placeholder="field name"
|
|
2721
|
+
@change=${(e) => { band.groupField = e.target.value; this._commitChange(); }} />
|
|
2722
|
+
</div>
|
|
2723
|
+
` : nothing}
|
|
2724
|
+
</div>
|
|
2282
2725
|
</div>
|
|
2283
2726
|
` : nothing}
|
|
2284
2727
|
|
|
2285
|
-
<div class="prop-section">
|
|
2286
|
-
|
|
2287
|
-
<div class="prop-
|
|
2288
|
-
<
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2728
|
+
<div class="prop-section" data-section="visibility">
|
|
2729
|
+
${sectionHeader('band-expert', 'Section Expert')}
|
|
2730
|
+
<div class="prop-section-body ${collapsed.has('band-expert') ? 'collapsed' : ''}">
|
|
2731
|
+
<div class="prop-row">
|
|
2732
|
+
<span class="prop-label">Visible</span>
|
|
2733
|
+
<div class="prop-toggle">
|
|
2734
|
+
<div class="prop-switch ${band.visible !== false ? 'active' : ''}"
|
|
2735
|
+
@click=${() => { band.visible = !(band.visible !== false); this._commitChange(); }}></div>
|
|
2736
|
+
<span style="font-size:11px;color:var(--zrd-text-muted);">${band.visible !== false ? 'On' : 'Off'}</span>
|
|
2737
|
+
</div>
|
|
2738
|
+
</div>
|
|
2739
|
+
<div class="prop-row">
|
|
2740
|
+
<span class="prop-label">Keep Together</span>
|
|
2741
|
+
<div class="prop-toggle">
|
|
2742
|
+
<div class="prop-switch ${band.keepTogether === true ? 'active' : ''}"
|
|
2743
|
+
@click=${() => { band.keepTogether = !band.keepTogether; this._commitChange(); }}></div>
|
|
2744
|
+
</div>
|
|
2745
|
+
</div>
|
|
2746
|
+
<div class="prop-row">
|
|
2747
|
+
<span class="prop-label">Repeat Page</span>
|
|
2748
|
+
<div class="prop-toggle">
|
|
2749
|
+
<div class="prop-switch ${band.repeatOnEveryPage === true ? 'active' : ''}"
|
|
2750
|
+
@click=${() => { band.repeatOnEveryPage = !band.repeatOnEveryPage; this._commitChange(); }}></div>
|
|
2751
|
+
</div>
|
|
2752
|
+
</div>
|
|
2301
2753
|
</div>
|
|
2302
2754
|
</div>
|
|
2303
2755
|
|
|
2304
|
-
<div class="prop-section"
|
|
2756
|
+
<div class="prop-section">
|
|
2305
2757
|
<button class="btn-danger" @click=${() => {
|
|
2306
2758
|
this._layout.bands = this._layout.bands.filter(b => b.id !== band.id);
|
|
2307
2759
|
this._selectedBandId = null;
|
|
@@ -2310,6 +2762,7 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
2310
2762
|
</div>
|
|
2311
2763
|
`;
|
|
2312
2764
|
}
|
|
2765
|
+
// ── Nothing selected — help card ──
|
|
2313
2766
|
if (!el)
|
|
2314
2767
|
return html `
|
|
2315
2768
|
<div class="help-card">
|
|
@@ -2328,244 +2781,322 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
|
|
|
2328
2781
|
</div>
|
|
2329
2782
|
</div>
|
|
2330
2783
|
`;
|
|
2784
|
+
// ── Single element selected ──
|
|
2785
|
+
const typeBadgeColors = {
|
|
2786
|
+
text: 'background:#e3f2fd;color:#1565c0;',
|
|
2787
|
+
field: 'background:#e8f5e9;color:#2e7d32;',
|
|
2788
|
+
image: 'background:#f3e5f5;color:#7b1fa2;',
|
|
2789
|
+
line: 'background:#eceff1;color:#546e7a;',
|
|
2790
|
+
rect: 'background:#eceff1;color:#546e7a;',
|
|
2791
|
+
barcode: 'background:#fff3e0;color:#e65100;',
|
|
2792
|
+
pageNumber: 'background:#fce4ec;color:#c62828;',
|
|
2793
|
+
currentDate: 'background:#fff8e1;color:#f57f17;',
|
|
2794
|
+
};
|
|
2795
|
+
const badgeStyle = typeBadgeColors[el.type] || 'background:#f5f5f5;color:#333;';
|
|
2331
2796
|
return html `
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
<
|
|
2335
|
-
<span class="prop-label">X</span>
|
|
2336
|
-
<input class="prop-input" type="number" step="0.5" .value=${String(el.x)}
|
|
2337
|
-
@change=${(e) => this._updateElementProp('x', Number(e.target.value))} />
|
|
2338
|
-
<span class="prop-unit">${unitLabel}</span>
|
|
2339
|
-
</div>
|
|
2340
|
-
<div class="prop-row">
|
|
2341
|
-
<span class="prop-label">Y</span>
|
|
2342
|
-
<input class="prop-input" type="number" step="0.5" .value=${String(el.y)}
|
|
2343
|
-
@change=${(e) => this._updateElementProp('y', Number(e.target.value))} />
|
|
2344
|
-
<span class="prop-unit">${unitLabel}</span>
|
|
2345
|
-
</div>
|
|
2346
|
-
<div class="prop-row">
|
|
2347
|
-
<span class="prop-label">Width</span>
|
|
2348
|
-
<input class="prop-input" type="number" step="0.5" .value=${String(el.width)}
|
|
2349
|
-
@change=${(e) => this._updateElementProp('width', Number(e.target.value))} />
|
|
2350
|
-
<span class="prop-unit">${unitLabel}</span>
|
|
2351
|
-
</div>
|
|
2352
|
-
<div class="prop-row">
|
|
2353
|
-
<span class="prop-label">Height</span>
|
|
2354
|
-
<input class="prop-input" type="number" step="0.5" .value=${String(el.height)}
|
|
2355
|
-
@change=${(e) => this._updateElementProp('height', Number(e.target.value))} />
|
|
2356
|
-
<span class="prop-unit">${unitLabel}</span>
|
|
2357
|
-
</div>
|
|
2797
|
+
<!-- Element type badge -->
|
|
2798
|
+
<div class="prop-section" style="display:flex;align-items:center;gap:8px;">
|
|
2799
|
+
<span class="element-type-badge" style="${badgeStyle}">${ELEMENT_TYPE_ICONS[el.type] || '?'} ${el.type}</span>
|
|
2358
2800
|
</div>
|
|
2359
2801
|
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
<
|
|
2365
|
-
|
|
2366
|
-
|
|
2802
|
+
<!-- Position -->
|
|
2803
|
+
<div class="prop-section" data-section="position">
|
|
2804
|
+
${sectionHeader('el-pos', 'Position')}
|
|
2805
|
+
<div class="prop-section-body ${collapsed.has('el-pos') ? 'collapsed' : ''}">
|
|
2806
|
+
<div class="prop-grid-2x2">
|
|
2807
|
+
<div class="prop-grid-item">
|
|
2808
|
+
<span class="prop-grid-label">X</span>
|
|
2809
|
+
<input type="number" step="0.5" .value=${String(el.x)}
|
|
2810
|
+
@change=${(e) => this._updateElementProp('x', Number(e.target.value))} />
|
|
2811
|
+
<span class="prop-unit">${unitLabel}</span>
|
|
2812
|
+
</div>
|
|
2813
|
+
<div class="prop-grid-item">
|
|
2814
|
+
<span class="prop-grid-label">Y</span>
|
|
2815
|
+
<input type="number" step="0.5" .value=${String(el.y)}
|
|
2816
|
+
@change=${(e) => this._updateElementProp('y', Number(e.target.value))} />
|
|
2817
|
+
<span class="prop-unit">${unitLabel}</span>
|
|
2818
|
+
</div>
|
|
2819
|
+
<div class="prop-grid-item">
|
|
2820
|
+
<span class="prop-grid-label">W</span>
|
|
2821
|
+
<input type="number" step="0.5" .value=${String(el.width)}
|
|
2822
|
+
@change=${(e) => this._updateElementProp('width', Number(e.target.value))} />
|
|
2823
|
+
<span class="prop-unit">${unitLabel}</span>
|
|
2824
|
+
</div>
|
|
2825
|
+
<div class="prop-grid-item">
|
|
2826
|
+
<span class="prop-grid-label">H</span>
|
|
2827
|
+
<input type="number" step="0.5" .value=${String(el.height)}
|
|
2828
|
+
@change=${(e) => this._updateElementProp('height', Number(e.target.value))} />
|
|
2829
|
+
<span class="prop-unit">${unitLabel}</span>
|
|
2830
|
+
</div>
|
|
2831
|
+
</div>
|
|
2367
2832
|
</div>
|
|
2368
2833
|
</div>
|
|
2369
2834
|
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
<
|
|
2382
|
-
|
|
2383
|
-
</
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
<
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
<
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
</
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
<
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2835
|
+
<!-- Style -->
|
|
2836
|
+
<div class="prop-section" data-section="style">
|
|
2837
|
+
${sectionHeader('el-style', 'Style')}
|
|
2838
|
+
<div class="prop-section-body ${collapsed.has('el-style') ? 'collapsed' : ''}">
|
|
2839
|
+
<!-- Font family + size -->
|
|
2840
|
+
<div class="prop-font-row">
|
|
2841
|
+
<select class="prop-input" .value=${el.style?.fontFamily || 'Arial'}
|
|
2842
|
+
@change=${(e) => this._updateElementStyle('fontFamily', e.target.value)}>
|
|
2843
|
+
<option>Arial</option><option>Times New Roman</option><option>Courier New</option>
|
|
2844
|
+
<option>Verdana</option><option>Georgia</option><option>Helvetica</option>
|
|
2845
|
+
</select>
|
|
2846
|
+
<input class="prop-input" type="number" .value=${String(el.style?.fontSize || 10)}
|
|
2847
|
+
@change=${(e) => this._updateElementStyle('fontSize', Number(e.target.value))} />
|
|
2848
|
+
</div>
|
|
2849
|
+
|
|
2850
|
+
<!-- Bold / Italic / Underline segmented -->
|
|
2851
|
+
<div class="prop-row" style="grid-template-columns:68px 1fr;">
|
|
2852
|
+
<span class="prop-label">Format</span>
|
|
2853
|
+
<div class="prop-segmented">
|
|
2854
|
+
<button class="prop-seg-btn ${el.style?.fontWeight === 'bold' ? 'active' : ''}" style="font-weight:bold;"
|
|
2855
|
+
@click=${() => this._updateElementStyle('fontWeight', el.style?.fontWeight === 'bold' ? 'normal' : 'bold')}>B</button>
|
|
2856
|
+
<button class="prop-seg-btn ${el.style?.fontStyle === 'italic' ? 'active' : ''}" style="font-style:italic;"
|
|
2857
|
+
@click=${() => this._updateElementStyle('fontStyle', el.style?.fontStyle === 'italic' ? 'normal' : 'italic')}>I</button>
|
|
2858
|
+
<button class="prop-seg-btn ${el.style?.textDecoration === 'underline' ? 'active' : ''}" style="text-decoration:underline;"
|
|
2859
|
+
@click=${() => this._updateElementStyle('textDecoration', el.style?.textDecoration === 'underline' ? 'none' : 'underline')}>U</button>
|
|
2860
|
+
</div>
|
|
2861
|
+
</div>
|
|
2862
|
+
|
|
2863
|
+
<!-- Horizontal alignment segmented -->
|
|
2864
|
+
<div class="prop-row" style="grid-template-columns:68px 1fr;">
|
|
2865
|
+
<span class="prop-label">Align</span>
|
|
2866
|
+
<div class="prop-segmented">
|
|
2867
|
+
<button class="prop-seg-btn ${(el.style?.textAlign || 'left') === 'left' ? 'active' : ''}"
|
|
2868
|
+
@click=${() => this._updateElementStyle('textAlign', 'left')}>\u2190</button>
|
|
2869
|
+
<button class="prop-seg-btn ${el.style?.textAlign === 'center' ? 'active' : ''}"
|
|
2870
|
+
@click=${() => this._updateElementStyle('textAlign', 'center')}>\u2194</button>
|
|
2871
|
+
<button class="prop-seg-btn ${el.style?.textAlign === 'right' ? 'active' : ''}"
|
|
2872
|
+
@click=${() => this._updateElementStyle('textAlign', 'right')}>\u2192</button>
|
|
2873
|
+
</div>
|
|
2874
|
+
</div>
|
|
2875
|
+
|
|
2876
|
+
<!-- Vertical alignment segmented -->
|
|
2877
|
+
<div class="prop-row" style="grid-template-columns:68px 1fr;">
|
|
2878
|
+
<span class="prop-label">V.Align</span>
|
|
2879
|
+
<div class="prop-segmented">
|
|
2880
|
+
<button class="prop-seg-btn ${(el.style?.verticalAlign || 'top') === 'top' ? 'active' : ''}"
|
|
2881
|
+
@click=${() => this._updateElementStyle('verticalAlign', 'top')}>\u2191</button>
|
|
2882
|
+
<button class="prop-seg-btn ${el.style?.verticalAlign === 'middle' ? 'active' : ''}"
|
|
2883
|
+
@click=${() => this._updateElementStyle('verticalAlign', 'middle')}>\u2195</button>
|
|
2884
|
+
<button class="prop-seg-btn ${el.style?.verticalAlign === 'bottom' ? 'active' : ''}"
|
|
2885
|
+
@click=${() => this._updateElementStyle('verticalAlign', 'bottom')}>\u2193</button>
|
|
2886
|
+
</div>
|
|
2887
|
+
</div>
|
|
2888
|
+
|
|
2889
|
+
<!-- Text color -->
|
|
2890
|
+
<div class="prop-row">
|
|
2891
|
+
<span class="prop-label">Color</span>
|
|
2892
|
+
<div class="prop-color-row">
|
|
2893
|
+
<div class="prop-color-swatch" style="background:${el.style?.color || '#333333'}">
|
|
2894
|
+
<input type="color" .value=${el.style?.color || '#333333'}
|
|
2895
|
+
@change=${(e) => this._updateElementStyle('color', e.target.value)} />
|
|
2896
|
+
</div>
|
|
2897
|
+
<input class="prop-color-hex" .value=${el.style?.color || '#333333'}
|
|
2898
|
+
@change=${(e) => this._updateElementStyle('color', e.target.value)} />
|
|
2899
|
+
</div>
|
|
2900
|
+
</div>
|
|
2901
|
+
|
|
2902
|
+
<!-- Background color -->
|
|
2903
|
+
<div class="prop-row">
|
|
2904
|
+
<span class="prop-label">Bg Color</span>
|
|
2905
|
+
<div class="prop-color-row">
|
|
2906
|
+
<div class="prop-color-swatch" style="background:${el.style?.backgroundColor || '#ffffff'}">
|
|
2907
|
+
<input type="color" .value=${el.style?.backgroundColor || '#ffffff'}
|
|
2908
|
+
@change=${(e) => this._updateElementStyle('backgroundColor', e.target.value)} />
|
|
2909
|
+
</div>
|
|
2910
|
+
<input class="prop-color-hex" .value=${el.style?.backgroundColor || '#ffffff'}
|
|
2911
|
+
@change=${(e) => this._updateElementStyle('backgroundColor', e.target.value)} />
|
|
2912
|
+
</div>
|
|
2913
|
+
</div>
|
|
2914
|
+
|
|
2915
|
+
<!-- Border -->
|
|
2916
|
+
<div class="prop-row">
|
|
2917
|
+
<span class="prop-label">Border</span>
|
|
2918
|
+
<input class="prop-input" .value=${el.style?.borderBottom || ''}
|
|
2919
|
+
placeholder="1px solid #ccc"
|
|
2920
|
+
@change=${(e) => {
|
|
2429
2921
|
const v = e.target.value;
|
|
2430
2922
|
this._updateElementStyle('borderTop', v);
|
|
2431
2923
|
this._updateElementStyle('borderRight', v);
|
|
2432
2924
|
this._updateElementStyle('borderBottom', v);
|
|
2433
2925
|
this._updateElementStyle('borderLeft', v);
|
|
2434
2926
|
}} />
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
<
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2927
|
+
</div>
|
|
2928
|
+
|
|
2929
|
+
<!-- Padding -->
|
|
2930
|
+
<div class="prop-row">
|
|
2931
|
+
<span class="prop-label">Padding</span>
|
|
2932
|
+
<input class="prop-input" type="number" .value=${String(el.style?.padding || 0)}
|
|
2933
|
+
@change=${(e) => this._updateElementStyle('padding', Number(e.target.value))} />
|
|
2934
|
+
</div>
|
|
2935
|
+
|
|
2936
|
+
<!-- Word wrap toggle -->
|
|
2937
|
+
<div class="prop-row">
|
|
2938
|
+
<span class="prop-label">Word Wrap</span>
|
|
2939
|
+
<div class="prop-toggle">
|
|
2940
|
+
<div class="prop-switch ${el.style?.wordWrap === true ? 'active' : ''}"
|
|
2941
|
+
@click=${() => this._updateElementStyle('wordWrap', !el.style?.wordWrap)}></div>
|
|
2942
|
+
<span style="font-size:11px;color:var(--zrd-text-muted);">${el.style?.wordWrap ? 'On' : 'Off'}</span>
|
|
2943
|
+
</div>
|
|
2944
|
+
</div>
|
|
2445
2945
|
</div>
|
|
2446
2946
|
</div>
|
|
2447
2947
|
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
<
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
<
|
|
2462
|
-
<
|
|
2463
|
-
|
|
2464
|
-
|
|
2948
|
+
<!-- Visibility -->
|
|
2949
|
+
<div class="prop-section" data-section="visibility">
|
|
2950
|
+
${sectionHeader('el-vis', 'Visibility')}
|
|
2951
|
+
<div class="prop-section-body ${collapsed.has('el-vis') ? 'collapsed' : ''}">
|
|
2952
|
+
<div class="prop-row">
|
|
2953
|
+
<span class="prop-label">Visible</span>
|
|
2954
|
+
<div class="prop-toggle">
|
|
2955
|
+
<div class="prop-switch ${el.visible !== false ? 'active' : ''}"
|
|
2956
|
+
@click=${() => this._updateElementProp('visible', !(el.visible !== false))}></div>
|
|
2957
|
+
<span style="font-size:11px;color:var(--zrd-text-muted);">${el.visible !== false ? 'On' : 'Off'}</span>
|
|
2958
|
+
</div>
|
|
2959
|
+
</div>
|
|
2960
|
+
<div class="prop-row">
|
|
2961
|
+
<span class="prop-label">Print On</span>
|
|
2962
|
+
<select class="prop-input" .value=${el.printOn || 'all'}
|
|
2963
|
+
@change=${(e) => this._updateElementProp('printOn', e.target.value)}>
|
|
2964
|
+
<option value="all">All Pages</option>
|
|
2965
|
+
<option value="first">First Page Only</option>
|
|
2966
|
+
<option value="last">Last Page Only</option>
|
|
2967
|
+
<option value="odd">Odd Pages</option>
|
|
2968
|
+
<option value="even">Even Pages</option>
|
|
2969
|
+
</select>
|
|
2970
|
+
</div>
|
|
2465
2971
|
</div>
|
|
2466
2972
|
</div>
|
|
2467
2973
|
|
|
2974
|
+
<!-- Type-specific sections -->
|
|
2468
2975
|
${el.type === 'text' ? html `
|
|
2469
|
-
<div class="prop-section">
|
|
2470
|
-
|
|
2471
|
-
<div class="prop-
|
|
2472
|
-
<textarea class="prop-input"
|
|
2976
|
+
<div class="prop-section" data-section="data">
|
|
2977
|
+
${sectionHeader('el-content', 'Content')}
|
|
2978
|
+
<div class="prop-section-body ${collapsed.has('el-content') ? 'collapsed' : ''}">
|
|
2979
|
+
<textarea class="prop-input" .value=${el.content}
|
|
2473
2980
|
@change=${(e) => this._updateElementProp('content', e.target.value)}></textarea>
|
|
2474
2981
|
</div>
|
|
2475
2982
|
</div>
|
|
2476
2983
|
` : nothing}
|
|
2477
2984
|
|
|
2478
2985
|
${el.type === 'field' ? html `
|
|
2479
|
-
<div class="prop-section">
|
|
2480
|
-
|
|
2481
|
-
<div class="prop-
|
|
2482
|
-
<
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
<
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
<
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
<
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2986
|
+
<div class="prop-section" data-section="data">
|
|
2987
|
+
${sectionHeader('el-field', 'Field Binding')}
|
|
2988
|
+
<div class="prop-section-body ${collapsed.has('el-field') ? 'collapsed' : ''}">
|
|
2989
|
+
<div class="prop-row">
|
|
2990
|
+
<span class="prop-label">Source</span>
|
|
2991
|
+
<select class="prop-input" .value=${el.dataSource || ''}
|
|
2992
|
+
@change=${(e) => this._updateElementProp('dataSource', e.target.value)}>
|
|
2993
|
+
<option value="">-- select --</option>
|
|
2994
|
+
${this._layout.dataSources.map(ds => html `<option value="${ds.id}">${ds.name}</option>`)}
|
|
2995
|
+
</select>
|
|
2996
|
+
</div>
|
|
2997
|
+
<div class="prop-row">
|
|
2998
|
+
<span class="prop-label">Field</span>
|
|
2999
|
+
${this._renderFieldSelect(el)}
|
|
3000
|
+
</div>
|
|
3001
|
+
<div class="prop-row">
|
|
3002
|
+
<span class="prop-label">Format</span>
|
|
3003
|
+
<input class="prop-input" .value=${el.format || ''}
|
|
3004
|
+
placeholder="$#,##0.00"
|
|
3005
|
+
@change=${(e) => this._updateElementProp('format', e.target.value)} />
|
|
3006
|
+
</div>
|
|
3007
|
+
<div class="prop-row">
|
|
3008
|
+
<span class="prop-label">Aggregate</span>
|
|
3009
|
+
<select class="prop-input" .value=${el.aggregate || ''}
|
|
3010
|
+
@change=${(e) => this._updateElementProp('aggregate', e.target.value || undefined)}>
|
|
3011
|
+
<option value="">None</option>
|
|
3012
|
+
<option>sum</option><option>avg</option><option>count</option><option>min</option><option>max</option>
|
|
3013
|
+
</select>
|
|
3014
|
+
</div>
|
|
2506
3015
|
</div>
|
|
2507
3016
|
</div>
|
|
2508
3017
|
` : nothing}
|
|
2509
3018
|
|
|
2510
3019
|
${el.type === 'image' ? html `
|
|
2511
|
-
<div class="prop-section">
|
|
2512
|
-
|
|
2513
|
-
<div class="prop-
|
|
2514
|
-
<
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
<
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
3020
|
+
<div class="prop-section" data-section="data">
|
|
3021
|
+
${sectionHeader('el-image', 'Image')}
|
|
3022
|
+
<div class="prop-section-body ${collapsed.has('el-image') ? 'collapsed' : ''}">
|
|
3023
|
+
<div class="prop-row">
|
|
3024
|
+
<span class="prop-label">URL/Expr</span>
|
|
3025
|
+
<input class="prop-input" .value=${el.src || ''}
|
|
3026
|
+
@change=${(e) => this._updateElementProp('src', e.target.value)} />
|
|
3027
|
+
</div>
|
|
3028
|
+
<div class="prop-row">
|
|
3029
|
+
<span class="prop-label">Fit</span>
|
|
3030
|
+
<select class="prop-input" .value=${el.fit || 'contain'}
|
|
3031
|
+
@change=${(e) => this._updateElementProp('fit', e.target.value)}>
|
|
3032
|
+
<option>contain</option><option>cover</option><option>fill</option><option>none</option>
|
|
3033
|
+
</select>
|
|
3034
|
+
</div>
|
|
2524
3035
|
</div>
|
|
2525
3036
|
</div>
|
|
2526
3037
|
` : nothing}
|
|
2527
3038
|
|
|
2528
3039
|
${el.type === 'barcode' ? html `
|
|
2529
|
-
<div class="prop-section">
|
|
2530
|
-
|
|
2531
|
-
<div class="prop-
|
|
2532
|
-
<
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
<
|
|
2540
|
-
|
|
2541
|
-
|
|
3040
|
+
<div class="prop-section" data-section="data">
|
|
3041
|
+
${sectionHeader('el-barcode', 'Barcode')}
|
|
3042
|
+
<div class="prop-section-body ${collapsed.has('el-barcode') ? 'collapsed' : ''}">
|
|
3043
|
+
<div class="prop-row">
|
|
3044
|
+
<span class="prop-label">Type</span>
|
|
3045
|
+
<select class="prop-input" .value=${el.barcodeType || 'qr'}
|
|
3046
|
+
@change=${(e) => this._updateElementProp('barcodeType', e.target.value)}>
|
|
3047
|
+
<option>qr</option><option>code128</option><option>ean13</option><option>code39</option>
|
|
3048
|
+
</select>
|
|
3049
|
+
</div>
|
|
3050
|
+
<div class="prop-row">
|
|
3051
|
+
<span class="prop-label">Value</span>
|
|
3052
|
+
<input class="prop-input" .value=${el.value || ''}
|
|
3053
|
+
@change=${(e) => this._updateElementProp('value', e.target.value)} />
|
|
3054
|
+
</div>
|
|
2542
3055
|
</div>
|
|
2543
3056
|
</div>
|
|
2544
3057
|
` : nothing}
|
|
2545
3058
|
|
|
2546
3059
|
${el.type === 'pageNumber' ? html `
|
|
2547
|
-
<div class="prop-section">
|
|
2548
|
-
|
|
2549
|
-
<div class="prop-
|
|
2550
|
-
<
|
|
2551
|
-
|
|
2552
|
-
|
|
3060
|
+
<div class="prop-section" data-section="data">
|
|
3061
|
+
${sectionHeader('el-pagenum', 'Page Number')}
|
|
3062
|
+
<div class="prop-section-body ${collapsed.has('el-pagenum') ? 'collapsed' : ''}">
|
|
3063
|
+
<div class="prop-row">
|
|
3064
|
+
<span class="prop-label">Format</span>
|
|
3065
|
+
<input class="prop-input" .value=${el.format || 'Page {page} of {pages}'}
|
|
3066
|
+
@change=${(e) => this._updateElementProp('format', e.target.value)} />
|
|
3067
|
+
</div>
|
|
2553
3068
|
</div>
|
|
2554
3069
|
</div>
|
|
2555
3070
|
` : nothing}
|
|
2556
3071
|
|
|
2557
3072
|
${el.type === 'currentDate' ? html `
|
|
2558
|
-
<div class="prop-section">
|
|
2559
|
-
|
|
2560
|
-
<div class="prop-
|
|
2561
|
-
<
|
|
2562
|
-
|
|
2563
|
-
|
|
3073
|
+
<div class="prop-section" data-section="data">
|
|
3074
|
+
${sectionHeader('el-date', 'Date Format')}
|
|
3075
|
+
<div class="prop-section-body ${collapsed.has('el-date') ? 'collapsed' : ''}">
|
|
3076
|
+
<div class="prop-row">
|
|
3077
|
+
<span class="prop-label">Format</span>
|
|
3078
|
+
<input class="prop-input" .value=${el.format || 'dd/MM/yyyy'}
|
|
3079
|
+
@change=${(e) => this._updateElementProp('format', e.target.value)} />
|
|
3080
|
+
</div>
|
|
2564
3081
|
</div>
|
|
2565
3082
|
</div>
|
|
2566
3083
|
` : nothing}
|
|
2567
3084
|
|
|
2568
|
-
|
|
3085
|
+
<!-- Z-Order -->
|
|
3086
|
+
<div class="prop-section">
|
|
3087
|
+
${sectionHeader('el-zorder', 'Z-Order')}
|
|
3088
|
+
<div class="prop-section-body ${collapsed.has('el-zorder') ? 'collapsed' : ''}">
|
|
3089
|
+
<div class="z-order-buttons">
|
|
3090
|
+
<button class="btn-small" @click=${() => this._changeZOrder(el.id, this._selectedBandId, 'front')} title="Bring to Front">\u2B06 Front</button>
|
|
3091
|
+
<button class="btn-small" @click=${() => this._changeZOrder(el.id, this._selectedBandId, 'forward')} title="Bring Forward">\u2191 Fwd</button>
|
|
3092
|
+
<button class="btn-small" @click=${() => this._changeZOrder(el.id, this._selectedBandId, 'backward')} title="Send Backward">\u2193 Bwd</button>
|
|
3093
|
+
<button class="btn-small" @click=${() => this._changeZOrder(el.id, this._selectedBandId, 'back')} title="Send to Back">\u2B07 Back</button>
|
|
3094
|
+
</div>
|
|
3095
|
+
</div>
|
|
3096
|
+
</div>
|
|
3097
|
+
|
|
3098
|
+
<!-- Delete -->
|
|
3099
|
+
<div class="prop-section">
|
|
2569
3100
|
<button class="btn-danger" @click=${this._deleteSelectedElement}>Delete Element</button>
|
|
2570
3101
|
</div>
|
|
2571
3102
|
`;
|
|
@@ -2654,9 +3185,18 @@ __decorate([
|
|
|
2654
3185
|
__decorate([
|
|
2655
3186
|
state()
|
|
2656
3187
|
], ZenttoReportDesigner.prototype, "_showBandMenu", void 0);
|
|
3188
|
+
__decorate([
|
|
3189
|
+
state()
|
|
3190
|
+
], ZenttoReportDesigner.prototype, "_mobileLeftOpen", void 0);
|
|
3191
|
+
__decorate([
|
|
3192
|
+
state()
|
|
3193
|
+
], ZenttoReportDesigner.prototype, "_mobileRightOpen", void 0);
|
|
2657
3194
|
__decorate([
|
|
2658
3195
|
state()
|
|
2659
3196
|
], ZenttoReportDesigner.prototype, "_guides", void 0);
|
|
3197
|
+
__decorate([
|
|
3198
|
+
state()
|
|
3199
|
+
], ZenttoReportDesigner.prototype, "_collapsedSections", void 0);
|
|
2660
3200
|
__decorate([
|
|
2661
3201
|
state()
|
|
2662
3202
|
], ZenttoReportDesigner.prototype, "_pageSetupDraft", void 0);
|