@operato/board 10.0.0-beta.9 → 10.0.0
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/CHANGELOG.md +530 -0
- package/dist/src/component/container.js +18 -3
- package/dist/src/component/container.js.map +1 -1
- package/dist/src/component/conveyance.d.ts +2 -0
- package/dist/src/component/conveyance.js +38 -0
- package/dist/src/component/conveyance.js.map +1 -0
- package/dist/src/component/etc.js +2 -10
- package/dist/src/component/etc.js.map +1 -1
- package/dist/src/component/facility.d.ts +2 -0
- package/dist/src/component/facility.js +35 -0
- package/dist/src/component/facility.js.map +1 -0
- package/dist/src/component/index.d.ts +5 -0
- package/dist/src/component/index.js +5 -0
- package/dist/src/component/index.js.map +1 -1
- package/dist/src/component/line.js +4 -28
- package/dist/src/component/line.js.map +1 -1
- package/dist/src/component/manufacturing.d.ts +2 -0
- package/dist/src/component/manufacturing.js +41 -0
- package/dist/src/component/manufacturing.js.map +1 -0
- package/dist/src/component/register-default-groups.js +19 -2
- package/dist/src/component/register-default-groups.js.map +1 -1
- package/dist/src/component/shape.js +5 -29
- package/dist/src/component/shape.js.map +1 -1
- package/dist/src/component/storage.d.ts +2 -0
- package/dist/src/component/storage.js +27 -0
- package/dist/src/component/storage.js.map +1 -0
- package/dist/src/component/text-and-media.js +2 -25
- package/dist/src/component/text-and-media.js.map +1 -1
- package/dist/src/component/transport.d.ts +2 -0
- package/dist/src/component/transport.js +36 -0
- package/dist/src/component/transport.js.map +1 -0
- package/dist/src/component/warehouse.d.ts +1 -0
- package/dist/src/component/warehouse.js +8 -1
- package/dist/src/component/warehouse.js.map +1 -1
- package/dist/src/data-storage/board-model-cache.d.ts +30 -0
- package/dist/src/data-storage/board-model-cache.js +93 -0
- package/dist/src/data-storage/board-model-cache.js.map +1 -0
- package/dist/src/graphql/data-subscription.js +5 -1
- package/dist/src/graphql/data-subscription.js.map +1 -1
- package/dist/src/graphql/playback-buffer.d.ts +79 -0
- package/dist/src/graphql/playback-buffer.js +139 -0
- package/dist/src/graphql/playback-buffer.js.map +1 -0
- package/dist/src/graphql/playback-buffer.test.d.ts +1 -0
- package/dist/src/graphql/playback-buffer.test.js +261 -0
- package/dist/src/graphql/playback-buffer.test.js.map +1 -0
- package/dist/src/graphql/playback-subscription.d.ts +33 -18
- package/dist/src/graphql/playback-subscription.js +158 -91
- package/dist/src/graphql/playback-subscription.js.map +1 -1
- 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/modeller/bulk-create-dialog.d.ts +51 -0
- package/dist/src/modeller/bulk-create-dialog.js +531 -0
- package/dist/src/modeller/bulk-create-dialog.js.map +1 -0
- package/dist/src/modeller/bulk-create.d.ts +101 -0
- package/dist/src/modeller/bulk-create.js +199 -0
- package/dist/src/modeller/bulk-create.js.map +1 -0
- package/dist/src/modeller/component-toolbar/component-menu.js +8 -1
- package/dist/src/modeller/component-toolbar/component-menu.js.map +1 -1
- package/dist/src/modeller/edit-toolbar.d.ts +16 -16
- package/dist/src/modeller/edit-toolbar.js +248 -208
- package/dist/src/modeller/edit-toolbar.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 +6 -1
- package/dist/src/modeller/scene-viewer/ox-scene-viewer.js.map +1 -1
- package/dist/src/ox-board-modeller.d.ts +8 -1
- package/dist/src/ox-board-modeller.js +125 -6
- package/dist/src/ox-board-modeller.js.map +1 -1
- package/dist/src/ox-board-preview.d.ts +36 -0
- package/dist/src/ox-board-preview.js +114 -0
- package/dist/src/ox-board-preview.js.map +1 -0
- package/dist/src/ox-board-template-list.d.ts +1 -0
- package/dist/src/ox-board-template-list.js +19 -1
- package/dist/src/ox-board-template-list.js.map +1 -1
- package/dist/src/ox-board-viewer.d.ts +8 -1
- package/dist/src/ox-board-viewer.js +99 -32
- package/dist/src/ox-board-viewer.js.map +1 -1
- package/dist/src/ox-playback-controls.d.ts +8 -0
- package/dist/src/ox-playback-controls.js +114 -18
- package/dist/src/ox-playback-controls.js.map +1 -1
- package/dist/src/selector/ox-board-selector.js +11 -1
- package/dist/src/selector/ox-board-selector.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -16
- package/translations/en.json +19 -1
- package/translations/ja.json +19 -1
- package/translations/ko.json +19 -1
- package/translations/ms.json +19 -1
- package/translations/zh.json +19 -1
|
@@ -3,10 +3,56 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { __decorate } from "tslib";
|
|
5
5
|
import { html, LitElement } from 'lit';
|
|
6
|
-
import { property, state
|
|
6
|
+
import { property, state } from 'lit/decorators.js';
|
|
7
7
|
import { copyToClipboard, isMacOS } from '@operato/utils';
|
|
8
|
+
import { OxPopup } from '@operato/popup';
|
|
8
9
|
import { style } from './edit-toolbar-style.js';
|
|
10
|
+
import './bulk-create-dialog.js';
|
|
9
11
|
const MACOS = isMacOS();
|
|
12
|
+
/**
|
|
13
|
+
* 컴포넌트 model JSON 만으로 type-aware bounding box (width, height) 추출.
|
|
14
|
+
*
|
|
15
|
+
* things-scene 의 component.bounds 는 *path 의 bounding box* 인데, model JSON 만
|
|
16
|
+
* 으로는 path 를 그릴 수 없으므로 type-specific 필드를 직접 본다.
|
|
17
|
+
* - width/height 명시 (Rect, Container 등) → 그대로
|
|
18
|
+
* - rx/ry (Ellipse 등) → 2*rx, 2*ry
|
|
19
|
+
* - points 배열 (Line, Polygon 등) → 점들의 bounding box
|
|
20
|
+
* - 그 외 → 50x50 fallback
|
|
21
|
+
*
|
|
22
|
+
* bulk-paste 가 영역 안에 N 개 복제할 때 *원본 크기를 보존* 하기 위해 사용.
|
|
23
|
+
*/
|
|
24
|
+
function deriveBoundsFromModel(model) {
|
|
25
|
+
if (typeof model.width === 'number' && typeof model.height === 'number') {
|
|
26
|
+
return { width: model.width, height: model.height };
|
|
27
|
+
}
|
|
28
|
+
if (typeof model.rx === 'number' && typeof model.ry === 'number') {
|
|
29
|
+
return { width: 2 * Math.abs(model.rx), height: 2 * Math.abs(model.ry) };
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(model.points) && model.points.length > 0) {
|
|
32
|
+
let minX = Infinity;
|
|
33
|
+
let maxX = -Infinity;
|
|
34
|
+
let minY = Infinity;
|
|
35
|
+
let maxY = -Infinity;
|
|
36
|
+
for (const p of model.points) {
|
|
37
|
+
if (typeof (p === null || p === void 0 ? void 0 : p.x) === 'number') {
|
|
38
|
+
if (p.x < minX)
|
|
39
|
+
minX = p.x;
|
|
40
|
+
if (p.x > maxX)
|
|
41
|
+
maxX = p.x;
|
|
42
|
+
}
|
|
43
|
+
if (typeof (p === null || p === void 0 ? void 0 : p.y) === 'number') {
|
|
44
|
+
if (p.y < minY)
|
|
45
|
+
minY = p.y;
|
|
46
|
+
if (p.y > maxY)
|
|
47
|
+
maxY = p.y;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (isFinite(minX) && isFinite(maxX) && isFinite(minY) && isFinite(maxY)) {
|
|
51
|
+
return { width: maxX - minX, height: maxY - minY };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { width: 50, height: 50 };
|
|
55
|
+
}
|
|
10
56
|
export class EditToolbar extends LitElement {
|
|
11
57
|
constructor() {
|
|
12
58
|
super(...arguments);
|
|
@@ -16,7 +62,6 @@ export class EditToolbar extends LitElement {
|
|
|
16
62
|
this._gizmoAttached = false;
|
|
17
63
|
}
|
|
18
64
|
firstUpdated() {
|
|
19
|
-
var _a, _b;
|
|
20
65
|
this.addEventListener('mousewheel', this.onWheelEvent.bind(this), false);
|
|
21
66
|
// 툴바 버튼 클릭 후 씬으로 포커스를 돌려서 키보드 단축키가 계속 작동하도록 한다
|
|
22
67
|
this.addEventListener('click', () => {
|
|
@@ -33,33 +78,7 @@ export class EditToolbar extends LitElement {
|
|
|
33
78
|
console.error('model paste failed', e);
|
|
34
79
|
}
|
|
35
80
|
});
|
|
36
|
-
|
|
37
|
-
this.zorders.forEach(zorder => zorder.addEventListener('click', this.onTapZorder.bind(this)));
|
|
38
|
-
this.distributes.forEach(distribute => distribute.addEventListener('click', this.onTapDistribute.bind(this)));
|
|
39
|
-
this.undo.addEventListener('click', this.onTapUndo.bind(this));
|
|
40
|
-
this.redo.addEventListener('click', this.onTapRedo.bind(this));
|
|
41
|
-
this.fullscreen.addEventListener('click', this.onTapFullscreen.bind(this));
|
|
42
|
-
this.styleCopy.addEventListener('click', this.onStartStylePasteMode.bind(this));
|
|
43
|
-
this.databindCopy.addEventListener('click', this.onStartDatabindPasteMode.bind(this));
|
|
44
|
-
this.cut.addEventListener('click', this.onTapCut.bind(this));
|
|
45
|
-
this.copy.addEventListener('click', this.onTapCopy.bind(this));
|
|
46
|
-
this.paste.addEventListener('click', this.onTapPaste.bind(this));
|
|
47
|
-
this.delete.addEventListener('click', this.onTapDelete.bind(this));
|
|
48
|
-
this.renderRoot
|
|
49
|
-
.querySelector('#font-increase')
|
|
50
|
-
.addEventListener('click', this.onTapFontIncrease.bind(this));
|
|
51
|
-
this.renderRoot
|
|
52
|
-
.querySelector('#font-decrease')
|
|
53
|
-
.addEventListener('click', this.onTapFontDecrease.bind(this));
|
|
54
|
-
(_a = this.renderRoot.querySelector('#group')) === null || _a === void 0 ? void 0 : _a.addEventListener('click', this.onTapGroup.bind(this));
|
|
55
|
-
(_b = this.renderRoot.querySelector('#ungroup')) === null || _b === void 0 ? void 0 : _b.addEventListener('click', this.onTapUngroup.bind(this));
|
|
56
|
-
this.renderRoot
|
|
57
|
-
.querySelector('#toggle-property')
|
|
58
|
-
.addEventListener('click', this.onTapToggle.bind(this));
|
|
59
|
-
this.renderRoot
|
|
60
|
-
.querySelector('#fit-scene')
|
|
61
|
-
.addEventListener('click', this.onTapFitScene.bind(this));
|
|
62
|
-
this.renderRoot.querySelector('#preview').addEventListener('click', this.onTapPreview.bind(this));
|
|
81
|
+
// 모든 버튼은 템플릿 @click 디렉티브로 바인딩 — 조건부 렌더링 시에도 정상 작동
|
|
63
82
|
}
|
|
64
83
|
updated(changes) {
|
|
65
84
|
changes.has('scene') && this.onSceneChanged(this.scene, changes.get('scene'));
|
|
@@ -70,82 +89,43 @@ export class EditToolbar extends LitElement {
|
|
|
70
89
|
<div tools>
|
|
71
90
|
<span><slot></slot></span>
|
|
72
91
|
|
|
73
|
-
<span button id="undo" title="undo (${this.getShortcutString('cmd', 'z')})"> </span>
|
|
74
|
-
<span button id="redo" title="redo (${this.getShortcutString('cmd', 'shift', 'z')})"> </span>
|
|
92
|
+
<span button id="undo" title="undo (${this.getShortcutString('cmd', 'z')})" @click=${this.onTapUndo}> </span>
|
|
93
|
+
<span button id="redo" title="redo (${this.getShortcutString('cmd', 'shift', 'z')})" @click=${this.onTapRedo}> </span>
|
|
75
94
|
|
|
76
95
|
<span class="vline"></span>
|
|
77
96
|
|
|
78
|
-
<span button id="style-copy" title="style copy (${this.getShortcutString('cmd', '1')})"> </span>
|
|
79
|
-
<span button id="databind-copy" title="databind copy (${this.getShortcutString('cmd', '2')})"> </span>
|
|
97
|
+
<span button id="style-copy" title="style copy (${this.getShortcutString('cmd', '1')})" @click=${this.onStartStylePasteMode}> </span>
|
|
98
|
+
<span button id="databind-copy" title="databind copy (${this.getShortcutString('cmd', '2')})" @click=${this.onStartDatabindPasteMode}> </span>
|
|
80
99
|
|
|
81
100
|
<span class="vline"></span>
|
|
82
101
|
|
|
83
|
-
<span button id="cut" title="cut (${this.getShortcutString('cmd', 'x')})"> </span>
|
|
84
|
-
<span button id="copy" title="copy (${this.getShortcutString('cmd', 'c')})"> </span>
|
|
85
|
-
<span button id="paste" title="paste (${this.getShortcutString('cmd', 'v')})"> </span>
|
|
86
|
-
<span
|
|
87
|
-
button
|
|
88
|
-
id="delete"
|
|
102
|
+
<span button id="cut" title="cut (${this.getShortcutString('cmd', 'x')})" @click=${this.onTapCut}> </span>
|
|
103
|
+
<span button id="copy" title="copy (${this.getShortcutString('cmd', 'c')})" @click=${this.onTapCopy}> </span>
|
|
104
|
+
<span button id="paste" title="paste (${this.getShortcutString('cmd', 'v')})" @click=${this.onTapPaste}> </span>
|
|
105
|
+
<span button id="delete"
|
|
89
106
|
title="delete (${this.getShortcutString('backspace')}, ${this.getShortcutString('delete')})"
|
|
90
|
-
|
|
91
|
-
</span>
|
|
107
|
+
@click=${this.onTapDelete}> </span>
|
|
92
108
|
|
|
93
109
|
<span class="vline"></span>
|
|
94
110
|
|
|
95
|
-
<span
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
</span>
|
|
102
|
-
<span
|
|
103
|
-
button
|
|
104
|
-
data-align="center"
|
|
105
|
-
id="align-center"
|
|
106
|
-
title="align center (${this.getShortcutString('alt', 'shift', 'c')})"
|
|
107
|
-
>
|
|
108
|
-
</span>
|
|
109
|
-
<span
|
|
110
|
-
button
|
|
111
|
-
data-align="right"
|
|
112
|
-
id="align-right"
|
|
113
|
-
title="align right (${this.getShortcutString('alt', 'shift', 'r')})"
|
|
114
|
-
>
|
|
115
|
-
</span>
|
|
116
|
-
|
|
117
|
-
<span button data-align="top" id="align-top" title="align top (${this.getShortcutString('alt', 'shift', 't')})">
|
|
118
|
-
</span>
|
|
119
|
-
<span
|
|
120
|
-
button
|
|
121
|
-
data-align="middle"
|
|
122
|
-
id="align-middle"
|
|
123
|
-
title="align middle (${this.getShortcutString('alt', 'shift', 'm')})"
|
|
124
|
-
>
|
|
125
|
-
</span>
|
|
126
|
-
<span
|
|
127
|
-
button
|
|
128
|
-
data-align="bottom"
|
|
129
|
-
id="align-bottom"
|
|
130
|
-
title="align bottom (${this.getShortcutString('alt', 'shift', 'b')})"
|
|
131
|
-
>
|
|
132
|
-
</span>
|
|
111
|
+
<span button data-align="left" id="align-left"
|
|
112
|
+
title="align left (${this.getShortcutString('alt', 'shift', 'l')})" @click=${this.onTapAlign}> </span>
|
|
113
|
+
<span button data-align="center" id="align-center"
|
|
114
|
+
title="align center (${this.getShortcutString('alt', 'shift', 'c')})" @click=${this.onTapAlign}> </span>
|
|
115
|
+
<span button data-align="right" id="align-right"
|
|
116
|
+
title="align right (${this.getShortcutString('alt', 'shift', 'r')})" @click=${this.onTapAlign}> </span>
|
|
133
117
|
|
|
134
|
-
<span
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</span>
|
|
118
|
+
<span button data-align="top" id="align-top"
|
|
119
|
+
title="align top (${this.getShortcutString('alt', 'shift', 't')})" @click=${this.onTapAlign}> </span>
|
|
120
|
+
<span button data-align="middle" id="align-middle"
|
|
121
|
+
title="align middle (${this.getShortcutString('alt', 'shift', 'm')})" @click=${this.onTapAlign}> </span>
|
|
122
|
+
<span button data-align="bottom" id="align-bottom"
|
|
123
|
+
title="align bottom (${this.getShortcutString('alt', 'shift', 'b')})" @click=${this.onTapAlign}> </span>
|
|
141
124
|
|
|
142
|
-
<span
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
title="distribute vertically (${this.getShortcutString('alt', 'shift', 'v')})"
|
|
147
|
-
>
|
|
148
|
-
</span>
|
|
125
|
+
<span button data-distribute="HORIZONTAL" id="distribute-horizontal"
|
|
126
|
+
title="distribute horizontally (${this.getShortcutString('alt', 'shift', 'h')})" @click=${this.onTapDistribute}> </span>
|
|
127
|
+
<span button data-distribute="VERTICAL" id="distribute-vertical"
|
|
128
|
+
title="distribute vertically (${this.getShortcutString('alt', 'shift', 'v')})" @click=${this.onTapDistribute}> </span>
|
|
149
129
|
|
|
150
130
|
${this._dimension === '3d'
|
|
151
131
|
? html `
|
|
@@ -160,42 +140,22 @@ export class EditToolbar extends LitElement {
|
|
|
160
140
|
? html `
|
|
161
141
|
<span class="vline"></span>
|
|
162
142
|
|
|
163
|
-
<span
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
button
|
|
172
|
-
id="back"
|
|
173
|
-
data-zorder="back"
|
|
174
|
-
title="send to back (${this.getShortcutString('cmd', 'shift', 'b')})"
|
|
175
|
-
>
|
|
176
|
-
</span>
|
|
177
|
-
<span
|
|
178
|
-
button
|
|
179
|
-
id="forward"
|
|
180
|
-
data-zorder="forward"
|
|
181
|
-
title="bring forward (${this.getShortcutString('cmd', 'f')})"
|
|
182
|
-
>
|
|
183
|
-
</span>
|
|
184
|
-
<span
|
|
185
|
-
button
|
|
186
|
-
id="backward"
|
|
187
|
-
data-zorder="backward"
|
|
188
|
-
title="send backward (${this.getShortcutString('cmd', 'b')})"
|
|
189
|
-
>
|
|
190
|
-
</span>
|
|
143
|
+
<span button id="front" data-zorder="front"
|
|
144
|
+
title="bring to front (${this.getShortcutString('cmd', 'shift', 'f')})" @click=${this.onTapZorder}> </span>
|
|
145
|
+
<span button id="back" data-zorder="back"
|
|
146
|
+
title="send to back (${this.getShortcutString('cmd', 'shift', 'b')})" @click=${this.onTapZorder}> </span>
|
|
147
|
+
<span button id="forward" data-zorder="forward"
|
|
148
|
+
title="bring forward (${this.getShortcutString('cmd', 'f')})" @click=${this.onTapZorder}> </span>
|
|
149
|
+
<span button id="backward" data-zorder="backward"
|
|
150
|
+
title="send backward (${this.getShortcutString('cmd', 'b')})" @click=${this.onTapZorder}> </span>
|
|
191
151
|
`
|
|
192
152
|
: ''}
|
|
193
153
|
|
|
194
154
|
${this._dimension === '2d' ? html `
|
|
195
155
|
<span class="vline"></span>
|
|
196
156
|
|
|
197
|
-
<span button id="group" title="group (${this.getShortcutString('cmd', 'g')})"> </span>
|
|
198
|
-
<span button id="ungroup" title="ungroup (${this.getShortcutString('cmd', 'shift', 'g')})"> </span>
|
|
157
|
+
<span button id="group" title="group (${this.getShortcutString('cmd', 'g')})" @click=${this.onTapGroup}> </span>
|
|
158
|
+
<span button id="ungroup" title="ungroup (${this.getShortcutString('cmd', 'shift', 'g')})" @click=${this.onTapUngroup}> </span>
|
|
199
159
|
` : this._dimension === '3d' ? html `
|
|
200
160
|
<span class="vline"></span>
|
|
201
161
|
|
|
@@ -228,27 +188,23 @@ export class EditToolbar extends LitElement {
|
|
|
228
188
|
|
|
229
189
|
<span class="vline"></span>
|
|
230
190
|
|
|
231
|
-
<span button id="font-increase" title="increase font size"></span>
|
|
232
|
-
<span button id="font-decrease" title="decrease font size" style="scale: 0.7;"></span>
|
|
191
|
+
<span button id="font-increase" title="increase font size" @click=${this.onTapFontIncrease}></span>
|
|
192
|
+
<span button id="font-decrease" title="decrease font size" style="scale: 0.7;" @click=${this.onTapFontDecrease}></span>
|
|
233
193
|
|
|
234
194
|
<span class="vline"></span>
|
|
235
195
|
<span padding></span>
|
|
236
196
|
|
|
237
|
-
<span button id="fit-scene" title="fit scene (${this.getShortcutString('cmd', 'd')})"> </span>
|
|
197
|
+
<span button id="fit-scene" title="fit scene (${this.getShortcutString('cmd', 'd')})" @click=${this.onTapFitScene}> </span>
|
|
238
198
|
|
|
239
199
|
<span class="vline"></span>
|
|
240
200
|
|
|
241
|
-
<span button id="preview" title="preview (${this.getShortcutString('ctrl', 'p')})"> </span>
|
|
201
|
+
<span button id="preview" title="preview (${this.getShortcutString('ctrl', 'p')})" @click=${this.onTapPreview}> </span>
|
|
242
202
|
|
|
243
|
-
<span button id="fullscreen" title="fullscreen (${this.getShortcutString('f11')})"> </span>
|
|
203
|
+
<span button id="fullscreen" title="fullscreen (${this.getShortcutString('f11')})" @click=${this.onTapFullscreen}> </span>
|
|
244
204
|
|
|
245
|
-
<span
|
|
246
|
-
button
|
|
247
|
-
id="toggle-property"
|
|
205
|
+
<span button id="toggle-property"
|
|
248
206
|
title="toggle property panel (${this.getShortcutString('cmd', 'h')})"
|
|
249
|
-
toggles="true"
|
|
250
|
-
>
|
|
251
|
-
</span>
|
|
207
|
+
toggles="true" @click=${this.onTapToggle}> </span>
|
|
252
208
|
</div>
|
|
253
209
|
`;
|
|
254
210
|
}
|
|
@@ -329,7 +285,12 @@ export class EditToolbar extends LitElement {
|
|
|
329
285
|
this.onTapCut();
|
|
330
286
|
break;
|
|
331
287
|
case 'KeyV':
|
|
332
|
-
if (ctrlKey && !
|
|
288
|
+
if (ctrlKey && shiftKey && !altKey) {
|
|
289
|
+
// Ctrl+Shift+V = Bulk Paste — 클립보드의 *단일* 컴포넌트를 N개로 일괄 붙여넣기.
|
|
290
|
+
this.onBulkPaste();
|
|
291
|
+
defaultPrevent = true;
|
|
292
|
+
}
|
|
293
|
+
else if (ctrlKey && !shiftKey) {
|
|
333
294
|
this.onTapPaste();
|
|
334
295
|
defaultPrevent = false;
|
|
335
296
|
}
|
|
@@ -349,21 +310,35 @@ export class EditToolbar extends LitElement {
|
|
|
349
310
|
this.onTapUngroup();
|
|
350
311
|
}
|
|
351
312
|
break;
|
|
352
|
-
case '
|
|
313
|
+
case 'BracketRight':
|
|
314
|
+
// Ctrl+] = Bring Forward, Ctrl+Shift+] = Bring to Front
|
|
315
|
+
// (Photoshop / Illustrator 표준 컨벤션)
|
|
353
316
|
if (this._dimension === '2d') {
|
|
354
|
-
if (ctrlKey && !shiftKey)
|
|
317
|
+
if (ctrlKey && !shiftKey) {
|
|
355
318
|
(_a = this.scene) === null || _a === void 0 ? void 0 : _a.zorder('forward');
|
|
356
|
-
|
|
319
|
+
defaultPrevent = true;
|
|
320
|
+
}
|
|
321
|
+
else if (ctrlKey && shiftKey) {
|
|
357
322
|
(_b = this.scene) === null || _b === void 0 ? void 0 : _b.zorder('front');
|
|
323
|
+
defaultPrevent = true;
|
|
324
|
+
}
|
|
358
325
|
}
|
|
359
326
|
break;
|
|
360
|
-
case '
|
|
327
|
+
case 'BracketLeft':
|
|
328
|
+
// Ctrl+[ = Send Backward, Ctrl+Shift+[ = Send to Back
|
|
361
329
|
if (this._dimension === '2d') {
|
|
362
|
-
if (ctrlKey && !shiftKey)
|
|
330
|
+
if (ctrlKey && !shiftKey) {
|
|
363
331
|
(_c = this.scene) === null || _c === void 0 ? void 0 : _c.zorder('backward');
|
|
364
|
-
|
|
332
|
+
defaultPrevent = true;
|
|
333
|
+
}
|
|
334
|
+
else if (ctrlKey && shiftKey) {
|
|
365
335
|
(_d = this.scene) === null || _d === void 0 ? void 0 : _d.zorder('back');
|
|
336
|
+
defaultPrevent = true;
|
|
337
|
+
}
|
|
366
338
|
}
|
|
339
|
+
break;
|
|
340
|
+
case 'KeyB':
|
|
341
|
+
// zorder 는 BracketLeft 로 이동. KeyB 의 Alt+Shift+B = align bottom 만 유지.
|
|
367
342
|
if (altKey && shiftKey)
|
|
368
343
|
this.onTapAlign('bottom');
|
|
369
344
|
break;
|
|
@@ -408,11 +383,23 @@ export class EditToolbar extends LitElement {
|
|
|
408
383
|
this.onTapFitScene();
|
|
409
384
|
defaultPrevent = true;
|
|
410
385
|
}
|
|
386
|
+
else if (!shiftKey && !altKey) {
|
|
387
|
+
const target = e.composedPath()[0];
|
|
388
|
+
const tagName = target.tagName;
|
|
389
|
+
if (!target.isContentEditable && tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA') {
|
|
390
|
+
this.onTapDataBinding();
|
|
391
|
+
defaultPrevent = true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
411
394
|
break;
|
|
412
395
|
case 'KeyE':
|
|
413
396
|
if (ctrlKey && shiftKey)
|
|
414
397
|
this.onTapDownloadModel();
|
|
415
398
|
break;
|
|
399
|
+
case 'F10':
|
|
400
|
+
this.onTapEditModel();
|
|
401
|
+
defaultPrevent = true;
|
|
402
|
+
break;
|
|
416
403
|
case 'Digit1':
|
|
417
404
|
if (ctrlKey) {
|
|
418
405
|
defaultPrevent = true;
|
|
@@ -435,19 +422,24 @@ export class EditToolbar extends LitElement {
|
|
|
435
422
|
e.preventDefault();
|
|
436
423
|
return true;
|
|
437
424
|
}
|
|
425
|
+
_setDisabled(id, disabled) {
|
|
426
|
+
const el = this.renderRoot.querySelector(`#${id}`);
|
|
427
|
+
disabled ? el === null || el === void 0 ? void 0 : el.setAttribute('disabled', '') : el === null || el === void 0 ? void 0 : el.removeAttribute('disabled');
|
|
428
|
+
}
|
|
438
429
|
onExecute(command, undoable, redoable) {
|
|
439
|
-
|
|
440
|
-
|
|
430
|
+
this._setDisabled('undo', !undoable);
|
|
431
|
+
this._setDisabled('redo', !redoable);
|
|
441
432
|
}
|
|
442
433
|
onUndo(undoable, redoable) {
|
|
443
|
-
|
|
444
|
-
|
|
434
|
+
this._setDisabled('undo', !undoable);
|
|
435
|
+
this._setDisabled('redo', !redoable);
|
|
445
436
|
}
|
|
446
437
|
onRedo(undoable, redoable) {
|
|
447
|
-
|
|
448
|
-
|
|
438
|
+
this._setDisabled('undo', !undoable);
|
|
439
|
+
this._setDisabled('redo', !redoable);
|
|
449
440
|
}
|
|
450
441
|
onSceneChanged(after, before) {
|
|
442
|
+
var _a, _b;
|
|
451
443
|
if (before) {
|
|
452
444
|
before.off('execute', this.onExecute, this);
|
|
453
445
|
before.off('undo', this.onUndo, this);
|
|
@@ -461,6 +453,9 @@ export class EditToolbar extends LitElement {
|
|
|
461
453
|
after.on('redo', this.onRedo, this);
|
|
462
454
|
after.on('dimension', this._onDimensionChanged, this);
|
|
463
455
|
after.on('gizmoattach', this._onGizmoAttachChanged, this);
|
|
456
|
+
// scene 설정 시 현재 dimension을 즉시 반영 (초기 이벤트를 놓친 경우 대비)
|
|
457
|
+
const threed = (_b = (_a = after.model_layer) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.threed;
|
|
458
|
+
this._dimension = threed ? '3d' : '2d';
|
|
464
459
|
}
|
|
465
460
|
}
|
|
466
461
|
_onDimensionChanged(dimension) {
|
|
@@ -475,27 +470,23 @@ export class EditToolbar extends LitElement {
|
|
|
475
470
|
(_b = (_a = this.scene) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.setGizmoMode(mode);
|
|
476
471
|
}
|
|
477
472
|
onSelectedChanged(after, before) {
|
|
478
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
479
473
|
var hasSelection = after.length > 0;
|
|
480
474
|
var alignable = after.length > 1;
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
hasSelection ? btn === null || btn === void 0 ? void 0 : btn.removeAttribute('disabled') : btn === null || btn === void 0 ? void 0 : btn.setAttribute('disabled', '');
|
|
485
|
-
});
|
|
486
|
-
this.aligners.forEach(aligner => alignable ? aligner.removeAttribute('disabled') : aligner.setAttribute('disabled', ''));
|
|
475
|
+
['style-copy', 'databind-copy', 'cut', 'copy', 'delete'].forEach(id => this._setDisabled(id, !hasSelection));
|
|
476
|
+
// 정렬 버튼
|
|
477
|
+
this.renderRoot.querySelectorAll('[data-align]').forEach(el => alignable ? el.removeAttribute('disabled') : el.setAttribute('disabled', ''));
|
|
487
478
|
if (this._dimension === '2d') {
|
|
488
479
|
var movable = after.length === 1;
|
|
489
480
|
/* forward, backward 이동은 한 컴포넌트만 가능하다. */
|
|
490
|
-
|
|
491
|
-
|
|
481
|
+
this._setDisabled('forward', !movable);
|
|
482
|
+
this._setDisabled('backward', !movable);
|
|
492
483
|
/* 여러 컴포넌트는 front, back 이동이 가능하다. */
|
|
493
|
-
|
|
494
|
-
|
|
484
|
+
this._setDisabled('front', !(alignable || movable));
|
|
485
|
+
this._setDisabled('back', !(alignable || movable));
|
|
495
486
|
}
|
|
496
|
-
|
|
487
|
+
// 분배 버튼
|
|
488
|
+
this.renderRoot.querySelectorAll('[data-distribute]').forEach(el => alignable ? el.removeAttribute('disabled') : el.setAttribute('disabled', ''));
|
|
497
489
|
if (this._dimension === '3d') {
|
|
498
|
-
const hasSelection = after.length > 0;
|
|
499
490
|
this.renderRoot.querySelectorAll('.gizmo-btn').forEach(btn => {
|
|
500
491
|
hasSelection ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', '');
|
|
501
492
|
});
|
|
@@ -547,6 +538,97 @@ export class EditToolbar extends LitElement {
|
|
|
547
538
|
var _a;
|
|
548
539
|
(_a = this.scene) === null || _a === void 0 ? void 0 : _a.remove();
|
|
549
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* 멀티 컴포넌트 일괄 붙여넣기 — Ctrl+Shift+V 진입점.
|
|
543
|
+
*
|
|
544
|
+
* 흐름:
|
|
545
|
+
* 1. 클립보드 (this.cliped) 검증 — *단일* 컴포넌트만 허용
|
|
546
|
+
* 2. scene.startBulkCreateMode → AddLayer 가 캔버스 위에 드래그 박스 그림
|
|
547
|
+
* 3. 사용자가 영역 드래그 → Promise resolve({area, template})
|
|
548
|
+
* ESC/취소 → Promise resolve(null)
|
|
549
|
+
* 4. 영역이 결정되면 dialog 오픈
|
|
550
|
+
*/
|
|
551
|
+
async onBulkPaste() {
|
|
552
|
+
var _a;
|
|
553
|
+
if (!this.cliped) {
|
|
554
|
+
console.warn('bulk-paste: 먼저 Ctrl+C 로 컴포넌트를 복사하세요.');
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
let parsed;
|
|
558
|
+
try {
|
|
559
|
+
parsed = JSON.parse(this.cliped);
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
console.warn('bulk-paste: 클립보드 파싱 실패', e);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
566
|
+
return;
|
|
567
|
+
if (parsed.length > 1) {
|
|
568
|
+
console.warn('bulk-paste: 단일 컴포넌트만 일괄 붙여넣기 가능. 다중은 일반 paste 사용.');
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const template = parsed[0];
|
|
572
|
+
// bulk 모드 임을 시각 구분하기 위해 파란 점선 (일반 add 의 검정과 색만 차별).
|
|
573
|
+
const result = await ((_a = this.scene) === null || _a === void 0 ? void 0 : _a.startBulkCreateMode(template, { boxStrokeStyle: '#2964c2' }));
|
|
574
|
+
if (result) {
|
|
575
|
+
this._openBulkCreateDialog(template, result.area);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
_openBulkCreateDialog(template, initialArea) {
|
|
579
|
+
var _a;
|
|
580
|
+
const dialogEl = document.createElement('bulk-create-dialog');
|
|
581
|
+
dialogEl.template = template;
|
|
582
|
+
dialogEl.initialArea = initialArea;
|
|
583
|
+
// scene 의 모든 id 수집 — indexMap 기반 scene.ids 대신 트리 walk 로 안정성 확보.
|
|
584
|
+
const existingIds = new Set();
|
|
585
|
+
const collectIds = (comp) => {
|
|
586
|
+
var _a, _b, _c;
|
|
587
|
+
if (!comp)
|
|
588
|
+
return;
|
|
589
|
+
const id = (_b = (_a = comp === null || comp === void 0 ? void 0 : comp.state) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : (_c = comp === null || comp === void 0 ? void 0 : comp.model) === null || _c === void 0 ? void 0 : _c.id;
|
|
590
|
+
if (typeof id === 'string' && id)
|
|
591
|
+
existingIds.add(id);
|
|
592
|
+
const children = comp.components || [];
|
|
593
|
+
for (const c of children)
|
|
594
|
+
collectIds(c);
|
|
595
|
+
};
|
|
596
|
+
const root = (_a = this.scene) === null || _a === void 0 ? void 0 : _a.root;
|
|
597
|
+
if (root)
|
|
598
|
+
collectIds(root);
|
|
599
|
+
dialogEl.existingIds = existingIds;
|
|
600
|
+
dialogEl.onConfirm = (models) => {
|
|
601
|
+
const scene = this.scene;
|
|
602
|
+
if (!scene || !models.length) {
|
|
603
|
+
popup === null || popup === void 0 ? void 0 : popup.close();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
// cliped 의 template model 자체에서 type-aware bounds 추출. things-scene 의
|
|
607
|
+
// component.bounds (= path bounding box) 와 동일 결과를 model JSON 의
|
|
608
|
+
// type-specific 필드 (width/height, rx/ry, points) 만으로 계산.
|
|
609
|
+
const bounds = deriveBoundsFromModel(template);
|
|
610
|
+
scene.undoableChange(() => {
|
|
611
|
+
for (const m of models) {
|
|
612
|
+
const cx = m.left + m.width / 2;
|
|
613
|
+
const cy = m.top + m.height / 2;
|
|
614
|
+
scene.add(m, {
|
|
615
|
+
left: cx - bounds.width / 2,
|
|
616
|
+
top: cy - bounds.height / 2,
|
|
617
|
+
width: bounds.width,
|
|
618
|
+
height: bounds.height
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
popup === null || popup === void 0 ? void 0 : popup.close();
|
|
623
|
+
};
|
|
624
|
+
dialogEl.onCancel = () => {
|
|
625
|
+
popup === null || popup === void 0 ? void 0 : popup.close();
|
|
626
|
+
};
|
|
627
|
+
const popup = OxPopup.open({
|
|
628
|
+
template: dialogEl,
|
|
629
|
+
backdrop: true
|
|
630
|
+
});
|
|
631
|
+
}
|
|
550
632
|
onTapSelectAll() {
|
|
551
633
|
var _a;
|
|
552
634
|
(_a = this.scene) === null || _a === void 0 ? void 0 : _a.select('(child)');
|
|
@@ -638,9 +720,15 @@ export class EditToolbar extends LitElement {
|
|
|
638
720
|
onTapPreview() {
|
|
639
721
|
this.dispatchEvent(new CustomEvent('open-preview'));
|
|
640
722
|
}
|
|
723
|
+
onTapDataBinding() {
|
|
724
|
+
this.dispatchEvent(new CustomEvent('open-data-binding'));
|
|
725
|
+
}
|
|
641
726
|
onTapDownloadModel() {
|
|
642
727
|
this.dispatchEvent(new CustomEvent('download-model'));
|
|
643
728
|
}
|
|
729
|
+
onTapEditModel() {
|
|
730
|
+
this.dispatchEvent(new CustomEvent('edit-model'));
|
|
731
|
+
}
|
|
644
732
|
onTapDistribute(e) {
|
|
645
733
|
if (!this.scene)
|
|
646
734
|
return;
|
|
@@ -668,52 +756,4 @@ __decorate([
|
|
|
668
756
|
__decorate([
|
|
669
757
|
state()
|
|
670
758
|
], EditToolbar.prototype, "_gizmoAttached", void 0);
|
|
671
|
-
__decorate([
|
|
672
|
-
query('#redo')
|
|
673
|
-
], EditToolbar.prototype, "redo", void 0);
|
|
674
|
-
__decorate([
|
|
675
|
-
query('#undo')
|
|
676
|
-
], EditToolbar.prototype, "undo", void 0);
|
|
677
|
-
__decorate([
|
|
678
|
-
query('#fullscreen')
|
|
679
|
-
], EditToolbar.prototype, "fullscreen", void 0);
|
|
680
|
-
__decorate([
|
|
681
|
-
query('#style-copy')
|
|
682
|
-
], EditToolbar.prototype, "styleCopy", void 0);
|
|
683
|
-
__decorate([
|
|
684
|
-
query('#databind-copy')
|
|
685
|
-
], EditToolbar.prototype, "databindCopy", void 0);
|
|
686
|
-
__decorate([
|
|
687
|
-
query('#cut')
|
|
688
|
-
], EditToolbar.prototype, "cut", void 0);
|
|
689
|
-
__decorate([
|
|
690
|
-
query('#copy')
|
|
691
|
-
], EditToolbar.prototype, "copy", void 0);
|
|
692
|
-
__decorate([
|
|
693
|
-
query('#paste')
|
|
694
|
-
], EditToolbar.prototype, "paste", void 0);
|
|
695
|
-
__decorate([
|
|
696
|
-
query('#delete')
|
|
697
|
-
], EditToolbar.prototype, "delete", void 0);
|
|
698
|
-
__decorate([
|
|
699
|
-
query('#forward')
|
|
700
|
-
], EditToolbar.prototype, "forward", void 0);
|
|
701
|
-
__decorate([
|
|
702
|
-
query('#backward')
|
|
703
|
-
], EditToolbar.prototype, "backward", void 0);
|
|
704
|
-
__decorate([
|
|
705
|
-
query('#front')
|
|
706
|
-
], EditToolbar.prototype, "front", void 0);
|
|
707
|
-
__decorate([
|
|
708
|
-
query('#back')
|
|
709
|
-
], EditToolbar.prototype, "back", void 0);
|
|
710
|
-
__decorate([
|
|
711
|
-
queryAll('[data-align]')
|
|
712
|
-
], EditToolbar.prototype, "aligners", void 0);
|
|
713
|
-
__decorate([
|
|
714
|
-
queryAll('[data-zorder]')
|
|
715
|
-
], EditToolbar.prototype, "zorders", void 0);
|
|
716
|
-
__decorate([
|
|
717
|
-
queryAll('[data-distribute]')
|
|
718
|
-
], EditToolbar.prototype, "distributes", void 0);
|
|
719
759
|
//# sourceMappingURL=edit-toolbar.js.map
|