First reworks

This commit is contained in:
2024-10-28 17:49:46 +01:00
parent fef8c092a9
commit 1b2472bc1a
155 changed files with 536 additions and 4281 deletions

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
import type { CanvasColor } from "~/types/canvas";
type Direction = 'bottom' | 'top' | 'left' | 'right';
interface Props
{
path: {
path: string;
from: { x: number; y: number };
to: { x: number; y: number };
side: Direction;
};
color?: CanvasColor;
label?: string;
}
const props = defineProps<Props>();
const rotation: Record<Direction, string> = {
top: "180",
bottom: "0",
left: "90",
right: "270"
};
</script>
<template>
<g :style="{'--canvas-color': color?.hex}" class="z-0">
<path :style="`stroke-linecap: butt; stroke-width: calc(3px * var(--zoom-multiplier));`" :class="color?.class ? `stroke-light-${color.class} dark:stroke-dark-${color.class}` : ((color && color?.hex !== undefined) ? 'stroke-[color:var(--canvas-color)]' : 'stroke-light-40 dark:stroke-dark-40')" class="fill-none stroke-[4px]" :d="path.path"></path>
<g :style="`transform: translate(${path.to.x}px, ${path.to.y}px) scale(var(--zoom-multiplier)) rotate(${rotation[path.side]}deg);`">
<polygon :class="color?.class ? `fill-light-${color.class} dark:fill-dark-${color.class}` : ((color && color?.hex !== undefined) ? 'fill-[color:var(--canvas-color)]' : 'fill-light-40 dark:fill-dark-40')" points="0,0 6.5,10.4 -6.5,10.4"></polygon>
</g>
</g>
</template>

View File

