@djvlc/sandbox 1.0.0 → 1.0.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/dist/index.cjs +296 -10
- package/dist/index.d.cts +51 -19
- package/dist/index.d.ts +51 -19
- package/dist/index.js +296 -10
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -51,6 +51,7 @@ var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
|
51
51
|
MessageType2["COMPONENT_CONTEXT_MENU"] = "component_context_menu";
|
|
52
52
|
MessageType2["COMPONENT_DRAG_START"] = "component_drag_start";
|
|
53
53
|
MessageType2["COMPONENT_DRAG_END"] = "component_drag_end";
|
|
54
|
+
MessageType2["COMPONENT_DROP"] = "component_drop";
|
|
54
55
|
MessageType2["ERROR"] = "error";
|
|
55
56
|
MessageType2["LOG"] = "log";
|
|
56
57
|
return MessageType2;
|
|
@@ -290,6 +291,14 @@ var SandboxClient = class {
|
|
|
290
291
|
this.runtime = null;
|
|
291
292
|
this.selectedComponents = /* @__PURE__ */ new Set();
|
|
292
293
|
this.hoveredComponent = null;
|
|
294
|
+
this.dragState = {
|
|
295
|
+
isDragging: false,
|
|
296
|
+
draggedComponentId: null,
|
|
297
|
+
draggedElement: null,
|
|
298
|
+
dropTarget: null,
|
|
299
|
+
dropPosition: null,
|
|
300
|
+
dropIndicator: null
|
|
301
|
+
};
|
|
293
302
|
// ==================== 私有方法 ====================
|
|
294
303
|
this.handleMessage = async (event) => {
|
|
295
304
|
const message = event.data;
|
|
@@ -351,6 +360,12 @@ var SandboxClient = class {
|
|
|
351
360
|
this.options = {
|
|
352
361
|
targetOrigin: "*",
|
|
353
362
|
debug: false,
|
|
363
|
+
enableDrag: true,
|
|
364
|
+
dragOptions: {
|
|
365
|
+
dragOpacity: 0.5,
|
|
366
|
+
showDropIndicator: true,
|
|
367
|
+
dropIndicatorColor: "#1890ff"
|
|
368
|
+
},
|
|
354
369
|
...options
|
|
355
370
|
};
|
|
356
371
|
}
|
|
@@ -447,17 +462,19 @@ var SandboxClient = class {
|
|
|
447
462
|
* 注入编辑器样式
|
|
448
463
|
*/
|
|
449
464
|
injectEditorStyles() {
|
|
465
|
+
const indicatorColor = this.options.dragOptions?.dropIndicatorColor || "#1890ff";
|
|
450
466
|
const style = document.createElement("style");
|
|
467
|
+
style.id = "djvlc-editor-styles";
|
|
451
468
|
style.textContent = `
|
|
452
469
|
/* \u9009\u4E2D\u72B6\u6001 */
|
|
453
470
|
[data-component-id].djvlc-selected {
|
|
454
|
-
outline: 2px solid
|
|
471
|
+
outline: 2px solid ${indicatorColor} !important;
|
|
455
472
|
outline-offset: 2px;
|
|
456
473
|
}
|
|
457
474
|
|
|
458
475
|
/* \u60AC\u505C\u72B6\u6001 */
|
|
459
476
|
[data-component-id].djvlc-hovered {
|
|
460
|
-
outline: 1px dashed
|
|
477
|
+
outline: 1px dashed ${indicatorColor} !important;
|
|
461
478
|
outline-offset: 1px;
|
|
462
479
|
}
|
|
463
480
|
|
|
@@ -471,39 +488,300 @@ var SandboxClient = class {
|
|
|
471
488
|
pointer-events: auto;
|
|
472
489
|
}
|
|
473
490
|
|
|
491
|
+
/* \u53EF\u62D6\u62FD\u72B6\u6001 */
|
|
492
|
+
[data-component-id][draggable="true"] {
|
|
493
|
+
cursor: grab;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
[data-component-id][draggable="true"]:active {
|
|
497
|
+
cursor: grabbing;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* \u62D6\u62FD\u4E2D\u72B6\u6001 */
|
|
501
|
+
[data-component-id].djvlc-dragging {
|
|
502
|
+
opacity: ${this.options.dragOptions?.dragOpacity || 0.5};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* \u653E\u7F6E\u76EE\u6807\u72B6\u6001 */
|
|
506
|
+
[data-component-id].djvlc-drop-target {
|
|
507
|
+
outline: 2px dashed ${indicatorColor} !important;
|
|
508
|
+
outline-offset: 4px;
|
|
509
|
+
}
|
|
510
|
+
|
|
474
511
|
/* \u62D6\u62FD\u5360\u4F4D\u7B26 */
|
|
475
512
|
.djvlc-drop-indicator {
|
|
476
513
|
position: absolute;
|
|
477
|
-
background:
|
|
478
|
-
opacity: 0.
|
|
514
|
+
background: ${indicatorColor};
|
|
515
|
+
opacity: 0.8;
|
|
479
516
|
pointer-events: none;
|
|
480
517
|
z-index: 10000;
|
|
518
|
+
transition: all 0.1s ease;
|
|
481
519
|
}
|
|
482
520
|
|
|
483
521
|
.djvlc-drop-indicator.horizontal {
|
|
484
522
|
height: 4px;
|
|
485
523
|
width: 100%;
|
|
524
|
+
border-radius: 2px;
|
|
486
525
|
}
|
|
487
526
|
|
|
488
527
|
.djvlc-drop-indicator.vertical {
|
|
489
528
|
width: 4px;
|
|
490
529
|
height: 100%;
|
|
530
|
+
border-radius: 2px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* \u653E\u7F6E\u63D0\u793A\u7BAD\u5934 */
|
|
534
|
+
.djvlc-drop-indicator::before {
|
|
535
|
+
content: '';
|
|
536
|
+
position: absolute;
|
|
537
|
+
width: 8px;
|
|
538
|
+
height: 8px;
|
|
539
|
+
background: ${indicatorColor};
|
|
540
|
+
border-radius: 50%;
|
|
541
|
+
left: -4px;
|
|
542
|
+
top: -2px;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.djvlc-drop-indicator::after {
|
|
546
|
+
content: '';
|
|
547
|
+
position: absolute;
|
|
548
|
+
width: 8px;
|
|
549
|
+
height: 8px;
|
|
550
|
+
background: ${indicatorColor};
|
|
551
|
+
border-radius: 50%;
|
|
552
|
+
right: -4px;
|
|
553
|
+
top: -2px;
|
|
491
554
|
}
|
|
492
555
|
`;
|
|
493
556
|
document.head.appendChild(style);
|
|
494
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* 初始化拖拽事件监听
|
|
560
|
+
*/
|
|
561
|
+
initDragEventListeners() {
|
|
562
|
+
if (!this.options.enableDrag) return;
|
|
563
|
+
this.makeComponentsDraggable();
|
|
564
|
+
document.addEventListener("dragstart", (e) => {
|
|
565
|
+
const target = e.target;
|
|
566
|
+
const componentEl = this.findComponentElement(target);
|
|
567
|
+
if (!componentEl) return;
|
|
568
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
569
|
+
if (!componentId) return;
|
|
570
|
+
this.dragState.isDragging = true;
|
|
571
|
+
this.dragState.draggedComponentId = componentId;
|
|
572
|
+
this.dragState.draggedElement = componentEl;
|
|
573
|
+
componentEl.classList.add("djvlc-dragging");
|
|
574
|
+
e.dataTransfer?.setData("text/plain", componentId);
|
|
575
|
+
e.dataTransfer?.setData("application/json", JSON.stringify({
|
|
576
|
+
componentId,
|
|
577
|
+
componentType: componentEl.getAttribute("data-component-type")
|
|
578
|
+
}));
|
|
579
|
+
if (e.dataTransfer) {
|
|
580
|
+
e.dataTransfer.effectAllowed = "move";
|
|
581
|
+
}
|
|
582
|
+
this.log("debug", "Drag start:", componentId);
|
|
583
|
+
});
|
|
584
|
+
document.addEventListener("dragover", (e) => {
|
|
585
|
+
if (!this.dragState.isDragging) return;
|
|
586
|
+
e.preventDefault();
|
|
587
|
+
const target = e.target;
|
|
588
|
+
const componentEl = this.findComponentElement(target);
|
|
589
|
+
if (!componentEl || componentEl === this.dragState.draggedElement) {
|
|
590
|
+
this.hideDropIndicator();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
594
|
+
if (!componentId) return;
|
|
595
|
+
const rect = componentEl.getBoundingClientRect();
|
|
596
|
+
const y = e.clientY;
|
|
597
|
+
const threshold = rect.height / 3;
|
|
598
|
+
let position;
|
|
599
|
+
if (y < rect.top + threshold) {
|
|
600
|
+
position = "before";
|
|
601
|
+
} else if (y > rect.bottom - threshold) {
|
|
602
|
+
position = "after";
|
|
603
|
+
} else {
|
|
604
|
+
position = "inside";
|
|
605
|
+
}
|
|
606
|
+
if (this.dragState.dropTarget !== componentEl || this.dragState.dropPosition !== position) {
|
|
607
|
+
this.dragState.dropTarget?.classList.remove("djvlc-drop-target");
|
|
608
|
+
this.dragState.dropTarget = componentEl;
|
|
609
|
+
this.dragState.dropPosition = position;
|
|
610
|
+
if (position === "inside") {
|
|
611
|
+
componentEl.classList.add("djvlc-drop-target");
|
|
612
|
+
this.hideDropIndicator();
|
|
613
|
+
} else {
|
|
614
|
+
componentEl.classList.remove("djvlc-drop-target");
|
|
615
|
+
this.showDropIndicator(componentEl, position);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
document.addEventListener("dragleave", (e) => {
|
|
620
|
+
const target = e.target;
|
|
621
|
+
const componentEl = this.findComponentElement(target);
|
|
622
|
+
if (componentEl && componentEl === this.dragState.dropTarget) {
|
|
623
|
+
const relatedTarget = e.relatedTarget;
|
|
624
|
+
if (!relatedTarget || !componentEl.contains(relatedTarget)) {
|
|
625
|
+
componentEl.classList.remove("djvlc-drop-target");
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
document.addEventListener("drop", (e) => {
|
|
630
|
+
if (!this.dragState.isDragging) return;
|
|
631
|
+
e.preventDefault();
|
|
632
|
+
const { draggedComponentId, dropTarget, dropPosition } = this.dragState;
|
|
633
|
+
if (draggedComponentId && dropTarget && dropPosition) {
|
|
634
|
+
const targetComponentId = dropTarget.getAttribute("data-component-id");
|
|
635
|
+
if (targetComponentId && targetComponentId !== draggedComponentId) {
|
|
636
|
+
this.log("info", "Drop:", draggedComponentId, dropPosition, targetComponentId);
|
|
637
|
+
this.send("component_drop" /* COMPONENT_DROP */, {
|
|
638
|
+
componentId: draggedComponentId,
|
|
639
|
+
targetComponentId,
|
|
640
|
+
position: dropPosition
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
this.endDrag();
|
|
645
|
+
});
|
|
646
|
+
document.addEventListener("dragend", () => {
|
|
647
|
+
this.endDrag();
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* 使组件可拖拽
|
|
652
|
+
*/
|
|
653
|
+
makeComponentsDraggable() {
|
|
654
|
+
const components = document.querySelectorAll("[data-component-id]");
|
|
655
|
+
components.forEach((el) => {
|
|
656
|
+
el.draggable = true;
|
|
657
|
+
});
|
|
658
|
+
const observer = new MutationObserver((mutations) => {
|
|
659
|
+
mutations.forEach((mutation) => {
|
|
660
|
+
mutation.addedNodes.forEach((node) => {
|
|
661
|
+
if (node instanceof HTMLElement) {
|
|
662
|
+
if (node.hasAttribute("data-component-id")) {
|
|
663
|
+
node.draggable = true;
|
|
664
|
+
}
|
|
665
|
+
node.querySelectorAll("[data-component-id]").forEach((el) => {
|
|
666
|
+
el.draggable = true;
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
observer.observe(document.body, {
|
|
673
|
+
childList: true,
|
|
674
|
+
subtree: true
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* 显示放置指示器
|
|
679
|
+
*/
|
|
680
|
+
showDropIndicator(target, position) {
|
|
681
|
+
if (!this.options.dragOptions?.showDropIndicator) return;
|
|
682
|
+
let indicator = this.dragState.dropIndicator;
|
|
683
|
+
if (!indicator) {
|
|
684
|
+
indicator = document.createElement("div");
|
|
685
|
+
indicator.className = "djvlc-drop-indicator horizontal";
|
|
686
|
+
document.body.appendChild(indicator);
|
|
687
|
+
this.dragState.dropIndicator = indicator;
|
|
688
|
+
}
|
|
689
|
+
const rect = target.getBoundingClientRect();
|
|
690
|
+
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
691
|
+
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
|
|
692
|
+
indicator.style.left = `${rect.left + scrollLeft}px`;
|
|
693
|
+
indicator.style.width = `${rect.width}px`;
|
|
694
|
+
if (position === "before") {
|
|
695
|
+
indicator.style.top = `${rect.top + scrollTop - 2}px`;
|
|
696
|
+
} else {
|
|
697
|
+
indicator.style.top = `${rect.bottom + scrollTop - 2}px`;
|
|
698
|
+
}
|
|
699
|
+
indicator.style.display = "block";
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* 隐藏放置指示器
|
|
703
|
+
*/
|
|
704
|
+
hideDropIndicator() {
|
|
705
|
+
if (this.dragState.dropIndicator) {
|
|
706
|
+
this.dragState.dropIndicator.style.display = "none";
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* 结束拖拽
|
|
711
|
+
*/
|
|
712
|
+
endDrag() {
|
|
713
|
+
this.dragState.draggedElement?.classList.remove("djvlc-dragging");
|
|
714
|
+
this.dragState.dropTarget?.classList.remove("djvlc-drop-target");
|
|
715
|
+
this.hideDropIndicator();
|
|
716
|
+
this.dragState = {
|
|
717
|
+
isDragging: false,
|
|
718
|
+
draggedComponentId: null,
|
|
719
|
+
draggedElement: null,
|
|
720
|
+
dropTarget: null,
|
|
721
|
+
dropPosition: null,
|
|
722
|
+
dropIndicator: this.dragState.dropIndicator
|
|
723
|
+
// 保留 indicator 元素
|
|
724
|
+
};
|
|
725
|
+
}
|
|
495
726
|
async handleInit(data) {
|
|
496
727
|
this.log("info", "Init with config:", data);
|
|
728
|
+
void data;
|
|
729
|
+
if (this.runtime) {
|
|
730
|
+
try {
|
|
731
|
+
await this.runtime.init();
|
|
732
|
+
} catch (error) {
|
|
733
|
+
this.log("error", "Failed to init runtime:", error);
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
497
737
|
}
|
|
498
738
|
async handleLoadPage(data) {
|
|
499
739
|
this.log("info", "Load page:", data.pageUid);
|
|
740
|
+
if (!this.runtime) {
|
|
741
|
+
throw new Error("Runtime not set");
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
await this.runtime.load();
|
|
745
|
+
await this.runtime.render();
|
|
746
|
+
this.syncState();
|
|
747
|
+
} catch (error) {
|
|
748
|
+
this.log("error", "Failed to load page:", error);
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
500
751
|
}
|
|
501
752
|
async handleUpdateSchema(data) {
|
|
502
753
|
this.log("info", "Update schema", data.fullUpdate ? "(full)" : "(partial)");
|
|
754
|
+
if (!this.runtime) {
|
|
755
|
+
throw new Error("Runtime not set");
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
if (data.fullUpdate) {
|
|
759
|
+
await this.runtime.load();
|
|
760
|
+
await this.runtime.render();
|
|
761
|
+
} else {
|
|
762
|
+
await this.runtime.load();
|
|
763
|
+
await this.runtime.render();
|
|
764
|
+
}
|
|
765
|
+
this.syncState();
|
|
766
|
+
} catch (error) {
|
|
767
|
+
this.log("error", "Failed to update schema:", error);
|
|
768
|
+
throw error;
|
|
769
|
+
}
|
|
503
770
|
}
|
|
504
771
|
async handleRefresh() {
|
|
505
772
|
this.log("info", "Refresh page");
|
|
506
|
-
|
|
773
|
+
if (this.runtime) {
|
|
774
|
+
try {
|
|
775
|
+
await this.runtime.load();
|
|
776
|
+
await this.runtime.render();
|
|
777
|
+
this.syncState();
|
|
778
|
+
} catch (error) {
|
|
779
|
+
this.log("error", "Failed to refresh:", error);
|
|
780
|
+
window.location.reload();
|
|
781
|
+
}
|
|
782
|
+
} else {
|
|
783
|
+
window.location.reload();
|
|
784
|
+
}
|
|
507
785
|
}
|
|
508
786
|
handleSelectComponent(data) {
|
|
509
787
|
if (data.componentId) {
|
|
@@ -539,13 +817,20 @@ var SandboxClient = class {
|
|
|
539
817
|
this.runtime?.updateComponent(data.componentId, data.props || {});
|
|
540
818
|
}
|
|
541
819
|
handleAddComponent(data) {
|
|
542
|
-
this.log("info", "Add component:", data.component.
|
|
820
|
+
this.log("info", "Add component:", data.component.componentType);
|
|
821
|
+
this.log("warn", "addComponent not supported, need to reload page");
|
|
543
822
|
}
|
|
544
823
|
handleMoveComponent(data) {
|
|
545
824
|
this.log("info", "Move component:", data.componentId, "to", data.targetIndex);
|
|
825
|
+
this.log("warn", "moveComponent not supported, need to reload page");
|
|
546
826
|
}
|
|
547
827
|
handleDeleteComponent(data) {
|
|
548
828
|
this.log("info", "Delete component:", data.componentId);
|
|
829
|
+
this.log("warn", "deleteComponent not supported, need to reload page");
|
|
830
|
+
this.selectedComponents.delete(data.componentId);
|
|
831
|
+
if (this.hoveredComponent === data.componentId) {
|
|
832
|
+
this.hoveredComponent = null;
|
|
833
|
+
}
|
|
549
834
|
}
|
|
550
835
|
handleSyncVariables(data) {
|
|
551
836
|
Object.entries(data.variables).forEach(([key, value]) => {
|
|
@@ -564,11 +849,12 @@ var SandboxClient = class {
|
|
|
564
849
|
});
|
|
565
850
|
}
|
|
566
851
|
syncState() {
|
|
852
|
+
const runtimeState = this.runtime?.getState();
|
|
567
853
|
const state = {
|
|
568
|
-
phase:
|
|
569
|
-
page:
|
|
570
|
-
pageUid:
|
|
571
|
-
pageVersionId:
|
|
854
|
+
phase: runtimeState?.phase || "idle",
|
|
855
|
+
page: runtimeState?.page ? {
|
|
856
|
+
pageUid: runtimeState.page.pageUid,
|
|
857
|
+
pageVersionId: runtimeState.page.pageVersionId
|
|
572
858
|
} : void 0,
|
|
573
859
|
selectedComponents: Array.from(this.selectedComponents),
|
|
574
860
|
hoveredComponent: this.hoveredComponent
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { PageSchema,
|
|
1
|
+
import { PageSchema, ComponentNode } from '@djvlc/contracts-types';
|
|
2
|
+
import { DjvlcRuntime } from '@djvlc/runtime-core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 沙箱通信协议
|
|
@@ -28,6 +29,7 @@ declare enum MessageType {
|
|
|
28
29
|
COMPONENT_CONTEXT_MENU = "component_context_menu",
|
|
29
30
|
COMPONENT_DRAG_START = "component_drag_start",
|
|
30
31
|
COMPONENT_DRAG_END = "component_drag_end",
|
|
32
|
+
COMPONENT_DROP = "component_drop",
|
|
31
33
|
ERROR = "error",
|
|
32
34
|
LOG = "log"
|
|
33
35
|
}
|
|
@@ -124,14 +126,14 @@ interface UpdateComponentMessageData {
|
|
|
124
126
|
/** 更新的样式 */
|
|
125
127
|
style?: Record<string, unknown>;
|
|
126
128
|
/** 完整组件实例 */
|
|
127
|
-
component?:
|
|
129
|
+
component?: ComponentNode;
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
130
132
|
* Add Component 消息数据
|
|
131
133
|
*/
|
|
132
134
|
interface AddComponentMessageData {
|
|
133
135
|
/** 组件实例 */
|
|
134
|
-
component:
|
|
136
|
+
component: ComponentNode;
|
|
135
137
|
/** 父组件 ID */
|
|
136
138
|
parentId?: string;
|
|
137
139
|
/** 插入位置索引 */
|
|
@@ -187,6 +189,17 @@ interface SyncStateMessageData {
|
|
|
187
189
|
/** 悬停组件 */
|
|
188
190
|
hoveredComponent: string | null;
|
|
189
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Component Drop 消息数据
|
|
194
|
+
*/
|
|
195
|
+
interface ComponentDropMessageData {
|
|
196
|
+
/** 被拖拽的组件 ID */
|
|
197
|
+
componentId: string;
|
|
198
|
+
/** 目标组件 ID */
|
|
199
|
+
targetComponentId: string;
|
|
200
|
+
/** 放置位置 */
|
|
201
|
+
position: 'before' | 'after' | 'inside';
|
|
202
|
+
}
|
|
190
203
|
/**
|
|
191
204
|
* Error 消息数据
|
|
192
205
|
*/
|
|
@@ -329,20 +342,7 @@ declare class SandboxHost {
|
|
|
329
342
|
* 沙箱客户端
|
|
330
343
|
* 在 iframe 预览中使用,响应 Editor 的控制
|
|
331
344
|
*/
|
|
332
|
-
|
|
333
|
-
* 运行时接口(避免直接依赖 @djvlc/runtime-core)
|
|
334
|
-
*/
|
|
335
|
-
interface RuntimeLike {
|
|
336
|
-
getState(): {
|
|
337
|
-
phase: string;
|
|
338
|
-
page: {
|
|
339
|
-
pageUid: string;
|
|
340
|
-
pageVersionId: string;
|
|
341
|
-
} | null;
|
|
342
|
-
};
|
|
343
|
-
updateComponent(componentId: string, props: Record<string, unknown>): void;
|
|
344
|
-
setVariable(key: string, value: unknown): void;
|
|
345
|
-
}
|
|
345
|
+
|
|
346
346
|
/**
|
|
347
347
|
* 沙箱客户端配置
|
|
348
348
|
*/
|
|
@@ -351,6 +351,17 @@ interface SandboxClientOptions {
|
|
|
351
351
|
targetOrigin?: string;
|
|
352
352
|
/** 调试模式 */
|
|
353
353
|
debug?: boolean;
|
|
354
|
+
/** 是否启用拖拽 */
|
|
355
|
+
enableDrag?: boolean;
|
|
356
|
+
/** 拖拽配置 */
|
|
357
|
+
dragOptions?: {
|
|
358
|
+
/** 拖拽时的透明度 */
|
|
359
|
+
dragOpacity?: number;
|
|
360
|
+
/** 是否显示放置指示器 */
|
|
361
|
+
showDropIndicator?: boolean;
|
|
362
|
+
/** 放置指示器颜色 */
|
|
363
|
+
dropIndicatorColor?: string;
|
|
364
|
+
};
|
|
354
365
|
}
|
|
355
366
|
/**
|
|
356
367
|
* 沙箱客户端
|
|
@@ -360,11 +371,12 @@ declare class SandboxClient {
|
|
|
360
371
|
private runtime;
|
|
361
372
|
private selectedComponents;
|
|
362
373
|
private hoveredComponent;
|
|
374
|
+
private dragState;
|
|
363
375
|
constructor(options?: SandboxClientOptions);
|
|
364
376
|
/**
|
|
365
377
|
* 设置运行时实例
|
|
366
378
|
*/
|
|
367
|
-
setRuntime(runtime:
|
|
379
|
+
setRuntime(runtime: DjvlcRuntime): void;
|
|
368
380
|
/**
|
|
369
381
|
* 连接到父窗口
|
|
370
382
|
*/
|
|
@@ -381,6 +393,26 @@ declare class SandboxClient {
|
|
|
381
393
|
* 注入编辑器样式
|
|
382
394
|
*/
|
|
383
395
|
injectEditorStyles(): void;
|
|
396
|
+
/**
|
|
397
|
+
* 初始化拖拽事件监听
|
|
398
|
+
*/
|
|
399
|
+
initDragEventListeners(): void;
|
|
400
|
+
/**
|
|
401
|
+
* 使组件可拖拽
|
|
402
|
+
*/
|
|
403
|
+
private makeComponentsDraggable;
|
|
404
|
+
/**
|
|
405
|
+
* 显示放置指示器
|
|
406
|
+
*/
|
|
407
|
+
private showDropIndicator;
|
|
408
|
+
/**
|
|
409
|
+
* 隐藏放置指示器
|
|
410
|
+
*/
|
|
411
|
+
private hideDropIndicator;
|
|
412
|
+
/**
|
|
413
|
+
* 结束拖拽
|
|
414
|
+
*/
|
|
415
|
+
private endDrag;
|
|
384
416
|
private handleMessage;
|
|
385
417
|
private handleInit;
|
|
386
418
|
private handleLoadPage;
|
|
@@ -403,4 +435,4 @@ declare class SandboxClient {
|
|
|
403
435
|
private log;
|
|
404
436
|
}
|
|
405
437
|
|
|
406
|
-
export { type AddComponentMessageData, type BaseMessage, type ComponentEventMessageData, type DeleteComponentMessageData, type ErrorMessageData, type HoverComponentMessageData, type InitMessageData, type LoadPageMessageData, MessageType, type MoveComponentMessageData, type ReadyMessageData, type ResponseMessage, SandboxClient, type SandboxClientOptions, SandboxHost, type SandboxHostOptions, type SelectComponentMessageData, type SyncStateMessageData, type UpdateComponentMessageData, type UpdateSchemaMessageData, createMessage, createResponse, generateMessageId, isDjvlcMessage };
|
|
438
|
+
export { type AddComponentMessageData, type BaseMessage, type ComponentDropMessageData, type ComponentEventMessageData, type DeleteComponentMessageData, type ErrorMessageData, type HoverComponentMessageData, type InitMessageData, type LoadPageMessageData, MessageType, type MoveComponentMessageData, type ReadyMessageData, type ResponseMessage, SandboxClient, type SandboxClientOptions, SandboxHost, type SandboxHostOptions, type SelectComponentMessageData, type SyncStateMessageData, type UpdateComponentMessageData, type UpdateSchemaMessageData, createMessage, createResponse, generateMessageId, isDjvlcMessage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { PageSchema,
|
|
1
|
+
import { PageSchema, ComponentNode } from '@djvlc/contracts-types';
|
|
2
|
+
import { DjvlcRuntime } from '@djvlc/runtime-core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 沙箱通信协议
|
|
@@ -28,6 +29,7 @@ declare enum MessageType {
|
|
|
28
29
|
COMPONENT_CONTEXT_MENU = "component_context_menu",
|
|
29
30
|
COMPONENT_DRAG_START = "component_drag_start",
|
|
30
31
|
COMPONENT_DRAG_END = "component_drag_end",
|
|
32
|
+
COMPONENT_DROP = "component_drop",
|
|
31
33
|
ERROR = "error",
|
|
32
34
|
LOG = "log"
|
|
33
35
|
}
|
|
@@ -124,14 +126,14 @@ interface UpdateComponentMessageData {
|
|
|
124
126
|
/** 更新的样式 */
|
|
125
127
|
style?: Record<string, unknown>;
|
|
126
128
|
/** 完整组件实例 */
|
|
127
|
-
component?:
|
|
129
|
+
component?: ComponentNode;
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
130
132
|
* Add Component 消息数据
|
|
131
133
|
*/
|
|
132
134
|
interface AddComponentMessageData {
|
|
133
135
|
/** 组件实例 */
|
|
134
|
-
component:
|
|
136
|
+
component: ComponentNode;
|
|
135
137
|
/** 父组件 ID */
|
|
136
138
|
parentId?: string;
|
|
137
139
|
/** 插入位置索引 */
|
|
@@ -187,6 +189,17 @@ interface SyncStateMessageData {
|
|
|
187
189
|
/** 悬停组件 */
|
|
188
190
|
hoveredComponent: string | null;
|
|
189
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Component Drop 消息数据
|
|
194
|
+
*/
|
|
195
|
+
interface ComponentDropMessageData {
|
|
196
|
+
/** 被拖拽的组件 ID */
|
|
197
|
+
componentId: string;
|
|
198
|
+
/** 目标组件 ID */
|
|
199
|
+
targetComponentId: string;
|
|
200
|
+
/** 放置位置 */
|
|
201
|
+
position: 'before' | 'after' | 'inside';
|
|
202
|
+
}
|
|
190
203
|
/**
|
|
191
204
|
* Error 消息数据
|
|
192
205
|
*/
|
|
@@ -329,20 +342,7 @@ declare class SandboxHost {
|
|
|
329
342
|
* 沙箱客户端
|
|
330
343
|
* 在 iframe 预览中使用,响应 Editor 的控制
|
|
331
344
|
*/
|
|
332
|
-
|
|
333
|
-
* 运行时接口(避免直接依赖 @djvlc/runtime-core)
|
|
334
|
-
*/
|
|
335
|
-
interface RuntimeLike {
|
|
336
|
-
getState(): {
|
|
337
|
-
phase: string;
|
|
338
|
-
page: {
|
|
339
|
-
pageUid: string;
|
|
340
|
-
pageVersionId: string;
|
|
341
|
-
} | null;
|
|
342
|
-
};
|
|
343
|
-
updateComponent(componentId: string, props: Record<string, unknown>): void;
|
|
344
|
-
setVariable(key: string, value: unknown): void;
|
|
345
|
-
}
|
|
345
|
+
|
|
346
346
|
/**
|
|
347
347
|
* 沙箱客户端配置
|
|
348
348
|
*/
|
|
@@ -351,6 +351,17 @@ interface SandboxClientOptions {
|
|
|
351
351
|
targetOrigin?: string;
|
|
352
352
|
/** 调试模式 */
|
|
353
353
|
debug?: boolean;
|
|
354
|
+
/** 是否启用拖拽 */
|
|
355
|
+
enableDrag?: boolean;
|
|
356
|
+
/** 拖拽配置 */
|
|
357
|
+
dragOptions?: {
|
|
358
|
+
/** 拖拽时的透明度 */
|
|
359
|
+
dragOpacity?: number;
|
|
360
|
+
/** 是否显示放置指示器 */
|
|
361
|
+
showDropIndicator?: boolean;
|
|
362
|
+
/** 放置指示器颜色 */
|
|
363
|
+
dropIndicatorColor?: string;
|
|
364
|
+
};
|
|
354
365
|
}
|
|
355
366
|
/**
|
|
356
367
|
* 沙箱客户端
|
|
@@ -360,11 +371,12 @@ declare class SandboxClient {
|
|
|
360
371
|
private runtime;
|
|
361
372
|
private selectedComponents;
|
|
362
373
|
private hoveredComponent;
|
|
374
|
+
private dragState;
|
|
363
375
|
constructor(options?: SandboxClientOptions);
|
|
364
376
|
/**
|
|
365
377
|
* 设置运行时实例
|
|
366
378
|
*/
|
|
367
|
-
setRuntime(runtime:
|
|
379
|
+
setRuntime(runtime: DjvlcRuntime): void;
|
|
368
380
|
/**
|
|
369
381
|
* 连接到父窗口
|
|
370
382
|
*/
|
|
@@ -381,6 +393,26 @@ declare class SandboxClient {
|
|
|
381
393
|
* 注入编辑器样式
|
|
382
394
|
*/
|
|
383
395
|
injectEditorStyles(): void;
|
|
396
|
+
/**
|
|
397
|
+
* 初始化拖拽事件监听
|
|
398
|
+
*/
|
|
399
|
+
initDragEventListeners(): void;
|
|
400
|
+
/**
|
|
401
|
+
* 使组件可拖拽
|
|
402
|
+
*/
|
|
403
|
+
private makeComponentsDraggable;
|
|
404
|
+
/**
|
|
405
|
+
* 显示放置指示器
|
|
406
|
+
*/
|
|
407
|
+
private showDropIndicator;
|
|
408
|
+
/**
|
|
409
|
+
* 隐藏放置指示器
|
|
410
|
+
*/
|
|
411
|
+
private hideDropIndicator;
|
|
412
|
+
/**
|
|
413
|
+
* 结束拖拽
|
|
414
|
+
*/
|
|
415
|
+
private endDrag;
|
|
384
416
|
private handleMessage;
|
|
385
417
|
private handleInit;
|
|
386
418
|
private handleLoadPage;
|
|
@@ -403,4 +435,4 @@ declare class SandboxClient {
|
|
|
403
435
|
private log;
|
|
404
436
|
}
|
|
405
437
|
|
|
406
|
-
export { type AddComponentMessageData, type BaseMessage, type ComponentEventMessageData, type DeleteComponentMessageData, type ErrorMessageData, type HoverComponentMessageData, type InitMessageData, type LoadPageMessageData, MessageType, type MoveComponentMessageData, type ReadyMessageData, type ResponseMessage, SandboxClient, type SandboxClientOptions, SandboxHost, type SandboxHostOptions, type SelectComponentMessageData, type SyncStateMessageData, type UpdateComponentMessageData, type UpdateSchemaMessageData, createMessage, createResponse, generateMessageId, isDjvlcMessage };
|
|
438
|
+
export { type AddComponentMessageData, type BaseMessage, type ComponentDropMessageData, type ComponentEventMessageData, type DeleteComponentMessageData, type ErrorMessageData, type HoverComponentMessageData, type InitMessageData, type LoadPageMessageData, MessageType, type MoveComponentMessageData, type ReadyMessageData, type ResponseMessage, SandboxClient, type SandboxClientOptions, SandboxHost, type SandboxHostOptions, type SelectComponentMessageData, type SyncStateMessageData, type UpdateComponentMessageData, type UpdateSchemaMessageData, createMessage, createResponse, generateMessageId, isDjvlcMessage };
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
|
19
19
|
MessageType2["COMPONENT_CONTEXT_MENU"] = "component_context_menu";
|
|
20
20
|
MessageType2["COMPONENT_DRAG_START"] = "component_drag_start";
|
|
21
21
|
MessageType2["COMPONENT_DRAG_END"] = "component_drag_end";
|
|
22
|
+
MessageType2["COMPONENT_DROP"] = "component_drop";
|
|
22
23
|
MessageType2["ERROR"] = "error";
|
|
23
24
|
MessageType2["LOG"] = "log";
|
|
24
25
|
return MessageType2;
|
|
@@ -258,6 +259,14 @@ var SandboxClient = class {
|
|
|
258
259
|
this.runtime = null;
|
|
259
260
|
this.selectedComponents = /* @__PURE__ */ new Set();
|
|
260
261
|
this.hoveredComponent = null;
|
|
262
|
+
this.dragState = {
|
|
263
|
+
isDragging: false,
|
|
264
|
+
draggedComponentId: null,
|
|
265
|
+
draggedElement: null,
|
|
266
|
+
dropTarget: null,
|
|
267
|
+
dropPosition: null,
|
|
268
|
+
dropIndicator: null
|
|
269
|
+
};
|
|
261
270
|
// ==================== 私有方法 ====================
|
|
262
271
|
this.handleMessage = async (event) => {
|
|
263
272
|
const message = event.data;
|
|
@@ -319,6 +328,12 @@ var SandboxClient = class {
|
|
|
319
328
|
this.options = {
|
|
320
329
|
targetOrigin: "*",
|
|
321
330
|
debug: false,
|
|
331
|
+
enableDrag: true,
|
|
332
|
+
dragOptions: {
|
|
333
|
+
dragOpacity: 0.5,
|
|
334
|
+
showDropIndicator: true,
|
|
335
|
+
dropIndicatorColor: "#1890ff"
|
|
336
|
+
},
|
|
322
337
|
...options
|
|
323
338
|
};
|
|
324
339
|
}
|
|
@@ -415,17 +430,19 @@ var SandboxClient = class {
|
|
|
415
430
|
* 注入编辑器样式
|
|
416
431
|
*/
|
|
417
432
|
injectEditorStyles() {
|
|
433
|
+
const indicatorColor = this.options.dragOptions?.dropIndicatorColor || "#1890ff";
|
|
418
434
|
const style = document.createElement("style");
|
|
435
|
+
style.id = "djvlc-editor-styles";
|
|
419
436
|
style.textContent = `
|
|
420
437
|
/* \u9009\u4E2D\u72B6\u6001 */
|
|
421
438
|
[data-component-id].djvlc-selected {
|
|
422
|
-
outline: 2px solid
|
|
439
|
+
outline: 2px solid ${indicatorColor} !important;
|
|
423
440
|
outline-offset: 2px;
|
|
424
441
|
}
|
|
425
442
|
|
|
426
443
|
/* \u60AC\u505C\u72B6\u6001 */
|
|
427
444
|
[data-component-id].djvlc-hovered {
|
|
428
|
-
outline: 1px dashed
|
|
445
|
+
outline: 1px dashed ${indicatorColor} !important;
|
|
429
446
|
outline-offset: 1px;
|
|
430
447
|
}
|
|
431
448
|
|
|
@@ -439,39 +456,300 @@ var SandboxClient = class {
|
|
|
439
456
|
pointer-events: auto;
|
|
440
457
|
}
|
|
441
458
|
|
|
459
|
+
/* \u53EF\u62D6\u62FD\u72B6\u6001 */
|
|
460
|
+
[data-component-id][draggable="true"] {
|
|
461
|
+
cursor: grab;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
[data-component-id][draggable="true"]:active {
|
|
465
|
+
cursor: grabbing;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/* \u62D6\u62FD\u4E2D\u72B6\u6001 */
|
|
469
|
+
[data-component-id].djvlc-dragging {
|
|
470
|
+
opacity: ${this.options.dragOptions?.dragOpacity || 0.5};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* \u653E\u7F6E\u76EE\u6807\u72B6\u6001 */
|
|
474
|
+
[data-component-id].djvlc-drop-target {
|
|
475
|
+
outline: 2px dashed ${indicatorColor} !important;
|
|
476
|
+
outline-offset: 4px;
|
|
477
|
+
}
|
|
478
|
+
|
|
442
479
|
/* \u62D6\u62FD\u5360\u4F4D\u7B26 */
|
|
443
480
|
.djvlc-drop-indicator {
|
|
444
481
|
position: absolute;
|
|
445
|
-
background:
|
|
446
|
-
opacity: 0.
|
|
482
|
+
background: ${indicatorColor};
|
|
483
|
+
opacity: 0.8;
|
|
447
484
|
pointer-events: none;
|
|
448
485
|
z-index: 10000;
|
|
486
|
+
transition: all 0.1s ease;
|
|
449
487
|
}
|
|
450
488
|
|
|
451
489
|
.djvlc-drop-indicator.horizontal {
|
|
452
490
|
height: 4px;
|
|
453
491
|
width: 100%;
|
|
492
|
+
border-radius: 2px;
|
|
454
493
|
}
|
|
455
494
|
|
|
456
495
|
.djvlc-drop-indicator.vertical {
|
|
457
496
|
width: 4px;
|
|
458
497
|
height: 100%;
|
|
498
|
+
border-radius: 2px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* \u653E\u7F6E\u63D0\u793A\u7BAD\u5934 */
|
|
502
|
+
.djvlc-drop-indicator::before {
|
|
503
|
+
content: '';
|
|
504
|
+
position: absolute;
|
|
505
|
+
width: 8px;
|
|
506
|
+
height: 8px;
|
|
507
|
+
background: ${indicatorColor};
|
|
508
|
+
border-radius: 50%;
|
|
509
|
+
left: -4px;
|
|
510
|
+
top: -2px;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.djvlc-drop-indicator::after {
|
|
514
|
+
content: '';
|
|
515
|
+
position: absolute;
|
|
516
|
+
width: 8px;
|
|
517
|
+
height: 8px;
|
|
518
|
+
background: ${indicatorColor};
|
|
519
|
+
border-radius: 50%;
|
|
520
|
+
right: -4px;
|
|
521
|
+
top: -2px;
|
|
459
522
|
}
|
|
460
523
|
`;
|
|
461
524
|
document.head.appendChild(style);
|
|
462
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* 初始化拖拽事件监听
|
|
528
|
+
*/
|
|
529
|
+
initDragEventListeners() {
|
|
530
|
+
if (!this.options.enableDrag) return;
|
|
531
|
+
this.makeComponentsDraggable();
|
|
532
|
+
document.addEventListener("dragstart", (e) => {
|
|
533
|
+
const target = e.target;
|
|
534
|
+
const componentEl = this.findComponentElement(target);
|
|
535
|
+
if (!componentEl) return;
|
|
536
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
537
|
+
if (!componentId) return;
|
|
538
|
+
this.dragState.isDragging = true;
|
|
539
|
+
this.dragState.draggedComponentId = componentId;
|
|
540
|
+
this.dragState.draggedElement = componentEl;
|
|
541
|
+
componentEl.classList.add("djvlc-dragging");
|
|
542
|
+
e.dataTransfer?.setData("text/plain", componentId);
|
|
543
|
+
e.dataTransfer?.setData("application/json", JSON.stringify({
|
|
544
|
+
componentId,
|
|
545
|
+
componentType: componentEl.getAttribute("data-component-type")
|
|
546
|
+
}));
|
|
547
|
+
if (e.dataTransfer) {
|
|
548
|
+
e.dataTransfer.effectAllowed = "move";
|
|
549
|
+
}
|
|
550
|
+
this.log("debug", "Drag start:", componentId);
|
|
551
|
+
});
|
|
552
|
+
document.addEventListener("dragover", (e) => {
|
|
553
|
+
if (!this.dragState.isDragging) return;
|
|
554
|
+
e.preventDefault();
|
|
555
|
+
const target = e.target;
|
|
556
|
+
const componentEl = this.findComponentElement(target);
|
|
557
|
+
if (!componentEl || componentEl === this.dragState.draggedElement) {
|
|
558
|
+
this.hideDropIndicator();
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
562
|
+
if (!componentId) return;
|
|
563
|
+
const rect = componentEl.getBoundingClientRect();
|
|
564
|
+
const y = e.clientY;
|
|
565
|
+
const threshold = rect.height / 3;
|
|
566
|
+
let position;
|
|
567
|
+
if (y < rect.top + threshold) {
|
|
568
|
+
position = "before";
|
|
569
|
+
} else if (y > rect.bottom - threshold) {
|
|
570
|
+
position = "after";
|
|
571
|
+
} else {
|
|
572
|
+
position = "inside";
|
|
573
|
+
}
|
|
574
|
+
if (this.dragState.dropTarget !== componentEl || this.dragState.dropPosition !== position) {
|
|
575
|
+
this.dragState.dropTarget?.classList.remove("djvlc-drop-target");
|
|
576
|
+
this.dragState.dropTarget = componentEl;
|
|
577
|
+
this.dragState.dropPosition = position;
|
|
578
|
+
if (position === "inside") {
|
|
579
|
+
componentEl.classList.add("djvlc-drop-target");
|
|
580
|
+
this.hideDropIndicator();
|
|
581
|
+
} else {
|
|
582
|
+
componentEl.classList.remove("djvlc-drop-target");
|
|
583
|
+
this.showDropIndicator(componentEl, position);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
document.addEventListener("dragleave", (e) => {
|
|
588
|
+
const target = e.target;
|
|
589
|
+
const componentEl = this.findComponentElement(target);
|
|
590
|
+
if (componentEl && componentEl === this.dragState.dropTarget) {
|
|
591
|
+
const relatedTarget = e.relatedTarget;
|
|
592
|
+
if (!relatedTarget || !componentEl.contains(relatedTarget)) {
|
|
593
|
+
componentEl.classList.remove("djvlc-drop-target");
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
document.addEventListener("drop", (e) => {
|
|
598
|
+
if (!this.dragState.isDragging) return;
|
|
599
|
+
e.preventDefault();
|
|
600
|
+
const { draggedComponentId, dropTarget, dropPosition } = this.dragState;
|
|
601
|
+
if (draggedComponentId && dropTarget && dropPosition) {
|
|
602
|
+
const targetComponentId = dropTarget.getAttribute("data-component-id");
|
|
603
|
+
if (targetComponentId && targetComponentId !== draggedComponentId) {
|
|
604
|
+
this.log("info", "Drop:", draggedComponentId, dropPosition, targetComponentId);
|
|
605
|
+
this.send("component_drop" /* COMPONENT_DROP */, {
|
|
606
|
+
componentId: draggedComponentId,
|
|
607
|
+
targetComponentId,
|
|
608
|
+
position: dropPosition
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
this.endDrag();
|
|
613
|
+
});
|
|
614
|
+
document.addEventListener("dragend", () => {
|
|
615
|
+
this.endDrag();
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* 使组件可拖拽
|
|
620
|
+
*/
|
|
621
|
+
makeComponentsDraggable() {
|
|
622
|
+
const components = document.querySelectorAll("[data-component-id]");
|
|
623
|
+
components.forEach((el) => {
|
|
624
|
+
el.draggable = true;
|
|
625
|
+
});
|
|
626
|
+
const observer = new MutationObserver((mutations) => {
|
|
627
|
+
mutations.forEach((mutation) => {
|
|
628
|
+
mutation.addedNodes.forEach((node) => {
|
|
629
|
+
if (node instanceof HTMLElement) {
|
|
630
|
+
if (node.hasAttribute("data-component-id")) {
|
|
631
|
+
node.draggable = true;
|
|
632
|
+
}
|
|
633
|
+
node.querySelectorAll("[data-component-id]").forEach((el) => {
|
|
634
|
+
el.draggable = true;
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
observer.observe(document.body, {
|
|
641
|
+
childList: true,
|
|
642
|
+
subtree: true
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 显示放置指示器
|
|
647
|
+
*/
|
|
648
|
+
showDropIndicator(target, position) {
|
|
649
|
+
if (!this.options.dragOptions?.showDropIndicator) return;
|
|
650
|
+
let indicator = this.dragState.dropIndicator;
|
|
651
|
+
if (!indicator) {
|
|
652
|
+
indicator = document.createElement("div");
|
|
653
|
+
indicator.className = "djvlc-drop-indicator horizontal";
|
|
654
|
+
document.body.appendChild(indicator);
|
|
655
|
+
this.dragState.dropIndicator = indicator;
|
|
656
|
+
}
|
|
657
|
+
const rect = target.getBoundingClientRect();
|
|
658
|
+
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
659
|
+
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
|
|
660
|
+
indicator.style.left = `${rect.left + scrollLeft}px`;
|
|
661
|
+
indicator.style.width = `${rect.width}px`;
|
|
662
|
+
if (position === "before") {
|
|
663
|
+
indicator.style.top = `${rect.top + scrollTop - 2}px`;
|
|
664
|
+
} else {
|
|
665
|
+
indicator.style.top = `${rect.bottom + scrollTop - 2}px`;
|
|
666
|
+
}
|
|
667
|
+
indicator.style.display = "block";
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 隐藏放置指示器
|
|
671
|
+
*/
|
|
672
|
+
hideDropIndicator() {
|
|
673
|
+
if (this.dragState.dropIndicator) {
|
|
674
|
+
this.dragState.dropIndicator.style.display = "none";
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* 结束拖拽
|
|
679
|
+
*/
|
|
680
|
+
endDrag() {
|
|
681
|
+
this.dragState.draggedElement?.classList.remove("djvlc-dragging");
|
|
682
|
+
this.dragState.dropTarget?.classList.remove("djvlc-drop-target");
|
|
683
|
+
this.hideDropIndicator();
|
|
684
|
+
this.dragState = {
|
|
685
|
+
isDragging: false,
|
|
686
|
+
draggedComponentId: null,
|
|
687
|
+
draggedElement: null,
|
|
688
|
+
dropTarget: null,
|
|
689
|
+
dropPosition: null,
|
|
690
|
+
dropIndicator: this.dragState.dropIndicator
|
|
691
|
+
// 保留 indicator 元素
|
|
692
|
+
};
|
|
693
|
+
}
|
|
463
694
|
async handleInit(data) {
|
|
464
695
|
this.log("info", "Init with config:", data);
|
|
696
|
+
void data;
|
|
697
|
+
if (this.runtime) {
|
|
698
|
+
try {
|
|
699
|
+
await this.runtime.init();
|
|
700
|
+
} catch (error) {
|
|
701
|
+
this.log("error", "Failed to init runtime:", error);
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
465
705
|
}
|
|
466
706
|
async handleLoadPage(data) {
|
|
467
707
|
this.log("info", "Load page:", data.pageUid);
|
|
708
|
+
if (!this.runtime) {
|
|
709
|
+
throw new Error("Runtime not set");
|
|
710
|
+
}
|
|
711
|
+
try {
|
|
712
|
+
await this.runtime.load();
|
|
713
|
+
await this.runtime.render();
|
|
714
|
+
this.syncState();
|
|
715
|
+
} catch (error) {
|
|
716
|
+
this.log("error", "Failed to load page:", error);
|
|
717
|
+
throw error;
|
|
718
|
+
}
|
|
468
719
|
}
|
|
469
720
|
async handleUpdateSchema(data) {
|
|
470
721
|
this.log("info", "Update schema", data.fullUpdate ? "(full)" : "(partial)");
|
|
722
|
+
if (!this.runtime) {
|
|
723
|
+
throw new Error("Runtime not set");
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
if (data.fullUpdate) {
|
|
727
|
+
await this.runtime.load();
|
|
728
|
+
await this.runtime.render();
|
|
729
|
+
} else {
|
|
730
|
+
await this.runtime.load();
|
|
731
|
+
await this.runtime.render();
|
|
732
|
+
}
|
|
733
|
+
this.syncState();
|
|
734
|
+
} catch (error) {
|
|
735
|
+
this.log("error", "Failed to update schema:", error);
|
|
736
|
+
throw error;
|
|
737
|
+
}
|
|
471
738
|
}
|
|
472
739
|
async handleRefresh() {
|
|
473
740
|
this.log("info", "Refresh page");
|
|
474
|
-
|
|
741
|
+
if (this.runtime) {
|
|
742
|
+
try {
|
|
743
|
+
await this.runtime.load();
|
|
744
|
+
await this.runtime.render();
|
|
745
|
+
this.syncState();
|
|
746
|
+
} catch (error) {
|
|
747
|
+
this.log("error", "Failed to refresh:", error);
|
|
748
|
+
window.location.reload();
|
|
749
|
+
}
|
|
750
|
+
} else {
|
|
751
|
+
window.location.reload();
|
|
752
|
+
}
|
|
475
753
|
}
|
|
476
754
|
handleSelectComponent(data) {
|
|
477
755
|
if (data.componentId) {
|
|
@@ -507,13 +785,20 @@ var SandboxClient = class {
|
|
|
507
785
|
this.runtime?.updateComponent(data.componentId, data.props || {});
|
|
508
786
|
}
|
|
509
787
|
handleAddComponent(data) {
|
|
510
|
-
this.log("info", "Add component:", data.component.
|
|
788
|
+
this.log("info", "Add component:", data.component.componentType);
|
|
789
|
+
this.log("warn", "addComponent not supported, need to reload page");
|
|
511
790
|
}
|
|
512
791
|
handleMoveComponent(data) {
|
|
513
792
|
this.log("info", "Move component:", data.componentId, "to", data.targetIndex);
|
|
793
|
+
this.log("warn", "moveComponent not supported, need to reload page");
|
|
514
794
|
}
|
|
515
795
|
handleDeleteComponent(data) {
|
|
516
796
|
this.log("info", "Delete component:", data.componentId);
|
|
797
|
+
this.log("warn", "deleteComponent not supported, need to reload page");
|
|
798
|
+
this.selectedComponents.delete(data.componentId);
|
|
799
|
+
if (this.hoveredComponent === data.componentId) {
|
|
800
|
+
this.hoveredComponent = null;
|
|
801
|
+
}
|
|
517
802
|
}
|
|
518
803
|
handleSyncVariables(data) {
|
|
519
804
|
Object.entries(data.variables).forEach(([key, value]) => {
|
|
@@ -532,11 +817,12 @@ var SandboxClient = class {
|
|
|
532
817
|
});
|
|
533
818
|
}
|
|
534
819
|
syncState() {
|
|
820
|
+
const runtimeState = this.runtime?.getState();
|
|
535
821
|
const state = {
|
|
536
|
-
phase:
|
|
537
|
-
page:
|
|
538
|
-
pageUid:
|
|
539
|
-
pageVersionId:
|
|
822
|
+
phase: runtimeState?.phase || "idle",
|
|
823
|
+
page: runtimeState?.page ? {
|
|
824
|
+
pageUid: runtimeState.page.pageUid,
|
|
825
|
+
pageVersionId: runtimeState.page.pageVersionId
|
|
540
826
|
} : void 0,
|
|
541
827
|
selectedComponents: Array.from(this.selectedComponents),
|
|
542
828
|
hoveredComponent: this.hoveredComponent
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djvlc/sandbox",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "DJV 低代码平台沙箱通信模块",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
21
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
20
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --no-sourcemap",
|
|
21
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --no-sourcemap",
|
|
22
22
|
"test": "vitest run --passWithNoTests",
|
|
23
23
|
"test:watch": "vitest",
|
|
24
24
|
"lint": "eslint src --ext .ts",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"clean": "rimraf dist"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@djvlc/contracts-types": "
|
|
29
|
+
"@djvlc/contracts-types": "1.8.3",
|
|
30
|
+
"@djvlc/runtime-core": "1.0.1"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@types/node": "^20.10.0",
|