@zentto/report-designer 1.6.7 → 1.7.1

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.
@@ -72,14 +72,35 @@ const ELEMENT_TYPE_ICONS = {
72
72
  totalPages: '#',
73
73
  };
74
74
  const TOOLBOX_ITEMS = [
75
- { type: 'text', label: 'Text', icon: 'T', preview: 'Aa', description: 'Static text label or title' },
76
- { type: 'field', label: 'Field', icon: 'F', preview: '[Field]', description: 'Data-bound field from a source' },
77
- { type: 'image', label: 'Image', icon: '\u{1F5BC}', preview: '\u{1F304}', description: 'Static or dynamic image' },
78
- { type: 'line', label: 'Line', icon: '/', preview: '\u2500\u2500\u2500', description: 'Horizontal or vertical line' },
79
- { type: 'rect', label: 'Rectangle', icon: '[]', preview: '\u25AD', description: 'Rectangle or filled box' },
80
- { type: 'barcode', label: 'Barcode', icon: 'BC', preview: '||||', description: 'QR, Code128, EAN13, Code39' },
81
- { type: 'pageNumber', label: 'Page #', icon: '#', preview: '1/5', description: 'Current page number' },
82
- { type: 'currentDate', label: 'Date', icon: 'D', preview: '27/03', description: 'Current date/time stamp' },
75
+ // ─── Text & Data ───
76
+ { type: 'text', label: 'Text', icon: 'T', preview: 'Aa', description: 'Static text label or title', category: 'Text & Data' },
77
+ { type: 'field', label: 'Field', icon: 'F', preview: '[Field]', description: 'Data-bound field from a source', category: 'Text & Data' },
78
+ { type: 'text', label: 'Title', icon: 'H', preview: 'Title', description: 'Large bold title text', category: 'Text & Data', extraProps: { content: 'Title', style: { fontSize: 18, fontWeight: 'bold' }, width: 80, height: 10 } },
79
+ { type: 'text', label: 'Subtitle', icon: 'h', preview: 'Subtitle', description: 'Medium subtitle text', category: 'Text & Data', extraProps: { content: 'Subtitle', style: { fontSize: 13, fontWeight: 'bold', color: '#666' }, width: 60, height: 8 } },
80
+ { type: 'text', label: 'Label', icon: 'L', preview: 'Label:', description: 'Small bold label', category: 'Text & Data', extraProps: { content: 'Label:', style: { fontSize: 9, fontWeight: 'bold' }, width: 25, height: 6 } },
81
+ // ─── Shapes & Lines ───
82
+ { type: 'line', label: 'Line', icon: '\u2500', preview: '\u2500\u2500\u2500', description: 'Horizontal line separator', category: 'Shapes' },
83
+ { type: 'line', label: 'Thick Line', icon: '\u2501', preview: '\u2501\u2501\u2501', description: 'Thick separator line', category: 'Shapes', extraProps: { lineStyle: { color: '#000', width: 3, style: 'solid' }, width: 80 } },
84
+ { type: 'line', label: 'Dashed Line', icon: '\u2504', preview: '- - -', description: 'Dashed separator', category: 'Shapes', extraProps: { lineStyle: { color: '#999', width: 1, style: 'dashed' }, width: 80 } },
85
+ { type: 'rect', label: 'Rectangle', icon: '\u25AD', preview: '\u25AD', description: 'Rectangle or filled box', category: 'Shapes' },
86
+ { type: 'rect', label: 'Filled Box', icon: '\u25A0', preview: '\u25A0', description: 'Filled color box', category: 'Shapes', extraProps: { fill: '#1976d2', lineStyle: { width: 0 }, width: 30, height: 15 } },
87
+ { type: 'rect', label: 'Rounded Box', icon: '\u25A2', preview: '\u25A2', description: 'Box with rounded corners', category: 'Shapes', extraProps: { cornerRadius: 6, lineStyle: { color: '#ccc', width: 1, style: 'solid' }, width: 40, height: 15 } },
88
+ // ─── Media ───
89
+ { type: 'image', label: 'Image', icon: '\u{1F5BC}', preview: '\u{1F304}', description: 'Static or dynamic image', category: 'Media' },
90
+ { type: 'barcode', label: 'QR Code', icon: '\u2588', preview: 'QR', description: 'QR code from field or value', category: 'Media', extraProps: { barcodeType: 'qr', value: '12345', width: 20, height: 20 } },
91
+ { type: 'barcode', label: 'Barcode 128', icon: '|||', preview: '||||', description: 'Code128 barcode', category: 'Media', extraProps: { barcodeType: 'code128', value: '12345' } },
92
+ { type: 'barcode', label: 'EAN-13', icon: '\u2584', preview: 'EAN', description: 'EAN-13 product barcode', category: 'Media', extraProps: { barcodeType: 'ean13', value: '5901234123457' } },
93
+ // ─── Charts ───
94
+ { type: 'chart', label: 'Bar Chart', icon: '\u{1F4CA}', preview: '\u{2581}\u{2583}\u{2585}\u{2587}', description: 'Bar chart visualization', category: 'Charts', extraProps: { chartType: 'bar', labelField: '', valueFields: [], width: 80, height: 50 } },
95
+ { type: 'chart', label: 'Line Chart', icon: '\u{1F4C8}', preview: '\u{2571}\u{2572}\u{2571}', description: 'Line chart with trends', category: 'Charts', extraProps: { chartType: 'line', labelField: '', valueFields: [], width: 80, height: 50 } },
96
+ { type: 'chart', label: 'Pie Chart', icon: '\u{1F967}', preview: '\u{25D4}', description: 'Pie/donut chart', category: 'Charts', extraProps: { chartType: 'pie', labelField: '', valueFields: [], width: 50, height: 50 } },
97
+ { type: 'chart', label: 'Area Chart', icon: '\u{1F30A}', preview: '\u{2586}\u{2584}\u{2582}', description: 'Area chart with fill', category: 'Charts', extraProps: { chartType: 'area', labelField: '', valueFields: [], width: 80, height: 50 } },
98
+ { type: 'chart', label: 'Combo Chart', icon: '\u{1F4CA}', preview: '\u{2581}\u{2571}\u{2585}', description: 'Bars + lines combined', category: 'Charts', extraProps: { chartType: 'combo', labelField: '', valueFields: [], width: 80, height: 50 } },
99
+ // ─── Page & Special ───
100
+ { type: 'pageNumber', label: 'Page Number', icon: '#', preview: '1/5', description: 'Page N of M', category: 'Special' },
101
+ { type: 'currentDate', label: 'Print Date', icon: '\u{1F4C5}', preview: '27/03', description: 'Current date/time', category: 'Special' },
102
+ { type: 'currentDate', label: 'Print Time', icon: '\u{1F552}', preview: '14:30', description: 'Current time only', category: 'Special', extraProps: { format: 'HH:mm:ss' } },
103
+ { type: 'text', label: 'Watermark', icon: '\u{1F4A7}', preview: 'DRAFT', description: 'Semi-transparent watermark text', category: 'Special', extraProps: { content: 'DRAFT', style: { fontSize: 48, fontWeight: 'bold', color: '#ccc', textAlign: 'center', opacity: 0.3 }, width: 120, height: 30 } },
83
104
  ];
