@operato/board 10.0.0-beta.4 → 10.0.0-beta.41

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 (47) hide show
  1. package/CHANGELOG.md +324 -0
  2. package/dist/src/component/container.js +1 -3
  3. package/dist/src/component/container.js.map +1 -1
  4. package/dist/src/component/etc.js +2 -10
  5. package/dist/src/component/etc.js.map +1 -1
  6. package/dist/src/component/line.js +4 -28
  7. package/dist/src/component/line.js.map +1 -1
  8. package/dist/src/component/shape.js +5 -29
  9. package/dist/src/component/shape.js.map +1 -1
  10. package/dist/src/component/text-and-media.js +2 -25
  11. package/dist/src/component/text-and-media.js.map +1 -1
  12. package/dist/src/data-storage/board-model-cache.d.ts +30 -0
  13. package/dist/src/data-storage/board-model-cache.js +93 -0
  14. package/dist/src/data-storage/board-model-cache.js.map +1 -0
  15. package/dist/src/graphql/playback-buffer.d.ts +79 -0
  16. package/dist/src/graphql/playback-buffer.js +139 -0
  17. package/dist/src/graphql/playback-buffer.js.map +1 -0
  18. package/dist/src/graphql/playback-buffer.test.d.ts +1 -0
  19. package/dist/src/graphql/playback-buffer.test.js +261 -0
  20. package/dist/src/graphql/playback-buffer.test.js.map +1 -0
  21. package/dist/src/graphql/playback-subscription.d.ts +89 -0
  22. package/dist/src/graphql/playback-subscription.js +258 -0
  23. package/dist/src/graphql/playback-subscription.js.map +1 -0
  24. package/dist/src/index.d.ts +2 -0
  25. package/dist/src/index.js +1 -0
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/modeller/edit-toolbar-style.js +38 -1
  28. package/dist/src/modeller/edit-toolbar-style.js.map +1 -1
  29. package/dist/src/modeller/edit-toolbar.d.ts +8 -16
  30. package/dist/src/modeller/edit-toolbar.js +204 -199
  31. package/dist/src/modeller/edit-toolbar.js.map +1 -1
  32. package/dist/src/modeller/scene-viewer/ox-scene-viewer.d.ts +2 -1
  33. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js +7 -11
  34. package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
  35. package/dist/src/ox-board-modeller.d.ts +8 -1
  36. package/dist/src/ox-board-modeller.js +125 -6
  37. package/dist/src/ox-board-modeller.js.map +1 -1
  38. package/dist/src/ox-board-viewer.d.ts +50 -1
  39. package/dist/src/ox-board-viewer.js +271 -28
  40. package/dist/src/ox-board-viewer.js.map +1 -1
  41. package/dist/src/ox-playback-controls.d.ts +56 -0
  42. package/dist/src/ox-playback-controls.js +515 -0
  43. package/dist/src/ox-playback-controls.js.map +1 -0
  44. package/dist/src/selector/ox-board-selector.js +11 -1
  45. package/dist/src/selector/ox-board-selector.js.map +1 -1
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +13 -12
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playback-subscription.js","sourceRoot":"","sources":["../../../src/graphql/playback-subscription.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, DataSubscriptionProvider } from '@hatiolab/things-scene'\nimport gql from 'graphql-tag'\nimport { PlaybackBuffer, type PlaybackSnapshot, type ChunkFetcher } from './playback-buffer.js'\n\nexport type PlaybackState = 'idle' | 'playing' | 'paused' | 'stopped'\n\nexport interface PlaybackStatus {\n state: PlaybackState\n currentTime: string\n speed: number\n bufferedRanges?: { from: number; to: number }[]\n}\n\nexport interface PlaybackConfig {\n /** 플레이백 가능한 시간 범위 */\n timeRange?: { from: Date; to: Date }\n}\n\n/**\n * PlaybackProvider — YouTube 스트리밍 방식의 청크 기반 플레이백\n *\n * - 10분 단위로 데이터를 fetch하여 버퍼에 적재\n * - 남은 데이터 1분 이하 시 다음 10분 prefetch\n * - seek 시 기존 버퍼 전체 폐기 후 fresh fetch\n * - requestAnimationFrame 기반 재생 루프\n */\nexport class PlaybackProvider implements DataSubscriptionProvider {\n private _components: Map<string, Set<Component>> = new Map()\n private _buffer: PlaybackBuffer | null = null\n private _state: PlaybackState = 'idle'\n private _speed: number = 1\n private _playHead: number = 0\n private _lastFrameTime: number = 0\n private _rafId: number = 0\n private _lastDistributedSnapshot: PlaybackSnapshot | null = null\n private _onStatusChange?: (status: PlaybackStatus) => void\n private _totalRange: { from: Date; to: Date } | null = null\n\n constructor(onStatusChange?: (status: PlaybackStatus) => void) {\n this._onStatusChange = onStatusChange\n }\n\n get state() { return this._state }\n get speed() { return this._speed }\n get currentTime() { return this._playHead ? new Date(this._playHead).toISOString() : '' }\n\n /**\n * DataSubscriptionProvider.subscribe 구현\n */\n async subscribe(tag: string, component: Component) {\n if (!this._components.has(tag)) {\n this._components.set(tag, new Set())\n }\n this._components.get(tag)!.add(component)\n\n return {\n unsubscribe: () => {\n const components = this._components.get(tag)\n if (components) {\n components.delete(component)\n if (components.size === 0) {\n this._components.delete(tag)\n }\n }\n }\n }\n }\n\n /**\n * 플레이백 시작\n */\n async start(fromTime: Date, speed: number = 1, totalRange?: { from: Date; to: Date }) {\n this.stop()\n\n this._speed = speed\n this._totalRange = totalRange || { from: fromTime, to: new Date(fromTime.getTime() + 3600000) }\n\n // 버퍼 생성 + 초기 로드\n this._buffer = new PlaybackBuffer(this._createFetcher(), this._totalRange)\n await this._buffer.loadInitial(fromTime.getTime())\n\n this._playHead = fromTime.getTime()\n this._state = 'playing'\n this._lastFrameTime = performance.now()\n\n this._startPlayLoop()\n this._notifyStatus()\n }\n\n /**\n * 일시정지\n */\n pause() {\n if (this._state !== 'playing') return\n this._state = 'paused'\n this._stopPlayLoop()\n this._notifyStatus()\n }\n\n /**\n * 재개\n */\n resume() {\n if (this._state !== 'paused') return\n this._state = 'playing'\n this._lastFrameTime = performance.now()\n this._startPlayLoop()\n this._notifyStatus()\n }\n\n /**\n * seek — 기존 버퍼 폐기 + 새 위치에서 fresh fetch\n */\n async seek(toTime: Date) {\n if (!this._buffer) return\n\n const wasPlaying = this._state === 'playing'\n this._stopPlayLoop()\n\n // 버퍼 전체 폐기 + 새 위치 로드\n await this._buffer.seek(toTime.getTime())\n this._playHead = toTime.getTime()\n this._lastDistributedSnapshot = null\n\n // 새 위치의 첫 스냅샷 즉시 배포\n const snap = this._buffer.getSnapshotAt(this._playHead)\n if (snap) {\n this._distributeSnapshot(snap.data)\n this._lastDistributedSnapshot = snap\n }\n\n if (wasPlaying) {\n this._state = 'playing'\n this._lastFrameTime = performance.now()\n this._startPlayLoop()\n }\n\n this._notifyStatus()\n }\n\n /**\n * 배속 변경\n */\n setSpeed(speed: number) {\n this._speed = speed\n this._notifyStatus()\n }\n\n /**\n * 중지\n */\n stop() {\n this._stopPlayLoop()\n this._buffer?.clear()\n this._buffer = null\n this._state = 'stopped'\n this._playHead = 0\n this._lastDistributedSnapshot = null\n this._notifyStatus()\n }\n\n /**\n * DataSubscriptionProvider.dispose 구현\n */\n dispose() {\n this.stop()\n this._state = 'idle'\n this._components.clear()\n }\n\n // --- 재생 루프 ---\n\n private _startPlayLoop() {\n this._rafId = requestAnimationFrame(this._playLoop)\n }\n\n private _stopPlayLoop() {\n if (this._rafId) {\n cancelAnimationFrame(this._rafId)\n this._rafId = 0\n }\n }\n\n private _playLoop = (now: number) => {\n if (this._state !== 'playing' || !this._buffer) return\n\n const delta = (now - this._lastFrameTime) * this._speed\n this._lastFrameTime = now\n this._playHead += delta\n\n // 전체 범위 끝 도달 체크\n if (this._totalRange && this._playHead >= this._totalRange.to.getTime()) {\n this._playHead = this._totalRange.to.getTime()\n this._state = 'stopped'\n this._notifyStatus()\n return\n }\n\n // 현재 스냅샷 배포\n const snap = this._buffer.getSnapshotAt(this._playHead)\n if (snap && snap !== this._lastDistributedSnapshot) {\n this._distributeSnapshot(snap.data)\n this._lastDistributedSnapshot = snap\n }\n\n // prefetch 체크 (비동기, 루프 블로킹 안 함)\n this._buffer.checkPrefetch(this._playHead)\n\n this._notifyStatus()\n\n // 다음 프레임\n this._rafId = requestAnimationFrame(this._playLoop)\n }\n\n // --- 데이터 배포 ---\n\n private _distributeSnapshot(snapshotData: any) {\n if (!snapshotData || typeof snapshotData !== 'object') return\n\n for (const [tag, value] of Object.entries(snapshotData)) {\n const components = this._components.get(tag)\n if (components) {\n for (const component of components) {\n component.data = value\n }\n }\n }\n }\n\n // --- 데이터 fetcher ---\n\n private _createFetcher(): ChunkFetcher {\n return async (fromTime: Date, toTime: Date): Promise<PlaybackSnapshot[]> => {\n try {\n const { client } = await import('@operato/graphql')\n const response = await client.query({\n query: gql`\n query PlaybackChunk($startTime: String!, $endTime: String!) {\n playback(startTime: $startTime, endTime: $endTime) {\n status\n playback {\n scenarios {\n scenarioName\n snapshots {\n type\n time\n data\n }\n }\n }\n }\n }\n `,\n variables: {\n startTime: fromTime.toISOString().replace('T', ' ').replace(/\\.\\d+Z$/, ''),\n endTime: toTime.toISOString().replace('T', ' ').replace(/\\.\\d+Z$/, '')\n },\n fetchPolicy: 'no-cache'\n })\n\n return this._parsePlaybackResponse(response.data?.playback)\n } catch (e) {\n console.error('[PlaybackProvider] chunk fetch error:', e)\n return []\n }\n }\n }\n\n /**\n * 백엔드 응답을 PlaybackSnapshot[] 형식으로 변환\n */\n private _parsePlaybackResponse(response: any): PlaybackSnapshot[] {\n if (!response?.status || !response?.playback?.scenarios) return []\n\n const snapshotMap = new Map<number, Record<string, any>>()\n\n for (const scenario of response.playback.scenarios) {\n for (const record of scenario.snapshots) {\n const time = new Date(record.time.replace(' ', 'T')).getTime()\n const data = typeof record.data === 'string' ? JSON.parse(record.data) : record.data\n\n if (!snapshotMap.has(time)) {\n snapshotMap.set(time, {})\n }\n const merged = snapshotMap.get(time)!\n // scenarioName을 tag로 사용\n merged[scenario.scenarioName] = data\n }\n }\n\n return Array.from(snapshotMap.entries())\n .map(([timestamp, data]) => ({ timestamp, data }))\n .sort((a, b) => a.timestamp - b.timestamp)\n }\n\n // --- 상태 통보 ---\n\n private _notifyStatus() {\n this._onStatusChange?.({\n state: this._state,\n currentTime: this._playHead ? new Date(this._playHead).toISOString() : '',\n speed: this._speed,\n bufferedRanges: this._buffer?.getBufferedRanges()\n })\n }\n}\n"]}
@@ -1,6 +1,8 @@
1
1
  export * from './types.js';
2
2
  export { registerDefaultGroups } from './component/register-default-groups.js';
3
3
  export { BoardDataStorage, PlaylistStorage } from './data-storage/data-storage.js';
4
+ export { BoardModelCache } from './data-storage/board-model-cache.js';
5
+ export type { CachedBoard } from './data-storage/board-model-cache.js';
4
6
  export * from './ox-board-viewer.js';
5
7
  export * from './ox-board-player.js';
6
8
  export * from './ox-board-modeller.js';
package/dist/src/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './types.js';
2
2
  export { registerDefaultGroups } from './component/register-default-groups.js';
3
3
  export { BoardDataStorage, PlaylistStorage } from './data-storage/data-storage.js';
4
+ export { BoardModelCache } from './data-storage/board-model-cache.js';
4
5
  export * from './ox-board-viewer.js';
5
6
  export * from './ox-board-player.js';
6
7
  export * from './ox-board-modeller.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAElF,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA","sourcesContent":["export * from './types.js'\n\nexport { registerDefaultGroups } from './component/register-default-groups.js'\nexport { BoardDataStorage, PlaylistStorage } from './data-storage/data-storage.js'\n\nexport * from './ox-board-viewer.js'\nexport * from './ox-board-player.js'\nexport * from './ox-board-modeller.js'\nexport * from './ox-board-list.js'\nexport * from './ox-board-template-viewer.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAE1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAGrE,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,wBAAwB,CAAA;AACtC,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA","sourcesContent":["export * from './types.js'\n\nexport { registerDefaultGroups } from './component/register-default-groups.js'\nexport { BoardDataStorage, PlaylistStorage } from './data-storage/data-storage.js'\nexport { BoardModelCache } from './data-storage/board-model-cache.js'\nexport type { CachedBoard } from './data-storage/board-model-cache.js'\n\nexport * from './ox-board-viewer.js'\nexport * from './ox-board-player.js'\nexport * from './ox-board-modeller.js'\nexport * from './ox-board-list.js'\nexport * from './ox-board-template-viewer.js'\n"]}
@@ -12,7 +12,7 @@ export const style = css `
12
12
  [tools] {
13
13
  display: flex;
14
14
  align-items: center;
15
- overflow: none;
15
+ overflow: hidden;
16
16
  padding: 0px 10px;
17
17
  }
18
18
 
@@ -201,6 +201,43 @@ export const style = css `
201
201
  background-position-y: -1593px;
202
202
  }
203
203
 
204
+ #align-z-front {
205
+ background-position-y: -142px;
206
+ filter: hue-rotate(180deg);
207
+ }
208
+
209
+ #align-z-middle {
210
+ background-position-y: -192px;
211
+ filter: hue-rotate(180deg);
212
+ }
213
+
214
+ #align-z-back {
215
+ background-position-y: -242px;
216
+ filter: hue-rotate(180deg);
217
+ }
218
+
219
+ #distribute-z {
220
+ background-position-y: -1593px;
221
+ filter: hue-rotate(180deg);
222
+ }
223
+
224
+ .gizmo-btn {
225
+ background: none !important;
226
+ color: rgba(255, 255, 255, 0.5);
227
+ min-width: 24px;
228
+ display: inline-flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ cursor: pointer;
232
+ border-radius: 3px;
233
+ padding: 2px;
234
+ }
235
+
236
+ .gizmo-btn[disabled] {
237
+ opacity: 0.3;
238
+ pointer-events: none;
239
+ }
240
+
204
241
  #toggle-property {
