@operato/board 8.0.0-alpha.9 → 8.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +256 -0
  2. package/demo/index-viewer.html +1 -1
  3. package/demo/index.html +1 -1
  4. package/dist/src/ox-board-player.js +2 -1
  5. package/dist/src/ox-board-player.js.map +1 -1
  6. package/dist/src/ox-board-template-viewer.d.ts +1 -1
  7. package/dist/src/ox-board-viewer.d.ts +3 -2
  8. package/dist/src/ox-board-viewer.js +16 -6
  9. package/dist/src/ox-board-viewer.js.map +1 -1
  10. package/dist/src/player/ox-board-wrapper.js +3 -1
  11. package/dist/src/player/ox-board-wrapper.js.map +1 -1
  12. package/dist/tsconfig.tsbuildinfo +1 -1
  13. package/package.json +25 -25
  14. package/.storybook/main.js +0 -3
  15. package/.storybook/server.mjs +0 -8
  16. package/src/component/3d.ts +0 -29
  17. package/src/component/chart-and-gauge.ts +0 -28
  18. package/src/component/container.ts +0 -63
  19. package/src/component/data-source.ts +0 -30
  20. package/src/component/etc.ts +0 -88
  21. package/src/component/form.ts +0 -42
  22. package/src/component/index.ts +0 -12
  23. package/src/component/iot.ts +0 -52
  24. package/src/component/line.ts +0 -156
  25. package/src/component/register-default-groups.ts +0 -28
  26. package/src/component/shape.ts +0 -156
  27. package/src/component/table.ts +0 -28
  28. package/src/component/text-and-media.ts +0 -125
  29. package/src/component/warehouse.ts +0 -26
  30. package/src/data-storage/data-storage.ts +0 -76
  31. package/src/graphql/board.ts +0 -144
  32. package/src/graphql/data-subscription.ts +0 -30
  33. package/src/graphql/favorite-board.ts +0 -25
  34. package/src/graphql/group.ts +0 -138
  35. package/src/graphql/index.ts +0 -4
  36. package/src/graphql/play-group.ts +0 -225
  37. package/src/graphql/scenario.ts +0 -79
  38. package/src/index.ts +0 -10
  39. package/src/modeller/component-toolbar/component-detail.ts +0 -52
  40. package/src/modeller/component-toolbar/component-menu.ts +0 -196
  41. package/src/modeller/component-toolbar/component-toolbar.ts +0 -196
  42. package/src/modeller/component-toolbar/mode-icons.ts +0 -88
  43. package/src/modeller/edit-toolbar-style.ts +0 -232
  44. package/src/modeller/edit-toolbar.ts +0 -587
  45. package/src/modeller/property-sidebar/abstract-property.ts +0 -69
  46. package/src/modeller/property-sidebar/data-binding/data-binding-mapper.ts +0 -475
  47. package/src/modeller/property-sidebar/data-binding/data-binding-value-map.ts +0 -19
  48. package/src/modeller/property-sidebar/data-binding/data-binding-value-range.ts +0 -19
  49. package/src/modeller/property-sidebar/data-binding/data-binding.ts +0 -480
  50. package/src/modeller/property-sidebar/effects/effects-shared-style.ts +0 -62
  51. package/src/modeller/property-sidebar/effects/effects.ts +0 -69
  52. package/src/modeller/property-sidebar/effects/property-animation.ts +0 -146
  53. package/src/modeller/property-sidebar/effects/property-animations.ts +0 -93
  54. package/src/modeller/property-sidebar/effects/property-event-hover.ts +0 -200
  55. package/src/modeller/property-sidebar/effects/property-event-tap.ts +0 -251
  56. package/src/modeller/property-sidebar/effects/property-event.ts +0 -73
  57. package/src/modeller/property-sidebar/effects/property-shadow.ts +0 -114
  58. package/src/modeller/property-sidebar/effects/value-converter.ts +0 -23
  59. package/src/modeller/property-sidebar/inspector/inspector.ts +0 -404
  60. package/src/modeller/property-sidebar/property-shared-style.ts +0 -136
  61. package/src/modeller/property-sidebar/property-sidebar.ts +0 -326
  62. package/src/modeller/property-sidebar/shapes/box-padding-editor-styles.ts +0 -94
  63. package/src/modeller/property-sidebar/shapes/shapes.ts +0 -432
  64. package/src/modeller/property-sidebar/specifics/specific-properties-builder.ts +0 -152
  65. package/src/modeller/property-sidebar/specifics/specifics.ts +0 -81
  66. package/src/modeller/property-sidebar/styles/styles.ts +0 -577
  67. package/src/modeller/scene-viewer/confidential-overlay.ts +0 -18
  68. package/src/modeller/scene-viewer/ox-scene-handler.ts +0 -40
  69. package/src/modeller/scene-viewer/ox-scene-layer.ts +0 -42
  70. package/src/modeller/scene-viewer/ox-scene-property.ts +0 -10
  71. package/src/modeller/scene-viewer/ox-scene-viewer.ts +0 -263
  72. package/src/ox-board-component-info.ts +0 -236
  73. package/src/ox-board-list.ts +0 -401
  74. package/src/ox-board-modeller.ts +0 -408
  75. package/src/ox-board-player-style.ts +0 -200
  76. package/src/ox-board-player.ts +0 -331
  77. package/src/ox-board-template-list.ts +0 -267
  78. package/src/ox-board-template-viewer.ts +0 -198
  79. package/src/ox-board-viewer.ts +0 -718
  80. package/src/ox-editor-board-selector.ts +0 -91
  81. package/src/ox-property-editor-board-selector.ts +0 -23
  82. package/src/player/ox-board-player-carousel.ts +0 -197
  83. package/src/player/ox-board-player-grid.ts +0 -78
  84. package/src/player/ox-board-wrapper.ts +0 -150
  85. package/src/selector/board-creation-popup.ts +0 -151
  86. package/src/selector/board-thumbnail-card.ts +0 -175
  87. package/src/selector/ox-board-creation-card.ts +0 -98
  88. package/src/selector/ox-board-selector.ts +0 -382
  89. package/src/types.ts +0 -63
  90. package/stories/property-data-binding.stories.ts +0 -34
  91. package/tsconfig.json +0 -24
  92. package/web-dev-server.config.mjs +0 -30
  93. package/web-test-runner.config.mjs +0 -29
