@fmsim/board 2.0.0-beta.6 → 2.0.0-beta.60
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 +3868 -2431
- 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/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.js +2 -4
- package/dist/src/layers/movement-layer.js.map +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/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 +1 -0
- 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/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 +5 -2
- 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 +41 -13
- package/dist/src/ox-board-viewer.js +363 -85
- 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 +67 -0
- package/dist/src/ox-playback-controls.js +594 -0
- package/dist/src/ox-playback-controls.js.map +1 -0
- package/dist/src/playback/playback-engine.d.ts +95 -0
- package/dist/src/playback/playback-engine.js +368 -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 +3 -3
|
@@ -4,9 +4,11 @@ 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 { PlaybackEngine } from './playback/playback-engine.js';
|
|
11
|
+
import { create, LoadTracker, LoadingOverlay, SCENE_MODE } from '@hatiolab/things-scene';
|
|
10
12
|
import { isIOS, togglefullscreen } from '@operato/utils';
|
|
11
13
|
import { ScrollbarStyles } from '@operato/styles';
|
|
12
14
|
import { DataStorage } from './data-storage/data-storage.js';
|
|
@@ -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,54 @@ 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();
|
|
176
180
|
this.resizeHandler = () => {
|
|
177
181
|
this._scene && this._scene.fit();
|
|
178
182
|
};
|
|
183
|
+
// ── Data Subscription ──
|
|
184
|
+
this._dataSubscriptionProvider = isRestfulAvailable() ? undefined : new DataSubscriptionProviderImpl();
|
|
185
|
+
}
|
|
186
|
+
get _is3dMode() {
|
|
187
|
+
var _a, _b, _c;
|
|
188
|
+
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
189
|
}
|
|
180
190
|
render() {
|
|
181
|
-
var
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
var _a, _b;
|
|
192
|
+
var fabGroup = this.hideFullscreen
|
|
193
|
+
? html ``
|
|
194
|
+
: html `
|
|
195
|
+
<div
|
|
196
|
+
fab-group
|
|
197
|
+
?expanded=${this._fabExpanded}
|
|
186
198
|
@mouseover=${(e) => this.transientShowButtons(true)}
|
|
187
199
|
@mouseout=${(e) => this.transientShowButtons()}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
>
|
|
201
|
+
<md-fab
|
|
202
|
+
class="main-fab"
|
|
203
|
+
?expanded=${this._fabExpanded}
|
|
204
|
+
@click=${() => { this._fabExpanded = !this._fabExpanded; }}
|
|
205
|
+
title="menu"
|
|
206
|
+
><md-icon slot="icon">add</md-icon></md-fab>
|
|
207
|
+
|
|
208
|
+
${isIOS() ? nothing : html `<md-fab
|
|
209
|
+
class="sub-fab"
|
|
210
|
+
@click=${() => { this._fabExpanded = false; this.onTapFullscreen(); }}
|
|
211
|
+
title="fullscreen"
|
|
212
|
+
><md-icon slot="icon">${document.fullscreenElement ? 'fullscreen_exit' : 'fullscreen'}</md-icon></md-fab>`}
|
|
213
|
+
|
|
214
|
+
<md-fab
|
|
215
|
+
class="sub-fab"
|
|
216
|
+
@click=${() => { this._fabExpanded = false; this.onTapPlayback(); }}
|
|
217
|
+
title=${this._playbackActive ? 'stop playback' : 'start playback'}
|
|
218
|
+
><md-icon slot="icon">${this._playbackActive ? 'stop' : 'replay'}</md-icon></md-fab>
|
|
219
|
+
|
|
220
|
+
<md-fab
|
|
221
|
+
class="sub-fab"
|
|
222
|
+
@click=${() => { this._fabExpanded = false; this.onTapToggle3D(); }}
|
|
223
|
+
title=${this._is3dMode ? '2D mode' : '3D mode'}
|
|
224
|
+
><md-icon slot="icon">${this._is3dMode ? 'view_in_ar' : '3d_rotation'}</md-icon></md-fab>
|
|
225
|
+
</div>
|
|
226
|
+
`;
|
|
192
227
|
var prev = !this.hideNavigation
|
|
193
228
|
? html `
|
|
194
229
|
<md-icon
|
|
@@ -216,74 +251,88 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
216
251
|
return html `
|
|
217
252
|
${prev}
|
|
218
253
|
|
|
219
|
-
<div
|
|
220
|
-
id="target"
|
|
221
|
-
@touchstart=${(e) => this.transientShowButtons()}
|
|
222
|
-
@mousemove=${(e) => this.transientShowButtons()}
|
|
223
|
-
></div>
|
|
254
|
+
<div id="target"></div>
|
|
224
255
|
|
|
225
256
|
<slot></slot>
|
|
226
|
-
${next} ${
|
|
257
|
+
${next} ${fabGroup}
|
|
258
|
+
|
|
259
|
+
${this._playbackActive
|
|
260
|
+
? html `<ox-playback-controls
|
|
261
|
+
.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'}
|
|
262
|
+
@playback-start=${this._onPlaybackStart}
|
|
263
|
+
@playback-pause=${this._onPlaybackPause}
|
|
264
|
+
@playback-resume=${this._onPlaybackResume}
|
|
265
|
+
@playback-stop=${this._onPlaybackStop}
|
|
266
|
+
@playback-seek=${this._onPlaybackSeek}
|
|
267
|
+
@playback-speed=${this._onPlaybackSpeed}
|
|
268
|
+
></ox-playback-controls>`
|
|
269
|
+
: nothing}
|
|
227
270
|
`;
|
|
228
271
|
}
|
|
272
|
+
/** connect/disconnect 시 일괄 등록/해제할 window 이벤트 목록 (모니터링 연동) */
|
|
273
|
+
get _monitoringEvents() {
|
|
274
|
+
return [
|
|
275
|
+
['fetchIFactsData', this.fetchIFactsDataHandler], // 장비 아이디 object를 캔버스 가운데로
|
|
276
|
+
['fmbShowMovement', this.fmbShowMovementHandler], // Movement Data 표시
|
|
277
|
+
['fmbMonitoringInit', this.fmbMonitoringInitHandler], // 보드 상태 정사이즈 복원
|
|
278
|
+
['clickComponent', this.clickComponentHandler], // BoardComponentInfo popup
|
|
279
|
+
['fmbStartAnimation', this.fmbStartAnimation], // AnimatedIcon 추가/제거
|
|
280
|
+
['fmbStopAnimation', this.fmbStopAnimation],
|
|
281
|
+
['fmbResetAnimation', this.fmbResetAnimation],
|
|
282
|
+
['fmbShowLegend', this.fmbShowLegendHandler], // 범례 표시
|
|
283
|
+
['fmbHideLegend', this.fmbHideLegendHandler],
|
|
284
|
+
['fmbShowPopup', this.fmbShowPopupHandler], // 팝업 표시
|
|
285
|
+
['fmbHidePopup', this.fmbHidePopupHandler],
|
|
286
|
+
['fmbShowFloatingContainer', this.fmbShowFloatingContainerHandler], // FloatingContainer
|
|
287
|
+
['fmbHideFloatingContainer', this.fmbHideFloatingContainerHandler]
|
|
288
|
+
];
|
|
289
|
+
}
|
|
229
290
|
connectedCallback() {
|
|
230
291
|
super.connectedCallback();
|
|
231
292
|
window.addEventListener('resize', this.resizeHandler);
|
|
293
|
+
// 3D 모드에서도 마우스 움직임 감지 (document 레벨, capture phase)
|
|
294
|
+
document.addEventListener('pointermove', this._pointerMoveHandler, true);
|
|
232
295
|
this.renderRoot.addEventListener('close-scene', e => {
|
|
233
296
|
e.preventDefault();
|
|
234
297
|
this.onTapPrev();
|
|
235
298
|
}, 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);
|
|
299
|
+
for (const [type, handler] of this._monitoringEvents) {
|
|
300
|
+
window.addEventListener(type, handler);
|
|
301
|
+
}
|
|
257
302
|
}
|
|
258
303
|
disconnectedCallback() {
|
|
304
|
+
var _a, _b, _c;
|
|
259
305
|
super.disconnectedCallback();
|
|
260
306
|
window.removeEventListener('resize', this.resizeHandler);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// 모니터링에서 보드 상태를 원래상태 (정사이즈)로 돌려놓는다.
|
|
266
|
-
window.removeEventListener('fmbMonitoringInit', this.fmbMonitoringInitHandler);
|
|
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);
|
|
307
|
+
document.removeEventListener('pointermove', this._pointerMoveHandler, true);
|
|
308
|
+
for (const [type, handler] of this._monitoringEvents) {
|
|
309
|
+
window.removeEventListener(type, handler);
|
|
310
|
+
}
|
|
282
311
|
this.lastComponent = null;
|
|
312
|
+
// 플레이백이 활성 상태면 명시적 stop (provider resume 포함)
|
|
313
|
+
if (this._playbackActive) {
|
|
314
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.stop();
|
|
315
|
+
(_b = this._playbackEngine) === null || _b === void 0 ? void 0 : _b.detachScene();
|
|
316
|
+
(_c = this._dataSubscriptionProvider) === null || _c === void 0 ? void 0 : _c.resume();
|
|
317
|
+
this._playbackActive = false;
|
|
318
|
+
}
|
|
319
|
+
if (this._playbackEngine) {
|
|
320
|
+
this._playbackEngine.dispose();
|
|
321
|
+
this._playbackEngine = undefined;
|
|
322
|
+
}
|
|
283
323
|
this.closeScene();
|
|
284
324
|
}
|
|
285
325
|
updated(changes) {
|
|
326
|
+
// loadTracker가 외부에서 전달되면 즉시 overlay 연결 (fetch 중에도 표시)
|
|
327
|
+
if (changes.has('loadTracker') && this.loadTracker) {
|
|
328
|
+
this._ensureOverlay(this.loadTracker);
|
|
329
|
+
}
|
|
286
330
|
if (changes.has('board')) {
|
|
331
|
+
// tracker: 외부에서 안 왔으면 여기서 생성
|
|
332
|
+
if (!this.loadTracker) {
|
|
333
|
+
this.loadTracker = new LoadTracker();
|
|
334
|
+
this._ensureOverlay(this.loadTracker);
|
|
335
|
+
}
|
|
287
336
|
this.hidePopup();
|
|
288
337
|
this.closeScene();
|
|
289
338
|
if (this.board && this.board.id) {
|
|
@@ -303,32 +352,42 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
303
352
|
}
|
|
304
353
|
}
|
|
305
354
|
async initSceneAsync() {
|
|
306
|
-
if (!this.board || !this.board.id)
|
|
355
|
+
if (!this.board || !this.board.id || !this.provider)
|
|
307
356
|
return;
|
|
308
|
-
|
|
309
|
-
|
|
357
|
+
try {
|
|
358
|
+
this._scene = await this.provider.create(this.board.id);
|
|
359
|
+
this.setupScene({ id: this.board.id, scene: this._scene });
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
console.error('Failed to load scene:', this.board.id, e);
|
|
363
|
+
}
|
|
310
364
|
}
|
|
311
365
|
initScene() {
|
|
312
|
-
if (!this.board || !this.board.id) {
|
|
366
|
+
if (!this.board || !this.board.id || !this.board.model) {
|
|
313
367
|
return;
|
|
314
368
|
}
|
|
315
369
|
this._scene = create({
|
|
316
|
-
model:
|
|
317
|
-
...this.board.model
|
|
318
|
-
},
|
|
370
|
+
model: JSON.parse(JSON.stringify(this.board.model)),
|
|
319
371
|
style: this.board.style,
|
|
320
372
|
mode: SCENE_MODE.VIEW,
|
|
321
373
|
handlers: ['mcs-event-handler', 'zoom-handler', 'shift-handler', 'waypoint-handler'],
|
|
322
374
|
layers: [{ type: 'scroll-layer' }, { type: 'movement-layer', id: 'movement-layer' }],
|
|
323
375
|
refProvider: this.provider,
|
|
324
376
|
dataStorage: this.board.id !== 'preview' ? new DataStorage(this.board.id) : undefined,
|
|
325
|
-
dataSubscriptionProvider:
|
|
377
|
+
dataSubscriptionProvider: this._dataSubscriptionProvider,
|
|
378
|
+
loading: this.loadTracker ? { tracker: this.loadTracker } : undefined
|
|
326
379
|
});
|
|
327
380
|
if (this.baseUrl) {
|
|
328
381
|
this._scene.baseUrl = this.baseUrl;
|
|
329
382
|
}
|
|
330
383
|
this.setupScene({ id: this.board.id, scene: this._scene });
|
|
331
384
|
}
|
|
385
|
+
_ensureOverlay(tracker) {
|
|
386
|
+
if (!this._loadingOverlay) {
|
|
387
|
+
this._loadingOverlay = new LoadingOverlay();
|
|
388
|
+
}
|
|
389
|
+
this._loadingOverlay.attach(this.renderRoot, tracker);
|
|
390
|
+
}
|
|
332
391
|
closeScene() {
|
|
333
392
|
if (this._scene) {
|
|
334
393
|
this.unbindSceneEvents(this._scene);
|
|
@@ -348,6 +407,10 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
348
407
|
}
|
|
349
408
|
setupScene({ id, scene }) {
|
|
350
409
|
var _a;
|
|
410
|
+
// 플레이백 리셋
|
|
411
|
+
if (this._playbackActive) {
|
|
412
|
+
this._onPlaybackStop();
|
|
413
|
+
}
|
|
351
414
|
this._scene = scene;
|
|
352
415
|
const backgroundColor = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root.state.fillStyle;
|
|
353
416
|
if (typeof backgroundColor === 'string') {
|
|
@@ -371,7 +434,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
371
434
|
this.transientShowButtons();
|
|
372
435
|
}
|
|
373
436
|
async showScene(boardId, bindingData) {
|
|
374
|
-
if (!boardId)
|
|
437
|
+
if (!boardId || !this.provider)
|
|
375
438
|
return;
|
|
376
439
|
try {
|
|
377
440
|
var scene = await this.provider.get(boardId, true);
|
|
@@ -424,7 +487,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
424
487
|
transientShowButtons(stop) {
|
|
425
488
|
var buttons = [];
|
|
426
489
|
!this.hideNavigation && buttons.push(this._next, this._prev);
|
|
427
|
-
!this.hideFullscreen && buttons.push(this.
|
|
490
|
+
!this.hideFullscreen && buttons.push(this._fabGroup);
|
|
428
491
|
if (buttons.length == 0) {
|
|
429
492
|
return;
|
|
430
493
|
}
|
|
@@ -447,7 +510,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
447
510
|
}
|
|
448
511
|
this._forward.length <= 0 ? this._next.setAttribute('hidden', '') : this._next.removeAttribute('hidden');
|
|
449
512
|
this._backward.length <= 0 ? this._prev.setAttribute('hidden', '') : this._prev.removeAttribute('hidden');
|
|
450
|
-
this.
|
|
513
|
+
this._fabGroup && this._fabGroup.removeAttribute('hidden');
|
|
451
514
|
this._fade_animations.forEach(animation => {
|
|
452
515
|
animation.cancel();
|
|
453
516
|
if (stop)
|
|
@@ -485,6 +548,182 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
485
548
|
onTapFullscreen() {
|
|
486
549
|
togglefullscreen(this, () => this.requestUpdate(), () => this.requestUpdate());
|
|
487
550
|
}
|
|
551
|
+
onTapToggle3D() {
|
|
552
|
+
var _a, _b, _c;
|
|
553
|
+
const modelLayer = (_b = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.rootModel;
|
|
554
|
+
if (!(modelLayer === null || modelLayer === void 0 ? void 0 : modelLayer.toggleThreeD))
|
|
555
|
+
return;
|
|
556
|
+
const entering3D = !modelLayer.is3dMode;
|
|
557
|
+
modelLayer.toggleThreeD();
|
|
558
|
+
this.requestUpdate();
|
|
559
|
+
// fmsim: 3D 진입 시 자동 회전
|
|
560
|
+
if (entering3D && ((_c = modelLayer._threeCapability) === null || _c === void 0 ? void 0 : _c.isInitialized)) {
|
|
561
|
+
modelLayer._threeCapability.controlsManager.reset();
|
|
562
|
+
modelLayer._threeCapability.controlsManager.setAutoRotate(true);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
onTapPlayback() {
|
|
566
|
+
var _a;
|
|
567
|
+
if (this._playbackActive) {
|
|
568
|
+
this._onPlaybackStop();
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
// 플레이백 시작 시 팝업 닫기
|
|
572
|
+
this.hidePopup();
|
|
573
|
+
// 1) 라이브 데이터 구독 실제 종료
|
|
574
|
+
(_a = this._dataSubscriptionProvider) === null || _a === void 0 ? void 0 : _a.pause();
|
|
575
|
+
this._unsubscribeSceneData();
|
|
576
|
+
// 2) 라이브 데이터 초기화 (깨끗한 상태에서 playback 시작)
|
|
577
|
+
this._resetSceneData();
|
|
578
|
+
this._playbackActive = true;
|
|
579
|
+
if (!this._playbackEngine) {
|
|
580
|
+
this._playbackEngine = new PlaybackEngine();
|
|
581
|
+
this._playbackEngine.on('frame', ({ time }) => {
|
|
582
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
583
|
+
if (controls)
|
|
584
|
+
controls.currentTime = time;
|
|
585
|
+
});
|
|
586
|
+
this._playbackEngine.on('state-change', (state) => {
|
|
587
|
+
if (state === 'stopped') {
|
|
588
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
589
|
+
if (controls)
|
|
590
|
+
controls.playbackState = 'stopped';
|
|
591
|
+
}
|
|
592
|
+
this.requestUpdate();
|
|
593
|
+
});
|
|
594
|
+
this._playbackEngine.on('buffer-update', ({ bufferedRanges }) => {
|
|
595
|
+
const controls = this.renderRoot.querySelector('ox-playback-controls');
|
|
596
|
+
if (controls)
|
|
597
|
+
controls.bufferedRanges = bufferedRanges;
|
|
598
|
+
});
|
|
599
|
+
if (this._fetchPlaybackData) {
|
|
600
|
+
this._playbackEngine.setFetchFunction(this._fetchPlaybackData);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (this._scene) {
|
|
604
|
+
this._playbackEngine.attachScene(this._scene);
|
|
605
|
+
}
|
|
606
|
+
this.dispatchEvent(new CustomEvent('playback-toggle', {
|
|
607
|
+
bubbles: true,
|
|
608
|
+
composed: true,
|
|
609
|
+
detail: { active: true }
|
|
610
|
+
}));
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async _onPlaybackStart(e) {
|
|
614
|
+
const { fromTime, speed } = e.detail;
|
|
615
|
+
if (!this._playbackEngine)
|
|
616
|
+
return;
|
|
617
|
+
const endTime = new Date(fromTime.getTime() + 3600000);
|
|
618
|
+
if (this._scene)
|
|
619
|
+
this._scene.playbackMode = true;
|
|
620
|
+
try {
|
|
621
|
+
await this._playbackEngine.startChunked(fromTime, endTime, speed);
|
|
622
|
+
}
|
|
623
|
+
catch (ex) {
|
|
624
|
+
console.error('[Playback] load failed:', ex);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
_onPlaybackPause() {
|
|
628
|
+
var _a;
|
|
629
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.pause();
|
|
630
|
+
}
|
|
631
|
+
_onPlaybackResume() {
|
|
632
|
+
var _a;
|
|
633
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.resume();
|
|
634
|
+
}
|
|
635
|
+
_onPlaybackStop() {
|
|
636
|
+
var _a, _b, _c;
|
|
637
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.stop();
|
|
638
|
+
(_b = this._playbackEngine) === null || _b === void 0 ? void 0 : _b.detachScene();
|
|
639
|
+
this._playbackActive = false;
|
|
640
|
+
// playback 데이터 초기화 + playbackMode 해제 + 라이브 데이터 구독 재개
|
|
641
|
+
if (this._scene)
|
|
642
|
+
this._scene.playbackMode = false;
|
|
643
|
+
this._resetSceneData();
|
|
644
|
+
this._resubscribeSceneData();
|
|
645
|
+
(_c = this._dataSubscriptionProvider) === null || _c === void 0 ? void 0 : _c.resume();
|
|
646
|
+
this.dispatchEvent(new CustomEvent('playback-toggle', {
|
|
647
|
+
bubbles: true,
|
|
648
|
+
composed: true,
|
|
649
|
+
detail: { active: false }
|
|
650
|
+
}));
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* 플레이백 시작 시: RESTful data-subscription 컴포넌트의 WebSocket 을 실제 disconnect.
|
|
654
|
+
* GraphQL 경로는 _dataSubscriptionProvider.pause() 에서 일괄 처리.
|
|
655
|
+
*/
|
|
656
|
+
_unsubscribeSceneData() {
|
|
657
|
+
var _a;
|
|
658
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
659
|
+
if (!root || typeof root.traverse !== 'function')
|
|
660
|
+
return;
|
|
661
|
+
root.traverse((c) => {
|
|
662
|
+
if (!c || c === root)
|
|
663
|
+
return;
|
|
664
|
+
// RESTful data-subscription 컴포넌트 — 자체 WebSocket disconnect
|
|
665
|
+
if (c.subscription) {
|
|
666
|
+
try {
|
|
667
|
+
c.subscription.unsubscribe();
|
|
668
|
+
}
|
|
669
|
+
catch (_a) { }
|
|
670
|
+
c.subscription = null;
|
|
671
|
+
}
|
|
672
|
+
if (c.client) {
|
|
673
|
+
try {
|
|
674
|
+
c.client.unsubscribeAll();
|
|
675
|
+
c.client.close(true);
|
|
676
|
+
}
|
|
677
|
+
catch (_b) { }
|
|
678
|
+
c.client = null;
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* 플레이백 종료 시: RESTful data-subscription 컴포넌트의 WebSocket 을 재연결.
|
|
684
|
+
* GraphQL 경로는 _dataSubscriptionProvider.resume() 에서 일괄 처리.
|
|
685
|
+
*/
|
|
686
|
+
_resubscribeSceneData() {
|
|
687
|
+
var _a;
|
|
688
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
689
|
+
if (!root || typeof root.traverse !== 'function')
|
|
690
|
+
return;
|
|
691
|
+
root.traverse((c) => {
|
|
692
|
+
if (!c || c === root)
|
|
693
|
+
return;
|
|
694
|
+
// RESTful data-subscription 컴포넌트 — WebSocket 재연결
|
|
695
|
+
if (typeof c.requestData === 'function' && !c.client) {
|
|
696
|
+
c.requestData();
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
_resetSceneData() {
|
|
701
|
+
var _a, _b, _c;
|
|
702
|
+
const root = (_a = this._scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
703
|
+
if (!root || typeof root.traverse !== 'function')
|
|
704
|
+
return;
|
|
705
|
+
root.traverse((c) => {
|
|
706
|
+
if (!c || c === root)
|
|
707
|
+
return;
|
|
708
|
+
try {
|
|
709
|
+
if ('data' in c)
|
|
710
|
+
c.data = null;
|
|
711
|
+
}
|
|
712
|
+
catch (_a) { }
|
|
713
|
+
});
|
|
714
|
+
if ((_b = root.carrierManager) === null || _b === void 0 ? void 0 : _b.dispose)
|
|
715
|
+
root.carrierManager.dispose();
|
|
716
|
+
if ((_c = root.transitionEffect) === null || _c === void 0 ? void 0 : _c.dispose)
|
|
717
|
+
root.transitionEffect.dispose();
|
|
718
|
+
}
|
|
719
|
+
_onPlaybackSeek(e) {
|
|
720
|
+
var _a;
|
|
721
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.seekTo(e.detail.toTime);
|
|
722
|
+
}
|
|
723
|
+
_onPlaybackSpeed(e) {
|
|
724
|
+
var _a;
|
|
725
|
+
(_a = this._playbackEngine) === null || _a === void 0 ? void 0 : _a.setSpeed(e.detail.speed);
|
|
726
|
+
}
|
|
488
727
|
onRunBoard() {
|
|
489
728
|
this.dispatchEvent(new CustomEvent('run-board', { bubbles: true, composed: true, detail: this.board.id }));
|
|
490
729
|
}
|
|
@@ -578,6 +817,7 @@ let BoardViewer = class BoardViewer extends LitElement {
|
|
|
578
817
|
}
|
|
579
818
|
if (this.popup) {
|
|
580
819
|
this.popup.component = component;
|
|
820
|
+
this.popup.playbackMode = this._playbackActive;
|
|
581
821
|
}
|
|
582
822
|
}
|
|
583
823
|
hidePopup() {
|
|
@@ -643,10 +883,51 @@ BoardViewer.styles = [
|
|
|
643
883
|
right: 5px;
|
|
644
884
|
}
|
|
645
885
|
|
|
646
|
-
|
|
886
|
+
[fab-group] {
|
|
647
887
|
position: absolute;
|
|
648
888
|
bottom: 15px;
|
|
649
889
|
right: 16px;
|
|
890
|
+
display: flex;
|
|
891
|
+
flex-direction: column-reverse;
|
|
892
|
+
align-items: center;
|
|
893
|
+
gap: 12px;
|
|
894
|
+
z-index: 1000;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
[fab-group] md-fab {
|
|
898
|
+
transition: transform 0.2s ease, opacity 0.2s ease;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
[fab-group] md-fab.main-fab md-icon {
|
|
902
|
+
transition: transform 0.3s ease;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
[fab-group] md-fab.main-fab[expanded] md-icon {
|
|
906
|
+
transform: rotate(45deg);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
[fab-group] md-fab.sub-fab {
|
|
910
|
+
--md-fab-container-width: 40px;
|
|
911
|
+
--md-fab-container-height: 40px;
|
|
912
|
+
--md-icon-size: 20px;
|
|
913
|
+
transform: scale(0);
|
|
914
|
+
opacity: 0;
|
|
915
|
+
pointer-events: none;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
[fab-group][expanded] md-fab.sub-fab {
|
|
919
|
+
transform: scale(1);
|
|
920
|
+
opacity: 1;
|
|
921
|
+
pointer-events: auto;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
[fab-group] md-fab.sub-fab[disabled] {
|
|
925
|
+
opacity: 0.4;
|
|
926
|
+
pointer-events: none;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
ox-playback-controls {
|
|
930
|
+
transition: opacity 0.3s ease;
|
|
650
931
|
}
|
|
651
932
|
|
|
652
933
|
[hidden] {
|
|
@@ -693,6 +974,9 @@ __decorate([
|
|
|
693
974
|
__decorate([
|
|
694
975
|
property({ type: Object })
|
|
695
976
|
], BoardViewer.prototype, "values", void 0);
|
|
977
|
+
__decorate([
|
|
978
|
+
property({ type: Object })
|
|
979
|
+
], BoardViewer.prototype, "loadTracker", void 0);
|
|
696
980
|
__decorate([
|
|
697
981
|
property({ type: Boolean, reflect: true, attribute: 'hide-fullscreen' })
|
|
698
982
|
], BoardViewer.prototype, "hideFullscreen", void 0);
|
|
@@ -701,19 +985,10 @@ __decorate([
|
|
|
701
985
|
], BoardViewer.prototype, "hideNavigation", void 0);
|
|
702
986
|
__decorate([
|
|
703
987
|
state()
|
|
704
|
-
], BoardViewer.prototype, "
|
|
988
|
+
], BoardViewer.prototype, "_fabExpanded", void 0);
|
|
705
989
|
__decorate([
|
|
706
990
|
state()
|
|
707
|
-
], BoardViewer.prototype, "
|
|
708
|
-
__decorate([
|
|
709
|
-
state()
|
|
710
|
-
], BoardViewer.prototype, "_backward", void 0);
|
|
711
|
-
__decorate([
|
|
712
|
-
state()
|
|
713
|
-
], BoardViewer.prototype, "_oldtarget", void 0);
|
|
714
|
-
__decorate([
|
|
715
|
-
state()
|
|
716
|
-
], BoardViewer.prototype, "_fade_animations", void 0);
|
|
991
|
+
], BoardViewer.prototype, "_playbackActive", void 0);
|
|
717
992
|
__decorate([
|
|
718
993
|
query('#target')
|
|
719
994
|
], BoardViewer.prototype, "_target", void 0);
|
|
@@ -724,8 +999,11 @@ __decorate([
|
|
|
724
999
|
query('#next')
|
|
725
1000
|
], BoardViewer.prototype, "_next", void 0);
|
|
726
1001
|
__decorate([
|
|
727
|
-
query('
|
|
728
|
-
], BoardViewer.prototype, "
|
|
1002
|
+
query('[fab-group]')
|
|
1003
|
+
], BoardViewer.prototype, "_fabGroup", void 0);
|
|
1004
|
+
__decorate([
|
|
1005
|
+
property({ attribute: false })
|
|
1006
|
+
], BoardViewer.prototype, "_fetchPlaybackData", void 0);
|
|
729
1007
|
BoardViewer = __decorate([
|
|
730
1008
|
customElement('ox-board-viewer')
|
|
731
1009
|
], BoardViewer);
|