@zentto/report-designer 1.6.2 → 1.6.4
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/dist/data-panel/code-editor.d.ts +29 -0
- package/dist/data-panel/code-editor.d.ts.map +1 -0
- package/dist/data-panel/code-editor.js +368 -0
- package/dist/data-panel/code-editor.js.map +1 -0
- package/dist/data-panel/data-source-tree.d.ts +18 -15
- package/dist/data-panel/data-source-tree.d.ts.map +1 -1
- package/dist/data-panel/data-source-tree.js +383 -372
- package/dist/data-panel/data-source-tree.js.map +1 -1
- package/dist/zentto-report-designer.d.ts +2 -0
- package/dist/zentto-report-designer.d.ts.map +1 -1
- package/dist/zentto-report-designer.js +60 -3
- package/dist/zentto-report-designer.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @zentto/report-designer —
|
|
2
|
-
//
|
|
3
|
-
// Works standalone (static DataSourceDef[]) and with lazy-loading (DataPanelProvider)
|
|
1
|
+
// @zentto/report-designer — Field Explorer (Crystal Reports-style)
|
|
2
|
+
// Categories: Database Fields, Formula Fields, Parameter Fields, Running Totals, Group Fields, Special Fields
|
|
4
3
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
5
4
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6
5
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -11,20 +10,17 @@ import { LitElement, html, css, nothing } from 'lit';
|
|
|
11
10
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
12
11
|
// ─── Field Type Styling ───────────────────────────────────────────
|
|
13
12
|
const FIELD_TYPE_COLORS = {
|
|
14
|
-
string: '#4caf50',
|
|
15
|
-
|
|
16
|
-
currency: '#ff9800',
|
|
17
|
-
date: '#e91e63',
|
|
18
|
-
boolean: '#9c27b0',
|
|
19
|
-
image: '#00bcd4',
|
|
13
|
+
string: '#4caf50', number: '#1976d2', currency: '#ff9800',
|
|
14
|
+
date: '#e91e63', boolean: '#9c27b0', image: '#00bcd4',
|
|
20
15
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
// ─── Category Icons ───────────────────────────────────────────────
|
|
17
|
+
const CATEGORY_ICONS = {
|
|
18
|
+
database: { icon: '\u{1F5C4}', color: '#1565c0' },
|
|
19
|
+
formula: { icon: '\u{1D465}', color: '#e65100' }, // italic x
|
|
20
|
+
parameter: { icon: '?\u{FE0F}', color: '#2e7d32' },
|
|
21
|
+
runningTotal: { icon: '\u{03A3}', color: '#7b1fa2' }, // sigma
|
|
22
|
+
group: { icon: '\u{229E}', color: '#00695c' },
|
|
23
|
+
special: { icon: '\u{2606}', color: '#37474f' },
|
|
28
24
|
};
|
|
29
25
|
// ─── Component ────────────────────────────────────────────────────
|
|
30
26
|
let DataSourceTree = class DataSourceTree extends LitElement {
|
|
@@ -32,30 +28,19 @@ let DataSourceTree = class DataSourceTree extends LitElement {
|
|
|
32
28
|
super(...arguments);
|
|
33
29
|
this.dataSources = [];
|
|
34
30
|
this.relations = [];
|
|
31
|
+
this.layout = null;
|
|
35
32
|
this.dataProvider = null;
|
|
36
|
-
this._tree = [];
|
|
37
33
|
this._expandedIds = new Set();
|
|
38
34
|
this._searchQuery = '';
|
|
39
35
|
this._contextMenu = null;
|
|
40
|
-
this._closeContextMenu = () => {
|
|
41
|
-
this._contextMenu = null;
|
|
42
|
-
};
|
|
36
|
+
this._closeContextMenu = () => { this._contextMenu = null; };
|
|
43
37
|
}
|
|
44
38
|
static { this.styles = css `
|
|
45
39
|
:host {
|
|
46
|
-
display:
|
|
40
|
+
display: flex; flex-direction: column;
|
|
47
41
|
font-family: 'Segoe UI', Roboto, Arial, sans-serif;
|
|
48
|
-
font-size: 12px;
|
|
49
|
-
|
|
50
|
-
height: 100%;
|
|
51
|
-
overflow: hidden;
|
|
52
|
-
user-select: none;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.tree-container {
|
|
56
|
-
display: flex;
|
|
57
|
-
flex-direction: column;
|
|
58
|
-
height: 100%;
|
|
42
|
+
font-size: 12px; color: var(--zrd-text, #333);
|
|
43
|
+
height: 100%; overflow: hidden; user-select: none;
|
|
59
44
|
}
|
|
60
45
|
|
|
61
46
|
/* ─── Search ──────────────────────────────── */
|
|
@@ -65,408 +50,434 @@ let DataSourceTree = class DataSourceTree extends LitElement {
|
|
|
65
50
|
flex-shrink: 0;
|
|
66
51
|
}
|
|
67
52
|
.search-input {
|
|
68
|
-
width: 100%;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
font-size: 11px;
|
|
73
|
-
outline: none;
|
|
74
|
-
background: var(--zrd-input-bg, #fff);
|
|
75
|
-
color: var(--zrd-text, #333);
|
|
53
|
+
width: 100%; padding: 5px 8px;
|
|
54
|
+
border: 1px solid var(--zrd-border, #ddd); border-radius: 4px;
|
|
55
|
+
font-size: 11px; outline: none;
|
|
56
|
+
background: var(--zrd-input-bg, #fff); color: var(--zrd-text, #333);
|
|
76
57
|
box-sizing: border-box;
|
|
77
58
|
}
|
|
78
|
-
.search-input:focus {
|
|
79
|
-
border-color: var(--zrd-primary, #1976d2);
|
|
80
|
-
}
|
|
81
|
-
.search-input::placeholder {
|
|
82
|
-
color: var(--zrd-text-muted, #999);
|
|
83
|
-
}
|
|
59
|
+
.search-input:focus { border-color: var(--zrd-primary, #1976d2); }
|
|
84
60
|
|
|
85
61
|
/* ─── Tree List ───────────────────────────── */
|
|
86
|
-
.tree-list {
|
|
87
|
-
flex: 1;
|
|
88
|
-
overflow-y: auto;
|
|
89
|
-
overflow-x: hidden;
|
|
90
|
-
padding: 4px 0;
|
|
91
|
-
}
|
|
62
|
+
.tree-list { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 2px 0; }
|
|
92
63
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
align-items: center;
|
|
96
|
-
padding:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
64
|
+
/* ─── Category Header ─────────────────────── */
|
|
65
|
+
.category {
|
|
66
|
+
display: flex; align-items: center;
|
|
67
|
+
padding: 5px 8px; cursor: pointer;
|
|
68
|
+
font-weight: 600; font-size: 11.5px;
|
|
69
|
+
border-bottom: 1px solid var(--zrd-border, #f0f0f0);
|
|
70
|
+
}
|
|
71
|
+
.category:hover { background: var(--zrd-hover, rgba(25,118,210,0.04)); }
|
|
72
|
+
.category-icon {
|
|
73
|
+
width: 18px; height: 18px; display: flex; align-items: center;
|
|
74
|
+
justify-content: center; margin-right: 4px; font-size: 12px;
|
|
75
|
+
border-radius: 3px; flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
.category-label { flex: 1; }
|
|
78
|
+
.category-count {
|
|
79
|
+
font-size: 9px; color: var(--zrd-text-muted, #999);
|
|
80
|
+
font-weight: normal; padding: 1px 5px;
|
|
81
|
+
background: var(--zrd-hover, #f0f0f0); border-radius: 8px;
|
|
82
|
+
}
|
|
83
|
+
.category-chevron {
|
|
84
|
+
font-size: 9px; color: var(--zrd-text-muted, #999);
|
|
85
|
+
transition: transform 0.15s; margin-right: 4px;
|
|
86
|
+
}
|
|
87
|
+
.category-chevron.open { transform: rotate(90deg); }
|
|
88
|
+
.category-add {
|
|
89
|
+
font-size: 14px; color: var(--zrd-primary, #1976d2);
|
|
90
|
+
cursor: pointer; margin-left: 4px; line-height: 1;
|
|
91
|
+
background: none; border: none; padding: 0 2px;
|
|
92
|
+
}
|
|
93
|
+
.category-add:hover { color: #0d47a1; }
|
|
104
94
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
justify-content: center;
|
|
111
|
-
font-size: 10px;
|
|
112
|
-
color: var(--zrd-text-muted, #999);
|
|
113
|
-
flex-shrink: 0;
|
|
114
|
-
transition: transform 0.15s ease;
|
|
115
|
-
}
|
|
116
|
-
.node-chevron.expanded {
|
|
117
|
-
transform: rotate(90deg);
|
|
118
|
-
}
|
|
119
|
-
.node-chevron.leaf {
|
|
120
|
-
visibility: hidden;
|
|
95
|
+
/* ─── Data Source (table) ──────────────────── */
|
|
96
|
+
.ds-node {
|
|
97
|
+
display: flex; align-items: center;
|
|
98
|
+
padding: 3px 8px 3px 24px; cursor: pointer;
|
|
99
|
+
font-size: 11.5px;
|
|
121
100
|
}
|
|
122
|
-
|
|
123
|
-
.
|
|
124
|
-
width:
|
|
125
|
-
|
|
126
|
-
display: flex;
|
|
127
|
-
align-items: center;
|
|
128
|
-
justify-content: center;
|
|
129
|
-
margin-right: 4px;
|
|
130
|
-
font-size: 11px;
|
|
101
|
+
.ds-node:hover { background: var(--zrd-hover, rgba(25,118,210,0.06)); }
|
|
102
|
+
.ds-icon {
|
|
103
|
+
width: 16px; height: 16px; display: flex; align-items: center;
|
|
104
|
+
justify-content: center; margin-right: 4px; font-size: 11px;
|
|
131
105
|
flex-shrink: 0;
|
|
132
|
-
border-radius: 3px;
|
|
133
|
-
}
|
|
134
|
-
.node-icon.connection { background: #e3f2fd; color: #1565c0; }
|
|
135
|
-
.node-icon.schema { background: #fff3e0; color: #e65100; }
|
|
136
|
-
.node-icon.table { background: #e8f5e9; color: #2e7d32; }
|
|
137
|
-
.node-icon.view { background: #f3e5f5; color: #7b1fa2; }
|
|
138
|
-
.node-icon.field { font-weight: 700; font-size: 10px; }
|
|
139
|
-
|
|
140
|
-
.node-label {
|
|
141
|
-
flex: 1;
|
|
142
|
-
overflow: hidden;
|
|
143
|
-
text-overflow: ellipsis;
|
|
144
|
-
font-size: 11.5px;
|
|
145
106
|
}
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
font-size: 9px;
|
|
149
|
-
padding: 1px 5px;
|
|
150
|
-
border-radius: 3px;
|
|
107
|
+
.ds-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
108
|
+
.ds-type {
|
|
109
|
+
font-size: 9px; color: var(--zrd-text-muted, #aaa);
|
|
151
110
|
margin-left: 4px;
|
|
152
|
-
color: #fff;
|
|
153
|
-
flex-shrink: 0;
|
|
154
111
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
margin-right: 2px;
|
|
159
|
-
color: #ff9800;
|
|
112
|
+
.ds-chevron {
|
|
113
|
+
font-size: 8px; color: var(--zrd-text-muted, #999);
|
|
114
|
+
transition: transform 0.15s; margin-right: 4px; width: 10px;
|
|
160
115
|
}
|
|
116
|
+
.ds-chevron.open { transform: rotate(90deg); }
|
|
161
117
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
118
|
+
/* ─── Field Row ───────────────────────────── */
|
|
119
|
+
.field-row {
|
|
120
|
+
display: flex; align-items: center;
|
|
121
|
+
padding: 2px 8px 2px 42px; cursor: grab;
|
|
122
|
+
font-size: 11px; min-height: 20px;
|
|
123
|
+
}
|
|
124
|
+
.field-row:hover { background: var(--zrd-hover, rgba(25,118,210,0.06)); }
|
|
125
|
+
.field-row:active { cursor: grabbing; }
|
|
126
|
+
.field-icon {
|
|
127
|
+
font-size: 10px; font-weight: 700; margin-right: 4px;
|
|
128
|
+
width: 14px; text-align: center; flex-shrink: 0;
|
|
129
|
+
}
|
|
130
|
+
.field-pk { color: #ff9800; font-size: 10px; margin-right: 2px; }
|
|
131
|
+
.field-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
132
|
+
.field-badge {
|
|
133
|
+
font-size: 8px; padding: 1px 4px; border-radius: 3px;
|
|
134
|
+
color: #fff; font-weight: 600; margin-left: 4px; flex-shrink: 0;
|
|
135
|
+
}
|
|
136
|
+
.field-nullable { font-size: 9px; color: var(--zrd-text-muted, #bbb); margin-left: 2px; }
|
|
167
137
|
|
|
168
|
-
/* ───
|
|
169
|
-
.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
cursor: grabbing;
|
|
138
|
+
/* ─── Special/Formula/Param fields ─────────── */
|
|
139
|
+
.special-row {
|
|
140
|
+
display: flex; align-items: center;
|
|
141
|
+
padding: 3px 8px 3px 28px; cursor: grab;
|
|
142
|
+
font-size: 11px;
|
|
174
143
|
}
|
|
144
|
+
.special-row:hover { background: var(--zrd-hover, rgba(25,118,210,0.06)); }
|
|
145
|
+
.special-icon { margin-right: 6px; font-size: 12px; }
|
|
175
146
|
|
|
176
147
|
/* ─── Context Menu ────────────────────────── */
|
|
177
148
|
.context-menu {
|
|
178
|
-
position: fixed;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
183
|
-
padding: 4px 0;
|
|
184
|
-
min-width: 160px;
|
|
185
|
-
z-index: 10000;
|
|
186
|
-
}
|
|
187
|
-
.context-menu-item {
|
|
188
|
-
padding: 6px 14px;
|
|
189
|
-
cursor: pointer;
|
|
190
|
-
font-size: 11.5px;
|
|
191
|
-
display: flex;
|
|
192
|
-
align-items: center;
|
|
193
|
-
gap: 8px;
|
|
194
|
-
}
|
|
195
|
-
.context-menu-item:hover {
|
|
196
|
-
background: var(--zrd-hover, rgba(25, 118, 210, 0.08));
|
|
149
|
+
position: fixed; background: var(--zrd-panel-bg, #fff);
|
|
150
|
+
border: 1px solid var(--zrd-border, #ddd); border-radius: 6px;
|
|
151
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.15); padding: 4px 0;
|
|
152
|
+
min-width: 160px; z-index: 10000;
|
|
197
153
|
}
|
|
198
|
-
.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
margin: 4px 0;
|
|
154
|
+
.cm-item {
|
|
155
|
+
padding: 5px 14px; cursor: pointer; font-size: 11px;
|
|
156
|
+
display: flex; align-items: center; gap: 6px;
|
|
202
157
|
}
|
|
158
|
+
.cm-item:hover { background: var(--zrd-hover, rgba(25,118,210,0.08)); }
|
|
159
|
+
.cm-divider { height: 1px; background: var(--zrd-border, #e0e0e0); margin: 3px 0; }
|
|
203
160
|
|
|
204
|
-
/* ───
|
|
205
|
-
.
|
|
206
|
-
|
|
207
|
-
text-align: center;
|
|
208
|
-
color: var(--zrd-text-muted, #999);
|
|
209
|
-
font-size: 11.5px;
|
|
210
|
-
line-height: 1.5;
|
|
161
|
+
/* ─── Relation indicator ──────────────────── */
|
|
162
|
+
.rel-badge {
|
|
163
|
+
font-size: 9px; color: var(--zrd-primary, #1976d2); margin-left: 3px;
|
|
211
164
|
}
|
|
212
165
|
|
|
213
|
-
.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
height: 12px;
|
|
217
|
-
border: 2px solid var(--zrd-border, #ddd);
|
|
218
|
-
border-top-color: var(--zrd-primary, #1976d2);
|
|
219
|
-
border-radius: 50%;
|
|
220
|
-
animation: spin 0.6s linear infinite;
|
|
221
|
-
}
|
|
222
|
-
@keyframes spin { to { transform: rotate(360deg); } }
|
|
223
|
-
|
|
224
|
-
/* ─── Relations Badge ─────────────────────── */
|
|
225
|
-
.relation-indicator {
|
|
226
|
-
font-size: 9px;
|
|
227
|
-
color: var(--zrd-primary, #1976d2);
|
|
228
|
-
margin-left: 4px;
|
|
166
|
+
.empty-hint {
|
|
167
|
+
padding: 10px 12px; text-align: center;
|
|
168
|
+
color: var(--zrd-text-muted, #999); font-size: 11px; line-height: 1.5;
|
|
229
169
|
}
|
|
230
170
|
`; }
|
|
231
171
|
// ─── Lifecycle ────────────────────────────────────────────────
|
|
232
172
|
connectedCallback() {
|
|
233
173
|
super.connectedCallback();
|
|
234
174
|
document.addEventListener('click', this._closeContextMenu);
|
|
175
|
+
// Auto-expand Database Fields by default
|
|
176
|
+
this._expandedIds.add('cat::database');
|
|
235
177
|
}
|
|
236
178
|
disconnectedCallback() {
|
|
237
179
|
super.disconnectedCallback();
|
|
238
180
|
document.removeEventListener('click', this._closeContextMenu);
|
|
239
181
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Group data sources by connectionId (or 'local' for static)
|
|
249
|
-
const byConnection = new Map();
|
|
250
|
-
for (const ds of this.dataSources) {
|
|
251
|
-
const connId = ds.connectionId || 'local';
|
|
252
|
-
if (!byConnection.has(connId))
|
|
253
|
-
byConnection.set(connId, []);
|
|
254
|
-
byConnection.get(connId).push(ds);
|
|
255
|
-
}
|
|
256
|
-
for (const [connId, sources] of byConnection) {
|
|
257
|
-
// Group by schema
|
|
258
|
-
const bySchema = new Map();
|
|
259
|
-
for (const ds of sources) {
|
|
260
|
-
const schema = ds.schema || 'default';
|
|
261
|
-
if (!bySchema.has(schema))
|
|
262
|
-
bySchema.set(schema, []);
|
|
263
|
-
bySchema.get(schema).push(ds);
|
|
264
|
-
}
|
|
265
|
-
const schemaNodes = [];
|
|
266
|
-
for (const [schemaName, schemaSources] of bySchema) {
|
|
267
|
-
const tableNodes = schemaSources.map(ds => {
|
|
268
|
-
const isView = ds.table?.toLowerCase().startsWith('v_') || ds.table?.toLowerCase().startsWith('vw_');
|
|
269
|
-
const fieldNodes = (ds.fields || []).map(f => ({
|
|
270
|
-
id: `${ds.id}::${f.name}`,
|
|
271
|
-
label: f.label || f.name,
|
|
272
|
-
icon: FIELD_TYPE_ICONS[f.type] || 'F',
|
|
273
|
-
type: 'field',
|
|
274
|
-
fieldDef: f,
|
|
275
|
-
dataSourceId: ds.id,
|
|
276
|
-
}));
|
|
277
|
-
return {
|
|
278
|
-
id: `table::${ds.id}`,
|
|
279
|
-
label: ds.table || ds.name,
|
|
280
|
-
icon: isView ? '\u{1F441}' : '\u{1F4CB}',
|
|
281
|
-
type: (isView ? 'view' : 'table'),
|
|
282
|
-
children: fieldNodes,
|
|
283
|
-
dataSourceId: ds.id,
|
|
284
|
-
schemaName: ds.schema,
|
|
285
|
-
tableName: ds.table || ds.name,
|
|
286
|
-
connectionId: ds.connectionId,
|
|
287
|
-
};
|
|
288
|
-
});
|
|
289
|
-
if (bySchema.size > 1 || schemaName !== 'default') {
|
|
290
|
-
schemaNodes.push({
|
|
291
|
-
id: `schema::${connId}::${schemaName}`,
|
|
292
|
-
label: schemaName,
|
|
293
|
-
icon: '\u{1F4C1}',
|
|
294
|
-
type: 'schema',
|
|
295
|
-
children: tableNodes,
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
schemaNodes.push(...tableNodes);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (byConnection.size > 1) {
|
|
303
|
-
tree.push({
|
|
304
|
-
id: `conn::${connId}`,
|
|
305
|
-
label: connId === 'local' ? 'Local Data' : connId,
|
|
306
|
-
icon: '\u{1F5C4}',
|
|
307
|
-
type: 'connection',
|
|
308
|
-
children: schemaNodes,
|
|
309
|
-
connectionId: connId,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
tree.push(...schemaNodes);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
this._tree = tree;
|
|
317
|
-
}
|
|
318
|
-
// ─── Filtering ────────────────────────────────────────────────
|
|
319
|
-
_matchesSearch(node, query) {
|
|
320
|
-
if (!query)
|
|
321
|
-
return true;
|
|
322
|
-
const q = query.toLowerCase();
|
|
323
|
-
if (node.label.toLowerCase().includes(q))
|
|
324
|
-
return true;
|
|
325
|
-
if (node.children) {
|
|
326
|
-
return node.children.some(c => this._matchesSearch(c, q));
|
|
327
|
-
}
|
|
328
|
-
return false;
|
|
182
|
+
// ─── Toggle / Drag ────────────────────────────────────────────
|
|
183
|
+
_toggle(id) {
|
|
184
|
+
const s = new Set(this._expandedIds);
|
|
185
|
+
if (s.has(id))
|
|
186
|
+
s.delete(id);
|
|
187
|
+
else
|
|
188
|
+
s.add(id);
|
|
189
|
+
this._expandedIds = s;
|
|
329
190
|
}
|
|
330
|
-
|
|
331
|
-
_toggleNode(nodeId) {
|
|
332
|
-
const expanded = new Set(this._expandedIds);
|
|
333
|
-
if (expanded.has(nodeId)) {
|
|
334
|
-
expanded.delete(nodeId);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
expanded.add(nodeId);
|
|
338
|
-
}
|
|
339
|
-
this._expandedIds = expanded;
|
|
340
|
-
}
|
|
341
|
-
_onFieldDragStart(e, node) {
|
|
342
|
-
if (!node.fieldDef || !node.dataSourceId)
|
|
343
|
-
return;
|
|
191
|
+
_onFieldDrag(e, dsId, fieldName) {
|
|
344
192
|
e.dataTransfer?.setData('element-type', 'field');
|
|
345
|
-
e.dataTransfer?.setData('field-ds',
|
|
346
|
-
e.dataTransfer?.setData('field-name',
|
|
193
|
+
e.dataTransfer?.setData('field-ds', dsId);
|
|
194
|
+
e.dataTransfer?.setData('field-name', fieldName);
|
|
195
|
+
e.dataTransfer.effectAllowed = 'copy';
|
|
196
|
+
}
|
|
197
|
+
_onSpecialDrag(e, type) {
|
|
198
|
+
e.dataTransfer?.setData('element-type', type);
|
|
347
199
|
e.dataTransfer.effectAllowed = 'copy';
|
|
348
|
-
this.dispatchEvent(new CustomEvent('field-drag-start', {
|
|
349
|
-
detail: { dataSourceId: node.dataSourceId, field: node.fieldDef },
|
|
350
|
-
bubbles: true, composed: true,
|
|
351
|
-
}));
|
|
352
200
|
}
|
|
353
|
-
|
|
354
|
-
if (!node.fieldDef || !node.dataSourceId)
|
|
355
|
-
return;
|
|
201
|
+
_onFieldDblClick(dsId, fieldName) {
|
|
356
202
|
this.dispatchEvent(new CustomEvent('field-double-click', {
|
|
357
|
-
detail: { dataSourceId:
|
|
203
|
+
detail: { dataSourceId: dsId, field: { name: fieldName } },
|
|
358
204
|
bubbles: true, composed: true,
|
|
359
205
|
}));
|
|
360
206
|
}
|
|
361
|
-
|
|
207
|
+
// ─── Context Menu ─────────────────────────────────────────────
|
|
208
|
+
_onContextMenu(e, dsId, category) {
|
|
362
209
|
e.preventDefault();
|
|
363
210
|
e.stopPropagation();
|
|
364
|
-
this._contextMenu = { x: e.clientX, y: e.clientY,
|
|
211
|
+
this._contextMenu = { x: e.clientX, y: e.clientY, dsId, category };
|
|
365
212
|
}
|
|
366
213
|
_contextAction(action) {
|
|
367
|
-
const
|
|
368
|
-
if (!node)
|
|
369
|
-
return;
|
|
214
|
+
const ctx = this._contextMenu;
|
|
370
215
|
this._contextMenu = null;
|
|
371
216
|
this.dispatchEvent(new CustomEvent('tree-action', {
|
|
372
|
-
detail: { action, node },
|
|
217
|
+
detail: { action, node: { dataSourceId: ctx?.dsId, category: ctx?.category } },
|
|
373
218
|
bubbles: true, composed: true,
|
|
374
219
|
}));
|
|
375
220
|
}
|
|
376
|
-
// ───
|
|
377
|
-
|
|
378
|
-
|
|
221
|
+
// ─── Search ───────────────────────────────────────────────────
|
|
222
|
+
_matches(label) {
|
|
223
|
+
if (!this._searchQuery)
|
|
224
|
+
return true;
|
|
225
|
+
return label.toLowerCase().includes(this._searchQuery.toLowerCase());
|
|
379
226
|
}
|
|
380
|
-
// ───
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
227
|
+
// ─── Relation count ───────────────────────────────────────────
|
|
228
|
+
_relCount(dsId) {
|
|
229
|
+
return (this.relations || []).filter(r => r.parentSource === dsId || r.childSource === dsId).length;
|
|
230
|
+
}
|
|
231
|
+
// ─── Render ───────────────────────────────────────────────────
|
|
232
|
+
render() {
|
|
233
|
+
const layout = this.layout;
|
|
234
|
+
const ds = this.dataSources;
|
|
235
|
+
const params = layout?.parameters || [];
|
|
236
|
+
const runningTotals = layout?.runningTotals || [];
|
|
237
|
+
const groups = layout?.groups || [];
|
|
238
|
+
// Formula fields = any FieldElement with expression
|
|
239
|
+
const formulaFields = [];
|
|
240
|
+
for (const band of (layout?.bands || [])) {
|
|
241
|
+
for (const el of band.elements) {
|
|
242
|
+
if (el.type === 'field' && el.expression) {
|
|
243
|
+
formulaFields.push({ dsId: el.dataSource, field: el.field, expression: el.expression });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
384
246
|
}
|
|
385
|
-
const isExpanded = this._expandedIds.has(node.id) ||
|
|
386
|
-
(this._searchQuery && this._matchesSearch(node, this._searchQuery));
|
|
387
|
-
const hasChildren = node.children && node.children.length > 0;
|
|
388
|
-
const isField = node.type === 'field';
|
|
389
|
-
const paddingLeft = 8 + depth * 14;
|
|
390
|
-
const relations = node.dataSourceId ? this._getRelationsForDs(node.dataSourceId) : [];
|
|
391
247
|
return html `
|
|
392
|
-
<div
|
|
393
|
-
class="
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
\u25B6
|
|
405
|
-
</span>
|
|
406
|
-
<span class="node-icon ${node.type}"
|
|
407
|
-
style="${isField && node.fieldDef ? `color:${FIELD_TYPE_COLORS[node.fieldDef.type] || '#666'}` : ''}">
|
|
408
|
-
${node.icon}
|
|
409
|
-
</span>
|
|
410
|
-
${isField && node.fieldDef?.isPrimaryKey ? html `<span class="pk-icon">\u{1F511}</span>` : nothing}
|
|
411
|
-
<span class="node-label">${node.label}</span>
|
|
412
|
-
${isField && node.fieldDef ? html `
|
|
413
|
-
<span class="node-badge" style="background:${FIELD_TYPE_COLORS[node.fieldDef.type] || '#999'}">
|
|
414
|
-
${node.fieldDef.nativeType || node.fieldDef.type}
|
|
415
|
-
</span>
|
|
416
|
-
${node.fieldDef.nullable ? html `<span class="nullable-icon">?</span>` : nothing}
|
|
417
|
-
` : nothing}
|
|
418
|
-
${(node.type === 'table' || node.type === 'view') && relations.length > 0
|
|
419
|
-
? html `<span class="relation-indicator">\u{1F517}${relations.length}</span>`
|
|
420
|
-
: nothing}
|
|
421
|
-
${node.loading ? html `<span class="loading-indicator"></span>` : nothing}
|
|
248
|
+
<div class="search-box">
|
|
249
|
+
<input class="search-input" type="text" placeholder="Search fields..."
|
|
250
|
+
.value=${this._searchQuery}
|
|
251
|
+
@input=${(e) => { this._searchQuery = e.target.value; }} />
|
|
252
|
+
</div>
|
|
253
|
+
<div class="tree-list">
|
|
254
|
+
${this._renderDatabaseFields(ds)}
|
|
255
|
+
${this._renderFormulaFields(formulaFields)}
|
|
256
|
+
${this._renderParameterFields(params)}
|
|
257
|
+
${this._renderRunningTotals(runningTotals)}
|
|
258
|
+
${this._renderGroupFields(groups)}
|
|
259
|
+
${this._renderSpecialFields()}
|
|
422
260
|
</div>
|
|
423
|
-
${
|
|
424
|
-
? node.children.map(child => this._renderNode(child, depth + 1))
|
|
425
|
-
: nothing}
|
|
261
|
+
${this._renderContextMenu()}
|
|
426
262
|
`;
|
|
427
263
|
}
|
|
428
|
-
|
|
429
|
-
|
|
264
|
+
// ─── 1. Database Fields ───────────────────────────────────────
|
|
265
|
+
_renderDatabaseFields(dataSources) {
|
|
266
|
+
const catId = 'cat::database';
|
|
267
|
+
const isOpen = this._expandedIds.has(catId);
|
|
268
|
+
const cat = CATEGORY_ICONS.database;
|
|
269
|
+
return html `
|
|
270
|
+
<div class="category" @click=${() => this._toggle(catId)}
|
|
271
|
+
@contextmenu=${(e) => this._onContextMenu(e, undefined, 'database')}>
|
|
272
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
273
|
+
<span class="category-icon" style="background:#e3f2fd;color:${cat.color}">${cat.icon}</span>
|
|
274
|
+
<span class="category-label">Database Fields</span>
|
|
275
|
+
<span class="category-count">${dataSources.length}</span>
|
|
276
|
+
<button class="category-add" title="Add Data Source"
|
|
277
|
+
@click=${(e) => { e.stopPropagation(); this._contextAction('add-datasource'); }}>+</button>
|
|
278
|
+
</div>
|
|
279
|
+
${isOpen ? html `
|
|
280
|
+
${dataSources.length === 0
|
|
281
|
+
? html `<div class="empty-hint">No data sources.<br/>Click + to connect a database, Zentto API, or REST endpoint.</div>`
|
|
282
|
+
: dataSources.map(ds => this._renderDataSource(ds))}
|
|
283
|
+
` : nothing}
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
286
|
+
_renderDataSource(ds) {
|
|
287
|
+
const nodeId = `ds::${ds.id}`;
|
|
288
|
+
const isOpen = this._expandedIds.has(nodeId) || !!this._searchQuery;
|
|
289
|
+
const fields = ds.fields || [];
|
|
290
|
+
const rels = this._relCount(ds.id);
|
|
291
|
+
const isView = ds.table?.toLowerCase().startsWith('v_') || ds.table?.toLowerCase().startsWith('vw_');
|
|
292
|
+
const icon = isView ? '\u{1F441}' : '\u{1F4CB}';
|
|
293
|
+
const visibleFields = fields.filter(f => this._matches(f.label || f.name));
|
|
294
|
+
if (this._searchQuery && visibleFields.length === 0 && !this._matches(ds.name))
|
|
430
295
|
return nothing;
|
|
431
|
-
const { x, y, node } = this._contextMenu;
|
|
432
296
|
return html `
|
|
433
|
-
<div class="
|
|
434
|
-
@
|
|
435
|
-
<
|
|
436
|
-
|
|
437
|
-
</
|
|
438
|
-
<
|
|
439
|
-
|
|
440
|
-
</div>
|
|
441
|
-
<div class="context-menu-divider"></div>
|
|
442
|
-
<div class="context-menu-item" @click=${() => this._contextAction('create-relation')}>
|
|
443
|
-
\u{1F517} Create Relation...
|
|
444
|
-
</div>
|
|
445
|
-
<div class="context-menu-item" @click=${() => this._contextAction('show-in-er')}>
|
|
446
|
-
\u{1F4CA} Show in ER Diagram
|
|
447
|
-
</div>
|
|
297
|
+
<div class="ds-node" @click=${() => this._toggle(nodeId)}
|
|
298
|
+
@contextmenu=${(e) => this._onContextMenu(e, ds.id)}>
|
|
299
|
+
<span class="ds-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
300
|
+
<span class="ds-icon">${icon}</span>
|
|
301
|
+
<span class="ds-label">${ds.table || ds.name}</span>
|
|
302
|
+
<span class="ds-type">${ds.type}</span>
|
|
303
|
+
${rels > 0 ? html `<span class="rel-badge">\u{1F517}${rels}</span>` : nothing}
|
|
448
304
|
</div>
|
|
305
|
+
${isOpen ? visibleFields.map(f => this._renderField(ds.id, f)) : nothing}
|
|
449
306
|
`;
|
|
450
307
|
}
|
|
451
|
-
|
|
308
|
+
_renderField(dsId, f) {
|
|
309
|
+
const color = FIELD_TYPE_COLORS[f.type] || '#666';
|
|
452
310
|
return html `
|
|
453
|
-
<div class="
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
311
|
+
<div class="field-row" draggable="true"
|
|
312
|
+
@dragstart=${(e) => this._onFieldDrag(e, dsId, f.name)}
|
|
313
|
+
@dblclick=${() => this._onFieldDblClick(dsId, f.name)}>
|
|
314
|
+
${f.isPrimaryKey ? html `<span class="field-pk">\u{1F511}</span>` : nothing}
|
|
315
|
+
<span class="field-icon" style="color:${color}">\u{25C6}</span>
|
|
316
|
+
<span class="field-label">${f.label || f.name}</span>
|
|
317
|
+
<span class="field-badge" style="background:${color}">${f.nativeType || f.type}</span>
|
|
318
|
+
${f.nullable ? html `<span class="field-nullable">?</span>` : nothing}
|
|
319
|
+
</div>
|
|
320
|
+
`;
|
|
321
|
+
}
|
|
322
|
+
// ─── 2. Formula Fields ────────────────────────────────────────
|
|
323
|
+
_renderFormulaFields(formulas) {
|
|
324
|
+
const catId = 'cat::formula';
|
|
325
|
+
const isOpen = this._expandedIds.has(catId);
|
|
326
|
+
const cat = CATEGORY_ICONS.formula;
|
|
327
|
+
return html `
|
|
328
|
+
<div class="category" @click=${() => this._toggle(catId)}>
|
|
329
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
330
|
+
<span class="category-icon" style="background:#fff3e0;color:${cat.color}">${cat.icon}</span>
|
|
331
|
+
<span class="category-label">Formula Fields</span>
|
|
332
|
+
<span class="category-count">${formulas.length}</span>
|
|
333
|
+
<button class="category-add" title="New Formula"
|
|
334
|
+
@click=${(e) => { e.stopPropagation(); this._contextAction('add-formula'); }}>+</button>
|
|
335
|
+
</div>
|
|
336
|
+
${isOpen ? html `
|
|
337
|
+
${formulas.length === 0
|
|
338
|
+
? html `<div class="empty-hint" style="padding:6px 28px;font-size:10px;text-align:left;">No formula fields. Add expressions to field elements.</div>`
|
|
339
|
+
: formulas.map(f => html `
|
|
340
|
+
<div class="special-row">
|
|
341
|
+
<span class="special-icon" style="color:${cat.color}">${cat.icon}</span>
|
|
342
|
+
<span class="field-label">${f.field}</span>
|
|
343
|
+
<span style="font-size:9px;color:#999;margin-left:4px">${f.expression}</span>
|
|
344
|
+
</div>
|
|
345
|
+
`)}
|
|
346
|
+
` : nothing}
|
|
347
|
+
`;
|
|
348
|
+
}
|
|
349
|
+
// ─── 3. Parameter Fields ──────────────────────────────────────
|
|
350
|
+
_renderParameterFields(params) {
|
|
351
|
+
const catId = 'cat::parameter';
|
|
352
|
+
const isOpen = this._expandedIds.has(catId);
|
|
353
|
+
const cat = CATEGORY_ICONS.parameter;
|
|
354
|
+
return html `
|
|
355
|
+
<div class="category" @click=${() => this._toggle(catId)}>
|
|
356
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
357
|
+
<span class="category-icon" style="background:#e8f5e9;color:${cat.color}">${cat.icon}</span>
|
|
358
|
+
<span class="category-label">Parameter Fields</span>
|
|
359
|
+
<span class="category-count">${params.length}</span>
|
|
360
|
+
<button class="category-add" title="New Parameter"
|
|
361
|
+
@click=${(e) => { e.stopPropagation(); this._contextAction('add-parameter'); }}>+</button>
|
|
362
|
+
</div>
|
|
363
|
+
${isOpen ? html `
|
|
364
|
+
${params.length === 0
|
|
365
|
+
? html `<div class="empty-hint" style="padding:6px 28px;font-size:10px;text-align:left;">No parameters defined.</div>`
|
|
366
|
+
: params.map(p => html `
|
|
367
|
+
<div class="special-row" draggable="true"
|
|
368
|
+
@dragstart=${(e) => this._onSpecialDrag(e, 'parameter')}>
|
|
369
|
+
<span class="special-icon" style="color:${cat.color}">?</span>
|
|
370
|
+
<span class="field-label">${p.label || p.name}</span>
|
|
371
|
+
<span class="field-badge" style="background:${cat.color}">${p.type}</span>
|
|
372
|
+
</div>
|
|
373
|
+
`)}
|
|
374
|
+
` : nothing}
|
|
375
|
+
`;
|
|
376
|
+
}
|
|
377
|
+
// ─── 4. Running Totals ────────────────────────────────────────
|
|
378
|
+
_renderRunningTotals(totals) {
|
|
379
|
+
const catId = 'cat::runningTotal';
|
|
380
|
+
const isOpen = this._expandedIds.has(catId);
|
|
381
|
+
const cat = CATEGORY_ICONS.runningTotal;
|
|
382
|
+
return html `
|
|
383
|
+
<div class="category" @click=${() => this._toggle(catId)}>
|
|
384
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
385
|
+
<span class="category-icon" style="background:#f3e5f5;color:${cat.color}">${cat.icon}</span>
|
|
386
|
+
<span class="category-label">Running Total Fields</span>
|
|
387
|
+
<span class="category-count">${totals.length}</span>
|
|
388
|
+
<button class="category-add" title="New Running Total"
|
|
389
|
+
@click=${(e) => { e.stopPropagation(); this._contextAction('add-running-total'); }}>+</button>
|
|
390
|
+
</div>
|
|
391
|
+
${isOpen ? html `
|
|
392
|
+
${totals.length === 0
|
|
393
|
+
? html `<div class="empty-hint" style="padding:6px 28px;font-size:10px;text-align:left;">No running totals defined.</div>`
|
|
394
|
+
: totals.map(rt => html `
|
|
395
|
+
<div class="special-row">
|
|
396
|
+
<span class="special-icon" style="color:${cat.color}">\u{03A3}</span>
|
|
397
|
+
<span class="field-label">${rt.field} (${rt.summarize})</span>
|
|
398
|
+
</div>
|
|
399
|
+
`)}
|
|
400
|
+
` : nothing}
|
|
401
|
+
`;
|
|
402
|
+
}
|
|
403
|
+
// ─── 5. Group Fields ──────────────────────────────────────────
|
|
404
|
+
_renderGroupFields(groups) {
|
|
405
|
+
const catId = 'cat::group';
|
|
406
|
+
const isOpen = this._expandedIds.has(catId);
|
|
407
|
+
const cat = CATEGORY_ICONS.group;
|
|
408
|
+
return html `
|
|
409
|
+
<div class="category" @click=${() => this._toggle(catId)}>
|
|
410
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
411
|
+
<span class="category-icon" style="background:#e0f2f1;color:${cat.color}">${cat.icon}</span>
|
|
412
|
+
<span class="category-label">Group Name Fields</span>
|
|
413
|
+
<span class="category-count">${groups.length}</span>
|
|
414
|
+
</div>
|
|
415
|
+
${isOpen ? html `
|
|
416
|
+
${groups.length === 0
|
|
417
|
+
? html `<div class="empty-hint" style="padding:6px 28px;font-size:10px;text-align:left;">No groups defined.</div>`
|
|
418
|
+
: groups.map(g => html `
|
|
419
|
+
<div class="special-row">
|
|
420
|
+
<span class="special-icon" style="color:${cat.color}">\u{229E}</span>
|
|
421
|
+
<span class="field-label">${g.field}</span>
|
|
422
|
+
<span style="font-size:9px;color:#999;margin-left:4px">${g.sort || 'asc'}</span>
|
|
423
|
+
</div>
|
|
424
|
+
`)}
|
|
425
|
+
` : nothing}
|
|
426
|
+
`;
|
|
427
|
+
}
|
|
428
|
+
// ─── 6. Special Fields ────────────────────────────────────────
|
|
429
|
+
_renderSpecialFields() {
|
|
430
|
+
const catId = 'cat::special';
|
|
431
|
+
const isOpen = this._expandedIds.has(catId);
|
|
432
|
+
const cat = CATEGORY_ICONS.special;
|
|
433
|
+
const specialFields = [
|
|
434
|
+
{ type: 'pageNumber', label: 'Page Number', icon: '#' },
|
|
435
|
+
{ type: 'totalPages', label: 'Total Pages', icon: '##' },
|
|
436
|
+
{ type: 'currentDate', label: 'Print Date', icon: '\u{1F4C5}' },
|
|
437
|
+
{ type: 'text', label: 'Text Object', icon: 'T' },
|
|
438
|
+
{ type: 'line', label: 'Line', icon: '\u2500' },
|
|
439
|
+
{ type: 'rect', label: 'Box', icon: '\u25AD' },
|
|
440
|
+
{ type: 'image', label: 'Picture', icon: '\u{1F5BC}' },
|
|
441
|
+
{ type: 'barcode', label: 'Barcode', icon: '\u{2584}' },
|
|
442
|
+
];
|
|
443
|
+
return html `
|
|
444
|
+
<div class="category" @click=${() => this._toggle(catId)}>
|
|
445
|
+
<span class="category-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
446
|
+
<span class="category-icon" style="background:#eceff1;color:${cat.color}">${cat.icon}</span>
|
|
447
|
+
<span class="category-label">Special Fields</span>
|
|
448
|
+
<span class="category-count">${specialFields.length}</span>
|
|
449
|
+
</div>
|
|
450
|
+
${isOpen ? specialFields.map(sf => html `
|
|
451
|
+
<div class="special-row" draggable="true"
|
|
452
|
+
@dragstart=${(e) => this._onSpecialDrag(e, sf.type)}>
|
|
453
|
+
<span class="special-icon">${sf.icon}</span>
|
|
454
|
+
<span class="field-label">${sf.label}</span>
|
|
467
455
|
</div>
|
|
456
|
+
`) : nothing}
|
|
457
|
+
`;
|
|
458
|
+
}
|
|
459
|
+
// ─── Context Menu ─────────────────────────────────────────────
|
|
460
|
+
_renderContextMenu() {
|
|
461
|
+
if (!this._contextMenu)
|
|
462
|
+
return nothing;
|
|
463
|
+
const { x, y, dsId, category } = this._contextMenu;
|
|
464
|
+
return html `
|
|
465
|
+
<div class="context-menu" style="left:${x}px;top:${y}px"
|
|
466
|
+
@click=${(e) => e.stopPropagation()}>
|
|
467
|
+
${dsId ? html `
|
|
468
|
+
<div class="cm-item" @click=${() => this._contextAction('preview-data')}>\u{1F50D} Preview Data</div>
|
|
469
|
+
<div class="cm-item" @click=${() => this._contextAction('add-all-fields')}>\u2795 Add All Fields</div>
|
|
470
|
+
<div class="cm-divider"></div>
|
|
471
|
+
<div class="cm-item" @click=${() => this._contextAction('create-relation')}>\u{1F517} Create Relation...</div>
|
|
472
|
+
<div class="cm-item" @click=${() => this._contextAction('show-in-er')}>\u{1F4CA} Show in ER Diagram</div>
|
|
473
|
+
<div class="cm-divider"></div>
|
|
474
|
+
<div class="cm-item" @click=${() => this._contextAction('remove-datasource')}>\u{1F5D1} Remove Data Source</div>
|
|
475
|
+
` : html `
|
|
476
|
+
<div class="cm-item" @click=${() => this._contextAction('add-datasource')}>\u{1F5C4} Connect Database...</div>
|
|
477
|
+
<div class="cm-item" @click=${() => this._contextAction('add-zentto')}>\u{1F310} Zentto Login...</div>
|
|
478
|
+
<div class="cm-item" @click=${() => this._contextAction('add-rest')}>\u{1F517} REST API...</div>
|
|
479
|
+
`}
|
|
468
480
|
</div>
|
|
469
|
-
${this._renderContextMenu()}
|
|
470
481
|
`;
|
|
471
482
|
}
|
|
472
483
|
};
|
|
@@ -476,12 +487,12 @@ __decorate([
|
|
|
476
487
|
__decorate([
|
|
477
488
|
property({ type: Array })
|
|
478
489
|
], DataSourceTree.prototype, "relations", void 0);
|
|
490
|
+
__decorate([
|
|
491
|
+
property({ type: Object })
|
|
492
|
+
], DataSourceTree.prototype, "layout", void 0);
|
|
479
493
|
__decorate([
|
|
480
494
|
property({ attribute: false })
|
|
481
495
|
], DataSourceTree.prototype, "dataProvider", void 0);
|
|
482
|
-
__decorate([
|
|
483
|
-
state()
|
|
484
|
-
], DataSourceTree.prototype, "_tree", void 0);
|
|
485
496
|
__decorate([
|
|
486
497
|
state()
|
|
487
498
|
], DataSourceTree.prototype, "_expandedIds", void 0);
|