Merge branch 'dev' of https://git.peaceultime.com/peaceultime/obsidian-visualiser into dev
This commit is contained in:
commit
83ac9b1f36
|
|
@ -1,4 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { Position } from '#shared/canvas.util';
|
||||||
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
||||||
const rotation: Record<Direction, string> = {
|
const rotation: Record<Direction, string> = {
|
||||||
top: "180",
|
top: "180",
|
||||||
|
|
@ -6,6 +7,27 @@ const rotation: Record<Direction, string> = {
|
||||||
left: "90",
|
left: "90",
|
||||||
right: "270"
|
right: "270"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cancelEvent = (e: Event) => e.preventDefault();
|
||||||
|
function center(touches: TouchList): Position
|
||||||
|
{
|
||||||
|
const pos = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
for(const touch of touches)
|
||||||
|
{
|
||||||
|
pos.x += touch.clientX;
|
||||||
|
pos.y += touch.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.x /= touches.length;
|
||||||
|
pos.y /= touches.length;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
function distance(touches: TouchList): number
|
||||||
|
{
|
||||||
|
const [A, B] = touches;
|
||||||
|
return Math.hypot(B.clientX - A.clientX, B.clientY - A.clientY);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
@ -29,14 +51,14 @@ const nodes = computed(() => {
|
||||||
{ bg: `bg-light-${e.color?.class} dark:bg-dark-${e.color?.class}`, border: `border-light-${e.color?.class} dark:border-dark-${e.color?.class}`, outline: `outline-light-${e.color?.class} dark:outline-dark-${e.color?.class}` } :
|
{ bg: `bg-light-${e.color?.class} dark:bg-dark-${e.color?.class}`, border: `border-light-${e.color?.class} dark:border-dark-${e.color?.class}`, outline: `outline-light-${e.color?.class} dark:outline-dark-${e.color?.class}` } :
|
||||||
{ bg: `bg-colored`, border: `border-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } :
|
{ bg: `bg-colored`, border: `border-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } :
|
||||||
{ border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40`, outline: `outline-light-40 dark:outline-dark-40` } }))
|
{ border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40`, outline: `outline-light-40 dark:outline-dark-40` } }))
|
||||||
})
|
});
|
||||||
const edges = computed(() => {
|
const edges = computed(() => {
|
||||||
return canvas.edges.map(e => {
|
return canvas.edges.map(e => {
|
||||||
const from = canvas.nodes.find(f => f.id === e.fromNode), to = canvas.nodes.find(f => f.id === e.toNode);
|
const from = canvas.nodes.find(f => f.id === e.fromNode), to = canvas.nodes.find(f => f.id === e.toNode);
|
||||||
const path = getPath(from!, e.fromSide, to!, e.toSide)!;
|
const path = getPath(from!, e.fromSide, to!, e.toSide)!;
|
||||||
return { ...e, from, to, path };
|
return { ...e, from, to, path };
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const reset = (_: MouseEvent) => {
|
const reset = (_: MouseEvent) => {
|
||||||
zoom.value = minZoom.value;
|
zoom.value = minZoom.value;
|
||||||
|
|
@ -45,56 +67,162 @@ const reset = (_: MouseEvent) => {
|
||||||
dispY.value = 0;
|
dispY.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelEvent = (e: Event) => e.preventDefault()
|
onMounted(() => {
|
||||||
useHover(({ hovering }) => {
|
let lastX = 0, lastY = 0, lastDistance = 0;
|
||||||
if (!hovering) {
|
let box = canvasRef.value?.getBoundingClientRect()!;
|
||||||
//@ts-ignore
|
const dragMove = (e: MouseEvent) => {
|
||||||
window.removeEventListener('wheel', cancelEvent, { passive: false });
|
dispX.value -= (lastX - e.clientX) / zoom.value;
|
||||||
document.removeEventListener('gesturestart', cancelEvent)
|
dispY.value -= (lastY - e.clientY) / zoom.value;
|
||||||
document.removeEventListener('gesturechange', cancelEvent)
|
lastX = e.clientX;
|
||||||
return
|
lastY = e.clientY;
|
||||||
}
|
};
|
||||||
|
const dragEnd = (e: MouseEvent) => {
|
||||||
window.addEventListener('wheel', cancelEvent, { passive: false });
|
window.removeEventListener('mouseup', dragEnd);
|
||||||
document.addEventListener('gesturestart', cancelEvent)
|
window.removeEventListener('mousemove', dragMove);
|
||||||
document.addEventListener('gesturechange', cancelEvent)
|
};
|
||||||
}, {
|
canvasRef.value?.addEventListener('mouseenter', () => {
|
||||||
domTarget: canvasRef,
|
window.addEventListener('wheel', cancelEvent, { passive: false });
|
||||||
});
|
document.addEventListener('gesturestart', cancelEvent);
|
||||||
|
document.addEventListener('gesturechange', cancelEvent);
|
||||||
|
|
||||||
const dragHandler = useDrag(({ delta: [x, y] }: { delta: number[] }) => {
|
canvasRef.value?.addEventListener('mouseleave', () => {
|
||||||
if(editing.value === undefined)
|
window.removeEventListener('wheel', cancelEvent);
|
||||||
{
|
document.removeEventListener('gesturestart', cancelEvent);
|
||||||
dispX.value += x / zoom.value;
|
document.removeEventListener('gesturechange', cancelEvent);
|
||||||
dispY.value += y / zoom.value;
|
});
|
||||||
|
})
|
||||||
|
window.addEventListener('resize', () => box = canvasRef.value?.getBoundingClientRect()!);
|
||||||
|
canvasRef.value?.addEventListener('mousedown', (e) => {
|
||||||
|
if(e.button === 1)
|
||||||
|
{
|
||||||
|
lastX = e.clientX;
|
||||||
|
lastY = e.clientY;
|
||||||
|
|
||||||
|
window.addEventListener('mouseup', dragEnd, { passive: true });
|
||||||
|
window.addEventListener('mousemove', dragMove, { passive: true });
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
canvasRef.value?.addEventListener('wheel', (e) => {
|
||||||
|
if(!editing.value)
|
||||||
|
{
|
||||||
|
if((zoom.value >= 3 && e.deltaY < 0) || (zoom.value <= minZoom.value && e.deltaY > 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const diff = Math.exp(e.deltaY * -0.001);
|
||||||
|
const centerX = (box.x + box.width / 2), centerY = (box.y + box.height / 2);
|
||||||
|
const mousex = centerX - e.clientX, mousey = centerY - e.clientY;
|
||||||
|
|
||||||
|
dispX.value -= mousex / (diff * zoom.value) - mousex / zoom.value;
|
||||||
|
dispY.value -= mousey / (diff * zoom.value) - mousey / zoom.value;
|
||||||
|
|
||||||
|
zoom.value = clamp(zoom.value * diff, minZoom.value, 3);
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
canvasRef.value?.addEventListener('touchstart', (e) => {
|
||||||
|
({ x: lastX, y: lastY } = center(e.touches));
|
||||||
|
|
||||||
|
if(e.touches.length > 1)
|
||||||
|
{
|
||||||
|
lastDistance = distance(e.touches);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasRef.value?.addEventListener('touchend', touchend, { passive: true });
|
||||||
|
canvasRef.value?.addEventListener('touchcancel', touchcancel, { passive: true });
|
||||||
|
canvasRef.value?.addEventListener('touchmove', touchmove, { passive: true });
|
||||||
|
}, { passive: true });
|
||||||
|
const touchend = (e: TouchEvent) => {
|
||||||
|
if(e.touches.length > 1)
|
||||||
|
{
|
||||||
|
({ x: lastX, y: lastY } = center(e.touches));
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasRef.value?.removeEventListener('touchend', touchend);
|
||||||
|
canvasRef.value?.removeEventListener('touchcancel', touchcancel);
|
||||||
|
canvasRef.value?.removeEventListener('touchmove', touchmove);
|
||||||
|
};
|
||||||
|
const touchcancel = (e: TouchEvent) => {
|
||||||
|
if(e.touches.length > 1)
|
||||||
|
{
|
||||||
|
({ x: lastX, y: lastY } = center(e.touches));
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasRef.value?.removeEventListener('touchend', touchend);
|
||||||
|
canvasRef.value?.removeEventListener('touchcancel', touchcancel);
|
||||||
|
canvasRef.value?.removeEventListener('touchmove', touchmove);
|
||||||
|
};
|
||||||
|
const touchmove = (e: TouchEvent) => {
|
||||||
|
const pos = center(e.touches);
|
||||||
|
dispX.value -= (lastX - pos.x);
|
||||||
|
dispY.value -= (lastY - pos.y);
|
||||||
|
lastX = pos.x;
|
||||||
|
lastY = pos.y;
|
||||||
|
|
||||||
|
if(e.touches.length === 2)
|
||||||
|
{
|
||||||
|
const dist = distance(e.touches);
|
||||||
|
const diff = lastDistance / dist;
|
||||||
|
|
||||||
|
zoom.value = clamp(zoom.value * diff, minZoom.value, 3); //@TODO
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
function selectNode(node: CanvasNode, e: MouseEvent)
|
||||||
|
{
|
||||||
|
const target = e.currentTarget as HTMLElement;
|
||||||
|
let lastX = 0, lastY = 0;
|
||||||
|
|
||||||
|
const unselect = (_e: MouseEvent) => {
|
||||||
|
if(_e.button === 0 && _e.target !== target)
|
||||||
|
{
|
||||||
|
focusing.value = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dragStart = (_e: MouseEvent) => {
|
||||||
|
if(e.button !== 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastX = _e.clientX;
|
||||||
|
lastY= _e.clientY;
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', dragMove, { passive: true });
|
||||||
|
window.addEventListener('mouseup', dragEnd, { passive: true });
|
||||||
|
};
|
||||||
|
const dragMove = (_e: MouseEvent) => {
|
||||||
|
node.x -= (lastX - e.clientX)/ zoom.value;
|
||||||
|
node.y -= (lastY - e.clientY)/ zoom.value;
|
||||||
|
|
||||||
|
focusing.value = node;
|
||||||
|
|
||||||
|
lastX = _e.clientX;
|
||||||
|
lastY= _e.clientY;
|
||||||
}
|
}
|
||||||
}, {
|
const dragEnd = (_e: MouseEvent) => {
|
||||||
useTouch: true,
|
window.removeEventListener('mousemove', dragMove);
|
||||||
domTarget: canvasRef,
|
window.removeEventListener('mouseup', dragEnd);
|
||||||
});
|
|
||||||
const wheelHandler = useWheel(({ delta: [x, y] }: { delta: number[] }) => {
|
|
||||||
if(editing.value === undefined)
|
|
||||||
{
|
|
||||||
zoom.value = clamp(zoom.value + y * -0.001, minZoom.value, 3);
|
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
eventOptions: {
|
window.removeEventListener('mousemove', dragMove);
|
||||||
capture: true,
|
window.removeEventListener('mouseup', dragEnd);
|
||||||
},
|
target.removeEventListener('mousedown', dragStart);
|
||||||
domTarget: canvasRef,
|
|
||||||
});
|
focusing.value = node;
|
||||||
const pinchHandler = usePinch(({ offset: [z] }: { offset: number[] }) => {
|
|
||||||
if(editing.value === undefined)
|
//canvasRef.value?.addEventListener('mousedown', unselect, { once: true, passive: true });
|
||||||
{
|
target.addEventListener('mousedown', dragStart, { passive: true });
|
||||||
zoom.value = clamp(z / 2048, minZoom.value, 3);
|
}
|
||||||
}
|
function editNode(node: CanvasNode, e: MouseEvent)
|
||||||
}, {
|
{
|
||||||
domTarget: canvasRef,
|
editing.value = node;
|
||||||
});
|
}
|
||||||
|
function resizeNode(e: MouseEvent, x: number, y: number)
|
||||||
|
{
|
||||||
|
const target = e.currentTarget;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="canvasRef" class="absolute top-0 left-0 overflow-hidden w-full h-full touch-none" :style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }" @click.self="() => { focusing = undefined; editing = undefined; }">
|
<div ref="canvasRef" class="absolute top-0 left-0 overflow-hidden w-full h-full touch-none" :style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
|
||||||
<div class="flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4">
|
<div class="flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4">
|
||||||
<div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
|
<div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
|
||||||
<Tooltip message="Zoom avant" side="right">
|
<Tooltip message="Zoom avant" side="right">
|
||||||
|
|
@ -140,19 +268,29 @@ const pinchHandler = usePinch(({ offset: [z] }: { offset: number[] }) => {
|
||||||
<div :style="{
|
<div :style="{
|
||||||
'--tw-translate-x': `${dispX}px`,
|
'--tw-translate-x': `${dispX}px`,
|
||||||
'--tw-translate-y': `${dispY}px`,
|
'--tw-translate-y': `${dispY}px`,
|
||||||
'--tw-scale-x': `${zoom}`,
|
'--tw-scale': `${zoom}`,
|
||||||
'--tw-scale-y': `${zoom}`,
|
'transform': 'scale3d(var(--tw-scale), var(--tw-scale), 1) translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)',
|
||||||
transform: 'scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)'
|
'transform-origin': 'center center',
|
||||||
}">
|
}" class="h-full">
|
||||||
<div class="absolute top-0 left-0 w-full h-full origin-center pointer-events-none *:pointer-events-auto *:select-none touch-none">
|
<div class="absolute top-0 left-0 w-full h-full pointer-events-none *:pointer-events-auto *:select-none touch-none">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="node of nodes" :key="node.id" class="absolute" :style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-color': node.color?.hex}" :class="{'-z-10': node.type === 'group', 'z-10': node.type !== 'group'}">
|
<div v-for="node of nodes" :key="node.id" class="absolute" :style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-color': node.color?.hex}" :class="{'-z-10': node.type === 'group', 'z-10': node.type !== 'group'}">
|
||||||
<div v-if="editing?.id !== node.id" style="outline-style: solid;" :class="[node.class.border, node.class.outline, { '!outline-4': focusing?.id === node.id }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex" @click.left="() => { focusing = node; editing = undefined; }" @dblclick="() => { if(node.type === 'text') editing = node; }">
|
<div v-if="editing?.id !== node.id" style="outline-style: solid;" :class="[node.class.border, node.class.outline, { '!outline-4 cursor-move': focusing?.id === node.id }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full flex">
|
||||||
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07]" :class="node.class.bg">
|
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07]" :class="node.class.bg" @click.left="(e) => selectNode(node, e)" @dblclick.left="(e) => editNode(node, e)">
|
||||||
<div v-if="node.text?.length > 0" class="flex items-center">
|
<div v-if="node.text?.length > 0" class="flex items-center">
|
||||||
<MarkdownRenderer :content="node.text" :proses="{ a: FakeA }" />
|
<MarkdownRenderer :content="node.text" :proses="{ a: FakeA }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="focusing?.id === node.id">
|
||||||
|
<span @mousedown="(e) => resizeNode(e, 0, -1)" id=" n" class="cursor-n-resize absolute -top-2 left-0 right-0 h-4"></span> <!-- North -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, 0, 1)" id=" s" class="cursor-s-resize absolute -bottom-2 left-0 right-0 h-4"></span> <!-- South -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, -1, 0)" id=" e" class="cursor-e-resize absolute top-0 bottom-0 -left-2 w-4"></span> <!-- East -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, 1, 0)" id=" w" class="cursor-w-resize absolute top-0 bottom-0 -right-2 w-4"></span> <!-- West -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, 1, -1)" id="nw" class="cursor-nw-resize absolute -top-2 -left-2 w-4 h-4"></span> <!-- North West -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, -1, -1)" id="ne" class="cursor-ne-resize absolute -top-2 -right-2 w-4 h-4"></span> <!-- North East -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, -1, 1)" id="se" class="cursor-se-resize absolute -bottom-2 -right-2 w-4 h-4"></span> <!-- South East -->
|
||||||
|
<span @mousedown="(e) => resizeNode(e, 1, 1)" id="sw" class="cursor-sw-resize absolute -bottom-2 -left-2 w-4 h-4"></span> <!-- South West -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else style="outline-style: solid;" :class="[node.class.border, node.class.outline, { '!outline-4': focusing?.id === node.id }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex" >
|
<div v-else style="outline-style: solid;" :class="[node.class.border, node.class.outline, { '!outline-4': focusing?.id === node.id }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex" >
|
||||||
<Editor v-model="node.text" />
|
<Editor v-model="node.text" />
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ watchEffect(() => {
|
||||||
.cm-editor
|
.cm-editor
|
||||||
{
|
{
|
||||||
@apply bg-transparent;
|
@apply bg-transparent;
|
||||||
@apply flex-1
|
@apply flex-1;
|
||||||
}
|
}
|
||||||
.cm-editor .cm-content
|
.cm-editor .cm-content
|
||||||
{
|
{
|
||||||
|
|
|
||||||
BIN
db.sqlite-shm
BIN
db.sqlite-shm
Binary file not shown.
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
Loading…
Reference in New Issue