@@ -1,718 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/fab/fab.js'
3
- import './ox-board-component-info.js'
4
-
5
- import { css, html, LitElement, PropertyValues } from 'lit'
6
- import { customElement, property, query, state } from 'lit/decorators.js'
7
- import * as XLSX from 'xlsx'
8
-
9
- import { Component, create, ReferenceProvider, SCENE_MODE } from '@hatiolab/things-scene'
10
- import { isIOS, togglefullscreen } from '@operato/utils'
11
- import { ScrollbarStyles } from '@operato/styles'
12
-
13
- import { BoardDataStorage } from './data-storage/data-storage.js'
14
- import { DataSubscriptionProviderImpl } from './graphql/data-subscription.js'
15
- import { runScenario, startScenario } from './graphql/scenario.js'
16
-
17
- import { BoardComponentInfo } from './ox-board-component-info.js'
18
- import { fetchPlayGroupByName } from './graphql/play-group.js'
19
-
20
- function objectToQueryString(obj: any) {
21
- const keyValuePairs = []
22
- for (const key in obj) {
23
- if (Object.hasOwnProperty.call(obj, key)) {
24
- const value = obj[key]
25
- if (typeof value === 'object') {
26
- keyValuePairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(value))}`)
27
- } else {
28
- keyValuePairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
29
- }
30
- }
31
- }
32
- return keyValuePairs.join('&')
33
- }
34
-
35
- @customElement('ox-board-viewer')
36
- export class BoardViewer extends LitElement {
37
- static styles = [
38
- ScrollbarStyles,
39
- css`
40
- :host {
41
- display: flex;
42
- flex-direction: column;
43
-
44
- position: relative;
45
-
46
- width: 100%; /* 전체화면보기를 위해서 필요함. */
47
- overflow: hidden;
48
- }
49
-
50
- #target {
51
- flex: 1;
52
-
53
- width: 100%; /* 전체화면보기를 위해서 필요함. */
54
- height: 100%;
55
-
56
- outline: 0;
57
- }
58
-
59
- ::slotted(ox-board-component-info) {
60
- position: absolute;
61
- right: 10px;
62
- top: 10px;
63
- width: 240px;
64
- opacity: 0.9;
65
- box-shadow: 2px 2px 4px gray;
66
- max-height: 600px;
67
- overflow: auto;
68
- }
69
-
70
- /* navigation buttons */
71
- #prev,
72
- #next {
73
- z-index: 10;
74
- position: absolute;
75
- top: 45%;
76
- min-width: 50px;
77
- width: 50px;
78
- height: 50px;
79
- margin: 0;
80
- padding: 0;
81
- color: #fff;
82
- user-select: none;
83
-
84
- --md-icon-size: 3em;
85
- }
86
-
87
- md-icon[hidden] {
88
- display: none;
89
- }
90
-
91
- md-icon:hover {
92
- background-color: rgba(0, 0, 0, 0.2);
93
- border-radius: 50%;
94
- }
95
-
96
- #prev {
97
- left: 5px;
98
- }
99
-
100
- #next {
101
- right: 5px;
102
- }
103
-
104
- #fullscreen {
105
- position: absolute;
106
- bottom: 15px;
107
- right: 16px;
108
- }
109
-
110
- [hidden] {
111
- display: none;
112
- }
113
-
114
- @media print {
115
- md-fab,
116
- md-icon {
117
- display: none;
118
- }
119
- }
120
- `
121
- ]
122
-
123
- @property({ type: String }) baseUrl: string = ''
124
- @property({ type: Object }) board: any = {}
125
- @property({ type: Object }) provider: ReferenceProvider | null = null
126
- @property({ type: Object }) data: any
127
- @property({ type: Object }) values: any
128
-
129
- @property({ type: Boolean, reflect: true, attribute: 'hide-fullscreen' }) hideFullscreen = false
130
- @property({ type: Boolean, reflect: true, attribute: 'hide-navigation' }) hideNavigation = false
131
-
132
- @state() _scene: any = null
133
- @state() _forward: { id: string; scene: any }[] = []
134
- @state() _backward: { id: string; scene: any }[] = []
135
-
136
- @state() _oldtarget?: HTMLElement
137
- @state() _fade_animations?: Array<Animation>
138
-
139
- public currentBoardId?: string = this.board?.id
140
- private popup?: BoardComponentInfo
141
-
142
- @query('#target') _target!: HTMLElement
143
- @query('#prev') _prev!: HTMLElement
144
- @query('#next') _next!: HTMLElement
145
- @query('#fullscreen') _fullscreen!: HTMLElement
146
-
147
- render() {
148
- var fullscreen =
149
- !isIOS() && !this.hideFullscreen
150
- ? html`
151
- <md-fab
152
- id="fullscreen"
153
- @click=${(e: Event) => this.onTapFullscreen()}
154
- @mouseover=${(e: Event) => this.transientShowButtons(true)}
155
- @mouseout=${(e: Event) => this.transientShowButtons()}
156
- title="fullscreen"
157
- >
158
- <md-icon slot="icon">${document.fullscreenElement ? 'fullscreen_exit' : 'fullscreen'}</md-icon>
159
- </md-fab>
160
- `
161
- : html``
162
-
163
- var prev = !this.hideNavigation
164
- ? html`
165
- <md-icon
166
- id="prev"
167
- @click=${(e: Event) => this.onTapPrev()}
168
- @mouseover=${(e: Event) => this.transientShowButtons(true)}
169
- @mouseout=${(e: Event) => this.transientShowButtons()}
170
- hidden
171
- >keyboard_arrow_left</md-icon
172
- >
173
- `
174
- : html``
175
-
176
- var next = !this.hideNavigation
177
- ? html`
178
- <md-icon
179
- id="next"
180
- @click=${(e: Event) => this.onTapNext()}
181
- @mouseover=${(e: Event) => this.transientShowButtons(true)}
182
- @mouseout=${(e: Event) => this.transientShowButtons()}
183
- hidden
184
- >keyboard_arrow_right</md-icon
185
- >
186
- `
187
- : html``
188
-
189
- return html`
190
- ${prev}
191
-
192
- <div
193
- id="target"
194
- @touchstart=${(e: Event) => this.transientShowButtons()}
195
- @mousemove=${(e: Event) => this.transientShowButtons()}
196
- ></div>
197
-
198
- <slot></slot>
199
- ${next} ${fullscreen}
200
- `
201
- }
202
-
203
- private resizeHandler = () => {
204
- this._scene && this._scene.fit()
205
- }
206
-
207
- connectedCallback() {
208
- super.connectedCallback()
209
-
210
- window.addEventListener('resize', this.resizeHandler)
211
-
212
- window.addEventListener('orientationchange', this.resizeHandler)
213
-
214
- this.renderRoot.addEventListener(
215
- 'close-scene',
216
- e => {
217
- e.preventDefault()
218
- this.onTapPrev()
219
- },
220
- false
221
- )
222
- }
223
-
224
- disconnectedCallback() {
225
- window.removeEventListener('resize', this.resizeHandler)
226
- window.removeEventListener('orientationchange', this.resizeHandler)
227
-
228
- super.disconnectedCallback()
229
-
230
- this.closeScene()
231
- }
232
-
233
- updated(changes: PropertyValues<this>) {
234
- if (changes.has('board')) {
235
- this.hidePopup()
236
- this.closeScene()
237
-
238
- if (this.board && this.board.id) {
239
- if (this.board.model) {
240
- this.initScene()
241
- } else {
242
- this.initSceneAsync()
243
- }
244
- }
245
- }
246
-
247
- if (changes.has('data') && this._scene && this.data) {
248
- this._scene.data = this.data
249
- }
250
-
251
- if (changes.has('values') && this._scene && this.values) {
252
- this._scene.values = this.values
253
- }
254
- }
255
-
256
- async initSceneAsync() {
257
- if (!this.board || !this.board.id) return
258
-
259
- this._scene = await this.provider!.create(this.board.id)
260
- this.setupScene({ id: this.board.id, scene: this._scene })
261
- }
262
-
263
- initScene() {
264
- if (!this.board || !this.board.id) return
265
-
266
- this._scene = create({
267
- model: {
268
- ...this.board.model
269
- },
270
- mode: SCENE_MODE.VIEW,
271
- refProvider: this.provider!,
272
- dataStorage: this.board.id !== 'preview' ? new BoardDataStorage(this.board.id) : undefined,
273
- dataSubscriptionProvider: new DataSubscriptionProviderImpl()
274
- })
275
-
276
- if (this.baseUrl) {
277
- this._scene.baseUrl = this.baseUrl
278
- }
279
-
280
- // this.provider!.add(this.board.id, this._scene)
281
-
282
- this.setupScene({ id: this.board.id, scene: this._scene })
283
- }
284
-
285
- closeScene() {
286
- if (this._scene) {
287
- this.unbindSceneEvents(this._scene)
288
-
289
- this._scene.target = null
290
- this._scene.release && this._scene.release()
291
-
292
- this._scene = null
293
- }
294
-
295
- // delete queued scenes
296
- this._forward.forEach(({ scene }) => scene.release && scene.release())
297
- this._forward = []
298
-
299
- this._backward.forEach(({ scene }) => scene.release && scene.release())
300
- this._backward = []
301
- }
302
-
303
- releaseScene() {
304
- this.closeScene()
305
- this.transientShowButtons()
306
- }
307
-
308
- setupScene({ id, scene }: { id: string; scene: any }) {
309
- this._scene = scene
310
-
311
- const backgroundColor = this._scene?.root.state.fillStyle
312
- if (typeof backgroundColor === 'string') {
313
- this.style.backgroundColor = backgroundColor
314
- } else {
315
- this.style.backgroundColor = 'initial'
316
- }
317
-
318
- /* scene의 기존 target을 보관한다. */
319
- this._oldtarget = this._scene.target
320
-
321
- this._scene.fit(this._scene.fitMode)
322
- this._scene.target = this._target
323
-
324
- if (this.data) {
325
- this._scene.data = this.data
326
- }
327
- if (this.values) {
328
- this._scene.values = this.values
329
- }
330
- this.currentBoardId = id
331
-
332
- this.bindSceneEvents()
333
-
334
- this.transientShowButtons()
335
- }
336
-
337
- async showScene(boardId: string, bindingData?: any) {
338
- if (!boardId) return
339
-
340
- try {
341
- var scene = await this.provider!.get(boardId, true)
342
-
343
- if (scene === this._scene) {
344
- scene.release && scene.release()
345
- return
346
- }
347
-
348
- if (this._scene) {
349
- /* old scene을 _backward에 보관한다. */
350
- this.unbindSceneEvents(this._scene)
351
- /* 원래의 target에 되돌린다. */
352
- this._scene.target = this._oldtarget
353
- this._backward.push({ id: this.currentBoardId!, scene: this._scene })
354
- }
355
-
356
- this._scene = scene
357
-
358
- this._forward.forEach(({ scene }) => scene.release && scene.release())
359
-
360
- /* forward를 비운다. */
361
- this._forward = []
362
-
363
- this.setupScene({ id: boardId, scene })
364
-
365
- if (bindingData) {
366
- scene.data = bindingData
367
- }
368
- } catch (e) {
369
- console.error(e)
370
- }
371
- }
372
-
373
- async gotoPlayer(playGroupName: string, bindingData?: any) {
374
- const id = (await fetchPlayGroupByName(playGroupName))?.id
375
- const queryString = bindingData ? objectToQueryString(bindingData) : ''
376
-
377
- if (document.querySelector('things-app')) {
378
- history.pushState({}, '', `board-player/${id}?${queryString}`)
379
- window.dispatchEvent(new Event('popstate'))
380
- } else {
381
- location.href = `headless-player/${id}?${queryString}`
382
- }
383
- }
384
-
385
- bindSceneEvents() {
386
- this._scene.on('run', this.onRunBoard, this)
387
- this._scene.on('goto', this.onLinkGoto, this)
388
- this._scene.on('goto-playlist', this.onLinkGotoPlaylist, this)
389
- this._scene.on('link-open', this.onLinkOpen, this)
390
- this._scene.on('link-move', this.onLinkMove, this)
391
- this._scene.on('route-page', this.onRoutePage, this)
392
- this._scene.on('start-scenario', this.onStartScenario, this)
393
- this._scene.on('run-scenario', this.onRunScenario, this)
394
- this._scene.on('export-data', this.onExportData, this)
395
- this._scene.on('import-data', this.onImportData, this)
396
- this._scene.on('click', this.onClickEvent, this)
397
- }
398
-
399
- unbindSceneEvents(scene: any) {
400
- scene.off('run', this.onRunBoard, this)
401
- scene.off('goto', this.onLinkGoto, this)
402
- scene.off('goto-playlist', this.onLinkGotoPlaylist, this)
403
- scene.off('link-open', this.onLinkOpen, this)
404
- scene.off('link-move', this.onLinkMove, this)
405
- scene.off('route-page', this.onRoutePage, this)
406
- scene.off('start-scenario', this.onStartScenario, this)
407
- scene.off('run-scenario', this.onRunScenario, this)
408
- scene.off('export-data', this.onExportData, this)
409
- scene.off('import-data', this.onImportData, this)
410
- scene.off('click', this.onClickEvent, this)
411
- }
412
-
413
- transientShowButtons(stop?: boolean) {
414
- var buttons = []
415
- !this.hideNavigation && buttons.push(this._next, this._prev)
416
- !this.hideFullscreen && buttons.push(this._fullscreen)
417
-
418
- if (buttons.length == 0) {
419
- return
420
- }
421
-
422
- if (!this._fade_animations) {
423
- this._fade_animations = buttons
424
- .filter(button => button)
425
- .map(button => {
426
- let animation = button.animate(
427
- [
428
- {
429
- opacity: 1,
430
- easing: 'ease-in'
431
- },
432
- { opacity: 0 }
433
- ],
434
- { delay: 1000, duration: 2000 }
435
- )
436
-
437
- animation.onfinish = () => {
438
- button.setAttribute('hidden', '')
439
- }
440
-
441
- return animation
442
- })
443
- }
444
-
445
- this._forward.length <= 0 ? this._next.setAttribute('hidden', '') : this._next.removeAttribute('hidden')
446
- this._backward.length <= 0 ? this._prev.setAttribute('hidden', '') : this._prev.removeAttribute('hidden')
447
- this._fullscreen && this._fullscreen.removeAttribute('hidden')
448
-
449
- this._fade_animations.forEach(animation => {
450
- animation.cancel()
451
- if (stop) return
452
-
453
- animation.play()
454
- })
455
- }
456
-
457
- /* event handlers */
458
-
459
- onTapNext() {
460
- var { id, scene } = this._forward.pop() || {}
461
- if (!scene) return
462
-
463
- if (this._scene) {
464
- this._scene.target = null
465
- /* 원래의 target에 되돌린다. */
466
- this._scene.target = this._oldtarget
467
- this.unbindSceneEvents(this._scene)
468
- this._backward.push({ id: id!, scene: this._scene })
469
- }
470
-
471
- this.setupScene({ id: id!, scene })
472
- }
473
-
474
- onTapPrev() {
475
- var { id, scene } = this._backward.pop() || {}
476
- if (!scene) return
477
-
478
- if (this._scene) {
479
- this._scene.target = null
480
- /* 원래의 target에 되돌린다. */
481
- this._scene.target = this._oldtarget
482
- this.unbindSceneEvents(this._scene)
483
- this._forward.push({ id: id!, scene: this._scene })
484
- }
485
-
486
- this.setupScene({ id: id!, scene })
487
- }
488
-
489
- onTapFullscreen() {
490
- togglefullscreen(
491
- this,
492
- () => this.requestUpdate(),
493
- () => this.requestUpdate()
494
- )
495
- }
496
-
497
- onRunBoard() {
498
- this.dispatchEvent(new CustomEvent('run-board', { bubbles: true, composed: true, detail: this.board.id }))
499
- }
500
-
501
- onLinkGoto(targetBoardId: string, options: any, fromComponent: any) {
502
- const { input, output } = options || { input: '(self)' }
503
- const data = input && fromComponent.root.findFirst(input, fromComponent)?.data
504
- this.showScene(targetBoardId, data)
505
- }
506
-
507
- onLinkGotoPlaylist(targetPlayGroupName: string, options: any, fromComponent: any) {
508
- const { input, output } = options || { input: '(self)' }
509
- const data = input && fromComponent.root.findFirst(input, fromComponent)?.data
510
- this.gotoPlayer(targetPlayGroupName, data)
511
- }
512
-
513
- onLinkOpen(url: string, value: any, fromComponent: Component) {
514
- if (!url) return
515
-
516
- try {
517
- window.open(url)
518
- } catch (ex) {
519
- document.dispatchEvent(
520
- new CustomEvent('notify', {
521
- detail: {
522
- level: 'error',
523
- message: ex,
524
- ex
525
- }
526
- })
527
- )
528
- }
529
- }
530
-
531
- onLinkMove(url: string, value: any, fromComponent: Component) {
532
- if (!url) {
533
- return
534
- }
535
-
536
- location.href = url
537
- }
538
-
539
- onRoutePage(page: string) {
540
- if (!page) {
541
- return
542
- }
543
-
544
- history.pushState({}, '', page)
545
- window.dispatchEvent(new Event('popstate'))
546
- }
547
-
548
- async onStartScenario(scenario: string, value: string | number | object, component: Component) {
549
- try {
550
- await startScenario(scenario, scenario, value)
551
- } catch (e) {
552
- console.error(e)
553
- }
554
- }
555
-
556
- async onRunScenario(scenario: string, value: string | number | object, component: Component) {
557
- try {
558
- component.data = await runScenario(scenario, value)
559
- } catch (e) {
560
- console.error(e)
561
- }
562
- }
563
-
564
- async onExportData(filename: string, value: string | number | object, component: Component) {
565
- try {
566
- const data = component.data
567
-
568
- const worksheet = XLSX.utils.json_to_sheet(data)
569
- const workbook = XLSX.utils.book_new()
570
- XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
571
- XLSX.writeFile(workbook, filename || 'export-data.xlsx')
572
- } catch (e) {
573
- console.error(e)
574
- }
575
- }
576
-
577
- async onImportData(_: string, value: string | number | object, component: Component) {
578
- const fileInput = document.createElement('input')
579
- fileInput.type = 'file'
580
- fileInput.accept = '.xlsx, .xls'
581
- fileInput.style.display = 'none'
582
-
583
- fileInput.addEventListener(
584
- 'change',
585
- (event: Event) => {
586
- const input = event.target as HTMLInputElement
587
- const file = input.files?.[0]
588
- if (!file) {
589
- return
590
- }
591
-
592
- const reader = new FileReader()
593
- reader.onload = function (event) {
594
- const data = new Uint8Array(event.target?.result as ArrayBuffer)
595
- const workbook = XLSX.read(data, { type: 'array' })
596
-
597
- // 첫 번째 시트의 데이타만 가져온다.
598
- const firstSheetName = workbook.SheetNames[0]
599
- const worksheet = workbook.Sheets[firstSheetName]
600
- component.data = XLSX.utils.sheet_to_json(worksheet)
601
-
602
- input.remove()
603
- }
604
- reader.readAsArrayBuffer(file)
605
- },
606
- false
607
- )
608
-
609
- document.body.appendChild(fileInput)
610
- fileInput.click()
611
- }
612
-
613
- onClickEvent(e: MouseEvent, hint: any) {
614
- const component = hint.origin
615
-
616
- if (!this.popup && e.altKey) {
617
- const popup = document.createElement('ox-board-component-info') as BoardComponentInfo
618
- popup.addEventListener('close', e => {
619
- e.stopPropagation()
620
- this.hidePopup()
621
- })
622
-
623
- this.appendChild(popup)
624
- this.popup = popup
625
- }
626
-
627
- if (this.popup) {
628
- this.popup.component = component
629
- }
630
- }
631
-
632
- hidePopup() {
633
- if (this.popup) {
634
- this.removeChild(this.popup)
635
- delete this.popup
636
- }
637
- }
638
-
639
- getSceneData() {
640
- return this._scene?.data
641
- }
642
-
643
- getSceneValues() {
644
- return this._scene?.values
645
- }
646
-
647
- async getSceneImageData(base64 = false) {
648
- if (!this._scene) {
649
- return
650
- }
651
-
652
- var { width, height } = this._scene.model
653
- var pixelRatio = window.devicePixelRatio
654
-
655
- // 1. Scene의 바운드에 근거하여, 오프스크린 캔바스를 만든다.
656
- var canvas = document.createElement('canvas')
657
- canvas.width = Number(width)
658
- canvas.height = Number(height)
659
-
660
- var root = this._scene.root
661
- // 2. 모델레이어의 원래 위치와 스케일을 저장한다.
662
- var translate = root.get('translate')
663
- var scale = root.get('scale')
664
-
665
- // 3. 위치와 스케일 기본 설정.
666
- root.set('translate', { x: 0, y: 0 })
667
- root.set('scale', { x: 1 / pixelRatio, y: 1 / pixelRatio })
668
-
669
- // 4. 오프스크린 캔바스의 Context2D를 구한뒤, 모델레이어를 그 위에 그린다.
670
- var context = canvas.getContext('2d')
671
-
672
- root.draw(context)
673
-
674
- root.set('translate', translate)
675
- root.set('scale', scale)
676
-
677
- var data = base64 ? canvas.toDataURL() : context!.getImageData(0, 0, width, height).data
678
-
679
- return {
680
- width,
681
- height,
682
- data
683
- }
684
- }
685
-
686
- async printTrick(image: string) {
687
- var viewTarget: HTMLElement | null = null
688
- var printTarget: HTMLImageElement | null = null
689
-
690
- if (!image) {
691
- image = (await this.getSceneImageData(true))?.data as string
692
- }
693
-
694
- printTarget = document.createElement('img')
695
- printTarget.id = 'target'
696
- printTarget.src = image
697
- printTarget.style.width = '100%'
698
- printTarget.style.height = '100%'
699
-
700
- const x = (mql: MediaQueryListEvent) => {
701
- if (mql.matches) {
702
- if (!viewTarget) {
703
- viewTarget = (this.renderRoot as ShadowRoot)!.getElementById('target')
704
- this.renderRoot.replaceChild(printTarget!, viewTarget!)
705
- }
706
- } else {
707
- this.renderRoot.replaceChild(viewTarget!, printTarget!)
708
- printTarget!.remove()
709
- mediaQueryList.removeEventListener('change', x)
710
- }
711
- }
712
-
713
- if (window.matchMedia) {
714
- var mediaQueryList = window.matchMedia('print')
715
- mediaQueryList.addEventListener('change', x)
716
- }
717
- }
718
- }