Добавляем все файлы
This commit is contained in:
306
main_plugin/dgrm/shapes/shape-evt-proc.js
Executable file
306
main_plugin/dgrm/shapes/shape-evt-proc.js
Executable file
@@ -0,0 +1,306 @@
|
||||
import { child, classAdd, classDel, deepCopy, svgEl } from '../infrastructure/util.js';
|
||||
import { moveEvtProc } from '../infrastructure/move-evt-proc.js';
|
||||
import { path, dirReverse } from './path.js';
|
||||
import { textareaCreate } from '../infrastructure/svg-text-area.js';
|
||||
import { settingsPnlCreate } from './shape-settings.js';
|
||||
import { placeToCell, pointInCanvas } from '../infrastructure/move-scale-applay.js';
|
||||
import { ShapeSmbl } from './shape-smbl.js';
|
||||
import { svgTextDraw } from '../infrastructure/svg-text-draw.js';
|
||||
import { PathSmbl } from './path-smbl.js';
|
||||
import { CanvasSmbl } from '../infrastructure/canvas-smbl.js';
|
||||
import { canvasSelectionClearSet } from '../diagram/canvas-clear.js';
|
||||
import { listenCopy } from '../diagram/group-select-applay.js';
|
||||
|
||||
/**
|
||||
* provides:
|
||||
* - shape move
|
||||
* - connectors
|
||||
*
|
||||
* - text editor
|
||||
* - standard edit panel
|
||||
* - onTextChange callback
|
||||
* @param {CanvasElement} canvas
|
||||
* @param {string} shapeHtml must have '<text data-key="text">'
|
||||
* @param {ShapeData & { title?: string, styles?: string[]}} shapeData
|
||||
* @param {ConnectorsData} cons
|
||||
* @param {SettingsPnlCreateFn=} settingsPnlCreateFn
|
||||
* @param {{(txtEl:SVGTextElement):void}} onTextChange
|
||||
*/
|
||||
export function shapeCreate(canvas, shapeData, shapeHtml, cons, onTextChange, settingsPnlCreateFn) {
|
||||
/** @type {ShapeElement} */
|
||||
const el = svgEl('g', `${shapeHtml}
|
||||
${Object.entries(cons)
|
||||
.map(cc => `<circle data-key="${cc[0]}" data-connect="${cc[1].dir}" class="hovertrack" data-evt-index="2" r="10" cx="0" cy="0" style="transform: translate(${cc[1].position.x}px, ${cc[1].position.y}px);" />`)
|
||||
.join()}`);
|
||||
|
||||
const textSettings = {
|
||||
/** @type {SVGTextElement} */
|
||||
el: child(el, 'text'),
|
||||
/** vericale middle, em */
|
||||
vMid: 0
|
||||
};
|
||||
|
||||
svgTextDraw(textSettings.el, textSettings.vMid, shapeData.title);
|
||||
|
||||
const shapeProc = shapeEditEvtProc(canvas, el, shapeData, cons, textSettings,
|
||||
settingsPnlCreateFn,
|
||||
// onTextChange
|
||||
() => onTextChange(textSettings.el));
|
||||
|
||||
return {
|
||||
el,
|
||||
cons,
|
||||
draw: shapeProc.draw
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* provides:
|
||||
* - shape move
|
||||
* - connectors
|
||||
* - copy fn
|
||||
*
|
||||
* - text editor
|
||||
* - standard edit panel
|
||||
* - onTextChange callback
|
||||
* @param {CanvasElement} canvas
|
||||
* @param {ShapeElement} svgGrp
|
||||
* @param {ShapeData & { title?: string, styles?: string[]}} shapeData
|
||||
* @param {ConnectorsData} connectorsInnerPosition
|
||||
* @param { {el:SVGTextElement, vMid: number} } textSettings vMid in em
|
||||
* @param {{():void}} onTextChange
|
||||
* @param {SettingsPnlCreateFn} settingsPnlCreateFn
|
||||
*/
|
||||
function shapeEditEvtProc(canvas, svgGrp, shapeData, connectorsInnerPosition, textSettings, settingsPnlCreateFn, onTextChange) {
|
||||
/** @type {{dispose():void, draw():void}} */
|
||||
let textEditor;
|
||||
|
||||
/** @type { {position:(bottomX:number, bottomY:number)=>void, del:()=>void} } */
|
||||
let settingsPnl;
|
||||
|
||||
function unSelect() {
|
||||
textEditor?.dispose(); textEditor = null;
|
||||
settingsPnl?.del(); settingsPnl = null;
|
||||
}
|
||||
|
||||
/** @param {string} txt */
|
||||
function onTxtChange(txt) {
|
||||
shapeData.title = txt;
|
||||
onTextChange();
|
||||
}
|
||||
|
||||
const settingPnlCreate = settingsPnlCreateFn ?? settingsPnlCreate;
|
||||
const shapeProc = shapeEvtProc(canvas, svgGrp, shapeData, connectorsInnerPosition,
|
||||
// onEdit
|
||||
() => {
|
||||
textEditor = textareaCreate(textSettings.el, textSettings.vMid, shapeData.title, onTxtChange, onTxtChange);
|
||||
|
||||
const position = svgGrp.getBoundingClientRect();
|
||||
settingsPnl = settingPnlCreate(canvas, svgGrp, position.left + 10, position.top + 10);
|
||||
},
|
||||
// onUnselect
|
||||
unSelect
|
||||
);
|
||||
|
||||
if (shapeData.styles) { classAdd(svgGrp, ...shapeData.styles); }
|
||||
|
||||
svgGrp[ShapeSmbl].del = function() {
|
||||
shapeProc.del();
|
||||
svgGrp.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
draw: () => {
|
||||
shapeProc.drawPosition();
|
||||
|
||||
if (settingsPnl) {
|
||||
const position = svgGrp.getBoundingClientRect();
|
||||
settingsPnl.position(position.left + 10, position.top + 10);
|
||||
}
|
||||
|
||||
if (textEditor) { textEditor.draw(); }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* provides:
|
||||
* - shape move
|
||||
* - connectors
|
||||
* - copy fn
|
||||
* - onEdit, onEditStop callbacks
|
||||
* @param {CanvasElement} canvas
|
||||
* @param {ShapeElement} svgGrp
|
||||
* @param {ShapeData} shapeData
|
||||
* @param {ConnectorsData} connectorsInnerPosition
|
||||
* @param {{():void}} onEdit
|
||||
* @param {{():void}} onUnselect
|
||||
*/
|
||||
function shapeEvtProc(canvas, svgGrp, shapeData, connectorsInnerPosition, onEdit, onUnselect) {
|
||||
classAdd(svgGrp, 'hovertrack');
|
||||
|
||||
/** @type {ConnectorsData} */
|
||||
const connectorsData = deepCopy(connectorsInnerPosition);
|
||||
|
||||
/** @type { Set<PathElement> } */
|
||||
const paths = new Set();
|
||||
|
||||
function drawPosition() {
|
||||
svgGrp.style.transform = `translate(${shapeData.position.x}px, ${shapeData.position.y}px)`;
|
||||
|
||||
// paths
|
||||
for (const connectorKey in connectorsInnerPosition) {
|
||||
connectorsData[connectorKey].position = {
|
||||
x: connectorsInnerPosition[connectorKey].position.x + shapeData.position.x,
|
||||
y: connectorsInnerPosition[connectorKey].position.y + shapeData.position.y
|
||||
};
|
||||
}
|
||||
|
||||
for (const path of paths) {
|
||||
path[PathSmbl].draw();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {0|1|2}
|
||||
* 0 - init, 1 - selected, 2 - edit
|
||||
*/
|
||||
let state = 0;
|
||||
|
||||
/** @type {()=>void} */
|
||||
let listenCopyDispose;
|
||||
|
||||
function unSelect() {
|
||||
onUnselect();
|
||||
|
||||
state = 0;
|
||||
classDel(svgGrp, 'select');
|
||||
classDel(svgGrp, 'highlight');
|
||||
|
||||
canvasSelectionClearSet(canvas, null);
|
||||
if (listenCopyDispose) { listenCopyDispose(); listenCopyDispose = null; }
|
||||
}
|
||||
|
||||
const moveProcReset = moveEvtProc(
|
||||
canvas.ownerSVGElement,
|
||||
svgGrp,
|
||||
canvas[CanvasSmbl].data,
|
||||
shapeData.position,
|
||||
// onMoveStart
|
||||
/** @param {PointerEvent & { target: Element} } evt */
|
||||
evt => {
|
||||
unSelect();
|
||||
|
||||
const connectorKey = evt.target.getAttribute('data-connect');
|
||||
if (connectorKey) {
|
||||
moveProcReset();
|
||||
|
||||
const diagramEl = document.getElementById('diagram');
|
||||
const rect = diagramEl.getBoundingClientRect();
|
||||
const x = evt.clientX - rect.left;
|
||||
const y = evt.clientY - rect.top;
|
||||
|
||||
const pathEl = path(canvas, {
|
||||
s: { shape: { shapeEl: svgGrp, connectorKey } },
|
||||
e: {
|
||||
data: {
|
||||
dir: dirReverse(connectorsData[connectorKey].dir),
|
||||
position: pointInCanvas(canvas[CanvasSmbl].data, x, y)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
svgGrp.parentNode.append(pathEl);
|
||||
pathEl[PathSmbl].pointerCapture(evt);
|
||||
paths.add(pathEl);
|
||||
}
|
||||
},
|
||||
// onMove
|
||||
drawPosition,
|
||||
// onMoveEnd
|
||||
_ => {
|
||||
placeToCell(shapeData.position, canvas[CanvasSmbl].data.cell);
|
||||
drawPosition();
|
||||
},
|
||||
// onClick
|
||||
_ => {
|
||||
// in edit mode
|
||||
if (state === 2) { return; }
|
||||
|
||||
// to edit mode
|
||||
if (state === 1) {
|
||||
state = 2;
|
||||
classDel(svgGrp, 'select');
|
||||
classAdd(svgGrp, 'highlight');
|
||||
// edit mode
|
||||
onEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
// to select mode
|
||||
state = 1;
|
||||
classAdd(svgGrp, 'select');
|
||||
|
||||
canvasSelectionClearSet(canvas, unSelect);
|
||||
listenCopyDispose = listenCopy(() => [svgGrp]);
|
||||
},
|
||||
// onOutdown
|
||||
unSelect);
|
||||
|
||||
svgGrp[ShapeSmbl] = {
|
||||
/**
|
||||
* @param {string} connectorKey
|
||||
* @param {PathElement} pathEl
|
||||
*/
|
||||
pathAdd: function(connectorKey, pathEl) {
|
||||
paths.add(pathEl);
|
||||
return connectorsData[connectorKey];
|
||||
},
|
||||
|
||||
/** @param {PathElement} pathEl */
|
||||
pathDel: function(pathEl) {
|
||||
paths.delete(pathEl);
|
||||
},
|
||||
|
||||
drawPosition,
|
||||
|
||||
data: shapeData
|
||||
};
|
||||
|
||||
return {
|
||||
drawPosition,
|
||||
del: () => {
|
||||
unSelect();
|
||||
moveProcReset();
|
||||
for (const path of paths) {
|
||||
path[PathSmbl].del();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @typedef { {x:number, y:number} } Point */
|
||||
/** @typedef { {position:Point, scale:number, cell:number} } CanvasData */
|
||||
|
||||
/** @typedef { 'left' | 'right' | 'top' | 'bottom' } PathDir */
|
||||
/** @typedef { {position: Point, dir: PathDir} } PathEnd */
|
||||
/** @typedef { Object.<string, PathEnd> } ConnectorsData */
|
||||
|
||||
/** @typedef { {type: number, position: Point, styles?:string[]} } ShapeData */
|
||||
/**
|
||||
@typedef {{
|
||||
pathAdd(connectorKey:string, pathEl:PathElement): PathEnd
|
||||
pathDel(pathEl:PathElement): void
|
||||
drawPosition: ()=>void
|
||||
data: ShapeData
|
||||
del?: ()=>void
|
||||
draw?: ()=>void
|
||||
}} Shape
|
||||
*/
|
||||
|
||||
/** @typedef { {(canvas:CanvasElement, shapeElement:ShapeElement, bottomX:number, bottomY:number):{position(btmX:number, btmY:number):void, del():void} } } SettingsPnlCreateFn */
|
||||
|
||||
/** @typedef { import('../infrastructure/canvas-smbl.js').CanvasElement } CanvasElement */
|
||||
/** @typedef {import('./shape-smbl').ShapeElement} ShapeElement */
|
||||
/** @typedef {import('./path').Path} Path */
|
||||
/** @typedef {import('./path-smbl').PathElement} PathElement */
|
||||
Reference in New Issue
Block a user