@fmsim/board 2.0.0-beta.7 → 2.0.0-beta.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +4024 -2059
- package/demo/index-modeller.html +1 -1
- package/demo/index-player.html +2 -2
- package/demo/index-viewer.html +2 -2
- package/demo/index.html +2 -2
- package/dist/src/component/etc.js +35 -0
- package/dist/src/component/etc.js.map +1 -1
- package/dist/src/component/index.d.ts +1 -0
- package/dist/src/component/index.js +1 -0
- package/dist/src/component/index.js.map +1 -1
- package/dist/src/component/restful-input-attachment-selector.d.ts +1 -1
- package/dist/src/component/restful-property-editor-backdrop-faces.d.ts +50 -0
- package/dist/src/component/restful-property-editor-backdrop-faces.js +524 -0
- package/dist/src/component/restful-property-editor-backdrop-faces.js.map +1 -0
- package/dist/src/data-storage/board-model-cache.d.ts +13 -0
- package/dist/src/data-storage/board-model-cache.js +66 -0
- package/dist/src/data-storage/board-model-cache.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/layers/movement-layer.d.ts +1 -1
- package/dist/src/layers/movement-layer.js +3 -7
- package/dist/src/layers/movement-layer.js.map +1 -1
- package/dist/src/modeller/component-toolbar/component-menu.d.ts +1 -1
- package/dist/src/modeller/component-toolbar/component-toolbar.d.ts +1 -1
- package/dist/src/modeller/edit-toolbar-style.js +43 -3
- package/dist/src/modeller/edit-toolbar-style.js.map +1 -1
- package/dist/src/modeller/edit-toolbar.d.ts +8 -14
- package/dist/src/modeller/edit-toolbar.js +208 -211
- package/dist/src/modeller/edit-toolbar.js.map +1 -1
- package/dist/src/modeller/property-sidebar/abstract-property.js +10 -2
- package/dist/src/modeller/property-sidebar/abstract-property.js.map +1 -1
- package/dist/src/modeller/property-sidebar/data-binding/data-binding.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/effects/effects.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/effects/property-event-hover.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/effects/property-event.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/inspector/inspector.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/property-sidebar.d.ts +1 -1
- package/dist/src/modeller/property-sidebar/shapes/shapes.js +31 -0
- package/dist/src/modeller/property-sidebar/shapes/shapes.js.map +1 -1
- package/dist/src/modeller/property-sidebar/specifics/specifics.d.ts +2 -1
- package/dist/src/modeller/property-sidebar/specifics/specifics.js +10 -0
- package/dist/src/modeller/property-sidebar/specifics/specifics.js.map +1 -1
- package/dist/src/modeller/property-sidebar/styles/styles.d.ts +1 -1
- package/dist/src/modeller/scene-viewer/ox-scene-layer.d.ts +4 -3
- package/dist/src/modeller/scene-viewer/ox-scene-layer.js +2 -1
- package/dist/src/modeller/scene-viewer/ox-scene-layer.js.map +1 -1
- package/dist/src/modeller/scene-viewer/ox-scene-viewer.d.ts +2 -1
- package/dist/src/modeller/scene-viewer/ox-scene-viewer.js +5 -1
- package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
- package/dist/src/ox-board-component-info.d.ts +1 -0
- package/dist/src/ox-board-component-info.js +5 -1
- package/dist/src/ox-board-component-info.js.map +1 -1
- package/dist/src/ox-board-list.js +2 -2
- package/dist/src/ox-board-list.js.map +1 -1
- package/dist/src/ox-board-modeller.d.ts +6 -3
- package/dist/src/ox-board-modeller.js +14 -4
- package/dist/src/ox-board-modeller.js.map +1 -1
- package/dist/src/ox-board-viewer.d.ts +60 -13
- package/dist/src/ox-board-viewer.js +572 -86
- package/dist/src/ox-board-viewer.js.map +1 -1
- package/dist/src/ox-editor-board-selector.d.ts +3 -0
- package/dist/src/ox-editor-board-selector.js +38 -9
- package/dist/src/ox-editor-board-selector.js.map +1 -1
- package/dist/src/ox-playback-controls.d.ts +154 -0
- package/dist/src/ox-playback-controls.js +947 -0
- package/dist/src/ox-playback-controls.js.map +1 -0
- package/dist/src/playback/playback-engine.d.ts +106 -0
- package/dist/src/playback/playback-engine.js +395 -0
- package/dist/src/playback/playback-engine.js.map +1 -0
- package/dist/src/selector/board-creation-popup.js +2 -2
- package/dist/src/selector/board-creation-popup.js.map +1 -1
- package/dist/src/selector/board-thumbnail-card.js +6 -6
- package/dist/src/selector/board-thumbnail-card.js.map +1 -1
- package/dist/src/selector/ox-board-creation-card.js +2 -2
- package/dist/src/selector/ox-board-creation-card.js.map +1 -1
- package/dist/src/selector/ox-board-selector.js +21 -10
- package/dist/src/selector/ox-board-selector.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/icons/components/text.png +0 -0
- package/icons/icon-htoolbar.png +0 -0
- package/package.json +8 -4
|
@@ -4,10 +4,12 @@ import '@material/web/icon/icon.js';
|
|
|
4
4
|
import './layers/movement-layer.js';
|
|
5
5
|
import './ox-board-component-info.js';
|
|
6
6
|
import './layers/mcs-mouse-event-handler.js';
|
|
7
|
-
import
|
|
7
|
+
import './ox-playback-controls.js';
|
|
8
|
+
import { css, html, LitElement, nothing } from 'lit';
|
|
8
9
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { PlaybackEngine } from './playback/playback-engine.js';
|
|
11
|
+
import { create, LoadTracker, LoadingOverlay, SCENE_MODE } from '@hatiolab/things-scene';
|
|
12
|
+
import { isIOS, fullscreen, exitfullscreen, togglefullscreen } from '@operato/utils';
|
|
11
13
|
import { ScrollbarStyles } from '@operato/styles';
|
|
12
14
|
import { DataStorage } from './data-storage/data-storage.js';
|
|
13
15
|
import { runScenario, startScenario, DataSubscriptionProviderImpl, isRestfulAvailable } from '@fmsim/api';
|
|
@@ -24,6 +26,8 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
24
26
|
this._forward = [];
|
|
25
27
|
this._backward = [];
|
|
26
28
|
this._oldtarget = null;
|
|
29
|
+
this._fabExpanded = false;
|
|
30
|
+
this._playbackActive = false;
|
|
27
31
|
this.currentBoardId = (_a = this.board) === null || _a === void 0 ? void 0 : _a.id;
|
|
28
32
|
this.popup = null;
|
|
29
33
|
this.lastComponent = null;
|
|
@@ -33,7 +37,6 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
33
37
|
const id = e.detail.value;
|
|
34
38
|
const components = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.findAll(`#${id}`);
|
|
35
39
|
if (!components || components.length == 0) {
|
|
36
|
-
console.log('No equipment was found.');
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
window.dispatchEvent(new CustomEvent('clickComponent', { detail: components[0] }));
|
|
@@ -173,22 +176,189 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
173
176
|
var _a;
|
|
174
177
|
(_a = this._scene) === null || _a === void 0 ? void 0 : _a.root.trigger('deco-reset');
|
|
175
178
|
};
|
|
179
|
+
this._pointerMoveHandler = () => this.transientShowButtons();
|
|
180
|
+
/**
|
|
181
|
+
* playback 모드는 전체화면 모드와 한 몸 — 어떤 경로로든 전체화면이 해제되면
|
|
182
|
+
* playback 도 함께 종료. (단독 전체화면 토글은 기존처럼 동작하도록 `_playbackActive` 가드.)
|
|
183
|
+
*/
|
|
184
|
+
this._fullscreenChangeHandler = () => {
|
|
185
|
+
if (!document.fullscreenElement && this._playbackActive) {
|
|
186
|
+
this._onPlaybackStop();
|
|
187
|
+
}
|
|
188
|
+
};
|
|
176
189
|
this.resizeHandler = () => {
|
|
177
190
|
this._scene && this._scene.fit();
|
|
178
191
|
};
|
|
192
|
+
// ── Data Subscription ──
|
|
193
|
+
this._dataSubscriptionProvider = isRestfulAvailable() ? undefined : new DataSubscriptionProviderImpl();
|
|
194
|
+
}
|
|
195
|
+
get _is3dMode() {
|
|
196
|
+
var _a, _b, _c;
|
|
197
|
+
return !!((_c = (_b = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.rootModel) === null || _c === void 0 ? void 0 : _c.is3dMode);
|
|
179
198
|
}
|
|
180
199
|
render() {
|
|
181
|
-
var
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
200
|
+
var _a, _b;
|
|
201
|
+
var fabGroup = this.hideFullscreen
|
|
202
|
+
? html ``
|
|
203
|
+
: html `
|
|
204
|
+
<div
|
|
205
|
+
fab-group
|
|
206
|
+
?expanded=${this._fabExpanded}
|
|
186
207
|
@mouseover=${(e) => this.transientShowButtons(true)}
|
|
187
208
|
@mouseout=${(e) => this.transientShowButtons()}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
209
|
+
>
|
|
210
|
+
<md-fab
|
|
211
|
+
class="main-fab"
|
|
212
|
+
?expanded=${this._fabExpanded}
|
|
213
|
+
@click=${() => { this._fabExpanded = !this._fabExpanded; }}
|
|
214
|
+
title="menu"
|
|
215
|
+
><md-icon slot="icon">add</md-icon></md-fab>
|
|
216
|
+
|
|
217
|
+
${isIOS() ? nothing : html `<md-fab
|
|
218
|
+
class="sub-fab"
|
|
219
|
+
@click=${() => { this._fabExpanded = false; this.onTapFullscreen(); }}
|
|
220
|
+
title="fullscreen"
|
|
221
|
+
><md-icon slot="icon">${document.fullscreenElement ? 'fullscreen_exit' : 'fullscreen'}</md-icon></md-fab>`}
|
|
222
|
+
|
|
223
|
+
<md-fab
|
|
224
|
+
class="sub-fab"
|
|
225
|
+
@click=${() => { this._fabExpanded = false; this.onTapPlayback(); }}
|
|
226
|
+
title=${this._playbackActive ? 'stop playback' : 'start playback'}
|
|
227
|
+
>${this._playbackActive
|
|
228
|
+
? html `<svg slot="icon" width="48" height="48" viewBox="0 0 100 100" style="display: block;">
|
|
229
|
+
<circle cx="50" cy="50" r="50" fill="#F9EBEA" />
|
|
230
|
+
<g transform="translate(-5, 0)">
|
|
231
|
+
<path
|
|
232
|
+
d="M 52,22 A 28,28 0 1,1 30,40"
|
|
233
|
+
fill="none"
|
|
234
|
+
stroke="#F87171"
|
|
235
|
+
stroke-width="5"
|
|
236
|
+
stroke-linecap="round"
|
|
237
|
+
/>
|
|
238
|
+
<path d="M 52,12 L 37,24 L 55,33 Z" fill="#F87171" />
|
|
239
|
+
</g>
|
|
240
|
+
<rect
|
|
241
|
+
x="36"
|
|
242
|
+
y="36"
|
|
243
|
+
width="28"
|
|
244
|
+
height="28"
|
|
245
|
+
rx="3"
|
|
246
|
+
fill="#F87171"
|
|
247
|
+
stroke="#F87171"
|
|
248
|
+
stroke-width="3"
|
|
249
|
+
stroke-linejoin="round"
|
|
250
|
+
/>
|
|
251
|
+
<text
|
|
252
|
+
x="50"
|
|
253
|
+
y="53.5"
|
|
254
|
+
font-family="Verdana, sans-serif"
|
|
255
|
+
font-size="9"
|
|
256
|
+
font-weight="900"
|
|
257
|
+
fill="#ffffff"
|
|
258
|
+
text-anchor="middle"
|
|
259
|
+
>OFF</text>
|
|
260
|
+
</svg>`
|
|
261
|
+
: html `<svg slot="icon" width="48" height="48" viewBox="0 0 100 100" style="display: block;">
|
|
262
|
+
<circle cx="50" cy="50" r="50" fill="#F9EBEA" />
|
|
263
|
+
<g transform="translate(-5, 0)">
|
|
264
|
+
<path
|
|
265
|
+
d="M 52,22 A 28,28 0 1,1 30,40"
|
|
266
|
+
fill="none"
|
|
267
|
+
stroke="#F87171"
|
|
268
|
+
stroke-width="5"
|
|
269
|
+
stroke-linecap="round"
|
|
270
|
+
/>
|
|
271
|
+
<path d="M 52,12 L 37,24 L 55,33 Z" fill="#F87171" />
|
|
272
|
+
</g>
|
|
273
|
+
<path
|
|
274
|
+
d="M 42,35 L 65,50 L 42,65 Z"
|
|
275
|
+
fill="#F87171"
|
|
276
|
+
stroke="#F87171"
|
|
277
|
+
stroke-width="3"
|
|
278
|
+
stroke-linejoin="round"
|
|
279
|
+
/>
|
|
280
|
+
<text
|
|
281
|
+
x="49.5"
|
|
282
|
+
y="53"
|
|
283
|
+
font-family="Verdana, sans-serif"
|
|
284
|
+
font-size="9"
|
|
285
|
+
font-weight="900"
|
|
286
|
+
fill="#ffffff"
|
|
287
|
+
text-anchor="middle"
|
|
288
|
+
>RE:</text>
|
|
289
|
+
</svg>`}</md-fab>
|
|
290
|
+
|
|
291
|
+
<md-fab
|
|
292
|
+
class="sub-fab"
|
|
293
|
+
@click=${() => { this._fabExpanded = false; this.onTapToggle3D(); }}
|
|
294
|
+
title=${this._is3dMode ? '2D mode' : '3D mode'}
|
|
295
|
+
>${this._is3dMode
|
|
296
|
+
? html `<svg slot="icon" width="48" height="48" viewBox="0 0 100 100" style="display: block;">
|
|
297
|
+
<circle cx="50" cy="50" r="50" fill="#F9EBEA" />
|
|
298
|
+
<g transform="rotate(55, 50, 50)">
|
|
299
|
+
<path
|
|
300
|
+
d="M 72,32 A 30,30 0 0,0 38,28"
|
|
301
|
+
fill="none"
|
|
302
|
+
stroke="#F87171"
|
|
303
|
+
stroke-width="5"
|
|
304
|
+
stroke-linecap="round"
|
|
305
|
+
/>
|
|
306
|
+
<path d="M 38,20 L 22,30 L 38,40 Z" fill="#F87171" />
|
|
307
|
+
</g>
|
|
308
|
+
<g transform="rotate(235, 50, 50)">
|
|
309
|
+
<path
|
|
310
|
+
d="M 72,32 A 30,30 0 0,0 38,28"
|
|
311
|
+
fill="none"
|
|
312
|
+
stroke="#F87171"
|
|
313
|
+
stroke-width="5"
|
|
314
|
+
stroke-linecap="round"
|
|
315
|
+
/>
|
|
316
|
+
<path d="M 38,20 L 22,30 L 38,40 Z" fill="#F87171" />
|
|
317
|
+
</g>
|
|
318
|
+
<text
|
|
319
|
+
x="50"
|
|
320
|
+
y="57.5"
|
|
321
|
+
font-family="Verdana, sans-serif"
|
|
322
|
+
font-size="20"
|
|
323
|
+
font-weight="900"
|
|
324
|
+
fill="#F87171"
|
|
325
|
+
text-anchor="middle"
|
|
326
|
+
>2D</text>
|
|
327
|
+
</svg>`
|
|
328
|
+
: html `<svg slot="icon" width="48" height="48" viewBox="0 0 100 100" style="display: block;">
|
|
329
|
+
<circle cx="50" cy="50" r="50" fill="#F9EBEA" />
|
|
330
|
+
<g transform="rotate(55, 50, 50)">
|
|
331
|
+
<path
|
|
332
|
+
d="M 72,32 A 30,30 0 0,0 38,28"
|
|
333
|
+
fill="none"
|
|
334
|
+
stroke="#F87171"
|
|
335
|
+
stroke-width="5"
|
|
336
|
+
stroke-linecap="round"
|
|
337
|
+
/>
|
|
338
|
+
<path d="M 38,20 L 22,30 L 38,40 Z" fill="#F87171" />
|
|
339
|
+
</g>
|
|
340
|
+
<g transform="rotate(235, 50, 50)">
|
|
341
|
+
<path
|
|
342
|
+
d="M 72,32 A 30,30 0 0,0 38,28"
|
|
343
|
+
fill="none"
|
|
344
|
+
stroke="#F87171"
|
|
345
|
+
stroke-width="5"
|
|
346
|
+
stroke-linecap="round"
|
|
347
|
+
/>
|
|
348
|
+
<path d="M 38,20 L 22,30 L 38,40 Z" fill="#F87171" />
|
|
349
|
+
</g>
|
|
350
|
+
<text
|
|
351
|
+
x="50"
|
|
352
|
+
y="57.5"
|
|
353
|
+
font-family="Verdana, sans-serif"
|
|
354
|
+
font-size="20"
|
|
355
|
+
font-weight="900"
|
|
356
|
+
fill="#F87171"
|
|
357
|
+
text-anchor="middle"
|
|
358
|
+
>3D</text>
|
|
359
|
+
</svg>`}</md-fab>
|
|
360
|
+
</div>
|
|
361
|
+
`;
|
|
192
362
|
var prev = !this.hideNavigation
|
|
193
363
|
? html `
|
|
194
364
|
<md-icon
|
|
@@ -216,74 +386,94 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
216
386
|
return html `
|
|
217
387
|
${prev}
|
|
218
388
|
|
|
219
|
-
<div
|
|
220
|
-
id="target"
|
|
221
|
-
@touchstart=${(e) => this.transientShowButtons()}
|
|
222
|
-
@mousemove=${(e) => this.transientShowButtons()}
|
|
223
|
-
></div>
|
|
389
|
+
<div id="target"></div>
|
|
224
390
|
|
|
225
391
|
<slot></slot>
|
|
226
|
-
${next} ${
|
|
392
|
+
${next} ${fabGroup}
|
|
393
|
+
|
|
394
|
+
${this._playbackActive
|
|
395
|
+
? html `<ox-playback-controls
|
|
396
|
+
.playbackState=${((_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.state) === 'playing' ? 'playing' : ((_b = this._playbackEngine) === null || _b === void 0 ? void 0 : _b.state) === 'paused' ? 'paused' : 'idle'}
|
|
397
|
+
@playback-start=${this._onPlaybackStart}
|
|
398
|
+
@playback-pause=${this._onPlaybackPause}
|
|
399
|
+
@playback-resume=${this._onPlaybackResume}
|
|
400
|
+
@playback-stop=${this._onPlaybackStop}
|
|
401
|
+
@playback-seek=${this._onPlaybackSeek}
|
|
402
|
+
@playback-seek-start=${this._onPlaybackSeekStart}
|
|
403
|
+
@playback-seek-preview=${this._onPlaybackSeekPreview}
|
|
404
|
+
@playback-speed=${this._onPlaybackSpeed}
|
|
405
|
+
></ox-playback-controls>`
|
|
406
|
+
: nothing}
|
|
227
407
|
`;
|
|
228
408
|
}
|
|
409
|
+
/** connect/disconnect 시 일괄 등록/해제할 window 이벤트 목록 (모니터링 연동) */
|
|
410
|
+
get _monitoringEvents() {
|
|
411
|
+
return [
|
|
412
|
+
['fetchIFactsData', this.fetchIFactsDataHandler], // 장비 아이디 object를 캔버스 가운데로
|
|
413
|
+
['fmbShowMovement', this.fmbShowMovementHandler], // Movement Data 표시
|
|
414
|
+
['fmbMonitoringInit', this.fmbMonitoringInitHandler], // 보드 상태 정사이즈 복원
|
|
415
|
+
['clickComponent', this.clickComponentHandler], // BoardComponentInfo popup
|
|
416
|
+
['fmbStartAnimation', this.fmbStartAnimation], // AnimatedIcon 추가/제거
|
|
417
|
+
['fmbStopAnimation', this.fmbStopAnimation],
|
|
418
|
+
['fmbResetAnimation', this.fmbResetAnimation],
|
|
419
|
+
['fmbShowLegend', this.fmbShowLegendHandler], // 범례 표시
|
|
420
|
+
['fmbHideLegend', this.fmbHideLegendHandler],
|
|
421
|
+
['fmbShowPopup', this.fmbShowPopupHandler], // 팝업 표시
|
|
422
|
+
['fmbHidePopup', this.fmbHidePopupHandler],
|
|
423
|
+
['fmbShowFloatingContainer', this.fmbShowFloatingContainerHandler], // FloatingContainer
|
|
424
|
+
['fmbHideFloatingContainer', this.fmbHideFloatingContainerHandler]
|
|
425
|
+
];
|
|
426
|
+
}
|
|
229
427
|
connectedCallback() {
|
|
230
428
|
super.connectedCallback();
|
|
231
429
|
window.addEventListener('resize', this.resizeHandler);
|
|
430
|
+
// 3D 모드에서도 마우스 움직임 감지 (document 레벨, capture phase)
|
|
431
|
+
document.addEventListener('pointermove', this._pointerMoveHandler, true);
|
|
432
|
+
document.addEventListener('fullscreenchange', this._fullscreenChangeHandler);
|
|
433
|
+
document.addEventListener('webkitfullscreenchange', this._fullscreenChangeHandler);
|
|
232
434
|
this.renderRoot.addEventListener('close-scene', e => {
|
|
233
435
|
e.preventDefault();
|
|
234
436
|
this.onTapPrev();
|
|
235
437
|
}, false);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
window.addEventListener('fmbShowMovement', this.fmbShowMovementHandler);
|
|
240
|
-
// 모니터링에서 보드 상태를 원래상태 (정사이즈)로 돌려놓는다.
|
|
241
|
-
window.addEventListener('fmbMonitoringInit', this.fmbMonitoringInitHandler);
|
|
242
|
-
// BoardComponentInfo popup을 보여준다.
|
|
243
|
-
window.addEventListener('clickComponent', this.clickComponentHandler);
|
|
244
|
-
// AnimatedIcon 을 추가하거나 제거한다.
|
|
245
|
-
window.addEventListener('fmbStartAnimation', this.fmbStartAnimation);
|
|
246
|
-
window.addEventListener('fmbStopAnimation', this.fmbStopAnimation);
|
|
247
|
-
window.addEventListener('fmbResetAnimation', this.fmbResetAnimation);
|
|
248
|
-
// 모니터링에서 범례를 보여준다.
|
|
249
|
-
window.addEventListener('fmbShowLegend', this.fmbShowLegendHandler);
|
|
250
|
-
window.addEventListener('fmbHideLegend', this.fmbHideLegendHandler);
|
|
251
|
-
// 모니터링에서 범례를 보여준다.
|
|
252
|
-
window.addEventListener('fmbShowPopup', this.fmbShowPopupHandler);
|
|
253
|
-
window.addEventListener('fmbHidePopup', this.fmbHidePopupHandler);
|
|
254
|
-
// 모니터링에서 FloatingContainer를 보여준다.
|
|
255
|
-
window.addEventListener('fmbShowFloatingContainer', this.fmbShowFloatingContainerHandler);
|
|
256
|
-
window.addEventListener('fmbHideFloatingContainer', this.fmbHideFloatingContainerHandler);
|
|
438
|
+
for (const [type, handler] of this._monitoringEvents) {
|
|
439
|
+
window.addEventListener(type, handler);
|
|
440
|
+
}
|
|
257
441
|
}
|
|
258
442
|
disconnectedCallback() {
|
|
443
|
+
var _a, _b, _c;
|
|
259
444
|
super.disconnectedCallback();
|
|
260
445
|
window.removeEventListener('resize', this.resizeHandler);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// BoardComponentInfo popup을 보여준다.
|
|
268
|
-
window.removeEventListener('clickComponent', this.clickComponentHandler);
|
|
269
|
-
// AnimatedIcon을 추가하거나 제거한다.
|
|
270
|
-
window.removeEventListener('fmbStartAnimation', this.fmbStartAnimation);
|
|
271
|
-
window.removeEventListener('fmbStopAnimation', this.fmbStopAnimation);
|
|
272
|
-
window.removeEventListener('fmbResetAnimation', this.fmbResetAnimation);
|
|
273
|
-
// 모니터링에서 범례를 보여준다.
|
|
274
|
-
window.removeEventListener('fmbShowLegend', this.fmbShowLegendHandler);
|
|
275
|
-
window.removeEventListener('fmbHideLegend', this.fmbHideLegendHandler);
|
|
276
|
-
// 모니터링에서 범례를 보여준다.
|
|
277
|
-
window.removeEventListener('fmbShowPopup', this.fmbShowPopupHandler);
|
|
278
|
-
window.removeEventListener('fmbHidePopup', this.fmbHidePopupHandler);
|
|
279
|
-
// 모니터링에서 FloatingContainer를 보여준다.
|
|
280
|
-
window.removeEventListener('fmbShowFloatingContainer', this.fmbShowFloatingContainerHandler);
|
|
281
|
-
window.removeEventListener('fmbHideFloatingContainer', this.fmbHideFloatingContainerHandler);
|
|
446
|
+
document.removeEventListener('pointermove', this._pointerMoveHandler, true);
|
|
447
|
+
document.removeEventListener('fullscreenchange', this._fullscreenChangeHandler);
|
|
448
|
+
document.removeEventListener('webkitfullscreenchange', this._fullscreenChangeHandler);
|
|
449
|
+
for (const [type, handler] of this._monitoringEvents) {
|
|
450
|
+
window.removeEventListener(type, handler);
|
|
451
|
+
}
|
|
282
452
|
this.lastComponent = null;
|
|
453
|
+
// 플레이백이 활성 상태면 명시적 stop (provider resume 포함)
|
|
454
|
+
if (this._playbackActive) {
|
|
455
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.stop();
|
|
456
|
+
(_b = this._playbackEngine) === null || _b === void 0 ? void 0 : _b.detachScene();
|
|
457
|
+
(_c = this._dataSubscriptionProvider) === null || _c === void 0 ? void 0 : _c.resume();
|
|
458
|
+
this._playbackActive = false;
|
|
459
|
+
}
|
|
460
|
+
if (this._playbackEngine) {
|
|
461
|
+
this._playbackEngine.dispose();
|
|
462
|
+
this._playbackEngine = undefined;
|
|
463
|
+
}
|
|
283
464
|
this.closeScene();
|
|
284
465
|
}
|
|
285
466
|
updated(changes) {
|
|
467
|
+
// loadTracker가 외부에서 전달되면 즉시 overlay 연결 (fetch 중에도 표시)
|
|
468
|
+
if (changes.has('loadTracker') && this.loadTracker) {
|
|
469
|
+
this._ensureOverlay(this.loadTracker);
|
|
470
|
+
}
|
|
286
471
|
if (changes.has('board')) {
|
|
472
|
+
// tracker: 외부에서 안 왔으면 여기서 생성
|
|
473
|
+
if (!this.loadTracker) {
|
|
474
|
+
this.loadTracker = new LoadTracker();
|
|
475
|
+
this._ensureOverlay(this.loadTracker);
|
|
476
|
+
}
|
|
287
477
|
this.hidePopup();
|
|
288
478
|
this.closeScene();
|
|
289
479
|
if (this.board && this.board.id) {
|
|
@@ -303,32 +493,42 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
303
493
|
}
|
|
304
494
|
}
|
|
305
495
|
async initSceneAsync() {
|
|
306
|
-
if (!this.board || !this.board.id)
|
|
496
|
+
if (!this.board || !this.board.id || !this.provider)
|
|
307
497
|
return;
|
|
308
|
-
|
|
309
|
-
|
|
498
|
+
try {
|
|
499
|
+
this._scene = await this.provider.create(this.board.id);
|
|
500
|
+
this.setupScene({ id: this.board.id, scene: this._scene });
|
|
501
|
+
}
|
|
502
|
+
catch (e) {
|
|
503
|
+
console.error('Failed to load scene:', this.board.id, e);
|
|
504
|
+
}
|
|
310
505
|
}
|
|
311
506
|
initScene() {
|
|
312
|
-
if (!this.board || !this.board.id) {
|
|
507
|
+
if (!this.board || !this.board.id || !this.board.model) {
|
|
313
508
|
return;
|
|
314
509
|
}
|
|
315
510
|
this._scene = create({
|
|
316
|
-
model:
|
|
317
|
-
...this.board.model
|
|
318
|
-
},
|
|
511
|
+
model: JSON.parse(JSON.stringify(this.board.model)),
|
|
319
512
|
style: this.board.style,
|
|
320
513
|
mode: SCENE_MODE.VIEW,
|
|
321
514
|
handlers: ['mcs-event-handler', 'zoom-handler', 'shift-handler', 'waypoint-handler'],
|
|
322
515
|
layers: [{ type: 'scroll-layer' }, { type: 'movement-layer', id: 'movement-layer' }],
|
|
323
516
|
refProvider: this.provider,
|
|
324
517
|
dataStorage: this.board.id !== 'preview' ? new DataStorage(this.board.id) : undefined,
|
|
325
|
-
dataSubscriptionProvider:
|
|
518
|
+
dataSubscriptionProvider: this._dataSubscriptionProvider,
|
|
519
|
+
loading: this.loadTracker ? { tracker: this.loadTracker } : undefined
|
|
326
520
|
});
|
|
327
521
|
if (this.baseUrl) {
|
|
328
522
|
this._scene.baseUrl = this.baseUrl;
|
|
329
523
|
}
|
|
330
524
|
this.setupScene({ id: this.board.id, scene: this._scene });
|
|
331
525
|
}
|
|
526
|
+
_ensureOverlay(tracker) {
|
|
527
|
+
if (!this._loadingOverlay) {
|
|
528
|
+
this._loadingOverlay = new LoadingOverlay();
|
|
529
|
+
}
|
|
530
|
+
this._loadingOverlay.attach(this.renderRoot, tracker);
|
|
531
|
+
}
|
|
332
532
|
closeScene() {
|
|
333
533
|
if (this._scene) {
|
|
334
534
|
this.unbindSceneEvents(this._scene);
|
|
@@ -348,6 +548,10 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
348
548
|
}
|
|
349
549
|
setupScene({ id, scene }) {
|
|
350
550
|
var _a;
|
|
551
|
+
// 플레이백 리셋
|
|
552
|
+
if (this._playbackActive) {
|
|
553
|
+
this._onPlaybackStop();
|
|
554
|
+
}
|
|
351
555
|
this._scene = scene;
|
|
352
556
|
const backgroundColor = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root.state.fillStyle;
|
|
353
557
|
if (typeof backgroundColor === 'string') {
|
|
@@ -371,7 +575,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
371
575
|
this.transientShowButtons();
|
|
372
576
|
}
|
|
373
577
|
async showScene(boardId, bindingData) {
|
|
374
|
-
if (!boardId)
|
|
578
|
+
if (!boardId || !this.provider)
|
|
375
579
|
return;
|
|
376
580
|
try {
|
|
377
581
|
var scene = await this.provider.get(boardId, true);
|
|
@@ -424,7 +628,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
424
628
|
transientShowButtons(stop) {
|
|
425
629
|
var buttons = [];
|
|
426
630
|
!this.hideNavigation && buttons.push(this._next, this._prev);
|
|
427
|
-
!this.hideFullscreen && buttons.push(this.
|
|
631
|
+
!this.hideFullscreen && buttons.push(this._fabGroup);
|
|
428
632
|
if (buttons.length == 0) {
|
|
429
633
|
return;
|
|
430
634
|
}
|
|
@@ -447,7 +651,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
447
651
|
}
|
|
448
652
|
this._forward.length <= 0 ? this._next.setAttribute('hidden', '') : this._next.removeAttribute('hidden');
|
|
449
653
|
this._backward.length <= 0 ? this._prev.setAttribute('hidden', '') : this._prev.removeAttribute('hidden');
|
|
450
|
-
this.
|
|
654
|
+
this._fabGroup && this._fabGroup.removeAttribute('hidden');
|
|
451
655
|
this._fade_animations.forEach(animation => {
|
|
452
656
|
animation.cancel();
|
|
453
657
|
if (stop)
|
|
@@ -485,6 +689,242 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
485
689
|
onTapFullscreen() {
|
|
486
690
|
togglefullscreen(this, () => this.requestUpdate(), () => this.requestUpdate());
|
|
487
691
|
}
|
|
692
|
+
onTapToggle3D() {
|
|
693
|
+
var _a, _b, _c;
|
|
694
|
+
const modelLayer = (_b = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.rootModel;
|
|
695
|
+
if (!(modelLayer === null || modelLayer === void 0 ? void 0 : modelLayer.toggleThreeD))
|
|
696
|
+
return;
|
|
697
|
+
const entering3D = !modelLayer.is3dMode;
|
|
698
|
+
modelLayer.toggleThreeD();
|
|
699
|
+
this.requestUpdate();
|
|
700
|
+
// fmsim: 3D 진입 시 자동 회전
|
|
701
|
+
if (entering3D && ((_c = modelLayer._threeCapability) === null || _c === void 0 ? void 0 : _c.isInitialized)) {
|
|
702
|
+
modelLayer._threeCapability.controlsManager.reset();
|
|
703
|
+
modelLayer._threeCapability.controlsManager.setAutoRotate(true);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
onTapPlayback() {
|
|
707
|
+
var _a;
|
|
708
|
+
if (this._playbackActive) {
|
|
709
|
+
this._onPlaybackStop();
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
// 플레이백 시작 시 팝업 닫기
|
|
713
|
+
this.hidePopup();
|
|
714
|
+
// 1) 라이브 데이터 구독 실제 종료
|
|
715
|
+
(_a = this._dataSubscriptionProvider) === null || _a === void 0 ? void 0 : _a.pause();
|
|
716
|
+
this._unsubscribeSceneData();
|
|
717
|
+
// 2) 라이브 데이터 초기화 (깨끗한 상태에서 playback 시작)
|
|
718
|
+
this._resetSceneData();
|
|
719
|
+
this._playbackActive = true;
|
|
720
|
+
if (!this._playbackEngine) {
|
|
721
|
+
this._playbackEngine = new PlaybackEngine();
|
|
722
|
+
this._playbackEngine.on('frame', ({ time }) => {
|
|
723
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
724
|
+
if (controls)
|
|
725
|
+
controls.currentTime = time;
|
|
726
|
+
});
|
|
727
|
+
this._playbackEngine.on('state-change', (state) => {
|
|
728
|
+
if (state === 'stopped') {
|
|
729
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
730
|
+
if (controls)
|
|
731
|
+
controls.playbackState = 'stopped';
|
|
732
|
+
}
|
|
733
|
+
this.requestUpdate();
|
|
734
|
+
});
|
|
735
|
+
this._playbackEngine.on('buffer-update', ({ bufferedRanges }) => {
|
|
736
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
737
|
+
if (controls)
|
|
738
|
+
controls.bufferedRanges = bufferedRanges;
|
|
739
|
+
});
|
|
740
|
+
if (this._fetchPlaybackData) {
|
|
741
|
+
this._playbackEngine.setFetchFunction(this._fetchPlaybackData);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (this._scene) {
|
|
745
|
+
this._playbackEngine.attachScene(this._scene);
|
|
746
|
+
}
|
|
747
|
+
this.dispatchEvent(new CustomEvent('playback-toggle', {
|
|
748
|
+
bubbles: true,
|
|
749
|
+
composed: true,
|
|
750
|
+
detail: { active: true }
|
|
751
|
+
}));
|
|
752
|
+
// playback 은 전체화면에서만 동작 — 이미 전체화면이면 그대로 유지
|
|
753
|
+
if (!document.fullscreenElement && !isIOS()) {
|
|
754
|
+
fullscreen(this, () => this.requestUpdate(), () => this.requestUpdate());
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
async _onPlaybackStart(e) {
|
|
759
|
+
const { fromTime, toTime, speed } = e.detail;
|
|
760
|
+
if (!this._playbackEngine)
|
|
761
|
+
return;
|
|
762
|
+
// toTime is the user-selected window END (= controls' _appliedTo).
|
|
763
|
+
// The previous hardcoded `fromTime + 1h` ignored the controls'
|
|
764
|
+
// range selection, so 10m / 30m / Range modes silently played past
|
|
765
|
+
// the requested window. Falling back to `+1h` only if toTime was
|
|
766
|
+
// omitted (legacy callers).
|
|
767
|
+
const endTime = toTime instanceof Date ? toTime : new Date(fromTime.getTime() + 3600000);
|
|
768
|
+
if (this._scene)
|
|
769
|
+
this._scene.playbackMode = true;
|
|
770
|
+
try {
|
|
771
|
+
await this._playbackEngine.startChunked(fromTime, endTime, speed);
|
|
772
|
+
}
|
|
773
|
+
catch (ex) {
|
|
774
|
+
console.error('[Playback] load failed:', ex);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
_onPlaybackPause() {
|
|
778
|
+
var _a;
|
|
779
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.pause();
|
|
780
|
+
}
|
|
781
|
+
_onPlaybackResume() {
|
|
782
|
+
var _a;
|
|
783
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.resume();
|
|
784
|
+
}
|
|
785
|
+
async _onPlaybackStop() {
|
|
786
|
+
var _a, _b, _c;
|
|
787
|
+
const scene = this._scene; // 비동기 구간 scene 교체 감지용 캡처
|
|
788
|
+
// 1) 즉시: 엔진 정지 + 애니메이션 억제 ON (in-flight 애니메이션 즉시 종료)
|
|
789
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.detachScene();
|
|
790
|
+
(_b = this._playbackEngine) === null || _b === void 0 ? void 0 : _b.stop();
|
|
791
|
+
this._playbackActive = false;
|
|
792
|
+
if (scene) {
|
|
793
|
+
scene.suppressAnimations = true;
|
|
794
|
+
scene.playbackMode = false;
|
|
795
|
+
}
|
|
796
|
+
// UI 신호 + fullscreen 해제
|
|
797
|
+
this.dispatchEvent(new CustomEvent('playback-toggle', {
|
|
798
|
+
bubbles: true,
|
|
799
|
+
composed: true,
|
|
800
|
+
detail: { active: false }
|
|
801
|
+
}));
|
|
802
|
+
if (document.fullscreenElement) {
|
|
803
|
+
exitfullscreen();
|
|
804
|
+
}
|
|
805
|
+
// 2) 1초 후: 보드 리셋 + live 구독 재개
|
|
806
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
807
|
+
if (this._playbackActive || this._scene !== scene)
|
|
808
|
+
return;
|
|
809
|
+
if (!scene)
|
|
810
|
+
return;
|
|
811
|
+
this._resetSceneData();
|
|
812
|
+
this._resubscribeSceneData();
|
|
813
|
+
(_c = this._dataSubscriptionProvider) === null || _c === void 0 ? void 0 : _c.resume();
|
|
814
|
+
// 3) 2초 후 (추가 1초): 억제 해제 — 첫 live batch 가 instant 배치된 뒤 정상 애니메이션 복원
|
|
815
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
816
|
+
if (this._scene === scene) {
|
|
817
|
+
scene.suppressAnimations = false;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* 플레이백 시작 시: RESTful data-subscription 컴포넌트의 WebSocket 을 실제 disconnect.
|
|
822
|
+
* GraphQL 경로는 _dataSubscriptionProvider.pause() 에서 일괄 처리.
|
|
823
|
+
*/
|
|
824
|
+
_unsubscribeSceneData() {
|
|
825
|
+
var _a;
|
|
826
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
827
|
+
if (!root || typeof root.traverse !== 'function')
|
|
828
|
+
return;
|
|
829
|
+
root.traverse((c) => {
|
|
830
|
+
if (!c || c === root)
|
|
831
|
+
return;
|
|
832
|
+
// RESTful data-subscription 컴포넌트 — 자체 WebSocket disconnect
|
|
833
|
+
if (c.subscription) {
|
|
834
|
+
try {
|
|
835
|
+
c.subscription.unsubscribe();
|
|
836
|
+
}
|
|
837
|
+
catch (_a) { }
|
|
838
|
+
c.subscription = null;
|
|
839
|
+
}
|
|
840
|
+
if (c.client) {
|
|
841
|
+
try {
|
|
842
|
+
c.client.unsubscribeAll();
|
|
843
|
+
c.client.close(true);
|
|
844
|
+
}
|
|
845
|
+
catch (_b) { }
|
|
846
|
+
c.client = null;
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* 플레이백 종료 시: RESTful data-subscription 컴포넌트의 WebSocket 을 재연결.
|
|
852
|
+
* GraphQL 경로는 _dataSubscriptionProvider.resume() 에서 일괄 처리.
|
|
853
|
+
*/
|
|
854
|
+
_resubscribeSceneData() {
|
|
855
|
+
var _a;
|
|
856
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
857
|
+
if (!root || typeof root.traverse !== 'function')
|
|
858
|
+
return;
|
|
859
|
+
root.traverse((c) => {
|
|
860
|
+
if (!c || c === root)
|
|
861
|
+
return;
|
|
862
|
+
// RESTful data-subscription 컴포넌트 — WebSocket 재연결
|
|
863
|
+
if (typeof c.requestData === 'function' && !c.client) {
|
|
864
|
+
c.requestData();
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
_resetSceneData() {
|
|
869
|
+
var _a, _b, _c, _d;
|
|
870
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
871
|
+
if (!root || typeof root.traverse !== 'function')
|
|
872
|
+
return;
|
|
873
|
+
root.traverse((c) => {
|
|
874
|
+
if (!c || c === root)
|
|
875
|
+
return;
|
|
876
|
+
// null 대신 {} 로 리셋 — typeof null === 'object' 함정 회피 + destructuring 안전 +
|
|
877
|
+
// statusColor 등 data?.X 형태 getter 가 자연스럽게 default 로 폴백
|
|
878
|
+
try {
|
|
879
|
+
if ('data' in c)
|
|
880
|
+
c.data = {};
|
|
881
|
+
}
|
|
882
|
+
catch (_a) { }
|
|
883
|
+
// 차량 등의 위치 캐시 초기화 — 없으면 이전 waypoint 경로로 스냅백되는 잔상 발생
|
|
884
|
+
if ('lastWaypoint' in c)
|
|
885
|
+
c.lastWaypoint = undefined;
|
|
886
|
+
});
|
|
887
|
+
// vehicle 의 waypoint 이동 애니메이션 전체 중단
|
|
888
|
+
// (Animation.stepAnimation 의 suppress 체크만으로는 WaypointHandler 의 animatorMap 이
|
|
889
|
+
// 완전히 비워지지 않을 수 있어 명시적 reset)
|
|
890
|
+
if ((_b = root.waypointHandler) === null || _b === void 0 ? void 0 : _b.reset)
|
|
891
|
+
root.waypointHandler.reset();
|
|
892
|
+
// 모든 decorator 제거 — bouncing-arrow/border-highlighting 같은 SVG 애니메이션 decorator 는
|
|
893
|
+
// decorator-registry 가 붙잡고 있어 data=null 만으로는 제거되지 않음. 명시적 reset 필요.
|
|
894
|
+
try {
|
|
895
|
+
root.trigger('deco-reset');
|
|
896
|
+
}
|
|
897
|
+
catch (_e) { }
|
|
898
|
+
if ((_c = root.carrierManager) === null || _c === void 0 ? void 0 : _c.dispose)
|
|
899
|
+
root.carrierManager.dispose();
|
|
900
|
+
if ((_d = root.transitionEffect) === null || _d === void 0 ? void 0 : _d.dispose)
|
|
901
|
+
root.transitionEffect.dispose();
|
|
902
|
+
}
|
|
903
|
+
_onPlaybackSeek(e) {
|
|
904
|
+
var _a;
|
|
905
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.seekTo(e.detail.toTime);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* 드래그 시작 신호 — 엔진의 재생 타이머 정지해서 스크러빙 동안
|
|
909
|
+
* scene 이 엔진 시간으로 독립 갱신되지 않도록 함. _state 는 유지되므로
|
|
910
|
+
* 드래그 종료 시 seekTo 가 원래 재생/일시정지 상태로 복원.
|
|
911
|
+
*/
|
|
912
|
+
_onPlaybackSeekStart() {
|
|
913
|
+
var _a;
|
|
914
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.pauseForSeek();
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* 드래그 중 디바운스된 seek preview. 해당 시간의 snapshot 만 scene 에 적용.
|
|
918
|
+
* 보드 리셋 없음, 애니메이션 억제.
|
|
919
|
+
*/
|
|
920
|
+
_onPlaybackSeekPreview(e) {
|
|
921
|
+
var _a;
|
|
922
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.seekPreview(e.detail.toTime);
|
|
923
|
+
}
|
|
924
|
+
_onPlaybackSpeed(e) {
|
|
925
|
+
var _a;
|
|
926
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.setSpeed(e.detail.speed);
|
|
927
|
+
}
|
|
488
928
|
onRunBoard() {
|
|
489
929
|
this.dispatchEvent(new CustomEvent('run-board', { bubbles: true, composed: true, detail: this.board.id }));
|
|
490
930
|
}
|
|
@@ -578,6 +1018,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
578
1018
|
}
|
|
579
1019
|
if (this.popup) {
|
|
580
1020
|
this.popup.component = component;
|
|
1021
|
+
this.popup.playbackMode = this._playbackActive;
|
|
581
1022
|
}
|
|
582
1023
|
}
|
|
583
1024
|
hidePopup() {
|
|
@@ -643,10 +1084,58 @@ BoardViewer.styles = [
|
|
|
643
1084
|
right: 5px;
|
|
644
1085
|
}
|
|
645
1086
|
|
|
646
|
-
|
|
1087
|
+
[fab-group] {
|
|
647
1088
|
position: absolute;
|
|
648
1089
|
bottom: 15px;
|
|
649
1090
|
right: 16px;
|
|
1091
|
+
display: flex;
|
|
1092
|
+
flex-direction: column-reverse;
|
|
1093
|
+
align-items: center;
|
|
1094
|
+
gap: 12px;
|
|
1095
|
+
z-index: 1000;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
[fab-group] md-fab {
|
|
1099
|
+
transition: transform 0.2s ease, opacity 0.2s ease;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
[fab-group] md-fab.main-fab md-icon {
|
|
1103
|
+
transition: transform 0.3s ease;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
[fab-group] md-fab.main-fab[expanded] md-icon {
|
|
1107
|
+
transform: rotate(45deg);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
[fab-group] md-fab.sub-fab {
|
|
1111
|
+
--md-fab-container-width: 40px;
|
|
1112
|
+
--md-fab-container-height: 40px;
|
|
1113
|
+
--md-icon-size: 20px;
|
|
1114
|
+
transform: scale(0);
|
|
1115
|
+
opacity: 0;
|
|
1116
|
+
pointer-events: none;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/* SVG icon 은 fab 전체 크기로 — 자체 원이 fab 표면을 대체. */
|
|
1120
|
+
[fab-group] md-fab.sub-fab svg[slot="icon"] {
|
|
1121
|
+
width: 40px;
|
|
1122
|
+
height: 40px;
|
|
1123
|
+
display: block;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
[fab-group][expanded] md-fab.sub-fab {
|
|
1127
|
+
transform: scale(1);
|
|
1128
|
+
opacity: 1;
|
|
1129
|
+
pointer-events: auto;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
[fab-group] md-fab.sub-fab[disabled] {
|
|
1133
|
+
opacity: 0.4;
|
|
1134
|
+
pointer-events: none;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
ox-playback-controls {
|
|
1138
|
+
transition: opacity 0.3s ease;
|
|
650
1139
|
}
|
|
651
1140
|
|
|
652
1141
|
[hidden] {
|
|
@@ -693,6 +1182,9 @@ __decorate([
|
|
|
693
1182
|
__decorate([
|
|
694
1183
|
property({ type: Object })
|
|
695
1184
|
], BoardViewer.prototype, "values", void 0);
|
|
1185
|
+
__decorate([
|
|
1186
|
+
property({ type: Object })
|
|
1187
|
+
], BoardViewer.prototype, "loadTracker", void 0);
|
|
696
1188
|
__decorate([
|
|
697
1189
|
property({ type: Boolean, reflect: true, attribute: 'hide-fullscreen' })
|
|
698
1190
|
], BoardViewer.prototype, "hideFullscreen", void 0);
|
|
@@ -701,19 +1193,10 @@ __decorate([
|
|
|
701
1193
|
], BoardViewer.prototype, "hideNavigation", void 0);
|
|
702
1194
|
__decorate([
|
|
703
1195
|
state()
|
|
704
|
-
], BoardViewer.prototype, "
|
|
705
|
-
__decorate([
|
|
706
|
-
state()
|
|
707
|
-
], BoardViewer.prototype, "_forward", void 0);
|
|
708
|
-
__decorate([
|
|
709
|
-
state()
|
|
710
|
-
], BoardViewer.prototype, "_backward", void 0);
|
|
711
|
-
__decorate([
|
|
712
|
-
state()
|
|
713
|
-
], BoardViewer.prototype, "_oldtarget", void 0);
|
|
1196
|
+
], BoardViewer.prototype, "_fabExpanded", void 0);
|
|
714
1197
|
__decorate([
|
|
715
1198
|
state()
|
|
716
|
-
], BoardViewer.prototype, "
|
|
1199
|
+
], BoardViewer.prototype, "_playbackActive", void 0);
|
|
717
1200
|
__decorate([
|
|
718
1201
|
query('#target')
|
|
719
1202
|
], BoardViewer.prototype, "_target", void 0);
|
|
@@ -724,8 +1207,11 @@ __decorate([
|
|
|
724
1207
|
query('#next')
|
|
725
1208
|
], BoardViewer.prototype, "_next", void 0);
|
|
726
1209
|
__decorate([
|
|
727
|
-
query('
|
|
728
|
-
], BoardViewer.prototype, "
|
|
1210
|
+
query('[fab-group]')
|
|
1211
|
+
], BoardViewer.prototype, "_fabGroup", void 0);
|
|
1212
|
+
__decorate([
|
|
1213
|
+
property({ attribute: false })
|
|
1214
|
+
], BoardViewer.prototype, "_fetchPlaybackData", void 0);
|
|
729
1215
|
BoardViewer = __decorate([
|
|
730
1216
|
customElement('ox-board-viewer')
|
|
731
1217
|
], BoardViewer);
|