@operato/property-panel 9.0.0-beta.14

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 (156) hide show
  1. package/.editorconfig +29 -0
  2. package/.storybook/main.js +5 -0
  3. package/.storybook/preview.js +52 -0
  4. package/.storybook/server.mjs +8 -0
  5. package/CHANGELOG.md +11 -0
  6. package/LICENSE +21 -0
  7. package/README.md +76 -0
  8. package/demo/index.html +30 -0
  9. package/dist/src/graphql/board.d.ts +6 -0
  10. package/dist/src/graphql/board.js +130 -0
  11. package/dist/src/graphql/board.js.map +1 -0
  12. package/dist/src/graphql/data-subscription.d.ts +5 -0
  13. package/dist/src/graphql/data-subscription.js +24 -0
  14. package/dist/src/graphql/data-subscription.js.map +1 -0
  15. package/dist/src/graphql/favorite-board.d.ts +1 -0
  16. package/dist/src/graphql/favorite-board.js +23 -0
  17. package/dist/src/graphql/favorite-board.js.map +1 -0
  18. package/dist/src/graphql/group.d.ts +7 -0
  19. package/dist/src/graphql/group.js +125 -0
  20. package/dist/src/graphql/group.js.map +1 -0
  21. package/dist/src/graphql/index.d.ts +4 -0
  22. package/dist/src/graphql/index.js +5 -0
  23. package/dist/src/graphql/index.js.map +1 -0
  24. package/dist/src/graphql/play-group.d.ts +13 -0
  25. package/dist/src/graphql/play-group.js +205 -0
  26. package/dist/src/graphql/play-group.js.map +1 -0
  27. package/dist/src/graphql/scenario.d.ts +6 -0
  28. package/dist/src/graphql/scenario.js +69 -0
  29. package/dist/src/graphql/scenario.js.map +1 -0
  30. package/dist/src/index.d.ts +7 -0
  31. package/dist/src/index.js +8 -0
  32. package/dist/src/index.js.map +1 -0
  33. package/dist/src/ox-property-panel.d.ts +46 -0
  34. package/dist/src/ox-property-panel.js +346 -0
  35. package/dist/src/ox-property-panel.js.map +1 -0
  36. package/dist/src/property-panel/abstract-property.d.ts +10 -0
  37. package/dist/src/property-panel/abstract-property.js +53 -0
  38. package/dist/src/property-panel/abstract-property.js.map +1 -0
  39. package/dist/src/property-panel/data-binding/data-binding-mapper.d.ts +58 -0
  40. package/dist/src/property-panel/data-binding/data-binding-mapper.js +380 -0
  41. package/dist/src/property-panel/data-binding/data-binding-mapper.js.map +1 -0
  42. package/dist/src/property-panel/data-binding/data-binding-value-map.d.ts +6 -0
  43. package/dist/src/property-panel/data-binding/data-binding-value-map.js +20 -0
  44. package/dist/src/property-panel/data-binding/data-binding-value-map.js.map +1 -0
  45. package/dist/src/property-panel/data-binding/data-binding-value-range.d.ts +6 -0
  46. package/dist/src/property-panel/data-binding/data-binding-value-range.js +20 -0
  47. package/dist/src/property-panel/data-binding/data-binding-value-range.js.map +1 -0
  48. package/dist/src/property-panel/data-binding/data-binding.d.ts +44 -0
  49. package/dist/src/property-panel/data-binding/data-binding.js +442 -0
  50. package/dist/src/property-panel/data-binding/data-binding.js.map +1 -0
  51. package/dist/src/property-panel/effects/effects.d.ts +24 -0
  52. package/dist/src/property-panel/effects/effects.js +72 -0
  53. package/dist/src/property-panel/effects/effects.js.map +1 -0
  54. package/dist/src/property-panel/effects/property-animation.d.ts +23 -0
  55. package/dist/src/property-panel/effects/property-animation.js +147 -0
  56. package/dist/src/property-panel/effects/property-animation.js.map +1 -0
  57. package/dist/src/property-panel/effects/property-animations.d.ts +22 -0
  58. package/dist/src/property-panel/effects/property-animations.js +70 -0
  59. package/dist/src/property-panel/effects/property-animations.js.map +1 -0
  60. package/dist/src/property-panel/effects/property-event-hover.d.ts +21 -0
  61. package/dist/src/property-panel/effects/property-event-hover.js +193 -0
  62. package/dist/src/property-panel/effects/property-event-hover.js.map +1 -0
  63. package/dist/src/property-panel/effects/property-event-tap.d.ts +36 -0
  64. package/dist/src/property-panel/effects/property-event-tap.js +262 -0
  65. package/dist/src/property-panel/effects/property-event-tap.js.map +1 -0
  66. package/dist/src/property-panel/effects/property-event.d.ts +22 -0
  67. package/dist/src/property-panel/effects/property-event.js +64 -0
  68. package/dist/src/property-panel/effects/property-event.js.map +1 -0
  69. package/dist/src/property-panel/effects/property-shadow.d.ts +23 -0
  70. package/dist/src/property-panel/effects/property-shadow.js +66 -0
  71. package/dist/src/property-panel/effects/property-shadow.js.map +1 -0
  72. package/dist/src/property-panel/effects/value-converter.d.ts +1 -0
  73. package/dist/src/property-panel/effects/value-converter.js +17 -0
  74. package/dist/src/property-panel/effects/value-converter.js.map +1 -0
  75. package/dist/src/property-panel/inspector/inspector.d.ts +27 -0
  76. package/dist/src/property-panel/inspector/inspector.js +357 -0
  77. package/dist/src/property-panel/inspector/inspector.js.map +1 -0
  78. package/dist/src/property-panel/shapes/shapes.d.ts +26 -0
  79. package/dist/src/property-panel/shapes/shapes.js +312 -0
  80. package/dist/src/property-panel/shapes/shapes.js.map +1 -0
  81. package/dist/src/property-panel/specifics/specific-properties-builder.d.ts +16 -0
  82. package/dist/src/property-panel/specifics/specific-properties-builder.js +138 -0
  83. package/dist/src/property-panel/specifics/specific-properties-builder.js.map +1 -0
  84. package/dist/src/property-panel/specifics/specifics.d.ts +25 -0
  85. package/dist/src/property-panel/specifics/specifics.js +84 -0
  86. package/dist/src/property-panel/specifics/specifics.js.map +1 -0
  87. package/dist/src/property-panel/styles/styles.d.ts +23 -0
  88. package/dist/src/property-panel/styles/styles.js +269 -0
  89. package/dist/src/property-panel/styles/styles.js.map +1 -0
  90. package/dist/src/types.d.ts +43 -0
  91. package/dist/src/types.js +2 -0
  92. package/dist/src/types.js.map +1 -0
  93. package/dist/stories/index.stories.d.ts +22 -0
  94. package/dist/stories/index.stories.js +121 -0
  95. package/dist/stories/index.stories.js.map +1 -0
  96. package/dist/stories/input-table-property.stories.d.ts +21 -0
  97. package/dist/stories/input-table-property.stories.js +84 -0
  98. package/dist/stories/input-table-property.stories.js.map +1 -0
  99. package/dist/test/ox-property-panel.test.d.ts +1 -0
  100. package/dist/test/ox-property-panel.test.js +24 -0
  101. package/dist/test/ox-property-panel.test.js.map +1 -0
  102. package/dist/tsconfig.tsbuildinfo +1 -0
  103. package/package.json +108 -0
  104. package/src/graphql/board.ts +144 -0
  105. package/src/graphql/data-subscription.ts +30 -0
  106. package/src/graphql/favorite-board.ts +25 -0
  107. package/src/graphql/group.ts +138 -0
  108. package/src/graphql/index.ts +4 -0
  109. package/src/graphql/play-group.ts +225 -0
  110. package/src/graphql/scenario.ts +79 -0
  111. package/src/index.ts +8 -0
  112. package/src/ox-property-panel.ts +347 -0
  113. package/src/property-panel/abstract-property.ts +67 -0
  114. package/src/property-panel/data-binding/data-binding-mapper.ts +412 -0
  115. package/src/property-panel/data-binding/data-binding-value-map.ts +19 -0
  116. package/src/property-panel/data-binding/data-binding-value-range.ts +19 -0
  117. package/src/property-panel/data-binding/data-binding.ts +464 -0
  118. package/src/property-panel/effects/effects.ts +77 -0
  119. package/src/property-panel/effects/property-animation.ts +155 -0
  120. package/src/property-panel/effects/property-animations.ts +74 -0
  121. package/src/property-panel/effects/property-event-hover.ts +212 -0
  122. package/src/property-panel/effects/property-event-tap.ts +269 -0
  123. package/src/property-panel/effects/property-event.ts +73 -0
  124. package/src/property-panel/effects/property-shadow.ts +77 -0
  125. package/src/property-panel/effects/value-converter.ts +17 -0
  126. package/src/property-panel/inspector/inspector.ts +407 -0
  127. package/src/property-panel/shapes/shapes.ts +321 -0
  128. package/src/property-panel/specifics/specific-properties-builder.ts +152 -0
  129. package/src/property-panel/specifics/specifics.ts +81 -0
  130. package/src/property-panel/styles/styles.ts +287 -0
  131. package/src/types.ts +63 -0
  132. package/stories/index.stories.ts +134 -0
  133. package/stories/input-table-property.stories.ts +96 -0
  134. package/test/ox-property-panel.test.ts +32 -0
  135. package/themes/app-theme.css +138 -0
  136. package/themes/calendar-theme.css +61 -0
  137. package/themes/dark.css +51 -0
  138. package/themes/grist-theme.css +175 -0
  139. package/themes/help-theme.css +57 -0
  140. package/themes/layout-theme.css +94 -0
  141. package/themes/light.css +51 -0
  142. package/themes/material-theme.css +23 -0
  143. package/themes/md-typescale-styles.css +100 -0
  144. package/themes/oops-theme.css +22 -0
  145. package/themes/report-theme.css +47 -0
  146. package/themes/spacing.css +23 -0
  147. package/themes/state-color.css +6 -0
  148. package/themes/tooltip-theme.css +11 -0
  149. package/translations/en.json +723 -0
  150. package/translations/ja.json +727 -0
  151. package/translations/ko.json +727 -0
  152. package/translations/ms.json +609 -0
  153. package/translations/zh.json +726 -0
  154. package/tsconfig.json +25 -0
  155. package/web-dev-server.config.mjs +27 -0
  156. package/web-test-runner.config.mjs +41 -0
