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