polarvo-layout 1.0.52 → 1.0.54

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polarvo-layout",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "type": "module",
5
5
  "author": "unigence <unigencelab@gmail.com>",
6
6
  "repository": {
@@ -58,7 +58,7 @@ async function saveSetting() {
58
58
  },
59
59
  };
60
60
 
61
- const result = await props.polarvo.userApi.save(saveData);
61
+ const result = await props.polarvo.userApi?.save(saveData);
62
62
  // 저장 성공 시 실행할 액션이 있다면 아래에 작성
63
63
  if (result) {
64
64
  }
@@ -21,8 +21,8 @@
21
21
  type="number"
22
22
  name="px"
23
23
  aria-label="픽셀 단위 크기"
24
- min="600"
25
- max="1200"
24
+ min="1"
25
+ max="1920"
26
26
  step="50"
27
27
  :value="displaySize?.px"
28
28
  class="border-b-2 pr-1 border-white focus:outline-none focus:border-b-2 focus:border-blue-500"
@@ -182,7 +182,7 @@ import { useRouter } from 'vue-router';
182
182
  const router = useRouter();
183
183
 
184
184
  async function logout() {
185
- const result = await props.polarvo.userApi.logout();
185
+ const result = await props.polarvo.userApi?.logout();
186
186
  if (result) {
187
187
  // 로그아웃 성공 시 라우터 이동
188
188
  //서브도메인 제거하기위해 아래와 같이 코드 짰습니다
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="canvas-wrapper w-full h-full max-h-[765px] p-4 overflow-y-auto overflow-x-hidden">
3
3
  <div id="canvas-container" class="border rounded-md w-full h-full bg-white" :style="containerStyle">
4
- <BaseLayout ref="baseLayoutRef" :polarvo="polarvo" class="relative" />
4
+ <BaseLayout :polarvo="polarvo" class="relative" />
5
5
  <div
6
6
  class="absolute bottom-4 left-4 right-4 bg-gray-50 flex items-center justify-center text-gray-400 rounded-lg hover:bg-gray-100 hover:text-gray-600 cursor-pointer"
7
7
  @click="expandContainer()"
@@ -13,7 +13,7 @@
13
13
  </template>
14
14
 
15
15
  <script setup>
16
- import { toRefs, ref, computed, watch, onMounted } from 'vue';
16
+ import { toRefs, ref, computed, watch } from 'vue';
17
17
  import BaseLayout from './BaseLayout.vue';
18
18
 
19
19
  const props = defineProps({
@@ -91,9 +91,6 @@ const containerStyle = computed(() => {
91
91
  backgroundSize: `${displaySize.value.gridSize}px ${displaySize.value.gridSize}px`,
92
92
  };
93
93
  });
94
-
95
- // 전체 elements
96
- // provide('elements', elements);
97
94
  </script>
98
95
 
99
96
  <style scoped lang="scss">
@@ -17,7 +17,7 @@ export const elementResource = {
17
17
  },
18
18
  searchBar: {
19
19
  w: 660,
20
- h: 40,
20
+ h: 32,
21
21
  },
22
22
  table: {
23
23
  w: 300,
@@ -66,10 +66,11 @@ class DisplayEngine {
66
66
  this.setActiveDesign(null);
67
67
  })
68
68
 
69
- this._subscribe('freeDrop:startDrag', () => {
70
- this.setActiveMenu(null);
71
- this.setActiveDesign(null);
72
- });
69
+ // freeDrop:setActiveElement가 항상 먼저 발행되어 아래 처리가 중복되므로 주석 처리
70
+ // this._subscribe('freeDrop:startDrag', () => {
71
+ // this.setActiveMenu(null);
72
+ // this.setActiveDesign(null);
73
+ // });
73
74
 
74
75
  this._subscribe('gridDrop:setActiveElement', ({ action }) => {
75
76
  if (action === 'update') return;
@@ -30,6 +30,7 @@ class FreeDropEngine {
30
30
  this._startPos = { x: 0, y: 0 }; // 드래그 시작 위치
31
31
  this._startSize = { w: 0, h: 0 }; // 드래그 시작 크기
32
32
  this._offsetPos = { x: 0, y: 0 }; // 마우스 오프셋 위치
33
+ this._dragOriginPos = { x: 0, y: 0 }; // 드래그 의도 감지용 마우스 시작 위치(뷰포트 기준)
33
34
  this._alreadyExist = false; // 배치요소 기존재 여부
34
35
 
35
36
  this._subscriptions = []; // 구독 해제 함수 저장용
@@ -75,7 +76,6 @@ class FreeDropEngine {
75
76
  this._activeConfig = $config;
76
77
 
77
78
  this.setActiveElement(null);
78
-
79
79
  });
80
80
 
81
81
  this._subscribe('system:setElements', ({ $elements }) => {
@@ -240,6 +240,7 @@ class FreeDropEngine {
240
240
  this._startPos = { x: 0, y: 0 };
241
241
  this._startSize = { w: 0, h: 0 };
242
242
  this._offsetPos = { x: 0, y: 0 };
243
+ this._dragOriginPos = { x: 0, y: 0 };
243
244
 
244
245
  this._activeSection = null;
245
246
  this._activeMode = null;
@@ -466,6 +467,11 @@ class FreeDropEngine {
466
467
  return { x: 0, y: 0 };
467
468
  }
468
469
 
470
+ if (!this._activeElement) {
471
+ console.error('[FreeDropEngine] 활성화된 배치요소를 찾을 수 없습니다.');
472
+ return { x: 0, y: 0 };
473
+ }
474
+
469
475
  const layoutRect = layoutEl.getBoundingClientRect();
470
476
  const rawX = event.clientX - layoutRect.left - offsetPos.x;
471
477
  const rawY = event.clientY - layoutRect.top - offsetPos.y;
@@ -535,7 +541,7 @@ class FreeDropEngine {
535
541
 
536
542
  // 최소 크기 제한
537
543
  newW = Math.max(40, newW);
538
- newH = Math.max(40, newH);
544
+ newH = Math.max(20, newH);
539
545
 
540
546
  return { newX, newY, newW, newH };
541
547
  }
@@ -547,6 +553,10 @@ class FreeDropEngine {
547
553
  if (rafId) return;
548
554
 
549
555
  rafId = requestAnimationFrame(() => {
556
+ if (!this._activeElement) {
557
+ rafId = null;
558
+ return;
559
+ }
550
560
  this._activeElement.position.x = position.x;
551
561
  this._activeElement.position.y = position.y;
552
562
 
@@ -619,6 +629,7 @@ class FreeDropEngine {
619
629
  // 배치요소 잠겨 있으면 드래그/삭제 불가
620
630
  if (element.isLocked) return;
621
631
  this._startPos = { x: element.position.x, y: element.position.y };
632
+ this._dragOriginPos = { x: event.clientX, y: event.clientY };
622
633
 
623
634
  const elementEl = document.getElementById(id);
624
635
  if (!elementEl) {
@@ -644,8 +655,8 @@ class FreeDropEngine {
644
655
 
645
656
  // [내부함수] 드래그 의도 감지 - 최소 드래그 거리 이상으로 마우스가 이동한 경우
646
657
  _detectDragIntent = (event) => {
647
- const deltaX = Math.abs(event.clientX - this._startPos.x);
648
- const deltaY = Math.abs(event.clientY - this._startPos.y);
658
+ const deltaX = Math.abs(event.clientX - this._dragOriginPos.x);
659
+ const deltaY = Math.abs(event.clientY - this._dragOriginPos.y);
649
660
 
650
661
  // 최소 드래그 거리(gridSize) 이상 이동 여부
651
662
  const isMoved = deltaX >= this._gridSize || deltaY >= this._gridSize;
@@ -703,21 +714,27 @@ class FreeDropEngine {
703
714
  // 1) 기존 배치요소인 경우
704
715
  if (this._alreadyExist) {
705
716
  // 충돌 시 기존 위치로 복귀
717
+ if (!this._activeElement) {
718
+ console.error('[FreeDropEngine] 활성화된 배치요소를 찾을 수 없습니다.');
719
+ this._resetDrag();
720
+ return;
721
+ }
722
+
706
723
  // if (this._detectElementCollision()) {
707
724
  // alert('아이템이 겹칩니다. 다른 위치로 드래그해주세요.');
708
725
  // this._activeElement.position.x = this._startPos.x;
709
726
  // this._activeElement.position.y = this._startPos.y;
710
727
  // this._resetDrag(true);
711
728
  // } else {
712
- // 로컬에서 position, size 업데이트
713
- this.eventBus.emit('freeDrop:requestUpdateLocalBounds', {
714
- elementId: this._activeElement.id,
715
- position: this._activeElement.position,
716
- size: this._activeElement.size,
717
- guides: this.guides,
718
- timestamp: Date.now(),
719
- });
720
- this._resetDrag(true, true);
729
+ // 로컬에서 position, size 업데이트
730
+ this.eventBus.emit('freeDrop:requestUpdateLocalBounds', {
731
+ elementId: this._activeElement.id,
732
+ position: this._activeElement.position,
733
+ size: this._activeElement.size,
734
+ guides: this.guides,
735
+ timestamp: Date.now(),
736
+ });
737
+ this._resetDrag(true, true);
721
738
  // }
722
739
 
723
740
  return;
@@ -751,16 +768,19 @@ class FreeDropEngine {
751
768
  this._startPos = { x: 0, y: 0 };
752
769
  this._startSize = { w: 0, h: 0 };
753
770
  this._offsetPos = { x: 0, y: 0 };
771
+ this._dragOriginPos = { x: 0, y: 0 };
754
772
 
755
773
  if (emitEvent) {
756
- this.eventBus.emit('freeDrop:requestUpdateElement', {
757
- historyEvent: historyEvent,
758
- elementId: this._activeElement.id,
759
- position: this._activeElement.position,
760
- size: this._activeElement.size,
761
- guides: this.guides,
762
- timestamp: Date.now(),
763
- });
774
+ if (this._activeElement) {
775
+ this.eventBus.emit('freeDrop:requestUpdateElement', {
776
+ historyEvent: historyEvent,
777
+ elementId: this._activeElement.id,
778
+ position: this._activeElement.position,
779
+ size: this._activeElement.size,
780
+ guides: this.guides,
781
+ timestamp: Date.now(),
782
+ });
783
+ }
764
784
  }
765
785
 
766
786
  document.removeEventListener('mousemove', this._startDragMove);
@@ -812,7 +832,7 @@ class FreeDropEngine {
812
832
  // this._activeElement.size = this._startSize;
813
833
  // this._resetResize();
814
834
  // } else {
815
- this._resetResize(true);
835
+ this._resetResize(true);
816
836
  // }
817
837
 
818
838
  return;
@@ -831,14 +851,16 @@ class FreeDropEngine {
831
851
  this._startSize = { w: 0, h: 0 };
832
852
  this._offsetPos = { x: 0, y: 0 };
833
853
 
834
- this.eventBus.emit('freeDrop:requestUpdateElement', {
835
- historyEvent: historyEvent,
836
- elementId: this._activeElement.id,
837
- position: this._activeElement.position,
838
- size: this._activeElement.size,
839
- guides: this.guides,
840
- timestamp: Date.now(),
841
- });
854
+ if (this._activeElement) {
855
+ this.eventBus.emit('freeDrop:requestUpdateElement', {
856
+ historyEvent: historyEvent,
857
+ elementId: this._activeElement.id,
858
+ position: this._activeElement.position,
859
+ size: this._activeElement.size,
860
+ guides: this.guides,
861
+ timestamp: Date.now(),
862
+ });
863
+ }
842
864
 
843
865
  document.removeEventListener('mousemove', this._startResizeMove);
844
866
  document.removeEventListener('mouseup', this._stopResize);
@@ -56,7 +56,7 @@ class GridDropEngine {
56
56
  this._activeSection = $section;
57
57
  this._activeMode = $mode;
58
58
  this._activeConfig = $config;
59
-
59
+
60
60
  this.setActiveElement(null);
61
61
  });
62
62
 
@@ -111,7 +111,7 @@ class GridDropEngine {
111
111
  });
112
112
 
113
113
  this._subscribe('system:restoredState', ({ domain, state: snapShot }) => {
114
- if (domain === 'all'|| domain === 'draft') {
114
+ if (domain === 'all' || domain === 'draft') {
115
115
  this._elements = snapShot.manager.elements || [];
116
116
  this._elementsUpdate = true;
117
117
 
@@ -355,7 +355,7 @@ class GridDropEngine {
355
355
  size: { colSpan: 1, rowSpan: 1 },
356
356
  section: this._activeSection,
357
357
  isLocked: false,
358
- ...item.propDefaults || {}, // 아이템의 기본 props 설정
358
+ ...(item.propDefaults || {}), // 아이템의 기본 props 설정
359
359
  };
360
360
 
361
361
  this.setActiveElement(null, 'add');
@@ -407,7 +407,12 @@ class GridDropEngine {
407
407
  return false;
408
408
  }
409
409
  const element = this.gridElements.find((el) => el.id === item.id);
410
+ if (!element) {
411
+ console.error('[GridDropEngine] 배치요소를 찾을 수 없습니다. ID:', item.id);
412
+ return false;
413
+ }
410
414
  this.setActiveElement(element, 'click');
415
+
411
416
  this._dragState.activeCell = { ...element.position };
412
417
  this._alreadyExist = true;
413
418
 
@@ -457,6 +462,10 @@ class GridDropEngine {
457
462
  // 단순 클릭 시에는 위치 변경 없음
458
463
  const { col, row } = this._targetCell;
459
464
  if (this._dragState.isDragging) {
465
+ if (!this._activeElement) {
466
+ console.error('[GridDropEngine] 활성화된 배치요소를 찾을 수 없습니다.');
467
+ return false;
468
+ }
460
469
  if (col === this._activeElement.position.col && row === this._activeElement.position.row) return;
461
470
 
462
471
  this._activeElement.position.col = col;
@@ -474,13 +483,15 @@ class GridDropEngine {
474
483
  /** [내부함수] 드래그 종료 */
475
484
  _stopDrag = () => {
476
485
  if (this._dragState.isDragging) {
477
- this.eventBus.emit('gridDrop:requestUpdateElement', {
478
- historyEvent: true,
479
- elementId: this._activeElement.id,
480
- position: this._activeElement.position,
481
- size: this._activeElement.size,
482
- timestamp: Date.now(),
483
- });
486
+ if (this._activeElement) {
487
+ this.eventBus.emit('gridDrop:requestUpdateElement', {
488
+ historyEvent: true,
489
+ elementId: this._activeElement.id,
490
+ position: this._activeElement.position,
491
+ size: this._activeElement.size,
492
+ timestamp: Date.now(),
493
+ });
494
+ }
484
495
  }
485
496
 
486
497
  this._dragState.isDragging = false;
@@ -518,7 +529,8 @@ class GridDropEngine {
518
529
  */
519
530
  _startResizeMove = (event) => {
520
531
  // 리사이징 중인 경우에만 드래그 가능
521
- if (!this._dragState.isResizing) return;
532
+ // if (!this._dragState.isResizing) return;
533
+ if (!this._dragState.isResizing || !this._activeElement || !this._activeConfig) return;
522
534
 
523
535
  const { gridColumns, gridRows } = this._activeConfig;
524
536
 
@@ -555,13 +567,15 @@ class GridDropEngine {
555
567
  /** [내부함수] 리사이징 종료 */
556
568
  _stopResize = () => {
557
569
  if (this._dragState.isResizing) {
558
- this.eventBus.emit('gridDrop:requestUpdateElement', {
559
- historyEvent: true,
560
- elementId: this._activeElement.id,
561
- position: this._activeElement.position,
562
- size: this._activeElement.size,
563
- timestamp: Date.now(),
564
- });
570
+ if (this._activeElement) {
571
+ this.eventBus.emit('gridDrop:requestUpdateElement', {
572
+ historyEvent: true,
573
+ elementId: this._activeElement.id,
574
+ position: this._activeElement.position,
575
+ size: this._activeElement.size,
576
+ timestamp: Date.now(),
577
+ });
578
+ }
565
579
  }
566
580
 
567
581
  this._dragState.isResizing = false;
@@ -283,12 +283,22 @@ class LayoutEngine {
283
283
  * @param {number} ratio - 새로운 비율 값
284
284
  */
285
285
  setRatio(type, index, ratio) {
286
+ if (type !== 'column' && type !== 'row') {
287
+ console.error('[LayoutEngine] 잘못된 type입니다:', type);
288
+ return { result: false, message: '잘못된 비율 타입입니다.' };
289
+ }
290
+
286
291
  const prevRatio = {
287
292
  column: [...this.gridRatio.column],
288
293
  row: [...this.gridRatio.row],
289
294
  };
290
295
 
291
296
  const ratioArray = this.gridRatio[type];
297
+ if (!Number.isInteger(index) || index < 0 || index >= ratioArray.length) {
298
+ console.error('[LayoutEngine] 잘못된 index입니다:', index);
299
+ return { result: false, message: '잘못된 비율 인덱스입니다.' };
300
+ }
301
+
292
302
  ratioArray[index] = Number(ratio);
293
303
 
294
304
  const totalWithout = ratioArray.reduce((sum, val, idx) => {
@@ -655,7 +655,7 @@ class EngineManager {
655
655
  if (!this._initialized) {
656
656
  await this._createEngines();
657
657
  await this._setupEngineConnections();
658
- this._initialized = true;
658
+ // this._initialized = true;
659
659
 
660
660
  // 세션 복원
661
661
  const saved = sessionStorage.getItem('polarvo-session');
@@ -742,7 +742,7 @@ class EngineManager {
742
742
  this._setupHistoryEngineConnections();
743
743
 
744
744
  // engineManager
745
- this.eventBus.subscribe('system:restoredState', ({ domain, state: snapShot }) => {
745
+ this._subscribe('system:restoredState', ({ domain, state: snapShot }) => {
746
746
  if (domain === 'display') {
747
747
  this._updateElementsScale(this._displaySize, snapShot.displaySize); // _displaySize = 이전 값
748
748
  this._displaySize = snapShot.displaySize;
@@ -750,10 +750,12 @@ class EngineManager {
750
750
  });
751
751
 
752
752
  // 가장 최신 상태를 세션 스토리지에 저장 (새로고침 대비)
753
- this.eventBus.subscribe('system:historyCheckpoint', () => {
753
+ this._subscribe('system:historyCheckpoint', () => {
754
754
  this.saveSession();
755
755
  });
756
756
 
757
+ this._initialized = true;
758
+
757
759
  // 모든 연결 설정 완료 후 초기화 완료 이벤트 발행
758
760
  this.eventBus.emit('system:engineInitialized', {
759
761
  state: this.state,
@@ -984,7 +986,6 @@ class EngineManager {
984
986
 
985
987
  addElement: (item) => gridDropEngine.addElement(item),
986
988
  toggleLock: () => gridDropEngine.toggleLock(),
987
- startDrag: (event, id) => gridDropEngine.startDrag(event, id),
988
989
  handleMouseDown: (event, id) => gridDropEngine.handleMouseDown(event, id),
989
990
  startResize: (event, id) => gridDropEngine.startResize(event, id),
990
991
  },
@@ -150,13 +150,13 @@ function useGridDrop(api) {
150
150
  }
151
151
  };
152
152
 
153
- const startDrag = (event, id) => {
154
- try {
155
- api.gridDrop.startDrag(event, id);
156
- } catch (error) {
157
- console.warn('[GridDropLibrary] startDrag 호출 중 오류 발생:', error);
158
- }
159
- };
153
+ // const startDrag = (event, id) => {
154
+ // try {
155
+ // api.gridDrop.startDrag(event, id);
156
+ // } catch (error) {
157
+ // console.warn('[GridDropLibrary] startDrag 호출 중 오류 발생:', error);
158
+ // }
159
+ // };
160
160
 
161
161
  const handleMouseDown = (event, id) => {
162
162
  try {
@@ -190,7 +190,6 @@ function useGridDrop(api) {
190
190
  // setActiveCell,
191
191
  addElement,
192
192
  toggleLock,
193
- startDrag,
194
193
  handleMouseDown,
195
194
  detectHoverCell,
196
195
  startResize,
@@ -108,7 +108,8 @@ function useLayout(api) {
108
108
  state.activeConfig = $config;
109
109
  });
110
110
 
111
- _subscribe('system:requestUpdateData', ({ $elements }) => {
111
+ // _subscribe('system:requestUpdateData', ({ $elements }) => {
112
+ _subscribe('system:requestUpdateElement', ({ $elements }) => {
112
113
  state.elements = $elements;
113
114
  });
114
115
 
@@ -7,7 +7,6 @@ import useGridDrop from './GridDropLibrary.js';
7
7
  import useHistory from './HistoryLibrary.js';
8
8
  import utils from '../utils/index.js';
9
9
 
10
-
11
10
  async function createPolarvoLayout(config = {}) {
12
11
  let manager = new EngineManager(config);
13
12
  await manager.initialize();
@@ -20,15 +19,21 @@ async function createPolarvoLayout(config = {}) {
20
19
  elements: new Map(),
21
20
  };
22
21
 
22
+ const displayLib = useDisplay(api);
23
+ const layoutLib = useLayout(api);
24
+ const freeDropLib = useFreeDrop(api);
25
+ const gridDropLib = useGridDrop(api);
26
+ const historyLib = useHistory(api);
27
+
23
28
  return {
24
- display: useDisplay(api),
25
- layout: useLayout(api),
26
- freeDrop: useFreeDrop(api),
27
- gridDrop: useGridDrop(api),
28
- history: useHistory(api),
29
+ display: displayLib,
30
+ layout: layoutLib,
31
+ freeDrop: freeDropLib,
32
+ gridDrop: gridDropLib,
33
+ history: historyLib,
29
34
  userApi: api.userApi,
30
35
  utils: utils,
31
-
36
+
32
37
  resetEngines: () => api.resetEngines(),
33
38
 
34
39
  components: {
@@ -43,6 +48,13 @@ async function createPolarvoLayout(config = {}) {
43
48
  destroy: () => {
44
49
  componentRegistry.sections.clear();
45
50
  componentRegistry.elements.clear();
51
+
52
+ displayLib.cleanup();
53
+ layoutLib.cleanup();
54
+ freeDropLib.cleanup();
55
+ gridDropLib.cleanup();
56
+ historyLib.cleanup();
57
+
46
58
  if (manager) {
47
59
  manager.destroy();
48
60
  manager = null;