@@ -0,0 +1,464 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@material/web/icon/icon.js'
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
+ import { PropertyGridStyles } from '@operato/styles/property-grid-styles.js'
17
+
18
+ import { AbstractProperty } from '../abstract-property.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
+ 'action',
37
+ 'options',
38
+ 'rotate',
39
+ 'dimension',
40
+ 'location',
41
+ 'accessor',
42
+ ['tap', '(tap)'],
43
+ '(action)'
44
+ ].map(prop => {
45
+ return typeof prop == 'string' ? { name: prop, label: prop } : { name: prop[0], label: prop[1] }
46
+ })
47
+
48
+ export class PropertyDataBinding extends ScopedElementsMixin(AbstractProperty) {
49
+ static styles = [
50
+ PropertyGridStyles,
51
+ css`
52
+ #tab-header {
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: space-between;
56
+ }
57
+
58
+ #tab-header > md-icon {
59
+ padding: 0;
60
+ margin: 0;
61
+ width: 25px;
62
+ height: 25px;
63
+ font-size: x-large;
64
+ border-bottom: 1px solid rgba(0, 0, 0, 0.2);
65
+ }
66
+
67
+ fieldset[collapsable][collapsed] > :not(legend) {
68
+ display: none;
69
+ }
70
+
71
+ ox-buttons-radio {
72
+ flex: 1;
73
+ height: 25px;
74
+ border: 1px solid rgba(0, 0, 0, 0.2);
75
+ border-width: 1px 1px 0 1px;
76
+ text-align: center;
77
+
78
+ display: flex;
79
+ padding: 0;
80
+ box-sizing: border-box;
81
+
82
+ width: 0; /* limit width */
83
+ overflow-x: hidden;
84
+ }
85
+
86
+ ox-buttons-radio > div {
87
+ background-color: rgba(0, 0, 0, 0.2);
88
+ border: 1px solid rgba(0, 0, 0, 0.07);
89
+ border-width: 0 0 2px 0;
90
+ padding: 0;
91
+ margin: 0;
92
+ color: #fff;
93
+ font-size: 13px;
94
+ max-width: 25px;
95
+ min-width: 25px;
96
+ }
97
+
98
+ ox-buttons-radio > div[disabled] {
99
+ background-color: rgba(0, 0, 0, 0.1);
100
+ }
101
+
102
+ ox-buttons-radio > div[active] {
103
+ border-color: rgb(242, 71, 28);
104
+ }
105
+
106
+ ox-buttons-radio > div.iron-selected {
107
+ background-color: var(--md-sys-color-surface);
108
+ color: var(--md-sys-color-on-surface);
109
+ }
110
+
111
+ div[binding] {
112
+ display: flex;
113
+ flex-direction: row-reverse;
114
+ background-color: var(--md-sys-color-surface);
115
+ color: var(--md-sys-color-on-surface);
116
+ overflow: hidden;
117
+ border-style: solid;
118
+ border-color: rgba(0, 0, 0, 0.2);
119
+ border-image: initial;
120
+ border-width: 0px 1px;
121
+ padding: 7px 5px 2px 5px;
122
+ }
123
+
124
+ md-icon {
125
+ margin-left: 5px;
126
+ color: var(--md-sys-color-on-secondary-container);
127
+ opacity: 0.8;
128
+ cursor: pointer;
129
+ --md-icon-size: 18px;
130
+ }
131
+
132
+ md-icon:hover {
133
+ color: var(--md-sys-color-on-primary-container);
134
+ opacity: 1;
135
+ }
136
+
137
+ md-icon[disabled] {
138
+ color: rgba(0, 0, 0, 0.1);
139
+ }
140
+
141
+ data-binding-mapper {
142
+ --things-select: {
143
+ min-width: 50%;
144
+ margin-bottom: 10px;
145
+ padding: 3px 20px 2px 5px;
146
+ -webkit-border-radius: 4px;
147
+ -moz-border-radius: 4px;
148
+ border-radius: 4px;
149
+ border: 1px solid rgba(0, 0, 0, 0.15);
150
+ font-size: 15px;
151
+ font-weight: 300;
152
+ -webkit-appearance: none;
153
+ };
154
+ }
155
+ `
156
+ ]
157
+
158
+ @property({ type: Object }) value?: Properties
159
+ @property({ type: Object }) scene?: Scene
160
+
161
+ @state() mappingIndex: number = 0
162
+ @state() _afterRender?: Function | null
163
+ @state() _dataExpanded: boolean = false
164
+
165
+ @query('#tabs') tabs!: HTMLElement
166
+ @query('#tab-nav-left-button') tabNavLeftButton!: HTMLElement
167
+ @query('#tab-nav-right-button') tabNavRightButton!: HTMLElement
168
+
169
+ private mapping: Properties = {}
170
+
171
+ get mappings() {
172
+ return this.value?.mappings || []
173
+ }
174
+
175
+ firstUpdated() {
176
+ this.renderRoot.addEventListener('change', this.onValueChange.bind(this))
177
+
178
+ this.tabContainer.addEventListener('scroll', () => {
179
+ this._onTabScroll()
180
+ })
181
+ }
182
+
183
+ updated(changes: PropertyValues<this>) {
184
+ if (changes.has('value')) {
185
+ this.onValueChanged()
186
+ }
187
+ }
188
+
189
+ static get scopedElements() {
190
+ return {
191
+ 'data-binding-mapper': DataBindingMapper
192
+ }
193
+ }
194
+
195
+ render() {
196
+ const value = this.value || {
197
+ mappings: []
198
+ }
199
+
200
+ return html`
201
+ <fieldset>
202
+ <legend>
203
+ <ox-title-with-help topic="board-modeller/data-binding" msgid="label.identifier"
204
+ >identifier</ox-title-with-help
205
+ >
206
+ </legend>
207
+ <div class="property-grid">
208
+ <label> <ox-i18n msgid="label.id">ID</ox-i18n> </label>
209
+ <input value-key="id" .value=${value.id || ''} />
210
+
211
+ <label> <ox-i18n msgid="label.class">Class</ox-i18n> </label>
212
+ <input value-key="class" .value=${value.class || ''} />
213
+
214
+ <label> <ox-i18n msgid="label.tag">Tag</ox-i18n> </label>
215
+ <input value-key="tag" .value=${value.tag || ''} />
216
+
217
+ <label> <ox-i18n msgid="label.template-prefix">Template Prefix</ox-i18n> </label>
218
+ <input value-key="templatePrefix" .value=${value.templatePrefix || ''} />
219
+
220
+ <input id="checkbox-ndns" type="checkbox" value-key="ndns" .checked=${value.ndns} />
221
+ <label for="checkbox-ndns"> <ox-i18n msgid="label.ndns">No Data No Show</ox-i18n> </label>
222
+
223
+ <input id="checkbox-sensitive" type="checkbox" value-key="sensitive" .checked=${value.sensitive} />
224
+ <label for="checkbox-sensitive">
225
+ <ox-i18n msgid="label.intent-sensitive">Intent Sensitive</ox-i18n>
226
+ </label>
227
+
228
+ <input id="checkbox-persistent" type="checkbox" value-key="persistent" .checked=${value.persistent} />
229
+ <label for="checkbox-persistent">
230
+ <ox-i18n msgid="label.persistent-data">Persistent Data</ox-i18n>
231
+ </label>
232
+ </div>
233
+ </fieldset>
234
+
235
+ <fieldset collapsable ?collapsed=${!this._dataExpanded}>
236
+ <legend>
237
+ <ox-title-with-help topic="board-modeller/initial-data" msgid="label.initial-data"
238
+ >initial value</ox-title-with-help
239
+ >
240
+ <md-icon
241
+ @click=${() => {
242
+ this._dataExpanded = !this._dataExpanded
243
+ }}
244
+ >${this._dataExpanded ? 'expand_less' : 'expand_more'}</md-icon
245
+ >
246
+ </legend>
247
+ <ox-input-data value-key="data" .value=${value.data}> </ox-input-data>
248
+ </fieldset>
249
+
250
+ <fieldset>
251
+ <legend>
252
+ <ox-title-with-help topic="board-modeller/data-spread" msgid="label.data-spread"
253
+ >Data Spread</ox-title-with-help
254
+ >
255
+ </legend>
256
+
257
+ <div id="tab-header">
258
+ <md-icon
259
+ id="tab-nav-left-button"
260
+ @click=${() => {
261
+ this._onTabScrollNavLeft()
262
+ }}
263
+ disabled
264
+ >chevron_left</md-icon
265
+ >
266
+
267
+ <ox-buttons-radio
268
+ id="tabs"
269
+ .value=${String(this.mappingIndex)}
270
+ @change=${(e: Event) => {
271
+ e.stopPropagation()
272
+ this._setMappingIndex((e.target as any).value)
273
+ }}
274
+ >
275
+ ${this.mappings.map((m: string, i: number) => html` <div data-value=${i} data-mapping>${i + 1}</div> `)}
276
+ <div data-value=${this.mappings.length} data-mapping disabled>${this.mappings.length + 1}</div>
277
+ </ox-buttons-radio>
278
+
279
+ <md-icon
280
+ id="tab-nav-right-button"
281
+ @click=${(e: Event) => {
282
+ this._onTabScrollNavRight()
283
+ }}
284
+ disabled
285
+ >chevron_right</md-icon
286
+ >
287
+ </div>
288
+
289
+ <div binding>
290
+ <md-icon style="font-size:19px" @click=${() => this._clearDataBindingMapper()} title="delete current tab"
291
+ >delete_forever</md-icon
292
+ >
293
+ <md-icon @click=${() => this._pasteDataBindingMapper()} title="replace current tab">content_paste</md-icon>
294
+ <md-icon style="font-size:17px" @click=${() => this._copyDataBindingMapper()} title="copy current tab"
295
+ >content_copy</md-icon
296
+ >
297
+ </div>
298
+
299
+ <data-binding-mapper
300
+ @value-change=${(e: CustomEvent) => this._onMappingChanged(e)}
301
+ .scene=${this.scene}
302
+ .mapping=${(value.mappings && value.mappings[this.mappingIndex]) || {}}
303
+ .properties=${PROPS}
304
+ >
305
+ </data-binding-mapper>
306
+ </fieldset>
307
+ `
308
+ }
309
+
310
+ _setMappingIndex(idx: number) {
311
+ this.mappingIndex = isNaN(Number(idx)) ? 0 : Number(idx)
312
+
313
+ this._onTabScroll()
314
+ }
315
+
316
+ _clearDataBindingMapper() {
317
+ var mappings = [...(this.value?.mappings || [])]
318
+ mappings.splice(this.mappingIndex, 1)
319
+ this.onAfterValueChange(
320
+ 'mappings',
321
+ mappings.filter(m => !!m)
322
+ )
323
+ }
324
+
325
+ _copyDataBindingMapper() {
326
+ clipboard = JSON.stringify(this.mappings[this.mappingIndex])
327
+ }
328
+
329
+ async _pasteDataBindingMapper() {
330
+ var index = this.mappingIndex
331
+ var mappings = [...(this.value?.mappings || [])]
332
+ mappings[this.mappingIndex] = JSON.parse(clipboard)
333
+
334
+ this.onAfterValueChange('mappings', mappings)
335
+
336
+ setTimeout(() => {
337
+ this._setMappingIndex(index)
338
+ }, 100)
339
+ }
340
+
341
+ async onValueChanged() {
342
+ await this.updateComplete
343
+
344
+ if (this._afterRender) {
345
+ this._afterRender()
346
+ } else {
347
+ this._setMappingIndex(0)
348
+ }
349
+
350
+ this._afterRender = null
351
+ }
352
+
353
+ onValueChange(e: Event) {
354
+ var element = e.target as HTMLElement
355
+ var key = element.getAttribute('value-key')
356
+
357
+ var value = this.getValueFromEventTarget(element)
358
+
359
+ if (!key) {
360
+ return
361
+ }
362
+
363
+ this.value = {
364
+ ...this.value,
365
+ [key]: value
366
+ }
367
+
368
+ this.onAfterValueChange(key, value)
369
+ }
370
+
371
+ get tabContainer() {
372
+ return this.tabs
373
+ }
374
+
375
+ async _onMappingChanged(e: CustomEvent) {
376
+ var mapping = (e.target as any).mapping
377
+
378
+ /* data spread target의 변경이 있는 경우, target 컴포넌트들의 태그를 블링크 시킨다 */
379
+ if (mapping && mapping.target) {
380
+ this.scene &&
381
+ this.scene.findAll(mapping.target, this.scene.selected && this.scene.selected[0]).forEach((c, i) => {
382
+ if (i == 0) c.trigger('decotagreset')
383
+ c.trigger('decotag', {})
384
+ })
385
+ }
386
+
387
+ /* mapping의 모든 속성이 편집되면, 모델에 반영한다. */
388
+ var mappings = [...(this.value?.mappings || [])]
389
+
390
+ if (mapping.target && mapping.property && mapping.rule) {
391
+ mappings[this.mappingIndex] = mapping
392
+
393
+ var mappingIdx = this.mappingIndex
394
+ this._afterRender = () => {
395
+ this._setMappingIndex(mappingIdx)
396
+ this.tabContainer.scrollLeft = this.tabContainer.scrollWidth
397
+ }
398
+
399
+ this.dispatchEvent(
400
+ new CustomEvent('property-change', {
401
+ bubbles: true,
402
+ composed: true,
403
+ detail: {
404
+ mappings: mappings.filter(m => !!m)
405
+ }
406
+ })
407
+ )
408
+
409
+ await this.requestUpdate()
410
+ } else if (!mapping.target && !mapping.property) {
411
+ const { accessor, source } = e.detail?.changed || {}
412
+
413
+ // accessor나 source를 입력중인 경우 tabIndex Change 방지
414
+ if (!accessor && !source) {
415
+ mappings[this.mappingIndex] = null
416
+ var nextMappingIdx = Math.max(this.mappingIndex - 1, 0)
417
+ this._afterRender = () => {
418
+ this._setMappingIndex(nextMappingIdx)
419
+ }
420
+ this.dispatchEvent(
421
+ new CustomEvent('property-change', {
422
+ bubbles: true,
423
+ composed: true,
424
+ detail: {
425
+ mappings: mappings.filter(m => !!m)
426
+ }
427
+ })
428
+ )
429
+ }
430
+ }
431
+ }
432
+
433
+ _onTabScroll() {
434
+ if (this.tabContainer.clientWidth == this.tabContainer.scrollWidth) {
435
+ this.tabNavLeftButton.setAttribute('disabled', '')
436
+ this.tabNavRightButton.setAttribute('disabled', '')
437
+ }
438
+ // left-end
439
+ else if (this.tabContainer.scrollLeft == 0) {
440
+ this.tabNavLeftButton.setAttribute('disabled', '')
441
+ this.tabNavRightButton.removeAttribute('disabled')
442
+ }
443
+ // right-end
444
+ else if (this.tabContainer.scrollLeft + this.tabContainer.clientWidth >= this.tabContainer.scrollWidth) {
445
+ this.tabNavLeftButton.removeAttribute('disabled')
446
+ this.tabNavRightButton.setAttribute('disabled', '')
447
+ } else {
448
+ this.tabNavLeftButton.removeAttribute('disabled')
449
+ this.tabNavRightButton.removeAttribute('disabled')
450
+ }
451
+ }
452
+
453
+ _onTabScrollNavLeft() {
454
+ this.tabContainer.style.scrollBehavior = 'smooth'
455
+ this.tabContainer.scrollLeft -= this.tabContainer.clientWidth
456
+ this.tabContainer.style.scrollBehavior = 'auto'
457
+ }
458
+
459
+ _onTabScrollNavRight() {
460
+ this.tabContainer.style.scrollBehavior = 'smooth'
461
+ this.tabContainer.scrollLeft += this.tabContainer.clientWidth
462
+ this.tabContainer.style.scrollBehavior = 'auto'
463
+ }
464
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@operato/i18n/ox-i18n.js'
6
+ import '@operato/help/ox-title-with-help.js'
7
+
8
+ import { css, html } from 'lit'
9
+ import { property } from 'lit/decorators.js'
10
+
11
+ import { Properties, Scene } from '@hatiolab/things-scene'
12
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
13
+ import { PropertyGridStyles } from '@operato/styles/property-grid-styles.js'
14
+
15
+ import { AbstractProperty } from '../abstract-property.js'
16
+ import { PropertyAnimations } from './property-animations.js'
17
+ import { PropertyEvent } from './property-event.js'
18
+ import { PropertyShadow } from './property-shadow.js'
19
+
20
+ export class PropertyEffects extends ScopedElementsMixin(AbstractProperty) {
21
+ static styles = [
22
+ PropertyGridStyles,
23
+ css`
24
+ :host {
25
+ display: flex;
26
+ flex-direction: column;
27
+ }
28
+ `
29
+ ]
30
+
31
+ @property({ type: Object }) value?: Properties
32
+ @property({ type: Object }) scene?: Scene
33
+
34
+ firstUpdated() {
35
+ this.renderRoot.addEventListener('change', this.onValueChange.bind(this))
36
+ }
37
+
38
+ static get scopedElements() {
39
+ return {
40
+ 'property-shadow': PropertyShadow,
41
+ 'property-animations': PropertyAnimations,
42
+ 'property-event': PropertyEvent
43
+ }
44
+ }
45
+
46
+ render() {
47
+ const value = this.value || {}
48
+
49
+ return html`
50
+ <fieldset>
51
+ <legend>
52
+ <ox-title-with-help topic="board-modeller/effects/shadow" msgid="label.shadow">shadow</ox-title-with-help>
53
+ </legend>
54
+
55
+ <property-shadow value-key="shadow" .value=${value.shadow || {}}> </property-shadow>
56
+ </fieldset>
57
+
58
+ <fieldset>
59
+ <legend>
60
+ <ox-title-with-help topic="board-modeller/effects/retention" msgid="label.retention"
61
+ >retention</ox-title-with-help
62
+ >
63
+ </legend>
64
+
65
+ <div class="property-grid">
66
+ <label> <ox-i18n msgid="label.retention">retention</ox-i18n> </label>
67
+ <input type="number" value-key="retention" .value=${value.retention} placeholder="ms" />
68
+ </div>
69
+ </fieldset>
70
+
71
+ <property-animations value-key="animation" .scene=${this.scene} .value=${value.animation || {}}>
72
+ </property-animations>
73
+
74
+ <property-event value-key="event" .scene=${this.scene} .value=${value.event || {}}> </property-event>
75
+ `
76
+ }
77
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @license Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import '@operato/input/ox-input-angle.js'
6
+ import '@operato/i18n/ox-i18n.js'
7
+
8
+ import { css, html, LitElement } from 'lit'
9
+ import { property } from 'lit/decorators.js'
10
+
11
+ import { Properties, Scene } from '@hatiolab/things-scene'
12
+ import { PropertyGridStyles } from '@operato/styles/property-grid-styles.js'
13
+
14
+ import { convert } from './value-converter.js'
15
+
16
+ /**
17
+ * 컴포넌트의 animation 속성을 편집하는 element
18
+
19
+ Example:
20
+
21
+ <property-animation .value=${animation}>
22
+ </property-animation>
23
+ */
24
+
25
+ export class PropertyAnimation extends LitElement {
26
+ static styles = [
27
+ PropertyGridStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+ }
32
+ `
33
+ ]
34
+
35
+ @property({ type: Object }) value?: Properties
36
+ @property({ type: Object }) scene?: Scene
37
+
38
+ firstUpdated() {
39
+ this.renderRoot.addEventListener('change', this.onValueChange.bind(this))
40
+ }
41
+
42
+ render() {
43
+ const value = this.value || {}
44
+
45
+ return html`
46
+ <div class="property-grid">
47
+ <label>Animation Type</label>
48
+ <select value-key="type" .value=${value.type}>
49
+ <option value="">None</option>
50
+ <option value="rotation">Rotation</option>
51
+ <option value="vibration">Vibration</option>
52
+ <option value="heartbeat">Heartbeat</option>
53
+ <option value="moving">Moving</option>
54
+ <option value="fade">Fade</option>
55
+ <option value="outline">Outline</option>
56
+ </select>
57
+
58
+ <label> <ox-i18n msgid="label.waiting-time">waiting time</ox-i18n> </label>
59
+ <input type="number" value-key="delay" .value=${value.delay} placeholder="ms" />
60
+
61
+ <label> <ox-i18n msgid="label.duration">duration</ox-i18n> </label>
62
+ <input type="number" value-key="duration" .value=${value.duration} placeholder="ms" />
63
+
64
+ <label> <ox-i18n msgid="label.animation-interval">interval</ox-i18n> </label>
65
+ <input type="number" value-key="interval" .value=${value.interval} placeholder="ms" />
66
+
67
+ ${value.type == 'rotation' || value.type == 'vibration'
68
+ ? html`
69
+ <label> <ox-i18n msgid="label.theta">theta</ox-i18n> </label>
70
+ <ox-input-angle value-key="theta" .value=${value.theta}> </ox-input-angle>
71
+ `
72
+ : html``}
73
+ ${value.type == 'heartbeat'
74
+ ? html`
75
+ <label> <ox-i18n msgid="label.scale">scale</ox-i18n> </label>
76
+ <input type="number" value-key="scale" .value=${value.scale} />
77
+ `
78
+ : html``}
79
+ ${value.type == 'moving'
80
+ ? html`
81
+ <label> <ox-i18n msgid="label.x-axes">X-axes</ox-i18n> </label>
82
+ <input type="number" value-key="x" .value=${value.x} />
83
+
84
+ <label> <ox-i18n msgid="label.y-axes">Y-axes</ox-i18n> </label>
85
+ <input type="number" value-key="y" .value=${value.y} />
86
+ `
87
+ : html``}
88
+ ${value.type == 'fade'
89
+ ? html`
90
+ <label> <ox-i18n msgid="label.start-alpha">start alpha</ox-i18n> </label>
91
+ <input type="number" value-key="startAlpha" .value=${value.startAlpha} />
92
+
93
+ <label> <ox-i18n msgid="label.end-alpha">end alpha</ox-i18n> </label>
94
+ <input type="number" value-key="endAlpha" .value=${value.endAlpha} />
95
+ `
96
+ : html``}
97
+ ${value.type == 'outline'
98
+ ? html`
99
+ <label> <ox-i18n msgid="label.target">target</ox-i18n> </label>
100
+ <input value-key="rideOn" .value=${value.rideOn || ''} list="target-list" />
101
+ <datalist id="target-list">
102
+ ${this.scene!.ids.map(info => info.key).map(id => html` <option value=${id}></option> `)}
103
+ </datalist>
104
+ `
105
+ : html``}
106
+
107
+ <input id="checkbox-repeat" value-key="repeat" type="checkbox" .checked=${value.repeat} />
108
+ <label for="checkbox-repeat" class="checkbox-label"> <ox-i18n msgid="label.repeat">repeat</ox-i18n> </label>
109
+
110
+ <input
111
+ id="checkbox-autoplay"
112
+ value-key="autoplay"
113
+ type="checkbox"
114
+ .checked=${value.autoplay || (value.autoplay ?? true)}
115
+ />
116
+ <label for="checkbox-autoplay" class="checkbox-label">
117
+ <ox-i18n msgid="label.autoplay">autoplay</ox-i18n>
118
+ </label>
119
+
120
+ <label>delta</label>
121
+ <select value-key="delta" .value=${value.delta}>
122
+ <option value="linear">linear</option>
123
+ <option value="quad">quad</option>
124
+ <option value="circ">circ</option>
125
+ <option value="back">back</option>
126
+ <option value="bounce">bounce</option>
127
+ <option value="elastic">elastic</option>
128
+ </select>
129
+
130
+ <label>ease</label>
131
+ <select value-key="ease" .value=${value.ease}>
132
+ <option value="in">in</option>
133
+ <option value="out">out</option>
134
+ <option value="inout">inout</option>
135
+ </select>
136
+ </div>
137
+ `
138
+ }
139
+
140
+ onValueChange(e: Event) {
141
+ var element = e.target as HTMLElement
142
+ var key = element.getAttribute('value-key')
143
+
144
+ if (!key) {
145
+ return
146
+ }
147
+
148
+ this.value = {
149
+ ...this.value,
150
+ [key]: convert(element)
151
+ }
152
+
153
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))
154
+ }
155
+ }