@operato/process 1.1.27 → 1.1.29

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.
Files changed (25) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/src/modeller/property-sidebar/data-binding/data-binding-mapper.d.ts +54 -0
  3. package/dist/src/modeller/property-sidebar/data-binding/data-binding-mapper.js +389 -0
  4. package/dist/src/modeller/property-sidebar/data-binding/data-binding-mapper.js.map +1 -0
  5. package/dist/src/modeller/property-sidebar/data-binding/data-binding.d.ts +44 -0
  6. package/dist/src/modeller/property-sidebar/data-binding/data-binding.js +455 -0
  7. package/dist/src/modeller/property-sidebar/data-binding/data-binding.js.map +1 -0
  8. package/dist/src/modeller/property-sidebar/effects/property-event-hover.js +6 -1
  9. package/dist/src/modeller/property-sidebar/effects/property-event-hover.js.map +1 -1
  10. package/dist/src/modeller/property-sidebar/effects/property-event-tap.js +18 -9
  11. package/dist/src/modeller/property-sidebar/effects/property-event-tap.js.map +1 -1
  12. package/dist/src/modeller/property-sidebar/property-sidebar.d.ts +2 -0
  13. package/dist/src/modeller/property-sidebar/property-sidebar.js +11 -0
  14. package/dist/src/modeller/property-sidebar/property-sidebar.js.map +1 -1
  15. package/dist/src/ox-process-viewer.d.ts +1 -0
  16. package/dist/src/ox-process-viewer.js +5 -0
  17. package/dist/src/ox-process-viewer.js.map +1 -1
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/package.json +11 -11
  20. package/src/modeller/property-sidebar/data-binding/data-binding-mapper.ts +419 -0
  21. package/src/modeller/property-sidebar/data-binding/data-binding.ts +476 -0
  22. package/src/modeller/property-sidebar/effects/property-event-hover.ts +6 -1
  23. package/src/modeller/property-sidebar/effects/property-event-tap.ts +16 -7
  24. package/src/modeller/property-sidebar/property-sidebar.ts +11 -0
  25. package/src/ox-process-viewer.ts +6 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@material/mwc-icon'
