Fixed the 'Anchors' example.

development
Junsik Shim 2022-01-22 12:54:58 +09:00
parent 3494d0fb8a
commit b2bb10ea7e
9 changed files with 154 additions and 98 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}

1
packages/core/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module '*.gif';

View File

@ -42,7 +42,7 @@ export type CellStateStyles = {
defaultVertex?: CellStateStyles;
deletable?: boolean;
direction?: DirectionValue;
edge?: string;
edgeStyle?: string;
editable?: boolean;
elbow?: string;
endArrow?: ArrowType;

View File

@ -14,17 +14,8 @@ import RectangleShape from './geometry/node/RectangleShape';
import { ALIGN } from '../util/Constants';
import Client from '../Client';
import InternalEvent from './event/InternalEvent';
import {
convertPoint,
getCurrentStyle,
getOffset,
} from '../util/styleUtils';
import {
getRotatedPoint,
ptSegDistSq,
relativeCcw,
toRadians,
} from '../util/mathUtils';
import { convertPoint, getCurrentStyle, getOffset } from '../util/styleUtils';
import { getRotatedPoint, ptSegDistSq, relativeCcw, toRadians } from '../util/mathUtils';
import MaxLog from '../gui/MaxLog';
import Translations from '../util/Translations';
import CellState from './cell/CellState';
@ -49,7 +40,6 @@ import { MouseEventListener } from '../types';
import ObjectCodec from '../serialization/ObjectCodec';
import CodecRegistry from '../serialization/CodecRegistry';
/**
* @class GraphView
* @extends {EventSource}
@ -236,9 +226,7 @@ export class GraphView extends EventSource {
this.viewStateChanged();
}
}
this.fireEvent(
new EventObject(InternalEvent.SCALE, { scale: value, previousScale })
);
this.fireEvent(new EventObject(InternalEvent.SCALE, { scale: value, previousScale }));
}
/**
@ -415,10 +403,10 @@ export class GraphView extends EventSource {
}
this.fireEvent(
new EventObject(InternalEvent.SCALE_AND_TRANSLATE, {
scale,
previousScale,
translate: this.translate,
new EventObject(InternalEvent.SCALE_AND_TRANSLATE, {
scale,
previousScale,
translate: this.translate,
previousTranslate: previousTranslate,
})
);
@ -1320,15 +1308,17 @@ export class GraphView extends EventSource {
let edgeStyle = this.isLoopStyleEnabled(edge, points, source, target)
? edge.style.loop ?? this.graph.defaultLoopStyle
: !edge.style.noEdgeStyle ?? false
? edge.style.edge
? edge.style.edgeStyle
: null;
// Converts string values to objects
if (typeof edgeStyle === 'string') {
let tmp = StyleRegistry.getValue(edgeStyle);
if (!tmp && this.isAllowEval()) {
tmp = eval(edgeStyle);
}
edgeStyle = tmp;
}
@ -2267,11 +2257,11 @@ export class GraphView extends EventSource {
*/
createHtml() {
var container = this.graph.container;
if (container != null) {
this.canvas = this.createHtmlPane('100%', '100%');
this.canvas.style.overflow = 'hidden';
// Uses minimal size for inner DIVs on Canvas. This is required
// for correct event processing in IE. If we have an overlapping
// DIV then the events on the cells are only fired for labels.
@ -2279,20 +2269,20 @@ export class GraphView extends EventSource {
this.drawPane = this.createHtmlPane('1px', '1px');
this.overlayPane = this.createHtmlPane('1px', '1px');
this.decoratorPane = this.createHtmlPane('1px', '1px');
this.canvas.appendChild(this.backgroundPane);
this.canvas.appendChild(this.drawPane);
this.canvas.appendChild(this.overlayPane);
this.canvas.appendChild(this.decoratorPane);
container.appendChild(this.canvas);
this.updateContainerStyle(container);
this.updateContainerStyle(container);
}
};
}
/**
* Function: updateHtmlCanvasSize
*
*
* Updates the size of the HTML canvas.
*/
updateHtmlCanvasSize(width: number, height: number) {
@ -2312,16 +2302,16 @@ export class GraphView extends EventSource {
this.canvas.style.height = '100%';
}
}
};
}
/**
* Function: createHtmlPane
*
*
* Creates and returns a drawing pane in HTML (DIV).
*/
createHtmlPane(width: string, height: string) {
var pane = document.createElement('DIV');
if (width != null && height != null) {
pane.style.position = 'absolute';
pane.style.left = '0px';
@ -2329,12 +2319,11 @@ export class GraphView extends EventSource {
pane.style.width = width;
pane.style.height = height;
} else {
pane.style.position = 'relative';
}
return pane;
};
}
/**
* Updates the style of the container after installing the SVG DOM elements.
@ -2357,8 +2346,8 @@ export class GraphView extends EventSource {
* Destroys the view and all its resources.
*/
destroy() {
let root: SVGElement | HTMLElement | null=null;
let root: SVGElement | HTMLElement | null = null;
if (this.canvas && this.canvas instanceof SVGElement) {
root = this.canvas.ownerSVGElement as SVGElement;
}

View File

@ -610,7 +610,6 @@ class ConnectionHandler extends EventSource implements GraphPlugin {
* Starts a new connection for the given state and coordinates.
*/
start(state: CellState, x: number, y: number, edgeState: CellState) {
console.log("ConnectionHandler start");
this.previous = state;
this.first = new Point(x, y);
this.edgeState = edgeState ?? this.createEdgeState();
@ -1191,9 +1190,8 @@ class ConnectionHandler extends EventSource implements GraphPlugin {
// Uses edge state to compute the terminal points
if (this.edgeState) {
this.updateEdgeState(current, constraint);
current = this.edgeState.absolutePoints[
this.edgeState.absolutePoints.length - 1
];
current =
this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 1];
pt2 = this.edgeState.absolutePoints[0];
} else {
if (this.currentState) {
@ -1225,9 +1223,8 @@ class ConnectionHandler extends EventSource implements GraphPlugin {
let tmp = pt2;
if (this.edgeState && this.edgeState.absolutePoints.length >= 2) {
const tmp2 = this.edgeState.absolutePoints[
this.edgeState.absolutePoints.length - 2
];
const tmp2 =
this.edgeState.absolutePoints[this.edgeState.absolutePoints.length - 2];
if (tmp2) {
tmp = tmp2;

View File

@ -26,6 +26,8 @@ import ConnectionConstraint from '../other/ConnectionConstraint';
import Point from '../geometry/Point';
import Cell from '../cell/Cell';
import pointImg from '../../../images/point.gif';
/**
* Handles constraints on connection targets. This class is in charge of
* showing fixed points when the mouse is over a vertex and handles constraints
@ -38,7 +40,7 @@ class ConstraintHandler {
* {@link Image} to be used as the image for fixed connection points.
*/
// pointImage: mxImage;
pointImage = new Image(`${Client.imageBasePath}/point.gif`, 5, 5);
pointImage = new Image(pointImg, 5, 5);
/**
* Reference to the enclosing {@link mxGraph}.
@ -81,8 +83,8 @@ class ConstraintHandler {
// Adds a graph model listener to update the current focus on changes
this.resetHandler = () => {
if (
this.currentFocus != null &&
this.graph.view.getState(this.currentFocus.cell) == null
this.currentFocus &&
!this.graph.view.getState(this.currentFocus.cell)
) {
this.reset();
} else {
@ -211,15 +213,15 @@ class ConstraintHandler {
// Gets cell under actual point if different from event location
if (
cell == null &&
point != null &&
!cell &&
point &&
(me.getGraphX() !== point.x || me.getGraphY() !== point.y)
) {
cell = this.graph.getCellAt(point.x, point.y);
}
// Uses connectable parent vertex if one exists
if (cell != null && !cell.isConnectable()) {
if (cell && !cell.isConnectable()) {
const parent = cell.getParent();
if (parent && parent.isVertex() && parent.isConnectable()) {
@ -246,7 +248,7 @@ class ConstraintHandler {
) {
if (this.isEnabled() && !this.isEventIgnored(me)) {
// Lazy installation of mouseleave handler
if (this.mouseleaveHandler == null && this.graph.container != null) {
if (!this.mouseleaveHandler && this.graph.container ) {
this.mouseleaveHandler = () => {
this.reset();
};
@ -255,8 +257,8 @@ class ConstraintHandler {
}
const tol = this.getTolerance(me);
const x = point != null ? point.x : me.getGraphX();
const y = point != null ? point.y : me.getGraphY();
const x = point ? point.x : me.getGraphX();
const y = point ? point.y : me.getGraphY();
const grid = new Rectangle(x - tol, y - tol, 2 * tol, 2 * tol);
const mouse = new Rectangle(
me.getGraphX() - tol,
@ -270,8 +272,8 @@ class ConstraintHandler {
// Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
if (
!this.isKeepFocusEvent(me) &&
(this.currentFocusArea == null ||
this.currentFocus == null ||
(!this.currentFocusArea ||
!this.currentFocus ||
state ||
!this.currentFocus.cell.isVertex() ||
!intersects(this.currentFocusArea, mouse)) &&
@ -289,9 +291,9 @@ class ConstraintHandler {
let tmp;
if (
this.focusIcons != null &&
this.constraints != null &&
(state == null || this.currentFocus === state)
this.focusIcons.length > 0 &&
this.constraints &&
(!state || this.currentFocus === state)
) {
const cx = mouse.getCenterX();
const cy = mouse.getCenterY();
@ -303,9 +305,9 @@ class ConstraintHandler {
if (
(this.intersects(this.focusIcons[i], mouse, source, existingEdge) ||
(point != null &&
(point &&
this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
(minDistSq == null || tmp < minDistSq)
(minDistSq === null || tmp < minDistSq)
) {
this.currentConstraint = this.constraints[i];
this.currentPoint = this.focusPoints[i];
@ -316,7 +318,7 @@ class ConstraintHandler {
tmp.width -= 1;
tmp.height -= 1;
if (this.focusHighlight == null) {
if (!this.focusHighlight) {
const hl = this.createHighlightShape();
hl.dialect = DIALECT.SVG;
hl.pointerEvents = false;
@ -325,7 +327,7 @@ class ConstraintHandler {
this.focusHighlight = hl;
const getState = () => {
return this.currentFocus != null ? this.currentFocus : state;
return this.currentFocus ? this.currentFocus : state;
};
InternalEvent.redirectMouseEvents(hl.node, this.graph, getState);
@ -337,7 +339,7 @@ class ConstraintHandler {
}
}
if (this.currentConstraint == null) {
if (!this.currentConstraint) {
this.destroyFocusHighlight();
}
} else {
@ -354,9 +356,9 @@ class ConstraintHandler {
*/
redraw() {
if (
this.currentFocus != null &&
this.constraints != null &&
this.focusIcons != null
this.currentFocus &&
this.constraints &&
this.focusIcons.length > 0
) {
const state = this.graph.view.getState(this.currentFocus.cell) as CellState;
this.currentFocus = state;
@ -423,12 +425,12 @@ class ConstraintHandler {
icon.init(this.graph.getView().getDecoratorPane());
// Move the icon behind all other overlays
if (icon.node.previousSibling != null) {
if (icon.node.previousSibling) {
icon.node.parentNode?.insertBefore(icon.node, icon.node.parentNode.firstChild);
}
const getState = () => {
return this.currentFocus != null ? this.currentFocus : state;
return this.currentFocus ? this.currentFocus : state;
};
icon.redraw();
@ -480,7 +482,7 @@ class ConstraintHandler {
this.graph.view.removeListener(this.resetHandler);
this.graph.removeListener(this.resetHandler);
if (this.mouseleaveHandler != null && this.graph.container != null) {
if (this.mouseleaveHandler && this.graph.container) {
InternalEvent.removeListener(
this.graph.container,
'mouseleave',

View File

@ -577,6 +577,7 @@ export const CellsMixin: PartialType = {
if (!style) {
style = {} as CellStateStyles;
}
return style;
},
@ -2009,7 +2010,9 @@ export const CellsMixin: PartialType = {
this.resetEdges(cells);
}
this.fireEvent(new EventObject(InternalEvent.CELLS_MOVED, { cells, dx, dy, disconnect }));
this.fireEvent(
new EventObject(InternalEvent.CELLS_MOVED, { cells, dx, dy, disconnect })
);
});
}
},
@ -2755,9 +2758,8 @@ export const CellsMixin: PartialType = {
*/
isCellResizable(cell) {
const style = this.getCurrentCellStyle(cell);
const r = this.isCellsResizable() &&
!this.isCellLocked(cell) &&
(style.resizable ?? true);
const r =
this.isCellsResizable() && !this.isCellLocked(cell) && (style.resizable ?? true);
return r;
},

View File

@ -113,7 +113,13 @@ class EdgeStyle {
* @param result Array of <Point> that represent the actual points of the
* edge.
*/
static EntityRelation(state: CellState, source: CellState, target: CellState, points: Point[], result: Point[]) {
static EntityRelation(
state: CellState,
source: CellState,
target: CellState,
points: Point[],
result: Point[]
) {
const { view } = state;
const { graph } = view;
const segment = getValue(state.style, 'segment', ENTITY_SEGMENT) * view.scale;
@ -218,7 +224,13 @@ class EdgeStyle {
/**
* Implements a self-reference, aka. loop.
*/
static Loop(state: CellState, source: CellState, target: CellState, points: Point[], result: Point[]) {
static Loop(
state: CellState,
source: CellState,
target: CellState,
points: Point[],
result: Point[]
) {
const pts = state.absolutePoints;
const p0 = pts[0];
@ -295,7 +307,13 @@ class EdgeStyle {
* unspecified. See <EntityRelation> for a description of the
* parameters.
*/
static ElbowConnector(state: CellState, source: CellState, target: CellState, points: Point[], result: Point[]) {
static ElbowConnector(
state: CellState,
source: CellState,
target: CellState,
points: Point[],
result: Point[]
) {
let pt = points != null && points.length > 0 ? points[0] : null;
let vertical = false;
@ -312,7 +330,6 @@ class EdgeStyle {
pt = <Point>state.view.transformControlPoint(state, pt);
vertical = pt.y < top || pt.y > bottom;
horizontal = pt.x < left || pt.x > right;
} else {
const left = Math.max(source.x, target.x);
const right = Math.min(source.x + source.width, target.x + target.width);
@ -338,7 +355,13 @@ class EdgeStyle {
* Implements a vertical elbow edge. See <EntityRelation> for a description
* of the parameters.
*/
static SideToSide(state: CellState, source: CellState, target: CellState, points: Point[], result: Point[]) {
static SideToSide(
state: CellState,
source: CellState,
target: CellState,
points: Point[],
result: Point[]
) {
const { view } = state;
let pt = points != null && points.length > 0 ? points[0] : null;
const pts = state.absolutePoints;
@ -407,7 +430,13 @@ class EdgeStyle {
* Implements a horizontal elbow edge. See <EntityRelation> for a
* description of the parameters.
*/
static TopToBottom(state: CellState, source: CellState, target: CellState, points: Point[], result: Point[]) {
static TopToBottom(
state: CellState,
source: CellState,
target: CellState,
points: Point[],
result: Point[]
) {
const { view } = state;
let pt = points != null && points.length > 0 ? points[0] : null;
const pts = state.absolutePoints;
@ -482,11 +511,23 @@ class EdgeStyle {
* @param result Array of <Point> that represent the actual points of the
* edge.
*/
static SegmentConnector(state: CellState, sourceScaled: CellState, targetScaled: CellState, controlHints: Point[], result: Point[]) {
static SegmentConnector(
state: CellState,
sourceScaled: CellState,
targetScaled: CellState,
controlHints: Point[],
result: Point[]
) {
// Creates array of all way- and terminalpoints
// TODO: Figure out what to do when there are nulls in `pts`!
const pts = <Point[]><unknown>EdgeStyle.scalePointArray(<Point[]><unknown>state.absolutePoints, state.view.scale);
const pts = <Point[]>(
(<unknown>(
EdgeStyle.scalePointArray(
<Point[]>(<unknown>state.absolutePoints),
state.view.scale
)
))
);
const source = EdgeStyle.scaleCellState(sourceScaled, state.view.scale);
const target = EdgeStyle.scaleCellState(targetScaled, state.view.scale);
const tol = 1;
@ -952,10 +993,19 @@ class EdgeStyle {
* @param result Array of <Point> that represent the actual points of the
* edge.
*/
static OrthConnector(state: CellState, sourceScaled: CellState, targetScaled: CellState, controlHints: Point[], result: Point[]) {
static OrthConnector(
state: CellState,
sourceScaled: CellState,
targetScaled: CellState,
controlHints: Point[],
result: Point[]
) {
// TODO: Figure out what to do when there are nulls in `pts`!
const pts = <Point[]><unknown>EdgeStyle.scalePointArray(<Point[]>state.absolutePoints, state.view.scale);
const pts = <Point[]>(
(<unknown>(
EdgeStyle.scalePointArray(<Point[]>state.absolutePoints, state.view.scale)
))
);
let source = EdgeStyle.scaleCellState(sourceScaled, state.view.scale);
let target = EdgeStyle.scaleCellState(targetScaled, state.view.scale);
@ -1022,9 +1072,11 @@ class EdgeStyle {
// console.log('source rotation', rotation);
if (rotation !== 0) {
const newRect = <Rectangle>getBoundingBox(
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
rotation
const newRect = <Rectangle>(
getBoundingBox(
new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
rotation
)
);
sourceX = newRect.x;
sourceY = newRect.y;
@ -1040,9 +1092,11 @@ class EdgeStyle {
// console.log('target rotation', rotation);
if (rotation !== 0) {
const newRect = <Rectangle>getBoundingBox(
new Rectangle(targetX, targetY, targetWidth, targetHeight),
rotation
const newRect = <Rectangle>(
getBoundingBox(
new Rectangle(targetX, targetY, targetWidth, targetHeight),
rotation
)
);
targetX = newRect.x;
targetY = newRect.y;
@ -1464,7 +1518,12 @@ class EdgeStyle {
}
}
static getRoutePattern(dir: number[], quad: number, dx: number, dy: number): number[] | null {
static getRoutePattern(
dir: number[],
quad: number,
dx: number,
dy: number
): number[] | null {
let sourceIndex = dir[0] === DIRECTION_MASK.EAST ? 3 : dir[0];
let targetIndex = dir[1] === DIRECTION_MASK.EAST ? 3 : dir[1];
@ -1478,7 +1537,8 @@ class EdgeStyle {
targetIndex += 4;
}
let result: number[] | null = EdgeStyle.routePatterns[sourceIndex - 1][targetIndex - 1];
let result: number[] | null =
EdgeStyle.routePatterns[sourceIndex - 1][targetIndex - 1];
if (dx === 0 || dy === 0) {
if (EdgeStyle.inlineRoutePatterns[sourceIndex - 1][targetIndex - 1] != null) {

View File

@ -44,16 +44,17 @@ const Template = ({ label, ...args }) => {
class MyCustomGraph extends Graph {
getAllConnectionConstraints(terminal, source) {
// Overridden to define per-shape connection points
if (terminal != null && terminal.shape != null) {
if (terminal.shape.stencil != null) {
if (terminal.shape.stencil.constraints != null) {
// Overridden to define per-geometry connection points
if (terminal && terminal.cell) {
if (terminal.shape.stencil) {
if (terminal.shape.stencil.constraints) {
return terminal.shape.stencil.constraints;
}
} else if (terminal.shape.constraints != null) {
return terminal.shape.constraints;
} else if (terminal.cell.geometry.constraints) {
return terminal.cell.geometry.constraints;
}
}
return null;
}
@ -116,8 +117,8 @@ const Template = ({ label, ...args }) => {
const e1 = graph.insertEdge({
parent,
value: '',
position: v1,
size: v2,
source: v1,
target: v2,
});
});