polarvo-layout 1.0.19 → 1.0.21

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 (28) hide show
  1. package/package.json +1 -1
  2. package/src/components/FastMenu/DesignFastmenu.vue +2 -2
  3. package/src/components/FastMenu/DisplayFastMenu.vue +2 -2
  4. package/src/components/FastMenu/LayoutFastMenu.vue +18 -10
  5. package/src/components/Layout/CanvasContainer.vue +30 -6
  6. package/src/components/SideBar/ElementSideBar.vue +2 -2
  7. package/src/components/SideBar/LayoutSettingSideBar.vue +18 -10
  8. package/src/components/SideBar/LayoutSideBar.vue +19 -8
  9. package/src/configs/index.js +1 -0
  10. package/src/core/engines/DisplayEngine.js +72 -64
  11. package/src/core/engines/FreeDropEngine.js +40 -42
  12. package/src/core/engines/GridDropEngine.js +33 -43
  13. package/src/core/engines/HistoryEngine.js +55 -51
  14. package/src/core/engines/LayoutEngine.js +89 -68
  15. package/src/core/engines/originals/FreeDropEngine_0402.js +786 -0
  16. package/src/core/engines/originals/GridDropEngine_0402.js +557 -0
  17. package/src/core/managers/EngineManager.js +93 -131
  18. package/src/core/managers/originals/EngineManager_0402.js +826 -0
  19. package/src/library/DisplayLibrary.js +14 -17
  20. package/src/library/FreeDropLibrary.js +6 -4
  21. package/src/library/GridDropLibrary.js +3 -5
  22. package/src/library/LayoutLibrary.js +45 -48
  23. package/src/library/originals/DisplayLibrary_0402.js +137 -0
  24. package/src/library/originals/FreeDropLibrary_0402.js +185 -0
  25. package/src/library/originals/GridDropLibrary_0402.js +202 -0
  26. package/src/library/originals/HistoryLibrary_0402.js +98 -0
  27. package/src/library/originals/LayoutLibrary_0402.js +264 -0
  28. package/src/utils/index.js +8 -3
@@ -4,11 +4,16 @@ import { nextTick } from 'vue';
4
4
  import EventBus from './EventBus.js';
5
5
  import ApiManager from './ApiManager.js';
6
6
  import utils from '../../utils/index.js';
7
- const { dataConverter } = utils;
7
+ const { dataConverter, calculateAspectRatio } = utils;
8
8
 
9
9
  import createDefaultConfig from '../../configs/index.js';
10
10
  const { initialState, resource } = createDefaultConfig();
11
11
 
12
+ const CONFIG_LIMITS = {
13
+ column: { min: 1, max: 12, name: '열 개수' },
14
+ row: { min: 1, max: 50, name: '행 개수' },
15
+ gap: { min: 0, max: 12, name: '갭 크기' },
16
+ };
12
17
  // event 발행 시 'system'으로 시작하는 이름 사용