@@ -1,59 +0,0 @@
<script setup lang="ts">
import type { CanvasNode } from '~/types/canvas';
interface Props {
node: CanvasNode;
zoom: number;
}
const props = defineProps<Props>();
const size = Math.max(props.node.width, props.node.height);
const colors = computed(() => {
if(props.node.color)
{
const color = props.node.color;
return color?.class ? { bg: `bg-light-${color?.class} dark:bg-dark-${color?.class}`, border: `border-light-${color?.class} dark:border-dark-${color?.class}`} : { bg: `bg-colored`, border: `border-[color:var(--canvas-color)]` };
}
else
{
return { border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40` };
}
})
</script>
<style>
.bg-colored
{
--tw-bg-opacity: 1;
background-color: rgba(from var(--canvas-color) r g b / var(--tw-bg-opacity));
}
</style>
<template>
<div 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 :class="[colors.border]" class="border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex">
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07]" :class="colors.bg">
<template v-if="node.type === 'group' || zoom > Math.min(0.4, 1000 / size)">
<div v-if="node.text?.length > 0" class="flex items-center">
<Markdown :content="node.text" />
</div>
</template>
<template v-else>
<div class="flex flex-1 justify-center items-center bg-light-30 dark:bg-dark-30">
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="svg-icon lucide-align-left">
<line x1="21" y1="6" x2="3" y2="6"></line>
<line x1="15" y1="12" x2="3" y2="12"></line>
<line x1="17" y1="18" x2="3" y2="18"></line>
</svg>
</div>
</div>
</template>
</div>
</div>
<div v-if="node.type === 'group' && node.label !== undefined" :class="[colors.border]" style="max-width: 100%; font-size: calc(18px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider border-4 truncate inline-block text-light-100 dark:text-dark-100 absolute bottom-[100%] mb-2 px-2 py-1 font-thin">{{ node.label }}</div>
</div>
</template>

View File

@@ -1,244 +0,0 @@
<script setup lang="ts">
import { useDrag, usePinch, useWheel } from '@vueuse/gesture';
import type { CanvasContent, CanvasNode } from '~/types/canvas';
import { clamp } from '#imports';
interface Props
{
canvas: CanvasContent;
}
const props = defineProps<Props>();
const dispX = ref(0), dispY = ref(0), minZoom = ref(0.1), zoom = ref(0.5);
const canvas = useTemplateRef('canvas');
const reset = (_: MouseEvent) => {
zoom.value = minZoom.value;
dispX.value = 0;
dispY.value = 0;
}
function edgePos(side: 'bottom' | 'top' | 'left' | 'right', pos: { x: number, y: number }, offset: number): { x: number, y: number } {
switch (side) {
case "left":
return {
x: pos.x - offset,
y: pos.y
};
case "right":
return {
x: pos.x + offset,
y: pos.y
};
case "top":
return {
x: pos.x,
y: pos.y - offset
};
case "bottom":
return {
x: pos.x,
y: pos.y + offset
}
}
}
function getNode(id: string): CanvasNode | undefined
{
return props.canvas.nodes.find(e => e.id === id);
}
function posFromDir(e: { minX: number, minY: number, maxX: number, maxY: number }, t: 'bottom' | 'top' | 'left' | 'right'): { x: number, y: number } {
switch (t) {
case "top":
return { x: (e.minX + e.maxX) / 2, y: e.minY };
case "right":
return { x: e.maxX, y: (e.minY + e.maxY) / 2 };
case "bottom":
return { x: (e.minX + e.maxX) / 2, y: e.maxY };
case "left":
return { x: e.minX, y: (e.minY + e.maxY) / 2 };
}
}
function getBbox(node: CanvasNode): { minX: number, minY: number, maxX: number, maxY: number } {
return { minX: node.x, minY: node.y, maxX: node.x + node.width, maxY: node.y + node.height };
}
function path(from: CanvasNode, fromSide: 'bottom' | 'top' | 'left' | 'right', to: CanvasNode, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
if(from === undefined || to === undefined)
{
return {
path: '',
from: {},
to: {},
toSide: '',
}
}
const start = posFromDir(getBbox(from), fromSide), end = posFromDir(getBbox(to), toSide);
return bezier(start, fromSide, end, toSide);
}
function bezier(from: { x: number, y: number }, fromSide: 'bottom' | 'top' | 'left' | 'right', to: { x: number, y: number }, toSide: 'bottom' | 'top' | 'left' | 'right'): any {
const r = Math.hypot(from.x - to.x, from.y - to.y), o = clamp(r / 2, 70, 150), a = edgePos(fromSide, from, o), s = edgePos(toSide, to, o);
return {
path: `M${from.x},${from.y} C${a.x},${a.y} ${s.x},${s.y} ${to.x},${to.y}`,
from: from,
to: to,
side: toSide,
};
}
function labelCenter(from: CanvasNode, fromSide: 'bottom' | 'top' | 'left' | 'right', to: CanvasNode, toSide: 'bottom' | 'top' | 'left' | 'right'): string {
const start = posFromDir(getBbox(from), fromSide), end = posFromDir(getBbox(to), toSide);
const len = Math.hypot(start.x - end.x, start.y - end.y), offset = clamp(len / 2, 70, 150), b = edgePos(fromSide, start, offset), s = edgePos(toSide, end, offset);
const center = getCenter(start, end, b, s, 0.5);
return `translate(${center.x}px, ${center.y}px)`;
}
function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r: { x: number, y: number }, o: { x: number, y: number }, e: number): { x: number, y: number } {
const a = 1 - e, s = a * a * a, l = 3 * e * a * a, c = 3 * e * e * a, u = e * e * e;
return {
x: s * n.x + l * r.x + c * o.x + u * i.x,
y: s * n.y + l * r.y + c * o.y + u * i.y
};
}
/*
stroke-light-red
stroke-light-orange
stroke-light-yellow
stroke-light-green
stroke-light-cyan
stroke-light-purple
dark:stroke-dark-red
dark:stroke-dark-orange
dark:stroke-dark-yellow
dark:stroke-dark-green
dark:stroke-dark-cyan
dark:stroke-dark-purple
fill-light-red
fill-light-orange
fill-light-yellow
fill-light-green
fill-light-cyan
fill-light-purple
dark:fill-dark-red
dark:fill-dark-orange
dark:fill-dark-yellow
dark:fill-dark-green
dark:fill-dark-cyan
dark:fill-dark-purple
bg-light-red
bg-light-orange
bg-light-yellow
bg-light-green
bg-light-cyan
bg-light-purple
dark:bg-dark-red
dark:bg-dark-orange
dark:bg-dark-yellow
dark:bg-dark-green
dark:bg-dark-cyan
dark:bg-dark-purple
border-light-red
border-light-orange
border-light-yellow
border-light-green
border-light-cyan
border-light-purple
dark:border-dark-red
dark:border-dark-orange
dark:border-dark-yellow
dark:border-dark-green
dark:border-dark-cyan
dark:border-dark-purple
*/
const pinchHandler = usePinch(({ event: Event, offset: [z] }: { event: Event, offset: number[] }) => {
event?.preventDefault();
console.log(z);
zoom.value = clamp(z / 2048, minZoom.value, 3);
}, {
domTarget: canvas,
eventOptions: { passive: false, }
})
const wheelHandler = useWheel(({ event: Event, delta: [x, y] }: { event: Event, delta: number[] }) => {
event?.preventDefault();
zoom.value = clamp(zoom.value + y * -0.001, minZoom.value, 3);
}, {
domTarget: canvas,
eventOptions: { passive: false, }
})
const dragHandler = useDrag(({ event: Event, delta: [x, y] }: { event: Event, delta: number[] }) => {
event?.preventDefault();
dispX.value += x / zoom.value;
dispY.value += y / zoom.value;
}, {
domTarget: canvas,
eventOptions: { passive: false, }
})
</script>
<template>
<Suspense>
<template #default>
<div id="canvas" ref="canvas" 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="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 absolute sm:top-2 top-10 left-2 z-30 overflow-hidden">
<div @click="zoom = clamp(zoom * 1.1, minZoom, 3)" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer"
aria-label="Zoom in" data-tooltip-position="left">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M5 12h14"></path>
<path d="M12 5v14"></path>
</svg>
</div>
<div @click="zoom = 1" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer" aria-label="Reset zoom"
data-tooltip-position="left">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
<path d="M21 3v5h-5"></path>
</svg>
</div>
<div @click="reset" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer" aria-label="Zoom to fit"
data-tooltip-position="left">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M8 3H5a2 2 0 0 0-2 2v3"></path>
<path d="M21 8V5a2 2 0 0 0-2-2h-3"></path>
<path d="M3 16v3a2 2 0 0 0 2 2h3"></path>
<path d="M16 21h3a2 2 0 0 0 2-2v-3"></path>
</svg>
</div>
<div @click="zoom = clamp(zoom * 0.9, minZoom, 3)" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer"
aria-label="Zoom out" data-tooltip-position="left">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M5 12h14"></path>
</svg>
</div>
</div>
<div class="absolute top-0 left-0 w-full h-full origin-center pointer-events-none *:pointer-events-auto *:select-none"
:style="{transform: `scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
<div>
<CanvasNode v-for="node of props.canvas.nodes" :key="node.id" :node="node" :zoom="zoom" />
</div>
<template v-for="edge of props.canvas.edges">
<div :key="edge.id" v-if="edge.label" class="absolute z-10"
:style="{ transform: labelCenter(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide) }">
<div class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 -translate-x-[50%] -translate-y-[50%]">{{ edge.label }}</div>
</div>
</template>
<svg class="absolute top-0 left-0 overflow-visible w-full h-full origin-top pointer-events-none">
<CanvasEdge v-for="edge of props.canvas.edges" :key="edge.id"
:path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)"
:color="edge.color" :label="edge.label" />
</svg>
</div>
</div>
</template>
<template #fallback>
<div class="loading"></div>
</template>
</Suspense>
</template>