refactor IdentityGraph component to improve touch event handling and state management with IndexedDB
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import {
|
||||
Plus,
|
||||
Upload,
|
||||
@@ -675,7 +676,11 @@
|
||||
}
|
||||
|
||||
async function saveGraph() {
|
||||
const data = { nodes, links, transform };
|
||||
const data = {
|
||||
nodes: JSON.parse(JSON.stringify(nodes)),
|
||||
links: JSON.parse(JSON.stringify(links)),
|
||||
transform: JSON.parse(JSON.stringify(transform)),
|
||||
};
|
||||
await saveToIndexedDB('graph-v1', data);
|
||||
}
|
||||
|
||||
@@ -814,7 +819,7 @@
|
||||
nodes = normalizeNodes(demoData.nodes);
|
||||
links = demoData.links;
|
||||
centerView();
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
goto(window.location.pathname, { replaceState: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -837,7 +842,7 @@
|
||||
} else {
|
||||
centerView();
|
||||
}
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
goto(window.location.pathname, { replaceState: true });
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -1153,7 +1158,9 @@
|
||||
}
|
||||
|
||||
function handleNodeTouchStart(e: TouchEvent, nodeId: string) {
|
||||
e.preventDefault();
|
||||
if (e.cancelable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const touch = e.touches[0];
|
||||
if (!touch) return;
|
||||
clearTouchHold();
|
||||
@@ -1190,6 +1197,39 @@
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function nodeTouchStartAction(node: Element, nodeId: string) {
|
||||
let currentHandler: EventListener | null = null;
|
||||
let currentId = nodeId;
|
||||
|
||||
const setupHandler = (id: string) => {
|
||||
if (currentHandler) {
|
||||
node.removeEventListener('touchstart', currentHandler);
|
||||
}
|
||||
currentHandler = (e: Event) => {
|
||||
if (e instanceof TouchEvent) {
|
||||
handleNodeTouchStart(e, id);
|
||||
}
|
||||
};
|
||||
node.addEventListener('touchstart', currentHandler, { passive: false });
|
||||
currentId = id;
|
||||
};
|
||||
|
||||
setupHandler(nodeId);
|
||||
|
||||
return {
|
||||
update(newNodeId: string) {
|
||||
if (newNodeId !== currentId) {
|
||||
setupHandler(newNodeId);
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
if (currentHandler) {
|
||||
node.removeEventListener('touchstart', currentHandler);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
const mouse = getScreenCoords(e);
|
||||
const dx = mouse.x - lastMouse.x;
|
||||
@@ -1296,14 +1336,18 @@
|
||||
}
|
||||
|
||||
function handleTouchStart(e: TouchEvent) {
|
||||
e.preventDefault();
|
||||
if (e.cancelable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.touches.length === 0) return;
|
||||
const touch = e.touches[0];
|
||||
handleMouseDown(touchToMouseEvent(touch, 'mousedown'));
|
||||
}
|
||||
|
||||
function handleTouchMove(e: TouchEvent) {
|
||||
e.preventDefault();
|
||||
if (e.cancelable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.touches.length === 0) return;
|
||||
const touch = e.touches[0];
|
||||
if (touchHoldTimeout && !isLongPressing) {
|
||||
@@ -1319,7 +1363,9 @@
|
||||
}
|
||||
|
||||
function handleTouchEnd(e: TouchEvent) {
|
||||
e.preventDefault();
|
||||
if (e.cancelable) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const wasLongPressing = isLongPressing;
|
||||
clearTouchHold();
|
||||
if (wasLongPressing) {
|
||||
@@ -1701,6 +1747,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
const element = containerElement;
|
||||
if (element) {
|
||||
element.addEventListener('touchstart', handleTouchStart, { passive: false });
|
||||
element.addEventListener('touchmove', handleTouchMove, { passive: false });
|
||||
element.addEventListener('touchend', handleTouchEnd, { passive: false });
|
||||
element.addEventListener('touchcancel', handleTouchEnd, { passive: false });
|
||||
|
||||
return () => {
|
||||
element.removeEventListener('touchstart', handleTouchStart);
|
||||
element.removeEventListener('touchmove', handleTouchMove);
|
||||
element.removeEventListener('touchend', handleTouchEnd);
|
||||
element.removeEventListener('touchcancel', handleTouchEnd);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const log = (msg: string) => {
|
||||
console.log(msg);
|
||||
@@ -1932,10 +1995,6 @@
|
||||
onmousemove={handleMouseMove}
|
||||
onmouseup={handleMouseUp}
|
||||
onmouseleave={handleMouseUp}
|
||||
ontouchstart={handleTouchStart}
|
||||
ontouchmove={handleTouchMove}
|
||||
ontouchend={handleTouchEnd}
|
||||
ontouchcancel={handleTouchEnd}
|
||||
tabindex="0"
|
||||
onkeydown={handleKeydown}
|
||||
role="application"
|
||||
@@ -2185,7 +2244,7 @@
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onmousedown={(e) => handleNodeMouseDown(e, node.id)}
|
||||
ontouchstart={(e) => handleNodeTouchStart(e, node.id)}
|
||||
use:nodeTouchStartAction={node.id}
|
||||
onmouseenter={() => (hoverNodeId = node.id)}
|
||||
onmouseleave={() => (hoverNodeId = null)}
|
||||
onkeydown={(e) => e.key === 'Enter' && (selectedNodeId = node.id)}
|
||||
|
||||
Reference in New Issue
Block a user