polarvo-layout 1.0.21 → 1.0.23
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 +9 -8
- package/src/components/MiniMenu/LayoutMiniMenu.vue +2 -3
- package/src/components/SideBar/LayoutSideBar.vue +19 -4
- package/src/core/engines/LayoutEngine.js +11 -3
- package/src/core/managers/EngineManager.js +62 -2
- package/src/library/LayoutLibrary.js +1 -1
package/package.json
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
<div
|
|
3
3
|
:id="preview ? 'previewLayout' : 'baseLayout'"
|
|
4
4
|
class="grid h-full p-2 bg-transparent rounded-lg"
|
|
5
|
-
:class="[layoutType + '-layout', `grid-cols-${gridNumber?.column}`, 'mb-4'
|
|
5
|
+
:class="[layoutType + '-layout', `grid-cols-${gridNumber?.column}`, { 'mb-4': layoutType === 'three-column-split' }]"
|
|
6
6
|
:style="getBaseStyle"
|
|
7
|
+
@contextmenu.prevent
|
|
7
8
|
>
|
|
8
9
|
<template v-for="(section, key) in layoutData" :key="key">
|
|
9
10
|
<PolarLayout
|
|
@@ -50,9 +51,9 @@ import FreeLayout from './FreeLayout.vue';
|
|
|
50
51
|
import GridLayout from './GridLayout.vue';
|
|
51
52
|
|
|
52
53
|
// 우클릭 방지
|
|
53
|
-
window.oncontextmenu = () => {
|
|
54
|
-
|
|
55
|
-
};
|
|
54
|
+
// window.oncontextmenu = () => {
|
|
55
|
+
// return false;
|
|
56
|
+
// };
|
|
56
57
|
|
|
57
58
|
const emit = defineEmits(['click:section']);
|
|
58
59
|
const props = defineProps({
|
|
@@ -67,7 +68,7 @@ const props = defineProps({
|
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
const layoutType = computed(() => {
|
|
70
|
-
if (layoutName.value.eng
|
|
71
|
+
if (layoutName.value.eng === null) return 'no-split';
|
|
71
72
|
return layoutName.value.eng
|
|
72
73
|
.replace(/([A-Z])/g, '-$1')
|
|
73
74
|
.toLowerCase()
|
|
@@ -86,8 +87,8 @@ const getBaseStyle = computed(() => {
|
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
const getSectionStyle = (section) => ({
|
|
89
|
-
'--grid-columns': section.config?.gridColumns
|
|
90
|
-
'--grid-gap': `${section.config?.gridGap
|
|
90
|
+
'--grid-columns': section.config?.gridColumns ?? 3,
|
|
91
|
+
'--grid-gap': `${section.config?.gridGap ?? 5}px`,
|
|
91
92
|
position: 'relative',
|
|
92
93
|
});
|
|
93
94
|
|
|
@@ -95,7 +96,7 @@ const getSectionClass = (key, section) => ({
|
|
|
95
96
|
[key]: true,
|
|
96
97
|
[section.mode]: true,
|
|
97
98
|
'section-active': activeSection.value === key,
|
|
98
|
-
disabled: activeSection.value
|
|
99
|
+
disabled: activeSection.value !== key && !props.preview,
|
|
99
100
|
});
|
|
100
101
|
</script>
|
|
101
102
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<div class="grid gap-1 grid-cols-1 mx-1 mb-3">
|
|
6
6
|
<div
|
|
7
7
|
class="item flex flex-col items-center gap-1 p-2 border border-gray-200 rounded-md bg-white cursor-pointer hover:border-gray-400"
|
|
8
|
-
:class="{ active: layoutName.eng
|
|
8
|
+
:class="{ active: layoutName.eng === 'NoSplit' }"
|
|
9
9
|
@click="setLayoutName('NoSplit')"
|
|
10
10
|
>
|
|
11
11
|
<component class="text-gray-600" :is="layoutSource.noSplit.icon"></component>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
v-for="(layout, key) in layoutSource.split"
|
|
19
19
|
:key="key"
|
|
20
20
|
class="item flex flex-col items-center gap-1 p-2 border border-gray-200 rounded-md bg-white cursor-pointer hover:border-gray-400"
|
|
21
|
-
:class="{ active: layoutName.eng
|
|
21
|
+
:class="{ active: layoutName.eng === key }"
|
|
22
22
|
@click="setLayoutName(key)"
|
|
23
23
|
>
|
|
24
24
|
<component class="text-gray-600" :is="layout.icon"></component>
|
|
@@ -46,7 +46,6 @@ watch(
|
|
|
46
46
|
() => {
|
|
47
47
|
emits('click:layout');
|
|
48
48
|
},
|
|
49
|
-
{ deep: true }
|
|
50
49
|
);
|
|
51
50
|
</script>
|
|
52
51
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
</button>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
|
-
<div v-if="layoutName.eng
|
|
13
|
+
<div v-if="layoutName.eng !== 'NoSplit'">
|
|
14
14
|
<h5 class="mt-4 block text-sm font-bold text-gray-600">레이아웃 설정</h5>
|
|
15
15
|
<div class="mt-2 border border-gray-200 rounded-md bg-gray-100">
|
|
16
16
|
<div class="px-4 py-2">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
type="number"
|
|
22
22
|
id="section-gap"
|
|
23
23
|
:value="gapSize"
|
|
24
|
-
@change="
|
|
24
|
+
@change="handleGapSize($event)"
|
|
25
25
|
/>
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
@@ -91,10 +91,25 @@ function handleGridRatio(type, index, event) {
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
const { result, message } = setRatio(type, index, value)
|
|
94
|
+
const { result, message } = setRatio(type, index, Number(value));
|
|
95
95
|
|
|
96
96
|
if (!result) {
|
|
97
|
-
alert(message)
|
|
97
|
+
alert(message);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleGapSize(event) {
|
|
102
|
+
const value = event.target.value;
|
|
103
|
+
if (Number(value) < 0 || isNaN(Number(value))) {
|
|
104
|
+
alert('갭 크기는 0 이상의 숫자여야 합니다.');
|
|
105
|
+
event.target.value = gapSize.value; // 원래 값으로 복원
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const { result, message } = setGapSize(Number(value));
|
|
110
|
+
|
|
111
|
+
if (!result) {
|
|
112
|
+
alert(message);
|
|
98
113
|
}
|
|
99
114
|
}
|
|
100
115
|
</script>
|
|
@@ -171,6 +171,7 @@ class LayoutEngine {
|
|
|
171
171
|
// 1. layoutName(내부 변수) 변경
|
|
172
172
|
this._layoutName = name;
|
|
173
173
|
|
|
174
|
+
const currentGridRatio = { ...this.gridRatio };
|
|
174
175
|
const { layoutData: userData, gridRatio, gapSize } = screenConfig || {};
|
|
175
176
|
// 2. layoutData 기본값 세팅
|
|
176
177
|
this._prepareLayoutData(name, userData);
|
|
@@ -179,7 +180,7 @@ class LayoutEngine {
|
|
|
179
180
|
this._prepareLayoutConfig(name, gridRatio ?? null, gapSize ?? null);
|
|
180
181
|
|
|
181
182
|
// 초기 설정인 경우 설정값 전파
|
|
182
|
-
if (
|
|
183
|
+
if (setting) {
|
|
183
184
|
this.eventBus.emit('layout:setLayoutName', {
|
|
184
185
|
$screenConfig: screenConfig,
|
|
185
186
|
$layoutData: this.layoutData,
|
|
@@ -196,6 +197,7 @@ class LayoutEngine {
|
|
|
196
197
|
$gridNumber: this.gridNumber,
|
|
197
198
|
$gridRatio: this.gridRatio,
|
|
198
199
|
$gapSize: this.gapSize,
|
|
200
|
+
$prev: { gridRatio: currentGridRatio },
|
|
199
201
|
timestamp: Date.now(),
|
|
200
202
|
});
|
|
201
203
|
}
|
|
@@ -267,14 +269,14 @@ class LayoutEngine {
|
|
|
267
269
|
* @param {number} size - 새로운 갭 사이즈
|
|
268
270
|
*/
|
|
269
271
|
setGapSize(size) {
|
|
270
|
-
if (Number(size) < 0 || isNaN(Number(size))) return;
|
|
271
|
-
|
|
272
272
|
this.gapSize = size;
|
|
273
273
|
|
|
274
274
|
this.eventBus.emit('layout:updateGapSize', {
|
|
275
275
|
$gapSize: this.gapSize,
|
|
276
276
|
timestamp: Date.now(),
|
|
277
277
|
});
|
|
278
|
+
|
|
279
|
+
return { result: true, message: '갭 사이즈가 성공적으로 변경되었습니다.' };
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
/** 섹션 비율 변경
|
|
@@ -283,6 +285,11 @@ class LayoutEngine {
|
|
|
283
285
|
* @param {number} ratio - 새로운 비율 값
|
|
284
286
|
*/
|
|
285
287
|
setRatio(type, index, ratio) {
|
|
288
|
+
const prevRatio = {
|
|
289
|
+
column: [...this.gridRatio.column],
|
|
290
|
+
row: [...this.gridRatio.row],
|
|
291
|
+
};
|
|
292
|
+
|
|
286
293
|
const ratioArray = this.gridRatio[type];
|
|
287
294
|
ratioArray[index] = Number(ratio);
|
|
288
295
|
|
|
@@ -328,6 +335,7 @@ class LayoutEngine {
|
|
|
328
335
|
this.eventBus.emit('layout:updateRatio', {
|
|
329
336
|
$type: type,
|
|
330
337
|
$gridRatio: this.gridRatio,
|
|
338
|
+
$prev: { gridRatio: prevRatio },
|
|
331
339
|
timestamp: Date.now(),
|
|
332
340
|
});
|
|
333
341
|
|
|
@@ -34,6 +34,7 @@ class EngineManager {
|
|
|
34
34
|
// (캐시) dataConverter에서 사용하는 현재 컨테이너 크기
|
|
35
35
|
this._containerWidth = 1920;
|
|
36
36
|
this._containerHeight = 1080;
|
|
37
|
+
this._displaySize = { px: 1920, aspectRatio: '16/9' };
|
|
37
38
|
|
|
38
39
|
this._subscriptions = [];
|
|
39
40
|
this._initialized = false;
|
|
@@ -172,6 +173,7 @@ class EngineManager {
|
|
|
172
173
|
/** [내부함수] displayEngine 연결 설정 */
|
|
173
174
|
_setupDisplayEngineConnections() {
|
|
174
175
|
this._subscribe('display:displayChanged', ({ $displaySize, prev }) => {
|
|
176
|
+
this._displaySize = $displaySize;
|
|
175
177
|
this._updateElementsScale(prev.displaySize, $displaySize);
|
|
176
178
|
});
|
|
177
179
|
}
|
|
@@ -202,14 +204,25 @@ class EngineManager {
|
|
|
202
204
|
/** ---------------------------------- layout Engine ---------------------------------- **/
|
|
203
205
|
/** [내부함수] layoutEngine 연결 설정 */
|
|
204
206
|
_setupLayoutEngineConnections() {
|
|
207
|
+
this._subscribe('layout:updateRatio', ({ $type, $gridRatio, $prev }) => {
|
|
208
|
+
const prevGridRatio = $prev?.gridRatio || { column: [100], row: [100] };
|
|
209
|
+
this._updateElementsScaleByRatio($type, prevGridRatio, $gridRatio);
|
|
210
|
+
});
|
|
211
|
+
|
|
205
212
|
// layoutName 변경에 따른 activeSection 초기화
|
|
206
|
-
this._subscribe('layout:setLayoutName', ({ $layoutData }) => {
|
|
213
|
+
this._subscribe('layout:setLayoutName', ({ $layoutData, $screenConfig }) => {
|
|
214
|
+
this._displaySize = { ...$screenConfig.displaySize };
|
|
215
|
+
|
|
207
216
|
this.state.layoutData = $layoutData;
|
|
208
217
|
this.setActiveSection('section1', true);
|
|
209
218
|
});
|
|
210
|
-
|
|
219
|
+
|
|
220
|
+
this._subscribe('layout:updateLayoutName', ({ $layoutData, $gridRatio, $prev }) => {
|
|
211
221
|
this.state.layoutData = $layoutData;
|
|
212
222
|
this.setActiveSection(null, true);
|
|
223
|
+
|
|
224
|
+
const prevGridRatio = $prev?.gridRatio || { column: [100], row: [100] };
|
|
225
|
+
this._updateElementsScaleByRatio('column', prevGridRatio, $gridRatio);
|
|
213
226
|
});
|
|
214
227
|
|
|
215
228
|
this._subscribe('layout:updateLayoutData', ({ $layoutData, activeSection }) => {
|
|
@@ -217,6 +230,53 @@ class EngineManager {
|
|
|
217
230
|
});
|
|
218
231
|
}
|
|
219
232
|
|
|
233
|
+
_updateElementsScaleByRatio(type, oldValue, newValue) {
|
|
234
|
+
const oldLength = oldValue[type].length;
|
|
235
|
+
const newLength = newValue[type].length;
|
|
236
|
+
|
|
237
|
+
let newDisplaySize = { ...this._displaySize };
|
|
238
|
+
|
|
239
|
+
// 열 또는 행 개수가 늘어나는 경우 - 요소 크기 줄이기
|
|
240
|
+
if (oldLength < newLength) {
|
|
241
|
+
newDisplaySize.px = newDisplaySize.px / newLength;
|
|
242
|
+
this._updateElementsScale(this._displaySize, newDisplaySize);
|
|
243
|
+
}
|
|
244
|
+
// 열 또는 행 개수가 줄어드는 경우 - 요소 크기 늘리기
|
|
245
|
+
else if (oldLength > newLength) {
|
|
246
|
+
newDisplaySize.px = newDisplaySize.px / oldLength;
|
|
247
|
+
this._updateElementsScale(newDisplaySize, this._displaySize);
|
|
248
|
+
}
|
|
249
|
+
// 열 또는 행 개수는 동일하지만 비율이 변경되는 경우 - 요소 크기 조정
|
|
250
|
+
else {
|
|
251
|
+
this.state.elements = this.state.elements.map((el) => {
|
|
252
|
+
const sectionIndex = el.section ? parseInt(el.section.replace('section', '')) - 1 : 0;
|
|
253
|
+
const oldRatio = oldValue[type][sectionIndex] / 100;
|
|
254
|
+
const newRatio = newValue[type][sectionIndex] / 100;
|
|
255
|
+
|
|
256
|
+
const scaleFactor = newRatio / oldRatio;
|
|
257
|
+
return type === 'column'
|
|
258
|
+
? {
|
|
259
|
+
...el,
|
|
260
|
+
position: { ...el.position, x: el.position.x * scaleFactor },
|
|
261
|
+
size: { ...el.size, w: el.size.w * scaleFactor },
|
|
262
|
+
}
|
|
263
|
+
: {
|
|
264
|
+
...el,
|
|
265
|
+
position: { ...el.position, y: el.position.y * scaleFactor },
|
|
266
|
+
size: { ...el.size, h: el.size.h * scaleFactor },
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// nextTick(() => {
|
|
272
|
+
this.eventBus.emit('system:requestElementsScale', {
|
|
273
|
+
historyEvent: true,
|
|
274
|
+
$elements: this.state.elements,
|
|
275
|
+
timestamp: Date.now(),
|
|
276
|
+
});
|
|
277
|
+
// });
|
|
278
|
+
}
|
|
279
|
+
|
|
220
280
|
/** activeSection 변경
|
|
221
281
|
* @param {string} name - 섹션 이름
|
|
222
282
|
* @param {boolean} setting - 설정모드 여부
|