105 lines
5.4 KiB
Vue
105 lines
5.4 KiB
Vue
<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 flex">
|
|
<div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07]" :class="style.bg" @click.left="(e) => selectNode(node, e)" @dblclick.left="(e) => editNode(node, e)">
|
|
<div v-if="node.text?.length > 0" class="flex items-center">
|
|
<MarkdownRenderer :content="node.text" :proses="{ a: FakeA }" />
|
|
</div>
|
|
</div>
|
|
<div v-if="focusing">
|
|
<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 v-else style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4': focusing }]" 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" />
|
|
</div>
|
|
<div v-if="!editing && node.type === 'group' && node.label !== undefined" @click.left="(e) => selectNode(node, e)" @dblclick.left="(e) => editNode(node, e)" :class="style.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>
|
|
<input v-else-if="editing && node.type === 'group'" v-model="node.label" autofocus :class="[style.border, style.outline]" 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%] appearance-none bg-transparent outline-4 mb-2 px-2 py-1 font-thin min-w-4" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import FakeA from '../prose/FakeA.vue';
|
|
import type { CanvasNode } from '~/types/canvas';
|
|
|
|
const { node, index } = defineProps<{
|
|
node: CanvasNode
|
|
index: number
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'select', node: CanvasNode, index: number, event: Event): void,
|
|
(e: 'edit', node: CanvasNode, index: number, event: Event): void,
|
|
(e: 'resize', node: CanvasNode, index: number, event: Event): void,
|
|
(e: 'move', index: number, x: number, y: number): void,
|
|
(e: 'resize', index: number, x: number, y: number): void,
|
|
}>();
|
|
|
|
const dom = useTemplateRef('dom');
|
|
const focusing = ref(false), editing = ref(false);
|
|
|
|
function selectNode(node: CanvasNode, _e: Event) {
|
|
if(editing.value)
|
|
return;
|
|
|
|
focusing.value = true;
|
|
emit('select', node, index, _e);
|
|
|
|
dom.value?.addEventListener('mousedown', dragstart, { passive: true });
|
|
}
|
|
function editNode(node: CanvasNode, e: Event) {
|
|
focusing.value = true;
|
|
editing.value = true;
|
|
|
|
dom.value?.removeEventListener('mousedown', dragstart);
|
|
emit('edit', node, index, e);
|
|
}
|
|
function resizeNode(e: Event, x: number, y: number) {
|
|
e.stopImmediatePropagation();
|
|
emit('resize', node, index, e);
|
|
}
|
|
function unselect() {
|
|
focusing.value = false;
|
|
editing.value = false;
|
|
|
|
dom.value?.removeEventListener('mousedown', dragstart);
|
|
}
|
|
|
|
let lastx = 0, lasty = 0;
|
|
const dragmove = (e: MouseEvent) => {
|
|
if(e.button !== 0)
|
|
return;
|
|
|
|
emit('move', index, lastx - e.clientX, lasty - e.clientY);
|
|
|
|
lastx = e.clientX, lasty = e.clientY;
|
|
};
|
|
const dragend = (e: MouseEvent) => {
|
|
if(e.button !== 0)
|
|
return;
|
|
|
|
window.removeEventListener('mousemove', dragmove);
|
|
window.removeEventListener('mouseup', dragend);
|
|
};
|
|
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 });
|
|
};
|
|
|
|
defineExpose({ unselect, dom });
|
|
|
|
const style = computed(() => {
|
|
return node.color ? node.color?.class ?
|
|
{ bg: `bg-light-${node.color?.class} dark:bg-dark-${node.color?.class}`, border: `border-light-${node.color?.class} dark:border-dark-${node.color?.class}`, outline: `outline-light-${node.color?.class} dark:outline-dark-${node.color?.class}` } :
|
|
{ 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` }
|
|
});
|
|
</script> |