84
105
  let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
85
106
  constructor() {
@@ -130,6 +151,10 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
130
151
  this._zenttoConnectorOpen = false;
131
152
  this._showCode = false;
132
153
  this._formulaEditorOpen = false;
154
+ this._showLeftPanel = true;
155
+ this._showRightPanel = true;
156
+ this._leftWidth = 200;
157
+ this._rightWidth = 240;
133
158
  this._undoStack = [];
134
159
  this._redoStack = [];
135
160
  this._editingReportName = false;
@@ -219,12 +244,15 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
219
244
 
220
245
  .designer {
221
246
  display: grid;
222
- grid-template-columns: 220px 1fr 260px;
223
247
  grid-template-rows: auto auto 1fr;
224
248
  height: 100%;
225
249
  background: var(--zrd-bg);
226
250
  color: var(--zrd-text);
227
251
  }
252
+ .designer.panels-both { grid-template-columns: var(--left-w, 200px) 1fr var(--right-w, 240px); }
253
+ .designer.panels-left { grid-template-columns: var(--left-w, 200px) 1fr; }
254
+ .designer.panels-right { grid-template-columns: 1fr var(--right-w, 240px); }
255
+ .designer.panels-none { grid-template-columns: 1fr; }
228
256
 
229
257
  /* ─── Top Toolbar ─── */
230
258
  .top-toolbar {
@@ -296,53 +324,94 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
296
324
  color: var(--zrd-accent);
297
325
  font-weight: 600;
298
326
  }
299
- .panel-content { padding: 8px; }
327
+ .panel-content {
328
+ padding: 4px;
329
+ overflow-y: auto;
330
+ flex: 1;
331
+ }
332
+
333
+ /* ─── Resize Handle (between panels) ─── */
334
+ .panel-resize {
335
+ width: 4px;
336
+ cursor: col-resize;
337
+ background: transparent;
338
+ transition: background 0.15s;
339
+ flex-shrink: 0;
340
+ }
341
+ .panel-resize:hover, .panel-resize.active { background: var(--zrd-accent); }
342
+
343
+ /* ─── Panel Toggle Buttons (in toolbar) ─── */
344
+ .panel-toggle {
345
+ padding: 2px 6px;
346
+ border: 1px solid var(--zrd-border);
347
+ border-radius: 3px;
348
+ cursor: pointer;
349
+ font-size: 10px;
350
+ background: var(--zrd-panel-bg);
351
+ color: var(--zrd-text);
352
+ }
353
+ .panel-toggle:hover { background: var(--zrd-accent-light); }
354
+ .panel-toggle.hidden { opacity: 0.4; text-decoration: line-through; }
300
355
 
301
- /* ─── Toolbox ─── */
356
+ /* ─── Toolbox (2-column grid) ─── */
357
+ .toolbox-grid {
358
+ display: grid;
359
+ grid-template-columns: 1fr 1fr;
360
+ gap: 3px;
361
+ padding: 2px;
362
+ }
363
+ .toolbox-category {
364
+ grid-column: 1 / -1;
365
+ padding: 4px 6px 2px;
366
+ font-size: 9px;
367
+ font-weight: 600;
368
+ color: var(--zrd-text-muted);
369
+ text-transform: uppercase;
370
+ letter-spacing: 0.5px;
371
+ margin-top: 2px;
372
+ }
302
373
  .toolbox-item {
303
374
  display: flex;
304
- align-items: flex-start;
305
- gap: 8px;
306
- padding: 8px;
375
+ flex-direction: column;
376
+ align-items: center;
377
+ gap: 2px;
378
+ padding: 6px 4px;
307
379
  border: 1px solid var(--zrd-border);
308
380
  border-radius: 4px;
309
- margin-bottom: 4px;
310
381
  cursor: grab;
311
382
  user-select: none;
383
+ text-align: center;
384
+ min-height: 48px;
385
+ justify-content: center;
312
386
  }
313
- .toolbox-item:hover { background: var(--zrd-accent-light); }
387
+ .toolbox-item:hover { background: var(--zrd-accent-light); border-color: var(--zrd-accent); }
388
+ .toolbox-item:active { cursor: grabbing; }
314
389
  .toolbox-icon {
315
- width: 36px;
316
- height: 36px;
390
+ width: 28px;
391
+ height: 28px;
317
392
  display: flex;
318
393
  align-items: center;
319
394
  justify-content: center;
320
395
  background: var(--zrd-accent-light);
321
396
  border-radius: 4px;
322
397
  font-weight: bold;
323
- font-size: 14px;
398
+ font-size: 13px;
324
399
  color: var(--zrd-accent);
325
400
  flex-shrink: 0;
326
401
  }
327
402
  .toolbox-icon[data-type="text"] { font-family: Georgia, serif; font-style: italic; }
328
- .toolbox-icon[data-type="field"] { background: #e3f2fd; font-size: 11px; }
329
- .toolbox-icon[data-type="line"] { font-size: 16px; letter-spacing: -3px; }
330
- .toolbox-icon[data-type="rect"] { font-size: 20px; }
331
- .toolbox-icon[data-type="barcode"] { font-family: monospace; letter-spacing: -1px; font-size: 16px; }
332
- .toolbox-info {
333
- display: flex;
334
- flex-direction: column;
335
- gap: 2px;
336
- min-width: 0;
337
- }
403
+ .toolbox-icon[data-type="field"] { background: #e3f2fd; font-size: 10px; }
404
+ .toolbox-icon[data-type="line"] { font-size: 14px; letter-spacing: -3px; }
405
+ .toolbox-icon[data-type="rect"] { font-size: 18px; }
406
+ .toolbox-icon[data-type="barcode"] { font-family: monospace; letter-spacing: -1px; font-size: 14px; }
338
407
  .toolbox-label {
339
- font-weight: 600;
340
- font-size: 12px;
341
- }
342
- .toolbox-desc {
343
- font-size: 10px;
344
- color: var(--zrd-text-muted);
345
- line-height: 1.3;
408
+ font-size: 9px;
409
+ font-weight: 500;
410
+ line-height: 1.1;
411
+ overflow: hidden;
412
+ text-overflow: ellipsis;
413
+ white-space: nowrap;
414
+ max-width: 100%;
346
415
  }
347
416
 
348
417
  /* ─── Canvas Area ─── */
@@ -1163,9 +1232,7 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
1163
1232
 
1164
1233
  /* ─── Responsive: Grid Layout ─── */
1165
1234
  @media (max-width: 1024px) {
1166
- .designer {
1167
- grid-template-columns: 180px 1fr 220px;
1168
- }
1235
+ .designer.panels-both { grid-template-columns: 160px 1fr 200px; }
1169
1236
  }
1170
1237
  @media (max-width: 768px) {
1171
1238
  .designer {
@@ -1575,6 +1642,9 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
1575
1642
  case 'barcode':
1576
1643
  element = { ...baseProps, type: 'barcode', barcodeType: 'qr', value: '12345', width: 15, height: 15 };
1577
1644
  break;
1645
+ case 'chart':
1646
+ element = { ...baseProps, type: 'chart', chartType: 'bar', dataSource: '', labelField: '', valueFields: [], width: 80, height: 50 };
1647
+ break;
1578
1648
  case 'pageNumber':
1579
1649
  element = { ...baseProps, type: 'pageNumber', format: 'Page {page} of {pages}', width: 30 };
1580
1650
  break;
@@ -1901,7 +1971,10 @@ let ZenttoReportDesigner = class ZenttoReportDesigner extends LitElement {
1901
1971
  });
1902
1972
  }
1903
1973
  else if (type) {
1904
- this._addElementToBand(bandId, type);
1974
+ // Check for extra props from toolbox items (Title, Watermark, etc.)
1975
+ const extraJson = e.dataTransfer?.getData('extra-props');
1976
+ const extraProps = extraJson ? JSON.parse(extraJson) : undefined;
1977
+ this._addElementToBand(bandId, type, extraProps);
1905
1978
  }
1906
1979
  }
1907
1980
  _onBandDragOver(e) {
@@ -2327,7 +2400,8 @@ REGLAS:
2327
2400
  const marginLeftPx = this._toPx(this._layout.margins.left);
2328
2401
  const gridSize = this._toPx(this.gridSnap);
2329
2402
  return html `
2330
- <div class="designer">
2403
+ <div class="designer ${this._showLeftPanel && this._showRightPanel ? 'panels-both' : this._showLeftPanel ? 'panels-left' : this._showRightPanel ? 'panels-right' : 'panels-none'}"
2404
+ style="--left-w:${this._leftWidth}px;--right-w:${this._rightWidth}px">
2331
2405
  ${this._renderTopToolbar()}
2332
2406
  ${this._renderPageSetupBar()}
2333
2407
 
@@ -2335,7 +2409,7 @@ REGLAS:
2335
2409
  <div class="mobile-overlay" @click=${() => { this._mobileLeftOpen = false; this._mobileRightOpen = false; }}></div>
2336
2410
  ` : nothing}
2337
2411
 
2338
- <div class="left-panel ${this._mobileLeftOpen ? 'mobile-open' : ''}">
2412
+ ${this._showLeftPanel ? html `<div class="left-panel ${this._mobileLeftOpen ? 'mobile-open' : ''}" style="display:flex;flex-direction:column;overflow:hidden;">
2339
2413
  <div class="panel-tabs">
2340
2414
  <div class="panel-tab ${this._activePanel === 'toolbox' ? 'active' : ''}"
2341
2415
  @click=${() => this._activePanel = 'toolbox'}>Toolbox</div>
@@ -2346,7 +2420,7 @@ REGLAS:
2346
2420
  ${this._activePanel === 'toolbox' ? this._renderToolbox() : null}
2347
2421
  ${this._activePanel === 'data' ? this._renderDataPanel() : null}
2348
2422
  </div>
2349
- </div>
2423
+ </div>` : nothing}
2350
2424
 
2351
2425
  <div class="canvas-area"
2352
2426
  @wheel=${(e) => this._handleWheel(e)}
@@ -2397,9 +2471,9 @@ REGLAS:
2397
2471
  </div>
2398
2472
  </div>
2399
2473
 
2400
- <div class="right-panel ${this._mobileRightOpen ? 'mobile-open' : ''}">
2474
+ ${this._showRightPanel ? html `<div class="right-panel ${this._mobileRightOpen ? 'mobile-open' : ''}">
2401
2475
  ${this._renderPropertiesPanel()}
2402
- </div>
2476
+ </div>` : nothing}
2403
2477
  </div>
2404
2478
  ${this._renderContextMenu()}
2405
2479
  ${this._renderPageSetupDialog()}
@@ -2978,23 +3052,51 @@ REGLAS:
2978
3052
  <button @click=${() => { this._zoom = 1.5; }} title="Zoom 150%">150%</button>
2979
3053
  <button @click=${() => { this._zoom = 2; }} title="Zoom 200%">200%</button>
2980
3054
  </div>
3055
+
3056
+ <!-- Panel Toggles -->
3057
+ <div style="display:flex;gap:2px;margin-left:4px;border-left:1px solid var(--zrd-border);padding-left:6px;">
3058
+ <button class="panel-toggle ${this._showLeftPanel ? '' : 'hidden'}"
3059
+ title="${this._showLeftPanel ? 'Hide Toolbox/Data' : 'Show Toolbox/Data'}"
3060
+ @click=${() => { this._showLeftPanel = !this._showLeftPanel; }}>
3061
+ \u{2630}
3062
+ </button>
3063
+ <button class="panel-toggle ${this._showRightPanel ? '' : 'hidden'}"
3064
+ title="${this._showRightPanel ? 'Hide Properties' : 'Show Properties'}"
3065
+ @click=${() => { this._showRightPanel = !this._showRightPanel; }}>
3066
+ \u{2699}
3067
+ </button>
3068
+ </div>
2981
3069
  </div>
2982
3070
  `;
2983
3071
  }
2984
3072
  // ─── Toolbox ──────────────────────────────────────────────────
2985
3073
  _renderToolbox() {
3074
+ const categories = new Map();
3075
+ for (const item of TOOLBOX_ITEMS) {
3076
+ if (!categories.has(item.category))
3077
+ categories.set(item.category, []);
3078
+ categories.get(item.category).push(item);
3079
+ }
2986
3080
  return html `
2987
- ${TOOLBOX_ITEMS.map(item => html `
2988
- <div class="toolbox-item"
2989
- draggable="true"
2990
- @dragstart=${(e) => this._onToolboxDragStart(e, item.type)}>
2991
- <div class="toolbox-icon" data-type="${item.type}">${item.preview}</div>
2992
- <div class="toolbox-info">
2993
- <span class="toolbox-label">${item.label}</span>
2994
- <span class="toolbox-desc">${item.description}</span>
2995
- </div>
2996
- </div>
2997
- `)}
3081
+ <div class="toolbox-grid">
3082
+ ${[...categories.entries()].map(([cat, items]) => html `
3083
+ <div class="toolbox-category">${cat}</div>
3084
+ ${items.map(item => html `
3085
+ <div class="toolbox-item"
3086
+ draggable="true"
3087
+ title="${item.description}"
3088
+ @dragstart=${(e) => {
3089
+ this._onToolboxDragStart(e, item.type);
3090
+ if (item.extraProps) {
3091
+ e.dataTransfer?.setData('extra-props', JSON.stringify(item.extraProps));
3092
+ }
3093
+ }}>
3094
+ <div class="toolbox-icon" data-type="${item.type}">${item.preview}</div>
3095
+ <span class="toolbox-label">${item.label}</span>
3096
+ </div>
3097
+ `)}
3098
+ `)}
3099
+ </div>
2998
3100
  `;
2999
3101
  }
3000
3102
  // ─── Data Panel (Tree + Actions) ─────────────────────────────
@@ -3023,31 +3125,28 @@ REGLAS:
3023
3125
  ${this._renderOverlays()}
3024
3126
  `;
3025
3127
  }
3026
- // Action buttons bar
3027
- const hasActions = this.dbProvider || this.zenttoProvider || this._layout.dataSources.length > 0;
3128
+ // Action buttons bar — always show so user can connect data sources
3028
3129
  return html `
3029
- ${hasActions ? html `
3030
- <div style="padding:6px 8px;display:flex;gap:4px;border-bottom:1px solid var(--zrd-border,#e0e0e0);flex-shrink:0;flex-wrap:wrap;">
3031
- ${this.dbProvider ? html `
3032
- <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:var(--zrd-primary,#1976d2);min-width:70px;"
3033
- @click=${() => { this._dbConnectorOpen = true; }}>
3034
- \u{1F5C4} DB
3035
- </button>
3036
- ` : nothing}
3037
- ${this.zenttoProvider ? html `
3038
- <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:#e65100;min-width:70px;"
3039
- @click=${() => { this._zenttoConnectorOpen = true; }}>
3040
- \u{1F310} Zentto
3041
- </button>
3042
- ` : nothing}
3043
- ${this._layout.dataSources.length > 0 ? html `
3044
- <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:var(--zrd-text,#555);min-width:70px;"
3045
- @click=${() => { this._erOverlayOpen = true; }}>
3046
- \u{1F4CA} ER
3047
- </button>
3048
- ` : nothing}
3049
- </div>
3050
- ` : nothing}
3130
+ <div style="padding:6px 8px;display:flex;gap:4px;border-bottom:1px solid var(--zrd-border,#e0e0e0);flex-shrink:0;flex-wrap:wrap;">
3131
+ <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:var(--zrd-primary,#1976d2);min-width:50px;"
3132
+ @click=${() => { this._dbConnectorOpen = true; }}>
3133
+ \u{1F5C4} DB
3134
+ </button>
3135
+ <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:#e65100;min-width:50px;"
3136
+ @click=${() => { this._zenttoConnectorOpen = true; }}>
3137
+ \u{1F310} Zentto
3138
+ </button>
3139
+ <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:#2e7d32;min-width:50px;"
3140
+ @click=${() => { this.dispatchEvent(new CustomEvent('add-rest-source', { bubbles: true, composed: true })); }}>
3141
+ \u{1F517} REST
3142
+ </button>
3143
+ ${this._layout.dataSources.length > 0 ? html `
3144
+ <button style="flex:1;padding:5px 6px;border:1px solid var(--zrd-border,#ddd);border-radius:4px;cursor:pointer;font-size:10px;font-weight:600;background:var(--zrd-input-bg,#fff);color:var(--zrd-text,#555);min-width:50px;"
3145
+ @click=${() => { this._erOverlayOpen = true; }}>
3146
+ \u{1F4CA} ER
3147
+ </button>
3148
+ ` : nothing}
3149
+ </div>
3051
3150
  <zrd-data-source-tree
3052
3151
  .dataSources=${this._layout.dataSources}
3053
3152
  .relations=${this._layout.relations || []}
@@ -3091,7 +3190,10 @@ REGLAS:
3091
3190
  _renderErOverlay() {
3092
3191
  return html `
3093
3192
  <div style="position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:10000;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px);"
3094
- @click=${() => { this._erOverlayOpen = false; }}>
3193
+ @click=${() => { this._erOverlayOpen = false; }}
3194
+ @keydown=${(e) => { if (e.key === 'Escape')
3195
+ this._erOverlayOpen = false; }}
3196
+ tabindex="-1">
3095
3197
  <div style="background:var(--zrd-panel-bg,#fff);border-radius:12px;box-shadow:0 20px 60px rgba(0,0,0,0.3);width:85vw;height:80vh;display:flex;flex-direction:column;overflow:hidden;"
3096
3198
  @click=${(e) => e.stopPropagation()}>
3097
3199
  <div style="padding:16px 20px;border-bottom:1px solid var(--zrd-border,#eee);display:flex;justify-content:space-between;align-items:center;">
@@ -3103,6 +3205,8 @@ REGLAS:
3103
3205
  <zrd-er-diagram
3104
3206
  .dataSources=${this._layout.dataSources}
3105
3207
  .relations=${this._layout.relations || []}
3208
+ .dbProvider=${this.dbProvider}
3209
+ .zenttoProvider=${this.zenttoProvider}
3106
3210
  @relation-edit=${(e) => {
3107
3211
  this._editingRelation = e.detail.relation;
3108
3212
  this._relationEditorOpen = true;
@@ -3115,8 +3219,7 @@ REGLAS:
3115
3219
  this._layout = { ...this._layout, relations: (this._layout.relations || []).filter(r => r.id !== id) };
3116
3220
  this._commitChange();
3117
3221
  }}
3118
- @add-datasource=${() => { this._erOverlayOpen = false; this._dbConnectorOpen = true; this._activePanel = 'data'; }}
3119
- @add-zentto=${() => { this._erOverlayOpen = false; this._zenttoConnectorOpen = true; this._activePanel = 'data'; }}
3222
+ @datasources-ready=${(e) => this._onDatasourcesReady(e.detail)}
3120
3223
  @add-rest=${() => { }}
3121
3224
  @table-focus=${() => {
3122
3225
  this._erOverlayOpen = false;
@@ -3385,6 +3488,9 @@ REGLAS:
3385
3488
  case 'barcode':
3386
3489
  content = `[${el.barcodeType}]`;
3387
3490
  break;
3491
+ case 'chart':
3492
+ content = `\u{1F4CA} [${el.chartType || 'chart'}]`;
3493
+ break;
3388
3494
  case 'pageNumber':
3389
3495
  content = el.format || 'Page #';
3390
3496
  break;
@@ -4182,6 +4288,18 @@ __decorate([
4182
4288
  __decorate([
4183
4289
  state()
4184
4290
  ], ZenttoReportDesigner.prototype, "_formulaEditorOpen", void 0);
4291
+ __decorate([
4292
+ state()
4293
+ ], ZenttoReportDesigner.prototype, "_showLeftPanel", void 0);
4294
+ __decorate([
4295
+ state()
4296
+ ], ZenttoReportDesigner.prototype, "_showRightPanel", void 0);
4297
+ __decorate([
4298
+ state()
4299
+ ], ZenttoReportDesigner.prototype, "_leftWidth", void 0);
4300
+ __decorate([
4301
+ state()
4302
+ ], ZenttoReportDesigner.prototype, "_rightWidth", void 0);
4185
4303
  __decorate([
4186
4304
  state()
4187
4305
  ], ZenttoReportDesigner.prototype, "_undoStack", void 0);