6
+ import '@operato/help/ox-title-with-help.js'
7
+ import '@operato/input/ox-buttons-radio.js'
8
+ import '@operato/input/ox-input-data.js'
9
+ import '@operato/i18n/ox-i18n.js'
10
+
11
+ import { css, html, PropertyValues } from 'lit'
12
+ import { property, query, state } from 'lit/decorators.js'
13
+
14
+ import { Properties, Scene } from '@hatiolab/things-scene'
15
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
16
+
17
+ import { AbstractProperty } from '../abstract-property.js'
18
+ import { PropertySharedStyle } from '../property-shared-style.js'
19
+ import { DataBindingMapper } from './data-binding-mapper.js'
20
+
21
+ var clipboard = '{}'
22
+
23
+ const PROPS = [
24
+ '',
25
+ 'text',
26
+ ['fillStyle', 'fill style'],
27
+ ['strokeStyle', 'stroke style'],
28
+ ['fontColor', 'font color'],
29
+ 'value',
30
+ 'data',
31
+ 'source',
32
+ 'hidden',
33
+ 'started',
34
+ 'play',
35
+ ['ref', 'reference'],
36
+ 'options',
37
+ 'rotate',
38
+ 'scale',
39
+ 'translate',
40
+ 'dimension',
41
+ 'location',
42
+ 'accessor'
43
+ ].map(prop => {
44
+ return typeof prop == 'string' ? { name: prop, label: prop } : { name: prop[0], label: prop[1] }
45
+ })
46
+
47
+ export class PropertyDataBinding extends ScopedElementsMixin(AbstractProperty) {
48
+ static styles = [
49
+ PropertySharedStyle,
50
+ css`
51
+ #tab-header {
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: space-between;
55
+ }
56
+
57
+ #tab-header > mwc-icon {
58
+ padding: 0;
59
+ margin: 0;
60
+ width: 25px;
61
+ height: 25px;
62
+ font-size: x-large;
63
+ border-bottom: 1px solid rgba(0, 0, 0, 0.2);
64
+ }
65
+
66
+ fieldset[collapsable] legend {
67
+ box-sizing: border-box;
68
+ width: 100%;
69
+ }
70
+
71
+ fieldset[collapsable] legend mwc-icon {
72
+ float: right;
73
+ font-size: medium;
74
+ margin: 0;
75
+ }
76
+
77
+ fieldset[collapsable][collapsed] > :not(legend) {
78
+ display: none;
79
+ }
80
+
81
+ ox-buttons-radio {
82
+ flex: 1;
83
+ height: 25px;
84
+ border: 1px solid rgba(0, 0, 0, 0.2);
85
+ border-width: 1px 1px 0 1px;
86
+ text-align: center;
87
+
88
+ display: flex;
89
+ padding: 0;
90
+ box-sizing: border-box;
91
+
92
+ width: 0; /* limit width */
93
+ overflow-x: hidden;
94
+ }
95
+
96
+ ox-buttons-radio > div {
97
+ background-color: rgba(0, 0, 0, 0.2);
98
+ border: 1px solid rgba(0, 0, 0, 0.07);
99
+ border-width: 0 0 2px 0;
100
+ padding: 0;
101
+ color: #fff;
102
+ font-size: 13px;
103
+ max-width: 25px;
104
+ min-width: 25px;
105
+ }
106
+
107
+ ox-buttons-radio > div[disabled] {
108
+ background-color: rgba(0, 0, 0, 0.1);
109
+ }
110
+
111
+ ox-buttons-radio > div[active] {
112
+ border-color: rgb(242, 71, 28);
113
+ }
114
+
115
+ ox-buttons-radio > div.iron-selected {
116
+ background-color: rgba(255, 255, 255, 0.5);
117
+ color: #585858;
118
+ }
119
+
120
+ div[binding] {
121
+ display: flex;
122
+ flex-direction: row-reverse;
123
+ background-color: rgba(255, 255, 255, 0.5);
124
+ overflow: hidden;
125
+ border-style: solid;
126
+ border-color: rgba(0, 0, 0, 0.2);
127
+ border-image: initial;
128
+ border-width: 0px 1px;
129
+ padding: 7px 5px 2px 5px;
130
+ }
131
+
132
+ mwc-icon {
133
+ margin-left: 5px;
134
+ color: var(--secondary-color);
135
+ opacity: 0.8;
136
+ cursor: pointer;
137
+ --mdc-icon-size: 18px;
138
+ }
139
+
140
+ mwc-icon:hover {
141
+ color: var(--primary-color);
142
+ opacity: 1;
143
+ }
144
+
145
+ mwc-icon[disabled] {
146
+ color: rgba(0, 0, 0, 0.1);
147
+ }
148
+
149
+ data-binding-mapper {
150
+ --things-select: {
151
+ min-width: 50%;
152
+ margin-bottom: 10px;
153
+ padding: 3px 20px 2px 5px;
154
+ -webkit-border-radius: 4px;
155
+ -moz-border-radius: 4px;
156
+ border-radius: 4px;
157
+ border: 1px solid rgba(0, 0, 0, 0.15);
158
+ font-size: 15px;
159
+ font-weight: 300;
160
+ -webkit-appearance: none;
161
+ }
162
+ }
163
+ `
164
+ ]
165
+
166
+ @property({ type: Object }) value?: Properties
167
+ @property({ type: Object }) scene?: Scene
168
+
169
+ @state() mappingIndex: number = 0
170
+ @state() _afterRender?: Function | null
171
+ @state() _dataExpanded: boolean = false
172
+
173
+ @query('#tabs') tabs!: HTMLElement
174
+ @query('#tab-nav-left-button') tabNavLeftButton!: HTMLElement
175
+ @query('#tab-nav-right-button') tabNavRightButton!: HTMLElement
176
+
177
+ private mapping: Properties = {}
178
+
179
+ get mappings() {
180
+ return this.value?.mappings || []
181
+ }
182
+
183
+ firstUpdated() {
184
+ this.renderRoot.addEventListener('change', this._onValueChange.bind(this))
185
+
186
+ this.tabContainer.addEventListener('scroll', () => {
187
+ this._onTabScroll()
188
+ })
189
+ }
190
+
191
+ updated(changes: PropertyValues<this>) {
192
+ if (changes.has('value')) {
193
+ this._onValueChanged()
194
+ }
195
+ }
196
+
197
+ static get scopedElements() {
198
+ return {
199
+ 'data-binding-mapper': DataBindingMapper
200
+ }
201
+ }
202
+
203
+ render() {
204
+ const value = this.value || {
205
+ mappings: []
206
+ }
207
+
208
+ return html`
209
+ <fieldset>
210
+ <legend>
211
+ <ox-title-with-help topic="board-modeller/data-binding" msgid="label.identifier"
212
+ >identifier</ox-title-with-help
213
+ >
214
+ </legend>
215
+ <div class="property-grid">
216
+ <label> <ox-i18n msgid="label.id">ID</ox-i18n> </label>
217
+ <input value-key="id" .value=${value.id || ''} />
218
+
219
+ <label> <ox-i18n msgid="label.class">Class</ox-i18n> </label>
220
+ <input value-key="class" .value=${value.class || ''} />
221
+
222
+ <label> <ox-i18n msgid="label.tag">Tag</ox-i18n> </label>
223
+ <input value-key="tag" .value=${value.tag || ''} />
224
+
225
+ <label> <ox-i18n msgid="label.template-prefix">Template Prefix</ox-i18n> </label>
226
+ <input value-key="templatePrefix" .value=${value.templatePrefix || ''} />
227
+
228
+ <div class="checkbox-row">
229
+ <input id="checkbox-ndns" type="checkbox" value-key="ndns" .checked=${value.ndns} />
230
+ <label for="checkbox-ndns"> <ox-i18n msgid="label.ndns">No Data No Show</ox-i18n> </label>
231
+ </div>
232
+
233
+ <div class="checkbox-row">
234
+ <input id="checkbox-sensitive" type="checkbox" value-key="sensitive" .checked=${value.sensitive} />
235
+ <label for="checkbox-sensitive">
236
+ <ox-i18n msgid="label.intent-sensitive">Intent Sensitive</ox-i18n>
237
+ </label>
238
+ </div>
239
+
240
+ <div class="checkbox-row">
241
+ <input id="checkbox-persistent" type="checkbox" value-key="persistent" .checked=${value.persistent} />
242
+ <label for="checkbox-persistent">
243
+ <ox-i18n msgid="label.persistent-data">Persistent Data</ox-i18n>
244
+ </label>
245
+ </div>
246
+ </div>
247
+ </fieldset>
248
+
249
+ <fieldset collapsable ?collapsed=${!this._dataExpanded}>
250
+ <legend>
251
+ <ox-title-with-help topic="board-modeller/initial-data" msgid="label.initial-data"
252
+ >initial value</ox-title-with-help
253
+ >
254
+ <mwc-icon
255
+ @click=${() => {
256
+ this._dataExpanded = !this._dataExpanded
257
+ }}
258
+ >${this._dataExpanded ? 'expand_less' : 'expand_more'}</mwc-icon
259
+ >
260
+ </legend>
261
+ <ox-input-data value-key="data" .value=${value.data}> </ox-input-data>
262
+ </fieldset>
263
+
264
+ <fieldset>
265
+ <legend>
266
+ <ox-title-with-help topic="board-modeller/data-spread" msgid="label.data-spread"
267
+ >Data Spread</ox-title-with-help
268
+ >
269
+ </legend>
270
+
271
+ <div id="tab-header">
272
+ <mwc-icon
273
+ id="tab-nav-left-button"
274
+ @click=${() => {
275
+ this._onTabScrollNavLeft()
276
+ }}
277
+ disabled
278
+ >chevron_left</mwc-icon
279
+ >
280
+
281
+ <ox-buttons-radio
282
+ id="tabs"
283
+ .value=${String(this.mappingIndex)}
284
+ @change=${(e: Event) => {
285
+ e.stopPropagation()
286
+ this._setMappingIndex((e.target as any).value)
287
+ }}
288
+ >
289
+ ${this.mappings.map((m: string, i: number) => html` <div data-value=${i} data-mapping>${i + 1}</div> `)}
290
+ <div data-value=${this.mappings.length} data-mapping disabled>${this.mappings.length + 1}</div>
291
+ </ox-buttons-radio>
292
+
293
+ <mwc-icon
294
+ id="tab-nav-right-button"
295
+ @click=${(e: Event) => {
296
+ this._onTabScrollNavRight()
297
+ }}
298
+ disabled
299
+ >chevron_right</mwc-icon
300
+ >
301
+ </div>
302
+
303
+ <div binding>
304
+ <mwc-icon style="font-size:19px" @click=${() => this._clearDataBindingMapper()} title="delete current tab"
305
+ >delete_forever</mwc-icon
306
+ >
307
+ <mwc-icon @click=${() => this._pasteDataBindingMapper()} title="replace all tabs">content_paste</mwc-icon>
308
+ <mwc-icon style="font-size:17px" @click=${() => this._copyDataBindingMapper()} title="copy all tabs"
309
+ >content_copy</mwc-icon
310
+ >
311
+ </div>
312
+
313
+ <data-binding-mapper
314
+ @value-change=${(e: CustomEvent) => this._onMappingChanged(e)}
315
+ .scene=${this.scene}
316
+ .mapping=${(value.mappings && value.mappings[this.mappingIndex]) || {}}
317
+ .properties=${PROPS}
318
+ >
319
+ </data-binding-mapper>
320
+ </fieldset>
321
+ `
322
+ }
323
+
324
+ _setMappingIndex(idx: number) {
325
+ this.mappingIndex = isNaN(Number(idx)) ? 0 : Number(idx)
326
+
327
+ this._onTabScroll()
328
+ }
329
+
330
+ _clearDataBindingMapper() {
331
+ var mappings = [...(this.value?.mappings || [])]
332
+ mappings.splice(this.mappingIndex, 1)
333
+ this._onAfterValueChange(
334
+ 'mappings',
335
+ mappings.filter(m => !!m)
336
+ )
337
+ }
338
+
339
+ _copyDataBindingMapper() {
340
+ clipboard = JSON.stringify(this.mappings[this.mappingIndex])
341
+ }
342
+
343
+ async _pasteDataBindingMapper() {
344
+ var index = this.mappingIndex
345
+ var mappings = [...(this.value?.mappings || [])]
346
+ mappings[this.mappingIndex] = JSON.parse(clipboard)
347
+
348
+ this._onAfterValueChange('mappings', mappings)
349
+
350
+ setTimeout(() => {
351
+ this._setMappingIndex(index)
352
+ }, 100)
353
+ }
354
+
355
+ async _onValueChanged() {
356
+ await this.updateComplete
357
+
358
+ if (this._afterRender) {
359
+ this._afterRender()
360
+ } else {
361
+ this._setMappingIndex(0)
362
+ }
363
+
364
+ this._afterRender = null
365
+ }
366
+
367
+ _onValueChange(e: Event) {
368
+ var element = e.target as HTMLElement
369
+ var key = element.getAttribute('value-key')
370
+
371
+ var value = this._getValueFromEventTarget(element)
372
+
373
+ if (!key) {
374
+ return
375
+ }
376
+
377
+ this.value = {
378
+ ...this.value,
379
+ [key]: value
380
+ }
381
+
382
+ this._onAfterValueChange(key, value)
383
+ }
384
+
385
+ get tabContainer() {
386
+ return this.tabs
387
+ }
388
+
389
+ async _onMappingChanged(e: CustomEvent) {
390
+ var mapping = (e.target as any).mapping
391
+
392
+ /* data spread target의 변경이 있는 경우, target 컴포넌트들의 태그를 블링크 시킨다 */
393
+ if (mapping && mapping.target) {
394
+ this.scene &&
395
+ this.scene.findAll(mapping.target, this.scene.selected && this.scene.selected[0]).forEach((c, i) => {
396
+ if (i == 0) c.trigger('decotagreset')
397
+ c.trigger('decotag', {})
398
+ })
399
+ }
400
+
401
+ /* mapping의 모든 속성이 편집되면, 모델에 반영한다. */
402
+ var mappings = [...(this.value?.mappings || [])]
403
+
404
+ if (mapping.target && mapping.property && mapping.rule) {
405
+ mappings[this.mappingIndex] = mapping
406
+
407
+ var mappingIdx = this.mappingIndex
408
+ this._afterRender = () => {
409
+ this._setMappingIndex(mappingIdx)
410
+ this.tabContainer.scrollLeft = this.tabContainer.scrollWidth
411
+ }
412
+
413
+ this.dispatchEvent(
414
+ new CustomEvent('property-change', {
415
+ bubbles: true,
416
+ composed: true,
417
+ detail: {
418
+ mappings: mappings.filter(m => !!m)
419
+ }
420
+ })
421
+ )
422
+
423
+ await this.requestUpdate()
424
+ } else if (!mapping.target && !mapping.property) {
425
+ // accessor를 입력중인 경우 tabIndex Change 방지
426
+ if (e.detail && e.detail.changed && !e.detail.changed.accessor) {
427
+ mappings[this.mappingIndex] = null
428
+ var nextMappingIdx = Math.max(this.mappingIndex - 1, 0)
429
+ this._afterRender = () => {
430
+ this._setMappingIndex(nextMappingIdx)
431
+ }
432
+ this.dispatchEvent(
433
+ new CustomEvent('property-change', {
434
+ bubbles: true,
435
+ composed: true,
436
+ detail: {
437
+ mappings: mappings.filter(m => !!m)
438
+ }
439
+ })
440
+ )
441
+ }
442
+ }
443
+ }
444
+
445
+ _onTabScroll() {
446
+ if (this.tabContainer.clientWidth == this.tabContainer.scrollWidth) {
447
+ this.tabNavLeftButton.setAttribute('disabled', '')
448
+ this.tabNavRightButton.setAttribute('disabled', '')
449
+ }
450
+ // left-end
451
+ else if (this.tabContainer.scrollLeft == 0) {
452
+ this.tabNavLeftButton.setAttribute('disabled', '')
453
+ this.tabNavRightButton.removeAttribute('disabled')
454
+ }
455
+ // right-end
456
+ else if (this.tabContainer.scrollLeft + this.tabContainer.clientWidth >= this.tabContainer.scrollWidth) {
457
+ this.tabNavLeftButton.removeAttribute('disabled')
458
+ this.tabNavRightButton.setAttribute('disabled', '')
459
+ } else {
460
+ this.tabNavLeftButton.removeAttribute('disabled')
461
+ this.tabNavRightButton.removeAttribute('disabled')
462
+ }
463
+ }
464
+
465
+ _onTabScrollNavLeft() {
466
+ this.tabContainer.style.scrollBehavior = 'smooth'
467
+ this.tabContainer.scrollLeft -= this.tabContainer.clientWidth
468
+ this.tabContainer.style.scrollBehavior = 'auto'
469
+ }
470
+
471
+ _onTabScrollNavRight() {
472
+ this.tabContainer.style.scrollBehavior = 'smooth'
473
+ this.tabContainer.scrollLeft += this.tabContainer.clientWidth
474
+ this.tabContainer.style.scrollBehavior = 'auto'
475
+ }
476
+ }
@@ -35,7 +35,12 @@ export class PropertyEventHover extends LitElement {
35
35
  <label> <ox-i18n msgid="label.action">action</ox-i18n> </label>
36
36
  <select id="tap-select" value-key="action" .value=${action || ''}>
37
37
  <option value=""></option>
38
- <option value="popup">popup target board</option>
38
+ <option value="popup">popup target process</option>
39
+ <option value="infoWindow">open infowindow</option>
40
+ <option value="data-toggle">toggle(true/false) target component data</option>
41
+ <option value="data-tristate">tristate(0/1/2) target component data</option>
42
+ <option value="data-set">set value to target component data</option>
43
+ <option value="value-set">set value to target component value</option>
39
44
  </select>
40
45
 
41
46
  <label> <ox-i18n msgid="label.target">target</ox-i18n> </label>
@@ -41,7 +41,7 @@ export class PropertyEventTap extends LitElement {
41
41
  value-key="action"
42
42
  .value=${action || ''}
43
43
  @change=${async (e: Event) => {
44
- if ((e.target as HTMLSelectElement).value === 'start-scenario') {
44
+ if ((e.target as HTMLSelectElement).value.includes('scenario')) {
45
45
  this.scenarios = (await scenarios()).map(({ name, description }) => {
46
46
  return { value: name, description }
47
47
  })
@@ -49,15 +49,22 @@ export class PropertyEventTap extends LitElement {
49
49
  }}
50
50
  >
51
51
  <option value=""></option>
52
- <option value="goto">go to target board</option>
52
+ <option value="run">run this process</option>
53
+ <option value="goto">go to target process</option>
53
54
  <option value="link-open">open new window for target link</option>
54
55
  <option value="link-move">move to target link</option>
55
56
  <option value="route-page">route to page</option>
56
- <option value="popup">popup target board</option>
57
- <option value="modal-popup">modal popup target board</option>
58
- <option value="close-scene">close current board</option>
57
+ <option value="popup">popup target process</option>
58
+ <option value="modal-popup">modal popup target process</option>
59
+ <option value="close-scene">close current process</option>
59
60
  <option value="infoWindow">open infowindow</option>
60
61
  <option value="toggle-info-window">toggle infowindow</option>
62
+ <option value="data-toggle">toggle(true/false) target component data</option>
63
+ <option value="data-tristate">tristate(0/1/2) target component data</option>
64
+ <option value="data-set">set value to target component data</option>
65
+ <option value="value-set">set value to target component value</option>
66
+ <option value="start-scenario">start the given scenario</option>
67
+ <option value="run-scenario">run the given scenario</option>
61
68
  </select>
62
69
 
63
70
  <label> <ox-i18n msgid="label.target">target</ox-i18n> </label>
@@ -70,7 +77,8 @@ export class PropertyEventTap extends LitElement {
70
77
  custom-editor
71
78
  ></things-editor-board-selector>
72
79
  `
73
- : html`
80
+ : action !== 'run'
81
+ ? html`
74
82
  <input
75
83
  value-key="target"
76
84
  .value=${target}
@@ -86,7 +94,8 @@ export class PropertyEventTap extends LitElement {
86
94
  ({ value, description }) => html` <option .value=${value}>${description}</option> `
87
95
  )}
88
96
  </datalist>
89
- `}
97
+ `
98
+ : html``}
90
99
  ${action === 'goto' || action?.includes('popup')
91
100
  ? html`
92
101
  <label for="input"> <ox-i18n msgid="label.input-data">input</ox-i18n> </label>
@@ -12,6 +12,7 @@ import { BOUNDS, Component, Model, Scene } from '@hatiolab/things-scene'
12
12
  import { ScopedElementsMixin } from '@open-wc/scoped-elements'
13
13
  import { ScrollbarStyles } from '@operato/styles'
14
14
 
15
+ import { PropertyDataBinding } from './data-binding/data-binding.js'
15
16
  import { PropertyEffects } from './effects/effects.js'
16
17
  import { PropertyShapes } from './shapes/shapes.js'
17
18
  import { PropertySpecific } from './specifics/specifics.js'
@@ -99,6 +100,7 @@ export class PropertySidebar extends ScopedElementsMixin(LitElement) {
99
100
  'process-property-style': PropertyStyles,
100
101
  'process-property-effect': PropertyEffects,
101
102
  'process-property-specific': PropertySpecific,
103
+ 'process-property-data-binding': PropertyDataBinding,
102
104
  'process-scene-inspector': SceneInspector
103
105
  }
104
106
  }
@@ -117,6 +119,7 @@ export class PropertySidebar extends ScopedElementsMixin(LitElement) {
117
119
  <mwc-icon name="shape" ?selected=${tabName == 'shape'}>format_shapes</mwc-icon>
118
120
  <mwc-icon name="style" ?selected=${tabName == 'style'}>palette</mwc-icon>
119
121
  <mwc-icon name="effect" ?selected=${tabName == 'effect'}>movie_filter</mwc-icon>
122
+ <mwc-icon name="data-binding" ?selected=${tabName == 'data-binding'}>share</mwc-icon>
120
123
  <mwc-icon name="inspector" ?selected=${tabName == 'inspector'}>visibility</mwc-icon>
121
124
  </div>
122
125
 
@@ -156,6 +159,14 @@ export class PropertySidebar extends ScopedElementsMixin(LitElement) {
156
159
  <process-property-effect .value=${this.model} .scene=${this.scene} ?active=${tabName == 'effect'}>
157
160
  </process-property-effect>
158
161
  `,
162
+ 'data-binding': html`
163
+ <process-property-data-binding
164
+ .scene=${this.scene}
165
+ .value=${this.model}
166
+ ?active=${tabName == 'data-binding'}
167
+ >
168
+ </process-property-data-binding>
169
+ `,
159
170
  inspector: html`
160
171
  <process-scene-inspector .scene=${this.scene} ?active=${tabName == 'inspector'}></process-scene-inspector>
161
172
  `
@@ -321,6 +321,7 @@ export class OxProcessViewer extends LitElement {
321
321
  }
322
322
 
323
323
  bindSceneEvents() {
324
+ this._scene.on('run', this.onRunProcess, this)
324
325
  this._scene.on('goto', this.onLinkGoto, this)
325
326
  this._scene.on('link-open', this.onLinkOpen, this)
326
327
  this._scene.on('link-move', this.onLinkMove, this)
@@ -330,6 +331,7 @@ export class OxProcessViewer extends LitElement {
330
331
  }
331
332
 
332
333
  unbindSceneEvents(scene: any) {
334
+ scene.off('run', this.onRunProcess, this)
333
335
  scene.off('goto', this.onLinkGoto, this)
334
336
  scene.off('link-open', this.onLinkOpen, this)
335
337
  scene.off('link-move', this.onLinkMove, this)
@@ -422,6 +424,10 @@ export class OxProcessViewer extends LitElement {
422
424
  )
423
425
  }
424
426
 
427
+ onRunProcess() {
428
+ this.dispatchEvent(new CustomEvent('run-process', { bubbles: true, composed: true, detail: this.process.id }))
429
+ }
430
+
425
431
  onLinkGoto(targetProcessId: string, options: any, fromComponent: any) {
426
432
  const { input, output } = options || { input: '(self)' }
427
433
  const data = input && fromComponent.root.findFirst(input, fromComponent)?.data