@pixerate/editor-svelte 0.10.0 → 0.11.0
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/canvas/index.d.ts +115 -0
- package/dist/canvas/index.js +321 -32
- package/dist/canvas/index.js.map +1 -1
- package/package.json +2 -2
- package/src/canvas/FloatingHorizontalScrollbar.svelte +110 -0
- package/src/canvas/__tests__/canvas-multi-drag.test.ts +112 -0
- package/src/canvas/__tests__/canvas-node-sync.svelte.test.ts +104 -0
- package/src/canvas/createCanvasMultiDrag.svelte.ts +175 -0
- package/src/canvas/createCanvasNodeSync.svelte.ts +150 -0
- package/src/canvas/createCanvasSelection.svelte.ts +51 -0
- package/src/canvas/index.d.ts +115 -0
- package/src/canvas/index.ts +30 -0
- package/src/canvas/types.ts +1 -0
package/dist/canvas/index.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export interface CanvasInteractionPresetConfig {
|
|
|
93
93
|
multiSelectionKey?: string[];
|
|
94
94
|
minZoom?: number;
|
|
95
95
|
maxZoom?: number;
|
|
96
|
+
selectionMode?: 'partial' | 'full';
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
/**
|
|
@@ -571,3 +572,117 @@ export declare function createCanvasExplosion<
|
|
|
571
572
|
getSnapshot(parentId: string): ExplosionSnapshot<TNode, TEdge> | undefined;
|
|
572
573
|
};
|
|
573
574
|
|
|
575
|
+
export interface HandleConfig {
|
|
576
|
+
id?: string | null;
|
|
577
|
+
type: 'source' | 'target';
|
|
578
|
+
position: any;
|
|
579
|
+
x?: number;
|
|
580
|
+
y?: number;
|
|
581
|
+
width?: number;
|
|
582
|
+
height?: number;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export interface NodeMeasurement {
|
|
586
|
+
width: number;
|
|
587
|
+
height: number;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export declare function preserveNodeMeasurements<TNode extends CanvasNode = CanvasNode>(
|
|
591
|
+
newNodes: TNode[],
|
|
592
|
+
existingNodes: TNode[] | Map<string, TNode>
|
|
593
|
+
): TNode[];
|
|
594
|
+
|
|
595
|
+
export declare function normalizeNodeHandles<TNode extends CanvasNode = CanvasNode>(
|
|
596
|
+
node: TNode,
|
|
597
|
+
handles?: HandleConfig[]
|
|
598
|
+
): TNode;
|
|
599
|
+
|
|
600
|
+
export interface CreateCanvasNodeSyncOptions<
|
|
601
|
+
TItem,
|
|
602
|
+
TNode extends CanvasNode = CanvasNode,
|
|
603
|
+
TEdge extends CanvasEdge = CanvasEdge
|
|
604
|
+
> {
|
|
605
|
+
items: () => TItem[];
|
|
606
|
+
toNode: (
|
|
607
|
+
item: TItem,
|
|
608
|
+
index: number,
|
|
609
|
+
context: { existingNode?: TNode; prevNodesMap: Map<string, TNode> }
|
|
610
|
+
) => TNode;
|
|
611
|
+
toEdges?: (items: TItem[], nodes: TNode[]) => TEdge[];
|
|
612
|
+
defaultHandles?: (node: TNode) => HandleConfig[] | undefined;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export declare function createCanvasNodeSync<
|
|
616
|
+
TItem,
|
|
617
|
+
TNode extends CanvasNode = CanvasNode,
|
|
618
|
+
TEdge extends CanvasEdge = CanvasEdge
|
|
619
|
+
>(options: CreateCanvasNodeSyncOptions<TItem, TNode, TEdge>): {
|
|
620
|
+
nodes: TNode[];
|
|
621
|
+
edges: TEdge[];
|
|
622
|
+
updateNodeMeasurements(id: string, width: number, height: number): void;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
export interface MultiDragFollowerOffset {
|
|
626
|
+
deltaX: number;
|
|
627
|
+
deltaY: number;
|
|
628
|
+
originalPosition: XYPosition;
|
|
629
|
+
stackOffsetX: number;
|
|
630
|
+
stackOffsetY: number;
|
|
631
|
+
rotationDeg: number;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export interface MultiDragState {
|
|
635
|
+
leadId: string;
|
|
636
|
+
offsets: Record<string, MultiDragFollowerOffset>;
|
|
637
|
+
count: number;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export interface CreateCanvasMultiDragOptions {
|
|
641
|
+
stackOffsetStepX?: number;
|
|
642
|
+
stackOffsetStepY?: number;
|
|
643
|
+
rotationAngles?: number[];
|
|
644
|
+
baseZIndex?: number;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export interface FollowerTransform {
|
|
648
|
+
isFollower: boolean;
|
|
649
|
+
stackOffsetX: number;
|
|
650
|
+
stackOffsetY: number;
|
|
651
|
+
rotationDeg: number;
|
|
652
|
+
zIndex: number;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export declare function createCanvasMultiDrag(options?: CreateCanvasMultiDragOptions): {
|
|
656
|
+
readonly dragState: MultiDragState | null;
|
|
657
|
+
readonly isMultiDrag: boolean;
|
|
658
|
+
readonly count: number;
|
|
659
|
+
readonly leadId: string | null;
|
|
660
|
+
startMultiDrag(
|
|
661
|
+
leadNodeId: string,
|
|
662
|
+
selectedNodeIds: string[] | Set<string>,
|
|
663
|
+
nodes: CanvasNode[] | Map<string, CanvasNode>
|
|
664
|
+
): MultiDragState | null;
|
|
665
|
+
getFollowerTransform(nodeId: string): FollowerTransform;
|
|
666
|
+
cancelMultiDrag(
|
|
667
|
+
applyRestore?: (restores: { id: string; originalPosition: XYPosition }[]) => void
|
|
668
|
+
): { id: string; originalPosition: XYPosition }[];
|
|
669
|
+
commitMultiDrag(): void;
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
export interface CanvasSelectionOptions {
|
|
673
|
+
isSpacebarPanning?: boolean;
|
|
674
|
+
selectionMode?: 'partial' | 'full';
|
|
675
|
+
multiSelectionKey?: string[];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export declare function getMarqueeSelectionPreset(
|
|
679
|
+
options?: CanvasSelectionOptions
|
|
680
|
+
): CanvasInteractionPresetConfig;
|
|
681
|
+
|
|
682
|
+
export declare function handleDeselectOnEscape(
|
|
683
|
+
event: KeyboardEvent,
|
|
684
|
+
onDeselect: () => void
|
|
685
|
+
): boolean;
|
|
686
|
+
|
|
687
|
+
export declare const FloatingHorizontalScrollbar: any;
|
|
688
|
+
|
package/dist/canvas/index.js
CHANGED
|
@@ -279,8 +279,8 @@ function getLayoutedNodes(nodes, edges, getNodesBounds, options = {}) {
|
|
|
279
279
|
// src/canvas/createCanvasClipboard.svelte.ts
|
|
280
280
|
import * as $2 from "svelte/internal/client";
|
|
281
281
|
import { tick } from "svelte";
|
|
282
|
-
function isEventFromTextInput(
|
|
283
|
-
const target =
|
|
282
|
+
function isEventFromTextInput(event2) {
|
|
283
|
+
const target = event2.target;
|
|
284
284
|
if (!target) return false;
|
|
285
285
|
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable || target.closest?.('[contenteditable="true"]') || target.closest?.(".ProseMirror")) {
|
|
286
286
|
return true;
|
|
@@ -299,44 +299,44 @@ function isValidUrl(url) {
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
function createCanvasClipboard(options = {}) {
|
|
302
|
-
function copySelectedNodes(selectedNodes, connectedEdges,
|
|
303
|
-
if (
|
|
302
|
+
function copySelectedNodes(selectedNodes, connectedEdges, event2) {
|
|
303
|
+
if (event2 && isEventFromTextInput(event2)) {
|
|
304
304
|
return false;
|
|
305
305
|
}
|
|
306
306
|
if (selectedNodes.length === 0) return false;
|
|
307
307
|
const payload = { version: 1, nodes: selectedNodes, edges: connectedEdges };
|
|
308
308
|
const serialized = JSON.stringify(payload);
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
if (event2?.clipboardData) {
|
|
310
|
+
event2.clipboardData.setData("application/json", serialized);
|
|
311
|
+
event2.clipboardData.setData("text/plain", serialized);
|
|
312
|
+
event2.preventDefault();
|
|
313
313
|
} else if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
314
314
|
navigator.clipboard.writeText(serialized).catch(() => {
|
|
315
315
|
});
|
|
316
316
|
}
|
|
317
317
|
return true;
|
|
318
318
|
}
|
|
319
|
-
function cutSelectedNodes(selectedNodes, connectedEdges, onRemove,
|
|
320
|
-
if (
|
|
319
|
+
function cutSelectedNodes(selectedNodes, connectedEdges, onRemove, event2) {
|
|
320
|
+
if (event2 && isEventFromTextInput(event2)) {
|
|
321
321
|
return false;
|
|
322
322
|
}
|
|
323
|
-
const copied = copySelectedNodes(selectedNodes, connectedEdges,
|
|
323
|
+
const copied = copySelectedNodes(selectedNodes, connectedEdges, event2);
|
|
324
324
|
if (copied && onRemove) {
|
|
325
325
|
onRemove(selectedNodes);
|
|
326
326
|
}
|
|
327
327
|
return copied;
|
|
328
328
|
}
|
|
329
|
-
async function handlePasteEvent(
|
|
330
|
-
if (isEventFromTextInput(
|
|
329
|
+
async function handlePasteEvent(event2) {
|
|
330
|
+
if (isEventFromTextInput(event2)) {
|
|
331
331
|
return false;
|
|
332
332
|
}
|
|
333
|
-
const clipboardData =
|
|
333
|
+
const clipboardData = event2.clipboardData;
|
|
334
334
|
if (!clipboardData) return false;
|
|
335
335
|
const files = clipboardData.files;
|
|
336
336
|
if (files && files.length > 0) {
|
|
337
337
|
const imageFile = Array.from(files).find((f) => f.type.startsWith("image/"));
|
|
338
338
|
if (imageFile && options.onPasteImage) {
|
|
339
|
-
|
|
339
|
+
event2.preventDefault();
|
|
340
340
|
await options.onPasteImage(imageFile);
|
|
341
341
|
return true;
|
|
342
342
|
}
|
|
@@ -354,7 +354,7 @@ function createCanvasClipboard(options = {}) {
|
|
|
354
354
|
rawEdges = parsed.edges;
|
|
355
355
|
}
|
|
356
356
|
if (rawNodes && rawNodes.length > 0) {
|
|
357
|
-
|
|
357
|
+
event2.preventDefault();
|
|
358
358
|
const idMap = /* @__PURE__ */ new Map();
|
|
359
359
|
const newNodes = rawNodes.map((node) => {
|
|
360
360
|
const newId = crypto.randomUUID();
|
|
@@ -384,11 +384,11 @@ function createCanvasClipboard(options = {}) {
|
|
|
384
384
|
const text = clipboardData.getData("text/plain")?.trim();
|
|
385
385
|
if (text) {
|
|
386
386
|
if (isValidUrl(text) && options.onPasteUrl) {
|
|
387
|
-
|
|
387
|
+
event2.preventDefault();
|
|
388
388
|
options.onPasteUrl(text);
|
|
389
389
|
return true;
|
|
390
390
|
} else if (options.onPasteText) {
|
|
391
|
-
|
|
391
|
+
event2.preventDefault();
|
|
392
392
|
options.onPasteText(text);
|
|
393
393
|
return true;
|
|
394
394
|
}
|
|
@@ -421,19 +421,19 @@ function getOverlapArea(rectA, rectB) {
|
|
|
421
421
|
const yOverlap = Math.max(0, Math.min(rectA.y + rectA.height, rectB.y + rectB.height) - Math.max(rectA.y, rectB.y));
|
|
422
422
|
return xOverlap * yOverlap;
|
|
423
423
|
}
|
|
424
|
-
function resolvePrimaryTarget(targets, draggedNode, strategy, customResolver,
|
|
424
|
+
function resolvePrimaryTarget(targets, draggedNode, strategy, customResolver, event2) {
|
|
425
425
|
if (targets.length === 0) return null;
|
|
426
426
|
if (targets.length === 1) return targets[0];
|
|
427
427
|
if (customResolver) {
|
|
428
|
-
const resolved = customResolver(targets, draggedNode,
|
|
428
|
+
const resolved = customResolver(targets, draggedNode, event2);
|
|
429
429
|
if (resolved !== void 0) return resolved;
|
|
430
430
|
}
|
|
431
431
|
if (strategy === "first") {
|
|
432
432
|
return targets[0];
|
|
433
433
|
}
|
|
434
|
-
if (strategy === "pointer" &&
|
|
435
|
-
const clientX = "clientX" in
|
|
436
|
-
const clientY = "clientY" in
|
|
434
|
+
if (strategy === "pointer" && event2) {
|
|
435
|
+
const clientX = "clientX" in event2 ? event2.clientX : event2.touches?.[0]?.clientX;
|
|
436
|
+
const clientY = "clientY" in event2 ? event2.clientY : event2.touches?.[0]?.clientY;
|
|
437
437
|
if (typeof clientX === "number" && typeof clientY === "number") {
|
|
438
438
|
const pointerInside = targets.filter((target) => {
|
|
439
439
|
const r = getNodeRect(target);
|
|
@@ -596,8 +596,8 @@ function createCanvasShortcuts(options) {
|
|
|
596
596
|
const target = options.targetElement ?? (typeof window !== "undefined" ? window : null);
|
|
597
597
|
if (!target) return () => {
|
|
598
598
|
};
|
|
599
|
-
function handleKeyDown(
|
|
600
|
-
const keyboardEvent =
|
|
599
|
+
function handleKeyDown(event2) {
|
|
600
|
+
const keyboardEvent = event2;
|
|
601
601
|
if (isEventFromTextInput(keyboardEvent)) {
|
|
602
602
|
return;
|
|
603
603
|
}
|
|
@@ -679,9 +679,9 @@ function createCanvasInteractions(options = {}) {
|
|
|
679
679
|
let isSpacebarPanning = $4.state(false);
|
|
680
680
|
let trackpadDetected = $4.state(false);
|
|
681
681
|
let toggledPanMode = false;
|
|
682
|
-
function handleKeyDown(
|
|
683
|
-
if (
|
|
684
|
-
if ($4.get(isSpacebarPanning) || isEventFromTextInput(
|
|
682
|
+
function handleKeyDown(event2) {
|
|
683
|
+
if (event2.key === " " || event2.code === "Space") {
|
|
684
|
+
if ($4.get(isSpacebarPanning) || isEventFromTextInput(event2)) return;
|
|
685
685
|
$4.set(isSpacebarPanning, true);
|
|
686
686
|
toggledPanMode = true;
|
|
687
687
|
options.onPanModeChange?.(true);
|
|
@@ -690,8 +690,8 @@ function createCanvasInteractions(options = {}) {
|
|
|
690
690
|
}
|
|
691
691
|
}
|
|
692
692
|
}
|
|
693
|
-
function handleKeyUp(
|
|
694
|
-
if (
|
|
693
|
+
function handleKeyUp(event2) {
|
|
694
|
+
if (event2.key === " " || event2.code === "Space") {
|
|
695
695
|
if (!$4.get(isSpacebarPanning) && !toggledPanMode) return;
|
|
696
696
|
$4.set(isSpacebarPanning, false);
|
|
697
697
|
toggledPanMode = false;
|
|
@@ -701,8 +701,8 @@ function createCanvasInteractions(options = {}) {
|
|
|
701
701
|
}
|
|
702
702
|
}
|
|
703
703
|
}
|
|
704
|
-
function handleWheel(
|
|
705
|
-
if (
|
|
704
|
+
function handleWheel(event2) {
|
|
705
|
+
if (event2.ctrlKey && !$4.get(trackpadDetected)) {
|
|
706
706
|
$4.set(trackpadDetected, true);
|
|
707
707
|
options.onTrackpadDetected?.();
|
|
708
708
|
}
|
|
@@ -1380,7 +1380,290 @@ function createCanvasExplosion(graph, options = {}) {
|
|
|
1380
1380
|
getSnapshot
|
|
1381
1381
|
};
|
|
1382
1382
|
}
|
|
1383
|
+
|
|
1384
|
+
// src/canvas/createCanvasNodeSync.svelte.ts
|
|
1385
|
+
import * as $6 from "svelte/internal/client";
|
|
1386
|
+
import { untrack } from "svelte";
|
|
1387
|
+
function preserveNodeMeasurements(newNodes, existingNodes) {
|
|
1388
|
+
const map = existingNodes instanceof Map ? existingNodes : new Map(existingNodes.map((n) => [n.id, n]));
|
|
1389
|
+
for (const node of newNodes) {
|
|
1390
|
+
const prev = map.get(node.id);
|
|
1391
|
+
if (prev && prev.measured) {
|
|
1392
|
+
node.measured = { ...prev.measured };
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return newNodes;
|
|
1396
|
+
}
|
|
1397
|
+
function normalizeNodeHandles(node, handles = []) {
|
|
1398
|
+
node.handles = handles;
|
|
1399
|
+
return node;
|
|
1400
|
+
}
|
|
1401
|
+
function createCanvasNodeSync(options) {
|
|
1402
|
+
let nodes = $6.state($6.proxy([]));
|
|
1403
|
+
let edges = $6.state($6.proxy([]));
|
|
1404
|
+
$6.user_pre_effect(() => {
|
|
1405
|
+
const rawItems = options.items();
|
|
1406
|
+
const existingNodesMap = untrack(() => new Map($6.get(nodes).map((n) => [n.id, n])));
|
|
1407
|
+
const newNodes = rawItems.map((item, index) => {
|
|
1408
|
+
const existing = existingNodesMap.get(item?.id);
|
|
1409
|
+
const node = options.toNode(item, index, { existingNode: existing, prevNodesMap: existingNodesMap });
|
|
1410
|
+
if (existing && existing.measured && !node.measured) {
|
|
1411
|
+
node.measured = { ...existing.measured };
|
|
1412
|
+
}
|
|
1413
|
+
if (options.defaultHandles && !node.handles) {
|
|
1414
|
+
const handles = options.defaultHandles(node);
|
|
1415
|
+
if (handles) {
|
|
1416
|
+
node.handles = handles;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return node;
|
|
1420
|
+
});
|
|
1421
|
+
const newEdges = options.toEdges ? options.toEdges(rawItems, newNodes) : [];
|
|
1422
|
+
$6.set(nodes, newNodes, true);
|
|
1423
|
+
$6.set(edges, newEdges, true);
|
|
1424
|
+
});
|
|
1425
|
+
return {
|
|
1426
|
+
get nodes() {
|
|
1427
|
+
return $6.get(nodes);
|
|
1428
|
+
},
|
|
1429
|
+
set nodes(value) {
|
|
1430
|
+
$6.set(nodes, value, true);
|
|
1431
|
+
},
|
|
1432
|
+
get edges() {
|
|
1433
|
+
return $6.get(edges);
|
|
1434
|
+
},
|
|
1435
|
+
set edges(value) {
|
|
1436
|
+
$6.set(edges, value, true);
|
|
1437
|
+
},
|
|
1438
|
+
updateNodeMeasurements(id, width, height) {
|
|
1439
|
+
const node = $6.get(nodes).find((n) => n.id === id);
|
|
1440
|
+
if (node) {
|
|
1441
|
+
node.measured = { width, height };
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// src/canvas/createCanvasMultiDrag.svelte.ts
|
|
1448
|
+
import * as $7 from "svelte/internal/client";
|
|
1449
|
+
function createCanvasMultiDrag(options = {}) {
|
|
1450
|
+
const stepX = options.stackOffsetStepX ?? 4;
|
|
1451
|
+
const stepY = options.stackOffsetStepY ?? 4;
|
|
1452
|
+
const angles = options.rotationAngles ?? [-2, 2.5, -3, 3];
|
|
1453
|
+
const baseZIndex = options.baseZIndex ?? 50;
|
|
1454
|
+
let dragState = $7.state(null);
|
|
1455
|
+
function startMultiDrag(leadNodeId, selectedNodeIds, nodes) {
|
|
1456
|
+
const selectedSet = selectedNodeIds instanceof Set ? selectedNodeIds : new Set(selectedNodeIds);
|
|
1457
|
+
if (selectedSet.size <= 1 || !selectedSet.has(leadNodeId)) {
|
|
1458
|
+
$7.set(dragState, null);
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
const nodeMap = nodes instanceof Map ? nodes : new Map(nodes.map((n) => [n.id, n]));
|
|
1462
|
+
const leadNode = nodeMap.get(leadNodeId);
|
|
1463
|
+
if (!leadNode) {
|
|
1464
|
+
$7.set(dragState, null);
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
const leadPos = leadNode.position ?? { x: 0, y: 0 };
|
|
1468
|
+
const offsets = {};
|
|
1469
|
+
let followerIndex = 0;
|
|
1470
|
+
for (const id of selectedSet) {
|
|
1471
|
+
if (id === leadNodeId) continue;
|
|
1472
|
+
const follower = nodeMap.get(id);
|
|
1473
|
+
const fPos = follower?.position ?? { x: 0, y: 0 };
|
|
1474
|
+
const angle = angles[followerIndex % angles.length];
|
|
1475
|
+
offsets[id] = {
|
|
1476
|
+
deltaX: fPos.x - leadPos.x,
|
|
1477
|
+
deltaY: fPos.y - leadPos.y,
|
|
1478
|
+
originalPosition: { ...fPos },
|
|
1479
|
+
stackOffsetX: (followerIndex + 1) * stepX,
|
|
1480
|
+
stackOffsetY: (followerIndex + 1) * stepY,
|
|
1481
|
+
rotationDeg: angle
|
|
1482
|
+
};
|
|
1483
|
+
followerIndex++;
|
|
1484
|
+
}
|
|
1485
|
+
const state8 = { leadId: leadNodeId, offsets, count: selectedSet.size };
|
|
1486
|
+
$7.set(dragState, state8, true);
|
|
1487
|
+
return state8;
|
|
1488
|
+
}
|
|
1489
|
+
function getFollowerTransform(nodeId) {
|
|
1490
|
+
if (!$7.get(dragState) || !(nodeId in $7.get(dragState).offsets)) {
|
|
1491
|
+
return {
|
|
1492
|
+
isFollower: false,
|
|
1493
|
+
stackOffsetX: 0,
|
|
1494
|
+
stackOffsetY: 0,
|
|
1495
|
+
rotationDeg: 0,
|
|
1496
|
+
zIndex: 0
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
const offset = $7.get(dragState).offsets[nodeId];
|
|
1500
|
+
const followerKeys = Object.keys($7.get(dragState).offsets);
|
|
1501
|
+
const index = followerKeys.indexOf(nodeId);
|
|
1502
|
+
return {
|
|
1503
|
+
isFollower: true,
|
|
1504
|
+
stackOffsetX: offset.stackOffsetX,
|
|
1505
|
+
stackOffsetY: offset.stackOffsetY,
|
|
1506
|
+
rotationDeg: offset.rotationDeg,
|
|
1507
|
+
zIndex: baseZIndex - (index + 1)
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
function cancelMultiDrag(applyRestore) {
|
|
1511
|
+
if (!$7.get(dragState)) return [];
|
|
1512
|
+
const restores = Object.entries($7.get(dragState).offsets).map(([id, offset]) => ({ id, originalPosition: { ...offset.originalPosition } }));
|
|
1513
|
+
if (applyRestore) {
|
|
1514
|
+
applyRestore(restores);
|
|
1515
|
+
}
|
|
1516
|
+
$7.set(dragState, null);
|
|
1517
|
+
return restores;
|
|
1518
|
+
}
|
|
1519
|
+
function commitMultiDrag() {
|
|
1520
|
+
$7.set(dragState, null);
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
get dragState() {
|
|
1524
|
+
return $7.get(dragState);
|
|
1525
|
+
},
|
|
1526
|
+
get isMultiDrag() {
|
|
1527
|
+
return Boolean($7.get(dragState) && $7.get(dragState).count > 1);
|
|
1528
|
+
},
|
|
1529
|
+
get count() {
|
|
1530
|
+
return $7.get(dragState) ? $7.get(dragState).count : 0;
|
|
1531
|
+
},
|
|
1532
|
+
get leadId() {
|
|
1533
|
+
return $7.get(dragState)?.leadId ?? null;
|
|
1534
|
+
},
|
|
1535
|
+
startMultiDrag,
|
|
1536
|
+
getFollowerTransform,
|
|
1537
|
+
cancelMultiDrag,
|
|
1538
|
+
commitMultiDrag
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// src/canvas/createCanvasSelection.svelte.ts
|
|
1543
|
+
import * as $8 from "svelte/internal/client";
|
|
1544
|
+
function getMarqueeSelectionPreset(options = {}) {
|
|
1545
|
+
const isSpacebarPanning = options.isSpacebarPanning ?? false;
|
|
1546
|
+
return {
|
|
1547
|
+
snapGrid: [25, 25],
|
|
1548
|
+
panOnDrag: isSpacebarPanning ? [0, 1, 2] : [1, 2],
|
|
1549
|
+
panOnScroll: false,
|
|
1550
|
+
selectionOnDrag: !isSpacebarPanning,
|
|
1551
|
+
colorMode: "system",
|
|
1552
|
+
multiSelectionKey: options.multiSelectionKey ?? ["Shift", "Meta"],
|
|
1553
|
+
selectionMode: options.selectionMode ?? "partial"
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function handleDeselectOnEscape(event2, onDeselect) {
|
|
1557
|
+
if (event2.key === "Escape" || event2.code === "Escape") {
|
|
1558
|
+
onDeselect();
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
return false;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
// src/canvas/FloatingHorizontalScrollbar.svelte
|
|
1565
|
+
import "svelte/internal/disclose-version";
|
|
1566
|
+
import * as $9 from "svelte/internal/client";
|
|
1567
|
+
import { onMount } from "svelte";
|
|
1568
|
+
var root = $9.from_html(`<div class="fixed bottom-0 z-50 overflow-x-auto overflow-y-hidden border-t border-border/40 bg-background/80 shadow-md backdrop-blur-sm transition-opacity duration-150" role="scrollbar" aria-orientation="horizontal"><div></div></div>`);
|
|
1569
|
+
function FloatingHorizontalScrollbar($$anchor, $$props) {
|
|
1570
|
+
$9.push($$props, true);
|
|
1571
|
+
let targetElement = $9.prop($$props, "targetElement", 7, null), totalWidth = $9.prop($$props, "totalWidth", 3, 1e3), minHeightAboveFold = $9.prop($$props, "minHeightAboveFold", 3, 80);
|
|
1572
|
+
let scrollbarRef = $9.state(null);
|
|
1573
|
+
let isVisible = $9.state(false);
|
|
1574
|
+
let barLeft = $9.state(0);
|
|
1575
|
+
let barWidth = $9.state(0);
|
|
1576
|
+
let isSyncing = false;
|
|
1577
|
+
function updateMetrics() {
|
|
1578
|
+
if (!targetElement()) {
|
|
1579
|
+
$9.set(isVisible, false);
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
const rect = targetElement().getBoundingClientRect();
|
|
1583
|
+
const hasHorizontalOverflow = targetElement().scrollWidth > targetElement().clientWidth;
|
|
1584
|
+
const isTopInView = rect.top < window.innerHeight;
|
|
1585
|
+
const isBottomBelowViewport = rect.bottom > window.innerHeight;
|
|
1586
|
+
const isBottomAboveTop = rect.bottom > minHeightAboveFold();
|
|
1587
|
+
const shouldShow = hasHorizontalOverflow && isTopInView && isBottomBelowViewport && isBottomAboveTop;
|
|
1588
|
+
$9.set(isVisible, shouldShow, true);
|
|
1589
|
+
if (shouldShow) {
|
|
1590
|
+
const left = Math.max(0, rect.left);
|
|
1591
|
+
const right = Math.min(window.innerWidth, rect.right);
|
|
1592
|
+
$9.set(barLeft, left, true);
|
|
1593
|
+
$9.set(barWidth, Math.max(0, right - left), true);
|
|
1594
|
+
if ($9.get(scrollbarRef) && Math.abs($9.get(scrollbarRef).scrollLeft - targetElement().scrollLeft) > 1) {
|
|
1595
|
+
isSyncing = true;
|
|
1596
|
+
$9.get(scrollbarRef).scrollLeft = targetElement().scrollLeft;
|
|
1597
|
+
requestAnimationFrame(() => {
|
|
1598
|
+
isSyncing = false;
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
function handleFloatingScroll() {
|
|
1604
|
+
if (!$9.get(scrollbarRef) || !targetElement() || isSyncing) return;
|
|
1605
|
+
isSyncing = true;
|
|
1606
|
+
targetElement().scrollLeft = $9.get(scrollbarRef).scrollLeft;
|
|
1607
|
+
requestAnimationFrame(() => {
|
|
1608
|
+
isSyncing = false;
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
onMount(() => {
|
|
1612
|
+
updateMetrics();
|
|
1613
|
+
const onScrollOrResize = () => {
|
|
1614
|
+
updateMetrics();
|
|
1615
|
+
};
|
|
1616
|
+
window.addEventListener("scroll", onScrollOrResize, { passive: true });
|
|
1617
|
+
window.addEventListener("resize", onScrollOrResize, { passive: true });
|
|
1618
|
+
const target = targetElement();
|
|
1619
|
+
if (target) {
|
|
1620
|
+
target.addEventListener("scroll", onScrollOrResize, { passive: true });
|
|
1621
|
+
}
|
|
1622
|
+
return () => {
|
|
1623
|
+
window.removeEventListener("scroll", onScrollOrResize);
|
|
1624
|
+
window.removeEventListener("resize", onScrollOrResize);
|
|
1625
|
+
if (target) {
|
|
1626
|
+
target.removeEventListener("scroll", onScrollOrResize);
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
});
|
|
1630
|
+
$9.user_effect(() => {
|
|
1631
|
+
if (targetElement()) {
|
|
1632
|
+
updateMetrics();
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
var fragment = $9.comment();
|
|
1636
|
+
var node = $9.first_child(fragment);
|
|
1637
|
+
{
|
|
1638
|
+
var consequent = ($$anchor2) => {
|
|
1639
|
+
var div = root();
|
|
1640
|
+
$9.set_attribute(div, "aria-valuemin", 0);
|
|
1641
|
+
var div_1 = $9.only_child(div);
|
|
1642
|
+
$9.bind_this(div, ($$value) => $9.set(scrollbarRef, $$value), () => $9.get(scrollbarRef));
|
|
1643
|
+
$9.template_effect(
|
|
1644
|
+
($0) => {
|
|
1645
|
+
$9.set_style(div, `left: ${$9.get(barLeft) ?? ""}px; width: ${$9.get(barWidth) ?? ""}px; height: 14px;`);
|
|
1646
|
+
$9.set_attribute(div, "aria-controls", targetElement()?.id);
|
|
1647
|
+
$9.set_attribute(div, "aria-valuenow", targetElement()?.scrollLeft ?? 0);
|
|
1648
|
+
$9.set_attribute(div, "aria-valuemax", (targetElement()?.scrollWidth ?? totalWidth()) - (targetElement()?.clientWidth ?? 0));
|
|
1649
|
+
$9.set_style(div_1, `width: ${$0 ?? ""}px; height: 1px;`);
|
|
1650
|
+
},
|
|
1651
|
+
[
|
|
1652
|
+
() => Math.max(totalWidth(), targetElement()?.scrollWidth ?? 0)
|
|
1653
|
+
]
|
|
1654
|
+
);
|
|
1655
|
+
$9.event("scroll", div, handleFloatingScroll);
|
|
1656
|
+
$9.append($$anchor2, div);
|
|
1657
|
+
};
|
|
1658
|
+
$9.if(node, ($$render) => {
|
|
1659
|
+
if ($9.get(isVisible)) $$render(consequent);
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
$9.append($$anchor, fragment);
|
|
1663
|
+
$9.pop();
|
|
1664
|
+
}
|
|
1383
1665
|
export {
|
|
1666
|
+
FloatingHorizontalScrollbar,
|
|
1384
1667
|
calculateDirectionalDisplacement,
|
|
1385
1668
|
calculateGraphDifferences,
|
|
1386
1669
|
calculateReflowDisplacement,
|
|
@@ -1392,17 +1675,23 @@ export {
|
|
|
1392
1675
|
createCanvasExplosion,
|
|
1393
1676
|
createCanvasGraph,
|
|
1394
1677
|
createCanvasInteractions,
|
|
1678
|
+
createCanvasMultiDrag,
|
|
1679
|
+
createCanvasNodeSync,
|
|
1395
1680
|
createCanvasShortcuts,
|
|
1396
1681
|
createFanOutTrajectory,
|
|
1397
1682
|
defaultSvelteFlowPreset,
|
|
1398
1683
|
getCenteredNodePosition,
|
|
1399
1684
|
getLayoutedNodes,
|
|
1685
|
+
getMarqueeSelectionPreset,
|
|
1686
|
+
handleDeselectOnEscape,
|
|
1400
1687
|
isEventFromTextInput,
|
|
1401
1688
|
isValidCanvasNode,
|
|
1402
1689
|
isValidUrl,
|
|
1403
1690
|
linearPositionTrajectory,
|
|
1404
1691
|
linearTrajectory,
|
|
1405
1692
|
miroCompatiblePreset,
|
|
1693
|
+
normalizeNodeHandles,
|
|
1694
|
+
preserveNodeMeasurements,
|
|
1406
1695
|
replaceNodeInGraph,
|
|
1407
1696
|
restorePanelPointerEvents,
|
|
1408
1697
|
runMultiNodeTransition
|