205
242
  background-position-y: -1392px;
206
243
  }
@@ -1 +1 @@
1
- {"version":3,"file":"edit-toolbar-style.js","sourceRoot":"","sources":["../../../src/modeller/edit-toolbar-style.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiOvB,CAAA","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { css } from 'lit'\n\nexport const style = css`\n :host {\n background-color: var(--edit-toolbar-background-color, var(--md-sys-color-secondary, #394e64));\n\n overflow-x: hidden;\n }\n\n [tools] {\n display: flex;\n align-items: center;\n overflow: none;\n padding: 0px 10px;\n }\n\n [tools] > * {\n padding: 0px;\n }\n\n [tools] > span[button] {\n min-width: 30px;\n }\n\n [tools] > span[padding] {\n flex: 1;\n }\n\n [tools] > .vline {\n display: block;\n flex: none;\n border-left: 1px solid rgba(255, 255, 255, 0.2);\n border-right: 1px solid rgba(0, 0, 0, 0.15);\n width: 0px;\n height: 18px;\n margin: 0 3px;\n }\n\n span[button] {\n min-height: 35px;\n\n background: var(--url-icon-htoolbar) no-repeat;\n background-position-x: 50%;\n opacity: 0.8;\n }\n span[button]:hover {\n opacity: 1;\n background-color: rgba(0, 0, 0, 0.1);\n cursor: pointer;\n }\n\n #fullscreen,\n #toggle-property {\n flex: none;\n }\n\n #align-left {\n background-position-y: 8px;\n }\n\n #align-center {\n background-position-y: -42px;\n }\n\n #align-right {\n background-position-y: -92px;\n }\n\n #align-top {\n background-position-y: -142px;\n }\n\n #align-middle {\n background-position-y: -192px;\n }\n\n #align-bottom {\n background-position-y: -242px;\n }\n\n #undo {\n background-position-y: -592px;\n }\n\n #redo {\n background-position-y: -642px;\n }\n\n #front {\n background-position-y: -292px;\n }\n\n #back {\n background-position-y: -342px;\n }\n\n #forward {\n background-position-y: -392px;\n }\n\n #backward {\n background-position-y: -442px;\n }\n\n #symmetry-x {\n background-position-y: -492px;\n }\n\n #symmetry-y {\n background-position-y: -542px;\n }\n\n #group {\n background-position-y: -492px;\n }\n\n #ungroup {\n background-position-y: -542px;\n }\n\n #fullscreen {\n background-position-y: -692px;\n }\n\n #toggle-property {\n background-position-y: -692px;\n float: right;\n }\n\n #zoomin {\n background-position-y: -742px;\n }\n\n #zoomout {\n background-position-y: -792px;\n }\n\n #fit-scene {\n background-position-y: -1492px;\n }\n\n #cut {\n background-position-y: -842px;\n }\n\n #copy {\n background-position-y: -892px;\n }\n\n #paste {\n background-position-y: -942px;\n }\n\n #delete {\n background-position-y: -992px;\n }\n\n #font-increase {\n background-position-y: -1042px;\n }\n\n #font-decrease {\n background-position-y: -1092px;\n }\n\n #style-copy {\n background-position-y: -1142px;\n }\n\n #databind-copy {\n background-position-y: -1692px;\n }\n\n #context-menu {\n background-position-y: -692px;\n }\n\n #symmetry-x {\n background-position-y: -1192px;\n }\n\n #symmetry-y {\n background-position-y: -1242px;\n }\n\n #rotate-cw {\n background-position-y: -1292px;\n }\n\n #rotate-ccw {\n background-position-y: -1342px;\n }\n\n #distribute-horizontal {\n background-position-y: -1542px;\n }\n\n #distribute-vertical {\n background-position-y: -1593px;\n }\n\n #toggle-property {\n background-position-y: -1392px;\n }\n\n #preview {\n background-position-y: -1640px;\n }\n\n /* bigger buttons */\n #fullscreen {\n background: var(--url-icon-fullscreen) 50% 10px no-repeat;\n width: var(--edit-toolbar-bigger-icon-size);\n height: var(--edit-toolbar-bigger-icon-size);\n border-left: var(--edit-toolbar-bigger-icon-line);\n }\n\n #toggle-property {\n background: var(--url-icon-collapse) 80% 10px no-repeat;\n width: var(--edit-toolbar-bigger-icon-size);\n height: var(--edit-toolbar-bigger-icon-size);\n border-left: var(--edit-toolbar-bigger-icon-line);\n }\n\n #toggle-property[active] {\n background: var(--url-icon-collapse-active) 80% 10px no-repeat;\n }\n`\n"]}
1
+ {"version":3,"file":"edit-toolbar-style.js","sourceRoot":"","sources":["../../../src/modeller/edit-toolbar-style.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsQvB,CAAA","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { css } from 'lit'\n\nexport const style = css`\n :host {\n background-color: var(--edit-toolbar-background-color, var(--md-sys-color-secondary, #394e64));\n\n overflow-x: hidden;\n }\n\n [tools] {\n display: flex;\n align-items: center;\n overflow: hidden;\n padding: 0px 10px;\n }\n\n [tools] > * {\n padding: 0px;\n }\n\n [tools] > span[button] {\n min-width: 30px;\n }\n\n [tools] > span[padding] {\n flex: 1;\n }\n\n [tools] > .vline {\n display: block;\n flex: none;\n border-left: 1px solid rgba(255, 255, 255, 0.2);\n border-right: 1px solid rgba(0, 0, 0, 0.15);\n width: 0px;\n height: 18px;\n margin: 0 3px;\n }\n\n span[button] {\n min-height: 35px;\n\n background: var(--url-icon-htoolbar) no-repeat;\n background-position-x: 50%;\n opacity: 0.8;\n }\n span[button]:hover {\n opacity: 1;\n background-color: rgba(0, 0, 0, 0.1);\n cursor: pointer;\n }\n\n #fullscreen,\n #toggle-property {\n flex: none;\n }\n\n #align-left {\n background-position-y: 8px;\n }\n\n #align-center {\n background-position-y: -42px;\n }\n\n #align-right {\n background-position-y: -92px;\n }\n\n #align-top {\n background-position-y: -142px;\n }\n\n #align-middle {\n background-position-y: -192px;\n }\n\n #align-bottom {\n background-position-y: -242px;\n }\n\n #undo {\n background-position-y: -592px;\n }\n\n #redo {\n background-position-y: -642px;\n }\n\n #front {\n background-position-y: -292px;\n }\n\n #back {\n background-position-y: -342px;\n }\n\n #forward {\n background-position-y: -392px;\n }\n\n #backward {\n background-position-y: -442px;\n }\n\n #symmetry-x {\n background-position-y: -492px;\n }\n\n #symmetry-y {\n background-position-y: -542px;\n }\n\n #group {\n background-position-y: -492px;\n }\n\n #ungroup {\n background-position-y: -542px;\n }\n\n #fullscreen {\n background-position-y: -692px;\n }\n\n #toggle-property {\n background-position-y: -692px;\n float: right;\n }\n\n #zoomin {\n background-position-y: -742px;\n }\n\n #zoomout {\n background-position-y: -792px;\n }\n\n #fit-scene {\n background-position-y: -1492px;\n }\n\n #cut {\n background-position-y: -842px;\n }\n\n #copy {\n background-position-y: -892px;\n }\n\n #paste {\n background-position-y: -942px;\n }\n\n #delete {\n background-position-y: -992px;\n }\n\n #font-increase {\n background-position-y: -1042px;\n }\n\n #font-decrease {\n background-position-y: -1092px;\n }\n\n #style-copy {\n background-position-y: -1142px;\n }\n\n #databind-copy {\n background-position-y: -1692px;\n }\n\n #context-menu {\n background-position-y: -692px;\n }\n\n #symmetry-x {\n background-position-y: -1192px;\n }\n\n #symmetry-y {\n background-position-y: -1242px;\n }\n\n #rotate-cw {\n background-position-y: -1292px;\n }\n\n #rotate-ccw {\n background-position-y: -1342px;\n }\n\n #distribute-horizontal {\n background-position-y: -1542px;\n }\n\n #distribute-vertical {\n background-position-y: -1593px;\n }\n\n #align-z-front {\n background-position-y: -142px;\n filter: hue-rotate(180deg);\n }\n\n #align-z-middle {\n background-position-y: -192px;\n filter: hue-rotate(180deg);\n }\n\n #align-z-back {\n background-position-y: -242px;\n filter: hue-rotate(180deg);\n }\n\n #distribute-z {\n background-position-y: -1593px;\n filter: hue-rotate(180deg);\n }\n\n .gizmo-btn {\n background: none !important;\n color: rgba(255, 255, 255, 0.5);\n min-width: 24px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 3px;\n padding: 2px;\n }\n\n .gizmo-btn[disabled] {\n opacity: 0.3;\n pointer-events: none;\n }\n\n #toggle-property {\n background-position-y: -1392px;\n }\n\n #preview {\n background-position-y: -1640px;\n }\n\n /* bigger buttons */\n #fullscreen {\n background: var(--url-icon-fullscreen) 50% 10px no-repeat;\n width: var(--edit-toolbar-bigger-icon-size);\n height: var(--edit-toolbar-bigger-icon-size);\n border-left: var(--edit-toolbar-bigger-icon-line);\n }\n\n #toggle-property {\n background: var(--url-icon-collapse) 80% 10px no-repeat;\n width: var(--edit-toolbar-bigger-icon-size);\n height: var(--edit-toolbar-bigger-icon-size);\n border-left: var(--edit-toolbar-bigger-icon-line);\n }\n\n #toggle-property[active] {\n background: var(--url-icon-collapse-active) 80% 10px no-repeat;\n }\n`\n"]}
@@ -8,23 +8,9 @@ export declare class EditToolbar extends LitElement {
8
8
  scene?: Scene;
9
9
  selected: any[];
10
10
  hideProperty: boolean;
11
+ private _dimension;
12
+ private _gizmoAttached;
11
13
  private cliped?;
12
- private redo;
13
- private undo;
14
- private fullscreen;
15
- private styleCopy;
16
- private databindCopy;
17
- private cut;
18
- private copy;
19
- private paste;
20
- private delete;
21
- private forward;
22
- private backward;
23
- private front;
24
- private back;
25
- private aligners;
26
- private zorders;
27
- private distributes;
28
14
  firstUpdated(): void;
29
15
  updated(changes: PropertyValues<this>): void;
30
16
  render(): import("lit-html").TemplateResult<1>;
@@ -32,10 +18,14 @@ export declare class EditToolbar extends LitElement {
32
18
  getSymbol(key: string): string;
33
19
  private getShortcutString;
34
20
  onShortcut(e: KeyboardEvent): boolean;
21
+ private _setDisabled;
35
22
  onExecute(command: string, undoable: boolean, redoable: boolean): void;
36
23
  onUndo(undoable: boolean, redoable: boolean): void;
37
24
  onRedo(undoable: boolean, redoable: boolean): void;
38
25
  onSceneChanged(after?: Scene, before?: Scene): void;
26
+ private _onDimensionChanged;
27
+ private _onGizmoAttachChanged;
28
+ private _setGizmoMode;
39
29
  onSelectedChanged(after: Component[], before: Component[]): void;
40
30
  onTapUndo(): void;
41
31
  onTapRedo(): void;
@@ -56,6 +46,8 @@ export declare class EditToolbar extends LitElement {
56
46
  onTapToggle(): void;
57
47
  onTapFitScene(): void;
58
48
  onTapPreview(): void;
49
+ onTapDataBinding(): void;
59
50
  onTapDownloadModel(): void;
51
+ onTapEditModel(): void;
60
52
  onTapDistribute(e: TouchEvent | string): void;
61
53
  }