@mhmttosun/dnd 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +327 -2
- package/css/answer-key.css +200 -0
- package/css/editor.css +1797 -0
- package/css/player.css +782 -0
- package/css/stage.css +490 -0
- package/dist/dnd-player-element.js +1758 -0
- package/dist/dnd.js +2041 -0
- package/dist/editor.css +3278 -0
- package/dist/editor.d.ts +23 -0
- package/dist/editor.js +8506 -0
- package/dist/index.d.ts +102 -0
- package/dist/player.css +782 -0
- package/dist/player.d.ts +1 -0
- package/dist/player.js +2041 -0
- package/dist/player.umd.cjs +1758 -0
- package/index.html +203 -0
- package/js/app.js +188 -0
- package/js/core/AnswerKeyManager.js +494 -0
- package/js/core/EditorCore.js +220 -0
- package/js/core/HistoryManager.js +194 -0
- package/js/core/LayerManager.js +86 -0
- package/js/core/ObjectManager.js +468 -0
- package/js/core/SelectionManager.js +459 -0
- package/js/core/StageManager.js +295 -0
- package/js/data/SampleProjects.js +883 -0
- package/js/engine/TransformEngine.js +632 -0
- package/js/engine/ValidationEngine.js +147 -0
- package/js/player/ActivityEngine.js +1272 -0
- package/js/player/ExportManager.js +820 -0
- package/js/ui/MiniToolbar.js +1546 -0
- package/js/ui/PropertyPanel.js +776 -0
- package/js/ui/UIManager.js +596 -0
- package/js/utils/AudioSynthesizer.js +111 -0
- package/package.json +69 -4
- package/src/ActivityPlayer.js +309 -0
- package/src/DNDEditor.js +217 -0
- package/src/components/DNDPlayerElement.js +191 -0
- package/src/index.js +12 -0
- package/src/player.js +13 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectionManager - Tekli ve çoklu nesne seçimi, seçim çerçevesi,
|
|
3
|
+
* 8 yönlü boyutlandırma tutamaçları ve döndürme tutamacını yönetir.
|
|
4
|
+
*/
|
|
5
|
+
class SelectionManager {
|
|
6
|
+
constructor(core, stageManager, objectManager, stageEl, options = {}) {
|
|
7
|
+
this.core = core;
|
|
8
|
+
this.stageManager = stageManager;
|
|
9
|
+
this.objectManager = objectManager;
|
|
10
|
+
this.stage = stageEl;
|
|
11
|
+
this.signal = options.signal;
|
|
12
|
+
|
|
13
|
+
this.selectedIds = new Set();
|
|
14
|
+
this.clipboard = [];
|
|
15
|
+
|
|
16
|
+
this.selectionOverlay = stageEl.querySelector('#selection-overlay');
|
|
17
|
+
this.init();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
init() {
|
|
21
|
+
this.setupEventListeners();
|
|
22
|
+
this.core.on('projectLoaded', () => this.clearSelection());
|
|
23
|
+
this.core.on('objectDeleted', (e) => this.deselect(e.object.id));
|
|
24
|
+
this.core.on('objectUpdated', () => this.updateSelectionBox());
|
|
25
|
+
this.core.on('modeChanged', () => {
|
|
26
|
+
if (this.core.getMode() !== 'design') {
|
|
27
|
+
this.clearSelection();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setupEventListeners() {
|
|
33
|
+
// Klavye kısayolları
|
|
34
|
+
window.addEventListener('keydown', (e) => {
|
|
35
|
+
// Eğer kullanıcı bir input, textarea veya contenteditable içindeyse dinleme
|
|
36
|
+
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement?.tagName) ||
|
|
37
|
+
document.activeElement?.isContentEditable) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.core.getMode() !== 'design') return;
|
|
42
|
+
|
|
43
|
+
// Silme: Delete veya Backspace
|
|
44
|
+
if (e.key === 'Delete' || e.key === 'Backspace') {
|
|
45
|
+
if (this.selectedIds.size > 0) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
this.deleteSelected();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Kopyala: Ctrl+C / Cmd+C
|
|
52
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
this.copy();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Yapıştır: Ctrl+V / Cmd+V
|
|
58
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'v') {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
this.paste();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Grupla: Ctrl+G / Cmd+G
|
|
64
|
+
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key.toLowerCase() === 'g') {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
this.groupSelected();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Grubu Çöz: Ctrl+Shift+G / Cmd+Shift+G
|
|
70
|
+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === 'g') {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
this.ungroupSelected();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Çoğalt: Ctrl+D / Cmd+D
|
|
76
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'd') {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
this.duplicateSelected();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Tümünü Seç: Ctrl+A / Cmd+A
|
|
82
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'a') {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
this.selectAll();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Seçimi Bırak: Escape
|
|
88
|
+
if (e.key === 'Escape') {
|
|
89
|
+
this.clearSelection();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Enter: Seçili nesneyi yerinde düzenlemeye başla
|
|
93
|
+
if (e.key === 'Enter' && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
|
94
|
+
if (this.selectedIds.size === 1) {
|
|
95
|
+
const primary = this.getPrimarySelected();
|
|
96
|
+
if (primary && !primary.locked) {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
this.core.emit('requestInlineTextEdit', { objectId: primary.id });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Ok Tuşlarıyla Kaydırma (Nudge)
|
|
104
|
+
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
|
105
|
+
if (this.selectedIds.size > 0) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
const step = e.shiftKey ? 10 : 1;
|
|
108
|
+
let dx = 0;
|
|
109
|
+
let dy = 0;
|
|
110
|
+
if (e.key === 'ArrowUp') dy = -step;
|
|
111
|
+
if (e.key === 'ArrowDown') dy = step;
|
|
112
|
+
if (e.key === 'ArrowLeft') dx = -step;
|
|
113
|
+
if (e.key === 'ArrowRight') dx = step;
|
|
114
|
+
|
|
115
|
+
this.nudgeSelected(dx, dy);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}, this.signal ? { signal: this.signal } : undefined);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
select(id, multi = false) {
|
|
122
|
+
if (this.core.getMode() !== 'design') return;
|
|
123
|
+
const obj = this.objectManager.getObject(id);
|
|
124
|
+
if (!obj) return;
|
|
125
|
+
|
|
126
|
+
if (!multi) {
|
|
127
|
+
this.selectedIds.clear();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Eğer nesne bir gruba aitse, grubun tüm üyelerini seç
|
|
131
|
+
if (obj.groupId) {
|
|
132
|
+
const groupMembers = this.objectManager.getObjects().filter(o => o.groupId === obj.groupId);
|
|
133
|
+
groupMembers.forEach(m => this.selectedIds.add(m.id));
|
|
134
|
+
} else {
|
|
135
|
+
this.selectedIds.add(id);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.updateSelectionBox();
|
|
139
|
+
this.core.emit('selectionChanged', {
|
|
140
|
+
selectedIds: Array.from(this.selectedIds),
|
|
141
|
+
primaryObject: obj
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
toggleSelect(id) {
|
|
146
|
+
const obj = this.objectManager.getObject(id);
|
|
147
|
+
if (!obj) return;
|
|
148
|
+
|
|
149
|
+
if (obj.groupId) {
|
|
150
|
+
const groupMembers = this.objectManager.getObjects().filter(o => o.groupId === obj.groupId);
|
|
151
|
+
const isAnySelected = groupMembers.some(m => this.selectedIds.has(m.id));
|
|
152
|
+
if (isAnySelected) {
|
|
153
|
+
groupMembers.forEach(m => this.selectedIds.delete(m.id));
|
|
154
|
+
} else {
|
|
155
|
+
groupMembers.forEach(m => this.selectedIds.add(m.id));
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
if (this.selectedIds.has(id)) {
|
|
159
|
+
this.deselect(id);
|
|
160
|
+
} else {
|
|
161
|
+
this.select(id, true);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
this.updateSelectionBox();
|
|
165
|
+
this.core.emit('selectionChanged', {
|
|
166
|
+
selectedIds: Array.from(this.selectedIds),
|
|
167
|
+
primaryObject: this.getPrimarySelected()
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
deselect(id) {
|
|
172
|
+
this.selectedIds.delete(id);
|
|
173
|
+
this.updateSelectionBox();
|
|
174
|
+
this.core.emit('selectionChanged', {
|
|
175
|
+
selectedIds: Array.from(this.selectedIds),
|
|
176
|
+
primaryObject: this.getPrimarySelected()
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
clearSelection() {
|
|
181
|
+
if (this.selectedIds.size === 0) return;
|
|
182
|
+
this.selectedIds.clear();
|
|
183
|
+
this.updateSelectionBox();
|
|
184
|
+
this.core.emit('selectionChanged', { selectedIds: [], primaryObject: null });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
selectAll() {
|
|
188
|
+
this.selectedIds.clear();
|
|
189
|
+
this.objectManager.getObjects().forEach(obj => {
|
|
190
|
+
if (!obj.locked && obj.visible !== false) {
|
|
191
|
+
this.selectedIds.add(obj.id);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
this.updateSelectionBox();
|
|
195
|
+
this.core.emit('selectionChanged', {
|
|
196
|
+
selectedIds: Array.from(this.selectedIds),
|
|
197
|
+
primaryObject: this.getPrimarySelected()
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
getSelectedObjects() {
|
|
202
|
+
return Array.from(this.selectedIds)
|
|
203
|
+
.map(id => this.objectManager.getObject(id))
|
|
204
|
+
.filter(Boolean);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
getPrimarySelected() {
|
|
208
|
+
if (this.selectedIds.size === 0) return null;
|
|
209
|
+
const firstId = this.selectedIds.values().next().value;
|
|
210
|
+
return this.objectManager.getObject(firstId) || null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
groupSelected() {
|
|
214
|
+
const selected = this.getSelectedObjects();
|
|
215
|
+
if (selected.length < 2) return null;
|
|
216
|
+
|
|
217
|
+
const newGroupId = 'grp_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 6);
|
|
218
|
+
selected.forEach(o => {
|
|
219
|
+
this.objectManager.updateObject(o.id, { groupId: newGroupId }, false);
|
|
220
|
+
});
|
|
221
|
+
this.core.pushHistoryState('Nesneleri Grupla');
|
|
222
|
+
this.updateSelectionBox();
|
|
223
|
+
this.core.emit('selectionChanged', {
|
|
224
|
+
selectedIds: Array.from(this.selectedIds),
|
|
225
|
+
primaryObject: this.getPrimarySelected()
|
|
226
|
+
});
|
|
227
|
+
return newGroupId;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
ungroupSelected() {
|
|
231
|
+
const selected = this.getSelectedObjects();
|
|
232
|
+
const groupIds = new Set(selected.map(o => o.groupId).filter(Boolean));
|
|
233
|
+
if (groupIds.size === 0) return false;
|
|
234
|
+
|
|
235
|
+
const allObjects = this.objectManager.getObjects();
|
|
236
|
+
allObjects.forEach(o => {
|
|
237
|
+
if (groupIds.has(o.groupId)) {
|
|
238
|
+
this.objectManager.updateObject(o.id, { groupId: null }, false);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
this.core.pushHistoryState('Grubu Çöz');
|
|
242
|
+
this.updateSelectionBox();
|
|
243
|
+
this.core.emit('selectionChanged', {
|
|
244
|
+
selectedIds: Array.from(this.selectedIds),
|
|
245
|
+
primaryObject: this.getPrimarySelected()
|
|
246
|
+
});
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
deleteSelected() {
|
|
251
|
+
const ids = Array.from(this.selectedIds);
|
|
252
|
+
if (ids.length === 0) return;
|
|
253
|
+
this.clearSelection();
|
|
254
|
+
if (ids.length === 1) {
|
|
255
|
+
this.objectManager.deleteObject(ids[0], true);
|
|
256
|
+
} else {
|
|
257
|
+
ids.forEach(id => this.objectManager.deleteObject(id, false));
|
|
258
|
+
this.core.pushHistoryState('Seçili Nesneleri Sil');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
duplicateSelected() {
|
|
263
|
+
const ids = Array.from(this.selectedIds);
|
|
264
|
+
if (ids.length === 0) return;
|
|
265
|
+
this.clearSelection();
|
|
266
|
+
const groupMap = new Map();
|
|
267
|
+
const newIds = [];
|
|
268
|
+
ids.forEach(id => {
|
|
269
|
+
const src = this.objectManager.getObject(id);
|
|
270
|
+
let targetGroupId = undefined;
|
|
271
|
+
if (src && src.groupId) {
|
|
272
|
+
if (!groupMap.has(src.groupId)) {
|
|
273
|
+
groupMap.set(src.groupId, 'grp_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 6));
|
|
274
|
+
}
|
|
275
|
+
targetGroupId = groupMap.get(src.groupId);
|
|
276
|
+
}
|
|
277
|
+
const dup = this.objectManager.duplicateObject(id, false);
|
|
278
|
+
if (dup) {
|
|
279
|
+
if (targetGroupId !== undefined) {
|
|
280
|
+
this.objectManager.updateObject(dup.id, { groupId: targetGroupId }, false);
|
|
281
|
+
}
|
|
282
|
+
newIds.push(dup.id);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
newIds.forEach(id => this.select(id, true));
|
|
286
|
+
this.core.pushHistoryState('Seçili Nesneleri Çoğalt');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
copy() {
|
|
290
|
+
this.clipboard = this.getSelectedObjects().map(o => JSON.parse(JSON.stringify(o)));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
paste() {
|
|
294
|
+
if (!this.clipboard || this.clipboard.length === 0) return;
|
|
295
|
+
this.clearSelection();
|
|
296
|
+
const groupMap = new Map();
|
|
297
|
+
const newIds = [];
|
|
298
|
+
this.clipboard.forEach(item => {
|
|
299
|
+
const copyProps = Object.assign({}, item);
|
|
300
|
+
delete copyProps.id;
|
|
301
|
+
copyProps.name = `${item.name} (Kopya)`;
|
|
302
|
+
copyProps.x = Math.min(this.core.data.stage.width - item.width, item.x + 25);
|
|
303
|
+
copyProps.y = Math.min(this.core.data.stage.height - item.height, item.y + 25);
|
|
304
|
+
if (item.groupId) {
|
|
305
|
+
if (!groupMap.has(item.groupId)) {
|
|
306
|
+
groupMap.set(item.groupId, 'grp_' + Date.now().toString(36) + '_' + Math.random().toString(36).substring(2, 6));
|
|
307
|
+
}
|
|
308
|
+
copyProps.groupId = groupMap.get(item.groupId);
|
|
309
|
+
}
|
|
310
|
+
const newObj = this.objectManager.createObject(item.type, item.role, copyProps, false);
|
|
311
|
+
if (newObj) newIds.push(newObj.id);
|
|
312
|
+
});
|
|
313
|
+
newIds.forEach(id => this.select(id, true));
|
|
314
|
+
this.core.pushHistoryState('Yapıştır');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
nudgeSelected(dx, dy) {
|
|
318
|
+
const targets = this.getSelectedObjects().filter(obj => !obj.locked);
|
|
319
|
+
if (targets.length === 0) return;
|
|
320
|
+
targets.forEach(obj => {
|
|
321
|
+
this.objectManager.updateObject(obj.id, {
|
|
322
|
+
x: Math.max(0, Math.min(this.core.data.stage.width - obj.width, obj.x + dx)),
|
|
323
|
+
y: Math.max(0, Math.min(this.core.data.stage.height - obj.height, obj.y + dy))
|
|
324
|
+
}, false);
|
|
325
|
+
});
|
|
326
|
+
this.core.pushHistoryState('Nesneleri Kaydır');
|
|
327
|
+
this.updateSelectionBox();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Seçim Kutusu DOM Güncellemesi
|
|
331
|
+
updateSelectionBox() {
|
|
332
|
+
if (!this.selectionOverlay) return;
|
|
333
|
+
|
|
334
|
+
// Önceki vurguları temizle
|
|
335
|
+
this.stage.querySelectorAll('.stage-object.is-selected').forEach(el => {
|
|
336
|
+
el.classList.remove('is-selected');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const selectedObjs = this.getSelectedObjects();
|
|
340
|
+
|
|
341
|
+
if (selectedObjs.length === 0 || this.core.getMode() !== 'design') {
|
|
342
|
+
this.selectionOverlay.style.display = 'none';
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
selectedObjs.forEach(obj => {
|
|
347
|
+
const domEl = this.stage.querySelector(`[data-id="${obj.id}"]`);
|
|
348
|
+
if (domEl) domEl.classList.add('is-selected');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
this.selectionOverlay.style.display = 'block';
|
|
352
|
+
|
|
353
|
+
if (selectedObjs.length === 1) {
|
|
354
|
+
// Tek nesne seçimi: Nesnenin kendi konumu ve dönüş açısı kullanılır
|
|
355
|
+
const obj = selectedObjs[0];
|
|
356
|
+
this.renderSingleSelectionBox(obj);
|
|
357
|
+
} else {
|
|
358
|
+
// Çoklu nesne seçimi: Tüm nesneleri kapsayan AABB kutusu
|
|
359
|
+
this.renderMultiSelectionBox(selectedObjs);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
renderSingleSelectionBox(obj) {
|
|
364
|
+
this.selectionOverlay.style.width = `${obj.width}px`;
|
|
365
|
+
this.selectionOverlay.style.height = `${obj.height}px`;
|
|
366
|
+
this.selectionOverlay.style.transform = `translate(${obj.x}px, ${obj.y}px) rotate(${obj.rotation || 0}deg)`;
|
|
367
|
+
this.selectionOverlay.style.transformOrigin = 'center center';
|
|
368
|
+
|
|
369
|
+
if (obj.locked) {
|
|
370
|
+
this.selectionOverlay.className = 'selection-box single-select is-locked-selection';
|
|
371
|
+
const isNearTop = (obj.y || 0) < 45;
|
|
372
|
+
const pillPosStyle = isNearTop ? 'bottom: -38px; top: auto;' : 'top: -38px;';
|
|
373
|
+
this.selectionOverlay.innerHTML = `
|
|
374
|
+
<div class="selection-border locked-border"></div>
|
|
375
|
+
<div class="selection-lock-floating-pill" style="${pillPosStyle}">
|
|
376
|
+
<button type="button" class="lock-pill-unlock-btn" title="Kilidi Aç">🔓 Kilidi Aç</button>
|
|
377
|
+
</div>
|
|
378
|
+
`;
|
|
379
|
+
const unlockBtn = this.selectionOverlay.querySelector('.lock-pill-unlock-btn');
|
|
380
|
+
if (unlockBtn) {
|
|
381
|
+
unlockBtn.onclick = (e) => {
|
|
382
|
+
e.stopPropagation();
|
|
383
|
+
e.preventDefault();
|
|
384
|
+
this.objectManager.updateObject(obj.id, { locked: false });
|
|
385
|
+
this.updateSelectionBox();
|
|
386
|
+
this.core.emit('selectionChanged', {
|
|
387
|
+
selectedIds: Array.from(this.selectedIds),
|
|
388
|
+
primaryObject: this.objectManager.getObject(obj.id)
|
|
389
|
+
});
|
|
390
|
+
if (window.app && window.app.miniToolbar) {
|
|
391
|
+
window.app.miniToolbar.show();
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Çizgi nesnesi için sadece iki uçta ölçeklendirme tutamaçları
|
|
399
|
+
if (obj.type === 'line') {
|
|
400
|
+
this.selectionOverlay.className = 'selection-box single-select is-line-selection';
|
|
401
|
+
this.selectionOverlay.innerHTML = `
|
|
402
|
+
<div class="resize-handle line-endpoint-handle handle-start" data-handle="w" title="Başlangıç ucunu uzat / kısalt"></div>
|
|
403
|
+
<div class="resize-handle line-endpoint-handle handle-end" data-handle="e" title="Bitiş ucunu uzat / kısalt"></div>
|
|
404
|
+
`;
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this.selectionOverlay.className = 'selection-box single-select';
|
|
409
|
+
// 8 tutamaç + döndürme tutamacı
|
|
410
|
+
this.selectionOverlay.innerHTML = `
|
|
411
|
+
<div class="selection-border"></div>
|
|
412
|
+
<div class="resize-handle handle-nw" data-handle="nw"></div>
|
|
413
|
+
<div class="resize-handle handle-n" data-handle="n"></div>
|
|
414
|
+
<div class="resize-handle handle-ne" data-handle="ne"></div>
|
|
415
|
+
<div class="resize-handle handle-e" data-handle="e"></div>
|
|
416
|
+
<div class="resize-handle handle-se" data-handle="se"></div>
|
|
417
|
+
<div class="resize-handle handle-s" data-handle="s"></div>
|
|
418
|
+
<div class="resize-handle handle-sw" data-handle="sw"></div>
|
|
419
|
+
<div class="resize-handle handle-w" data-handle="w"></div>
|
|
420
|
+
<div class="rotate-stem"></div>
|
|
421
|
+
<div class="rotate-handle" data-handle="rotate" title="Döndürmek için sürükleyin"></div>
|
|
422
|
+
`;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
renderMultiSelectionBox(objects) {
|
|
426
|
+
let minX = Infinity;
|
|
427
|
+
let minY = Infinity;
|
|
428
|
+
let maxX = -Infinity;
|
|
429
|
+
let maxY = -Infinity;
|
|
430
|
+
|
|
431
|
+
objects.forEach(o => {
|
|
432
|
+
minX = Math.min(minX, o.x);
|
|
433
|
+
minY = Math.min(minY, o.y);
|
|
434
|
+
maxX = Math.max(maxX, o.x + o.width);
|
|
435
|
+
maxY = Math.max(maxY, o.y + o.height);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const w = maxX - minX;
|
|
439
|
+
const h = maxY - minY;
|
|
440
|
+
|
|
441
|
+
this.selectionOverlay.style.width = `${w}px`;
|
|
442
|
+
this.selectionOverlay.style.height = `${h}px`;
|
|
443
|
+
this.selectionOverlay.style.transform = `translate(${minX}px, ${minY}px) rotate(0deg)`;
|
|
444
|
+
this.selectionOverlay.style.transformOrigin = 'center center';
|
|
445
|
+
this.selectionOverlay.className = 'selection-box multi-select';
|
|
446
|
+
|
|
447
|
+
const isGroup = objects.length > 1 && objects.every(o => o.groupId && o.groupId === objects[0].groupId);
|
|
448
|
+
|
|
449
|
+
// Çoklu seçimde sadece taşıma ve sınır çerçevesi görünür
|
|
450
|
+
this.selectionOverlay.innerHTML = `
|
|
451
|
+
<div class="selection-border ${isGroup ? 'group-border' : ''}"></div>
|
|
452
|
+
<div class="multi-count-badge ${isGroup ? 'is-group-badge' : ''}">${isGroup ? `👥 Grup (${objects.length} nesne)` : `${objects.length} nesne seçildi`}</div>
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Global namespace
|
|
458
|
+
window.DND = window.DND || {};
|
|
459
|
+
window.DND.SelectionManager = SelectionManager;
|