polarvo-layout 1.0.26 → 1.0.27
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/package.json +1 -1
- package/src/components/Layout/BaseLayout.vue +4 -3
- package/src/components/Layout/CanvasContainer.vue +5 -7
- package/src/components/Layout/FreeLayout.vue +1 -1
- package/src/components/Layout/GridLayout.vue +41 -14
- package/src/components/Layout/PolarLayout.vue +1 -0
- package/src/core/engines/DisplayEngine.js +12 -15
- package/src/core/engines/FreeDropEngine.js +100 -55
- package/src/core/engines/GridDropEngine.js +32 -12
- package/src/core/engines/HistoryEngine.js +27 -240
- package/src/core/engines/LayoutEngine.js +29 -31
- package/src/core/engines/originals/DisplayEngine_0423.js +247 -0
- package/src/core/engines/originals/{FreeDropEngine_0416.js → FreeDropEngine_0423.js} +88 -54
- package/src/core/engines/originals/{GridDropEngine_0402.js → GridDropEngine_0423.js} +39 -39
- package/src/core/engines/originals/HistoryEngine_0423.js +336 -0
- package/src/core/engines/originals/LayoutEngine_0423.js +346 -0
- package/src/core/managers/EngineManager.js +174 -36
- package/src/core/managers/originals/{EngineManager_0402.js → EngineManager_0423.js} +181 -151
- package/src/library/DisplayLibrary.js +11 -7
- package/src/library/FreeDropLibrary.js +7 -9
- package/src/library/GridDropLibrary.js +8 -9
- package/src/library/HistoryLibrary.js +2 -2
- package/src/library/LayoutLibrary.js +32 -13
- package/src/library/originals/{DisplayLibrary_0402.js → DisplayLibrary_0423.js} +15 -19
- package/src/library/originals/{FreeDropLibrary_0416.js → FreeDropLibrary_0423.js} +17 -8
- package/src/library/originals/{GridDropLibrary_0402.js → GridDropLibrary_0423.js} +5 -8
- package/src/library/originals/{HistoryLibrary_0402.js → HistoryLibrary_0423.js} +1 -2
- package/src/library/originals/{LayoutLibrary_0402.js → LayoutLibrary_0423.js} +34 -47
- package/src/components/Layout/originals/FreeLayout_0416.vue +0 -284
- package/src/components/Layout/originals/GridLayout_0416.vue +0 -202
package/package.json
CHANGED
|
@@ -68,9 +68,9 @@ const props = defineProps({
|
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
const layoutType = computed(() => {
|
|
71
|
-
if (layoutName.value
|
|
72
|
-
return layoutName.value
|
|
73
|
-
|
|
71
|
+
if (layoutName.value?.eng === null) return 'no-split';
|
|
72
|
+
return layoutName.value?.eng
|
|
73
|
+
?.replace(/([A-Z])/g, '-$1')
|
|
74
74
|
.toLowerCase()
|
|
75
75
|
.substring(1);
|
|
76
76
|
});
|
|
@@ -88,6 +88,7 @@ const getBaseStyle = computed(() => {
|
|
|
88
88
|
|
|
89
89
|
const getSectionStyle = (section) => ({
|
|
90
90
|
'--grid-columns': section.config?.gridColumns ?? 3,
|
|
91
|
+
'--grid-rows': section.config?.gridRows ?? 3,
|
|
91
92
|
'--grid-gap': `${section.config?.gridGap ?? 5}px`,
|
|
92
93
|
position: 'relative',
|
|
93
94
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</template>
|
|
14
14
|
|
|
15
15
|
<script setup>
|
|
16
|
-
import { toRefs, ref, computed, watch
|
|
16
|
+
import { toRefs, ref, computed, watch } from 'vue';
|
|
17
17
|
import BaseLayout from './BaseLayout.vue';
|
|
18
18
|
|
|
19
19
|
const props = defineProps({
|
|
@@ -36,7 +36,7 @@ const { elements } = toRefs(props.polarvo.layout.state);
|
|
|
36
36
|
const defaultHeight = computed(() => {
|
|
37
37
|
return (
|
|
38
38
|
elements.value?.reduce((max, el) => {
|
|
39
|
-
const bottom = (el.position?.y || 0) + (el.size?.h || 0)
|
|
39
|
+
const bottom = (el.position?.y || 0) + (el.size?.h || 0);
|
|
40
40
|
return Math.max(max, bottom);
|
|
41
41
|
}, 750) || 750
|
|
42
42
|
);
|
|
@@ -66,13 +66,13 @@ const containerStyle = computed(() => {
|
|
|
66
66
|
margin: '0 auto',
|
|
67
67
|
transform: `scale(${DEFAULT_DISPLAY_SIZE.percent / 100})`,
|
|
68
68
|
transformOrigin: 'top center',
|
|
69
|
-
|
|
69
|
+
backgroundImage: `
|
|
70
70
|
linear-gradient(rgba(0,0,0,0.05) 1px, transparent 1px),
|
|
71
71
|
linear-gradient(90deg, rgba(0,0,0,0.05) 1px, transparent 1px)
|
|
72
72
|
`,
|
|
73
|
-
|
|
73
|
+
backgroundSize: `${DEFAULT_DISPLAY_SIZE.gridSize}px ${DEFAULT_DISPLAY_SIZE.gridSize}px`,
|
|
74
|
+
};
|
|
74
75
|
}
|
|
75
|
-
}
|
|
76
76
|
|
|
77
77
|
return {
|
|
78
78
|
aspectRatio: displaySize.value.aspectRatio,
|
|
@@ -87,8 +87,6 @@ const containerStyle = computed(() => {
|
|
|
87
87
|
`,
|
|
88
88
|
backgroundSize: `${displaySize.value.gridSize}px ${displaySize.value.gridSize}px`,
|
|
89
89
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
90
|
});
|
|
93
91
|
|
|
94
92
|
// 전체 elements
|
|
@@ -61,7 +61,7 @@ const { setActiveDesign } = props.polarvo.display;
|
|
|
61
61
|
const { updateActiveElement } = props.polarvo.layout;
|
|
62
62
|
const { elements, guides, activeElement } = toRefs(props.polarvo.freeDrop.state);
|
|
63
63
|
const filteredElements = computed(() => {
|
|
64
|
-
return elements.value.filter((el) => props.elementIds?.includes(el.id) && el.section === props.sectionKey);
|
|
64
|
+
return elements.value.filter((el) => props.elementIds?.includes(el.id) && el.section === props.sectionKey) ?? [];
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
const selectedElement = inject('selectedElement');
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<script setup>
|
|
17
17
|
import GridItem from './GridItem.vue';
|
|
18
18
|
|
|
19
|
-
import { toRefs, getCurrentInstance, onMounted, onUnmounted, inject, watch, computed } from 'vue';
|
|
19
|
+
import { ref, toRefs, getCurrentInstance, onMounted, onUnmounted, inject, watch, computed } from 'vue';
|
|
20
20
|
|
|
21
21
|
const props = defineProps({
|
|
22
22
|
polarvo: {
|
|
@@ -46,16 +46,45 @@ const { updateActiveElement } = props.polarvo.layout;
|
|
|
46
46
|
const { elements, activeId, activeCell, activeElement } = toRefs(props.polarvo.gridDrop.state);
|
|
47
47
|
const { getElementStyle, setActiveElement, toggleLock, handleMouseDown, detectHoverCell, startResize } = props.polarvo.gridDrop;
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
const selectedElement = inject('selectedElement');
|
|
50
|
+
const activeEditMode = inject('activeEditMode');
|
|
51
|
+
|
|
52
|
+
import { omit, cloneDeep, debounce } from 'lodash-es';
|
|
53
|
+
|
|
54
|
+
const gridColumns = ref(3); // 동적으로 계산
|
|
55
|
+
const gridRows = ref(1); // 동적으로 계산
|
|
56
|
+
|
|
57
|
+
function initData() {
|
|
58
|
+
gridColumns.value = props.sectionData?.config?.gridColumns || 3;
|
|
59
|
+
gridRows.value = props.sectionData?.config?.gridRows || 1;
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
setMergedData();
|
|
62
|
+
}
|
|
54
63
|
|
|
55
|
-
|
|
64
|
+
const mergedData = ref([]); // 필드 데이터 + 빈 데이터 통합 리스트
|
|
65
|
+
const filteredElements = computed(() => {
|
|
66
|
+
return elements.value.filter((el) => props.elementIds?.includes(el.id) && el.section === props.sectionKey) ?? [];
|
|
67
|
+
});
|
|
68
|
+
watch(
|
|
69
|
+
() => filteredElements.value,
|
|
70
|
+
() => {
|
|
71
|
+
debouncedUpdate();
|
|
72
|
+
},
|
|
73
|
+
{ deep: true },
|
|
74
|
+
);
|
|
75
|
+
const debouncedUpdate = debounce(() => {
|
|
76
|
+
setMergedData();
|
|
77
|
+
// gridRows 업데이트
|
|
78
|
+
}, 10);
|
|
79
|
+
|
|
80
|
+
function setMergedData() {
|
|
81
|
+
const grid = Array.from({ length: gridRows.value + 1 }, () => Array(gridColumns.value + 1).fill(false));
|
|
82
|
+
// const filled = elements.value.filter((el) => props.elementIds?.includes(el.id) && el.section === props.sectionKey);
|
|
83
|
+
|
|
84
|
+
filteredElements.value.forEach(({ position: { col, row }, size: { colSpan, rowSpan } }) => {
|
|
56
85
|
for (let r = row; r < row + rowSpan; r++) {
|
|
57
86
|
for (let c = col; c < col + colSpan; c++) {
|
|
58
|
-
if (r >= 1 && c >= 1 && r <= gridRows && c <= gridColumns) {
|
|
87
|
+
if (r >= 1 && c >= 1 && r <= gridRows.value && c <= gridColumns.value) {
|
|
59
88
|
grid[r][c] = true;
|
|
60
89
|
}
|
|
61
90
|
}
|
|
@@ -63,16 +92,12 @@ const mergedData = computed(() => {
|
|
|
63
92
|
});
|
|
64
93
|
|
|
65
94
|
const empty = [];
|
|
66
|
-
for (let r = 1; r <= gridRows; r++) for (let c = 1; c <= gridColumns; c++) if (!grid[r][c]) empty.push({ row: r, col: c });
|
|
67
|
-
|
|
68
|
-
return [...filled, ...empty];
|
|
69
|
-
});
|
|
95
|
+
for (let r = 1; r <= gridRows.value; r++) for (let c = 1; c <= gridColumns.value; c++) if (!grid[r][c]) empty.push({ row: r, col: c });
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
mergedData.value = [...filteredElements.value, ...empty];
|
|
98
|
+
}
|
|
73
99
|
|
|
74
100
|
let _isElementSwitching = false;
|
|
75
|
-
import { omit, cloneDeep } from 'lodash-es';
|
|
76
101
|
watch(
|
|
77
102
|
() => activeElement.value?.id,
|
|
78
103
|
(newId, oldId) => {
|
|
@@ -108,6 +133,8 @@ watch(
|
|
|
108
133
|
onMounted(() => {
|
|
109
134
|
props.polarvo.components.register('sections', props.sectionKey, getCurrentInstance());
|
|
110
135
|
activeEditMode.value = false;
|
|
136
|
+
|
|
137
|
+
initData();
|
|
111
138
|
});
|
|
112
139
|
|
|
113
140
|
onUnmounted(() => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { cloneDeep } from 'lodash';
|
|
2
|
+
|
|
1
3
|
// event 발행 시 'display'으로 시작하는 이름 사용
|
|
2
4
|
const DISPLAY_LIMITS = {
|
|
3
5
|
percent: { max: 200, min: 0, unit: '%', name: '배율' },
|
|
@@ -110,12 +112,10 @@ class DisplayEngine {
|
|
|
110
112
|
* @return {object} - 현재 상태 객체
|
|
111
113
|
*/
|
|
112
114
|
getState() {
|
|
113
|
-
return {
|
|
115
|
+
return cloneDeep({
|
|
114
116
|
displayMode: this.displayMode,
|
|
115
|
-
displaySize:
|
|
116
|
-
|
|
117
|
-
activeDesign: this.activeDesign,
|
|
118
|
-
};
|
|
117
|
+
displaySize: this.displaySize,
|
|
118
|
+
});
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/** 상태 정보 설정
|
|
@@ -124,13 +124,6 @@ class DisplayEngine {
|
|
|
124
124
|
setState(state) {
|
|
125
125
|
this.displayMode = state.displayMode;
|
|
126
126
|
this.displaySize = { ...state.displaySize };
|
|
127
|
-
this.activeMenu = state.activeMenu;
|
|
128
|
-
this.activeDesign = state.activeDesign;
|
|
129
|
-
|
|
130
|
-
this.eventBus.emit('display:restoredState', {
|
|
131
|
-
stateData: state,
|
|
132
|
-
timestamp: Date.now(),
|
|
133
|
-
});
|
|
134
127
|
}
|
|
135
128
|
|
|
136
129
|
/** ---------------------------------- 공통 메소드 ---------------------------------- **/
|
|
@@ -214,14 +207,18 @@ class DisplayEngine {
|
|
|
214
207
|
}
|
|
215
208
|
const numSize = Number(size);
|
|
216
209
|
const limit = DISPLAY_LIMITS[type];
|
|
217
|
-
const result = {result: true, message: '사이즈가 성공적으로 변경되었습니다.'};
|
|
210
|
+
const result = { result: true, message: '사이즈가 성공적으로 변경되었습니다.' };
|
|
211
|
+
|
|
212
|
+
if (isNaN(numSize)) {
|
|
213
|
+
return { result: false, message: '유효한 숫자를 입력해주세요.' };
|
|
214
|
+
}
|
|
218
215
|
|
|
219
|
-
if (
|
|
216
|
+
if (numSize <= limit.min) {
|
|
220
217
|
result.result = false;
|
|
221
218
|
result.message = `설정 가능한 최소 ${limit.name}는 ${limit.min}${limit.unit}입니다.`;
|
|
222
219
|
}
|
|
223
220
|
|
|
224
|
-
if (
|
|
221
|
+
if (numSize > limit.max) {
|
|
225
222
|
result.result = false;
|
|
226
223
|
result.message = `설정 가능한 최대 ${limit.name}는 ${limit.max}${limit.unit}입니다.`;
|
|
227
224
|
}
|
|
@@ -52,19 +52,8 @@ class FreeDropEngine {
|
|
|
52
52
|
/** [내부함수] 구독 설정 */
|
|
53
53
|
_setupSubscriptions() {
|
|
54
54
|
// 초기화 완료 이벤트 구독 - from DisplayEngine
|
|
55
|
-
this._subscribe('display:engineInitialized', (
|
|
56
|
-
|
|
57
|
-
this._gridSize = gridSize;
|
|
58
|
-
|
|
59
|
-
this._initialize();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
this._subscribe('display:enginesReset', ({ $displaySize }) => {
|
|
63
|
-
const { gridSize } = $displaySize;
|
|
64
|
-
this._gridSize = gridSize;
|
|
65
|
-
|
|
66
|
-
this._initialize();
|
|
67
|
-
});
|
|
55
|
+
this._subscribe('display:engineInitialized', this._handleDisplayInit.bind(this));
|
|
56
|
+
this._subscribe('display:enginesReset', this._handleDisplayInit.bind(this));
|
|
68
57
|
|
|
69
58
|
// displayMode 변경 감지 (displaySize가 함께 초기화됨)
|
|
70
59
|
this._subscribe('display:displayChanged', ({ $displaySize }) => {
|
|
@@ -110,6 +99,7 @@ class FreeDropEngine {
|
|
|
110
99
|
this._elementsUpdate = true;
|
|
111
100
|
|
|
112
101
|
this.setActiveElement(this._elements.find((x) => x.id === $elementId));
|
|
102
|
+
|
|
113
103
|
this.eventBus.emit('freeDrop:requestUpdateData', {
|
|
114
104
|
elements: cloneDeep(this.freeElements),
|
|
115
105
|
guides: this.guides,
|
|
@@ -133,20 +123,45 @@ class FreeDropEngine {
|
|
|
133
123
|
});
|
|
134
124
|
});
|
|
135
125
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// this._elementsUpdate = true;
|
|
139
|
-
// });
|
|
140
|
-
|
|
141
|
-
this._subscribe('system:restoredState', ({ stateData }) => {
|
|
142
|
-
this._elements = stateData.elements;
|
|
126
|
+
this._subscribe('system:requestElementsScale', ({ $elements }) => {
|
|
127
|
+
this._elements = $elements;
|
|
143
128
|
this._elementsUpdate = true;
|
|
144
|
-
|
|
129
|
+
});
|
|
145
130
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
131
|
+
this._subscribe('system:restoredState', ({ domain, state: snapShot }) => {
|
|
132
|
+
if (domain === 'all') {
|
|
133
|
+
this._elements = snapShot.manager.elements || [];
|
|
134
|
+
this._elementsUpdate = true;
|
|
135
|
+
this.setActiveElement(null);
|
|
136
|
+
|
|
137
|
+
this.eventBus.emit('freeDrop:restoredState', {
|
|
138
|
+
elements: cloneDeep(this.freeElements),
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
this._gridNumber = snapShot.layout.gridNumber || null;
|
|
144
|
+
this._gridSize = snapShot.display.displaySize?.gridSize || null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (domain === 'manager') {
|
|
148
|
+
this._elements = snapShot.elements || [];
|
|
149
|
+
this._elementsUpdate = true;
|
|
150
|
+
this.setActiveElement(null);
|
|
151
|
+
|
|
152
|
+
this.eventBus.emit('freeDrop:restoredState', {
|
|
153
|
+
elements: cloneDeep(this.freeElements),
|
|
154
|
+
timestamp: Date.now(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (domain === 'layout') {
|
|
159
|
+
this._gridNumber = snapShot.gridNumber || null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (domain === 'display') {
|
|
163
|
+
this._gridSize = snapShot.displaySize?.gridSize || null;
|
|
164
|
+
}
|
|
150
165
|
});
|
|
151
166
|
}
|
|
152
167
|
|
|
@@ -231,6 +246,13 @@ class FreeDropEngine {
|
|
|
231
246
|
this._alreadyExist = false;
|
|
232
247
|
|
|
233
248
|
this._elementsUpdate = false;
|
|
249
|
+
this._elements = [];
|
|
250
|
+
this._freeElements = [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_handleDisplayInit({ $displaySize }) {
|
|
254
|
+
this._gridSize = $displaySize.gridSize;
|
|
255
|
+
this._initialize();
|
|
234
256
|
}
|
|
235
257
|
|
|
236
258
|
/** [내부함수] 그리드 스냅
|
|
@@ -265,7 +287,11 @@ class FreeDropEngine {
|
|
|
265
287
|
const x = event.clientX - offsetPos.x + size.w;
|
|
266
288
|
const y = event.clientY - offsetPos.y + size.h;
|
|
267
289
|
|
|
268
|
-
const result =
|
|
290
|
+
const result =
|
|
291
|
+
event.clientX - offsetPos.x >= layoutRect.left &&
|
|
292
|
+
event.clientY - offsetPos.y >= layoutRect.top &&
|
|
293
|
+
x <= layoutRect.right &&
|
|
294
|
+
y <= layoutRect.bottom;
|
|
269
295
|
|
|
270
296
|
return result;
|
|
271
297
|
}
|
|
@@ -275,7 +301,7 @@ class FreeDropEngine {
|
|
|
275
301
|
*/
|
|
276
302
|
_detectElementCollision() {
|
|
277
303
|
if (!this._activeElement) {
|
|
278
|
-
console.error('[FreeDropEngine] 충돌 검사를 위한 아이템 정보를 찾을 수 없습니다:', this._activeElement
|
|
304
|
+
console.error('[FreeDropEngine] 충돌 검사를 위한 아이템 정보를 찾을 수 없습니다:', this._activeElement?.id || 'unknown');
|
|
279
305
|
return false;
|
|
280
306
|
}
|
|
281
307
|
|
|
@@ -359,13 +385,13 @@ class FreeDropEngine {
|
|
|
359
385
|
this._offsetPos.x = element.size.w / 2;
|
|
360
386
|
this._offsetPos.y = element.size.h / 2;
|
|
361
387
|
|
|
362
|
-
|
|
363
|
-
// 신규 아이템 추가 시 바로 드래그 상태로 진입 (단, 기존 아이템과 달리 위치 기준이 마우스 포인터가 됨)
|
|
364
|
-
this.eventBus.emit('freeDrop:startDrag', { timestamp: Date.now() });
|
|
388
|
+
this._startDrag();
|
|
389
|
+
// // 신규 아이템 추가 시 바로 드래그 상태로 진입 (단, 기존 아이템과 달리 위치 기준이 마우스 포인터가 됨)
|
|
390
|
+
// this.eventBus.emit('freeDrop:startDrag', { timestamp: Date.now() });
|
|
365
391
|
|
|
366
|
-
this._dragState.isDragging = true;
|
|
367
|
-
document.addEventListener('mousemove', this._startDragMove);
|
|
368
|
-
document.addEventListener('mouseup', this._stopDrag);
|
|
392
|
+
// this._dragState.isDragging = true;
|
|
393
|
+
// document.addEventListener('mousemove', this._startDragMove);
|
|
394
|
+
// document.addEventListener('mouseup', this._stopDrag);
|
|
369
395
|
}
|
|
370
396
|
|
|
371
397
|
/** [내부함수] 배치요소 삭제
|
|
@@ -398,12 +424,11 @@ class FreeDropEngine {
|
|
|
398
424
|
|
|
399
425
|
this._activeElement = element;
|
|
400
426
|
this._alreadyExist = action === 'add' ? false : true;
|
|
401
|
-
this._elementsUpdate = true;
|
|
427
|
+
// this._elementsUpdate = true;
|
|
402
428
|
|
|
403
429
|
this.eventBus.emit('freeDrop:setActiveElement', {
|
|
404
|
-
|
|
430
|
+
$alreadyExist: this._alreadyExist,
|
|
405
431
|
activeElement: this._activeElement,
|
|
406
|
-
// elements: cloneDeep(this.freeElements),
|
|
407
432
|
timestamp: Date.now(),
|
|
408
433
|
});
|
|
409
434
|
}
|
|
@@ -413,6 +438,7 @@ class FreeDropEngine {
|
|
|
413
438
|
* @returns {object} - 스타일 객체
|
|
414
439
|
*/
|
|
415
440
|
getElementStyle(element) {
|
|
441
|
+
if (!element?.position || !element?.size) return {};
|
|
416
442
|
const { x, y } = element.position;
|
|
417
443
|
const { w, h } = element.size;
|
|
418
444
|
|
|
@@ -440,8 +466,18 @@ class FreeDropEngine {
|
|
|
440
466
|
const rawX = event.clientX - layoutRect.left - offsetPos.x;
|
|
441
467
|
const rawY = event.clientY - layoutRect.top - offsetPos.y;
|
|
442
468
|
|
|
443
|
-
const
|
|
444
|
-
const
|
|
469
|
+
const maxX = layoutRect.width - this._activeElement.size.w;
|
|
470
|
+
const maxY = layoutRect.height - this._activeElement.size.h;
|
|
471
|
+
|
|
472
|
+
// 클램핑: 경계 밖으로 나가도 경계에서 멈추고 마우스를 계속 추적
|
|
473
|
+
const clampedX = Math.min(Math.max(0, rawX), maxX);
|
|
474
|
+
const clampedY = Math.min(Math.max(0, rawY), maxY);
|
|
475
|
+
|
|
476
|
+
// const gridX = this._snapToGrid(rawX);
|
|
477
|
+
// const gridY = this._snapToGrid(rawY);
|
|
478
|
+
|
|
479
|
+
const gridX = this._snapToGrid(clampedX);
|
|
480
|
+
const gridY = this._snapToGrid(clampedY);
|
|
445
481
|
|
|
446
482
|
return { x: gridX, y: gridY };
|
|
447
483
|
}
|
|
@@ -523,7 +559,6 @@ class FreeDropEngine {
|
|
|
523
559
|
this.guides.h = this._activeElement.size.h;
|
|
524
560
|
}
|
|
525
561
|
|
|
526
|
-
|
|
527
562
|
// Vue 반응성 사이클 없이 직접 DOM 조작
|
|
528
563
|
const el = document.getElementById(this._activeElement.id);
|
|
529
564
|
if (el) {
|
|
@@ -550,7 +585,6 @@ class FreeDropEngine {
|
|
|
550
585
|
// timestamp: Date.now(),
|
|
551
586
|
// });
|
|
552
587
|
|
|
553
|
-
|
|
554
588
|
rafId = null;
|
|
555
589
|
});
|
|
556
590
|
};
|
|
@@ -572,6 +606,10 @@ class FreeDropEngine {
|
|
|
572
606
|
}
|
|
573
607
|
|
|
574
608
|
const element = this.freeElements.find((el) => el.id === id);
|
|
609
|
+
if (!element) {
|
|
610
|
+
console.error('[FreeDropEngine] 배치요소를 찾을 수 없습니다. ID:', id);
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
575
613
|
this.setActiveElement(element, 'click');
|
|
576
614
|
|
|
577
615
|
// 배치요소 잠겨 있으면 드래그/삭제 불가
|
|
@@ -587,10 +625,13 @@ class FreeDropEngine {
|
|
|
587
625
|
// 배치요소 위치 기준 오프셋 계산
|
|
588
626
|
const elementRect = elementEl.getBoundingClientRect();
|
|
589
627
|
if (elementRect) {
|
|
590
|
-
const { column, row } = this._gridNumber;
|
|
628
|
+
// const { column, row } = this._gridNumber;
|
|
591
629
|
|
|
592
|
-
this._offsetPos.x = (event.clientX - elementRect.left) / column;
|
|
593
|
-
this._offsetPos.y = (event.clientY - elementRect.top) / row;
|
|
630
|
+
// this._offsetPos.x = (event.clientX - elementRect.left) / column;
|
|
631
|
+
// this._offsetPos.y = (event.clientY - elementRect.top) / row;
|
|
632
|
+
|
|
633
|
+
this._offsetPos.x = event.clientX - elementRect.left;
|
|
634
|
+
this._offsetPos.y = event.clientY - elementRect.top;
|
|
594
635
|
}
|
|
595
636
|
|
|
596
637
|
document.addEventListener('mousemove', this._detectDragIntent);
|
|
@@ -610,12 +651,7 @@ class FreeDropEngine {
|
|
|
610
651
|
document.removeEventListener('mousemove', this._detectDragIntent);
|
|
611
652
|
document.removeEventListener('mouseup', this._cancelDragIntent);
|
|
612
653
|
|
|
613
|
-
|
|
614
|
-
this._dragState.isDragging = true;
|
|
615
|
-
|
|
616
|
-
this.eventBus.emit('freeDrop:startDrag', { timestamp: Date.now() });
|
|
617
|
-
document.addEventListener('mousemove', this._startDragMove);
|
|
618
|
-
document.addEventListener('mouseup', this._stopDrag);
|
|
654
|
+
this._startDrag();
|
|
619
655
|
// const position = this._getActiveElementPosition(event, this._offsetPos);
|
|
620
656
|
// this._updateElementBounds(position);
|
|
621
657
|
}
|
|
@@ -627,6 +663,15 @@ class FreeDropEngine {
|
|
|
627
663
|
document.removeEventListener('mouseup', this._cancelDragIntent);
|
|
628
664
|
};
|
|
629
665
|
|
|
666
|
+
_startDrag() {
|
|
667
|
+
// 드래그 확정 후 실제 드래그 리스너 등록
|
|
668
|
+
this._dragState.isDragging = true;
|
|
669
|
+
|
|
670
|
+
this.eventBus.emit('freeDrop:startDrag', { timestamp: Date.now() });
|
|
671
|
+
document.addEventListener('mousemove', this._startDragMove);
|
|
672
|
+
document.addEventListener('mouseup', this._stopDrag);
|
|
673
|
+
}
|
|
674
|
+
|
|
630
675
|
/** [내부함수] 드래그 진행
|
|
631
676
|
* @param {MouseEvent} event - 마우스 이벤트
|
|
632
677
|
*/
|
|
@@ -635,12 +680,12 @@ class FreeDropEngine {
|
|
|
635
680
|
if (this._dragState.isResizing) return;
|
|
636
681
|
|
|
637
682
|
// 레이아웃 내부 진입 여부
|
|
638
|
-
const isInsideLayout = this._detectLayoutEntry(event, this._offsetPos);
|
|
683
|
+
// const isInsideLayout = this._detectLayoutEntry(event, this._offsetPos);
|
|
639
684
|
|
|
640
|
-
if (isInsideLayout) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
685
|
+
// if (isInsideLayout) {
|
|
686
|
+
const position = this._getActiveElementPosition(event, this._offsetPos);
|
|
687
|
+
this._updateElementBounds(position);
|
|
688
|
+
// }
|
|
644
689
|
};
|
|
645
690
|
|
|
646
691
|
/** [내부함수] 드래그 종료 */
|
|
@@ -665,7 +710,7 @@ class FreeDropEngine {
|
|
|
665
710
|
elementId: this._activeElement.id,
|
|
666
711
|
position: this._activeElement.position,
|
|
667
712
|
size: this._activeElement.size,
|
|
668
|
-
|
|
713
|
+
guides: this.guides,
|
|
669
714
|
timestamp: Date.now(),
|
|
670
715
|
});
|
|
671
716
|
this._resetDrag(true, true);
|
|
@@ -103,20 +103,35 @@ class GridDropEngine {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// this._elementsUpdate = true;
|
|
109
|
-
// });
|
|
110
|
-
|
|
111
|
-
this._subscribe('system:restoredState', ({ stateData }) => {
|
|
112
|
-
this._elements = stateData.elements;
|
|
106
|
+
this._subscribe('system:requestElementsScale', ({ $elements }) => {
|
|
107
|
+
this._elements = $elements;
|
|
113
108
|
this._elementsUpdate = true;
|
|
114
|
-
|
|
109
|
+
});
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
this._subscribe('system:restoredState', ({ domain, state: snapShot }) => {
|
|
112
|
+
if (domain === 'all') {
|
|
113
|
+
this._elements = snapShot.manager.elements || [];
|
|
114
|
+
this._elementsUpdate = true;
|
|
115
|
+
|
|
116
|
+
this.setActiveElement(null);
|
|
117
|
+
|
|
118
|
+
this.eventBus.emit('gridDrop:restoredState', {
|
|
119
|
+
elements: cloneDeep(this.gridElements),
|
|
120
|
+
timestamp: Date.now(),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (domain === 'manager') {
|
|
125
|
+
this._elements = snapShot.elements || [];
|
|
126
|
+
this._elementsUpdate = true;
|
|
127
|
+
|
|
128
|
+
this.setActiveElement(null);
|
|
129
|
+
|
|
130
|
+
this.eventBus.emit('gridDrop:restoredState', {
|
|
131
|
+
elements: cloneDeep(this.gridElements),
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
120
135
|
});
|
|
121
136
|
}
|
|
122
137
|
|
|
@@ -246,6 +261,7 @@ class GridDropEngine {
|
|
|
246
261
|
return;
|
|
247
262
|
}
|
|
248
263
|
this._activeElement.isLocked = !this._activeElement.isLocked;
|
|
264
|
+
this._elementsUpdate = true;
|
|
249
265
|
|
|
250
266
|
this.eventBus.emit('gridDrop:toggleLock', {
|
|
251
267
|
elementId: this._activeElement.id,
|
|
@@ -436,6 +452,8 @@ class GridDropEngine {
|
|
|
436
452
|
// 단순 클릭 시에는 위치 변경 없음
|
|
437
453
|
const { col, row } = this._targetCell;
|
|
438
454
|
if (this._dragState.isDragging) {
|
|
455
|
+
if (col === this._activeElement.position.col && row === this._activeElement.position.row) return;
|
|
456
|
+
|
|
439
457
|
this._activeElement.position.col = col;
|
|
440
458
|
this._activeElement.position.row = row;
|
|
441
459
|
|
|
@@ -500,6 +518,8 @@ class GridDropEngine {
|
|
|
500
518
|
const { gridColumns, gridRows } = this._activeConfig;
|
|
501
519
|
|
|
502
520
|
const activeSection = document.querySelector('.section-active');
|
|
521
|
+
if (!activeSection) return;
|
|
522
|
+
|
|
503
523
|
const { dx, dy } = this._calculateDrag(
|
|
504
524
|
{ x: event.clientX, y: event.clientY },
|
|
505
525
|
{
|