Remove Tweening (looked laggy). Add edge property editing. Improve Edge selection and visualisation.

This commit is contained in:
2025-01-16 17:39:33 +01:00
parent 348c991c54
commit f32c51ca38
6 changed files with 77 additions and 110 deletions

View File

@@ -1,13 +1,14 @@
<template>
<div class="absolute overflow-visible h-px w-px">
<div v-if="edge.label" :style="{ transform: labelPos }" 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>
<input v-else-if="editing" @click="e => e.stopImmediatePropagation()" :style="{ transform: `${labelPos} translate(-50%, -50%)` }" class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 z-20" :v-model="edge.label" />
<svg ref="dom" class="absolute top-0 overflow-visible h-px w-px" @click="select" @dblclick="edit">
<div class="absolute overflow-visible">
<input v-if="editing" @click="e => e.stopImmediatePropagation()" :style="{ transform: `${labelPos} translate(-50%, -50%)` }" class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 z-20" v-model="edge.label" />
<div v-else-if="edge.label" :style="{ transform: `${labelPos} translate(-50%, -50%)` }" class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 z-20" @click.left="select" @dblclick.left="edit">{{ edge.label }}</div>
<svg ref="dom" class="absolute top-0 overflow-visible h-px w-px">
<g :style="{'--canvas-color': edge.color?.hex}" class="z-0">
<path :style="`stroke-linecap: butt; stroke-width: calc(3px * var(--zoom-multiplier));`" :class="[edge.color?.class ? `stroke-light-${edge.color.class} dark:stroke-dark-${edge.color.class}` : ((edge.color && edge.color?.hex !== undefined) ? 'stroke-[color:var(--canvas-color)]' : 'stroke-light-40 dark:stroke-dark-40'), { 'outline-4': focusing }]" 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="edge.color?.class ? `fill-light-${edge.color.class} dark:fill-dark-${edge.color.class}` : ((edge.color && edge.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>
<polygon :class="style.fill" points="0,0 6.5,10.4 -6.5,10.4"></polygon>
</g>
<path :style="`stroke-width: calc(${focusing ? 6 : 3}px * var(--zoom-multiplier));`" style="stroke-linecap: butt;" :class="style.stroke" class="fill-none stroke-[4px]" :d="path!.path"></path>
<path style="stroke-width: calc(22px * var(--zoom-multiplier));" class="fill-none transition-opacity z-30 opacity-0 hover:opacity-25" :class="[style.stroke, { 'opacity-25': focusing }]" :d="path!.path" @click="select" @dblclick="edit"></path>
</g>
</svg>
</div>
@@ -34,24 +35,33 @@ const emit = defineEmits<{
(e: 'select', id: Element): void,
(e: 'edit', id: Element): void,
(e: 'move', id: string, from: string, to: string): void,
(e: 'input', id: string, text?: string): void,
}>();
const dom = useTemplateRef('dom');
const focusing = ref(false), editing = ref(false);
const from = computed(() => nodes!.find(f => f.id === edge.fromNode))
const from = computed(() => nodes!.find(f => f.id === edge.fromNode));
const to = computed(() => nodes!.find(f => f.id === edge.toNode));
const path = computed(() => getPath(from.value!, edge.fromSide, to.value!, edge.toSide));
const labelPos = computed(() => labelCenter(from.value!, edge.fromSide, to.value!, edge.toSide));
let oldText = edge.label;
function select(e: Event) {
if(editing.value)
return;
console.log("Selecting %s (edge)", edge.id);
focusing.value = true;
emit('select', { type: 'edge', id: edge.id });
}
function edit(e: Event) {
oldText = edge.label;
console.log("Editing %s (edge)", edge.id);
focusing.value = true;
editing.value = true;
@@ -59,9 +69,27 @@ function edit(e: Event) {
emit('edit', { type: 'edge', id: edge.id });
}
function unselect() {
if(editing.value)
{
const text = edge.label;
if(text !== oldText)
{
edge.label = oldText;
emit('input', edge.id, text);
}
}
focusing.value = false;
editing.value = false;
}
defineExpose({ unselect, dom, id: edge.id });
const style = computed(() => {
return edge.color ? edge.color?.class ?
{ fill: `fill-light-${edge.color?.class} dark:fill-dark-${edge.color?.class}`, stroke: `stroke-light-${edge.color?.class} dark:stroke-dark-${edge.color?.class}`, outline: `outline-light-${edge.color?.class} dark:outline-dark-${edge.color?.class}` } :
{ fill: `fill-colored`, stroke: `stroke-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } :
{ stroke: `stroke-light-40 dark:stroke-dark-40`, fill: `fill-light-40 dark:fill-dark-40`, outline: `outline-light-40 dark:outline-dark-40` }
});
</script>

View File

@@ -1,7 +1,7 @@
<template>
<div class="absolute" ref="dom" :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 || node.type === 'group'" style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4 cursor-move': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full">
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07]" :class="style.bg" @click.left="(e) => { if(node.type !== 'group') selectNode(e) }" @dblclick.left="(e) => { if(node.type !== 'group') editNode(e) }">
<div v-if="!editing || node.type === 'group'" style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4 cursor-move': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full hover:outline-4">
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07] overflow-auto" :class="style.bg" @click.left="(e) => { if(node.type !== 'group') selectNode(e) }" @dblclick.left="(e) => { if(node.type !== 'group') editNode(e) }">
<div v-if="node.text?.length > 0" class="flex items-center">
<MarkdownRenderer :content="node.text" :proses="{ a: FakeA }" />
</div>
@@ -108,12 +108,14 @@ function unselect() {
if(editing.value)
{
const text = node.type === 'group' ? node.label : node.text;
if(text !== oldText)
{
if(node.type === 'group')
node.label = oldText;
else
node.text = oldText;
emit('input', node.id, text);
}
}