13
18
  class EngineManager {
14
19
  constructor(config = {}) {
@@ -26,9 +31,6 @@ class EngineManager {
26
31
  ...config.initialState,
27
32
  };
28
33
 
29
- // (캐시) layoutData 및 activeSection 변경 감지용 -> activeData getter에서 사용
30
- this._layoutData = null;
31
-
32
34
  // (캐시) dataConverter에서 사용하는 현재 컨테이너 크기
33
35
  this._containerWidth = 1920;
34
36
  this._containerHeight = 1080;
@@ -41,10 +43,11 @@ class EngineManager {
41
43
  * @return {object} - layoutData 중 activeSection 데이터 객체
42
44
  */
43
45
  get activeData() {
44
- if (!this._layoutData || !this.state.activeSection) {
46
+ if (!this.state.layoutData || !this.state.activeSection) {
45
47
  return null;
46
48
  }
47
- return this._layoutData[this.state.activeSection];
49
+
50
+ return this.state.layoutData[this.state.activeSection];
48
51
  }
49
52
 
50
53
  /** 상태 정보 반환
@@ -53,7 +56,7 @@ class EngineManager {
53
56
  getState() {
54
57
  return {
55
58
  elements: this.state.elements,
56
- layoutData: cloneDeep(this._layoutData),
59
+ layoutData: cloneDeep(this.state.layoutData),
57
60
  activeData: cloneDeep(this.activeData),
58
61
  activeSection: this.state.activeSection,
59
62
  };
@@ -64,7 +67,7 @@ class EngineManager {
64
67
  */
65
68
  setState(state) {
66
69
  this.state.elements = state.elements;
67
- this._layoutData = state.layoutData;
70
+ this.state.layoutData = state.layoutData;
68
71
  this.setActiveSection(state.activeSection);
69
72
 
70
73
  this.eventBus.emit('system:restoredState', {
@@ -78,52 +81,18 @@ class EngineManager {
78
81
  * @param {object} updates - 업데이트할 설정 값 객체
79
82
  */
80
83
  _updateActiveSectionConfig(type, updates) {
81
- if (!this._layoutData || !this.state.activeSection) {
84
+ if (!this.state.layoutData || !this.state.activeSection) {
82
85
  console.error('[EngineManager] activeData를 업데이트할 수 없습니다.');
83
86
  return;
84
87
  }
85
88
 
86
- Object.keys(updates).forEach((key) => {
87
- if (!(key in this._layoutData[this.state.activeSection])) {
88
- if (key == 'elementIds') {
89
- this._layoutData[this.state.activeSection][key] = [];
90
- } else {
91
- this._layoutData[this.state.activeSection][key] = null;
92
- }
93
- }
94
- });
95
-
96
- // 원본 데이터 업데이트
97
- this._layoutData[this.state.activeSection] = {
98
- ...this._layoutData[this.state.activeSection],
99
- ...updates,
100
- };
101
-
102
- // 이벤트 발행
103
89
  this.eventBus.emit('system:updateActiveSectionConfig', {
104
- type: type,
105
- layoutData: cloneDeep(this._layoutData),
106
- activeData: cloneDeep(this.activeData),
90
+ $section: this.state.activeSection,
91
+ changes: { ...updates },
107
92
  timestamp: Date.now(),
108
93
  });
109
94
  }
110
95
 
111
- // /** [내부함수] activeSection의 elementIds 업데이트
112
- // * @param {Array} elementIds - 새로운 elementIds 배열
113
- // */
114
- // _updateActiveElementIds(elementIds) {
115
- // if (!this._layoutData || !this.state.activeSection) {
116
- // console.error('[EngineManager] activeData를 업데이트할 수 없습니다.');
117
- // return;
118
- // }
119
-
120
- // if (!('elementIds' in this._layoutData[this.state.activeSection])) {
121
- // this._layoutData[this.state.activeSection].elementIds = [];
122
- // }
123
-
124
- // this._layoutData[this.state.activeSection].elementIds = elementIds;
125
- // }
126
-
127
96
  /** 현재 컨테이너 크기 설정 */
128
97
  setContainerSize() {
129
98
  try {
@@ -164,8 +133,7 @@ class EngineManager {
164
133
  this.state.elements = denormalizedElements.map((x) => (x.section ? { ...x } : { ...x, section: 'section1' }));
165
134
 
166
135
  this.eventBus.emit('system:setElements', {
167
- layoutData: cloneDeep(this._layoutData),
168
- elements: this.state.elements,
136
+ $elements: this.state.elements,
169
137
  timestamp: Date.now(),
170
138
  });
171
139
  }
@@ -203,31 +171,17 @@ class EngineManager {
203
171
  /** ---------------------------------- display Engine ---------------------------------- **/
204
172
  /** [내부함수] displayEngine 연결 설정 */
205
173
  _setupDisplayEngineConnections() {
206
- this._subscribe('display:updateDisplayMode', ({ displaySize, prev }) => {
207
- this._updateElementsScale(prev.displaySize, displaySize);
208
- });
209
-
210
- this._subscribe('display:updateDisplaySize', ({ displaySize, prev }) => {
211
- this._updateElementsScale(prev.displaySize, displaySize);
174
+ this._subscribe('display:displayChanged', ({ $displaySize, prev }) => {
175
+ this._updateElementsScale(prev.displaySize, $displaySize);
212
176
  });
213
177
  }
214
178
 
215
179
  _updateElementsScale(oldValue, newValue) {
216
180
  const oldWidth = oldValue.px;
217
- const oldHeight =
218
- oldValue.px /
219
- oldValue.aspectRatio
220
- .split('/')
221
- .map(Number)
222
- .reduce((a, b) => a / b);
181
+ const oldHeight = oldValue.px / calculateAspectRatio(oldValue.aspectRatio);
223
182
 
224
183
  const newWidth = newValue.px;
225
- const newHeight =
226
- newValue.px /
227
- newValue.aspectRatio
228
- .split('/')
229
- .map(Number)
230
- .reduce((a, b) => a / b);
184
+ const newHeight = newValue.px / calculateAspectRatio(newValue.aspectRatio);
231
185
 
232
186
  nextTick(() => {
233
187
  this.setContainerSize();
@@ -235,11 +189,11 @@ class EngineManager {
235
189
  const normalizeElements = dataConverter.normalize(this.state.elements, oldWidth, oldHeight);
236
190
  const denormalizedElements = dataConverter.denormalize(normalizeElements, newWidth, newHeight);
237
191
 
238
- this.state.elements = cloneDeep(denormalizedElements);
192
+ this.state.elements = denormalizedElements;
239
193
 
240
- this.eventBus.emit('system:requestUpdateElements', {
194
+ this.eventBus.emit('system:requestElementsScale', {
241
195
  historyEvent: true,
242
- elements: cloneDeep(this.state.elements),
196
+ $elements: this.state.elements,
243
197
  timestamp: Date.now(),
244
198
  });
245
199
  });
@@ -249,25 +203,17 @@ class EngineManager {
249
203
  /** [내부함수] layoutEngine 연결 설정 */
250
204
  _setupLayoutEngineConnections() {
251
205
  // layoutName 변경에 따른 activeSection 초기화
252
- this._subscribe('layout:setLayoutName', ({ screenConfig }) => {
253
- this._layoutData = screenConfig.layoutData;
206
+ this._subscribe('layout:setLayoutName', ({ $layoutData }) => {
207
+ this.state.layoutData = $layoutData;
254
208
  this.setActiveSection('section1', true);
255
-
256
- this.eventBus.emit('system:updateLayoutData', {
257
- layoutData: cloneDeep(this._layoutData),
258
- activeData: cloneDeep(this.activeData),
259
- timestamp: Date.now(),
260
- });
261
209
  });
262
- this._subscribe('layout:updateLayoutName', ({ layoutData }) => {
263
- this._layoutData = layoutData;
210
+ this._subscribe('layout:updateLayoutName', ({ $layoutData }) => {
211
+ this.state.layoutData = $layoutData;
264
212
  this.setActiveSection(null, true);
213
+ });
265
214
 
266
- this.eventBus.emit('system:updateLayoutData', {
267
- layoutData: cloneDeep(this._layoutData),
268
- activeData: cloneDeep(this.activeData),
269
- timestamp: Date.now(),
270
- });
215
+ this._subscribe('layout:updateLayoutData', ({ $layoutData, activeSection }) => {
216
+ this.state.layoutData = $layoutData;
271
217
  });
272
218
  }
273
219
 
@@ -281,8 +227,9 @@ class EngineManager {
281
227
  this.state.activeSection = name;
282
228
 
283
229
  this.eventBus.emit('system:updateActiveSection', {
284
- activeSection: name,
285
- activeData: cloneDeep(this.activeData),
230
+ $section: name,
231
+ $mode: this.activeData?.mode,
232
+ $config: this.activeData?.config,
286
233
  timestamp: Date.now(),
287
234
  });
288
235
  }
@@ -299,9 +246,23 @@ class EngineManager {
299
246
  if (!this.activeData || this.activeData.mode === mode) return;
300
247
  const updates = {
301
248
  mode: mode,
302
- elementIds: this.state.elements.filter((x) => x.mode === mode).map((x) => x.id),
249
+ elementIds: this.state.elements.filter((x) => x.section === this.state.activeSection && x.mode === mode).map((x) => x.id),
303
250
  };
304
251
 
252
+ if (mode === 'grid') {
253
+ updates.config = {
254
+ gridColumns: this.activeData.config?.gridColumns || 2,
255
+ gridRows: this.activeData.config?.gridRows || 2,
256
+ gridGap: this.activeData.config?.gridGap || 4,
257
+ gridRowRatio: this.activeData.config?.gridRowRatio || [],
258
+ gridColumnRatio: this.activeData.config?.gridColumnRatio || [],
259
+ };
260
+ } else if (mode === 'free') {
261
+ updates.config = {
262
+ showGuideLine: this.activeData.config?.showGuideLine || false,
263
+ };
264
+ }
265
+
305
266
  this._updateActiveSectionConfig('mode', updates);
306
267
  }
307
268
 
@@ -311,17 +272,18 @@ class EngineManager {
311
272
  */
312
273
  setSectionConfig(type, config) {
313
274
  if (!['column', 'row', 'gap', 'guideLine'].includes(type)) {
314
- console.error('[EngineManager] 잘못된 config 타입:', type);
315
- return;
275
+ return { result: false, message: '입력할 없는 config 타입입니다.' };
316
276
  }
317
277
 
318
278
  if (!this.activeData) return;
319
279
 
320
- const limits = {
321
- column: { min: 1, max: 12, name: ' 개수' },
322
- row: { min: 1, max: 50, name: '행 개수' },
323
- gap: { min: 0, max: 12, name: ' 크기' },
324
- };
280
+ const limit = CONFIG_LIMITS[type];
281
+ const result = { result: true, message: 'config가 성공적으로 변경되었습니다.' };
282
+ // const limits = {
283
+ // column: { min: 1, max: 12, name: ' 개수' },
284
+ // row: { min: 1, max: 50, name: '행 개수' },
285
+ // gap: { min: 0, max: 12, name: '갭 크기' },
286
+ // };
325
287
 
326
288
  let newConfig = {};
327
289
  let value = config;
@@ -329,13 +291,15 @@ class EngineManager {
329
291
  if (type === 'guideLine') {
330
292
  newConfig = { showGuideLine: value };
331
293
  } else {
332
- if (value < limits[type].min) {
333
- alert(`설정 가능한 최소 ${limits[type].name}는 ${limits[type].min}입니다.`);
294
+ if (value < limit.min) {
295
+ result.result = false;
296
+ result.message = `설정 가능한 최소 ${limit.name}는 ${limit.min}입니다.`;
334
297
  }
335
- if (value > limits[type].max) {
336
- alert(`설정 가능한 최대 ${limits[type].name}는 ${limits[type].max}입니다.`);
298
+ if (value > limit.max) {
299
+ result.result = false;
300
+ result.message = `설정 가능한 최대 ${limit.name}는 ${limit.max}입니다.`;
337
301
  }
338
- value = Math.min(Math.max(limits[type].min, value), limits[type].max);
302
+ value = Math.min(Math.max(limit.min, value), limit.max);
339
303
 
340
304
  if (type === 'column') {
341
305
  newConfig = { gridColumns: value };
@@ -348,6 +312,8 @@ class EngineManager {
348
312
 
349
313
  const updates = { config: { ...this.activeData.config, ...newConfig } };
350
314
  this._updateActiveSectionConfig('config', updates);
315
+
316
+ return result;
351
317
  }
352
318
 
353
319
  /** ---------------------------------- freeDrop Engine ---------------------------------- **/
@@ -358,19 +324,14 @@ class EngineManager {
358
324
  if (this.state.elements.some((el) => el.id === element.id)) {
359
325
  return;
360
326
  }
361
-
327
+
362
328
  this.state.elements.push(element);
363
329
  this._updateActiveSectionConfig('elementIds', { elementIds: [...this.activeData.elementIds, element.id] });
364
- // this._updateActiveElementIds([...this.activeData.elementIds, element.id]);
365
330
 
366
- console.log('this.state.elements', this.state.elements)
367
- this.eventBus.emit('system:requestUpdateData', {
331
+ this.eventBus.emit('system:requestUpdateElement', {
368
332
  action: 'add',
369
- elements: this.state.elements,
370
- elementId: element.id,
371
- layoutData: cloneDeep(this._layoutData),
372
- activeData: cloneDeep(this.activeData),
373
- activeSection: this.state.activeSection,
333
+ $elements: this.state.elements,
334
+ $elementId: element.id,
374
335
  timestamp: Date.now(),
375
336
  });
376
337
  });
@@ -381,13 +342,10 @@ class EngineManager {
381
342
  this._updateActiveSectionConfig('elementIds', { elementIds: this.activeData.elementIds.filter((elId) => elId !== elementId) });
382
343
  // this._updateActiveElementIds(this.activeData.elementIds.filter((elId) => elId !== elementId));
383
344
 
384
- this.eventBus.emit('system:requestUpdateData', {
345
+ this.eventBus.emit('system:requestUpdateElement', {
385
346
  action: 'delete',
386
- elements: this.state.elements,
387
- elementId: null,
388
- layoutData: cloneDeep(this._layoutData),
389
- activeData: cloneDeep(this.activeData),
390
- activeSection: this.state.activeSection,
347
+ $elements: this.state.elements,
348
+ $elementId: null,
391
349
  timestamp: Date.now(),
392
350
  });
393
351
  });
@@ -400,9 +358,9 @@ class EngineManager {
400
358
  element.size = size;
401
359
  }
402
360
 
403
- this.eventBus.emit('system:requestUpdateElements', {
361
+ this.eventBus.emit('system:requestElementsScale', {
404
362
  historyEvent: historyEvent,
405
- elements: cloneDeep(this.state.elements),
363
+ $elements: this.state.elements,
406
364
  guides: guides,
407
365
  timestamp: Date.now(),
408
366
  });
@@ -418,13 +376,10 @@ class EngineManager {
418
376
  this._updateActiveSectionConfig('elementIds', { elementIds: [...this.activeData.elementIds, element.id] });
419
377
  // this._updateActiveElementIds([...this.activeData.elementIds, element.id]);
420
378
 
421
- this.eventBus.emit('system:requestUpdateData', {
379
+ this.eventBus.emit('system:requestUpdateElement', {
422
380
  action: 'add',
423
- elements: this.state.elements,
424
- elementId: element.id,
425
- layoutData: cloneDeep(this._layoutData),
426
- activeData: cloneDeep(this.activeData),
427
- activeSection: this.state.activeSection,
381
+ $elements: this.state.elements,
382
+ $elementId: element.id,
428
383
  timestamp: Date.now(),
429
384
  });
430
385
  });
@@ -435,13 +390,10 @@ class EngineManager {
435
390
  this._updateActiveSectionConfig('elementIds', { elementIds: this.activeData.elementIds.filter((elId) => elId !== elementId) });
436
391
  // this._updateActiveElementIds(this.activeData.elementIds.filter((elId) => elId !== elementId));
437
392
 
438
- this.eventBus.emit('system:requestUpdateData', {
393
+ this.eventBus.emit('system:requestUpdateElement', {
439
394
  action: 'delete',
440
- elements: this.state.elements,
441
- elementId: null,
442
- layoutData: cloneDeep(this._layoutData),
443
- activeData: cloneDeep(this.activeData),
444
- activeSection: this.state.activeSection,
395
+ $elements: this.state.elements,
396
+ $elementId: null,
445
397
  timestamp: Date.now(),
446
398
  });
447
399
  });
@@ -456,9 +408,9 @@ class EngineManager {
456
408
  }
457
409
  }
458
410
 
459
- this.eventBus.emit('system:requestUpdateElements', {
411
+ this.eventBus.emit('system:requestElementsScale', {
460
412
  historyEvent: historyEvent,
461
- elements: cloneDeep(this.state.elements),
413
+ $elements: this.state.elements,
462
414
  timestamp: Date.now(),
463
415
  });
464
416
  });
@@ -648,7 +600,7 @@ class EngineManager {
648
600
  this.engines.clear();
649
601
  this._initialized = false;
650
602
 
651
- this._layoutData = null;
603
+ this.state.layoutData = null;
652
604
 
653
605
  this.state = {};
654
606
  this.eventBus = null;
@@ -658,16 +610,26 @@ class EngineManager {
658
610
 
659
611
  /** 엔진 초기화 */
660
612
  resetEngines() {
613
+ console.log('[EngineManager] 엔진 초기화 시작');
614
+
615
+ for (const [name, engine] of this.engines) {
616
+ if (typeof engine.reset === 'function') {
617
+ engine.reset();
618
+ }
619
+ }
620
+
661
621
  this.state = {
662
622
  ...initialState,
663
623
  ...this.config.initialState,
664
624
  };
665
-
625
+ this._dataUpdate = false;
666
626
  this._layoutData = null;
667
627
 
668
628
  this.eventBus.emit('system:enginesReset', {
669
629
  timestamp: Date.now(),
670
630
  });
631
+
632
+ console.log('[EngineManager] 엔진 리셋 완료');
671
633
  }
672
634
 
673
635
  /** ---------------------------------- 캡슐화 API 메소드 ---------------------------------- **/