Rework the structure to handle suppression (using ID instead of index). Add create history and removing.
This commit is contained in:
parent
9439dd2d95
commit
4433cf0e00
|
|
@ -1,5 +1,6 @@
|
|||
<script lang="ts">
|
||||
import type { Box, Position } from '#shared/canvas.util';
|
||||
import type CanvasNodeEditor from './canvas/CanvasNodeEditor.vue';
|
||||
type Direction = 'bottom' | 'top' | 'left' | 'right';
|
||||
const rotation: Record<Direction, string> = {
|
||||
top: "180",
|
||||
|
|
@ -24,7 +25,7 @@ interface HistoryEvent<T extends Action = Action>
|
|||
}
|
||||
interface HistoryAction<T extends Action>
|
||||
{
|
||||
element: number;
|
||||
element: string;
|
||||
from: ActionMap[T];
|
||||
to: ActionMap[T];
|
||||
}
|
||||
|
|
@ -71,9 +72,11 @@ import { labelCenter, getPath } from '#shared/canvas.util';
|
|||
const canvas = defineModel<CanvasContent>({ required: true, });
|
||||
|
||||
const dispX = ref(0), dispY = ref(0), minZoom = ref(0.1), zoom = ref(0.5);
|
||||
const focusing = ref<number>(), editing = ref<number>();
|
||||
const focusing = ref<string>(), editing = ref<string>();
|
||||
const canvasRef = useTemplateRef('canvasRef');
|
||||
const nodes = useTemplateRef('nodes');
|
||||
const nodes = useTemplateRef<InstanceType<typeof CanvasNodeEditor>[]>('nodes');
|
||||
|
||||
const focusedNode = computed(() => nodes.value?.find(e => !!e && e.id === focusing.value)), editedNode = computed(() => nodes.value?.find(e => !!e && e.id === editing.value));
|
||||
|
||||
const edges = computed(() => {
|
||||
return canvas.value.edges.map(e => {
|
||||
|
|
@ -83,8 +86,6 @@ const edges = computed(() => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
const history = ref<HistoryEvent[]>([]);
|
||||
const historyPos = ref(-1);
|
||||
const historyCursor = computed(() => history.value.length > 0 && historyPos.value > -1 ? history.value[historyPos.value] : undefined);
|
||||
|
|
@ -199,79 +200,82 @@ onMounted(() => {
|
|||
};
|
||||
});
|
||||
|
||||
function moveNode(index: number[], deltax: number, deltay: number)
|
||||
function moveNode(ids: string[], deltax: number, deltay: number)
|
||||
{
|
||||
const actions: HistoryAction<'move'>[] = [];
|
||||
for(const i of index)
|
||||
for(const id of ids)
|
||||
{
|
||||
const node = canvas.value.nodes[i];
|
||||
const node = canvas.value.nodes.find(e => e.id === id)!;
|
||||
|
||||
actions.push({ element: i, from: { x: node.x - deltax, y: node.y - deltay }, to: { x: node.x, y: node.y } });
|
||||
actions.push({ element: id, from: { x: node.x - deltax, y: node.y - deltay }, to: { x: node.x, y: node.y } });
|
||||
}
|
||||
|
||||
addAction('move', actions);
|
||||
}
|
||||
function resizeNode(index: number[], deltax: number, deltay: number, deltaw: number, deltah: number)
|
||||
function resizeNode(ids: string[], deltax: number, deltay: number, deltaw: number, deltah: number)
|
||||
{
|
||||
const actions: HistoryAction<'resize'>[] = [];
|
||||
for(const i of index)
|
||||
for(const id of ids)
|
||||
{
|
||||
const node = canvas.value.nodes[i];
|
||||
const node = canvas.value.nodes.find(e => e.id === id)!;
|
||||
|
||||
actions.push({ element: i, from: { x: node.x - deltax, y: node.y - deltay, w: node.width - deltaw, h: node.height - deltah }, to: { x: node.x, y: node.y, w: node.width, h: node.height } });
|
||||
actions.push({ element: id, from: { x: node.x - deltax, y: node.y - deltay, w: node.width - deltaw, h: node.height - deltah }, to: { x: node.x, y: node.y, w: node.width, h: node.height } });
|
||||
}
|
||||
|
||||
addAction('resize', actions);
|
||||
}
|
||||
function selectNode(index: number)
|
||||
function selectNode(id: string)
|
||||
{
|
||||
if(focusing.value !== index)
|
||||
if(focusing.value !== id)
|
||||
{
|
||||
unselectNode();
|
||||
}
|
||||
|
||||
nodes.value![index]?.dom?.addEventListener('click', stopPropagation, { passive: true });
|
||||
canvasRef.value?.addEventListener('click', unselectNode, { once: true });
|
||||
focusing.value = id;
|
||||
|
||||
focusing.value = index;
|
||||
focusedNode.value?.dom?.addEventListener('click', stopPropagation, { passive: true });
|
||||
canvasRef.value?.addEventListener('click', unselectNode, { once: true });
|
||||
}
|
||||
function editNode(index: number)
|
||||
function editNode(id: string)
|
||||
{
|
||||
nodes.value![index]?.dom?.addEventListener('wheel', stopPropagation, { passive: true });
|
||||
nodes.value![index]?.dom?.addEventListener('dblclick', stopPropagation, { passive: true });
|
||||
canvasRef.value?.addEventListener('click', unselectNode, { once: true });
|
||||
editing.value = id;
|
||||
|
||||
editing.value = index;
|
||||
focusedNode.value?.dom?.addEventListener('wheel', stopPropagation, { passive: true });
|
||||
focusedNode.value?.dom?.addEventListener('dblclick', stopPropagation, { passive: true });
|
||||
canvasRef.value?.addEventListener('click', unselectNode, { once: true });
|
||||
}
|
||||
function addNode(e: MouseEvent)
|
||||
function createNode(e: MouseEvent)
|
||||
{
|
||||
let box = canvasRef.value?.getBoundingClientRect()!;
|
||||
canvas.value.nodes.push({ id: getID(16), x: (e.layerX / zoom.value) - box.width / 2 - 50, y: (e.layerY / zoom.value) - box.height / 2 - 25, width: 100, height: 50, type: 'text' });
|
||||
const node: CanvasNode = { id: getID(16), x: (e.layerX / zoom.value) - box.width / 2 - 50, y: (e.layerY / zoom.value) - box.height / 2 - 25, width: 100, height: 50, type: 'text' };
|
||||
canvas.value.nodes.push(node);
|
||||
|
||||
addAction('create', [{ element: node.id, from: undefined, to: node }]);
|
||||
}
|
||||
function removeNode(index: number[])
|
||||
function removeNode(ids: string[])
|
||||
{
|
||||
/*const actions: HistoryAction<'remove'>[] = [];
|
||||
const actions: HistoryAction<'remove'>[] = [];
|
||||
unselectNode();
|
||||
|
||||
for(const i of index)
|
||||
for(const id of ids)
|
||||
{
|
||||
const [node] = canvas.value.nodes.splice(i, 1);
|
||||
actions.push({ element: i, from: node, to: undefined });
|
||||
const index = canvas.value.nodes.findIndex(e => e.id === id);
|
||||
actions.push({ element: id, from: canvas.value.nodes.splice(index, 1)[0], to: undefined });
|
||||
|
||||
console.log("Removing %s", i);
|
||||
console.log("Removing %s", id);
|
||||
}
|
||||
|
||||
addAction('remove', actions);*/
|
||||
addAction('remove', actions);
|
||||
}
|
||||
function editNodeProperty<T extends keyof CanvasNode>(index: number[], property: T, value: CanvasNode[T])
|
||||
function editNodeProperty<T extends keyof CanvasNode>(ids: string[], property: T, value: CanvasNode[T])
|
||||
{
|
||||
const actions: HistoryAction<'remove'>[] = [];
|
||||
|
||||
for(const i of index)
|
||||
for(const id of ids)
|
||||
{
|
||||
const copy = JSON.parse(JSON.stringify(canvas.value.nodes[i])) as CanvasNode;
|
||||
canvas.value.nodes[i][property] = value;
|
||||
actions.push({ element: i, from: copy, to: canvas.value.nodes[i] });
|
||||
const copy = JSON.parse(JSON.stringify(canvas.value.nodes.find(e => e.id === id)!)) as CanvasNode;
|
||||
canvas.value.nodes.find(e => e.id === id)![property] = value;
|
||||
actions.push({ element: id, from: copy, to: canvas.value.nodes.find(e => e.id === id)! });
|
||||
}
|
||||
|
||||
addAction('property', actions);
|
||||
|
|
@ -280,17 +284,17 @@ function editNodeProperty<T extends keyof CanvasNode>(index: number[], property:
|
|||
const unselectNode = () => {
|
||||
if(focusing.value !== undefined)
|
||||
{
|
||||
nodes.value![focusing.value]?.dom?.removeEventListener('click', stopPropagation);
|
||||
nodes.value![focusing.value]?.unselect();
|
||||
focusedNode.value?.dom?.removeEventListener('click', stopPropagation);
|
||||
focusedNode.value?.unselect();
|
||||
}
|
||||
focusing.value = undefined;
|
||||
|
||||
if(editing.value !== undefined)
|
||||
{
|
||||
nodes.value![editing.value]?.dom?.removeEventListener('wheel', stopPropagation);
|
||||
nodes.value![editing.value]?.dom?.removeEventListener('dblclick', stopPropagation);
|
||||
nodes.value![editing.value]?.dom?.removeEventListener('click', stopPropagation);
|
||||
nodes.value![editing.value]?.unselect();
|
||||
editedNode.value?.dom?.removeEventListener('wheel', stopPropagation);
|
||||
editedNode.value?.dom?.removeEventListener('dblclick', stopPropagation);
|
||||
editedNode.value?.dom?.removeEventListener('click', stopPropagation);
|
||||
editedNode.value?.unselect();
|
||||
}
|
||||
editing.value = undefined;
|
||||
};
|
||||
|
|
@ -300,40 +304,49 @@ const undo = () => {
|
|||
|
||||
for(const action of historyCursor.value.actions)
|
||||
{
|
||||
const node = canvas.value.nodes.find(e => e.id === action.element)!;
|
||||
switch(historyCursor.value.event)
|
||||
{
|
||||
case 'move':
|
||||
{
|
||||
const a = action as HistoryAction<'move'>;
|
||||
canvas.value.nodes[action.element].x = a.from.x;
|
||||
canvas.value.nodes[action.element].y = a.from.y;
|
||||
node.x = a.from.x;
|
||||
node.y = a.from.y;
|
||||
break;
|
||||
}
|
||||
case 'resize':
|
||||
{
|
||||
const a = action as HistoryAction<'resize'>;
|
||||
canvas.value.nodes[action.element].x = a.from.x;
|
||||
canvas.value.nodes[action.element].y = a.from.y;
|
||||
canvas.value.nodes[action.element].width = a.from.w;
|
||||
canvas.value.nodes[action.element].height = a.from.h;
|
||||
node.x = a.from.x;
|
||||
node.y = a.from.y;
|
||||
node.width = a.from.w;
|
||||
node.height = a.from.h;
|
||||
break;
|
||||
}
|
||||
case 'edit':
|
||||
{
|
||||
const a = action as HistoryAction<'edit'>;
|
||||
canvas.value.nodes[action.element].label = a.from;
|
||||
node.label = a.from;
|
||||
break;
|
||||
}
|
||||
case 'create':
|
||||
{
|
||||
const a = action as HistoryAction<'create'>;
|
||||
const index = canvas.value.nodes.findIndex(e => e.id === action.element);
|
||||
canvas.value.nodes.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
case 'remove':
|
||||
{
|
||||
const a = action as HistoryAction<'remove'>;
|
||||
canvas.value.nodes.splice(action.element, 0, a.from!);
|
||||
canvas.value.nodes.push(a.from!);
|
||||
break;
|
||||
}
|
||||
case 'property':
|
||||
{
|
||||
const a = action as HistoryAction<'property'>;
|
||||
canvas.value.nodes[action.element] = a.from;
|
||||
const index = canvas.value.nodes.findIndex(e => e.id === action.element);
|
||||
canvas.value.nodes[index] = a.from;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -357,40 +370,49 @@ const redo = () => {
|
|||
|
||||
for(const action of historyCursor.value.actions)
|
||||
{
|
||||
const node = canvas.value.nodes.find(e => e.id === action.element)!;
|
||||
switch(historyCursor.value.event)
|
||||
{
|
||||
case 'move':
|
||||
{
|
||||
const a = action as HistoryAction<'move'>;
|
||||
canvas.value.nodes[action.element].x = a.to.x;
|
||||
canvas.value.nodes[action.element].y = a.to.y;
|
||||
node.x = a.to.x;
|
||||
node.y = a.to.y;
|
||||
break;
|
||||
}
|
||||
case 'resize':
|
||||
{
|
||||
const a = action as HistoryAction<'resize'>;
|
||||
canvas.value.nodes[action.element].x = a.to.x;
|
||||
canvas.value.nodes[action.element].y = a.to.y;
|
||||
canvas.value.nodes[action.element].width = a.to.w;
|
||||
canvas.value.nodes[action.element].height = a.to.h;
|
||||
node.x = a.to.x;
|
||||
node.y = a.to.y;
|
||||
node.width = a.to.w;
|
||||
node.height = a.to.h;
|
||||
break;
|
||||
}
|
||||
case 'edit':
|
||||
{
|
||||
const a = action as HistoryAction<'edit'>;
|
||||
canvas.value.nodes[action.element].label = a.to;
|
||||
node.label = a.to;
|
||||
break;
|
||||
}
|
||||
case 'create':
|
||||
{
|
||||
const a = action as HistoryAction<'remove'>;
|
||||
canvas.value.nodes.push(a.to!);
|
||||
break;
|
||||
}
|
||||
case 'remove':
|
||||
{
|
||||
const a = action as HistoryAction<'remove'>;
|
||||
canvas.value.nodes.splice(action.element, 1);
|
||||
const index = canvas.value.nodes.findIndex(e => e.id === action.element);
|
||||
canvas.value.nodes.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
case 'property':
|
||||
{
|
||||
const a = action as HistoryAction<'property'>;
|
||||
canvas.value.nodes[action.element] = a.to;
|
||||
const index = canvas.value.nodes.findIndex(e => e.id === action.element);
|
||||
canvas.value.nodes[index] = a.to;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -407,7 +429,7 @@ useShortcuts({
|
|||
</script>
|
||||
|
||||
<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)) }" @dblclick.left="addNode">
|
||||
<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)) }" @dblclick.left="createNode">
|
||||
<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">
|
||||
<Tooltip message="Zoom avant" side="right">
|
||||
|
|
@ -483,7 +505,7 @@ useShortcuts({
|
|||
'transform-origin': 'center center',
|
||||
}" class="h-full">
|
||||
<div class="absolute top-0 left-0 w-full h-full pointer-events-none *:pointer-events-auto *:select-none touch-none">
|
||||
<div v-if="focusing !== undefined" class="absolute z-20 origin-bottom" :style="{transform: `translate(${canvas.nodes[focusing].x}px, ${canvas.nodes[focusing].y}px) translateY(-100%) translateY(-12px) translateX(-50%) translateX(${canvas.nodes[focusing].width / 2}px) scale(calc(1 / var(--tw-scale)))`}">
|
||||
<div v-if="focusing !== undefined && focusedNode !== undefined" class="absolute z-20 origin-bottom" :style="{transform: `translate(${focusedNode.x}px, ${focusedNode.y}px) translateY(-100%) translateY(-12px) translateX(-50%) translateX(${focusedNode.width / 2}px) scale(calc(1 / var(--tw-scale)))`}">
|
||||
<div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 flex flex-row">
|
||||
<PopoverRoot>
|
||||
<PopoverTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -38,17 +38,16 @@ import type { Direction } from '~/shared/canvas.util';
|
|||
import FakeA from '../prose/FakeA.vue';
|
||||
import type { CanvasNode } from '~/types/canvas';
|
||||
|
||||
const { node, index, zoom } = defineProps<{
|
||||
const { node, zoom } = defineProps<{
|
||||
node: CanvasNode
|
||||
index: number
|
||||
zoom: number
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select', index: number): void,
|
||||
(e: 'edit', index: number): void,
|
||||
(e: 'move', index: number, x: number, y: number): void,
|
||||
(e: 'resize', index: number, x: number, y: number, w: number, h: number): void,
|
||||
(e: 'select', id: string): void,
|
||||
(e: 'edit', id: string): void,
|
||||
(e: 'move', id: string, x: number, y: number): void,
|
||||
(e: 'resize', id: string, x: number, y: number, w: number, h: number): void,
|
||||
}>();
|
||||
|
||||
const dom = useTemplateRef('dom');
|
||||
|
|
@ -59,7 +58,7 @@ function selectNode(e: Event) {
|
|||
return;
|
||||
|
||||
focusing.value = true;
|
||||
emit('select', index);
|
||||
emit('select', node.id);
|
||||
|
||||
dom.value?.addEventListener('mousedown', dragstart, { passive: true });
|
||||
}
|
||||
|
|
@ -70,7 +69,7 @@ function editNode(e: Event) {
|
|||
e.stopImmediatePropagation();
|
||||
|
||||
dom.value?.removeEventListener('mousedown', dragstart);
|
||||
emit('edit', index);
|
||||
emit('edit', node.id);
|
||||
}
|
||||
function resizeNode(e: MouseEvent, x: number, y: number, w: number, h: number) {
|
||||
e.stopImmediatePropagation();
|
||||
|
|
@ -88,7 +87,7 @@ function resizeNode(e: MouseEvent, x: number, y: number, w: number, h: number) {
|
|||
const resizeend = (e: MouseEvent) => {
|
||||
if(e.button !== 0)
|
||||
return;
|
||||
emit('resize', index, node.x - startx, node.y - starty, node.width - startw, node.height - starth);
|
||||
emit('resize', node.id, node.x - startx, node.y - starty, node.width - startw, node.height - starth);
|
||||
|
||||
window.removeEventListener('mousemove', resizemove);
|
||||
window.removeEventListener('mouseup', resizeend);
|
||||
|
|
@ -123,7 +122,7 @@ const dragend = (e: MouseEvent) => {
|
|||
window.removeEventListener('mouseup', dragend);
|
||||
|
||||
if(node.x - lastx !== 0 && node.y - lasty !== 0)
|
||||
emit('move', index, node.x - lastx, node.y - lasty);
|
||||
emit('move', node.id, node.x - lastx, node.y - lasty);
|
||||
};
|
||||
const dragstart = (e: MouseEvent) => {
|
||||
if(e.button !== 0)
|
||||
|
|
@ -135,7 +134,7 @@ const dragstart = (e: MouseEvent) => {
|
|||
window.addEventListener('mouseup', dragend, { passive: true });
|
||||
};
|
||||
|
||||
defineExpose({ unselect, dom });
|
||||
defineExpose({ unselect, dom, ...node });
|
||||
|
||||
const style = computed(() => {
|
||||
return node.color ? node.color?.class ?
|
||||
|
|
|
|||
BIN
db.sqlite-wal
BIN
db.sqlite-wal
Binary file not shown.
Loading…
Reference in New Issue