@node-projects/web-component-designer-visualization-addons 0.1.147 → 0.1.149
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/components/BindingsEditor.d.ts +27 -12
- package/dist/components/BindingsEditor.js +570 -206
- package/dist/components/BindingsEditorHelp.d.ts +1 -0
- package/dist/components/BindingsEditorHelp.js +228 -0
- package/dist/index-min.js +493 -118
- package/dist/services/VisualizationBindingsService.js +9 -1
- package/package.json +1 -1
|
@@ -1,174 +1,319 @@
|
|
|
1
1
|
import { BaseCustomWebComponentConstructorAppend, html, css } from '@node-projects/base-custom-webcomponent';
|
|
2
|
-
import { BindingMode, BindingTarget } from '@node-projects/web-component-designer';
|
|
2
|
+
import { BindingMode, BindingTarget, assetsPath } from '@node-projects/web-component-designer';
|
|
3
3
|
import { BindingsEditorHistoric } from './BindingsEditorHistoric.js';
|
|
4
|
+
import { bindingsEditorHelpHtml } from './BindingsEditorHelp.js';
|
|
4
5
|
export class BindingsEditor extends BaseCustomWebComponentConstructorAppend {
|
|
5
|
-
static template = html `
|
|
6
|
-
<div id="root">
|
|
7
|
-
<div class="vertical-grid">
|
|
8
|
-
<div
|
|
9
|
-
<div
|
|
10
|
-
<div class="row">
|
|
11
|
-
<span
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<span
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
<span>
|
|
36
|
-
<
|
|
37
|
-
</div>
|
|
38
|
-
<div class="row">
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
</div>
|
|
44
|
-
<div
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<div style="width: 59%">value</div>
|
|
70
|
-
</div>
|
|
71
|
-
<template repeat:item="[[this.converters]]">
|
|
72
|
-
<div css:background-color="[[item.activeRow ? 'gray' : '']]" style="width: 100%; display: flex; height: 26px; justify-content: center; align-items: center; gap: 5px;">
|
|
73
|
-
<input type="text" value="{{item.key}}" @focus="[[this._focusRow(index)]]" style="width: 39%">
|
|
74
|
-
<input type="[[this._property.type == 'color' ? 'color' : 'text']]" value="{{item.value}}" @focus="[[this._focusRow(index)]]" style="width: 59%">
|
|
75
|
-
</div>
|
|
76
|
-
</template>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="controlbox" id="grid-controls">
|
|
80
|
-
<button type="button" id="add-row-button" value="add" @click="addConverter">
|
|
81
|
-
<span>add</span>
|
|
82
|
-
</button>
|
|
83
|
-
<button id="remove-row-button" value="remove" style="margin-top: 6px;" @click="removeConverter">
|
|
84
|
-
<span>remove</span>
|
|
85
|
-
</button>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
6
|
+
static template = html `
|
|
7
|
+
<div id="root">
|
|
8
|
+
<div class="vertical-grid">
|
|
9
|
+
<div id="full-width-col">
|
|
10
|
+
<div id="signals-area">
|
|
11
|
+
<div class="row">
|
|
12
|
+
<span class="section-label" title="Add signals by name and path. The variable name is used in the formula (__0, __1,... or a custom name).
Access signal objects in properties via ?propertyName, access the propertyValue via ??propertyName.
Access signal objects in properties of the target via #propertyName, access a propertyValue of the target via ##propertyName.
Bind to signal configurations via $objectId.
Bind to special values via §name (if supported by your framework).
You could also use signals inside of a Signal Name via {name}">objects</span>
|
|
13
|
+
<button id="helpBtn" class="info-btn" title="Help"><svg viewBox="0 0 512 512" width="16" height="16"><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg></button>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="bordered-section">
|
|
16
|
+
<div id="signalListHeader" class="signal-row signal-header">
|
|
17
|
+
<span class="signal-header-name">name</span>
|
|
18
|
+
<span class="signal-header-signal">signal</span>
|
|
19
|
+
<span class="signal-header-spacer"></span>
|
|
20
|
+
</div>
|
|
21
|
+
<div id="signalListContainer"></div>
|
|
22
|
+
<button id="addSignalBtn" class="add-signal-btn" @click="[[this._addSignal()]]">+ Add signal</button>
|
|
23
|
+
</div>
|
|
24
|
+
<div id="groupinvert" class="row">
|
|
25
|
+
<input type="checkbox" checked="{{this.invert::change}}">
|
|
26
|
+
<span>invert logic</span>
|
|
27
|
+
<button id="historicBtn" css:border="[[this.historic ? 'solid lime 5px' : 'none']]" @click="[[this.showHistoric()]]">historic</button>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="row">
|
|
30
|
+
<span class="section-label" title="javascript expression. access context with __ctx, result with __res, objects by name (__0, __1,... or custom name)">formula</span>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="row">
|
|
33
|
+
<node-projects-code-view-monaco id="expression" single-row language="javascript" .code="{{?this.expression}}" @code-changed="_refresh"></node-projects-code-view-monaco>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="row">
|
|
36
|
+
<span class="section-label writeback-label" title="write back the value build by a formula to a signal. maybe only usefull when a formula is used.">write back signal :</span>
|
|
37
|
+
<input class="writeback-input" .disabled="[[!this.expression]]" value="{{?this.writeBackSignal::change}}">
|
|
38
|
+
</div>
|
|
39
|
+
<div id="groupBindingMode" class="row">
|
|
40
|
+
<input type="checkbox" disabled="[[!this.twoWayPossible]]" checked="{{this.twoWay::change}}" @change="_refresh">
|
|
41
|
+
<span>two way binding</span>
|
|
42
|
+
<span class="events-label" css:display="[[this.twoWay ? 'inline' : 'none']]">events: </span>
|
|
43
|
+
<input class="events-input" css:display="[[this.twoWay ? 'inline-block' : 'none']]" title="to use multiple events, seprate them with semicolon (;)" value="{{?this.events::change}}">
|
|
44
|
+
</div>
|
|
45
|
+
<div css:display="[[this.twoWay ? 'block' : 'none']]">
|
|
46
|
+
<div class="row">
|
|
47
|
+
<span class="section-label" title="javascript expression. access property with 'value'">formula write back (two way)</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="row">
|
|
50
|
+
<node-projects-code-view-monaco id="expression2way" .read-only="[[!this.twoWay]]" $readonly="[[!this.twoWay]]" single-row language="javascript" .code="{{?this.expressionTwoWay}}"></node-projects-code-view-monaco>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div id="namedConverterRow" class="row">
|
|
57
|
+
<span class="converter-label">converter :</span>
|
|
58
|
+
<input id="namedConverterInput" value="{{?this.convertersString::change}}">
|
|
59
|
+
<button id="namedConverterBrowse" class="signal-select-btn" @click="[[this._selectNamedConverter()]]">...</button>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="bordered-section">
|
|
62
|
+
<div class="signal-row signal-header">
|
|
63
|
+
<span class="converter-header-col">condition</span>
|
|
64
|
+
<span class="converter-header-col">value</span>
|
|
65
|
+
<span class="converter-header-spacer"></span>
|
|
66
|
+
</div>
|
|
67
|
+
<div id="converterListContainer"></div>
|
|
68
|
+
<button class="add-signal-btn" @click="[[this._addConverter()]]">+ Add entry</button>
|
|
69
|
+
</div>
|
|
88
70
|
</div>`;
|
|
89
|
-
static style = css `
|
|
90
|
-
:host {
|
|
91
|
-
box-sizing: border-box;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
display:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
71
|
+
static style = css `
|
|
72
|
+
:host {
|
|
73
|
+
box-sizing: border-box;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.padding_top {
|
|
77
|
+
padding-top: 30px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.row{
|
|
81
|
+
margin-top: 3px;
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#converterListContainer {
|
|
87
|
+
max-height: 72px;
|
|
88
|
+
overflow-y: auto;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.input-headline {
|
|
92
|
+
height: 30px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
input[type="checkbox"] {
|
|
96
|
+
margin-right: 15px;
|
|
97
|
+
width: 15px;
|
|
98
|
+
height: 15px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
select {
|
|
102
|
+
width: 100%;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#root {
|
|
106
|
+
padding: 2px 10px;
|
|
107
|
+
display: grid;
|
|
108
|
+
grid-template-rows: min-content min-content;
|
|
109
|
+
overflow: auto;
|
|
110
|
+
height: calc(100% - 4px)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.vertical-grid {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: calc((100% - 150px) - 30px) 150px;
|
|
116
|
+
gap: 30px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#full-width-col {
|
|
120
|
+
grid-column: 1/3;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#signals-area {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.section-label {
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.bordered-section {
|
|
133
|
+
border: solid 1px black;
|
|
134
|
+
padding: 10px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.signal-header-name {
|
|
138
|
+
flex: 0 0 80px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.signal-header-signal, .converter-header-col {
|
|
142
|
+
flex: 1 1 auto;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.signal-header-spacer {
|
|
146
|
+
flex: 0 0 72px;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.converter-header-spacer {
|
|
150
|
+
flex: 0 0 47px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#groupinvert {
|
|
154
|
+
position: relative;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#historicBtn {
|
|
158
|
+
position: absolute;
|
|
159
|
+
right: 1px;
|
|
160
|
+
top: 5px;
|
|
161
|
+
padding: 10px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
node-projects-code-view-monaco {
|
|
165
|
+
width: 100%;
|
|
166
|
+
min-height: 17px;
|
|
167
|
+
height: 17px;
|
|
168
|
+
position: relative;
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
resize: vertical;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.writeback-label {
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.writeback-input {
|
|
178
|
+
width: 100%;
|
|
179
|
+
margin-left: 5px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.events-label {
|
|
183
|
+
margin-left: 15px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.events-input {
|
|
187
|
+
flex-grow: 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#namedConverterRow {
|
|
191
|
+
margin-top: 6px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.converter-label {
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#namedConverterInput {
|
|
199
|
+
flex: 1;
|
|
200
|
+
margin-left: 5px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#namedConverterBrowse {
|
|
204
|
+
margin-left: 3px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#grid input, #list input {
|
|
208
|
+
border:0px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#tagdata_type {
|
|
212
|
+
height: 24px;
|
|
213
|
+
font-size: inherit;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
node-projects-code-view-monaco:not([readonly]) {
|
|
217
|
+
border: 1px black solid;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
node-projects-code-view-monaco[readonly] {
|
|
221
|
+
border: 1px lightgray solid;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.signal-header {
|
|
225
|
+
color: #888;
|
|
226
|
+
font-size: 11px;
|
|
227
|
+
margin-top: 3px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.signal-row {
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: 3px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.signal-row input {
|
|
237
|
+
height: 22px;
|
|
238
|
+
box-sizing: border-box;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.signal-name-input {
|
|
242
|
+
min-width: 80px;
|
|
243
|
+
flex: 0 0 80px;
|
|
244
|
+
font-family: monospace;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.signal-path-input {
|
|
248
|
+
flex: 1 1 auto;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.signal-icon-btn {
|
|
252
|
+
flex: 0 0 22px;
|
|
253
|
+
width: 22px;
|
|
254
|
+
height: 22px;
|
|
255
|
+
padding: 0;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
display: flex;
|
|
258
|
+
align-items: center;
|
|
259
|
+
justify-content: center;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.signal-icon-btn img {
|
|
263
|
+
width: 12px;
|
|
264
|
+
height: 12px;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.signal-select-btn {
|
|
268
|
+
flex: 0 0 26px;
|
|
269
|
+
width: 26px;
|
|
270
|
+
height: 22px;
|
|
271
|
+
padding: 0;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
font-size: 11px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#signalListContainer {
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-direction: column;
|
|
279
|
+
gap: 3px;
|
|
280
|
+
max-height: 72px;
|
|
281
|
+
overflow-y: auto;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.add-signal-btn {
|
|
285
|
+
align-self: flex-start;
|
|
286
|
+
margin-top: 4px;
|
|
287
|
+
height: 22px;
|
|
288
|
+
cursor: pointer;
|
|
289
|
+
font-size: 11px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.info-btn {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
justify-content: center;
|
|
296
|
+
width: 22px;
|
|
297
|
+
height: 22px;
|
|
298
|
+
padding: 0;
|
|
299
|
+
border: none;
|
|
300
|
+
background: transparent;
|
|
301
|
+
cursor: pointer;
|
|
302
|
+
flex-shrink: 0;
|
|
303
|
+
margin-left: auto;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.info-btn:hover {
|
|
307
|
+
background: #eee;
|
|
308
|
+
border-radius: 3px;
|
|
162
309
|
}`;
|
|
163
310
|
static is = 'node-projects-visualization-bindings-editor';
|
|
164
311
|
static properties = {
|
|
165
312
|
twoWayPossible: Boolean,
|
|
166
313
|
twoWay: Boolean,
|
|
167
314
|
expression: String,
|
|
168
|
-
objectNames: String,
|
|
169
315
|
events: String,
|
|
170
316
|
invert: Boolean,
|
|
171
|
-
converters: Array,
|
|
172
317
|
historic: Object
|
|
173
318
|
};
|
|
174
319
|
twoWayPossible = false;
|
|
@@ -181,36 +326,57 @@ export class BindingsEditor extends BaseCustomWebComponentConstructorAppend {
|
|
|
181
326
|
events = '';
|
|
182
327
|
invert = false;
|
|
183
328
|
converters = [];
|
|
329
|
+
static namedConverters = [];
|
|
330
|
+
/** Extra help-page sections (raw HTML, same markup/classes as BindingsEditorHelp.ts) contributed by the host application.
|
|
331
|
+
* Left empty by default so the plain designer help page is unaffected. */
|
|
332
|
+
static helpSections = [];
|
|
184
333
|
convertersString;
|
|
185
|
-
|
|
334
|
+
/** Unified converter value for use in IBinding.converters when saving. Returns the named converter
|
|
335
|
+
* string when set, otherwise converts the inline key/value table to a Record, or undefined if empty. */
|
|
336
|
+
get converter() {
|
|
337
|
+
if (this.convertersString)
|
|
338
|
+
return this.convertersString;
|
|
339
|
+
if (this.converters.length > 0)
|
|
340
|
+
return Object.fromEntries(this.converters.map(c => [c.key, c.value]));
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
186
343
|
_property;
|
|
187
344
|
_binding;
|
|
188
345
|
_bindingTarget;
|
|
189
346
|
_serviceContainer;
|
|
190
347
|
_instanceServiceContainer;
|
|
191
348
|
_shell;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
349
|
+
_signals = [];
|
|
350
|
+
_signalListContainer;
|
|
351
|
+
_converterListContainer;
|
|
352
|
+
constructor(property, binding, bindingTarget, serviceContainer, instanceServiceContainer, shell, config = {}) {
|
|
195
353
|
super();
|
|
196
354
|
super._restoreCachedInititalValues();
|
|
197
|
-
//TODOS:
|
|
198
|
-
//typed values for converters
|
|
199
|
-
//default value for converter
|
|
200
|
-
this._objNmInput = this._getDomElement('objectName');
|
|
201
355
|
this._property = property;
|
|
202
356
|
this._binding = binding;
|
|
203
357
|
this._bindingTarget = bindingTarget;
|
|
204
358
|
this._serviceContainer = serviceContainer;
|
|
205
359
|
this._instanceServiceContainer = instanceServiceContainer;
|
|
206
360
|
this._shell = shell;
|
|
207
|
-
if (
|
|
208
|
-
this._getDomElement('
|
|
361
|
+
if (config?.namedConverters === false) {
|
|
362
|
+
this._getDomElement('namedConverterRow').style.display = 'none';
|
|
363
|
+
}
|
|
364
|
+
const historicBtn = this._getDomElement('historicBtn');
|
|
365
|
+
if (config?.showHistoric === false) {
|
|
366
|
+
historicBtn.style.display = 'none';
|
|
367
|
+
}
|
|
368
|
+
else if (config?.historicLabel) {
|
|
369
|
+
historicBtn.textContent = config.historicLabel;
|
|
209
370
|
}
|
|
210
371
|
}
|
|
211
372
|
ready() {
|
|
212
373
|
this._parseAttributesToProperties();
|
|
213
374
|
this._assignEvents();
|
|
375
|
+
this._signalListContainer = this._getDomElement('signalListContainer');
|
|
376
|
+
this._converterListContainer = this._getDomElement('converterListContainer');
|
|
377
|
+
this._getDomElement('helpBtn').onclick = () => this._showHelp();
|
|
378
|
+
if (BindingsEditor.namedConverters.length === 0)
|
|
379
|
+
this._getDomElement('namedConverterBrowse').style.display = 'none';
|
|
214
380
|
this.twoWayPossible = false;
|
|
215
381
|
if (this._bindingTarget == BindingTarget.property || this._bindingTarget == BindingTarget.attribute)
|
|
216
382
|
this.twoWayPossible = true;
|
|
@@ -221,7 +387,6 @@ export class BindingsEditor extends BaseCustomWebComponentConstructorAppend {
|
|
|
221
387
|
this.expressionTwoWay = this._binding.expressionTwoWay;
|
|
222
388
|
this.historic = this._binding.historic;
|
|
223
389
|
this.invert = this._binding.invert;
|
|
224
|
-
this.objectValueType = this._binding.type;
|
|
225
390
|
if (this._binding.bindableObjectNames)
|
|
226
391
|
this.objectNames = this._binding.bindableObjectNames.join(';');
|
|
227
392
|
if (this._binding.converter) {
|
|
@@ -249,54 +414,257 @@ export class BindingsEditor extends BaseCustomWebComponentConstructorAppend {
|
|
|
249
414
|
edt.style.height = (3 * 17) + 'px';
|
|
250
415
|
}
|
|
251
416
|
}
|
|
417
|
+
this._signals = this._parseObjectNames(this.objectNames);
|
|
418
|
+
if (this._signals.length === 0)
|
|
419
|
+
this._signals.push({ name: '', signal: '' });
|
|
420
|
+
this._renderSignalList();
|
|
421
|
+
this._renderConverterList();
|
|
252
422
|
this._bindingsParse();
|
|
253
|
-
this._objNmInput.focus();
|
|
254
423
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
424
|
+
_parseObjectNames(str) {
|
|
425
|
+
if (!str)
|
|
426
|
+
return [];
|
|
427
|
+
return str.split(';').filter(s => s.length > 0).map(s => {
|
|
428
|
+
const colonIdx = s.indexOf(':');
|
|
429
|
+
if (colonIdx > 0) {
|
|
430
|
+
return { name: s.slice(0, colonIdx), signal: s.slice(colonIdx + 1) };
|
|
431
|
+
}
|
|
432
|
+
return { name: '', signal: s };
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
_signalsToObjectNames() {
|
|
436
|
+
return this._signals.map(s => s.name ? s.name + ':' + s.signal : s.signal).join(';');
|
|
258
437
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
438
|
+
_renderSignalList() {
|
|
439
|
+
const container = this._signalListContainer;
|
|
440
|
+
container.innerHTML = '';
|
|
441
|
+
this._signals.forEach((sig, i) => {
|
|
442
|
+
const row = document.createElement('div');
|
|
443
|
+
row.className = 'signal-row';
|
|
444
|
+
const nameInput = document.createElement('input');
|
|
445
|
+
nameInput.className = 'signal-name-input';
|
|
446
|
+
nameInput.placeholder = `__${i}`;
|
|
447
|
+
nameInput.value = sig.name;
|
|
448
|
+
nameInput.title = 'variable name used in the formula (defaults to __' + i + ' if empty)';
|
|
449
|
+
nameInput.oninput = () => {
|
|
450
|
+
this._signals[i].name = nameInput.value;
|
|
451
|
+
this._syncSignals();
|
|
452
|
+
};
|
|
453
|
+
const signalInput = document.createElement('input');
|
|
454
|
+
signalInput.className = 'signal-path-input';
|
|
455
|
+
signalInput.value = sig.signal;
|
|
456
|
+
signalInput.placeholder = 'signal path';
|
|
457
|
+
signalInput.title = 'signal path (prefix: ? = signalProperty, ?? = property value, # = target property, ## = target property value)';
|
|
458
|
+
signalInput.oninput = () => {
|
|
459
|
+
this._signals[i].signal = signalInput.value;
|
|
460
|
+
this._syncSignals();
|
|
461
|
+
};
|
|
462
|
+
const copyBtn = document.createElement('button');
|
|
463
|
+
copyBtn.className = 'signal-icon-btn';
|
|
464
|
+
copyBtn.title = 'Duplicate';
|
|
465
|
+
const copyIcon = document.createElement('img');
|
|
466
|
+
copyIcon.src = assetsPath + 'icons/copy.svg';
|
|
467
|
+
copyIcon.style.cssText = 'width:12px; height:12px;';
|
|
468
|
+
copyBtn.appendChild(copyIcon);
|
|
469
|
+
copyBtn.onclick = () => {
|
|
470
|
+
this._signals.splice(i + 1, 0, { ...this._signals[i] });
|
|
471
|
+
this._syncSignals();
|
|
472
|
+
this._renderSignalList();
|
|
473
|
+
};
|
|
474
|
+
const selectBtn = document.createElement('button');
|
|
475
|
+
selectBtn.className = 'signal-select-btn';
|
|
476
|
+
selectBtn.textContent = '...';
|
|
477
|
+
selectBtn.title = 'Browse signal';
|
|
478
|
+
selectBtn.onclick = () => this._selectForRow(i, signalInput);
|
|
479
|
+
const delBtn = document.createElement('button');
|
|
480
|
+
delBtn.className = 'signal-icon-btn';
|
|
481
|
+
delBtn.title = 'Remove';
|
|
482
|
+
const delIcon = document.createElement('img');
|
|
483
|
+
delIcon.src = assetsPath + 'icons/delete.svg';
|
|
484
|
+
delIcon.style.cssText = 'width:12px; height:12px;';
|
|
485
|
+
delBtn.appendChild(delIcon);
|
|
486
|
+
delBtn.onclick = () => {
|
|
487
|
+
this._signals.splice(i, 1);
|
|
488
|
+
this._syncSignals();
|
|
489
|
+
this._renderSignalList();
|
|
490
|
+
};
|
|
491
|
+
row.appendChild(nameInput);
|
|
492
|
+
row.appendChild(signalInput);
|
|
493
|
+
row.appendChild(selectBtn);
|
|
494
|
+
row.appendChild(copyBtn);
|
|
495
|
+
row.appendChild(delBtn);
|
|
496
|
+
container.appendChild(row);
|
|
497
|
+
});
|
|
264
498
|
}
|
|
265
|
-
|
|
266
|
-
this.objectNames =
|
|
499
|
+
_syncSignals() {
|
|
500
|
+
this.objectNames = this._signalsToObjectNames();
|
|
267
501
|
this._bindingsRefresh();
|
|
268
502
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
});
|
|
503
|
+
_addSignal() {
|
|
504
|
+
this._signals.push({ name: '', signal: '' });
|
|
505
|
+
this._renderSignalList();
|
|
273
506
|
}
|
|
274
|
-
async
|
|
507
|
+
async _selectForRow(index, signalInput) {
|
|
275
508
|
let b = this._shell.createBindableObjectBrowser();
|
|
276
509
|
b.initialize(this._serviceContainer, this._instanceServiceContainer, 'binding');
|
|
277
510
|
b.title = 'select signal...';
|
|
278
511
|
const abortController = new AbortController();
|
|
279
512
|
b.objectDoubleclicked.on(() => {
|
|
280
513
|
abortController.abort();
|
|
281
|
-
|
|
282
|
-
this.objectNames += ';';
|
|
514
|
+
let prefix = '';
|
|
283
515
|
if (b.selectedObject.specialType == 'signalProperty') {
|
|
284
|
-
|
|
516
|
+
prefix = '?';
|
|
285
517
|
}
|
|
286
518
|
else if (b.selectedObject.bindabletype === 'property') {
|
|
287
|
-
|
|
519
|
+
prefix = '??';
|
|
288
520
|
}
|
|
289
|
-
this.
|
|
290
|
-
this.
|
|
521
|
+
this._signals[index].signal = prefix + b.selectedObject.fullName;
|
|
522
|
+
signalInput.value = this._signals[index].signal;
|
|
523
|
+
this._syncSignals();
|
|
291
524
|
});
|
|
292
525
|
let res = await this._shell.openConfirmation(b, { x: 100, y: 100, width: 400, height: 300, parent: this, abortSignal: abortController.signal });
|
|
293
526
|
if (res) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
527
|
+
let prefix = '';
|
|
528
|
+
if (b.selectedObject.specialType == 'signalProperty') {
|
|
529
|
+
prefix = '?';
|
|
530
|
+
}
|
|
531
|
+
else if (b.selectedObject.bindabletype === 'property') {
|
|
532
|
+
prefix = '??';
|
|
533
|
+
}
|
|
534
|
+
this._signals[index].signal = prefix + b.selectedObject.fullName;
|
|
535
|
+
signalInput.value = this._signals[index].signal;
|
|
536
|
+
this._syncSignals();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
_renderConverterList() {
|
|
540
|
+
const container = this._converterListContainer;
|
|
541
|
+
container.innerHTML = '';
|
|
542
|
+
const valueType = this._property?.type === 'color' ? 'color' : 'text';
|
|
543
|
+
this.converters.forEach((conv, i) => {
|
|
544
|
+
const row = document.createElement('div');
|
|
545
|
+
row.className = 'signal-row';
|
|
546
|
+
const keyInput = document.createElement('input');
|
|
547
|
+
keyInput.style.cssText = 'flex:1 1 auto; height:22px; box-sizing:border-box;';
|
|
548
|
+
keyInput.value = conv.key;
|
|
549
|
+
keyInput.placeholder = 'condition';
|
|
550
|
+
keyInput.oninput = () => { this.converters[i].key = keyInput.value; };
|
|
551
|
+
const valInput = document.createElement('input');
|
|
552
|
+
valInput.type = valueType;
|
|
553
|
+
valInput.style.cssText = 'flex:1 1 auto; height:22px; box-sizing:border-box;';
|
|
554
|
+
valInput.value = conv.value;
|
|
555
|
+
valInput.placeholder = 'value';
|
|
556
|
+
valInput.oninput = () => { this.converters[i].value = valInput.value; };
|
|
557
|
+
const copyBtn = document.createElement('button');
|
|
558
|
+
copyBtn.className = 'signal-icon-btn';
|
|
559
|
+
copyBtn.title = 'Duplicate';
|
|
560
|
+
const copyIcon = document.createElement('img');
|
|
561
|
+
copyIcon.src = assetsPath + 'icons/copy.svg';
|
|
562
|
+
copyIcon.style.cssText = 'width:12px; height:12px;';
|
|
563
|
+
copyBtn.appendChild(copyIcon);
|
|
564
|
+
copyBtn.onclick = () => {
|
|
565
|
+
this.converters.splice(i + 1, 0, { ...this.converters[i] });
|
|
566
|
+
this._renderConverterList();
|
|
567
|
+
};
|
|
568
|
+
const delBtn = document.createElement('button');
|
|
569
|
+
delBtn.className = 'signal-icon-btn';
|
|
570
|
+
delBtn.title = 'Remove';
|
|
571
|
+
const delIcon = document.createElement('img');
|
|
572
|
+
delIcon.src = assetsPath + 'icons/delete.svg';
|
|
573
|
+
delIcon.style.cssText = 'width:12px; height:12px;';
|
|
574
|
+
delBtn.appendChild(delIcon);
|
|
575
|
+
delBtn.onclick = () => {
|
|
576
|
+
this.converters.splice(i, 1);
|
|
577
|
+
this._renderConverterList();
|
|
578
|
+
};
|
|
579
|
+
row.appendChild(keyInput);
|
|
580
|
+
row.appendChild(valInput);
|
|
581
|
+
row.appendChild(copyBtn);
|
|
582
|
+
row.appendChild(delBtn);
|
|
583
|
+
container.appendChild(row);
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
_addConverter() {
|
|
587
|
+
this.converters.push({ key: '', value: '' });
|
|
588
|
+
this._renderConverterList();
|
|
589
|
+
}
|
|
590
|
+
async _selectNamedConverter() {
|
|
591
|
+
const list = BindingsEditor.namedConverters;
|
|
592
|
+
if (list.length === 0)
|
|
593
|
+
return;
|
|
594
|
+
let selectedName = '';
|
|
595
|
+
let selectedRow = null;
|
|
596
|
+
const outer = document.createElement('div');
|
|
597
|
+
outer.style.cssText = 'display:flex; flex-direction:column; height:100%; box-sizing:border-box; font-size:12px;';
|
|
598
|
+
const hint = document.createElement('div');
|
|
599
|
+
hint.style.cssText = 'padding:6px 8px 4px; color:#666; font-size:11px; border-bottom:1px solid #e0e0e0; flex-shrink:0;';
|
|
600
|
+
hint.textContent = 'Select a converter and confirm with OK.';
|
|
601
|
+
outer.appendChild(hint);
|
|
602
|
+
const listEl = document.createElement('div');
|
|
603
|
+
listEl.style.cssText = 'flex:1; overflow-y:auto; padding:4px;';
|
|
604
|
+
outer.appendChild(listEl);
|
|
605
|
+
const deselect = (row) => {
|
|
606
|
+
row.style.background = '';
|
|
607
|
+
row.style.borderLeftColor = 'transparent';
|
|
608
|
+
};
|
|
609
|
+
const select = (row, name) => {
|
|
610
|
+
if (selectedRow)
|
|
611
|
+
deselect(selectedRow);
|
|
612
|
+
selectedRow = row;
|
|
613
|
+
row.style.background = '#e8f4fd';
|
|
614
|
+
row.style.borderLeftColor = '#0078d7';
|
|
615
|
+
selectedName = name;
|
|
616
|
+
};
|
|
617
|
+
for (const nc of list) {
|
|
618
|
+
const row = document.createElement('div');
|
|
619
|
+
row.style.cssText = 'padding:5px 6px 5px 9px; cursor:pointer; border-radius:3px; border-left:3px solid transparent; margin-bottom:2px;';
|
|
620
|
+
const nameEl = document.createElement('div');
|
|
621
|
+
nameEl.style.cssText = 'font-family:monospace; font-weight:bold; color:#111;';
|
|
622
|
+
nameEl.textContent = nc.name;
|
|
623
|
+
row.appendChild(nameEl);
|
|
624
|
+
if (nc.description) {
|
|
625
|
+
const descEl = document.createElement('div');
|
|
626
|
+
descEl.style.cssText = 'color:#555; font-size:11px; margin-top:2px;';
|
|
627
|
+
descEl.textContent = nc.description;
|
|
628
|
+
row.appendChild(descEl);
|
|
629
|
+
}
|
|
630
|
+
if (nc.converter && Object.keys(nc.converter).length > 0) {
|
|
631
|
+
const table = document.createElement('div');
|
|
632
|
+
table.style.cssText = 'display:grid; grid-template-columns:max-content max-content 1fr; gap:1px 6px; margin-top:4px; padding:4px 6px; background:#f5f5f5; border-radius:2px; font-family:monospace; font-size:11px; color:#333;';
|
|
633
|
+
for (const [k, v] of Object.entries(nc.converter)) {
|
|
634
|
+
const keyEl = document.createElement('span');
|
|
635
|
+
keyEl.style.cssText = 'color:#555; text-align:right;';
|
|
636
|
+
keyEl.textContent = k;
|
|
637
|
+
const arrow = document.createElement('span');
|
|
638
|
+
arrow.style.cssText = 'color:#aaa;';
|
|
639
|
+
arrow.textContent = '→';
|
|
640
|
+
const valEl = document.createElement('span');
|
|
641
|
+
valEl.style.cssText = 'color:#0078d7; font-weight:bold;';
|
|
642
|
+
valEl.textContent = String(v);
|
|
643
|
+
table.appendChild(keyEl);
|
|
644
|
+
table.appendChild(arrow);
|
|
645
|
+
table.appendChild(valEl);
|
|
646
|
+
}
|
|
647
|
+
row.appendChild(table);
|
|
648
|
+
}
|
|
649
|
+
row.onmouseenter = () => { if (row !== selectedRow)
|
|
650
|
+
row.style.background = '#f5f5f5'; };
|
|
651
|
+
row.onmouseleave = () => { if (row !== selectedRow)
|
|
652
|
+
row.style.background = ''; };
|
|
653
|
+
row.onclick = () => select(row, nc.name);
|
|
654
|
+
listEl.appendChild(row);
|
|
655
|
+
}
|
|
656
|
+
const res = await this._shell.openConfirmation(outer, { x: 100, y: 100, width: 320, height: 300, parent: this });
|
|
657
|
+
if (res && selectedName) {
|
|
658
|
+
this.convertersString = selectedName;
|
|
659
|
+
this._getDomElement('namedConverterInput').value = selectedName;
|
|
297
660
|
this._bindingsRefresh();
|
|
298
661
|
}
|
|
299
662
|
}
|
|
663
|
+
_refresh() {
|
|
664
|
+
requestAnimationFrame(() => {
|
|
665
|
+
this._bindingsRefresh();
|
|
666
|
+
});
|
|
667
|
+
}
|
|
300
668
|
async showHistoric() {
|
|
301
669
|
let h = new BindingsEditorHistoric(this.historic);
|
|
302
670
|
const abortController = new AbortController();
|
|
@@ -310,17 +678,13 @@ export class BindingsEditor extends BaseCustomWebComponentConstructorAppend {
|
|
|
310
678
|
}
|
|
311
679
|
this._bindingsRefresh();
|
|
312
680
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
this.converters.splice(this._activeRow, 1);
|
|
321
|
-
this._activeRow = -1;
|
|
322
|
-
this._bindingsRefresh();
|
|
323
|
-
this._updatefocusedRow();
|
|
681
|
+
_showHelp() {
|
|
682
|
+
const iframe = document.createElement('iframe');
|
|
683
|
+
iframe.srcdoc = BindingsEditor.helpSections.length > 0
|
|
684
|
+
? bindingsEditorHelpHtml.replace('</div>\n</body>', BindingsEditor.helpSections.join('') + '</div>\n</body>')
|
|
685
|
+
: bindingsEditorHelpHtml;
|
|
686
|
+
iframe.style.cssText = 'width:100%; height:100%; border:none; display:block;';
|
|
687
|
+
this._shell.openModal(iframe, { title: 'Bindings Editor Help', parent: this, x: -660, y: -80, width: 630, height: 580 });
|
|
324
688
|
}
|
|
325
689
|
}
|
|
326
690
|
customElements.define(BindingsEditor.is, BindingsEditor);
|