Merge pull request #1 from maxGraph/mcyph-graph-refactor

Mcyph graph refactor
development
Dave Morrissey 2021-07-31 15:13:20 +10:00 committed by GitHub
commit 061ff34917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 1808 additions and 2697 deletions

View File

@ -119,8 +119,8 @@ import mxAnimation from './util/animate/mxAnimation';
import mxEffects from './util/animate/mxEffects'; import mxEffects from './util/animate/mxEffects';
import mxMorphing from './util/animate/mxMorphing'; import mxMorphing from './util/animate/mxMorphing';
import mxAbstractCanvas2D from './util/canvas/mxAbstractCanvas2D'; import mxAbstractCanvas2D from './util/canvas/AbstractCanvas2D';
import mxSvgCanvas2D from './util/canvas/mxSvgCanvas2D'; import mxSvgCanvas2D from './util/canvas/SvgCanvas2D';
import mxXmlCanvas2D from './util/canvas/mxXmlCanvas2D'; import mxXmlCanvas2D from './util/canvas/mxXmlCanvas2D';
import Dictionary from './util/Dictionary'; import Dictionary from './util/Dictionary';
@ -151,7 +151,7 @@ import mxPopupMenu from './util/gui/mxPopupMenu';
import mxToolbar from './util/gui/mxToolbar'; import mxToolbar from './util/gui/mxToolbar';
import mxWindow from './util/gui/mxWindow'; import mxWindow from './util/gui/mxWindow';
import Image from './view/image/Image'; import Image from './view/image/ImageBox';
import ImageBundle from './view/image/ImageBundle'; import ImageBundle from './view/image/ImageBundle';
import ImageExport from './view/image/ImageExport'; import ImageExport from './view/image/ImageExport';

View File

@ -1,4 +1,5 @@
import type Cell from './view/cell/datatypes/Cell'; import type Cell from './view/cell/datatypes/Cell';
import Shape from './view/geometry/shape/Shape';
export type CellMap = { export type CellMap = {
[id: string]: Cell; [id: string]: Cell;
@ -23,19 +24,88 @@ export type Properties = {
}; };
export type CellStateStyles = { export type CellStateStyles = {
[k: string]: string; absoluteArcSize: number;
align: AlignValue;
arcSize: number;
backgroundColor: ColorValue;
backgroundOutline: number;
curved: boolean;
dashed: boolean;
dashPattern: string;
direction: DirectionValue;
endArrow: ArrowType;
endFill: boolean;
endSize: number;
fillColor: ColorValue;
fillOpacity: number;
fixDash: boolean;
flipH: boolean;
flipV: boolean;
fontColor: ColorValue;
fontFamily: string;
fontSize: number;
fontStyle: number;
glass: boolean;
gradientColor: ColorValue;
gradientDirection: DirectionValue;
horizontal: boolean;
image: string;
imageAlign: AlignValue;
imageAspect: boolean;
imageBackground: ColorValue;
imageBorder: ColorValue;
imageHeight: number;
imageWidth: number;
indicatorColor: ColorValue;
indicatorHeight: number;
indicatorImage: string;
indicatorShape: Shape;
indicatorWidth: number;
labelBorderColor: ColorValue;
labelPosition: AlignValue;
margin: number;
opacity: number;
pointerEvents: boolean;
rotation: number;
rounded: boolean;
separatorColor: ColorValue;
shadow: boolean;
spacing: number;
spacingBottom: number;
spacingLeft: number;
spacingRight: number;
spacingTop: number;
startArrow: ArrowType;
startFill: boolean;
startSize: number;
strokeColor: ColorValue;
strokeOpacity: number;
strokeWidth: number;
swimlaneFillColor: ColorValue;
swimlaneLine: boolean;
textDirection: TextDirectionValue;
textOpacity: number;
verticalAlign: VAlignValue;
verticalLabelPosition: VAlignValue;
}; };
export type ColorValue = string | null; export type ColorValue = string;
export type DirectionValue = 'north' | 'south' | 'east' | 'west' | null; export type DirectionValue = 'north' | 'south' | 'east' | 'west';
export type AlignValue = export type TextDirectionValue = '' | 'ltr' | 'rtl' | 'auto';
| 'left' export type AlignValue = 'left' | 'center' | 'right';
| 'center' export type VAlignValue = 'top' | 'middle' | 'bottom';
| 'right' export type OverflowValue = 'fill' | 'width' | 'auto' | 'hidden' | 'scroll' | 'visible';
| 'top' export type ArrowType =
| 'middle' | 'none'
| 'bottom' | 'classic'
| null; | 'classicThin'
| 'block'
| 'blockThin'
| 'open'
| 'openThin'
| 'oval'
| 'diamond'
| 'diamondThin';
export type CanvasState = { export type CanvasState = {
dx: number; dx: number;
@ -48,9 +118,9 @@ export type CanvasState = {
gradientFillAlpha: number; gradientFillAlpha: number;
gradientColor: ColorValue; gradientColor: ColorValue;
gradientAlpha: number; gradientAlpha: number;
gradientDirection: string | null; gradientDirection: DirectionValue;
strokeColor: ColorValue; strokeColor: ColorValue;
strokeWidth: number | null; strokeWidth: number;
dashed: boolean; dashed: boolean;
dashPattern: string; dashPattern: string;
fixDash: boolean; fixDash: boolean;

View File

@ -5,13 +5,15 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import mxObjectIdentity from './mxObjectIdentity'; import ObjectIdentity from './ObjectIdentity';
//type Dictionary<T, U> = { //type Dictionary<T, U> = {
// [key: string]: U; // [key: string]: U;
//}; //};
type Visitor<T, U> = (key: string, value: U) => void; type MapKey = string;
type Visitor<MapKey, U> = (key: MapKey, value: U) => void;
/** /**
* Class: mxDictionary * Class: mxDictionary
@ -33,7 +35,7 @@ class Dictionary<T, U> {
* *
* Stores the (key, value) pairs in this dictionary. * Stores the (key, value) pairs in this dictionary.
*/ */
map: Dictionary<T, U> = {}; map: Record<MapKey, U> = {};
/** /**
* Function: clear * Function: clear
@ -50,7 +52,7 @@ class Dictionary<T, U> {
* Returns the value for the given key. * Returns the value for the given key.
*/ */
get(key: T) { get(key: T) {
const id = mxObjectIdentity.get(key); const id = ObjectIdentity.get(key);
return this.map[id]; return this.map[id];
} }
@ -62,7 +64,7 @@ class Dictionary<T, U> {
* value for that key. * value for that key.
*/ */
put(key: T, value: U) { put(key: T, value: U) {
const id = mxObjectIdentity.get(key); const id = ObjectIdentity.get(key);
const previous = this.map[id]; const previous = this.map[id];
this.map[id] = value; this.map[id] = value;
@ -76,7 +78,7 @@ class Dictionary<T, U> {
* has been removed. * has been removed.
*/ */
remove(key: T) { remove(key: T) {
const id = mxObjectIdentity.get(key); const id = ObjectIdentity.get(key);
const previous = this.map[id]; const previous = this.map[id];
delete this.map[id]; delete this.map[id];
@ -124,7 +126,7 @@ class Dictionary<T, U> {
* *
* visitor - A function that takes the key and value as arguments. * visitor - A function that takes the key and value as arguments.
*/ */
visit(visitor: Visitor<string, U>) { visit(visitor: Visitor<MapKey, U>) {
for (const key in this.map) { for (const key in this.map) {
visitor(key, this.map[key]); visitor(key, this.map[key]);
} }

View File

@ -29,7 +29,7 @@ type IdentityFunction = {
* *
* The identity for an object does not change during its lifecycle. * The identity for an object does not change during its lifecycle.
*/ */
class mxObjectIdentity { class ObjectIdentity {
/** /**
* Name of the field to be used to store the object ID. Default is * Name of the field to be used to store the object ID. Default is
* <code>mxObjectId</code>. * <code>mxObjectId</code>.
@ -48,9 +48,9 @@ class mxObjectIdentity {
if (isNullish(obj[FIELD_NAME])) { if (isNullish(obj[FIELD_NAME])) {
if (typeof obj === 'object') { if (typeof obj === 'object') {
const ctor = getFunctionName(obj.constructor); const ctor = getFunctionName(obj.constructor);
obj[FIELD_NAME] = `${ctor}#${mxObjectIdentity.counter++}`; obj[FIELD_NAME] = `${ctor}#${ObjectIdentity.counter++}`;
} else if (typeof obj === 'function') { } else if (typeof obj === 'function') {
obj[FIELD_NAME] = `Function#${mxObjectIdentity.counter++}`; obj[FIELD_NAME] = `Function#${ObjectIdentity.counter++}`;
} }
} }
@ -65,4 +65,4 @@ class mxObjectIdentity {
} }
} }
export default mxObjectIdentity; export default ObjectIdentity;

View File

@ -41,13 +41,8 @@ import Cell from '../view/cell/datatypes/Cell';
import Model from '../view/model/Model'; import Model from '../view/model/Model';
import graph from '../view/Graph'; import graph from '../view/Graph';
import type { import type { CellStateStyles, Properties, StyleProperties, StyleValue } from '../types';
CellStateStyles, import CellArray from '../view/cell/datatypes/CellArray';
Properties,
StyleProperties,
StyleValue,
} from '../types';
import CellArray from "../view/cell/datatypes/CellArray";
/** /**
* Class: mxUtils * Class: mxUtils
@ -164,11 +159,7 @@ export const parseCssNumber = (value: string) => {
* mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%'); * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%');
* (end) * (end)
*/ */
export const setPrefixedStyle = ( export const setPrefixedStyle = (style: StyleProperties, name: string, value: string) => {
style: StyleProperties,
name: string,
value: string
) => {
let prefix = null; let prefix = null;
if (mxClient.IS_SF || mxClient.IS_GC) { if (mxClient.IS_SF || mxClient.IS_GC) {
@ -343,11 +334,7 @@ export const getValue = (array: any, key: string, defaultValue?: any) => {
return value; return value;
}; };
export const getStringValue = ( export const getStringValue = (array: any, key: string, defaultValue: string) => {
array: any,
key: string,
defaultValue: string
) => {
let value = array != null ? array[key] : null; let value = array != null ? array[key] : null;
if (value == null) { if (value == null) {
value = defaultValue; value = defaultValue;
@ -455,10 +442,7 @@ export const equalEntries = (a: Properties | null, b: Properties | null) => {
for (var key in a) { for (var key in a) {
count--; count--;
if ( if ((!Number.isNaN(a[key]) || !Number.isNaN(b[key])) && a[key] !== b[key]) {
(!Number.isNaN(a[key]) || !Number.isNaN(b[key])) &&
a[key] !== b[key]
) {
return false; return false;
} }
} }
@ -588,10 +572,7 @@ export const arcToCurves = (
} }
sds = sds =
seif * seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd));
Math.sqrt(
(r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd)
);
} }
const txd = (sds * r1 * ryd) / r2; const txd = (sds * r1 * ryd) / r2;
@ -614,8 +595,7 @@ export const arcToCurves = (
const sse = (dr * 2) / Math.PI; const sse = (dr * 2) / Math.PI;
const seg = Math.ceil(sse < 0 ? -1 * sse : sse); const seg = Math.ceil(sse < 0 ? -1 * sse : sse);
const segr = dr / seg; const segr = dr / seg;
const t = const t = ((8 / 3) * Math.sin(segr / 4) * Math.sin(segr / 4)) / Math.sin(segr / 2);
((8 / 3) * Math.sin(segr / 4) * Math.sin(segr / 4)) / Math.sin(segr / 2);
const cpsir1 = cpsi * r1; const cpsir1 = cpsi * r1;
const cpsir2 = cpsi * r2; const cpsir2 = cpsi * r2;
const spsir1 = spsi * r1; const spsir1 = spsi * r1;
@ -679,10 +659,7 @@ export const getBoundingBox = (
const cos = Math.cos(rad); const cos = Math.cos(rad);
const sin = Math.sin(rad); const sin = Math.sin(rad);
cx = cx = cx != null ? cx : new Point(rect.x + rect.width / 2, rect.y + rect.height / 2);
cx != null
? cx
: new Point(rect.x + rect.width / 2, rect.y + rect.height / 2);
let p1 = new Point(rect.x, rect.y); let p1 = new Point(rect.x, rect.y);
let p2 = new Point(rect.x + rect.width, rect.y); let p2 = new Point(rect.x + rect.width, rect.y);
@ -708,12 +685,7 @@ export const getBoundingBox = (
* *
* Rotates the given point by the given cos and sin. * Rotates the given point by the given cos and sin.
*/ */
export const getRotatedPoint = ( export const getRotatedPoint = (pt: Point, cos: number, sin: number, c = new Point()) => {
pt: Point,
cos: number,
sin: number,
c = new Point()
) => {
const x = pt.x - c.x; const x = pt.x - c.x;
const y = pt.y - c.y; const y = pt.y - c.y;
@ -745,11 +717,7 @@ export const getPortConstraints = (
const value = getValue( const value = getValue(
terminal.style, terminal.style,
'portConstraint', 'portConstraint',
getValue( getValue(edge.style, source ? 'sourcePortConstraint' : 'targetPortConstraint', null)
edge.style,
source ? 'sourcePortConstraint' : 'targetPortConstraint',
null
)
); );
if (isNullish(value)) { if (isNullish(value)) {
@ -758,11 +726,7 @@ export const getPortConstraints = (
const directions = value.toString(); const directions = value.toString();
let returnValue = DIRECTION_MASK_NONE; let returnValue = DIRECTION_MASK_NONE;
const constraintRotationEnabled = getValue( const constraintRotationEnabled = getValue(terminal.style, 'portConstraintRotation', 0);
terminal.style,
'portConstraintRotation',
0
);
let rotation = 0; let rotation = 0;
if (constraintRotationEnabled == 1) { if (constraintRotationEnabled == 1) {
@ -876,11 +840,7 @@ export const reversePortConstraints = (constraint: number) => {
* Finds the index of the nearest segment on the given cell state for * Finds the index of the nearest segment on the given cell state for
* the specified coordinate pair. * the specified coordinate pair.
*/ */
export const findNearestSegment = ( export const findNearestSegment = (state: CellState, x: number, y: number) => {
state: CellState,
x: number,
y: number
) => {
let index = -1; let index = -1;
if (state.absolutePoints.length > 0) { if (state.absolutePoints.length > 0) {
@ -979,11 +939,7 @@ export const getDirectedBounds = (
* Returns the intersection between the polygon defined by the array of * Returns the intersection between the polygon defined by the array of
* points and the line between center and point. * points and the line between center and point.
*/ */
export const getPerimeterPoint = ( export const getPerimeterPoint = (pts: Point[], center: Point, point: Point) => {
pts: Point[],
center: Point,
point: Point
) => {
let min = null; let min = null;
for (let i = 0; i < pts.length - 1; i += 1) { for (let i = 0; i < pts.length - 1; i += 1) {
@ -1023,11 +979,7 @@ export const getPerimeterPoint = (
* p1 - <mxPoint> that represents the first point of the segment. * p1 - <mxPoint> that represents the first point of the segment.
* p2 - <mxPoint> that represents the second point of the segment. * p2 - <mxPoint> that represents the second point of the segment.
*/ */
export const rectangleIntersectsSegment = ( export const rectangleIntersectsSegment = (bounds: Rectangle, p1: Point, p2: Point) => {
bounds: Rectangle,
p1: Point,
p2: Point
) => {
const top = bounds.y; const top = bounds.y;
const left = bounds.x; const left = bounds.x;
const bottom = top + bounds.height; const bottom = top + bounds.height;
@ -1286,8 +1238,7 @@ export const getDocumentScrollOrigin = (doc: Document) => {
const y = const y =
wnd != null && window.pageYOffset !== undefined wnd != null && window.pageYOffset !== undefined
? window.pageYOffset ? window.pageYOffset
: (document.documentElement || document.body.parentNode || document.body) : (document.documentElement || document.body.parentNode || document.body).scrollTop;
.scrollTop;
return new Point(x, y); return new Point(x, y);
}; };
@ -1378,7 +1329,7 @@ export const convertPoint = (container: HTMLElement, x: number, y: number) => {
* *
* n - String representing the possibly numeric value. * n - String representing the possibly numeric value.
*/ */
export const isNumeric = (n: string) => { export const isNumeric = (n: any) => {
return ( return (
!Number.isNaN(parseFloat(n)) && !Number.isNaN(parseFloat(n)) &&
isFinite(+n) && isFinite(+n) &&
@ -1792,12 +1743,7 @@ export const removeAllStylenames = (style: string) => {
* key - Key of the style to be changed. * key - Key of the style to be changed.
* value - New value for the given key. * value - New value for the given key.
*/ */
export const setCellStyles = ( export const setCellStyles = (model: Model, cells: Cell[], key: string, value: any) => {
model: Model,
cells: Cell[],
key: string,
value: any
) => {
if (cells.length > 0) { if (cells.length > 0) {
model.beginUpdate(); model.beginUpdate();
try { try {
@ -1842,8 +1788,7 @@ export const setStyle = (style: string | null, key: string, value: any) => {
if (isValue) { if (isValue) {
style = `${key}=${value}${next < 0 ? ';' : style.substring(next)}`; style = `${key}=${value}${next < 0 ? ';' : style.substring(next)}`;
} else { } else {
style = style = next < 0 || next == style.length - 1 ? '' : style.substring(next + 1);
next < 0 || next == style.length - 1 ? '' : style.substring(next + 1);
} }
} else { } else {
const index = style.indexOf(`;${key}=`); const index = style.indexOf(`;${key}=`);
@ -1861,8 +1806,7 @@ export const setStyle = (style: string | null, key: string, value: any) => {
next < 0 ? ';' : style.substring(next) next < 0 ? ';' : style.substring(next)
}`; }`;
} else { } else {
style = style = style.substring(0, index) + (next < 0 ? ';' : style.substring(next));
style.substring(0, index) + (next < 0 ? ';' : style.substring(next));
} }
} }
} }
@ -2128,9 +2072,7 @@ export const getScaleForPageCount = (
} }
pageFormat = pageFormat =
pageFormat != null pageFormat != null ? pageFormat : new Rectangle(...PAGE_FORMAT_A4_PORTRAIT);
? pageFormat
: new Rectangle(...PAGE_FORMAT_A4_PORTRAIT);
const availablePageWidth = pageFormat.width - border * 2; const availablePageWidth = pageFormat.width - border * 2;
const availablePageHeight = pageFormat.height - border * 2; const availablePageHeight = pageFormat.height - border * 2;
@ -2202,8 +2144,7 @@ export const getScaleForPageCount = (
roundRowDownProportion = Math.floor(numRowPages - 1) / numRowPages; roundRowDownProportion = Math.floor(numRowPages - 1) / numRowPages;
} }
if (roundColumnDownProportion == 1) { if (roundColumnDownProportion == 1) {
roundColumnDownProportion = roundColumnDownProportion = Math.floor(numColumnPages - 1) / numColumnPages;
Math.floor(numColumnPages - 1) / numColumnPages;
} }
// Check which rounding down is smaller, but in the case of very small roundings // Check which rounding down is smaller, but in the case of very small roundings
@ -2279,13 +2220,11 @@ export const show = (
const dy = Math.ceil(y0 - bounds.y); const dy = Math.ceil(y0 - bounds.y);
if (w == null) { if (w == null) {
w = w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x);
Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x);
} }
if (h == null) { if (h == null) {
h = h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y);
Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y);
} }
doc.writeln('<html><head>'); doc.writeln('<html><head>');
@ -2322,7 +2261,7 @@ export const show = (
div.style.position = 'absolute'; div.style.position = 'absolute';
div.style.left = `${dx}px`; div.style.left = `${dx}px`;
div.style.top = `${dy}px`; div.style.top = `${dy}px`;
if (graph.container && graph.view.drawPane) { if (graph.container && graph.view.drawPane) {
let node = graph.container.firstChild; let node = graph.container.firstChild;
let svg: SVGElement | null = null; let svg: SVGElement | null = null;

View File

@ -8,6 +8,7 @@ import { arcToCurves, getRotatedPoint } from '../Utils';
import { import {
DEFAULT_FONTFAMILY, DEFAULT_FONTFAMILY,
DEFAULT_FONTSIZE, DEFAULT_FONTSIZE,
DIRECTION_EAST,
NONE, NONE,
SHADOWCOLOR, SHADOWCOLOR,
SHADOW_OFFSET_X, SHADOW_OFFSET_X,
@ -18,7 +19,15 @@ import mxUrlConverter from '../network/mxUrlConverter';
import Point from '../../view/geometry/Point'; import Point from '../../view/geometry/Point';
import { clone } from '../CloneUtils'; import { clone } from '../CloneUtils';
import type { CanvasState, ColorValue } from '../../types'; import type {
AlignValue,
CanvasState,
ColorValue,
DirectionValue,
OverflowValue,
TextDirectionValue,
VAlignValue,
} from '../../types';
/** /**
* Class: mxAbstractCanvas2D * Class: mxAbstractCanvas2D
@ -30,7 +39,7 @@ import type { CanvasState, ColorValue } from '../../types';
* *
* Constructs a new abstract canvas. * Constructs a new abstract canvas.
*/ */
class mxAbstractCanvas2D { class AbstractCanvas2D {
constructor() { constructor() {
/** /**
* Variable: converter * Variable: converter
@ -127,6 +136,9 @@ class mxAbstractCanvas2D {
*/ */
pointerEvents = false; pointerEvents = false;
// from Polyline (maybe from other shapes also)
pointerEventsValue: string | null = null;
/** /**
* Function: createUrlConverter * Function: createUrlConverter
* *
@ -159,12 +171,12 @@ class mxAbstractCanvas2D {
alpha: 1, alpha: 1,
fillAlpha: 1, fillAlpha: 1,
strokeAlpha: 1, strokeAlpha: 1,
fillColor: null, fillColor: NONE,
gradientFillAlpha: 1, gradientFillAlpha: 1,
gradientColor: null, gradientColor: NONE,
gradientAlpha: 1, gradientAlpha: 1,
gradientDirection: null, gradientDirection: DIRECTION_EAST,
strokeColor: null, strokeColor: NONE,
strokeWidth: 1, strokeWidth: 1,
dashed: false, dashed: false,
dashPattern: '3 3', dashPattern: '3 3',
@ -173,8 +185,8 @@ class mxAbstractCanvas2D {
lineJoin: 'miter', lineJoin: 'miter',
miterLimit: 10, miterLimit: 10,
fontColor: '#000000', fontColor: '#000000',
fontBackgroundColor: null, fontBackgroundColor: NONE,
fontBorderColor: null, fontBorderColor: NONE,
fontSize: DEFAULT_FONTSIZE, fontSize: DEFAULT_FONTSIZE,
fontFamily: DEFAULT_FONTFAMILY, fontFamily: DEFAULT_FONTFAMILY,
fontStyle: 0, fontStyle: 0,
@ -291,13 +303,7 @@ class mxAbstractCanvas2D {
* *
* Rotates the current state. * Rotates the current state.
*/ */
rotate( rotate(theta: number, flipH: boolean, flipV: boolean, cx: number, cy: number) {
theta: number,
flipH: boolean,
flipV: boolean,
cx: number,
cy: number
) {
// nop // nop
} }
@ -334,10 +340,8 @@ class mxAbstractCanvas2D {
* Sets the current fill color. * Sets the current fill color.
*/ */
setFillColor(value: ColorValue) { setFillColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.fillColor = value;
this.state.gradientColor = NONE;
this.state.fillColor = v;
this.state.gradientColor = null;
} }
/** /**
@ -352,7 +356,7 @@ class mxAbstractCanvas2D {
y: number, y: number,
w: number, w: number,
h: number, h: number,
direction: string | null, direction: DirectionValue,
alpha1 = 1, alpha1 = 1,
alpha2: number = 1 alpha2: number = 1
) { ) {
@ -370,8 +374,7 @@ class mxAbstractCanvas2D {
* Sets the current stroke color. * Sets the current stroke color.
*/ */
setStrokeColor(value: ColorValue) { setStrokeColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.strokeColor = value;
this.state.strokeColor = v;
} }
/** /**
@ -379,7 +382,7 @@ class mxAbstractCanvas2D {
* *
* Sets the current stroke width. * Sets the current stroke width.
*/ */
setStrokeWidth(value: number | null) { setStrokeWidth(value: number) {
this.state.strokeWidth = value; this.state.strokeWidth = value;
} }
@ -435,8 +438,7 @@ class mxAbstractCanvas2D {
* Sets the current font color. * Sets the current font color.
*/ */
setFontColor(value: ColorValue) { setFontColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.fontColor = value;
this.state.fontColor = v;
} }
/** /**
@ -445,8 +447,7 @@ class mxAbstractCanvas2D {
* Sets the current font background color. * Sets the current font background color.
*/ */
setFontBackgroundColor(value: ColorValue) { setFontBackgroundColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.fontBackgroundColor = value;
this.state.fontBackgroundColor = v;
} }
/** /**
@ -455,8 +456,7 @@ class mxAbstractCanvas2D {
* Sets the current font border color. * Sets the current font border color.
*/ */
setFontBorderColor(value: ColorValue) { setFontBorderColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.fontBorderColor = value;
this.state.fontBorderColor = v;
} }
/** /**
@ -501,8 +501,7 @@ class mxAbstractCanvas2D {
* Enables or disables and configures the current shadow. * Enables or disables and configures the current shadow.
*/ */
setShadowColor(value: ColorValue) { setShadowColor(value: ColorValue) {
const v = value === NONE ? null : value; this.state.shadowColor = value;
this.state.shadowColor = v;
} }
/** /**
@ -567,14 +566,7 @@ class mxAbstractCanvas2D {
* *
* Adds a bezier curve to the current path. * Adds a bezier curve to the current path.
*/ */
curveTo( curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) {
x1: number,
y1: number,
x2: number,
y2: number,
x3: number,
y3: number
) {
this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3); this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3);
} }
@ -624,14 +616,7 @@ class mxAbstractCanvas2D {
* *
* Closes the current path. * Closes the current path.
*/ */
close( close(x1?: number, y1?: number, x2?: number, y2?: number, x3?: number, y3?: number) {
x1?: number,
y1?: number,
x2?: number,
y2?: number,
x3?: number,
y3?: number
) {
this.addOp(this.closeOp); this.addOp(this.closeOp);
} }
@ -641,6 +626,59 @@ class mxAbstractCanvas2D {
* Empty implementation for backwards compatibility. This will be removed. * Empty implementation for backwards compatibility. This will be removed.
*/ */
end() {} end() {}
stroke() {}
fill() {}
fillAndStroke() {}
rect(x: number, y: number, w: number, h: number) {}
roundrect(x: number, y: number, w: number, h: number, r1: number, r2: number) {}
ellipse(x: number, y: number, w: number, h: number) {}
image(
x: number,
y: number,
w: number,
h: number,
src: string,
aspect = true,
flipH = false,
flipV = false
) {}
text(
x: number,
y: number,
w: number,
h: number,
str: string,
align: AlignValue,
valign: VAlignValue,
wrap: boolean,
format: string,
overflow: OverflowValue,
clip: boolean,
rotation = 0,
dir: TextDirectionValue
) {}
updateText(
x: number,
y: number,
w: number,
h: number,
align: AlignValue,
valign: VAlignValue,
wrap: boolean,
overflow: OverflowValue,
clip: boolean,
rotation = 0,
node: SVGElement
) {}
} }
export default mxAbstractCanvas2D; export default AbstractCanvas2D;

View File

@ -4,7 +4,7 @@
* Updated to ES9 syntax by David Morrissey 2021 * Updated to ES9 syntax by David Morrissey 2021
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import mxAbstractCanvas2D from './mxAbstractCanvas2D'; import mxAbstractCanvas2D from './AbstractCanvas2D';
import { import {
DEFAULT_FONTFAMILY, DEFAULT_FONTFAMILY,
DEFAULT_FONTSIZE, DEFAULT_FONTSIZE,
@ -916,21 +916,7 @@ class mxXmlCanvas2D extends mxAbstractCanvas2D {
* rotation - Number that specifies the angle of the rotation around the anchor point of the text. * rotation - Number that specifies the angle of the rotation around the anchor point of the text.
* dir - Optional string that specifies the text direction. Possible values are rtl and lrt. * dir - Optional string that specifies the text direction. Possible values are rtl and lrt.
*/ */
text( text(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) {
x,
y,
w,
h,
str,
align,
valign,
wrap,
format,
overflow,
clip,
rotation,
dir
) {
if (this.textEnabled && str != null) { if (this.textEnabled && str != null) {
if (isNode(str)) { if (isNode(str)) {
str = getOuterHtml(str); str = getOuterHtml(str);

View File

@ -5,7 +5,7 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Image from './image/Image'; import Image from './image/ImageBox';
import EventObject from './event/EventObject'; import EventObject from './event/EventObject';
import EventSource from './event/EventSource'; import EventSource from './event/EventSource';
import InternalEvent from './event/InternalEvent'; import InternalEvent from './event/InternalEvent';
@ -23,16 +23,16 @@ import CellRenderer from './cell/CellRenderer';
import CellEditor from './editing/CellEditor'; import CellEditor from './editing/CellEditor';
import Point from './geometry/Point'; import Point from './geometry/Point';
import { import {
getBoundingBox, getCurrentStyle, getBoundingBox,
getValue, hasScrollbars, parseCssNumber, getCurrentStyle,
getValue,
hasScrollbars,
parseCssNumber,
} from '../util/Utils'; } from '../util/Utils';
import Cell from './cell/datatypes/Cell'; import Cell from './cell/datatypes/Cell';
import Model from './model/Model'; import Model from './model/Model';
import Stylesheet from './style/Stylesheet'; import Stylesheet from './style/Stylesheet';
import { import { DIALECT_SVG, PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants';
DIALECT_SVG,
PAGE_FORMAT_A4_PORTRAIT,
} from '../util/Constants';
import ChildChange from './model/ChildChange'; import ChildChange from './model/ChildChange';
import GeometryChange from './geometry/GeometryChange'; import GeometryChange from './geometry/GeometryChange';
@ -42,12 +42,12 @@ import TerminalChange from './cell/edge/TerminalChange';
import ValueChange from './cell/ValueChange'; import ValueChange from './cell/ValueChange';
import CellState from './cell/datatypes/CellState'; import CellState from './cell/datatypes/CellState';
import { isNode } from '../util/DomUtils'; import { isNode } from '../util/DomUtils';
import CellArray from "./cell/datatypes/CellArray"; import CellArray from './cell/datatypes/CellArray';
import EdgeStyle from "./style/EdgeStyle"; import EdgeStyle from './style/EdgeStyle';
import EdgeHandler from "./cell/edge/EdgeHandler"; import EdgeHandler from './cell/edge/EdgeHandler';
import VertexHandler from "./cell/vertex/VertexHandler"; import VertexHandler from './cell/vertex/VertexHandler';
import EdgeSegmentHandler from "./cell/edge/EdgeSegmentHandler"; import EdgeSegmentHandler from './cell/edge/EdgeSegmentHandler';
import ElbowEdgeHandler from "./cell/edge/ElbowEdgeHandler"; import ElbowEdgeHandler from './cell/edge/ElbowEdgeHandler';
/** /**
* Extends {@link EventSource} to implement a graph component for * Extends {@link EventSource} to implement a graph component for
@ -83,9 +83,7 @@ class Graph extends EventSource {
this.model = model != null ? model : new Model(); this.model = model != null ? model : new Model();
this.cellRenderer = this.createCellRenderer(); this.cellRenderer = this.createCellRenderer();
this.setSelectionModel(this.createSelectionModel()); this.setSelectionModel(this.createSelectionModel());
this.setStylesheet( this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet());
stylesheet != null ? stylesheet : this.createStylesheet()
);
this.view = this.createGraphView(); this.view = this.createGraphView();
// Adds a graph model listener to update the view // Adds a graph model listener to update the view
@ -673,10 +671,7 @@ class Graph extends EventSource {
// Handles two special cases where the shape does not need to be // Handles two special cases where the shape does not need to be
// recreated from scratch, it only needs to be invalidated. // recreated from scratch, it only needs to be invalidated.
else if ( else if (change instanceof TerminalChange || change instanceof GeometryChange) {
change instanceof TerminalChange ||
change instanceof GeometryChange
) {
// Checks if the geometry has changed to avoid unnessecary revalidation // Checks if the geometry has changed to avoid unnessecary revalidation
if ( if (
change instanceof TerminalChange || change instanceof TerminalChange ||
@ -807,21 +802,13 @@ class Graph extends EventSource {
return new Rectangle( return new Rectangle(
parseCssNumber(css.paddingLeft) + parseCssNumber(css.paddingLeft) +
(css.borderLeftStyle != 'none' (css.borderLeftStyle != 'none' ? parseCssNumber(css.borderLeftWidth) : 0),
? parseCssNumber(css.borderLeftWidth)
: 0),
parseCssNumber(css.paddingTop) + parseCssNumber(css.paddingTop) +
(css.borderTopStyle != 'none' (css.borderTopStyle != 'none' ? parseCssNumber(css.borderTopWidth) : 0),
? parseCssNumber(css.borderTopWidth)
: 0),
parseCssNumber(css.paddingRight) + parseCssNumber(css.paddingRight) +
(css.borderRightStyle != 'none' (css.borderRightStyle != 'none' ? parseCssNumber(css.borderRightWidth) : 0),
? parseCssNumber(css.borderRightWidth)
: 0),
parseCssNumber(css.paddingBottom) + parseCssNumber(css.paddingBottom) +
(css.borderBottomStyle != 'none' (css.borderBottomStyle != 'none' ? parseCssNumber(css.borderBottomWidth) : 0)
? parseCssNumber(css.borderBottomWidth)
: 0)
); );
} }
@ -909,8 +896,7 @@ class Graph extends EventSource {
if (this.container != null) { if (this.container != null) {
// Adds spacing and border from css // Adds spacing and border from css
const cssBorder = this.getBorderSizes(); const cssBorder = this.getBorderSizes();
let w1: number = let w1: number = this.container.offsetWidth - cssBorder.x - cssBorder.width - 1;
this.container.offsetWidth - cssBorder.x - cssBorder.width - 1;
let h1: number = let h1: number =
maxHeight != null maxHeight != null
? maxHeight ? maxHeight
@ -962,19 +948,13 @@ class Graph extends EventSource {
const x0 = const x0 =
bounds.x != null bounds.x != null
? Math.floor( ? Math.floor(
this.view.translate.x - this.view.translate.x - bounds.x / s + border / s2 + margin / 2
bounds.x / s +
border / s2 +
margin / 2
) )
: border; : border;
const y0 = const y0 =
bounds.y != null bounds.y != null
? Math.floor( ? Math.floor(
this.view.translate.y - this.view.translate.y - bounds.y / s + border / s2 + margin / 2
bounds.y / s +
border / s2 +
margin / 2
) )
: border; : border;
@ -1026,9 +1006,7 @@ class Graph extends EventSource {
* *
* @param state {@link mxCellState} whose handler should be created. * @param state {@link mxCellState} whose handler should be created.
*/ */
createHandler( createHandler(state: CellState): mxEdgeHandler | VertexHandler | null {
state: CellState
): mxEdgeHandler | VertexHandler | null {
let result: mxEdgeHandler | VertexHandler | null = null; let result: mxEdgeHandler | VertexHandler | null = null;
if (state.cell.isEdge()) { if (state.cell.isEdge()) {
@ -1242,7 +1220,6 @@ class Graph extends EventSource {
cx: number = 0.5, cx: number = 0.5,
cy: number = 0.5 cy: number = 0.5
): void { ): void {
const container = <HTMLElement>this.container; const container = <HTMLElement>this.container;
const _hasScrollbars = hasScrollbars(this.container); const _hasScrollbars = hasScrollbars(this.container);
const padding = 2 * this.getBorder(); const padding = 2 * this.getBorder();

View File

@ -19,10 +19,10 @@ import graph from './Graph';
import ImageShape from './geometry/shape/node/ImageShape'; import ImageShape from './geometry/shape/node/ImageShape';
import InternalEvent from './event/InternalEvent'; import InternalEvent from './event/InternalEvent';
import utils from '../util/Utils'; import utils from '../util/Utils';
import Image from './image/Image'; import Image from './image/ImageBox';
import EventObject from './event/EventObject'; import EventObject from './event/EventObject';
import { getSource, isMouseEvent } from '../util/EventUtils'; import { getSource, isMouseEvent } from '../util/EventUtils';
import EventSource from "./event/EventSource"; import EventSource from './event/EventSource';
/** /**
* @class Outline * @class Outline
@ -510,10 +510,8 @@ class Outline {
); );
// Adds the scrollbar offset to the finder // Adds the scrollbar offset to the finder
this.bounds.x += this.bounds.x += (this.source.container.scrollLeft * navView.scale) / scale;
(this.source.container.scrollLeft * navView.scale) / scale; this.bounds.y += (this.source.container.scrollTop * navView.scale) / scale;
this.bounds.y +=
(this.source.container.scrollTop * navView.scale) / scale;
const selectionBorder = <RectangleShape>this.selectionBorder; const selectionBorder = <RectangleShape>this.selectionBorder;
let b = <Rectangle>selectionBorder.bounds; let b = <Rectangle>selectionBorder.bounds;
@ -567,12 +565,7 @@ class Outline {
const tol = !isMouseEvent(me.getEvent()) ? this.source.tolerance : 0; const tol = !isMouseEvent(me.getEvent()) ? this.source.tolerance : 0;
const hit = const hit =
tol > 0 tol > 0
? new Rectangle( ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol)
me.getGraphX() - tol,
me.getGraphY() - tol,
2 * tol,
2 * tol
)
: null; : null;
this.zoom = this.zoom =
me.isSource(this.sizer) || me.isSource(this.sizer) ||
@ -691,10 +684,7 @@ class Outline {
* ``` * ```
*/ */
getTranslateForEvent(me: InternalMouseEvent): Point { getTranslateForEvent(me: InternalMouseEvent): Point {
return new Point( return new Point(me.getX() - <number>this.startX, me.getY() - <number>this.startY);
me.getX() - <number>this.startX,
me.getY() - <number>this.startY
);
} }
/** /**
@ -713,10 +703,7 @@ class Outline {
if (!this.zoom) { if (!this.zoom) {
// Applies the new translation if the source // Applies the new translation if the source
// has no scrollbars // has no scrollbars
if ( if (!source.useScrollbarsForPanning || !utils.hasScrollbars(source.container)) {
!source.useScrollbarsForPanning ||
!utils.hasScrollbars(source.container)
) {
source.panGraph(0, 0); source.panGraph(0, 0);
dx /= outline.getView().scale; dx /= outline.getView().scale;
dy /= outline.getView().scale; dy /= outline.getView().scale;
@ -727,10 +714,7 @@ class Outline {
// Applies the new zoom // Applies the new zoom
const w = (<Rectangle>selectionBorder.bounds).width; const w = (<Rectangle>selectionBorder.bounds).width;
const { scale } = source.getView(); const { scale } = source.getView();
source.zoomTo( source.zoomTo(Math.max(this.minScale, scale - (dx * scale) / w), false);
Math.max(this.minScale, scale - (dx * scale) / w),
false
);
} }
this.update(); this.update();
@ -752,11 +736,7 @@ class Outline {
this.source.removeListener(this.refreshHandler); this.source.removeListener(this.refreshHandler);
this.source.getModel().removeListener(this.updateHandler); this.source.getModel().removeListener(this.updateHandler);
this.source.getView().removeListener(this.updateHandler); this.source.getView().removeListener(this.updateHandler);
InternalEvent.removeListener( InternalEvent.removeListener(this.source.container, 'scroll', this.updateHandler);
this.source.container,
'scroll',
this.updateHandler
);
// @ts-ignore // @ts-ignore
this.source = null; this.source = null;
} }

View File

@ -15,7 +15,7 @@ import {
import CellHighlight from '../selection/CellHighlight'; import CellHighlight from '../selection/CellHighlight';
import EventObject from '../event/EventObject'; import EventObject from '../event/EventObject';
import InternalEvent from '../event/InternalEvent'; import InternalEvent from '../event/InternalEvent';
import utils, { intersectsHotspot } from '../../util/Utils'; import utils, { intersectsHotspot, isNumeric } from '../../util/Utils';
import graph from '../Graph'; import graph from '../Graph';
import { ColorValue } from '../../types'; import { ColorValue } from '../../types';
import CellState from './datatypes/CellState'; import CellState from './datatypes/CellState';
@ -274,9 +274,9 @@ class CellMarker extends EventSource {
* Sets and marks the current valid state. * Sets and marks the current valid state.
*/ */
setCurrentState( setCurrentState(
state: CellState, state: CellState | null,
me: InternalMouseEvent, me: InternalMouseEvent,
color: ColorValue = null color: ColorValue | null = null
) { ) {
const isValid = state ? this.isValidState(state) : false; const isValid = state ? this.isValidState(state) : false;
color = color ?? this.getMarkerColor(me.getEvent(), state, isValid); color = color ?? this.getMarkerColor(me.getEvent(), state, isValid);
@ -305,8 +305,7 @@ class CellMarker extends EventSource {
* *
* Marks the given cell using the given color, or <validColor> if no color is specified. * Marks the given cell using the given color, or <validColor> if no color is specified.
*/ */
markCell(cell: Cell, markCell(cell: Cell, color: ColorValue) {
color: ColorValue) {
const state = this.graph.getView().getState(cell); const state = this.graph.getView().getState(cell);
if (state) { if (state) {
@ -353,9 +352,7 @@ class CellMarker extends EventSource {
* Returns the valid- or invalidColor depending on the value of isValid. * Returns the valid- or invalidColor depending on the value of isValid.
* The given <mxCellState> is ignored by this implementation. * The given <mxCellState> is ignored by this implementation.
*/ */
getMarkerColor(evt: Event, getMarkerColor(evt: Event, state: CellState | null, isValid: boolean) {
state: CellState,
isValid: boolean): string {
return isValid ? this.validColor : this.invalidColor; return isValid ? this.validColor : this.invalidColor;
} }
@ -379,7 +376,7 @@ class CellMarker extends EventSource {
* Returns the <mxCell> for the given event and cell. This returns the * Returns the <mxCell> for the given event and cell. This returns the
* given cell. * given cell.
*/ */
getCell(me: InternalMouseEvent): Cell { getCell(me: InternalMouseEvent) {
return me.getCell(); return me.getCell();
} }
@ -400,17 +397,21 @@ class CellMarker extends EventSource {
* This returns true if the <hotspot> is 0 or the coordinates are inside * This returns true if the <hotspot> is 0 or the coordinates are inside
* the hotspot for the given cell state. * the hotspot for the given cell state.
*/ */
intersects(state: CellState, me: InternalMouseEvent): boolean { intersects(state: CellState, me: InternalMouseEvent) {
if (this.hotspotEnabled) { const x = me.getGraphX();
const y = me.getGraphY();
if (this.hotspotEnabled && isNumeric(x) && isNumeric(y)) {
return intersectsHotspot( return intersectsHotspot(
state, state,
me.getGraphX(), x as number,
me.getGraphY(), y as number,
this.hotspot, this.hotspot,
MIN_HOTSPOT_SIZE, MIN_HOTSPOT_SIZE,
MAX_HOTSPOT_SIZE MAX_HOTSPOT_SIZE
); );
} }
return true; return true;
} }
@ -419,9 +420,7 @@ class CellMarker extends EventSource {
* *
* Destroys the handler and all its resources and DOM nodes. * Destroys the handler and all its resources and DOM nodes.
*/ */
destroy(): void { destroy() {
this.graph.getView().removeListener(this.resetHandler);
this.graph.getModel().removeListener(this.resetHandler);
this.highlight.destroy(); this.highlight.destroy();
} }
} }

View File

@ -8,8 +8,8 @@
import Point from '../geometry/Point'; import Point from '../geometry/Point';
import Rectangle from '../geometry/Rectangle'; import Rectangle from '../geometry/Rectangle';
import EventSource from '../event/EventSource'; import EventSource from '../event/EventSource';
import Image from '../image/Image'; import ImageBox from '../image/ImageBox';
import CellState from "./datatypes/CellState"; import CellState from './datatypes/CellState';
/** /**
* Class: mxCellOverlay * Class: mxCellOverlay
@ -66,12 +66,14 @@ import CellState from "./datatypes/CellState";
* (default). * (default).
*/ */
class CellOverlay extends EventSource { class CellOverlay extends EventSource {
constructor(image: Image, constructor(
tooltip: string | null=null, image: ImageBox,
align: string='right', tooltip: string | null = null,
verticalAlign: string='bottom', align: string = 'right',
offset: Point=new Point(), verticalAlign: string = 'bottom',
cursor: string='help') { offset: Point = new Point(),
cursor: string = 'help'
) {
super(); super();
this.image = image; this.image = image;
@ -85,14 +87,14 @@ class CellOverlay extends EventSource {
* *
* Holds the <mxImage> to be used as the icon. * Holds the <mxImage> to be used as the icon.
*/ */
image: Image | null = null; image: ImageBox;
/** /**
* Variable: tooltip * Variable: tooltip
* *
* Holds the optional string to be used as the tooltip. * Holds the optional string to be used as the tooltip.
*/ */
tooltip?: string | null = null; tooltip?: string | null;
/** /**
* Variable: align * Variable: align
@ -118,14 +120,14 @@ class CellOverlay extends EventSource {
* Holds the offset as an <mxPoint>. The offset will be scaled according to the * Holds the offset as an <mxPoint>. The offset will be scaled according to the
* current scale. * current scale.
*/ */
offset: Point = new Point(); offset = new Point();
/** /**
* Variable: cursor * Variable: cursor
* *
* Holds the cursor for the overlay. Default is 'help'. * Holds the cursor for the overlay. Default is 'help'.
*/ */
cursor: string = 'help'; cursor = 'help';
/** /**
* Variable: defaultOverlap * Variable: defaultOverlap
@ -133,7 +135,7 @@ class CellOverlay extends EventSource {
* Defines the overlapping for the overlay, that is, the proportional distance * Defines the overlapping for the overlay, that is, the proportional distance
* from the origin to the point defined by the alignment. Default is 0.5. * from the origin to the point defined by the alignment. Default is 0.5.
*/ */
defaultOverlap: number = 0.5; defaultOverlap = 0.5;
/** /**
* Function: getBounds * Function: getBounds
@ -173,7 +175,7 @@ class CellOverlay extends EventSource {
const s = state.view.scale; const s = state.view.scale;
let pt = null; let pt = null;
const image = <Image>this.image; const image = this.image;
const w = image.width; const w = image.width;
const h = image.height; const h = image.height;

View File

@ -286,7 +286,7 @@ class CellRenderer {
if (shape) { if (shape) {
shape.apply(state); shape.apply(state);
shape.image = state.getImage(); shape.imageSrc = state.getImage();
shape.indicatorColor = state.getIndicatorColor(); shape.indicatorColor = state.getIndicatorColor();
shape.indicatorStrokeColor = state.style.indicatorStrokeColor; shape.indicatorStrokeColor = state.style.indicatorStrokeColor;
shape.indicatorGradientColor = state.getIndicatorGradientColor(); shape.indicatorGradientColor = state.getIndicatorGradientColor();
@ -371,11 +371,7 @@ class CellRenderer {
} else if (value === 'indicated' && state.shape != null) { } else if (value === 'indicated' && state.shape != null) {
// @ts-ignore // @ts-ignore
shape[field] = state.shape.indicatorColor; shape[field] = state.shape.indicatorColor;
} else if ( } else if (key !== 'fillColor' && value === 'fillColor' && state.shape != null) {
key !== 'fillColor' &&
value === 'fillColor' &&
state.shape != null
) {
// @ts-ignore // @ts-ignore
shape[field] = state.style.fillColor; shape[field] = state.style.fillColor;
} else if ( } else if (
@ -465,9 +461,7 @@ class CellRenderer {
); );
state.text.opacity = state.text.opacity =
state.style.textOpacity == null ? 100 : state.style.textOpacity; state.style.textOpacity == null ? 100 : state.style.textOpacity;
state.text.dialect = isForceHtml state.text.dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
? DIALECT_STRICTHTML
: state.view.graph.dialect;
state.text.style = state.style; state.text.style = state.style;
state.text.state = state; state.text.state = state;
this.initializeLabel(state, state.text); this.initializeLabel(state, state.text);
@ -489,9 +483,7 @@ class CellRenderer {
// Dispatches the drop event to the graph which // Dispatches the drop event to the graph which
// consumes and executes the source function // consumes and executes the source function
const pt = convertPoint(graph.container, x, y); const pt = convertPoint(graph.container, x, y);
result = <CellState>( result = <CellState>graph.view.getState(graph.getCellAt(pt.x, pt.y));
graph.view.getState(graph.getCellAt(pt.x, pt.y))
);
} }
return result; return result;
}; };
@ -506,8 +498,7 @@ class CellRenderer {
new InternalMouseEvent(evt, state) new InternalMouseEvent(evt, state)
); );
forceGetCell = forceGetCell =
graph.dialect !== DIALECT_SVG && graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG';
getSource(evt).nodeName === 'IMG';
} }
}, },
(evt: MouseEvent) => { (evt: MouseEvent) => {
@ -576,8 +567,7 @@ class CellRenderer {
dict = new Dictionary(); dict = new Dictionary();
for (let i = 0; i < overlays.length; i += 1) { for (let i = 0; i < overlays.length; i += 1) {
const shape = const shape = state.overlays != null ? state.overlays.remove(overlays[i]) : null;
state.overlays != null ? state.overlays.remove(overlays[i]) : null;
if (shape == null) { if (shape == null) {
const tmp = new ImageShape( const tmp = new ImageShape(
@ -645,7 +635,7 @@ class CellRenderer {
} }
overlay.fireEvent( overlay.fireEvent(
new EventObject(InternalEvent.CLICK, {event: evt, cell: state.cell}) new EventObject(InternalEvent.CLICK, { event: evt, cell: state.cell })
); );
}); });
@ -655,14 +645,17 @@ class CellRenderer {
InternalEvent.consume(evt); InternalEvent.consume(evt);
}, },
(evt: Event) => { (evt: Event) => {
graph.event.fireMouseEvent(InternalEvent.MOUSE_MOVE, new InternalMouseEvent(evt, state)); graph.event.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt, state)
);
} }
); );
if (mxClient.IS_TOUCH) { if (mxClient.IS_TOUCH) {
InternalEvent.addListener(shape.node, 'touchend', (evt: Event) => { InternalEvent.addListener(shape.node, 'touchend', (evt: Event) => {
overlay.fireEvent( overlay.fireEvent(
new EventObject(InternalEvent.CLICK, {event: evt, cell: state.cell}) new EventObject(InternalEvent.CLICK, { event: evt, cell: state.cell })
); );
}); });
} }
@ -746,9 +739,7 @@ class CellRenderer {
// should go into the graph container directly in order to be clickable. Otherwise // should go into the graph container directly in order to be clickable. Otherwise
// it is obscured by the HTML label that overlaps the cell. // it is obscured by the HTML label that overlaps the cell.
const isForceHtml = const isForceHtml =
graph.isHtmlLabel(state.cell) && graph.isHtmlLabel(state.cell) && mxClient.NO_FO && graph.dialect === DIALECT_SVG;
mxClient.NO_FO &&
graph.dialect === DIALECT_SVG;
if (isForceHtml) { if (isForceHtml) {
control.dialect = DIALECT_PREFERHTML; control.dialect = DIALECT_PREFERHTML;
@ -791,7 +782,10 @@ class CellRenderer {
); );
}, },
(evt: Event) => { (evt: Event) => {
graph.event.fireMouseEvent(InternalEvent.MOUSE_UP, new InternalMouseEvent(evt, state)); graph.event.fireMouseEvent(
InternalEvent.MOUSE_UP,
new InternalMouseEvent(evt, state)
);
InternalEvent.consume(evt); InternalEvent.consume(evt);
} }
); );
@ -833,8 +827,7 @@ class CellRenderer {
* state - <mxCellState> whose shape fired the event. * state - <mxCellState> whose shape fired the event.
* evt - Mouse event which was fired. * evt - Mouse event which was fired.
*/ */
isShapeEvent(state: CellState, isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
evt: InternalMouseEvent | MouseEvent): boolean {
return true; return true;
} }
@ -849,8 +842,7 @@ class CellRenderer {
* state - <mxCellState> whose label fired the event. * state - <mxCellState> whose label fired the event.
* evt - Mouse event which was fired. * evt - Mouse event which was fired.
*/ */
isLabelEvent(state: CellState, isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean {
evt: InternalMouseEvent | MouseEvent): boolean {
return true; return true;
} }
@ -938,15 +930,13 @@ class CellRenderer {
* *
* state - <mxCellState> whose label should be redrawn. * state - <mxCellState> whose label should be redrawn.
*/ */
redrawLabel(state: CellState, redrawLabel(state: CellState, forced: boolean): void {
forced: boolean): void {
const { graph } = state.view; const { graph } = state.view;
const value = this.getLabelValue(state); const value = this.getLabelValue(state);
const wrapping = graph.isWrapping(state.cell); const wrapping = graph.isWrapping(state.cell);
const clipping = graph.isLabelClipped(state.cell); const clipping = graph.isLabelClipped(state.cell);
const isForceHtml = const isForceHtml =
state.view.graph.isHtmlLabel(state.cell) || state.view.graph.isHtmlLabel(state.cell) || (value != null && isNode(value));
(value != null && isNode(value));
const dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect; const dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect;
const overflow = state.style.overflow || 'visible'; const overflow = state.style.overflow || 'visible';
@ -961,11 +951,7 @@ class CellRenderer {
state.text = null; state.text = null;
} }
if ( if (state.text == null && value != null && (isNode(value) || value.length > 0)) {
state.text == null &&
value != null &&
(isNode(value) || value.length > 0)
) {
this.createLabel(state, value); this.createLabel(state, value);
} else if (state.text != null && (value == null || value.length == 0)) { } else if (state.text != null && (value == null || value.length == 0)) {
state.text.destroy(); state.text.destroy();
@ -977,10 +963,7 @@ class CellRenderer {
// result in getLabelBounds we apply the new style to the shape // result in getLabelBounds we apply the new style to the shape
if (forced) { if (forced) {
// Checks if a full repaint is needed // Checks if a full repaint is needed
if ( if (state.text.lastValue != null && this.isTextShapeInvalid(state, state.text)) {
state.text.lastValue != null &&
this.isTextShapeInvalid(state, state.text)
) {
// Forces a full repaint // Forces a full repaint
state.text.lastValue = null; state.text.lastValue = null;
} }
@ -1035,8 +1018,7 @@ class CellRenderer {
* state - <mxCellState> whose label should be checked. * state - <mxCellState> whose label should be checked.
* shape - <mxText> shape to be checked. * shape - <mxText> shape to be checked.
*/ */
isTextShapeInvalid(state: CellState, isTextShapeInvalid(state: CellState, shape: TextShape): boolean {
shape: TextShape): boolean {
function check(property: string, stylename: string, defaultValue: any) { function check(property: string, stylename: string, defaultValue: any) {
let result = false; let result = false;
@ -1049,8 +1031,7 @@ class CellRenderer {
) { ) {
result = result =
// @ts-ignore // @ts-ignore
parseFloat(String(shape[property])) - parseFloat(String(shape[property])) - parseFloat(String(shape.spacing)) !==
parseFloat(String(shape.spacing)) !==
(state.style[stylename] || defaultValue); (state.style[stylename] || defaultValue);
} else { } else {
// @ts-ignore // @ts-ignore
@ -1119,10 +1100,7 @@ class CellRenderer {
const { graph } = state.view; const { graph } = state.view;
const { scale } = state.view; const { scale } = state.view;
const isEdge = state.cell.isEdge(); const isEdge = state.cell.isEdge();
let bounds = new Rectangle( let bounds = new Rectangle(state.absoluteOffset.x, state.absoluteOffset.y);
state.absoluteOffset.x,
state.absoluteOffset.y
);
if (isEdge) { if (isEdge) {
// @ts-ignore // @ts-ignore
@ -1273,8 +1251,7 @@ class CellRenderer {
* *
* state - <mxCellState> whose overlays should be redrawn. * state - <mxCellState> whose overlays should be redrawn.
*/ */
redrawCellOverlays(state: CellState, redrawCellOverlays(state: CellState, forced: boolean = false): void {
forced: boolean = false): void {
this.createCellOverlays(state); this.createCellOverlays(state);
if (state.overlays != null) { if (state.overlays != null) {
@ -1329,8 +1306,7 @@ class CellRenderer {
* *
* state - <mxCellState> whose control should be redrawn. * state - <mxCellState> whose control should be redrawn.
*/ */
redrawControl(state: CellState, redrawControl(state: CellState, forced: boolean = false): void {
forced: boolean = false): void {
const image = state.view.graph.getFoldingImage(state); const image = state.view.graph.getFoldingImage(state);
if (state.control != null && image != null) { if (state.control != null && image != null) {
@ -1364,11 +1340,7 @@ class CellRenderer {
* Returns the bounds to be used to draw the control (folding icon) of the * Returns the bounds to be used to draw the control (folding icon) of the
* given state. * given state.
*/ */
getControlBounds( getControlBounds(state: CellState, w: number, h: number): Rectangle | null {
state: CellState,
w: number,
h: number
): Rectangle | null {
if (state.control != null) { if (state.control != null) {
const s = state.view.scale; const s = state.view.scale;
let cx = state.getCenterX(); let cx = state.getCenterX();
@ -1471,10 +1443,7 @@ class CellRenderer {
if (shapeNode.parentNode === state.view.graph.container) { if (shapeNode.parentNode === state.view.graph.container) {
let { canvas } = state.view; let { canvas } = state.view;
while ( while (canvas != null && canvas.parentNode !== state.view.graph.container) {
canvas != null &&
canvas.parentNode !== state.view.graph.container
) {
// @ts-ignore // @ts-ignore
canvas = canvas.parentNode; canvas = canvas.parentNode;
} }
@ -1482,10 +1451,7 @@ class CellRenderer {
if (canvas != null && canvas.nextSibling != null) { if (canvas != null && canvas.nextSibling != null) {
if (canvas.nextSibling !== shapeNode) { if (canvas.nextSibling !== shapeNode) {
// @ts-ignore // @ts-ignore
shapeNode.parentNode.insertBefore( shapeNode.parentNode.insertBefore(shapeNode, canvas.nextSibling);
shapeNode,
canvas.nextSibling
);
} }
} else { } else {
// @ts-ignore // @ts-ignore
@ -1497,10 +1463,7 @@ class CellRenderer {
shapeNode.parentNode.firstChild != shapeNode shapeNode.parentNode.firstChild != shapeNode
) { ) {
// Inserts the node as the first child of the parent to implement the order // Inserts the node as the first child of the parent to implement the order
shapeNode.parentNode.insertBefore( shapeNode.parentNode.insertBefore(shapeNode, shapeNode.parentNode.firstChild);
shapeNode,
shapeNode.parentNode.firstChild
);
} }
} }
@ -1527,9 +1490,7 @@ class CellRenderer {
* *
* state - <mxCellState> whose shapes should be returned. * state - <mxCellState> whose shapes should be returned.
*/ */
getShapesForState( getShapesForState(state: CellState): [Shape | null, TextShape | null, Shape | null] {
state: CellState
): [Shape | null, TextShape | null, Shape | null] {
return [state.shape, state.text, state.control]; return [state.shape, state.text, state.control];
} }
@ -1549,11 +1510,7 @@ class CellRenderer {
* be drawn into the DOM. If this is false then redraw and/or reconfigure * be drawn into the DOM. If this is false then redraw and/or reconfigure
* will not be called on the shape. * will not be called on the shape.
*/ */
redraw( redraw(state: CellState, force: boolean = false, rendering: boolean = true): void {
state: CellState,
force: boolean = false,
rendering: boolean = true
): void {
const shapeChanged = this.redrawShape(state, force, rendering); const shapeChanged = this.redrawShape(state, force, rendering);
if (state.shape != null && rendering) { if (state.shape != null && rendering) {
@ -1628,8 +1585,7 @@ class CellRenderer {
// Updates indicator shape // Updates indicator shape
if ( if (
state.shape != null && state.shape != null &&
state.shape.indicatorShape != state.shape.indicatorShape != this.getShape(state.getIndicatorShape())
this.getShape(state.getIndicatorShape())
) { ) {
if (state.shape.indicator != null) { if (state.shape.indicator != null) {
state.shape.indicator.destroy(); state.shape.indicator.destroy();
@ -1658,12 +1614,7 @@ class CellRenderer {
state.shape.bounds = null; state.shape.bounds = null;
} else { } else {
state.shape.points = null; state.shape.points = null;
state.shape.bounds = new Rectangle( state.shape.bounds = new Rectangle(state.x, state.y, state.width, state.height);
state.x,
state.y,
state.width,
state.height
);
} }
state.shape.scale = state.view.scale; state.shape.scale = state.view.scale;
@ -1700,8 +1651,7 @@ class CellRenderer {
shape.bounds == null || shape.bounds == null ||
shape.scale !== state.view.scale || shape.scale !== state.view.scale ||
(state.absolutePoints == null && !shape.bounds.equals(state)) || (state.absolutePoints == null && !shape.bounds.equals(state)) ||
(state.absolutePoints != null && (state.absolutePoints != null && !equalPoints(shape.points, state.absolutePoints))
!equalPoints(shape.points, state.absolutePoints))
); );
} }

View File

@ -13,16 +13,17 @@ import Dictionary from '../../util/Dictionary';
import GraphView from '../view/GraphView'; import GraphView from '../view/GraphView';
import Cell from './datatypes/Cell'; import Cell from './datatypes/Cell';
import CellState from './datatypes/CellState'; import CellState from './datatypes/CellState';
import Shape from "../geometry/shape/Shape"; import Shape from '../geometry/shape/Shape';
import graph from "../Graph"; import graph from '../Graph';
import CellArray from './datatypes/CellArray';
class TemporaryCellStates { class TemporaryCellStates {
constructor( constructor(
view: GraphView, view: GraphView,
scale: number = 1, scale: number = 1,
cells: Cell[], cells: CellArray,
isCellVisibleFn: Function | null = null, isCellVisibleFn: Function | null = null,
getLinkForCellState: Function | null = null getLinkForCellState: Function | null = null
) { ) {
this.view = view; this.view = view;
@ -31,17 +32,17 @@ class TemporaryCellStates {
this.oldBounds = view.getGraphBounds(); this.oldBounds = view.getGraphBounds();
this.oldStates = view.getStates(); this.oldStates = view.getStates();
this.oldScale = view.getScale(); this.oldScale = view.getScale();
this.oldDoRedrawShape = (<graph>view.graph).cellRenderer.doRedrawShape; this.oldDoRedrawShape = view.graph.cellRenderer.doRedrawShape;
const self = this; const self = this;
// Overrides doRedrawShape and paint shape to add links on shapes // Overrides doRedrawShape and paint shape to add links on shapes
if (getLinkForCellState != null) { if (getLinkForCellState != null) {
(<graph>view.graph).cellRenderer.doRedrawShape = (state: CellState) => { view.graph.cellRenderer.doRedrawShape = (state: CellState) => {
const shape = <Shape>state?.shape; const shape = <Shape>state?.shape;
const oldPaint = shape.paint; const oldPaint = shape.paint;
shape.paint = c => { shape.paint = (c) => {
const link = getLinkForCellState(state); const link = getLinkForCellState(state);
if (link != null) { if (link != null) {
c.setLink(link); c.setLink(link);
@ -52,7 +53,7 @@ class TemporaryCellStates {
} }
}; };
(<Function>self.oldDoRedrawShape).apply((<graph>view.graph).cellRenderer, [state]); (<Function>self.oldDoRedrawShape).apply(view.graph.cellRenderer, [state]);
shape.paint = oldPaint; shape.paint = oldPaint;
}; };
} }
@ -66,70 +67,53 @@ class TemporaryCellStates {
}; };
// Creates space for new states // Creates space for new states
view.setStates(new mxDictionary()); view.setStates(new Dictionary());
view.setScale(scale); view.setScale(scale);
if (cells != null) { view.resetValidationState();
view.resetValidationState(); let bbox = null;
let bbox = null;
// Validates the vertices and edges without adding them to // Validates the vertices and edges without adding them to
// the model so that the original cells are not modified // the model so that the original cells are not modified
for (const cell of cells) { for (const cell of cells) {
const bounds = view.getBoundingBox( const bounds = view.getBoundingBox(
view.validateCellState(<Cell>view.validateCell(<Cell>cell)) view.validateCellState(<Cell>view.validateCell(<Cell>cell))
); );
if (bbox == null) { if (bbox == null) {
bbox = bounds; bbox = bounds;
} else { } else {
bbox.add(bounds); bbox.add(<Rectangle>bounds);
}
} }
view.setGraphBounds(bbox || new Rectangle());
} }
view.setGraphBounds(bbox || new Rectangle());
} }
oldValidateCellState: Function | null; oldValidateCellState: Function | null;
oldDoRedrawShape: Function | null; oldDoRedrawShape: Function | null;
/** view: GraphView;
* Holds the width of the rectangle.
* @default 0
*/
// view: number;
view: GraphView | null = null;
/** /**
* Holds the height of the rectangle. * Holds the states of the rectangle.
* @default 0
*/ */
// oldStates: number; oldStates: Dictionary<string, CellState>;
oldStates: Dictionary | null = null;
/** /**
* Holds the height of the rectangle. * Holds the bounds of the rectangle.
* @default 0
*/ */
// oldBounds: number; oldBounds: Rectangle;
oldBounds: Rectangle | null = null;
/** /**
* Holds the height of the rectangle. * Holds the scale of the rectangle.
* @default 0
*/ */
oldScale: number = 0; oldScale: number;
/**
* Holds the height of the rectangle.
* @default 0
*/
// destroy(): void;
destroy(): void { destroy(): void {
const view = <GraphView>this.view; const view = this.view;
view.setScale(this.oldScale); view.setScale(this.oldScale);
view.setStates(this.oldStates); view.setStates(this.oldStates);
view.setGraphBounds(<Rectangle>this.oldBounds); view.setGraphBounds(this.oldBounds);
// @ts-ignore // @ts-ignore
view.validateCellState = <Function>this.oldValidateCellState; view.validateCellState = <Function>this.oldValidateCellState;
// @ts-ignore // @ts-ignore

View File

@ -1,6 +1,6 @@
import Cell from "./Cell"; import Cell from './Cell';
import Dictionary from "../../../util/Dictionary"; import Dictionary from '../../../util/Dictionary';
import mxObjectIdentity from "../../../util/mxObjectIdentity"; import ObjectIdentity from '../../../util/ObjectIdentity';
class CellArray extends Array<Cell> { class CellArray extends Array<Cell> {
// @ts-ignore // @ts-ignore
@ -25,12 +25,12 @@ class CellArray extends Array<Cell> {
// @ts-ignore // @ts-ignore
map(arg0: any, ...args: any): CellArray { map(arg0: any, ...args: any): CellArray {
return new CellArray(...<Cell[]>super.map(arg0, ...args)); return new CellArray(...(<Cell[]>super.map(arg0, ...args)));
} }
// @ts-ignore // @ts-ignore
filter(arg0: any, ...args: any): CellArray { filter(arg0: any, ...args: any): CellArray {
return new CellArray(...<Cell[]>super.filter(arg0, ...args)); return new CellArray(...(<Cell[]>super.filter(arg0, ...args)));
} }
/** /**
@ -59,10 +59,11 @@ class CellArray extends Array<Cell> {
* @param targets Boolean that specifies if target terminals should be contained * @param targets Boolean that specifies if target terminals should be contained
* in the result. Default is true. * in the result. Default is true.
*/ */
getOpposites(terminal: Cell, getOpposites(
sources: boolean=true, terminal: Cell,
targets: boolean=true): CellArray { sources: boolean = true,
targets: boolean = true
): CellArray {
const terminals = new CellArray(); const terminals = new CellArray();
for (let i = 0; i < this.length; i += 1) { for (let i = 0; i < this.length; i += 1) {
@ -72,24 +73,14 @@ class CellArray extends Array<Cell> {
// Checks if the terminal is the source of // Checks if the terminal is the source of
// the edge and if the target should be // the edge and if the target should be
// stored in the result // stored in the result
if ( if (source === terminal && target != null && target !== terminal && targets) {
source === terminal &&
target != null &&
target !== terminal &&
targets
) {
terminals.push(target); terminals.push(target);
} }
// Checks if the terminal is the taget of // Checks if the terminal is the taget of
// the edge and if the source should be // the edge and if the source should be
// stored in the result // stored in the result
else if ( else if (target === terminal && source != null && source !== terminal && sources) {
target === terminal &&
source != null &&
source !== terminal &&
sources
) {
terminals.push(source); terminals.push(source);
} }
} }
@ -157,9 +148,7 @@ class CellArray extends Array<Cell> {
* with all descendants. * with all descendants.
* @param mapping Optional mapping for existing clones. * @param mapping Optional mapping for existing clones.
*/ */
cloneCells(includeChildren: boolean=true, cloneCells(includeChildren: boolean = true, mapping: any = {}): CellArray {
mapping: any={}): CellArray {
const clones: CellArray = new CellArray(); const clones: CellArray = new CellArray();
for (const cell of this) { for (const cell of this) {
@ -179,11 +168,8 @@ class CellArray extends Array<Cell> {
* *
* @private * @private
*/ */
cloneCellImpl(cell: Cell, cloneCellImpl(cell: Cell, mapping: any = {}, includeChildren: boolean): Cell {
mapping: any={}, const ident = ObjectIdentity.get(cell);
includeChildren: boolean): Cell {
const ident = <string>mxObjectIdentity.get(cell);
let clone = mapping ? mapping[ident] : null; let clone = mapping ? mapping[ident] : null;
if (clone == null) { if (clone == null) {
@ -194,11 +180,7 @@ class CellArray extends Array<Cell> {
const childCount = cell.getChildCount(); const childCount = cell.getChildCount();
for (let i = 0; i < childCount; i += 1) { for (let i = 0; i < childCount; i += 1) {
const cloneChild = this.cloneCellImpl( const cloneChild = this.cloneCellImpl(<Cell>cell.getChildAt(i), mapping, true);
<Cell>cell.getChildAt(i),
mapping,
true
);
clone.insert(cloneChild); clone.insert(cloneChild);
} }
} }
@ -212,14 +194,11 @@ class CellArray extends Array<Cell> {
* *
* @private * @private
*/ */
restoreClone(clone: Cell, restoreClone(clone: Cell, cell: Cell, mapping: any): void {
cell: Cell,
mapping: any): void {
const source = cell.getTerminal(true); const source = cell.getTerminal(true);
if (source != null) { if (source != null) {
const tmp = mapping[mxObjectIdentity.get(source)]; const tmp = mapping[ObjectIdentity.get(source)];
if (tmp != null) { if (tmp != null) {
tmp.insertEdge(clone, true); tmp.insertEdge(clone, true);
} }
@ -227,7 +206,7 @@ class CellArray extends Array<Cell> {
const target = cell.getTerminal(false); const target = cell.getTerminal(false);
if (target != null) { if (target != null) {
const tmp = mapping[mxObjectIdentity.get(target)]; const tmp = mapping[ObjectIdentity.get(target)];
if (tmp != null) { if (tmp != null) {
tmp.insertEdge(clone, false); tmp.insertEdge(clone, false);
} }
@ -235,11 +214,7 @@ class CellArray extends Array<Cell> {
const childCount = clone.getChildCount(); const childCount = clone.getChildCount();
for (let i = 0; i < childCount; i += 1) { for (let i = 0; i < childCount; i += 1) {
this.restoreClone( this.restoreClone(<Cell>clone.getChildAt(i), <Cell>cell.getChildAt(i), mapping);
<Cell>clone.getChildAt(i),
<Cell>cell.getChildAt(i),
mapping
);
} }
} }
} }

View File

@ -12,11 +12,9 @@ import GraphView from '../../view/GraphView';
import Shape from '../../geometry/shape/Shape'; import Shape from '../../geometry/shape/Shape';
import TextShape from '../../geometry/shape/node/TextShape'; import TextShape from '../../geometry/shape/node/TextShape';
import Dictionary from '../../../util/Dictionary'; import Dictionary from '../../../util/Dictionary';
import { NONE } from '../../../util/Constants';
import type { CellStateStyles } from '../../../types'; import type { CellStateStyles } from '../../../types';
import Image from "../../image/Image";
import {ALIGN_MIDDLE, NONE} from "../../../util/Constants";
import {getValue} from "../../../util/Utils";
/** /**
* Class: mxCellState * Class: mxCellState
@ -210,20 +208,11 @@ class CellState extends Rectangle {
*/ */
getPerimeterBounds( getPerimeterBounds(
border: number = 0, border: number = 0,
bounds: Rectangle = new Rectangle( bounds: Rectangle = new Rectangle(this.x, this.y, this.width, this.height)
this.x,
this.y,
this.width,
this.height
)
) { ) {
if ( if (this.shape?.stencil?.aspect === 'fixed') {
this.shape &&
this.shape.stencil &&
this.shape.stencil.aspect === 'fixed'
) {
const aspect = this.shape.stencil.computeAspect( const aspect = this.shape.stencil.computeAspect(
this.style, this.shape,
bounds.x, bounds.x,
bounds.y, bounds.y,
bounds.width, bounds.width,
@ -455,6 +444,7 @@ class CellState extends Rectangle {
isLoop(state: CellState) { isLoop(state: CellState) {
const src = this.getVisibleTerminalState(true); const src = this.getVisibleTerminalState(true);
const trg = this.getVisibleTerminalState(false); const trg = this.getVisibleTerminalState(false);
return src && src === trg; return src && src === trg;
} }
@ -470,10 +460,8 @@ class CellState extends Rectangle {
* @param state {@link mxCellState} whose vertical alignment should be * @param state {@link mxCellState} whose vertical alignment should be
* returned. * returned.
*/ */
getVerticalAlign(): string | null { getVerticalAlign() {
return this.style != null return this.style.verticalAlign;
? this.style.verticalAlign || ALIGN_MIDDLE
: null;
} }
/** /**
@ -481,11 +469,14 @@ class CellState extends Rectangle {
* *
* @param state {@link mxCellState} to check. * @param state {@link mxCellState} to check.
*/ */
isTransparentState(): boolean { isTransparentState() {
let result = false; let result = false;
const stroke = getValue(this.style, 'strokeColor', NONE);
const fill = getValue(this.style, 'fillColor', NONE); const stroke = this.style.strokeColor;
result = stroke === NONE && fill === NONE && this.getImage(state) == null; const fill = this.style.fillColor;
result = stroke === NONE && fill === NONE && !this.getImageSrc();
return result; return result;
} }
@ -496,10 +487,8 @@ class CellState extends Rectangle {
* *
* @param state {@link mxCellState} whose image URL should be returned. * @param state {@link mxCellState} whose image URL should be returned.
*/ */
getImage(): Image | null { getImageSrc() {
return this.style != null return this.style.image;
? this.style.image
: null;
} }
/** /**
@ -510,10 +499,8 @@ class CellState extends Rectangle {
* @param state {@link mxCellState} whose indicator color should be * @param state {@link mxCellState} whose indicator color should be
* returned. * returned.
*/ */
getIndicatorColor(): string | null { getIndicatorColor() {
return this.style != null return this.style.indicatorColor;
? this.style.indicatorColor
: null;
} }
/** /**
@ -524,10 +511,8 @@ class CellState extends Rectangle {
* @param state {@link mxCellState} whose indicator gradient color should be * @param state {@link mxCellState} whose indicator gradient color should be
* returned. * returned.
*/ */
getIndicatorGradientColor(): string | null { getIndicatorGradientColor() {
return this.style != null return this.style.gradientColor;
? this.style.gradientColor
: null;
} }
/** /**
@ -537,10 +522,8 @@ class CellState extends Rectangle {
* *
* @param state {@link mxCellState} whose indicator shape should be returned. * @param state {@link mxCellState} whose indicator shape should be returned.
*/ */
getIndicatorShape(): string | null { getIndicatorShape() {
return this.style != null return this.style.indicatorShape;
? this.style.indicatorShape
: null;
} }
/** /**
@ -550,10 +533,8 @@ class CellState extends Rectangle {
* *
* @param state {@link mxCellState} whose indicator image should be returned. * @param state {@link mxCellState} whose indicator image should be returned.
*/ */
getIndicatorImage(): Image | null { getIndicatorImageSrc() {
return this.style != null return this.style.indicatorImage;
? this.style.indicatorImage
: null;
} }
} }

View File

@ -77,11 +77,7 @@ class EdgeHandler {
this.reset(); this.reset();
if (dirty) { if (dirty) {
this.graph.cellRenderer.redraw( this.graph.cellRenderer.redraw(this.state, false, state.view.isRendering());
this.state,
false,
state.view.isRendering()
);
} }
}; };
@ -329,10 +325,7 @@ class EdgeHandler {
this.parentHighlight.redraw(); this.parentHighlight.redraw();
} }
} else { } else {
if ( if (pstate != null && pstate.parentHighlight === this.parentHighlight) {
pstate != null &&
pstate.parentHighlight === this.parentHighlight
) {
pstate.parentHighlight = null; pstate.parentHighlight = null;
} }
@ -340,18 +333,12 @@ class EdgeHandler {
this.parentHighlight = null; this.parentHighlight = null;
} }
} else if (this.parentHighlightEnabled && visible) { } else if (this.parentHighlightEnabled && visible) {
if ( if (parent.isVertex() && pstate != null && pstate.parentHighlight == null) {
parent.isVertex() &&
pstate != null &&
pstate.parentHighlight == null
) {
this.parentHighlight = this.createParentHighlightShape(pstate); this.parentHighlight = this.createParentHighlightShape(pstate);
// VML dialect required here for event transparency in IE // VML dialect required here for event transparency in IE
this.parentHighlight.dialect = DIALECT_SVG; this.parentHighlight.dialect = DIALECT_SVG;
this.parentHighlight.pointerEvents = false; this.parentHighlight.pointerEvents = false;
this.parentHighlight.rotation = Number( this.parentHighlight.rotation = Number(pstate.style.rotation || '0');
pstate.style.rotation || '0'
);
this.parentHighlight.init(this.graph.getView().getOverlayPane()); this.parentHighlight.init(this.graph.getView().getOverlayPane());
this.parentHighlight.redraw(); this.parentHighlight.redraw();
@ -390,8 +377,7 @@ class EdgeHandler {
// Updates preferHtml // Updates preferHtml
this.preferHtml = this.preferHtml =
this.state.text != null && this.state.text != null && this.state.text.node.parentNode === this.graph.container;
this.state.text.node.parentNode === this.graph.container;
if (!this.preferHtml) { if (!this.preferHtml) {
// Checks source terminal // Checks source terminal
@ -429,10 +415,7 @@ class EdgeHandler {
} }
// Adds a rectangular handle for the label position // Adds a rectangular handle for the label position
this.label = new Point( this.label = new Point(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
this.state.absoluteOffset.x,
this.state.absoluteOffset.y
);
this.labelShape = this.createLabelHandleShape(); this.labelShape = this.createLabelHandleShape();
this.initBend(this.labelShape); this.initBend(this.labelShape);
this.labelShape.setCursor(CURSOR_LABEL_HANDLE); this.labelShape.setCursor(CURSOR_LABEL_HANDLE);
@ -525,7 +508,7 @@ class EdgeHandler {
null, null,
this.getSelectionColor() this.getSelectionColor()
); );
shape.strokewidth = this.getSelectionStrokeWidth(); shape.strokeWidth = this.getSelectionStrokeWidth();
shape.isDashed = this.isSelectionDashed(); shape.isDashed = this.isSelectionDashed();
return shape; return shape;
@ -616,10 +599,7 @@ class EdgeHandler {
let cell = super.getCell(me); let cell = super.getCell(me);
// Checks for cell at preview point (with grid) // Checks for cell at preview point (with grid)
if ( if ((cell === self.state.cell || cell == null) && self.currentPoint != null) {
(cell === self.state.cell || cell == null) &&
self.currentPoint != null
) {
cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y); cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y);
} }
@ -722,9 +702,7 @@ class EdgeHandler {
}); });
if (this.isHandleEnabled(i)) { if (this.isHandleEnabled(i)) {
bend.setCursor( bend.setCursor(terminal ? CURSOR_TERMINAL_HANDLE : CURSOR_BEND_HANDLE);
terminal ? CURSOR_TERMINAL_HANDLE : CURSOR_BEND_HANDLE
);
} }
bends.push(bend); bends.push(bend);
@ -842,12 +820,7 @@ class EdgeHandler {
createLabelHandleShape() { createLabelHandleShape() {
if (this.labelHandleImage != null) { if (this.labelHandleImage != null) {
const shape = new ImageShape( const shape = new ImageShape(
new Rectangle( new Rectangle(0, 0, this.labelHandleImage.width, this.labelHandleImage.height),
0,
0,
this.labelHandleImage.width,
this.labelHandleImage.height
),
this.labelHandleImage.src this.labelHandleImage.src
); );
@ -879,8 +852,7 @@ class EdgeHandler {
bend.dialect = DIALECT_STRICTHTML; bend.dialect = DIALECT_STRICTHTML;
bend.init(this.graph.container); bend.init(this.graph.container);
} else { } else {
bend.dialect = bend.dialect = this.graph.dialect !== DIALECT_SVG ? DIALECT_MIXEDHTML : DIALECT_SVG;
this.graph.dialect !== DIALECT_SVG ? DIALECT_MIXEDHTML : DIALECT_SVG;
bend.init(this.graph.getView().getOverlayPane()); bend.init(this.graph.getView().getOverlayPane());
} }
@ -913,12 +885,7 @@ class EdgeHandler {
const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1; const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1;
const hit = const hit =
this.allowHandleBoundsCheck && tol > 0 this.allowHandleBoundsCheck && tol > 0
? new Rectangle( ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol)
me.getGraphX() - tol,
me.getGraphY() - tol,
2 * tol,
2 * tol
)
: null; : null;
let minDistSq = null; let minDistSq = null;
@ -928,8 +895,7 @@ class EdgeHandler {
shape.node != null && shape.node != null &&
shape.node.style.display !== 'none' && shape.node.style.display !== 'none' &&
shape.node.style.visibility !== 'hidden' && shape.node.style.visibility !== 'hidden' &&
(me.isSource(shape) || (me.isSource(shape) || (hit != null && utils.intersects(shape.bounds, hit)))
(hit != null && utils.intersects(shape.bounds, hit)))
) { ) {
const dx = me.getGraphX() - shape.bounds.getCenterX(); const dx = me.getGraphX() - shape.bounds.getCenterX();
const dy = me.getGraphY() - shape.bounds.getCenterY(); const dy = me.getGraphY() - shape.bounds.getCenterY();
@ -1019,11 +985,7 @@ class EdgeHandler {
this.snapPoint = new Point(b.getCenterX(), b.getCenterY()); this.snapPoint = new Point(b.getCenterX(), b.getCenterY());
} }
if ( if (this.addEnabled && handle == null && this.isAddPointEvent(me.getEvent())) {
this.addEnabled &&
handle == null &&
this.isAddPointEvent(me.getEvent())
) {
this.addPoint(this.state, me.getEvent()); this.addPoint(this.state, me.getEvent());
me.consume(); me.consume();
} else if (handle != null && !me.isConsumed() && this.graph.isEnabled()) { } else if (handle != null && !me.isConsumed() && this.graph.isEnabled()) {
@ -1058,8 +1020,7 @@ class EdgeHandler {
this.startY = y; this.startY = y;
this.isSource = this.bends == null ? false : index === 0; this.isSource = this.bends == null ? false : index === 0;
this.isTarget = this.isTarget = this.bends == null ? false : index === this.bends.length - 1;
this.bends == null ? false : index === this.bends.length - 1;
this.isLabel = index === InternalEvent.LABEL_HANDLE; this.isLabel = index === InternalEvent.LABEL_HANDLE;
if (this.isSource || this.isTarget) { if (this.isSource || this.isTarget) {
@ -1067,8 +1028,7 @@ class EdgeHandler {
const terminal = cell.getTerminal(this.isSource); const terminal = cell.getTerminal(this.isSource);
if ( if (
(terminal == null && (terminal == null && this.graph.isTerminalPointMovable(cell, this.isSource)) ||
this.graph.isTerminalPointMovable(cell, this.isSource)) ||
(terminal != null && (terminal != null &&
this.graph.isCellDisconnectable(cell, terminal, this.isSource)) this.graph.isCellDisconnectable(cell, terminal, this.isSource))
) { ) {
@ -1189,10 +1149,7 @@ class EdgeHandler {
const snapToTerminal = (terminal) => { const snapToTerminal = (terminal) => {
if (terminal != null) { if (terminal != null) {
snapToPoint( snapToPoint(
new point( new point(view.getRoutingCenterX(terminal), view.getRoutingCenterY(terminal))
view.getRoutingCenterX(terminal),
view.getRoutingCenterY(terminal)
)
); );
} }
}; };
@ -1245,8 +1202,7 @@ class EdgeHandler {
if ( if (
this.marker.highlight != null && this.marker.highlight != null &&
this.marker.highlight.state != null && this.marker.highlight.state != null &&
this.marker.highlight.state.cell === this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell
this.constraintHandler.currentFocus.cell
) { ) {
// Direct repaint needed if cell already highlighted // Direct repaint needed if cell already highlighted
if (this.marker.highlight.shape.stroke !== 'transparent') { if (this.marker.highlight.shape.stroke !== 'transparent') {
@ -1254,10 +1210,7 @@ class EdgeHandler {
this.marker.highlight.repaint(); this.marker.highlight.repaint();
} }
} else { } else {
this.marker.markCell( this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
this.constraintHandler.currentFocus.cell,
'transparent'
);
} }
const model = this.graph.getModel(); const model = this.graph.getModel();
@ -1267,12 +1220,8 @@ class EdgeHandler {
!this.isSource !this.isSource
); );
const otherCell = other != null ? other.cell : null; const otherCell = other != null ? other.cell : null;
const source = this.isSource const source = this.isSource ? this.constraintHandler.currentFocus.cell : otherCell;
? this.constraintHandler.currentFocus.cell const target = this.isSource ? otherCell : this.constraintHandler.currentFocus.cell;
: otherCell;
const target = this.isSource
? otherCell
: this.constraintHandler.currentFocus.cell;
// Updates the error message of the handler // Updates the error message of the handler
this.error = this.validateConnection(source, target); this.error = this.validateConnection(source, target);
@ -1282,10 +1231,7 @@ class EdgeHandler {
result = this.constraintHandler.currentFocus; result = this.constraintHandler.currentFocus;
} }
if ( if (this.error != null || (result != null && !this.isCellEnabled(result.cell))) {
this.error != null ||
(result != null && !this.isCellEnabled(result.cell))
) {
this.constraintHandler.reset(); this.constraintHandler.reset();
} }
@ -1367,11 +1313,7 @@ class EdgeHandler {
const src = this.state.getVisibleTerminalState(true); const src = this.state.getVisibleTerminalState(true);
if (src != null) { if (src != null) {
const c = this.graph.getConnectionConstraint( const c = this.graph.getConnectionConstraint(this.state, src, true);
this.state,
src,
true
);
// Checks if point is not fixed // Checks if point is not fixed
if (c == null || this.graph.getConnectionPoint(src, c) == null) { if (c == null || this.graph.getConnectionPoint(src, c) == null) {
@ -1385,11 +1327,7 @@ class EdgeHandler {
const trg = this.state.getVisibleTerminalState(false); const trg = this.state.getVisibleTerminalState(false);
if (trg != null) { if (trg != null) {
const c = this.graph.getConnectionConstraint( const c = this.graph.getConnectionConstraint(this.state, trg, false);
this.state,
trg,
false
);
// Checks if point is not fixed // Checks if point is not fixed
if (c == null || this.graph.getConnectionPoint(trg, c) == null) { if (c == null || this.graph.getConnectionPoint(trg, c) == null) {
@ -1453,10 +1391,8 @@ class EdgeHandler {
const left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); const left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
const gridX = const gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; const gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
const gridY =
this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
return ( return (
this.outlineConnect && this.outlineConnect &&
@ -1484,16 +1420,8 @@ class EdgeHandler {
? terminalState ? terminalState
: this.state.getVisibleTerminalState(false); : this.state.getVisibleTerminalState(false);
let sourceConstraint = this.graph.getConnectionConstraint( let sourceConstraint = this.graph.getConnectionConstraint(edge, sourceState, true);
edge, let targetConstraint = this.graph.getConnectionConstraint(edge, targetState, false);
sourceState,
true
);
let targetConstraint = this.graph.getConnectionConstraint(
edge,
targetState,
false
);
let constraint = this.constraintHandler.currentConstraint; let constraint = this.constraintHandler.currentConstraint;
@ -1528,13 +1456,11 @@ class EdgeHandler {
this.marker.highlight.shape.stroke = outline this.marker.highlight.shape.stroke = outline
? OUTLINE_HIGHLIGHT_COLOR ? OUTLINE_HIGHLIGHT_COLOR
: 'transparent'; : 'transparent';
this.marker.highlight.shape.strokewidth = this.marker.highlight.shape.strokewidth = OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
this.marker.highlight.repaint(); this.marker.highlight.repaint();
} else if (this.marker.hasValidState()) { } else if (this.marker.hasValidState()) {
this.marker.highlight.shape.stroke = this.marker.highlight.shape.stroke =
me.getCell().isConnectable() && me.getCell().isConnectable() && this.marker.getValidState() !== me.getState()
this.marker.getValidState() !== me.getState()
? 'transparent' ? 'transparent'
: DEFAULT_VALID_COLOR; : DEFAULT_VALID_COLOR;
this.marker.highlight.shape.strokewidth = HIGHLIGHT_STROKEWIDTH / s / s; this.marker.highlight.shape.strokewidth = HIGHLIGHT_STROKEWIDTH / s / s;
@ -1550,10 +1476,8 @@ class EdgeHandler {
if (this.isSource || this.isTarget) { if (this.isSource || this.isTarget) {
if (constraint != null && constraint.point != null) { if (constraint != null && constraint.point != null) {
edge.style[this.isSource ? 'exitX' : 'entryX'] = edge.style[this.isSource ? 'exitX' : 'entryX'] = constraint.point.x;
constraint.point.x; edge.style[this.isSource ? 'exitY' : 'entryY'] = constraint.point.y;
edge.style[this.isSource ? 'exitY' : 'entryY'] =
constraint.point.y;
} else { } else {
delete edge.style[this.isSource ? 'exitX' : 'entryX']; delete edge.style[this.isSource ? 'exitX' : 'entryX'];
delete edge.style[this.isSource ? 'exitY' : 'entryY']; delete edge.style[this.isSource ? 'exitY' : 'entryY'];
@ -1564,21 +1488,11 @@ class EdgeHandler {
edge.setVisibleTerminalState(targetState, false); edge.setVisibleTerminalState(targetState, false);
if (!this.isSource || sourceState != null) { if (!this.isSource || sourceState != null) {
edge.view.updateFixedTerminalPoint( edge.view.updateFixedTerminalPoint(edge, sourceState, true, sourceConstraint);
edge,
sourceState,
true,
sourceConstraint
);
} }
if (!this.isTarget || targetState != null) { if (!this.isTarget || targetState != null) {
edge.view.updateFixedTerminalPoint( edge.view.updateFixedTerminalPoint(edge, targetState, false, targetConstraint);
edge,
targetState,
false,
targetConstraint
);
} }
if ((this.isSource || this.isTarget) && terminalState == null) { if ((this.isSource || this.isTarget) && terminalState == null) {
@ -1625,12 +1539,8 @@ class EdgeHandler {
this.index > InternalEvent.VIRTUAL_HANDLE this.index > InternalEvent.VIRTUAL_HANDLE
) { ) {
if (this.customHandles != null) { if (this.customHandles != null) {
this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent( this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent(me);
me this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].positionChanged();
);
this.customHandles[
InternalEvent.CUSTOM_HANDLE - this.index
].positionChanged();
if (this.shape != null && this.shape.node != null) { if (this.shape != null && this.shape.node != null) {
this.shape.node.style.display = 'none'; this.shape.node.style.display = 'none';
@ -1642,9 +1552,7 @@ class EdgeHandler {
} else { } else {
this.points = this.getPreviewPoints(this.currentPoint, me); this.points = this.getPreviewPoints(this.currentPoint, me);
let terminalState = let terminalState =
this.isSource || this.isTarget this.isSource || this.isTarget ? this.getPreviewTerminalState(me) : null;
? this.getPreviewTerminalState(me)
: null;
if ( if (
this.constraintHandler.currentConstraint != null && this.constraintHandler.currentConstraint != null &&
@ -1655,9 +1563,7 @@ class EdgeHandler {
} else if (this.outlineConnect) { } else if (this.outlineConnect) {
// Need to check outline before cloning terminal state // Need to check outline before cloning terminal state
const outline = const outline =
this.isSource || this.isTarget this.isSource || this.isTarget ? this.isOutlineConnectEvent(me) : false;
? this.isOutlineConnectEvent(me)
: false;
if (outline) { if (outline) {
terminalState = this.marker.highlight.state; terminalState = this.marker.highlight.state;
@ -1693,9 +1599,7 @@ class EdgeHandler {
// Sets the color of the preview to valid or invalid, updates the // Sets the color of the preview to valid or invalid, updates the
// points of the preview and redraws // points of the preview and redraws
const color = const color =
this.error == null this.error == null ? this.marker.validColor : this.marker.invalidColor;
? this.marker.validColor
: this.marker.invalidColor;
this.setPreviewColor(color); this.setPreviewColor(color);
this.abspoints = clone.absolutePoints; this.abspoints = clone.absolutePoints;
this.active = true; this.active = true;
@ -1813,9 +1717,7 @@ class EdgeHandler {
model.endUpdate(); model.endUpdate();
} }
} else if (this.graph.isAllowDanglingEdges()) { } else if (this.graph.isAllowDanglingEdges()) {
const pt = this.abspoints[ const pt = this.abspoints[this.isSource ? 0 : this.abspoints.length - 1];
this.isSource ? 0 : this.abspoints.length - 1
];
pt.x = this.roundLength( pt.x = this.roundLength(
pt.x / this.graph.view.scale - this.graph.view.translate.x pt.x / this.graph.view.scale - this.graph.view.translate.x
); );
@ -2074,12 +1976,7 @@ class EdgeHandler {
geo = geo.clone(); geo = geo.clone();
geo.setTerminalPoint(point, isSource); geo.setTerminalPoint(point, isSource);
model.setGeometry(edge, geo); model.setGeometry(edge, geo);
this.graph.connectCell( this.graph.connectCell(edge, null, isSource, new ConnectionConstraint());
edge,
null,
isSource,
new ConnectionConstraint()
);
} }
} finally { } finally {
model.endUpdate(); model.endUpdate();
@ -2130,11 +2027,7 @@ class EdgeHandler {
*/ */
// addPoint(state: mxCellState, evt: Event): void; // addPoint(state: mxCellState, evt: Event): void;
addPoint(state, evt) { addPoint(state, evt) {
const pt = utils.convertPoint( const pt = utils.convertPoint(this.graph.container, getClientX(evt), getClientY(evt));
this.graph.container,
getClientX(evt),
getClientY(evt)
);
const gridEnabled = this.graph.isGridEnabledEvent(evt); const gridEnabled = this.graph.isGridEnabledEvent(evt);
this.convertPoint(pt, gridEnabled); this.convertPoint(pt, gridEnabled);
this.addPointAt(state, pt.x, pt.y); this.addPointAt(state, pt.x, pt.y);
@ -2215,8 +2108,7 @@ class EdgeHandler {
let color = HANDLE_FILLCOLOR; let color = HANDLE_FILLCOLOR;
if ( if (
(terminal != null && (terminal != null && !this.graph.isCellDisconnectable(cell, terminal, isSource)) ||
!this.graph.isCellDisconnectable(cell, terminal, isSource)) ||
(terminal == null && !this.graph.isTerminalPointMovable(cell, isSource)) (terminal == null && !this.graph.isTerminalPointMovable(cell, isSource))
) { ) {
color = LOCKED_HANDLE_FILLCOLOR; color = LOCKED_HANDLE_FILLCOLOR;
@ -2278,10 +2170,7 @@ class EdgeHandler {
// Updates the handle for the label position // Updates the handle for the label position
let b = this.labelShape.bounds; let b = this.labelShape.bounds;
this.label = new Point( this.label = new Point(this.state.absoluteOffset.x, this.state.absoluteOffset.y);
this.state.absoluteOffset.x,
this.state.absoluteOffset.y
);
this.labelShape.bounds = new Rectangle( this.labelShape.bounds = new Rectangle(
Math.round(this.label.x - b.width / 2), Math.round(this.label.x - b.width / 2),
Math.round(this.label.y - b.height / 2), Math.round(this.label.y - b.height / 2),
@ -2378,9 +2267,7 @@ class EdgeHandler {
this.customHandles[i].shape.node.style.display = temp; this.customHandles[i].shape.node.style.display = temp;
// Hides custom handles during text editing // Hides custom handles during text editing
this.customHandles[ this.customHandles[i].shape.node.style.visibility = this.isCustomHandleVisible(
i
].shape.node.style.visibility = this.isCustomHandleVisible(
this.customHandles[i] this.customHandles[i]
) )
? '' ? ''
@ -2395,9 +2282,7 @@ class EdgeHandler {
* Returns true if the given custom handle is visible. * Returns true if the given custom handle is visible.
*/ */
isCustomHandleVisible(handle) { isCustomHandleVisible(handle) {
return ( return !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1;
!this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1
);
} }
/** /**
@ -2527,16 +2412,13 @@ class EdgeHandler {
} }
} }
if ( if (this.shape != null && !utils.equalPoints(this.shape.points, this.abspoints)) {
this.shape != null &&
!utils.equalPoints(this.shape.points, this.abspoints)
) {
this.shape.apply(this.state); this.shape.apply(this.state);
this.shape.points = this.abspoints.slice(); this.shape.points = this.abspoints.slice();
this.shape.scale = this.state.view.scale; this.shape.scale = this.state.view.scale;
this.shape.isDashed = this.isSelectionDashed(); this.shape.isDashed = this.isSelectionDashed();
this.shape.stroke = this.getSelectionColor(); this.shape.stroke = this.getSelectionColor();
this.shape.strokewidth = this.shape.strokeWidth =
this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale; this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale;
this.shape.isShadow = false; this.shape.isShadow = false;
this.shape.redraw(); this.shape.redraw();

View File

@ -21,7 +21,7 @@ import {
import InternalEvent from '../../event/InternalEvent'; import InternalEvent from '../../event/InternalEvent';
import Shape from '../../geometry/shape/Shape'; import Shape from '../../geometry/shape/Shape';
import InternalMouseEvent from '../../event/InternalMouseEvent'; import InternalMouseEvent from '../../event/InternalMouseEvent';
import Image from '../../image/Image'; import Image from '../../image/ImageBox';
import Graph from '../../Graph'; import Graph from '../../Graph';
import CellState from '../datatypes/CellState'; import CellState from '../datatypes/CellState';
@ -33,11 +33,12 @@ import CellState from '../datatypes/CellState';
class VertexHandle { class VertexHandle {
dependencies = ['snap', 'cells']; dependencies = ['snap', 'cells'];
constructor(state: CellState, constructor(
cursor: string | null = 'default', state: CellState,
image: Image | null = null, cursor: string | null = 'default',
shape: Shape | null = null) { image: Image | null = null,
shape: Shape | null = null
) {
this.graph = state.view.graph; this.graph = state.view.graph;
this.state = state; this.state = state;
this.cursor = cursor != null ? cursor : this.cursor; this.cursor = cursor != null ? cursor : this.cursor;
@ -221,12 +222,8 @@ class VertexHandle {
const tr = this.graph.view.translate; const tr = this.graph.view.translate;
const shapeBounds = <Rectangle>this.shape.bounds; const shapeBounds = <Rectangle>this.shape.bounds;
shapeBounds.x = Math.floor( shapeBounds.x = Math.floor((pt.x + tr.x) * scale - shapeBounds.width / 2);
(pt.x + tr.x) * scale - shapeBounds.width / 2 shapeBounds.y = Math.floor((pt.y + tr.y) * scale - shapeBounds.height / 2);
);
shapeBounds.y = Math.floor(
(pt.y + tr.y) * scale - shapeBounds.height / 2
);
// Needed to force update of text bounds // Needed to force update of text bounds
this.shape.redraw(); this.shape.redraw();
@ -240,8 +237,7 @@ class VertexHandle {
*/ */
isHtmlRequired(): boolean { isHtmlRequired(): boolean {
return ( return (
this.state.text != null && this.state.text != null && this.state.text.node.parentNode === this.graph.container
this.state.text.node.parentNode === this.graph.container
); );
} }

View File

@ -30,7 +30,7 @@ import mxClient from '../../../mxClient';
import { isMouseEvent, isShiftDown } from '../../../util/EventUtils'; import { isMouseEvent, isShiftDown } from '../../../util/EventUtils';
import Graph from '../../Graph'; import Graph from '../../Graph';
import CellState from '../datatypes/CellState'; import CellState from '../datatypes/CellState';
import Image from '../../image/Image'; import Image from '../../image/ImageBox';
import Cell from '../datatypes/Cell'; import Cell from '../datatypes/Cell';
/** /**
@ -238,15 +238,9 @@ class VertexHandler {
// VML dialect required here for event transparency in IE // VML dialect required here for event transparency in IE
this.selectionBorder.dialect = DIALECT_SVG; this.selectionBorder.dialect = DIALECT_SVG;
this.selectionBorder.pointerEvents = false; this.selectionBorder.pointerEvents = false;
this.selectionBorder.rotation = Number( this.selectionBorder.rotation = Number(this.state.style.rotation || '0');
this.state.style.rotation || '0'
);
this.selectionBorder.init(this.graph.getView().getOverlayPane()); this.selectionBorder.init(this.graph.getView().getOverlayPane());
InternalEvent.redirectMouseEvents( InternalEvent.redirectMouseEvents(this.selectionBorder.node, this.graph, this.state);
this.selectionBorder.node,
this.graph,
this.state
);
if (this.graph.isCellMovable(this.state.cell)) { if (this.graph.isCellMovable(this.state.cell)) {
this.selectionBorder.setCursor(CURSOR_MOVABLE_VERTEX); this.selectionBorder.setCursor(CURSOR_MOVABLE_VERTEX);
@ -357,9 +351,7 @@ class VertexHandler {
*/ */
// isConstrainedEvent(me: mxMouseEvent): boolean; // isConstrainedEvent(me: mxMouseEvent): boolean;
isConstrainedEvent(me) { isConstrainedEvent(me) {
return ( return isShiftDown(me.getEvent()) || this.state.style.aspect === 'fixed';
isShiftDown(me.getEvent()) || this.state.style.aspect === 'fixed'
);
} }
/** /**
@ -448,7 +440,7 @@ class VertexHandler {
null, null,
this.getSelectionColor() this.getSelectionColor()
); );
shape.strokewidth = this.getSelectionStrokeWidth(); shape.strokeWidth = this.getSelectionStrokeWidth();
shape.isDashed = this.isSelectionDashed(); shape.isDashed = this.isSelectionDashed();
return shape; return shape;
@ -560,17 +552,9 @@ class VertexHandler {
return shape; return shape;
} }
if (index === InternalEvent.ROTATION_HANDLE) { if (index === InternalEvent.ROTATION_HANDLE) {
return new EllipseShape( return new EllipseShape(bounds, fillColor || HANDLE_FILLCOLOR, HANDLE_STROKECOLOR);
bounds,
fillColor || HANDLE_FILLCOLOR,
HANDLE_STROKECOLOR
);
} }
return new RectangleShape( return new RectangleShape(bounds, fillColor || HANDLE_FILLCOLOR, HANDLE_STROKECOLOR);
bounds,
fillColor || HANDLE_FILLCOLOR,
HANDLE_STROKECOLOR
);
} }
/** /**
@ -604,19 +588,12 @@ class VertexHandler {
const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1; const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1;
const hit = const hit =
this.allowHandleBoundsCheck && tol > 0 this.allowHandleBoundsCheck && tol > 0
? new Rectangle( ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol)
me.getGraphX() - tol,
me.getGraphY() - tol,
2 * tol,
2 * tol
)
: null; : null;
const checkShape = (shape) => { const checkShape = (shape) => {
const st = const st =
shape != null && shape != null && shape.constructor !== ImageShape && this.allowHandleBoundsCheck
shape.constructor !== ImageShape &&
this.allowHandleBoundsCheck
? shape.strokewidth + shape.svgStrokeTolerance ? shape.strokewidth + shape.svgStrokeTolerance
: null; : null;
const real = const real =
@ -720,8 +697,7 @@ class VertexHandler {
// start(x: number, y: number, index: number): void; // start(x: number, y: number, index: number): void;
start(x, y, index) { start(x, y, index) {
if (this.selectionBorder != null) { if (this.selectionBorder != null) {
this.livePreviewActive = this.livePreviewActive = this.livePreview && this.state.cell.getChildCount() === 0;
this.livePreview && this.state.cell.getChildCount() === 0;
this.inTolerance = true; this.inTolerance = true;
this.childOffsetX = 0; this.childOffsetX = 0;
this.childOffsetY = 0; this.childOffsetY = 0;
@ -752,10 +728,7 @@ class VertexHandler {
this.preview = this.createSelectionShape(this.bounds); this.preview = this.createSelectionShape(this.bounds);
if ( if (
!( !(mxClient.IS_SVG && Number(this.state.style.rotation || '0') !== 0) &&
mxClient.IS_SVG &&
Number(this.state.style.rotation || '0') !== 0
) &&
this.state.text != null && this.state.text != null &&
this.state.text.node.parentNode === this.graph.container this.state.text.node.parentNode === this.graph.container
) { ) {
@ -774,8 +747,7 @@ class VertexHandler {
const dx = pos.x - this.state.getCenterX(); const dx = pos.x - this.state.getCenterX();
const dy = pos.y - this.state.getCenterY(); const dy = pos.y - this.state.getCenterY();
this.startAngle = this.startAngle = dx !== 0 ? (Math.atan(dy / dx) * 180) / Math.PI + 90 : 0;
dx !== 0 ? (Math.atan(dy / dx) * 180) / Math.PI + 90 : 0;
this.startDist = Math.sqrt(dx * dx + dy * dy); this.startDist = Math.sqrt(dx * dx + dy * dy);
} }
@ -789,10 +761,7 @@ class VertexHandler {
this.labelShape.node.style.display = ''; this.labelShape.node.style.display = '';
} else if (this.sizers != null && this.sizers[index] != null) { } else if (this.sizers != null && this.sizers[index] != null) {
this.sizers[index].node.style.display = ''; this.sizers[index].node.style.display = '';
} else if ( } else if (index <= InternalEvent.CUSTOM_HANDLE && this.customHandles != null) {
index <= InternalEvent.CUSTOM_HANDLE &&
this.customHandles != null
) {
this.customHandles[InternalEvent.CUSTOM_HANDLE - index].setVisible(true); this.customHandles[InternalEvent.CUSTOM_HANDLE - index].setVisible(true);
} }
@ -801,9 +770,7 @@ class VertexHandler {
this.edgeHandlers = []; this.edgeHandlers = [];
for (let i = 0; i < edges.length; i += 1) { for (let i = 0; i < edges.length; i += 1) {
const handler = this.graph.selectionCellsHandler.getHandler( const handler = this.graph.selectionCellsHandler.getHandler(edges[i]);
edges[i]
);
if (handler != null) { if (handler != null) {
this.edgeHandlers.push(handler); this.edgeHandlers.push(handler);
@ -933,12 +900,8 @@ class VertexHandler {
if (!this.inTolerance) { if (!this.inTolerance) {
if (this.index <= InternalEvent.CUSTOM_HANDLE) { if (this.index <= InternalEvent.CUSTOM_HANDLE) {
if (this.customHandles != null) { if (this.customHandles != null) {
this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent( this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent(me);
me this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].active = true;
);
this.customHandles[
InternalEvent.CUSTOM_HANDLE - this.index
].active = true;
if (this.ghostPreview != null) { if (this.ghostPreview != null) {
this.ghostPreview.apply(this.state); this.ghostPreview.apply(this.state);
@ -1010,9 +973,7 @@ class VertexHandler {
} }
const index = const index =
this.rotationShape != null this.rotationShape != null ? this.sizers.length - 2 : this.sizers.length - 1;
? this.sizers.length - 2
: this.sizers.length - 1;
this.moveSizerTo(this.sizers[index], point.x, point.y); this.moveSizerTo(this.sizers[index], point.x, point.y);
} }
@ -1148,26 +1109,14 @@ class VertexHandler {
this.unscaledBounds.y = max.y; this.unscaledBounds.y = max.y;
} }
if ( if (this.unscaledBounds.x + this.unscaledBounds.width > max.x + max.width) {
this.unscaledBounds.x + this.unscaledBounds.width >
max.x + max.width
) {
this.unscaledBounds.width -= this.unscaledBounds.width -=
this.unscaledBounds.x + this.unscaledBounds.x + this.unscaledBounds.width - max.x - max.width;
this.unscaledBounds.width -
max.x -
max.width;
} }
if ( if (this.unscaledBounds.y + this.unscaledBounds.height > max.y + max.height) {
this.unscaledBounds.y + this.unscaledBounds.height >
max.y + max.height
) {
this.unscaledBounds.height -= this.unscaledBounds.height -=
this.unscaledBounds.y + this.unscaledBounds.y + this.unscaledBounds.height - max.y - max.height;
this.unscaledBounds.height -
max.y -
max.height;
} }
} }
} }
@ -1211,12 +1160,8 @@ class VertexHandler {
this.bounds.y += dy3; this.bounds.y += dy3;
// Rounds unscaled bounds to int // Rounds unscaled bounds to int
this.unscaledBounds.x = this.roundLength( this.unscaledBounds.x = this.roundLength(this.unscaledBounds.x + dx3 / scale);
this.unscaledBounds.x + dx3 / scale this.unscaledBounds.y = this.roundLength(this.unscaledBounds.y + dy3 / scale);
);
this.unscaledBounds.y = this.roundLength(
this.unscaledBounds.y + dy3 / scale
);
this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width); this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width);
this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height); this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height);
@ -1370,15 +1315,12 @@ class VertexHandler {
this.customHandles[InternalEvent.CUSTOM_HANDLE - index] != null this.customHandles[InternalEvent.CUSTOM_HANDLE - index] != null
) { ) {
this.state.style = style; this.state.style = style;
this.customHandles[ this.customHandles[InternalEvent.CUSTOM_HANDLE - index].positionChanged();
InternalEvent.CUSTOM_HANDLE - index
].positionChanged();
} }
} }
} else if (index === InternalEvent.ROTATION_HANDLE) { } else if (index === InternalEvent.ROTATION_HANDLE) {
if (this.currentAlpha != null) { if (this.currentAlpha != null) {
const delta = const delta = this.currentAlpha - (this.state.style.rotation || 0);
this.currentAlpha - (this.state.style.rotation || 0);
if (delta !== 0) { if (delta !== 0) {
this.rotateCell(this.state.cell, delta); this.rotateCell(this.state.cell, delta);
@ -1388,9 +1330,7 @@ class VertexHandler {
} }
} else { } else {
const gridEnabled = this.graph.isGridEnabledEvent(me.getEvent()); const gridEnabled = this.graph.isGridEnabledEvent(me.getEvent());
const alpha = utils.toRadians( const alpha = utils.toRadians(this.state.style.rotation || '0');
this.state.style.rotation || '0'
);
const cos = Math.cos(-alpha); const cos = Math.cos(-alpha);
const sin = Math.sin(-alpha); const sin = Math.sin(-alpha);
@ -1579,20 +1519,14 @@ class VertexHandler {
if (geo != null) { if (geo != null) {
if (index === InternalEvent.LABEL_HANDLE) { if (index === InternalEvent.LABEL_HANDLE) {
const alpha = -utils.toRadians( const alpha = -utils.toRadians(this.state.style.rotation || '0');
this.state.style.rotation || '0'
);
const cos = Math.cos(alpha); const cos = Math.cos(alpha);
const sin = Math.sin(alpha); const sin = Math.sin(alpha);
const { scale } = this.graph.view; const { scale } = this.graph.view;
const pt = utils.getRotatedPoint( const pt = utils.getRotatedPoint(
new Point( new Point(
Math.round( Math.round((this.labelShape.bounds.getCenterX() - this.startX) / scale),
(this.labelShape.bounds.getCenterX() - this.startX) / scale Math.round((this.labelShape.bounds.getCenterY() - this.startY) / scale)
),
Math.round(
(this.labelShape.bounds.getCenterY() - this.startY) / scale
)
), ),
cos, cos,
sin sin
@ -1811,12 +1745,7 @@ class VertexHandler {
height = Math.abs(height); height = Math.abs(height);
} }
const result = new Rectangle( const result = new Rectangle(left + tr.x * scale, top + tr.y * scale, width, height);
left + tr.x * scale,
top + tr.y * scale,
width,
height
);
if (this.minBounds != null) { if (this.minBounds != null) {
result.width = Math.max( result.width = Math.max(
@ -1924,18 +1853,13 @@ class VertexHandler {
// Hides custom handles during text editing // Hides custom handles during text editing
this.customHandles[i].shape.node.style.visibility = this.customHandles[i].shape.node.style.visibility =
this.handlesVisible && this.handlesVisible && this.isCustomHandleVisible(this.customHandles[i])
this.isCustomHandleVisible(this.customHandles[i])
? '' ? ''
: 'hidden'; : 'hidden';
} }
} }
if ( if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null) {
this.sizers != null &&
this.sizers.length > 0 &&
this.sizers[0] != null
) {
if (this.index == null && this.manageSizers && this.sizers.length >= 8) { if (this.index == null && this.manageSizers && this.sizers.length >= 8) {
// KNOWN: Tolerance depends on event type (eg. 0 for mouse events) // KNOWN: Tolerance depends on event type (eg. 0 for mouse events)
const padding = this.getHandlePadding(); const padding = this.getHandlePadding();
@ -1990,9 +1914,7 @@ class VertexHandler {
'w-resize', 'w-resize',
]; ];
const alpha = utils.toRadians( const alpha = utils.toRadians(this.state.style.rotation || '0');
this.state.style.rotation || '0'
);
const cos = Math.cos(alpha); const cos = Math.cos(alpha);
const sin = Math.sin(alpha); const sin = Math.sin(alpha);
const da = Math.round((alpha * 4) / Math.PI); const da = Math.round((alpha * 4) / Math.PI);
@ -2062,36 +1984,25 @@ class VertexHandler {
if (this.rotationShape != null) { if (this.rotationShape != null) {
const alpha = utils.toRadians( const alpha = utils.toRadians(
this.currentAlpha != null this.currentAlpha != null ? this.currentAlpha : this.state.style.rotation || '0'
? this.currentAlpha
: this.state.style.rotation || '0'
); );
const cos = Math.cos(alpha); const cos = Math.cos(alpha);
const sin = Math.sin(alpha); const sin = Math.sin(alpha);
const ct = new Point(this.state.getCenterX(), this.state.getCenterY()); const ct = new Point(this.state.getCenterX(), this.state.getCenterY());
const pt = utils.getRotatedPoint( const pt = utils.getRotatedPoint(this.getRotationHandlePosition(), cos, sin, ct);
this.getRotationHandlePosition(),
cos,
sin,
ct
);
if (this.rotationShape.node != null) { if (this.rotationShape.node != null) {
this.moveSizerTo(this.rotationShape, pt.x, pt.y); this.moveSizerTo(this.rotationShape, pt.x, pt.y);
// Hides rotation handle during text editing // Hides rotation handle during text editing
this.rotationShape.node.style.visibility = this.rotationShape.node.style.visibility =
this.state.view.graph.isEditing() || !this.handlesVisible this.state.view.graph.isEditing() || !this.handlesVisible ? 'hidden' : '';
? 'hidden'
: '';
} }
} }
if (this.selectionBorder != null) { if (this.selectionBorder != null) {
this.selectionBorder.rotation = Number( this.selectionBorder.rotation = Number(this.state.style.rotation || '0');
this.state.style.rotation || '0'
);
} }
if (this.edgeHandlers != null) { if (this.edgeHandlers != null) {
@ -2107,9 +2018,7 @@ class VertexHandler {
* Returns true if the given custom handle is visible. * Returns true if the given custom handle is visible.
*/ */
isCustomHandleVisible(handle) { isCustomHandleVisible(handle) {
return ( return !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1;
!this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1
);
} }
/** /**
@ -2162,10 +2071,7 @@ class VertexHandler {
this.parentHighlight.redraw(); this.parentHighlight.redraw();
} }
} else { } else {
if ( if (pstate != null && pstate.parentHighlight === this.parentHighlight) {
pstate != null &&
pstate.parentHighlight === this.parentHighlight
) {
pstate.parentHighlight = null; pstate.parentHighlight = null;
} }
@ -2173,18 +2079,12 @@ class VertexHandler {
this.parentHighlight = null; this.parentHighlight = null;
} }
} else if (this.parentHighlightEnabled && visible) { } else if (this.parentHighlightEnabled && visible) {
if ( if (parent.isVertex() && pstate != null && pstate.parentHighlight == null) {
parent.isVertex() &&
pstate != null &&
pstate.parentHighlight == null
) {
this.parentHighlight = this.createParentHighlightShape(pstate); this.parentHighlight = this.createParentHighlightShape(pstate);
// VML dialect required here for event transparency in IE // VML dialect required here for event transparency in IE
this.parentHighlight.dialect = DIALECT_SVG; this.parentHighlight.dialect = DIALECT_SVG;
this.parentHighlight.pointerEvents = false; this.parentHighlight.pointerEvents = false;
this.parentHighlight.rotation = Number( this.parentHighlight.rotation = Number(pstate.style.rotation || '0');
pstate.style.rotation || '0'
);
this.parentHighlight.init(this.graph.getView().getOverlayPane()); this.parentHighlight.init(this.graph.getView().getOverlayPane());
this.parentHighlight.redraw(); this.parentHighlight.redraw();
@ -2207,10 +2107,7 @@ class VertexHandler {
if (this.preview.node.parentNode === this.graph.container) { if (this.preview.node.parentNode === this.graph.container) {
this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1); this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1);
this.preview.bounds.height = Math.max( this.preview.bounds.height = Math.max(0, this.preview.bounds.height - 1);
0,
this.preview.bounds.height - 1
);
} }
this.preview.rotation = Number(this.state.style.rotation || '0'); this.preview.rotation = Number(this.state.style.rotation || '0');

View File

@ -19,7 +19,13 @@ import {
TOOLTIP_VERTICAL_OFFSET, TOOLTIP_VERTICAL_OFFSET,
VALID_COLOR, VALID_COLOR,
} from '../../util/Constants'; } from '../../util/Constants';
import utils, { convertPoint, getOffset, getRotatedPoint, getValue, toRadians } from '../../util/Utils'; import utils, {
convertPoint,
getOffset,
getRotatedPoint,
getValue,
toRadians,
} from '../../util/Utils';
import InternalMouseEvent from '../event/InternalMouseEvent'; import InternalMouseEvent from '../event/InternalMouseEvent';
import ImageShape from '../geometry/shape/node/ImageShape'; import ImageShape from '../geometry/shape/node/ImageShape';
import CellMarker from '../cell/CellMarker'; import CellMarker from '../cell/CellMarker';
@ -36,7 +42,7 @@ import {
isShiftDown, isShiftDown,
} from '../../util/EventUtils'; } from '../../util/EventUtils';
import graph from '../Graph'; import graph from '../Graph';
import Image from '../image/Image'; import Image from '../image/ImageBox';
import CellState from '../cell/datatypes/CellState'; import CellState from '../cell/datatypes/CellState';
import Graph from '../Graph'; import Graph from '../Graph';
import ConnectionConstraint from './ConnectionConstraint'; import ConnectionConstraint from './ConnectionConstraint';
@ -201,8 +207,7 @@ type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell;
* the <mxCell> that represents the new edge. * the <mxCell> that represents the new edge.
*/ */
class ConnectionHandler extends EventSource { class ConnectionHandler extends EventSource {
constructor(graph: Graph, constructor(graph: Graph, factoryMethod: FactoryMethod | null = null) {
factoryMethod: FactoryMethod | null = null) {
super(); super();
this.graph = graph; this.graph = graph;
@ -660,7 +665,7 @@ class ConnectionHandler extends EventSource {
} }
return cell; return cell;
}; }
// Sets the highlight color according to validateConnection // Sets the highlight color according to validateConnection
isValidState(state: CellState) { isValidState(state: CellState) {
@ -696,11 +701,7 @@ class ConnectionHandler extends EventSource {
* *
* Starts a new connection for the given state and coordinates. * Starts a new connection for the given state and coordinates.
*/ */
start(state: CellState, start(state: CellState, x: number, y: number, edgeState: CellState): void {
x: number,
y: number,
edgeState: CellState): void {
this.previous = state; this.previous = state;
this.first = new Point(x, y); this.first = new Point(x, y);
this.edgeState = edgeState != null ? edgeState : this.createEdgeState(null); this.edgeState = edgeState != null ? edgeState : this.createEdgeState(null);
@ -733,8 +734,7 @@ class ConnectionHandler extends EventSource {
* cell - <mxCell> that represents the source terminal. * cell - <mxCell> that represents the source terminal.
* me - <mxMouseEvent> that is associated with this call. * me - <mxMouseEvent> that is associated with this call.
*/ */
isValidSource(cell: Cell, isValidSource(cell: Cell, me: InternalMouseEvent): boolean {
me: InternalMouseEvent): boolean {
return this.graph.isValidSource(cell); return this.graph.isValidSource(cell);
} }
@ -765,8 +765,7 @@ class ConnectionHandler extends EventSource {
* source - <mxCell> that represents the source terminal. * source - <mxCell> that represents the source terminal.
* target - <mxCell> that represents the target terminal. * target - <mxCell> that represents the target terminal.
*/ */
validateConnection(source: Cell, validateConnection(source: Cell, target: Cell): string {
target: Cell): string {
if (!this.isValidTarget(target)) { if (!this.isValidTarget(target)) {
return ''; return '';
} }
@ -798,10 +797,7 @@ class ConnectionHandler extends EventSource {
* state - <mxCellState> whose connect icons should be returned. * state - <mxCellState> whose connect icons should be returned.
*/ */
isMoveIconToFrontForState(state: CellState): boolean { isMoveIconToFrontForState(state: CellState): boolean {
if ( if (state.text != null && state.text.node.parentNode === this.graph.container) {
state.text != null &&
state.text.node.parentNode === this.graph.container
) {
return true; return true;
} }
return this.moveIconFront; return this.moveIconFront;
@ -841,10 +837,7 @@ class ConnectionHandler extends EventSource {
// Move the icon back in the overlay pane // Move the icon back in the overlay pane
if (this.moveIconBack && icon.node.previousSibling != null) { if (this.moveIconBack && icon.node.previousSibling != null) {
icon.node.parentNode.insertBefore( icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
icon.node,
icon.node.parentNode.firstChild
);
} }
} }
@ -886,8 +879,7 @@ class ConnectionHandler extends EventSource {
* *
* icons - Optional array of <mxImageShapes> to be redrawn. * icons - Optional array of <mxImageShapes> to be redrawn.
*/ */
redrawIcons(icons?: ImageShape[] | null, redrawIcons(icons?: ImageShape[] | null, state?: CellState): void {
state?: CellState): void {
if (icons != null && icons[0] != null && state != null) { if (icons != null && icons[0] != null && state != null) {
const pos = this.getIconPosition(icons[0], state); const pos = this.getIconPosition(icons[0], state);
icons[0].bounds.x = pos.x; icons[0].bounds.x = pos.x;
@ -897,8 +889,7 @@ class ConnectionHandler extends EventSource {
} }
// TODO: Document me! =========================================================================================================== // TODO: Document me! ===========================================================================================================
getIconPosition(icon: ImageShape, getIconPosition(icon: ImageShape, state: CellState): Point {
state: CellState): Point {
const { scale } = this.graph.getView(); const { scale } = this.graph.getView();
let cx = state.getCenterX(); let cx = state.getCenterX();
let cy = state.getCenterY(); let cy = state.getCenterY();
@ -909,9 +900,7 @@ class ConnectionHandler extends EventSource {
cx = size.width !== 0 ? state.x + (size.width * scale) / 2 : cx; cx = size.width !== 0 ? state.x + (size.width * scale) / 2 : cx;
cy = size.height !== 0 ? state.y + (size.height * scale) / 2 : cy; cy = size.height !== 0 ? state.y + (size.height * scale) / 2 : cy;
const alpha = toRadians( const alpha = toRadians(getValue(state.style, 'rotation') || 0);
getValue(state.style, 'rotation') || 0
);
if (alpha !== 0) { if (alpha !== 0) {
const cos = Math.cos(alpha); const cos = Math.cos(alpha);
@ -1066,10 +1055,8 @@ class ConnectionHandler extends EventSource {
const left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); const left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
const gridX = const gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left;
this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; const gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
const gridY =
this.currentPoint.y - this.graph.container.scrollTop + offset.y - top;
return ( return (
this.outlineConnect && this.outlineConnect &&
@ -1089,15 +1076,12 @@ class ConnectionHandler extends EventSource {
* Updates the current state for a given mouse move event by using * Updates the current state for a given mouse move event by using
* the <marker>. * the <marker>.
*/ */
updateCurrentState(me: InternalMouseEvent, updateCurrentState(me: InternalMouseEvent, point: Point): void {
point: Point): void {
this.constraintHandler.update( this.constraintHandler.update(
me, me,
this.first == null, this.first == null,
false, false,
this.first == null || me.isSource(this.marker.highlight.shape) this.first == null || me.isSource(this.marker.highlight.shape) ? null : point
? null
: point
); );
if ( if (
@ -1109,8 +1093,7 @@ class ConnectionHandler extends EventSource {
if ( if (
this.marker.highlight != null && this.marker.highlight != null &&
this.marker.highlight.state != null && this.marker.highlight.state != null &&
this.marker.highlight.state.cell === this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell
this.constraintHandler.currentFocus.cell
) { ) {
// Direct repaint needed if cell already highlighted // Direct repaint needed if cell already highlighted
if (this.marker.highlight.shape.stroke !== 'transparent') { if (this.marker.highlight.shape.stroke !== 'transparent') {
@ -1118,10 +1101,7 @@ class ConnectionHandler extends EventSource {
this.marker.highlight.repaint(); this.marker.highlight.repaint();
} }
} else { } else {
this.marker.markCell( this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent');
this.constraintHandler.currentFocus.cell,
'transparent'
);
} }
// Updates validation state // Updates validation state
@ -1137,8 +1117,7 @@ class ConnectionHandler extends EventSource {
if ( if (
this.error != null || this.error != null ||
(this.currentState != null && (this.currentState != null && !this.isCellEnabled(this.currentState.cell))
!this.isCellEnabled(this.currentState.cell))
) { ) {
this.constraintHandler.reset(); this.constraintHandler.reset();
} }
@ -1152,10 +1131,7 @@ class ConnectionHandler extends EventSource {
this.currentState = this.marker.getValidState(); this.currentState = this.marker.getValidState();
} }
if ( if (this.currentState != null && !this.isCellEnabled(this.currentState.cell)) {
this.currentState != null &&
!this.isCellEnabled(this.currentState.cell)
) {
this.constraintHandler.reset(); this.constraintHandler.reset();
this.marker.reset(); this.marker.reset();
this.currentState = null; this.currentState = null;
@ -1170,21 +1146,14 @@ class ConnectionHandler extends EventSource {
point = new point(me.getGraphX(), me.getGraphY()); point = new point(me.getGraphX(), me.getGraphY());
} }
const constraint = this.graph.getOutlineConstraint( const constraint = this.graph.getOutlineConstraint(point, this.currentState, me);
point,
this.currentState,
me
);
this.constraintHandler.setFocus(me, this.currentState, false); this.constraintHandler.setFocus(me, this.currentState, false);
this.constraintHandler.currentConstraint = constraint; this.constraintHandler.currentConstraint = constraint;
this.constraintHandler.currentPoint = point; this.constraintHandler.currentPoint = point;
} }
if (this.outlineConnect) { if (this.outlineConnect) {
if ( if (this.marker.highlight != null && this.marker.highlight.shape != null) {
this.marker.highlight != null &&
this.marker.highlight.shape != null
) {
const s = this.graph.view.scale; const s = this.graph.view.scale;
if ( if (
@ -1192,7 +1161,7 @@ class ConnectionHandler extends EventSource {
this.constraintHandler.currentFocus != null this.constraintHandler.currentFocus != null
) { ) {
this.marker.highlight.shape.stroke = OUTLINE_HIGHLIGHT_COLOR; this.marker.highlight.shape.stroke = OUTLINE_HIGHLIGHT_COLOR;
this.marker.highlight.shape.strokewidth = this.marker.highlight.shape.strokeWidth =
OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s; OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s;
this.marker.highlight.repaint(); this.marker.highlight.repaint();
} else if (this.marker.hasValidState()) { } else if (this.marker.hasValidState()) {
@ -1209,8 +1178,7 @@ class ConnectionHandler extends EventSource {
this.marker.highlight.shape.stroke = DEFAULT_VALID_COLOR; this.marker.highlight.shape.stroke = DEFAULT_VALID_COLOR;
} }
this.marker.highlight.shape.strokewidth = this.marker.highlight.shape.strokeWidth = HIGHLIGHT_STROKEWIDTH / s / s;
HIGHLIGHT_STROKEWIDTH / s / s;
this.marker.highlight.repaint(); this.marker.highlight.repaint();
} }
} }
@ -1246,8 +1214,7 @@ class ConnectionHandler extends EventSource {
* Called to snap the given point to the current preview. This snaps to the * Called to snap the given point to the current preview. This snaps to the
* first point of the preview if alt is not pressed. * first point of the preview if alt is not pressed.
*/ */
snapToPreview(me: MouseEvent, snapToPreview(me: MouseEvent, point: Point): void {
point: Point): void {
if (!isAltDown(me.getEvent()) && this.previous != null) { if (!isAltDown(me.getEvent()) && this.previous != null) {
const tol = (this.graph.gridSize * this.graph.view.scale) / 2; const tol = (this.graph.gridSize * this.graph.view.scale) / 2;
const tmp = const tmp =
@ -1271,8 +1238,7 @@ class ConnectionHandler extends EventSource {
* Handles the event by updating the preview edge or by highlighting * Handles the event by updating the preview edge or by highlighting
* a possible source or target terminal. * a possible source or target terminal.
*/ */
mouseMove(sender: MouseEvent, mouseMove(sender: MouseEvent, me: InternalMouseEvent): void {
me: InternalMouseEvent): void {
if ( if (
!me.isConsumed() && !me.isConsumed() &&
(this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown) (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown)
@ -1344,10 +1310,7 @@ class ConnectionHandler extends EventSource {
const h = this.selectedIcon.bounds.height; const h = this.selectedIcon.bounds.height;
if (this.currentState != null && this.targetConnectImage) { if (this.currentState != null && this.targetConnectImage) {
const pos = this.getIconPosition( const pos = this.getIconPosition(this.selectedIcon, this.currentState);
this.selectedIcon,
this.currentState
);
this.selectedIcon.bounds.x = pos.x; this.selectedIcon.bounds.x = pos.x;
this.selectedIcon.bounds.y = pos.y; this.selectedIcon.bounds.y = pos.y;
} else { } else {
@ -1402,10 +1365,7 @@ class ConnectionHandler extends EventSource {
if (this.currentState == null && this.movePreviewAway) { if (this.currentState == null && this.movePreviewAway) {
let tmp = pt2; let tmp = pt2;
if ( if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2) {
this.edgeState != null &&
this.edgeState.absolutePoints.length >= 2
) {
const tmp2 = this.edgeState.absolutePoints[ const tmp2 = this.edgeState.absolutePoints[
this.edgeState.absolutePoints.length - 2 this.edgeState.absolutePoints.length - 2
]; ];
@ -1476,10 +1436,7 @@ class ConnectionHandler extends EventSource {
me.consume(); me.consume();
} else if (!this.isEnabled() || !this.graph.isEnabled()) { } else if (!this.isEnabled() || !this.graph.isEnabled()) {
this.constraintHandler.reset(); this.constraintHandler.reset();
} else if ( } else if (this.previous !== this.currentState && this.edgeState == null) {
this.previous !== this.currentState &&
this.edgeState == null
) {
this.destroyIcons(); this.destroyIcons();
// Sets the cursor on the current shape // Sets the cursor on the current shape
@ -1507,18 +1464,13 @@ class ConnectionHandler extends EventSource {
me.consume(); me.consume();
} }
if ( if (!this.graph.isMouseDown && this.currentState != null && this.icons != null) {
!this.graph.isMouseDown &&
this.currentState != null &&
this.icons != null
) {
let hitsIcon = false; let hitsIcon = false;
const target = me.getSource(); const target = me.getSource();
for (let i = 0; i < this.icons.length && !hitsIcon; i += 1) { for (let i = 0; i < this.icons.length && !hitsIcon; i += 1) {
hitsIcon = hitsIcon =
target === this.icons[i].node || target === this.icons[i].node || target.parentNode === this.icons[i].node;
target.parentNode === this.icons[i].node;
} }
if (!hitsIcon) { if (!hitsIcon) {
@ -1535,8 +1487,7 @@ class ConnectionHandler extends EventSource {
* *
* Updates <edgeState>. * Updates <edgeState>.
*/ */
updateEdgeState(current: CellState, updateEdgeState(current: CellState, constraint: CellState): void {
constraint: CellState): void {
// TODO: Use generic method for writing constraint to style // TODO: Use generic method for writing constraint to style
if (this.sourceConstraint != null && this.sourceConstraint.point != null) { if (this.sourceConstraint != null && this.sourceConstraint.point != null) {
this.edgeState.style.exitX = this.sourceConstraint.point.x; this.edgeState.style.exitX = this.sourceConstraint.point.x;
@ -1551,10 +1502,7 @@ class ConnectionHandler extends EventSource {
delete this.edgeState.style.entryY; delete this.edgeState.style.entryY;
} }
this.edgeState.absolutePoints = [ this.edgeState.absolutePoints = [null, this.currentState != null ? null : current];
null,
this.currentState != null ? null : current,
];
this.graph.view.updateFixedTerminalPoint( this.graph.view.updateFixedTerminalPoint(
this.edgeState, this.edgeState,
this.previous, this.previous,
@ -1616,8 +1564,7 @@ class ConnectionHandler extends EventSource {
* state - <mxCellState> that represents the target cell state. * state - <mxCellState> that represents the target cell state.
* me - <mxMouseEvent> that represents the mouse move. * me - <mxMouseEvent> that represents the mouse move.
*/ */
getTargetPerimeterPoint(state: CellState, getTargetPerimeterPoint(state: CellState, me: MouseEvent): Point {
me: MouseEvent): Point {
let result = null; let result = null;
const { view } = state; const { view } = state;
const targetPerimeter = view.getPerimeterFunction(state); const targetPerimeter = view.getPerimeterFunction(state);
@ -1656,9 +1603,7 @@ class ConnectionHandler extends EventSource {
* next - <mxPoint> that represents the next point along the previewed edge. * next - <mxPoint> that represents the next point along the previewed edge.
* me - <mxMouseEvent> that represents the mouse move. * me - <mxMouseEvent> that represents the mouse move.
*/ */
getSourcePerimeterPoint(state: CellState, getSourcePerimeterPoint(state: CellState, next: Point, me: MouseEvent): Point {
next: Point,
me: MouseEvent): Point {
let result = null; let result = null;
const { view } = state; const { view } = state;
const sourcePerimeter = view.getPerimeterFunction(state); const sourcePerimeter = view.getPerimeterFunction(state);
@ -1677,12 +1622,7 @@ class ConnectionHandler extends EventSource {
); );
} }
let tmp = sourcePerimeter( let tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false);
view.getPerimeterBounds(state),
state,
next,
false
);
if (tmp != null) { if (tmp != null) {
if (theta !== 0) { if (theta !== 0) {
@ -1715,9 +1655,7 @@ class ConnectionHandler extends EventSource {
* icons - Array of currently displayed icons. * icons - Array of currently displayed icons.
* me - <mxMouseEvent> that contains the mouse event. * me - <mxMouseEvent> that contains the mouse event.
*/ */
updateIcons(state: CellState, updateIcons(state: CellState, icons: string[], me: InternalMouseEvent): void {
icons: string[],
me: InternalMouseEvent): void {
// empty // empty
} }
@ -1739,11 +1677,7 @@ class ConnectionHandler extends EventSource {
* Adds the waypoint for the given event to <waypoints>. * Adds the waypoint for the given event to <waypoints>.
*/ */
addWaypointForEvent(me: InternalMouseEvent): void { addWaypointForEvent(me: InternalMouseEvent): void {
let point = convertPoint( let point = convertPoint(this.graph.container, me.getX(), me.getY());
this.graph.container,
me.getX(),
me.getY()
);
const dx = Math.abs(point.x - this.first.x); const dx = Math.abs(point.x - this.first.x);
const dy = Math.abs(point.y - this.first.y); const dy = Math.abs(point.y - this.first.y);
const addPoint = const addPoint =
@ -1790,8 +1724,7 @@ class ConnectionHandler extends EventSource {
* *
* Handles the event by inserting the new connection. * Handles the event by inserting the new connection.
*/ */
mouseUp(sender: InternalMouseEvent, mouseUp(sender: InternalMouseEvent, me: InternalMouseEvent): void {
me: InternalMouseEvent): void {
if (!me.isConsumed() && this.isConnecting()) { if (!me.isConsumed() && this.isConnecting()) {
if (this.waypointsEnabled && !this.isStopEvent(me)) { if (this.waypointsEnabled && !this.isStopEvent(me)) {
this.addWaypointForEvent(me); this.addWaypointForEvent(me);
@ -1907,7 +1840,7 @@ class ConnectionHandler extends EventSource {
* returned. * returned.
*/ */
updatePreview(valid: boolean): void { updatePreview(valid: boolean): void {
this.shape.strokewidth = this.getEdgeWidth(valid); this.shape.strokeWidth = this.getEdgeWidth(valid);
this.shape.stroke = this.getEdgeColor(valid); this.shape.stroke = this.getEdgeColor(valid);
} }
@ -1955,15 +1888,8 @@ class ConnectionHandler extends EventSource {
* dropTarget - <mxCell> that represents the cell under the mouse when it was * dropTarget - <mxCell> that represents the cell under the mouse when it was
* released. * released.
*/ */
connect(source: Cell, connect(source: Cell, target: Cell, evt: MouseEvent, dropTarget: Cell): void {
target: Cell, if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges) {
evt: MouseEvent,
dropTarget: Cell): void {
if (
target != null ||
this.isCreateTarget(evt) ||
this.graph.allowDanglingEdges
) {
// Uses the common parent of source and target or // Uses the common parent of source and target or
// the default parent to insert the edge // the default parent to insert the edge
const model = this.graph.getModel(); const model = this.graph.getModel();
@ -2036,12 +1962,7 @@ class ConnectionHandler extends EventSource {
if (edge != null) { if (edge != null) {
// Updates the connection constraints // Updates the connection constraints
this.graph.setConnectionConstraint( this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint);
edge,
source,
true,
this.sourceConstraint
);
this.graph.setConnectionConstraint( this.graph.setConnectionConstraint(
edge, edge,
target, target,
@ -2070,11 +1991,7 @@ class ConnectionHandler extends EventSource {
tmp = tmp.getParent(); tmp = tmp.getParent();
} }
if ( if (tmp != null && tmp.parent != null && tmp.parent === edge.parent) {
tmp != null &&
tmp.parent != null &&
tmp.parent === edge.parent
) {
model.add(parent, edge, tmp.parent.getIndex(tmp)); model.add(parent, edge, tmp.parent.getIndex(tmp));
} }
} }
@ -2110,10 +2027,7 @@ class ConnectionHandler extends EventSource {
this.originalPoint.x / s - t.x, this.originalPoint.x / s - t.x,
this.originalPoint.y / s - t.y this.originalPoint.y / s - t.y
) )
: new Point( : new Point(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y);
this.currentPoint.x / s - t.x,
this.currentPoint.y / s - t.y
);
pt.x -= this.graph.panDx / this.graph.view.scale; pt.x -= this.graph.panDx / this.graph.view.scale;
pt.y -= this.graph.panDy / this.graph.view.scale; pt.y -= this.graph.panDy / this.graph.view.scale;
geo.setTerminalPoint(pt, false); geo.setTerminalPoint(pt, false);
@ -2154,8 +2068,7 @@ class ConnectionHandler extends EventSource {
* Selects the given edge after adding a new connection. The target argument * Selects the given edge after adding a new connection. The target argument
* contains the target vertex if one has been inserted. * contains the target vertex if one has been inserted.
*/ */
selectCells(edge: Cell, selectCells(edge: Cell, target: Cell): void {
target: Cell): void {
this.graph.setSelectionCell(edge); this.graph.setSelectionCell(edge);
} }
@ -2166,13 +2079,14 @@ class ConnectionHandler extends EventSource {
* implementation does only use <createEdge> if <factoryMethod> is defined, * implementation does only use <createEdge> if <factoryMethod> is defined,
* otherwise <mxGraph.insertEdge> will be used. * otherwise <mxGraph.insertEdge> will be used.
*/ */
insertEdge(parent: Cell, insertEdge(
id: string, parent: Cell,
value: any, id: string,
source: Cell, value: any,
target: Cell, source: Cell,
style: string): Cell { target: Cell,
style: string
): Cell {
if (this.factoryMethod == null) { if (this.factoryMethod == null) {
return this.graph.insertEdge(parent, id, value, source, target, style); return this.graph.insertEdge(parent, id, value, source, target, style);
} }
@ -2194,8 +2108,7 @@ class ConnectionHandler extends EventSource {
* evt - Mousedown event of the connect gesture. * evt - Mousedown event of the connect gesture.
* source - <mxCell> that represents the source terminal. * source - <mxCell> that represents the source terminal.
*/ */
createTargetVertex(evt: MouseEvent, createTargetVertex(evt: MouseEvent, source: Cell): Cell {
source: Cell): Cell {
// Uses the first non-relative source // Uses the first non-relative source
let geo = source.getGeometry(); let geo = source.getGeometry();
@ -2267,10 +2180,7 @@ class ConnectionHandler extends EventSource {
* target - <mxCell> that represents the target terminal. * target - <mxCell> that represents the target terminal.
* style - Optional style from the preview edge. * style - Optional style from the preview edge.
*/ */
createEdge(value?: any, createEdge(value?: any, source?: Cell, target?: Cell, style?: string): Cell {
source?: Cell,
target?: Cell,
style?: string): Cell {
let edge = null; let edge = null;
// Creates a new edge using the factoryMethod // Creates a new edge using the factoryMethod

View File

@ -4,7 +4,7 @@
* Updated to ES9 syntax by David Morrissey 2021 * Updated to ES9 syntax by David Morrissey 2021
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Image from '../image/Image'; import Image from '../image/ImageBox';
import mxClient from '../../mxClient'; import mxClient from '../../mxClient';
import { import {
DEFAULT_VALID_COLOR, DEFAULT_VALID_COLOR,
@ -240,11 +240,7 @@ class ConstraintHandler {
this.reset(); this.reset();
}; };
InternalEvent.addListener( InternalEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);
this.graph.container,
'mouseleave',
this.resetHandler
);
} }
const tol = this.getTolerance(me); const tol = this.getTolerance(me);
@ -296,12 +292,7 @@ class ConstraintHandler {
if ( if (
(this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (this.intersects(this.focusIcons[i], mouse, source, existingEdge) ||
(point != null && (point != null &&
this.intersects( this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
this.focusIcons[i],
grid,
source,
existingEdge
))) &&
(minDistSq == null || tmp < minDistSq) (minDistSq == null || tmp < minDistSq)
) { ) {
this.currentConstraint = this.constraints[i]; this.currentConstraint = this.constraints[i];
@ -358,12 +349,7 @@ class ConstraintHandler {
) { ) {
const state = this.graph.view.getState(this.currentFocus.cell); const state = this.graph.view.getState(this.currentFocus.cell);
this.currentFocus = state; this.currentFocus = state;
this.currentFocusArea = new Rectangle( this.currentFocusArea = new Rectangle(state.x, state.y, state.width, state.height);
state.x,
state.y,
state.width,
state.height
);
for (let i = 0; i < this.constraints.length; i += 1) { for (let i = 0; i < this.constraints.length; i += 1) {
const cp = this.graph.getConnectionPoint(state, this.constraints[i]); const cp = this.graph.getConnectionPoint(state, this.constraints[i]);
@ -391,9 +377,7 @@ class ConstraintHandler {
// setFocus(me: mxMouseEvent, state: mxCellState, source: mxCell): void; // setFocus(me: mxMouseEvent, state: mxCellState, source: mxCell): void;
setFocus(me, state, source) { setFocus(me, state, source) {
this.constraints = this.constraints =
state != null && state != null && !this.isStateIgnored(state, source) && state.cell.isConnectable()
!this.isStateIgnored(state, source) &&
state.cell.isConnectable()
? this.isEnabled() ? this.isEnabled()
? this.graph.getAllConnectionConstraints(state, source) || [] ? this.graph.getAllConnectionConstraints(state, source) || []
: [] : []
@ -402,12 +386,7 @@ class ConstraintHandler {
// Only uses cells which have constraints // Only uses cells which have constraints
if (this.constraints != null) { if (this.constraints != null) {
this.currentFocus = state; this.currentFocus = state;
this.currentFocusArea = new Rectangle( this.currentFocusArea = new Rectangle(state.x, state.y, state.width, state.height);
state.x,
state.y,
state.width,
state.height
);
if (this.focusIcons != null) { if (this.focusIcons != null) {
for (let i = 0; i < this.focusIcons.length; i += 1) { for (let i = 0; i < this.focusIcons.length; i += 1) {
@ -440,10 +419,7 @@ class ConstraintHandler {
// Move the icon behind all other overlays // Move the icon behind all other overlays
if (icon.node.previousSibling != null) { if (icon.node.previousSibling != null) {
icon.node.parentNode.insertBefore( icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
icon.node,
icon.node.parentNode.firstChild
);
} }
const getState = () => { const getState = () => {

View File

@ -5,6 +5,8 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
type EventProperties = Record<string, any>;
/** /**
* Class: mxEventObject * Class: mxEventObject
* *
@ -29,9 +31,9 @@
* (end) * (end)
*/ */
class EventObject { class EventObject {
constructor(name: string, ...args: any[]) { constructor(name = '', ...args: any[]) {
this.name = name; this.name = name;
this.properties = []; this.properties = {};
if (!!args[0] && args[0].constructor === Object) { if (!!args[0] && args[0].constructor === Object) {
// A literal object ({}) // A literal object ({})
@ -41,7 +43,7 @@ class EventObject {
} else { } else {
// two-values [key, value, key, value, ...] // two-values [key, value, key, value, ...]
for (let i = 0; i < args.length; i += 2) { for (let i = 0; i < args.length; i += 2) {
if (args[i + 1] != null) { if (args[i + 1] !== null) {
this.properties[args[i]] = args[i + 1]; this.properties[args[i]] = args[i + 1];
} }
} }
@ -53,14 +55,14 @@ class EventObject {
* *
* Holds the name. * Holds the name.
*/ */
name: string = ''; name: string;
/** /**
* Variable: properties * Variable: properties
* *
* Holds the properties as an associative array. * Holds the properties as an associative array.
*/ */
properties: any = null; properties: EventProperties;
/** /**
* Variable: consumed * Variable: consumed
@ -74,7 +76,7 @@ class EventObject {
* *
* Returns <name>. * Returns <name>.
*/ */
getName(): string { getName() {
return this.name; return this.name;
} }
@ -83,7 +85,7 @@ class EventObject {
* *
* Returns <properties>. * Returns <properties>.
*/ */
getProperties(): any { getProperties() {
return this.properties; return this.properties;
} }
@ -92,7 +94,7 @@ class EventObject {
* *
* Returns the property for the given key. * Returns the property for the given key.
*/ */
getProperty(key: string): any { getProperty(key: string) {
return this.properties[key]; return this.properties[key];
} }
@ -101,7 +103,7 @@ class EventObject {
* *
* Returns true if the event has been consumed. * Returns true if the event has been consumed.
*/ */
isConsumed(): boolean { isConsumed() {
return this.consumed; return this.consumed;
} }
@ -110,7 +112,7 @@ class EventObject {
* *
* Consumes the event. * Consumes the event.
*/ */
consume(): void { consume() {
this.consumed = true; this.consumed = true;
} }
} }

View File

@ -7,6 +7,11 @@
import EventObject from './EventObject'; import EventObject from './EventObject';
type EventListener = {
funct: Function;
name: string;
};
/** /**
* Class: mxEventSource * Class: mxEventSource
* *
@ -30,7 +35,7 @@ import EventObject from './EventObject';
* Constructs a new event source. * Constructs a new event source.
*/ */
class EventSource { class EventSource {
constructor(eventSource: EventSource | null=null) { constructor(eventSource: EventSource | null = null) {
this.eventSource = eventSource; this.eventSource = eventSource;
} }
@ -41,14 +46,14 @@ class EventSource {
* contains the event name followed by the respective listener for each * contains the event name followed by the respective listener for each
* registered listener. * registered listener.
*/ */
eventListeners: ({funct: Function, name: string})[] = []; eventListeners: EventListener[] = [];
/** /**
* Variable: eventsEnabled * Variable: eventsEnabled
* *
* Specifies if events can be fired. Default is true. * Specifies if events can be fired. Default is true.
*/ */
eventsEnabled: boolean = true; eventsEnabled = true;
/** /**
* Variable: eventSource * Variable: eventSource
@ -62,7 +67,7 @@ class EventSource {
* *
* Returns <eventsEnabled>. * Returns <eventsEnabled>.
*/ */
isEventsEnabled(): boolean { isEventsEnabled() {
return this.eventsEnabled; return this.eventsEnabled;
} }
@ -71,7 +76,7 @@ class EventSource {
* *
* Sets <eventsEnabled>. * Sets <eventsEnabled>.
*/ */
setEventsEnabled(value: boolean): void { setEventsEnabled(value: boolean) {
this.eventsEnabled = value; this.eventsEnabled = value;
} }
@ -80,7 +85,7 @@ class EventSource {
* *
* Returns <eventSource>. * Returns <eventSource>.
*/ */
getEventSource(): EventSource | null { getEventSource() {
return this.eventSource; return this.eventSource;
} }
@ -89,7 +94,7 @@ class EventSource {
* *
* Sets <eventSource>. * Sets <eventSource>.
*/ */
setEventSource(value: EventSource): void { setEventSource(value: EventSource) {
this.eventSource = value; this.eventSource = value;
} }
@ -101,13 +106,8 @@ class EventSource {
* *
* The parameters of the listener are the sender and an <mxEventObject>. * The parameters of the listener are the sender and an <mxEventObject>.
*/ */
addListener(name: string, addListener(name: string, funct: (...args: any[]) => any) {
funct: (...args: any[]) => any): void { this.eventListeners.push({ name, funct });
if (this.eventListeners == null) {
this.eventListeners = [];
}
this.eventListeners.push({name, funct});
} }
/** /**
@ -115,16 +115,14 @@ class EventSource {
* *
* Removes all occurrences of the given listener from <eventListeners>. * Removes all occurrences of the given listener from <eventListeners>.
*/ */
removeListener(funct: (...args: any[]) => any): void { removeListener(funct: (...args: any[]) => any) {
if (this.eventListeners != null) { let i = 0;
let i = 0;
while (i < this.eventListeners.length) { while (i < this.eventListeners.length) {
if (this.eventListeners[i].funct === funct) { if (this.eventListeners[i].funct === funct) {
this.eventListeners.splice(i, 1); this.eventListeners.splice(i, 1);
} else { } else {
i += 1; i += 1;
}
} }
} }
} }
@ -148,21 +146,21 @@ class EventSource {
* sender - Optional sender to be passed to the listener. Default value is * sender - Optional sender to be passed to the listener. Default value is
* the return value of <getEventSource>. * the return value of <getEventSource>.
*/ */
fireEvent(evt: EventObject, sender: any = null): void { fireEvent(evt: EventObject, sender: any = null) {
if (this.eventListeners != null && this.isEventsEnabled()) { if (this.isEventsEnabled()) {
if (evt == null) { if (!evt) {
evt = new EventObject(''); evt = new EventObject('');
} }
if (sender == null) { if (!sender) {
sender = this.getEventSource(); sender = this.getEventSource();
} }
if (sender == null) { if (!sender) {
sender = this; sender = this;
} }
for (const eventListener of this.eventListeners) { for (const eventListener of this.eventListeners) {
if (eventListener.name == null || eventListener.name === evt.getName()) { if (eventListener.name === null || eventListener.name === evt.getName()) {
eventListener.funct.apply(this, [sender, evt]); eventListener.funct.apply(this, [sender, evt]);
} }
} }

View File

@ -4,7 +4,13 @@
* Updated to ES9 syntax by David Morrissey 2021 * Updated to ES9 syntax by David Morrissey 2021
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import {getClientX, getClientY, getSource, isMouseEvent, isPopupTrigger} from '../../util/EventUtils'; import {
getClientX,
getClientY,
getSource,
isMouseEvent,
isPopupTrigger,
} from '../../util/EventUtils';
import { isAncestorNode } from '../../util/DomUtils'; import { isAncestorNode } from '../../util/DomUtils';
import CellState from '../cell/datatypes/CellState'; import CellState from '../cell/datatypes/CellState';
import Shape from '../geometry/shape/Shape'; import Shape from '../geometry/shape/Shape';
@ -119,11 +125,8 @@ class InternalMouseEvent {
* *
* Returns true if the given <mxShape> is the source of <evt>. * Returns true if the given <mxShape> is the source of <evt>.
*/ */
isSource(shape: Shape): boolean { isSource(shape: Shape) {
if (shape != null) { return shape ? isAncestorNode(shape.node, this.getSource()) : false;
return isAncestorNode(shape.node, this.getSource());
}
return false;
} }
/** /**
@ -131,7 +134,7 @@ class InternalMouseEvent {
* *
* Returns <evt.clientX>. * Returns <evt.clientX>.
*/ */
getX(): number { getX() {
return getClientX(this.getEvent()); return getClientX(this.getEvent());
} }
@ -140,7 +143,7 @@ class InternalMouseEvent {
* *
* Returns <evt.clientY>. * Returns <evt.clientY>.
*/ */
getY(): number { getY() {
return getClientY(this.getEvent()); return getClientY(this.getEvent());
} }

View File

@ -1,14 +1,14 @@
import Image from "../image/Image"; import Image from '../image/ImageBox';
import mxClient from "../../mxClient"; import mxClient from '../../mxClient';
import Graph from "../Graph"; import Graph from '../Graph';
import CellState from "../cell/datatypes/CellState"; import CellState from '../cell/datatypes/CellState';
import Cell from "../cell/datatypes/Cell"; import Cell from '../cell/datatypes/Cell';
import CellArray from "../cell/datatypes/CellArray"; import CellArray from '../cell/datatypes/CellArray';
import EventObject from "../event/EventObject"; import EventObject from '../event/EventObject';
import InternalEvent from "../event/InternalEvent"; import InternalEvent from '../event/InternalEvent';
import Geometry from "../geometry/Geometry"; import Geometry from '../geometry/Geometry';
import {getValue, toRadians} from "../../util/Utils"; import { getValue, toRadians } from '../../util/Utils';
import Rectangle from "../geometry/Rectangle"; import Rectangle from '../geometry/Rectangle';
/** /**
* GraphFoldingOptions * GraphFoldingOptions
@ -25,21 +25,22 @@ import Rectangle from "../geometry/Rectangle";
* a cell is first collapsed. * a cell is first collapsed.
*/ */
type GraphFoldingOptions = { type GraphFoldingOptions = {
foldingEnabled: boolean, foldingEnabled: boolean;
collapsedImage: Image, collapsedImage: Image;
expandedImage: Image, expandedImage: Image;
collapseToPreferredSize: boolean, collapseToPreferredSize: boolean;
}; };
class GraphFolding { class GraphFolding {
constructor(graph: Graph, constructor(
options: GraphFoldingOptions = { graph: Graph,
foldingEnabled: true, options: GraphFoldingOptions = {
collapsedImage: new Image(`${mxClient.imageBasePath}/collapsed.gif`, 9, 9), foldingEnabled: true,
expandedImage: new Image(`${mxClient.imageBasePath}/expanded.gif`, 9, 9), collapsedImage: new Image(`${mxClient.imageBasePath}/collapsed.gif`, 9, 9),
collapseToPreferredSize: true, expandedImage: new Image(`${mxClient.imageBasePath}/expanded.gif`, 9, 9),
}) { collapseToPreferredSize: true,
}
) {
this.graph = graph; this.graph = graph;
this.options = options; this.options = options;
} }
@ -53,8 +54,7 @@ class GraphFolding {
* the tooltip. * the tooltip.
* @default 'collapse-expand' * @default 'collapse-expand'
*/ */
collapseExpandResource: string = collapseExpandResource: string = mxClient.language != 'none' ? 'collapse-expand' : '';
mxClient.language != 'none' ? 'collapse-expand' : '';
/** /**
* *
@ -64,10 +64,7 @@ class GraphFolding {
/** /**
* Returns the cells which are movable in the given array of cells. * Returns the cells which are movable in the given array of cells.
*/ */
getFoldableCells( getFoldableCells(cells: CellArray, collapse: boolean = false): CellArray | null {
cells: CellArray,
collapse: boolean = false
): CellArray | null {
return this.graph.model.filterCells(cells, (cell: Cell) => { return this.graph.model.filterCells(cells, (cell: Cell) => {
return this.isCellFoldable(cell, collapse); return this.isCellFoldable(cell, collapse);
}); });
@ -272,12 +269,7 @@ class GraphFolding {
} }
} }
geo.alternateBounds = new Rectangle( geo.alternateBounds = new Rectangle(0, 0, bounds.width, bounds.height);
0,
0,
bounds.width,
bounds.height
);
} }
if (geo.alternateBounds != null) { if (geo.alternateBounds != null) {

View File

@ -7,7 +7,7 @@
import Point from './Point'; import Point from './Point';
import Rectangle from './Rectangle'; import Rectangle from './Rectangle';
import utils, { equalPoints, getRotatedPoint, toRadians } from '../../util/Utils'; import { equalPoints, getRotatedPoint, toRadians } from '../../util/Utils';
import { clone } from '../../util/CloneUtils'; import { clone } from '../../util/CloneUtils';
/** /**
@ -74,12 +74,7 @@ import { clone } from '../../util/CloneUtils';
* defines the absolute offset for the label inside the vertex or group. * defines the absolute offset for the label inside the vertex or group.
*/ */
class Geometry extends Rectangle { class Geometry extends Rectangle {
constructor( constructor(x = 0, y = 0, width = 0, height = 0) {
x: number = 0,
y: number = 0,
width: number = 0,
height: number = 0
) {
super(x, y, width, height); super(x, y, width, height);
} }

View File

@ -6,7 +6,9 @@
*/ */
import Rectangle from '../Rectangle'; import Rectangle from '../Rectangle';
import Shape from './Shape'; import Shape from './Shape';
import mxSvgCanvas2D from '../../../util/canvas/mxSvgCanvas2D'; import SvgCanvas2D from '../../../util/canvas/SvgCanvas2D';
import { ColorValue } from 'packages/core/src/types';
import { NONE } from 'packages/core/src/util/Constants';
/** /**
* Extends {@link Shape} to implement an actor shape. If a custom shape with one * Extends {@link Shape} to implement an actor shape. If a custom shape with one
@ -34,27 +36,21 @@ import mxSvgCanvas2D from '../../../util/canvas/mxSvgCanvas2D';
class Actor extends Shape { class Actor extends Shape {
constructor( constructor(
bounds: Rectangle | null = null, bounds: Rectangle | null = null,
fill: string | null = null, fill: ColorValue = NONE,
stroke: string | null = null, stroke: ColorValue = NONE,
strokewidth: number = 1 strokeWidth: number = 1
) { ) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
/** /**
* Redirects to redrawPath for subclasses to work. * Redirects to redrawPath for subclasses to work.
*/ */
paintVertexShape( paintVertexShape(c: SvgCanvas2D, x: number, y: number, w: number, h: number) {
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
c.translate(x, y); c.translate(x, y);
c.begin(); c.begin();
this.redrawPath(c, x, y, w, h); this.redrawPath(c, x, y, w, h);
@ -64,13 +60,7 @@ class Actor extends Shape {
/** /**
* Draws the path for this shape. * Draws the path for this shape.
*/ */
redrawPath( redrawPath(c: SvgCanvas2D, x: number, y: number, w: number, h: number) {
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
const width = w / 3; const width = w / 3;
c.moveTo(0, h); c.moveTo(0, h);
c.curveTo(0, (3 * h) / 5, 0, (2 * h) / 5, w / 2, (2 * h) / 5); c.curveTo(0, (3 * h) / 5, 0, (2 * h) / 5, w / 2, (2 * h) / 5);

View File

@ -8,7 +8,6 @@ import Rectangle from '../Rectangle';
import { import {
getBoundingBox, getBoundingBox,
getDirectedBounds, getDirectedBounds,
getValue,
isNotNullish, isNotNullish,
mod, mod,
} from '../../../util/Utils'; } from '../../../util/Utils';
@ -24,30 +23,22 @@ import {
SHADOW_OFFSET_Y, SHADOW_OFFSET_Y,
} from '../../../util/Constants'; } from '../../../util/Constants';
import Point from '../Point'; import Point from '../Point';
import mxSvgCanvas2D from '../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D';
import SvgCanvas2D from '../../../util/canvas/SvgCanvas2D';
import InternalEvent from '../../event/InternalEvent'; import InternalEvent from '../../event/InternalEvent';
import mxClient from '../../../mxClient'; import mxClient from '../../../mxClient';
import CellState from '../../cell/datatypes/CellState'; import CellState from '../../cell/datatypes/CellState';
import StencilShape from './node/StencilShape'; import StencilShape from './node/StencilShape';
import CellOverlay from '../../cell/CellOverlay'; import CellOverlay from '../../cell/CellOverlay';
import ImageBox from '../../image/ImageBox';
import type { import type {
ArrowType,
CellStateStyles, CellStateStyles,
ColorValue, ColorValue,
DirectionValue, DirectionValue,
GradientMap, GradientMap,
} from '../../../types'; } from '../../../types';
import Image from '../../image/Image';
const toBool = (i: any) => {
if (i === 0) return false;
if (i === 1) return true;
if (i === '0') return false;
if (i === '1') return true;
if (String(i).toLowerCase() === 'true') return true;
if (String(i).toLowerCase() === 'false') return false;
return !!i;
};
/** /**
* Base class for all shapes. * Base class for all shapes.
@ -94,9 +85,14 @@ const toBool = (i: any) => {
*/ */
class Shape { class Shape {
constructor(stencil: StencilShape | null = null) { constructor(stencil: StencilShape | null = null) {
// `stencil` is not null when instantiated directly,
// but can be null when instantiated through a child class.
if (stencil) { if (stencil) {
this.stencil = stencil; this.stencil = stencil;
} }
// moved from init()
this.node = this.create();
} }
/** /**
@ -109,13 +105,9 @@ class Shape {
* *
* container - DOM node that will contain the shape. * container - DOM node that will contain the shape.
*/ */
init(container: SVGElement | null = null) { init(container: SVGElement) {
if (!this.node) { if (!this.node.parentNode) {
this.node = this.create(); container.appendChild(this.node);
if (container) {
container.appendChild(this.node);
}
} }
} }
@ -125,7 +117,7 @@ class Shape {
* Sets the styles to their default values. * Sets the styles to their default values.
*/ */
initStyles() { initStyles() {
this.strokewidth = 1; this.strokeWidth = 1;
this.rotation = 0; this.rotation = 0;
this.opacity = 100; this.opacity = 100;
this.fillOpacity = 100; this.fillOpacity = 100;
@ -146,31 +138,31 @@ class Shape {
opacity = 100; opacity = 100;
isDashed = false; isDashed = false;
fill: string | null = null; fill: ColorValue = NONE;
gradient: string | null = null; gradient: ColorValue = NONE;
gradientDirection: string | null = null; gradientDirection: DirectionValue = DIRECTION_EAST;
fillOpacity = 100; fillOpacity = 100;
strokeOpacity: number | null = 100; strokeOpacity = 100;
stroke: string | null = null; stroke: ColorValue = NONE;
strokewidth: number | null = 1; strokeWidth = 1;
spacing: number | null = null; spacing = 0;
startSize: number | null = null; startSize = 1;
endSize: number | null = null; endSize = 1;
startArrow: string | null = null; startArrow: ArrowType = NONE;
endArrow: string | null = null; endArrow: ArrowType = NONE;
direction: DirectionValue | null = null; direction: DirectionValue = DIRECTION_EAST;
flipH = false; flipH = false;
@ -184,7 +176,7 @@ class Shape {
cursor = ''; cursor = '';
verticalTextRotation: number | null = null; verticalTextRotation = 0;
oldGradients: GradientMap = {}; oldGradients: GradientMap = {};
@ -238,7 +230,7 @@ class Shape {
* *
* Holds the outermost DOM node that represents this shape. * Holds the outermost DOM node that represents this shape.
*/ */
node: SVGGElement | null = null; node: SVGGElement;
/** /**
* Variable: state * Variable: state
@ -332,15 +324,15 @@ class Shape {
*/ */
useSvgBoundingBox = true; useSvgBoundingBox = true;
image: Image | null = null; image: ImageBox | null = null;
indicatorColor: ColorValue = null; indicatorColor: ColorValue = NONE;
indicatorStrokeColor: ColorValue = null; indicatorStrokeColor: ColorValue = NONE;
indicatorGradientColor: ColorValue = null; indicatorGradientColor: ColorValue = NONE;
indicatorDirection: DirectionValue = null; indicatorDirection: DirectionValue = DIRECTION_EAST;
/** /**
* Function: isHtmlAllowed * Function: isHtmlAllowed
@ -357,11 +349,11 @@ class Shape {
* *
* Returns 0, or 0.5 if <strokewidth> % 2 == 1. * Returns 0, or 0.5 if <strokewidth> % 2 == 1.
*/ */
getSvgScreenOffset() { getSvgScreenOffset(): number {
const sw = const sw =
this.stencil && this.stencil.strokewidth !== 'inherit' this.stencil && this.stencil.strokeWidthValue !== 'inherit'
? Number(this.stencil.strokewidth) ? Number(this.stencil.strokeWidthValue)
: this.strokewidth ?? 0; : this.strokeWidth ?? 0;
return mod(Math.max(1, Math.round(sw * this.scale)), 2) === 1 ? 0.5 : 0; return mod(Math.max(1, Math.round(sw * this.scale)), 2) === 1 ? 0.5 : 0;
} }
@ -398,8 +390,6 @@ class Shape {
* Creates and returns the SVG node(s) to represent this shape. * Creates and returns the SVG node(s) to represent this shape.
*/ */
redraw() { redraw() {
if (!this.node) return;
this.updateBoundsFromPoints(); this.updateBoundsFromPoints();
if (this.visible && this.checkBounds()) { if (this.visible && this.checkBounds()) {
@ -419,8 +409,6 @@ class Shape {
* Removes all child nodes and resets all CSS. * Removes all child nodes and resets all CSS.
*/ */
clear() { clear() {
if (!this.node) return;
while (this.node.lastChild) { while (this.node.lastChild) {
this.node.removeChild(this.node.lastChild); this.node.removeChild(this.node.lastChild);
} }
@ -435,18 +423,11 @@ class Shape {
const pts = this.points; const pts = this.points;
if (pts.length > 0 && pts[0]) { if (pts.length > 0 && pts[0]) {
this.bounds = new Rectangle( this.bounds = new Rectangle(Math.round(pts[0].x), Math.round(pts[0].y), 1, 1);
Math.round(pts[0].x),
Math.round(pts[0].y),
1,
1
);
for (const pt of pts) { for (const pt of pts) {
if (pt) { if (pt) {
this.bounds.add( this.bounds.add(new Rectangle(Math.round(pt.x), Math.round(pt.y), 1, 1));
new Rectangle(Math.round(pt.x), Math.round(pt.y), 1, 1)
);
} }
} }
} }
@ -460,7 +441,7 @@ class Shape {
* change the rectangle in-place. This implementation returns the given rect. * change the rectangle in-place. This implementation returns the given rect.
*/ */
getLabelBounds(rect: Rectangle) { getLabelBounds(rect: Rectangle) {
const d = getValue(this.style, 'direction', DIRECTION_EAST); const d = this.style?.direction ?? DIRECTION_EAST;
let bounds = rect.clone(); let bounds = rect.clone();
// Normalizes argument for getLabelMargins hook // Normalizes argument for getLabelMargins hook
@ -480,15 +461,11 @@ class Shape {
if (labelMargins) { if (labelMargins) {
labelMargins = labelMargins.clone(); labelMargins = labelMargins.clone();
let flipH = toBool(getValue(this.style, 'flipH', false)); let flipH = this.style?.flipH ?? false;
let flipV = toBool(getValue(this.style, 'flipV', false)); let flipV = this.style?.flipV ?? false;
// Handles special case for vertical labels // Handles special case for vertical labels
if ( if (this.state && this.state.text && this.state.text.isPaintBoundsInverted()) {
this.state &&
this.state.text &&
this.state.text.isPaintBoundsInverted()
) {
const tmp = labelMargins.x; const tmp = labelMargins.x;
labelMargins.x = labelMargins.height; labelMargins.x = labelMargins.height;
labelMargins.height = labelMargins.width; labelMargins.height = labelMargins.width;
@ -540,8 +517,6 @@ class Shape {
* Updates the SVG or VML shape. * Updates the SVG or VML shape.
*/ */
redrawShape() { redrawShape() {
if (!this.node) return;
const canvas = this.createCanvas(); const canvas = this.createCanvas();
if (canvas) { if (canvas) {
@ -552,7 +527,7 @@ class Shape {
this.paint(canvas); this.paint(canvas);
this.afterPaint(canvas); this.afterPaint(canvas);
if (this.node !== canvas.root) { if (this.node !== canvas.root && canvas.root) {
// Forces parsing in IE8 standards mode - slow! avoid // Forces parsing in IE8 standards mode - slow! avoid
this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML); this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML);
} }
@ -570,7 +545,7 @@ class Shape {
const canvas = this.createSvgCanvas(); const canvas = this.createSvgCanvas();
if (canvas && this.outline) { if (canvas && this.outline) {
canvas.setStrokeWidth(this.strokewidth); canvas.setStrokeWidth(this.strokeWidth);
canvas.setStrokeColor(this.stroke); canvas.setStrokeColor(this.stroke);
if (this.isDashed) { if (this.isDashed) {
@ -596,7 +571,7 @@ class Shape {
createSvgCanvas() { createSvgCanvas() {
if (!this.node) return null; if (!this.node) return null;
const canvas = new mxSvgCanvas2D(this.node, false); const canvas = new SvgCanvas2D(this.node, false);
canvas.strokeTolerance = this.pointerEvents ? this.svgStrokeTolerance : 0; canvas.strokeTolerance = this.pointerEvents ? this.svgStrokeTolerance : 0;
canvas.pointerEventsValue = this.svgPointerEvents; canvas.pointerEventsValue = this.svgPointerEvents;
@ -626,9 +601,9 @@ class Shape {
* Destroys the given canvas which was used for drawing. This implementation * Destroys the given canvas which was used for drawing. This implementation
* increments the reference counts on all shared gradients used in the canvas. * increments the reference counts on all shared gradients used in the canvas.
*/ */
destroyCanvas(canvas: mxSvgCanvas2D) { destroyCanvas(canvas: AbstractCanvas2D) {
// Manages reference counts // Manages reference counts
if (canvas instanceof mxSvgCanvas2D) { if (canvas instanceof SvgCanvas2D) {
// Increments ref counts // Increments ref counts
for (const key in canvas.gradients) { for (const key in canvas.gradients) {
const gradient = canvas.gradients[key]; const gradient = canvas.gradients[key];
@ -648,19 +623,19 @@ class Shape {
* *
* Invoked before paint is called. * Invoked before paint is called.
*/ */
beforePaint(c: mxSvgCanvas2D) {} beforePaint(c: AbstractCanvas2D) {}
/** /**
* Function: afterPaint * Function: afterPaint
* *
* Invokes after paint was called. * Invokes after paint was called.
*/ */
afterPaint(c: mxSvgCanvas2D) {} afterPaint(c: AbstractCanvas2D) {}
/** /**
* Generic rendering code. * Generic rendering code.
*/ */
paint(c: mxSvgCanvas2D) { paint(c: AbstractCanvas2D) {
let strokeDrawn = false; let strokeDrawn = false;
if (c && this.outline) { if (c && this.outline) {
@ -705,20 +680,13 @@ class Shape {
let bg = null; let bg = null;
if ( if (
(!this.stencil && (!this.stencil && this.points.length === 0 && this.shapePointerEvents) ||
this.points.length === 0 &&
this.shapePointerEvents) ||
(this.stencil && this.stencilPointerEvents) (this.stencil && this.stencilPointerEvents)
) { ) {
const bb = this.createBoundingBox(); const bb = this.createBoundingBox();
if (bb && this.node) { if (bb && this.node) {
bg = this.createTransparentSvgRectangle( bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height);
bb.x,
bb.y,
bb.width,
bb.height
);
this.node.appendChild(bg); this.node.appendChild(bg);
} }
} }
@ -727,7 +695,7 @@ class Shape {
this.stencil.drawShape(c, this, x, y, w, h); this.stencil.drawShape(c, this, x, y, w, h);
} else { } else {
// Stencils have separate strokewidth // Stencils have separate strokewidth
c.setStrokeWidth(this.strokewidth); c.setStrokeWidth(this.strokeWidth);
if (this.points.length > 0) { if (this.points.length > 0) {
// Paints edge shape // Paints edge shape
@ -763,49 +731,30 @@ class Shape {
/** /**
* Sets the state of the canvas for drawing the shape. * Sets the state of the canvas for drawing the shape.
*/ */
// configureCanvas(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; configureCanvas(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
configureCanvas( let dash = NONE;
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
let dash = null;
if (this.style != null) { if (this.style) {
dash = this.style.dashPattern; dash = this.style.dashPattern;
} }
c.setAlpha(<number>this.opacity / 100); c.setAlpha(this.opacity / 100);
c.setFillAlpha(<number>this.fillOpacity / 100); c.setFillAlpha(this.fillOpacity / 100);
c.setStrokeAlpha(<number>this.strokeOpacity / 100); c.setStrokeAlpha(this.strokeOpacity / 100);
// Sets alpha, colors and gradients // Sets alpha, colors and gradients
if (this.isShadow != null) { if (this.isShadow) {
c.setShadow(this.isShadow); c.setShadow(this.isShadow);
} }
// Dash pattern // Dash pattern
if (this.isDashed != null) { if (this.isDashed) {
c.setDashed( c.setDashed(this.isDashed, this.style?.fixDash ?? false);
this.isDashed,
this.style != null
? toBool(getValue(this.style, 'fixDash', false))
: false
);
} }
if (dash != null) { c.setDashPattern(dash);
c.setDashPattern(dash);
}
if ( if (this.fill !== NONE && this.gradient !== NONE) {
this.fill != null &&
this.fill !== NONE &&
this.gradient &&
this.gradient !== NONE
) {
const b = this.getGradientBounds(c, x, y, w, h); const b = this.getGradientBounds(c, x, y, w, h);
c.setGradient( c.setGradient(
this.fill, this.fill,
@ -828,14 +777,7 @@ class Shape {
* *
* Returns the bounding box for the gradient box for this shape. * Returns the bounding box for the gradient box for this shape.
*/ */
// getGradientBounds(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): mxRectangle; getGradientBounds(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
getGradientBounds(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
return new Rectangle(x, y, w, h); return new Rectangle(x, y, w, h);
} }
@ -844,25 +786,12 @@ class Shape {
* *
* Sets the scale and rotation on the given canvas. * Sets the scale and rotation on the given canvas.
*/ */
// updateTransform(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; updateTransform(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
updateTransform(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
// NOTE: Currently, scale is implemented in state and canvas. This will // NOTE: Currently, scale is implemented in state and canvas. This will
// move to canvas in a later version, so that the states are unscaled // move to canvas in a later version, so that the states are unscaled
// and untranslated and do not need an update after zooming or panning. // and untranslated and do not need an update after zooming or panning.
c.scale(this.scale); c.scale(this.scale);
c.rotate( c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2);
this.getShapeRotation(),
this.flipH,
this.flipV,
x + w / 2,
y + h / 2
);
} }
/** /**
@ -870,21 +799,10 @@ class Shape {
* *
* Paints the vertex shape. * Paints the vertex shape.
*/ */
// paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintVertexShape(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
this.paintBackground(c, x, y, w, h); this.paintBackground(c, x, y, w, h);
if ( if (!this.outline || !this.style || this.style.backgroundOutline === 0) {
!this.outline ||
this.style == null ||
toBool(getValue(this.style, 'backgroundOutline', 0) === false)
) {
c.setShadow(false); c.setShadow(false);
this.paintForeground(c, x, y, w, h); this.paintForeground(c, x, y, w, h);
} }
@ -895,55 +813,32 @@ class Shape {
* *
* Hook for subclassers. This implementation is empty. * Hook for subclassers. This implementation is empty.
*/ */
// paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {}
paintBackground(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {}
/** /**
* Hook for subclassers. This implementation is empty. * Hook for subclassers. This implementation is empty.
*/ */
// paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {}
paintForeground(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {}
/** /**
* Function: paintEdgeShape * Function: paintEdgeShape
* *
* Hook for subclassers. This implementation is empty. * Hook for subclassers. This implementation is empty.
*/ */
// paintEdgeShape(c: mxAbstractCanvas2D, pts: mxPoint[]): void; paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) {}
paintEdgeShape(c: mxSvgCanvas2D, pts: Point[]): void {}
/** /**
* Function: getArcSize * Function: getArcSize
* *
* Returns the arc size for the given dimension. * Returns the arc size for the given dimension.
*/ */
// getArcSize(w: number, h: number): number; getArcSize(w: number, h: number) {
getArcSize(w: number, h: number): number {
let r = 0; let r = 0;
if (toBool(getValue(this.style, 'absoluteArcSize', 0))) { if (this.style?.absoluteArcSize === 0) {
r = Math.min( r = Math.min(w / 2, Math.min(h / 2, (this.style?.arcSize ?? LINE_ARCSIZE) / 2));
w / 2,
Math.min(h / 2, getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2)
);
} else { } else {
const f = parseFloat( const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100;
String(
getValue(this.style, 'arcSize', RECTANGLE_ROUNDING_FACTOR * 100) / 100
)
);
r = Math.min(w * f, h * f); r = Math.min(w * f, h * f);
} }
return r; return r;
@ -954,16 +849,15 @@ class Shape {
* *
* Paints the glass gradient effect. * Paints the glass gradient effect.
*/ */
// paintGlassEffect(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number, arc: number): void;
paintGlassEffect( paintGlassEffect(
c: mxSvgCanvas2D, c: AbstractCanvas2D,
x: number, x: number,
y: number, y: number,
w: number, w: number,
h: number, h: number,
arc: number arc: number
) { ) {
const sw = Math.ceil((this.strokewidth ?? 0) / 2); const sw = Math.ceil((this.strokeWidth ?? 0) / 2);
const size = 0.4; const size = 0.4;
c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1); c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1);
@ -994,7 +888,7 @@ class Shape {
* Paints the given points with rounded corners. * Paints the given points with rounded corners.
*/ */
addPoints( addPoints(
c: mxSvgCanvas2D, c: AbstractCanvas2D,
pts: Point[], pts: Point[],
rounded: boolean = false, rounded: boolean = false,
arcSize: number, arcSize: number,
@ -1009,10 +903,7 @@ class Shape {
if (close && rounded) { if (close && rounded) {
pts = pts.slice(); pts = pts.slice();
const p0 = pts[0]; const p0 = pts[0];
const wp = new Point( const wp = new Point(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2);
pe.x + (p0.x - pe.x) / 2,
pe.y + (p0.y - pe.y) / 2
);
pts.splice(0, 0, wp); pts.splice(0, 0, wp);
} }
@ -1096,15 +987,15 @@ class Shape {
this.spacing = 0; this.spacing = 0;
this.fill = null; this.fill = NONE;
this.gradient = null; this.gradient = NONE;
this.gradientDirection = null; this.gradientDirection = DIRECTION_EAST;
this.stroke = null; this.stroke = NONE;
this.startSize = null; this.startSize = 1;
this.endSize = null; this.endSize = 1;
this.startArrow = null; this.startArrow = NONE;
this.endArrow = null; this.endArrow = NONE;
this.direction = null; this.direction = DIRECTION_EAST;
this.isShadow = false; this.isShadow = false;
this.isDashed = false; this.isDashed = false;
@ -1151,81 +1042,35 @@ class Shape {
this.state = state; this.state = state;
this.style = state.style; this.style = state.style;
const ifNotNullElse = (value: any, defaultValue: any) => {
if (isNotNullish(value)) {
return value;
}
return defaultValue;
};
if (this.style) { if (this.style) {
this.fill = ifNotNullElse(this.style.fillColor, this.fill); this.fill = this.style.fillColor;
this.gradient = ifNotNullElse(this.style.gradientColor, this.gradient); this.gradient = this.style.gradientColor;
this.gradientDirection = ifNotNullElse( this.gradientDirection = this.style.gradientDirection;
this.style.gradientDirection, this.opacity = this.style.opacity;
this.gradientDirection this.fillOpacity = this.style.fillOpacity;
); this.strokeOpacity = this.style.strokeOpacity;
this.opacity = ifNotNullElse(this.style.opacity, this.opacity); this.stroke = this.style.strokeColor;
this.fillOpacity = ifNotNullElse( this.strokeWidth = this.style.strokeWidth;
this.style.fillOpacity, this.spacing = this.style.spacing;
this.fillOpacity this.startSize = this.style.startSize;
); this.endSize = this.style.endSize;
this.strokeOpacity = ifNotNullElse( this.startArrow = this.style.startArrow;
this.style.strokeOpacity, this.endArrow = this.style.endArrow;
this.strokeOpacity this.rotation = this.style.rotation;
); this.direction = this.style.direction;
this.stroke = ifNotNullElse(this.style.strokeColor, this.stroke); this.flipH = this.style.flipH;
this.strokewidth = ifNotNullElse( this.flipV = this.style.flipV;
this.style.strokeWidth,
this.strokewidth
);
this.spacing = ifNotNullElse(this.style.spacing, this.spacing);
this.startSize = ifNotNullElse(this.style.startSize, this.startSize);
this.endSize = ifNotNullElse(this.style.endSize, this.endSize);
this.startArrow = ifNotNullElse(this.style.startArrow, this.startArrow);
this.endArrow = ifNotNullElse(this.style.endArrow, this.endArrow);
this.rotation = ifNotNullElse(this.style.rotation, this.rotation);
this.direction = ifNotNullElse(this.style.direction, this.direction);
this.flipH = toBool(ifNotNullElse(this.style.flipH, 0)); if (this.direction === DIRECTION_NORTH || this.direction === DIRECTION_SOUTH) {
this.flipV = toBool(ifNotNullElse(this.style.flipV, 0));
// Legacy support for stencilFlipH/V
if (this.stencil) {
this.flipH = toBool(
ifNotNullElse(this.style.stencilFlipH, this.flipH || 0)
);
this.flipV = toBool(
ifNotNullElse(this.style.stencilFlipV, this.flipV || 0)
);
}
if (
this.direction === DIRECTION_NORTH ||
this.direction === DIRECTION_SOUTH
) {
const tmp = this.flipH; const tmp = this.flipH;
this.flipH = this.flipV; this.flipH = this.flipV;
this.flipV = tmp; this.flipV = tmp;
} }
this.isShadow = toBool(ifNotNullElse(this.style.shadow, this.isShadow)); this.isShadow = this.style.shadow;
this.isDashed = toBool(ifNotNullElse(this.style.dashed, this.isDashed)); this.isDashed = this.style.dashed;
this.isRounded = toBool( this.isRounded = this.style.rounded;
ifNotNullElse(this.style.rounded, this.isRounded) this.glass = this.style.glass;
);
this.glass = toBool(ifNotNullElse(this.style.glass, this.glass));
if (this.fill === NONE) {
this.fill = null;
}
if (this.gradient === NONE) {
this.gradient = null;
}
if (this.stroke === NONE) {
this.stroke = null;
}
} }
} }
@ -1240,10 +1085,7 @@ class Shape {
*/ */
setCursor(cursor: string) { setCursor(cursor: string) {
this.cursor = cursor; this.cursor = cursor;
this.node.style.cursor = cursor;
if (this.node) {
this.node.style.cursor = cursor;
}
} }
/** /**
@ -1258,7 +1100,7 @@ class Shape {
/** /**
* Hook for subclassers. * Hook for subclassers.
*/ */
isRoundable(c: mxSvgCanvas2D, x: number, y: number, w: number, h: number) { isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
return false; return false;
} }
@ -1271,7 +1113,7 @@ class Shape {
updateBoundingBox() { updateBoundingBox() {
// Tries to get bounding box from SVG subsystem // Tries to get bounding box from SVG subsystem
// LATER: Use getBoundingClientRect for fallback in VML // LATER: Use getBoundingClientRect for fallback in VML
if (this.useSvgBoundingBox && this.node && this.node.ownerSVGElement) { if (this.useSvgBoundingBox && this.node.ownerSVGElement) {
try { try {
const b = this.node.getBBox(); const b = this.node.getBBox();
@ -1279,7 +1121,7 @@ class Shape {
this.boundingBox = new Rectangle(b.x, b.y, b.width, b.height); this.boundingBox = new Rectangle(b.x, b.y, b.width, b.height);
// Adds strokeWidth // Adds strokeWidth
this.boundingBox.grow(((this.strokewidth ?? 0) * this.scale) / 2); this.boundingBox.grow(((this.strokeWidth ?? 0) * this.scale) / 2);
return; return;
} }
@ -1316,8 +1158,7 @@ class Shape {
const bb = this.bounds.clone(); const bb = this.bounds.clone();
if ( if (
(this.stencil && (this.stencil &&
(this.direction === DIRECTION_NORTH || (this.direction === DIRECTION_NORTH || this.direction === DIRECTION_SOUTH)) ||
this.direction === DIRECTION_SOUTH)) ||
this.isPaintBoundsInverted() this.isPaintBoundsInverted()
) { ) {
bb.rotate90(); bb.rotate90();
@ -1334,8 +1175,9 @@ class Shape {
bbox.width += Math.ceil(SHADOW_OFFSET_X * this.scale); bbox.width += Math.ceil(SHADOW_OFFSET_X * this.scale);
bbox.height += Math.ceil(SHADOW_OFFSET_Y * this.scale); bbox.height += Math.ceil(SHADOW_OFFSET_Y * this.scale);
} }
// Adds strokeWidth // Adds strokeWidth
bbox.grow(((this.strokewidth ?? 0) * this.scale) / 2); bbox.grow(((this.strokeWidth ?? 0) * this.scale) / 2);
} }
/** /**
@ -1368,7 +1210,7 @@ class Shape {
getTextRotation() { getTextRotation() {
let rot = this.getRotation(); let rot = this.getRotation();
if (!toBool(getValue(this.style, 'horizontal', 1))) { if (!(this.style?.horizontal ?? true)) {
rot += this.verticalTextRotation || -90; // WARNING WARNING!!!! =============================================================================================== rot += this.verticalTextRotation || -90; // WARNING WARNING!!!! ===============================================================================================
} }
@ -1383,14 +1225,12 @@ class Shape {
getShapeRotation() { getShapeRotation() {
let rot = this.getRotation(); let rot = this.getRotation();
if (this.direction) { if (this.direction === DIRECTION_NORTH) {
if (this.direction === DIRECTION_NORTH) { rot += 270;
rot += 270; } else if (this.direction === DIRECTION_WEST) {
} else if (this.direction === DIRECTION_WEST) { rot += 180;
rot += 180; } else if (this.direction === DIRECTION_SOUTH) {
} else if (this.direction === DIRECTION_SOUTH) { rot += 90;
rot += 90;
}
} }
return rot; return rot;
@ -1413,6 +1253,8 @@ class Shape {
return rect; return rect;
} }
redrawHtmlShape() {}
/** /**
* Function: setTransparentBackgroundImage * Function: setTransparentBackgroundImage
* *
@ -1450,16 +1292,14 @@ class Shape {
* node associated with the shape using <mxEvent.release>. * node associated with the shape using <mxEvent.release>.
*/ */
destroy() { destroy() {
if (this.node) { InternalEvent.release(this.node);
InternalEvent.release(this.node);
if (this.node.parentNode) { if (this.node.parentNode) {
this.node.parentNode.removeChild(this.node); this.node.parentNode.removeChild(this.node);
}
this.node = null;
} }
this.node.innerHTML = '';
// Decrements refCount and removes unused // Decrements refCount and removes unused
this.releaseSvgGradients(this.oldGradients); this.releaseSvgGradients(this.oldGradients);
this.oldGradients = {}; this.oldGradients = {};

View File

@ -7,8 +7,9 @@
import Shape from '../Shape'; import Shape from '../Shape';
import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH } from '../../../../util/Constants'; import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH } from '../../../../util/Constants';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Point from '../../Point'; import Point from '../../Point';
import { ColorValue } from 'packages/core/src/types';
/** /**
* Extends {@link Shape} to implement an arrow shape. The shape is used to represent edges, not vertices. * Extends {@link Shape} to implement an arrow shape. The shape is used to represent edges, not vertices.
@ -16,31 +17,41 @@ import Point from '../../Point';
* This shape is registered under {@link mxConstants.SHAPE_ARROW} in {@link mxCellRenderer}. * This shape is registered under {@link mxConstants.SHAPE_ARROW} in {@link mxCellRenderer}.
*/ */
class Arrow extends Shape { class Arrow extends Shape {
constructor(points, fill, stroke, strokewidth, arrowWidth, spacing, endSize) { constructor(
points: Point[],
fill: ColorValue,
stroke: ColorValue,
strokeWidth = 1,
arrowWidth = ARROW_WIDTH,
spacing = ARROW_SPACING,
endSize = ARROW_SIZE
) {
super(); super();
this.points = points; this.points = points;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth != null ? strokewidth : 1; this.strokeWidth = strokeWidth;
this.arrowWidth = arrowWidth != null ? arrowWidth : ARROW_WIDTH; this.arrowWidth = arrowWidth;
this.spacing = spacing != null ? spacing : ARROW_SPACING; this.spacing = spacing;
this.endSize = endSize != null ? endSize : ARROW_SIZE; this.endSize = endSize;
} }
arrowWidth: number;
/** /**
* Augments the bounding box with the edge width and markers. * Augments the bounding box with the edge width and markers.
*/ */
augmentBoundingBox(bbox: Rectangle): void { augmentBoundingBox(bbox: Rectangle) {
super.augmentBoundingBox(bbox); super.augmentBoundingBox(bbox);
const w = Math.max(this.arrowWidth, this.endSize); const w = Math.max(this.arrowWidth, this.endSize);
bbox.grow((w / 2 + this.strokewidth) * this.scale); bbox.grow((w / 2 + this.strokeWidth) * this.scale);
} }
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) {
// Geometry of arrow // Geometry of arrow
const spacing = ARROW_SPACING; const spacing = ARROW_SPACING;
const width = ARROW_WIDTH; const width = ARROW_WIDTH;

View File

@ -6,11 +6,12 @@
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH, NONE } from '../../../../util/Constants'; import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH, NONE } from '../../../../util/Constants';
import utils, { getNumber, getValue, relativeCcw } from '../../../../util/Utils'; import { relativeCcw } from '../../../../util/Utils';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Point from '../../Point'; import Point from '../../Point';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import CellState from '../../../cell/datatypes/CellState'; import CellState from '../../../cell/datatypes/CellState';
import { ColorValue } from 'packages/core/src/types';
/** /**
* Extends {@link Shape} to implement an new rounded arrow shape with support for waypoints and double arrows. The * Extends {@link Shape} to implement an new rounded arrow shape with support for waypoints and double arrows. The
@ -19,37 +20,48 @@ import CellState from '../../../cell/datatypes/CellState';
* This shape is registered under {@link mxConstants.SHAPE_ARROW_CONNECTOR} in {@link mxCellRenderer}. * This shape is registered under {@link mxConstants.SHAPE_ARROW_CONNECTOR} in {@link mxCellRenderer}.
*/ */
class ArrowConnector extends Shape { class ArrowConnector extends Shape {
constructor(points, fill, stroke, strokewidth, arrowWidth, spacing, endSize) { constructor(
points: Point[],
fill: ColorValue,
stroke: ColorValue,
strokeWidth = 1,
arrowWidth = ARROW_WIDTH,
spacing = ARROW_SPACING,
endSize = ARROW_SIZE / 5
) {
super(); super();
this.points = points; this.points = points;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth != null ? strokewidth : 1; this.strokeWidth = strokeWidth;
this.arrowWidth = arrowWidth != null ? arrowWidth : ARROW_WIDTH; this.arrowWidth = arrowWidth;
this.arrowSpacing = spacing != null ? spacing : ARROW_SPACING; this.arrowSpacing = spacing;
this.startSize = ARROW_SIZE / 5; this.startSize = ARROW_SIZE / 5;
this.endSize = endSize != null ? endSize : ARROW_SIZE / 5; this.endSize = endSize;
} }
arrowWidth: number;
arrowSpacing: number;
/** /**
* Allows to use the SVG bounding box in SVG. * Allows to use the SVG bounding box in SVG.
* @defaultValue `false` for performance reasons. * @defaultValue `false` for performance reasons.
*/ */
useSvgBoundingBox: boolean = true; useSvgBoundingBox = true;
/** /**
* Function: isRoundable * Function: isRoundable
* *
* Hook for subclassers. * Hook for subclassers.
*/ */
isRoundable(): boolean { isRoundable() {
return true; return true;
} }
/** /**
* Overrides mxShape to reset spacing. * Overrides mxShape to reset spacing.
*/ */
resetStyles(): void { resetStyles() {
super.resetStyles(); super.resetStyles();
this.arrowSpacing = ARROW_SPACING; this.arrowSpacing = ARROW_SPACING;
} }
@ -60,9 +72,9 @@ class ArrowConnector extends Shape {
apply(state: CellState): void { apply(state: CellState): void {
super.apply(state); super.apply(state);
if (this.style != null) { if (this.style) {
this.startSize = getNumber(this.style, 'startSize', ARROW_SIZE / 5) * 3; this.startSize = this.style.startSize * 3;
this.endSize = getNumber(this.style, 'endSize', ARROW_SIZE / 5) * 3; this.endSize = this.style.endSize * 3;
} }
} }
@ -82,21 +94,18 @@ class ArrowConnector extends Shape {
w = Math.max(w, this.getEndArrowWidth()); w = Math.max(w, this.getEndArrowWidth());
} }
bbox.grow((w / 2 + this.strokewidth) * this.scale); bbox.grow((w / 2 + this.strokeWidth) * this.scale);
} }
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { paintEdgeShape(c: AbstractCanvas2D, pts: Point[]): void {
// Geometry of arrow // Geometry of arrow
let strokeWidth = this.strokewidth; let strokeWidth = this.strokeWidth;
if (this.outline) { if (this.outline) {
strokeWidth = Math.max( strokeWidth = Math.max(1, this.style?.strokeWidth ?? 0);
1,
utils.getNumber(this.style, 'strokeWidth', this.strokewidth)
);
} }
const startWidth = this.getStartArrowWidth() + strokeWidth; const startWidth = this.getStartArrowWidth() + strokeWidth;
@ -226,7 +235,7 @@ class ArrowConnector extends Shape {
// Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases // Higher strokewidths require a larger minimum bend, 0.35 covers all but the most extreme cases
const strokeWidthFactor = Math.max( const strokeWidthFactor = Math.max(
tmp, tmp,
Math.min(this.strokewidth / 200 + 0.04, 0.35) Math.min(this.strokeWidth / 200 + 0.04, 0.35)
); );
const angleFactor = const angleFactor =
pos !== 0 && isRounded pos !== 0 && isRounded
@ -387,7 +396,18 @@ class ArrowConnector extends Shape {
* *
* Paints the marker. * Paints the marker.
*/ */
paintMarker(c, ptX, ptY, nx, ny, size, arrowWidth, edgeWidth, spacing, initialMove) { paintMarker(
c: AbstractCanvas2D,
ptX: number,
ptY: number,
nx: number,
ny: number,
size: number,
arrowWidth: number,
edgeWidth: number,
spacing: number,
initialMove: boolean
) {
const widthArrowRatio = edgeWidth / arrowWidth; const widthArrowRatio = edgeWidth / arrowWidth;
const orthx = (edgeWidth * ny) / 2; const orthx = (edgeWidth * ny) / 2;
const orthy = (-edgeWidth * nx) / 2; const orthy = (-edgeWidth * nx) / 2;
@ -416,50 +436,50 @@ class ArrowConnector extends Shape {
/** /**
* @returns whether the arrow is rounded * @returns whether the arrow is rounded
*/ */
isArrowRounded(): boolean { isArrowRounded() {
return this.isRounded; return this.isRounded;
} }
/** /**
* @returns the width of the start arrow * @returns the width of the start arrow
*/ */
getStartArrowWidth(): number { getStartArrowWidth() {
return ARROW_WIDTH; return ARROW_WIDTH;
} }
/** /**
* @returns the width of the end arrow * @returns the width of the end arrow
*/ */
getEndArrowWidth(): number { getEndArrowWidth() {
return ARROW_WIDTH; return ARROW_WIDTH;
} }
/** /**
* @returns the width of the body of the edge * @returns the width of the body of the edge
*/ */
getEdgeWidth(): number { getEdgeWidth() {
return ARROW_WIDTH / 3; return ARROW_WIDTH / 3;
} }
/** /**
* @returns whether the ends of the shape are drawn * @returns whether the ends of the shape are drawn
*/ */
isOpenEnded(): boolean { isOpenEnded() {
return false; return false;
} }
/** /**
* @returns whether the start marker is drawn * @returns whether the start marker is drawn
*/ */
isMarkerStart(): boolean { isMarkerStart() {
return getValue(this.style, 'startArrow', NONE) !== NONE; return (this.style?.startArrow ?? NONE) !== NONE;
} }
/** /**
* @returns whether the end marker is drawn * @returns whether the end marker is drawn
*/ */
isMarkerEnd(): boolean { isMarkerEnd() {
return getValue(this.style, 'endArrow', NONE) !== NONE; return (this.style?.endArrow ?? NONE) !== NONE;
} }
} }

View File

@ -6,11 +6,12 @@
*/ */
import { DEFAULT_MARKERSIZE, NONE } from '../../../../util/Constants'; import { DEFAULT_MARKERSIZE, NONE } from '../../../../util/Constants';
import Polyline from './Polyline'; import Polyline from './Polyline';
import utils, { getNumber, getValue } from '../../../../util/Utils'; import { getNumber } from '../../../../util/Utils';
import Marker from './Marker'; import Marker from './Marker';
import Point from '../../Point'; import Point from '../../Point';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import { ColorValue } from 'packages/core/src/types';
/** /**
* Extends {@link mxShape} to implement a connector shape. * Extends {@link mxShape} to implement a connector shape.
@ -21,7 +22,7 @@ import Rectangle from '../../Rectangle';
* @extends {Polyline} * @extends {Polyline}
*/ */
class Connector extends Polyline { class Connector extends Polyline {
constructor(points: Point[], stroke: string, strokewidth: number) { constructor(points: Point[], stroke: ColorValue, strokewidth: number) {
super(points, stroke, strokewidth); super(points, stroke, strokewidth);
} }
@ -29,15 +30,15 @@ class Connector extends Polyline {
* Updates the <boundingBox> for this shape using <createBoundingBox> * Updates the <boundingBox> for this shape using <createBoundingBox>
* and augmentBoundingBox and stores the result in <boundingBox>. * and augmentBoundingBox and stores the result in <boundingBox>.
*/ */
updateBoundingBox(): void { updateBoundingBox() {
this.useSvgBoundingBox = this.style != null && this.style.curved === 1; this.useSvgBoundingBox = !!this.style?.curved;
super.updateBoundingBox(); super.updateBoundingBox();
} }
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { paintEdgeShape(c: AbstractCanvas2D, pts: Point[]): void {
// The indirection via functions for markers is needed in // The indirection via functions for markers is needed in
// order to apply the offsets before painting the line and // order to apply the offsets before painting the line and
// paint the markers after painting the line. // paint the markers after painting the line.
@ -51,11 +52,11 @@ class Connector extends Polyline {
c.setShadow(false); c.setShadow(false);
c.setDashed(false); c.setDashed(false);
if (sourceMarker != null) { if (sourceMarker) {
sourceMarker(); sourceMarker();
} }
if (targetMarker != null) { if (targetMarker) {
targetMarker(); targetMarker();
} }
} }
@ -63,14 +64,17 @@ class Connector extends Polyline {
/** /**
* Prepares the marker by adding offsets in pts and returning a function to paint the marker. * Prepares the marker by adding offsets in pts and returning a function to paint the marker.
*/ */
createMarker(c: mxAbstractCanvas2D, pts: Point[], source: boolean): Marker { createMarker(c: AbstractCanvas2D, pts: Point[], source: boolean) {
if (!this.style) return null;
let result = null; let result = null;
const n = pts.length; const n = pts.length;
const type = getValue(this.style, source ? 'startArrow' : 'endArrow'); const type = source ? this.style.startArrow : this.style.endArrow;
let p0 = source ? pts[1] : pts[n - 2]; let p0 = source ? pts[1] : pts[n - 2];
const pe = source ? pts[0] : pts[n - 1]; const pe = source ? pts[0] : pts[n - 1];
if (type != null && p0 != null && pe != null) { if (type !== NONE && p0 !== null && pe !== null) {
let count = 1; let count = 1;
// Uses next non-overlapping point // Uses next non-overlapping point
@ -92,15 +96,11 @@ class Connector extends Polyline {
const unitX = dx / dist; const unitX = dx / dist;
const unitY = dy / dist; const unitY = dy / dist;
const size = getNumber( const size = source ? this.style.startSize : this.style.endSize;
this.style,
source ? 'startSize' : 'endSize',
DEFAULT_MARKERSIZE
);
// Allow for stroke width in the end point used and the // Allow for stroke width in the end point used and the
// orthogonal vectors describing the direction of the marker // orthogonal vectors describing the direction of the marker
const filled = this.style[source ? 'startFill' : 'endFill'] !== 0; const filled = source ? this.style.startFill : this.style.endFill;
result = Marker.createMarker( result = Marker.createMarker(
c, c,
@ -111,7 +111,7 @@ class Connector extends Polyline {
unitY, unitY,
size, size,
source, source,
this.strokewidth, this.strokeWidth,
filled filled
); );
} }
@ -122,17 +122,19 @@ class Connector extends Polyline {
/** /**
* Augments the bounding box with the strokewidth and shadow offsets. * Augments the bounding box with the strokewidth and shadow offsets.
*/ */
augmentBoundingBox(bbox: Rectangle): void { augmentBoundingBox(bbox: Rectangle) {
super.augmentBoundingBox(bbox); super.augmentBoundingBox(bbox);
if (!this.style) return;
// Adds marker sizes // Adds marker sizes
let size = 0; let size = 0;
if (getValue(this.style, 'startArrow', NONE) !== NONE) { if (this.style.startArrow !== NONE) {
size = getNumber(this.style, 'startSize', DEFAULT_MARKERSIZE) + 1; size = getNumber(this.style, 'startSize', DEFAULT_MARKERSIZE) + 1;
} }
if (getValue(this.style, 'endArrow', NONE) !== NONE) { if (this.style.endArrow !== NONE) {
size = Math.max(size, getNumber(this.style, 'endSize', DEFAULT_MARKERSIZE)) + 1; size = Math.max(size, getNumber(this.style, 'endSize', DEFAULT_MARKERSIZE)) + 1;
} }

View File

@ -5,8 +5,9 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import { ColorValue } from 'packages/core/src/types';
/** /**
* Extends {@link Shape} to implement a horizontal line shape. * Extends {@link Shape} to implement a horizontal line shape.
@ -15,12 +16,12 @@ import Rectangle from '../../Rectangle';
* @extends {Shape} * @extends {Shape}
*/ */
class Line extends Shape { class Line extends Shape {
constructor(bounds: Rectangle, stroke: string, strokewidth: number, vertical: boolean) { constructor(bounds: Rectangle, stroke: ColorValue, strokeWidth = 1, vertical = false) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth != null ? strokewidth : 1; this.strokeWidth = strokeWidth;
this.vertical = vertical != null ? vertical : this.vertical; this.vertical = vertical;
} }
/** /**
@ -28,23 +29,17 @@ class Line extends Shape {
* *
* Whether to paint a vertical line. * Whether to paint a vertical line.
*/ */
vertical = false; vertical: boolean;
/** /**
* Redirects to redrawPath for subclasses to work. * Redirects to redrawPath for subclasses to work.
* @param {mxAbstractCanvas2D} c * @param {AbstractCanvas2D} c
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
paintVertexShape( paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
c: mxAbstractCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
c.begin(); c.begin();
if (this.vertical) { if (this.vertical) {

View File

@ -4,11 +4,15 @@
* Updated to ES9 syntax by David Morrissey 2021 * Updated to ES9 syntax by David Morrissey 2021
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import { ArrowType } from 'packages/core/src/types';
import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D';
import { import {
ARROW_CLASSIC, ARROW_CLASSIC,
ARROW_CLASSIC_THIN, ARROW_CLASSIC_THIN,
ARROW_DIAMOND, ARROW_DIAMOND,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import Point from '../../Point';
import Shape from '../Shape';
/** /**
* A static class that implements all markers for VML and SVG using a registry. * A static class that implements all markers for VML and SVG using a registry.
@ -21,15 +25,13 @@ class Marker {
* *
* Mapping: the attribute name on the object is the marker type, the associated value is the function to paint the marker * Mapping: the attribute name on the object is the marker type, the associated value is the function to paint the marker
*/ */
// static markers: object; static markers: Record<string, Function> = {};
static markers = [];
/** /**
* Adds a factory method that updates a given endpoint and returns a * Adds a factory method that updates a given endpoint and returns a
* function to paint the marker onto the given canvas. * function to paint the marker onto the given canvas.
*/ */
// static addMarker(type: string, funct: Function): void; static addMarker(type: string, funct: Function) {
static addMarker(type, funct) {
Marker.markers[type] = funct; Marker.markers[type] = funct;
} }
@ -38,9 +40,20 @@ class Marker {
* *
* Returns a function to paint the given marker. * Returns a function to paint the given marker.
*/ */
static createMarker(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) { static createMarker(
canvas: AbstractCanvas2D,
shape: Shape,
type: ArrowType,
pe: Point,
unitX: number,
unitY: number,
size: number,
source: boolean,
sw: number,
filled: boolean
) {
const funct = Marker.markers[type]; const funct = Marker.markers[type];
return funct != null return funct
? funct(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) ? funct(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
: null; : null;
} }
@ -50,10 +63,19 @@ class Marker {
* Adds the classic and block marker factory method. * Adds the classic and block marker factory method.
*/ */
(() => { (() => {
function createArrow(widthFactor) { function createArrow(widthFactor = 2) {
widthFactor = widthFactor != null ? widthFactor : 2; return (
canvas: AbstractCanvas2D,
return (canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) => { shape: Shape,
type: ArrowType,
pe: Point,
unitX: number,
unitY: number,
size: number,
source: boolean,
sw: number,
filled: boolean
) => {
// The angle of the forward facing arrow sides against the x axis is // The angle of the forward facing arrow sides against the x axis is
// 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for
// only half the strokewidth is processed ). // only half the strokewidth is processed ).
@ -103,10 +125,19 @@ class Marker {
Marker.addMarker('block', createArrow(2)); Marker.addMarker('block', createArrow(2));
Marker.addMarker('blockThin', createArrow(3)); Marker.addMarker('blockThin', createArrow(3));
function createOpenArrow(widthFactor) { function createOpenArrow(widthFactor = 2) {
widthFactor = widthFactor != null ? widthFactor : 2; return (
canvas: AbstractCanvas2D,
return (canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) => { shape: Shape,
type: ArrowType,
pe: Point,
unitX: number,
unitY: number,
size: number,
source: boolean,
sw: number,
filled: boolean
) => {
// The angle of the forward facing arrow sides against the x axis is // The angle of the forward facing arrow sides against the x axis is
// 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for
// only half the strokewidth is processed ). // only half the strokewidth is processed ).
@ -144,7 +175,18 @@ class Marker {
Marker.addMarker( Marker.addMarker(
'oval', 'oval',
(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) => { (
canvas: AbstractCanvas2D,
shape: Shape,
type: ArrowType,
pe: Point,
unitX: number,
unitY: number,
size: number,
source: boolean,
sw: number,
filled: boolean
) => {
const a = size / 2; const a = size / 2;
const pt = pe.clone(); const pt = pe.clone();
@ -163,7 +205,18 @@ class Marker {
} }
); );
function diamond(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) { function diamond(
canvas: AbstractCanvas2D,
shape: Shape,
type: ArrowType,
pe: Point,
unitX: number,
unitY: number,
size: number,
source: boolean,
sw: number,
filled: boolean
) {
// The angle of the forward facing arrow sides against the x axis is // The angle of the forward facing arrow sides against the x axis is
// 45 degrees, 1/sin(45) = 1.4142 / 2 = 0.7071 ( / 2 allows for // 45 degrees, 1/sin(45) = 1.4142 / 2 = 0.7071 ( / 2 allows for
// only half the strokewidth is processed ). Or 0.9862 for thin diamond. // only half the strokewidth is processed ). Or 0.9862 for thin diamond.

View File

@ -6,9 +6,9 @@
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import { LINE_ARCSIZE } from '../../../../util/Constants'; import { LINE_ARCSIZE } from '../../../../util/Constants';
import utils, { getValue } from '../../../../util/Utils';
import Point from '../../Point'; import Point from '../../Point';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import { ColorValue } from 'packages/core/src/types';
/** /**
* Class: mxPolyline * Class: mxPolyline
@ -31,42 +31,42 @@ import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D';
* 1. This is stored in <strokewidth>. * 1. This is stored in <strokewidth>.
*/ */
class Polyline extends Shape { class Polyline extends Shape {
constructor(points: Point[], stroke: string, strokewidth: number) { constructor(points: Point[], stroke: ColorValue, strokeWidth = 1) {
super(); super();
this.points = points; this.points = points;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth != null ? strokewidth : 1; this.strokeWidth = strokeWidth;
} }
/** /**
* Returns 0. * Returns 0.
*/ */
getRotation(): number { getRotation() {
return 0; return 0;
} }
/** /**
* Returns 0. * Returns 0.
*/ */
getShapeRotation(): number { getShapeRotation() {
return 0; return 0;
} }
/** /**
* Returns false. * Returns false.
*/ */
isPaintBoundsInverted(): boolean { isPaintBoundsInverted() {
return false; return false;
} }
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) {
const prev = c.pointerEventsValue; const prev = c.pointerEventsValue;
c.pointerEventsValue = 'stroke'; c.pointerEventsValue = 'stroke';
if (this.style == null || this.style.curved != 1) { if (!this.style || !this.style.curved) {
this.paintLine(c, pts, this.isRounded); this.paintLine(c, pts, this.isRounded);
} else { } else {
this.paintCurvedLine(c, pts); this.paintCurvedLine(c, pts);
@ -77,8 +77,9 @@ class Polyline extends Shape {
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintLine(c: mxAbstractCanvas2D, pts: Point[], rounded?: boolean): void { paintLine(c: AbstractCanvas2D, pts: Point[], rounded?: boolean) {
const arcSize = getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; const arcSize = this.style?.arcSize ?? LINE_ARCSIZE;
c.begin(); c.begin();
this.addPoints(c, pts, rounded, arcSize, false); this.addPoints(c, pts, rounded, arcSize, false);
c.stroke(); c.stroke();
@ -87,7 +88,7 @@ class Polyline extends Shape {
/** /**
* Paints the line shape. * Paints the line shape.
*/ */
paintCurvedLine(c: mxAbstractCanvas2D, pts: Point[]): void { paintCurvedLine(c: AbstractCanvas2D, pts: Point[]) {
c.begin(); c.begin();
const pt = pts[0]; const pt = pts[0];

View File

@ -5,7 +5,7 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Actor from '../Actor'; import Actor from '../Actor';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
/** /**
@ -14,30 +14,18 @@ import Rectangle from '../../Rectangle';
* This shape is registered under {@link mxConstants.SHAPE_CLOUD} in {@link cellRenderer}. * This shape is registered under {@link mxConstants.SHAPE_CLOUD} in {@link cellRenderer}.
*/ */
class CloudShape extends Actor { class CloudShape extends Actor {
constructor( constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) {
bounds: Rectangle,
fill: string,
stroke: string,
strokewidth: number = 1
) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
/** /**
* Draws the path for this shape. * Draws the path for this shape.
*/ */
// redrawPath(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
redrawPath(
c: mxAbstractCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
c.moveTo(0.25 * w, 0.25 * h); c.moveTo(0.25 * w, 0.25 * h);
c.curveTo(0.05 * w, 0.25 * h, 0, 0.5 * h, 0.16 * w, 0.55 * h); c.curveTo(0.05 * w, 0.25 * h, 0, 0.5 * h, 0.16 * w, 0.55 * h);
c.curveTo(0, 0.66 * h, 0.18 * w, 0.9 * h, 0.31 * w, 0.8 * h); c.curveTo(0, 0.66 * h, 0.18 * w, 0.9 * h, 0.31 * w, 0.8 * h);

View File

@ -5,10 +5,9 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import utils from '../../../../util/Utils'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import { NONE } from 'packages/core/src/util/Constants';
/** /**
* Extends {@link Shape} to implement an cylinder shape. If a custom shape with one filled area and an overlay path is * Extends {@link Shape} to implement an cylinder shape. If a custom shape with one filled area and an overlay path is
@ -17,52 +16,34 @@ import Rectangle from '../../Rectangle';
* This shape is registered under {@link mxConstants.SHAPE_CYLINDER} in {@link cellRenderer}. * This shape is registered under {@link mxConstants.SHAPE_CYLINDER} in {@link cellRenderer}.
*/ */
class CylinderShape extends Shape { class CylinderShape extends Shape {
constructor( constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) {
bounds: Rectangle,
fill: string,
stroke: string,
strokewidth: number = 1
) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
/** /**
* Defines the maximum height of the top and bottom part of the cylinder shape. * Defines the maximum height of the top and bottom part of the cylinder shape.
*/ */
// maxHeight: number;
maxHeight = 40; maxHeight = 40;
/** /**
* Sets stroke tolerance to 0 for SVG. * Sets stroke tolerance to 0 for SVG.
*/ */
// svgStrokeTolerance: number;
svgStrokeTolerance = 0; svgStrokeTolerance = 0;
/** /**
* Redirects to redrawPath for subclasses to work. * Redirects to redrawPath for subclasses to work.
*/ */
// paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintVertexShape(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
c.translate(x, y); c.translate(x, y);
c.begin(); c.begin();
this.redrawPath(c, x, y, w, h, false); this.redrawPath(c, x, y, w, h, false);
c.fillAndStroke(); c.fillAndStroke();
if ( if (!this.outline || !this.style || this.style.backgroundOutline === 0) {
!this.outline ||
this.style == null ||
utils.getValue(this.style, 'backgroundOutline', 0) == 0
) {
c.setShadow(false); c.setShadow(false);
c.begin(); c.begin();
this.redrawPath(c, x, y, w, h, true); this.redrawPath(c, x, y, w, h, true);
@ -73,17 +54,15 @@ class CylinderShape extends Shape {
/** /**
* Redirects to redrawPath for subclasses to work. * Redirects to redrawPath for subclasses to work.
*/ */
// getCylinderSize(x: number, y: number, w: number, h: number): number; getCylinderSize(x: number, y: number, w: number, h: number) {
getCylinderSize(x: number, y: number, w: number, h: number): number {
return Math.min(this.maxHeight, Math.round(h / 5)); return Math.min(this.maxHeight, Math.round(h / 5));
} }
/** /**
* Draws the path for this shape. * Draws the path for this shape.
*/ */
// redrawPath(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number, isForeground: boolean): void;
redrawPath( redrawPath(
c: mxSvgCanvas2D, c: AbstractCanvas2D,
x: number, x: number,
y: number, y: number,
w: number, w: number,
@ -92,10 +71,7 @@ class CylinderShape extends Shape {
): void { ): void {
const dy = this.getCylinderSize(x, y, w, h); const dy = this.getCylinderSize(x, y, w, h);
if ( if ((isForeground && this.fill !== NONE) || (!isForeground && this.fill === NONE)) {
(isForeground && this.fill != null) ||
(!isForeground && this.fill == null)
) {
c.moveTo(0, dy); c.moveTo(0, dy);
c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); c.curveTo(0, 2 * dy, w, 2 * dy, w, dy);

View File

@ -7,8 +7,7 @@
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import Shape from '../Shape'; import Shape from '../Shape';
import utils from '../../../../util/Utils'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D';
/** /**
* Extends {@link Shape} to implement a double ellipse shape. * Extends {@link Shape} to implement a double ellipse shape.
@ -39,32 +38,18 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D';
* ``` * ```
*/ */
class DoubleEllipseShape extends Shape { class DoubleEllipseShape extends Shape {
strokewidth: number; constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) {
constructor(
bounds: Rectangle,
fill: string,
stroke: string,
strokewidth: number = 1
) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
/** /**
* Paints the background. * Paints the background.
*/ */
// paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintBackground(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
c.ellipse(x, y, w, h); c.ellipse(x, y, w, h);
c.fillAndStroke(); c.fillAndStroke();
} }
@ -72,20 +57,11 @@ class DoubleEllipseShape extends Shape {
/** /**
* Paints the foreground. * Paints the foreground.
*/ */
// paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintForeground(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
if (!this.outline) { if (!this.outline) {
const margin = utils.getValue( const margin =
this.style, this.style?.margin ?? Math.min(3 + this.strokeWidth, Math.min(w / 5, h / 5));
'margin',
Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5))
);
x += margin; x += margin;
y += margin; y += margin;
w -= 2 * margin; w -= 2 * margin;
@ -103,16 +79,12 @@ class DoubleEllipseShape extends Shape {
/** /**
* @returns the bounds for the label. * @returns the bounds for the label.
*/ */
// getLabelBounds(rect: mxRectangle): mxRectangle;
getLabelBounds(rect: Rectangle) { getLabelBounds(rect: Rectangle) {
const margin = const margin =
utils.getValue( this.style?.margin ??
this.style, Math.min(
'margin', 3 + this.strokeWidth,
Math.min( Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale)
3 + this.strokewidth,
Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale)
)
) * this.scale; ) * this.scale;
return new Rectangle( return new Rectangle(

View File

@ -5,7 +5,7 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
/** /**
@ -13,29 +13,18 @@ import Rectangle from '../../Rectangle';
* This shape is registered under mxConstants.SHAPE_ELLIPSE in mxCellRenderer. * This shape is registered under mxConstants.SHAPE_ELLIPSE in mxCellRenderer.
*/ */
class EllipseShape extends Shape { class EllipseShape extends Shape {
constructor( constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) {
bounds: Rectangle,
fill: string,
stroke: string,
strokewidth: number = 1
) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
/** /**
* Paints the ellipse shape. * Paints the ellipse shape.
*/ */
paintVertexShape( paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
c.ellipse(x, y, w, h); c.ellipse(x, y, w, h);
c.fillAndStroke(); c.fillAndStroke();
} }

View File

@ -6,9 +6,8 @@
*/ */
import Actor from '../Actor'; import Actor from '../Actor';
import Point from '../../Point'; import Point from '../../Point';
import utils, { getValue } from '../../../../util/Utils';
import { LINE_ARCSIZE } from '../../../../util/Constants'; import { LINE_ARCSIZE } from '../../../../util/Constants';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
/** /**
* Implementation of the hexagon shape. * Implementation of the hexagon shape.
@ -28,8 +27,9 @@ class HexagonShape extends Actor {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
redrawPath(c: mxSvgCanvas2D, x: number, y: number, w: number, h: number): void { redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
const arcSize = getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2;
this.addPoints( this.addPoints(
c, c,
[ [

View File

@ -5,12 +5,12 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import utils from '../../../../util/Utils';
import RectangleShape from './RectangleShape'; import RectangleShape from './RectangleShape';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import CellState from '../../../cell/datatypes/CellState'; import CellState from '../../../cell/datatypes/CellState';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/SvgCanvas2D';
import CellOverlay from '../../../cell/CellOverlay'; import CellOverlay from '../../../cell/CellOverlay';
import { NONE } from 'packages/core/src/util/Constants';
/** /**
* Extends {@link mxShape} to implement an image shape. * Extends {@link mxShape} to implement an image shape.
@ -22,24 +22,21 @@ import CellOverlay from '../../../cell/CellOverlay';
class ImageShape extends RectangleShape { class ImageShape extends RectangleShape {
constructor( constructor(
bounds: Rectangle, bounds: Rectangle,
image: string, imageSrc: string,
fill: string = '#FFFFFF', fill: string = '#FFFFFF',
stroke: string = '#000000', stroke: string = '#000000',
strokewidth: number = 1 strokeWidth: number = 1
) { ) {
super(); super(bounds, fill, stroke, strokeWidth);
this.bounds = bounds;
this.image = image; this.imageSrc = imageSrc;
this.fill = fill;
this.stroke = stroke;
this.strokewidth = strokewidth;
this.shadow = false; this.shadow = false;
} }
// TODO: Document me!! // TODO: Document me!!
shadow: boolean; shadow: boolean;
image: string; imageSrc: string;
// Used in mxCellRenderer // Used in mxCellRenderer
overlay: CellOverlay | null = null; overlay: CellOverlay | null = null;
@ -54,8 +51,7 @@ class ImageShape extends RectangleShape {
/** /**
* Disables offset in IE9 for crisper image output. * Disables offset in IE9 for crisper image output.
*/ */
// getSvgScreenOffset(): number; getSvgScreenOffset() {
getSvgScreenOffset(): number {
return 0; return 0;
} }
@ -76,19 +72,12 @@ class ImageShape extends RectangleShape {
apply(state: CellState) { apply(state: CellState) {
super.apply(state); super.apply(state);
this.fill = null; this.fill = NONE;
this.stroke = null; this.stroke = NONE;
this.gradient = null; this.gradient = NONE;
if (this.style != null) { if (this.style) {
this.preserveImageAspect = this.preserveImageAspect = this.style.imageAspect;
utils.getNumber(this.style, 'imageAspect', 1) == 1;
// Legacy support for imageFlipH/V
this.flipH =
this.flipH || utils.getValue(this.style, 'imageFlipH', 0) == 1;
this.flipV =
this.flipV || utils.getValue(this.style, 'imageFlipV', 0) == 1;
} }
} }
@ -96,8 +85,7 @@ class ImageShape extends RectangleShape {
* Returns true if HTML is allowed for this shape. This implementation always * Returns true if HTML is allowed for this shape. This implementation always
* returns false. * returns false.
*/ */
// isHtmlAllowed(): boolean; isHtmlAllowed() {
isHtmlAllowed(): boolean {
return !this.preserveImageAspect; return !this.preserveImageAspect;
} }
@ -106,8 +94,7 @@ class ImageShape extends RectangleShape {
* this shape. This implementation falls back to <createVml> * this shape. This implementation falls back to <createVml>
* so that the HTML creation is optional. * so that the HTML creation is optional.
*/ */
// createHtml(): HTMLElement; createHtml() {
createHtml(): HTMLElement {
const node = document.createElement('div'); const node = document.createElement('div');
node.style.position = 'absolute'; node.style.position = 'absolute';
return node; return node;
@ -116,33 +103,19 @@ class ImageShape extends RectangleShape {
/** /**
* Disables inherited roundable support. * Disables inherited roundable support.
*/ */
// isRoundable(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): boolean; isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
isRoundable(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): boolean {
return false; return false;
} }
/** /**
* Generic background painting implementation. * Generic background painting implementation.
*/ */
// paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintVertexShape( if (this.imageSrc && this.style) {
c: mxSvgCanvas2D, const fill = this.style.imageBackground;
x: number, const stroke = this.style.imageBorder;
y: number,
w: number,
h: number
) {
if (this.image != null) {
const fill = utils.getValue(this.style, 'imageBackground', null);
let stroke = utils.getValue(this.style, 'imageBorder', null);
if (fill != null) { if (fill !== NONE) {
// Stroke rendering required for shadow // Stroke rendering required for shadow
c.setFillColor(fill); c.setFillColor(fill);
c.setStrokeColor(stroke); c.setStrokeColor(stroke);
@ -151,9 +124,7 @@ class ImageShape extends RectangleShape {
} }
// FlipH/V are implicit via mxShape.updateTransform // FlipH/V are implicit via mxShape.updateTransform
c.image(x, y, w, h, this.image, this.preserveImageAspect, false, false); c.image(x, y, w, h, this.imageSrc, this.preserveImageAspect, false, false);
stroke = utils.getValue(this.style, 'imageBorder', null);
if (stroke != null) { if (stroke != null) {
c.setShadow(false); c.setShadow(false);

View File

@ -13,9 +13,11 @@ import {
ALIGN_RIGHT, ALIGN_RIGHT,
ALIGN_TOP, ALIGN_TOP,
DEFAULT_IMAGESIZE, DEFAULT_IMAGESIZE,
NONE,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import RectangleShape from './RectangleShape'; import RectangleShape from './RectangleShape';
import utils from '../../../../util/Utils'; import { ColorValue } from 'packages/core/src/types';
import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D';
/** /**
* Class: mxLabel * Class: mxLabel
@ -37,24 +39,29 @@ import utils from '../../../../util/Utils';
* strokewidth - Optional integer that defines the stroke width. Default is * strokewidth - Optional integer that defines the stroke width. Default is
* 1. This is stored in <strokewidth>. * 1. This is stored in <strokewidth>.
*/ */
class Label extends RectangleShape { class LabelShape extends RectangleShape {
constructor(bounds, fill, stroke, strokewidth) { constructor(
super(bounds, fill, stroke, strokewidth); bounds: Rectangle,
fill: ColorValue,
stroke: ColorValue,
strokeWidth: number
) {
super(bounds, fill, stroke, strokeWidth);
} }
/** /**
* Default width and height for the image. * Default width and height for the image.
* @default mxConstants.DEFAULT_IMAGESIZE * @default mxConstants.DEFAULT_IMAGESIZE
*/ */
// imageSize: number;
imageSize = DEFAULT_IMAGESIZE; imageSize = DEFAULT_IMAGESIZE;
imageSrc: string | null = null;
/** /**
* Default value for image spacing * Default value for image spacing
* @type {number} * @type {number}
* @default 2 * @default 2
*/ */
// spacing: number;
spacing = 2; spacing = 2;
/** /**
@ -62,7 +69,6 @@ class Label extends RectangleShape {
* @type {number} * @type {number}
* @default 10 * @default 10
*/ */
// indicatorSize: number;
indicatorSize = 10; indicatorSize = 10;
/** /**
@ -70,17 +76,17 @@ class Label extends RectangleShape {
* @default 2 * @default 2
* @type {number} * @type {number}
*/ */
// indicatorSpacing: number;
indicatorSpacing = 2; indicatorSpacing = 2;
indicatorImageSrc: string | null = null;
/** /**
* Initializes the shape and the <indicator>. * Initializes the shape and the <indicator>.
*/ */
// init(container: HTMLElement): void; init(container: SVGElement) {
init(container) {
super.init(container); super.init(container);
if (this.indicatorShape != null) { if (this.indicatorShape) {
this.indicator = new this.indicatorShape(); this.indicator = new this.indicatorShape();
this.indicator.dialect = this.dialect; this.indicator.dialect = this.dialect;
this.indicator.init(this.node); this.indicator.init(this.node);
@ -91,9 +97,8 @@ class Label extends RectangleShape {
* Reconfigures this shape. This will update the colors of the indicator * Reconfigures this shape. This will update the colors of the indicator
* and reconfigure it if required. * and reconfigure it if required.
*/ */
// redraw(): void;
redraw() { redraw() {
if (this.indicator != null) { if (this.indicator) {
this.indicator.fill = this.indicatorColor; this.indicator.fill = this.indicatorColor;
this.indicator.stroke = this.indicatorStrokeColor; this.indicator.stroke = this.indicatorStrokeColor;
this.indicator.gradient = this.indicatorGradientColor; this.indicator.gradient = this.indicatorGradientColor;
@ -107,13 +112,8 @@ class Label extends RectangleShape {
* Returns true for non-rounded, non-rotated shapes with no glass gradient and * Returns true for non-rounded, non-rotated shapes with no glass gradient and
* no indicator shape. * no indicator shape.
*/ */
// isHtmlAllowed(): boolean;
isHtmlAllowed() { isHtmlAllowed() {
return ( return super.isHtmlAllowed() && this.indicatorColor === NONE && !!this.indicatorShape;
super.isHtmlAllowed() &&
this.indicatorColor == null &&
this.indicatorShape == null
);
} }
/** /**
@ -124,8 +124,7 @@ class Label extends RectangleShape {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
// paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintForeground(c, x, y, w, h) {
this.paintImage(c, x, y, w, h); this.paintImage(c, x, y, w, h);
this.paintIndicator(c, x, y, w, h); this.paintIndicator(c, x, y, w, h);
super.paintForeground(c, x, y, w, h); super.paintForeground(c, x, y, w, h);
@ -139,16 +138,15 @@ class Label extends RectangleShape {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
// paintImage(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintImage(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintImage(c, x, y, w, h) { if (this.imageSrc) {
if (this.image != null) {
const bounds = this.getImageBounds(x, y, w, h); const bounds = this.getImageBounds(x, y, w, h);
c.image( c.image(
bounds.x, bounds.x,
bounds.y, bounds.y,
bounds.width, bounds.width,
bounds.height, bounds.height,
this.image, this.imageSrc,
false, false,
false, false,
false false
@ -163,26 +161,12 @@ class Label extends RectangleShape {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
// getImageBounds(x: number, y: number, w: number, h: number): mxRectangle; getImageBounds(x: number, y: number, w: number, h: number) {
getImageBounds(x, y, w, h) { const align = this.style?.imageAlign ?? ALIGN_LEFT;
const align = utils.getValue(this.style, 'imageAlign', ALIGN_LEFT); const valign = this.style?.verticalAlign ?? ALIGN_MIDDLE;
const valign = utils.getValue( const width = this.style?.imageWidth ?? DEFAULT_IMAGESIZE;
this.style, const height = this.style?.imageHeight ?? DEFAULT_IMAGESIZE;
'verticalAlign', const spacing = this.style?.spacing ?? this.spacing + 5;
ALIGN_MIDDLE
);
const width = utils.getNumber(
this.style,
'imageWidth',
DEFAULT_IMAGESIZE
);
const height = utils.getNumber(
this.style,
'imageHeight',
DEFAULT_IMAGESIZE
);
const spacing =
utils.getNumber(this.style, 'spacing', this.spacing) + 5;
if (align === ALIGN_CENTER) { if (align === ALIGN_CENTER) {
x += (w - width) / 2; x += (w - width) / 2;
@ -213,19 +197,18 @@ class Label extends RectangleShape {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
// paintIndicator(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintIndicator(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintIndicator(c, x, y, w, h) { if (this.indicator) {
if (this.indicator != null) {
this.indicator.bounds = this.getIndicatorBounds(x, y, w, h); this.indicator.bounds = this.getIndicatorBounds(x, y, w, h);
this.indicator.paint(c); this.indicator.paint(c);
} else if (this.indicatorImage != null) { } else if (this.indicatorImageSrc) {
const bounds = this.getIndicatorBounds(x, y, w, h); const bounds = this.getIndicatorBounds(x, y, w, h);
c.image( c.image(
bounds.x, bounds.x,
bounds.y, bounds.y,
bounds.width, bounds.width,
bounds.height, bounds.height,
this.indicatorImage, this.indicatorImageSrc,
false, false,
false, false,
false false
@ -241,24 +224,11 @@ class Label extends RectangleShape {
* @param {number} h * @param {number} h
* @returns {Rectangle} * @returns {Rectangle}
*/ */
// getIndicatorBounds(x: number, y: number, w: number, h: number): mxRectangle; getIndicatorBounds(x: number, y: number, w: number, h: number) {
getIndicatorBounds(x, y, w, h) { const align = this.style?.imageAlign ?? ALIGN_LEFT;
const align = utils.getValue(this.style, 'imageAlign', ALIGN_LEFT); const valign = this.style?.verticalAlign ?? ALIGN_MIDDLE;
const valign = utils.getValue( const width = this.style?.indicatorWidth ?? this.indicatorSize;
this.style, const height = this.style?.indicatorHeight ?? this.indicatorSize;
'verticalAlign',
ALIGN_MIDDLE
);
const width = utils.getNumber(
this.style,
'indicatorWidth',
this.indicatorSize
);
const height = utils.getNumber(
this.style,
'indicatorHeight',
this.indicatorSize
);
const spacing = this.spacing + 5; const spacing = this.spacing + 5;
if (align === ALIGN_RIGHT) { if (align === ALIGN_RIGHT) {
@ -285,16 +255,15 @@ class Label extends RectangleShape {
/** /**
* Generic background painting implementation. * Generic background painting implementation.
*/ */
// redrawHtmlShape(): void;
redrawHtmlShape() { redrawHtmlShape() {
super.redrawHtmlShape(); super.redrawHtmlShape();
// Removes all children // Removes all children
while (this.node.hasChildNodes()) { while (this.node.hasChildNodes()) {
this.node.removeChild(this.node.lastChild); this.node.removeChild(this.node.lastChild as ChildNode);
} }
if (this.image != null) { if (this.imageSrc && this.bounds) {
const node = document.createElement('img'); const node = document.createElement('img');
node.style.position = 'relative'; node.style.position = 'relative';
node.setAttribute('border', '0'); node.setAttribute('border', '0');
@ -313,11 +282,11 @@ class Label extends RectangleShape {
node.style.width = `${Math.round(bounds.width)}px`; node.style.width = `${Math.round(bounds.width)}px`;
node.style.height = `${Math.round(bounds.height)}px`; node.style.height = `${Math.round(bounds.height)}px`;
node.src = this.image; node.src = this.imageSrc;
this.node.appendChild(node); this.node.appendChild(node);
} }
} }
} }
export default Label; export default LabelShape;

View File

@ -10,11 +10,10 @@ import {
NONE, NONE,
RECTANGLE_ROUNDING_FACTOR, RECTANGLE_ROUNDING_FACTOR,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import utils from '../../../../util/Utils';
import Shape from '../Shape'; import Shape from '../Shape';
import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import { ColorValue } from 'packages/core/src/types';
/** /**
* Extends {@link Shape} to implement a rectangle shape. * Extends {@link Shape} to implement a rectangle shape.
@ -24,84 +23,58 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D';
*/ */
class RectangleShape extends Shape { class RectangleShape extends Shape {
constructor( constructor(
bounds: Rectangle | null = null, bounds: Rectangle,
fill: string | null = '#FFFFFF', fill: ColorValue,
stroke: string | null = '#000000', stroke: ColorValue,
strokewidth: number = 1 strokeWidth: number = 1
) { ) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokeWidth;
} }
// TODO: Document me!
strokewidth: number;
/** /**
* Returns true for non-rounded, non-rotated shapes with no glass gradient. * Returns true for non-rounded, non-rotated shapes with no glass gradient.
*/ */
// isHtmlAllowed(): boolean; isHtmlAllowed() {
isHtmlAllowed(): boolean {
let events = true; let events = true;
if (this.style != null) { if (this.style) {
events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; events = this.style.pointerEvents;
} }
return ( return (
!this.isRounded && !this.isRounded &&
!this.glass && !this.glass &&
this.rotation === 0 && this.rotation === 0 &&
(events || (this.fill != null && this.fill !== NONE)) (events || this.fill !== NONE)
); );
} }
/** /**
* Generic background painting implementation. * Generic background painting implementation.
*/ */
// paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintBackground(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
let events = true; let events = true;
if (this.style != null) { if (this.style) {
events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; events = this.style.pointerEvents;
} }
if ( if (events || this.fill !== NONE || this.stroke !== NONE) {
events || if (!events && this.fill === NONE) {
(this.fill != null && this.fill !== NONE) ||
(this.stroke != null && this.stroke !== NONE)
) {
if (!events && (this.fill == null || this.fill === NONE)) {
c.pointerEvents = false; c.pointerEvents = false;
} }
if (this.isRounded) { if (this.isRounded) {
let r = 0; let r = 0;
if (utils.getValue(this.style, 'absoluteArcSize', 0) == '1') { if (this.style?.absoluteArcSize ?? false) {
r = Math.min( r = Math.min(w / 2, Math.min(h / 2, (this.style?.arcSize ?? LINE_ARCSIZE) / 2));
w / 2,
Math.min(
h / 2,
utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2
)
);
} else { } else {
const f = const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100;
utils.getValue(
this.style,
'arcSize',
RECTANGLE_ROUNDING_FACTOR * 100
) / 100;
r = Math.min(w * f, h * f); r = Math.min(w * f, h * f);
} }
@ -117,41 +90,22 @@ class RectangleShape extends Shape {
/** /**
* Adds roundable support. * Adds roundable support.
*/ */
// isRoundable(c?: mxAbstractCanvas2D, x?: number, y?: number, w?: number, h?: number): boolean; isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
isRoundable(
c: mxAbstractCanvas2D,
x: number,
y: number,
w: number,
h: number
): boolean {
return true; return true;
} }
/** /**
* Generic background painting implementation. * Generic background painting implementation.
*/ */
// paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number): void {
paintForeground( if (this.glass && !this.outline && this.fill !== NONE) {
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
): void {
if (
this.glass &&
!this.outline &&
this.fill != null &&
this.fill !== NONE
) {
this.paintGlassEffect( this.paintGlassEffect(
c, c,
x, x,
y, y,
w, w,
h, h,
this.getArcSize(w + this.strokewidth, h + this.strokewidth) this.getArcSize(w + this.strokeWidth, h + this.strokeWidth)
); );
} }
} }

View File

@ -6,10 +6,9 @@
*/ */
import Shape from '../Shape'; import Shape from '../Shape';
import Point from '../../Point'; import Point from '../../Point';
import utils from '../../../../util/Utils';
import { LINE_ARCSIZE } from '../../../../util/Constants'; import { LINE_ARCSIZE } from '../../../../util/Constants';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
/** /**
* Extends {@link Shape} to implement a rhombus (aka diamond) shape. * Extends {@link Shape} to implement a rhombus (aka diamond) shape.
@ -18,17 +17,12 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D';
* @extends {Shape} * @extends {Shape}
*/ */
class RhombusShape extends Shape { class RhombusShape extends Shape {
constructor( constructor(bounds: Rectangle, fill: string, stroke: string, strokewidth: number = 1) {
bounds: Rectangle,
fill: string,
stroke: string,
strokewidth: number = 1
) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth; this.strokeWidth = strokewidth;
} }
/** /**
@ -47,19 +41,12 @@ class RhombusShape extends Shape {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
// paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintVertexShape(
c: mxSvgCanvas2D,
x: number,
y: number,
w: number,
h: number
) {
const hw = w / 2; const hw = w / 2;
const hh = h / 2; const hh = h / 2;
const arcSize = const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2;
utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2;
c.begin(); c.begin();
this.addPoints( this.addPoints(
c, c,

View File

@ -9,18 +9,22 @@ import ConnectionConstraint from '../../../connection/ConnectionConstraint';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import Shape from '../Shape'; import Shape from '../Shape';
import Resources from '../../../../util/Resources'; import Resources from '../../../../util/Resources';
import utils, { getNumber, getValue, isNotNullish } from '../../../../util/Utils'; import { getNumber, getValue, isNotNullish } from '../../../../util/Utils';
import { import {
ALIGN_LEFT,
ALIGN_TOP,
DIRECTION_NORTH, DIRECTION_NORTH,
DIRECTION_SOUTH, DIRECTION_SOUTH,
NODETYPE_ELEMENT, NODETYPE_ELEMENT,
NONE, NONE,
RECTANGLE_ROUNDING_FACTOR, RECTANGLE_ROUNDING_FACTOR,
TEXT_DIRECTION_AUTO,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import StencilShapeRegistry from './StencilShapeRegistry'; import StencilShapeRegistry from './StencilShapeRegistry';
import { getChildNodes, getTextContent } from '../../../../util/DomUtils'; import { getChildNodes, getTextContent } from '../../../../util/DomUtils';
import Point from '../../Point'; import Point from '../../Point';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import { AlignValue, ColorValue, VAlignValue } from 'packages/core/src/types';
/** /**
* Implements a generic shape which is based on a XML node as a description. * Implements a generic shape which is based on a XML node as a description.
@ -107,7 +111,7 @@ class StencilShape extends Shape {
* *
* Holds the strokewidth direction from the description. * Holds the strokewidth direction from the description.
*/ */
strokeWidth: string | null = null; strokeWidthValue: string | null = null;
/** /**
* Function: parseDescription * Function: parseDescription
@ -132,7 +136,7 @@ class StencilShape extends Shape {
// user-defined stroke-width). Note that the strokewidth is scaled // user-defined stroke-width). Note that the strokewidth is scaled
// by the minimum scaling that is used to draw the shape (sx, sy). // by the minimum scaling that is used to draw the shape (sx, sy).
const sw = this.desc.getAttribute('strokewidth'); const sw = this.desc.getAttribute('strokewidth');
this.strokeWidth = isNotNullish(sw) ? sw : '1'; this.strokeWidthValue = isNotNullish(sw) ? sw : '1';
} }
/** /**
@ -144,10 +148,10 @@ class StencilShape extends Shape {
parseConstraints() { parseConstraints() {
const conns = this.desc.getElementsByTagName('connections')[0]; const conns = this.desc.getElementsByTagName('connections')[0];
if (conns != null) { if (conns) {
const tmp = getChildNodes(conns); const tmp = getChildNodes(conns);
if (tmp != null && tmp.length > 0) { if (tmp.length > 0) {
this.constraints = []; this.constraints = [];
for (let i = 0; i < tmp.length; i += 1) { for (let i = 0; i < tmp.length; i += 1) {
@ -165,7 +169,7 @@ class StencilShape extends Shape {
parseConstraint(node: Element) { parseConstraint(node: Element) {
const x = Number(node.getAttribute('x')); const x = Number(node.getAttribute('x'));
const y = Number(node.getAttribute('y')); const y = Number(node.getAttribute('y'));
const perimeter = node.getAttribute('perimeter') == '1'; const perimeter = node.getAttribute('perimeter') === '1';
const name = node.getAttribute('name'); const name = node.getAttribute('name');
return new ConnectionConstraint(new Point(x, y), perimeter, name); return new ConnectionConstraint(new Point(x, y), perimeter, name);
@ -182,7 +186,7 @@ class StencilShape extends Shape {
let result = this.evaluateAttribute(node, attribute, shape); let result = this.evaluateAttribute(node, attribute, shape);
const loc = node.getAttribute('localized'); const loc = node.getAttribute('localized');
if ((StencilShape.defaultLocalized && loc == null) || loc == '1') { if ((StencilShape.defaultLocalized && !loc) || loc === '1') {
result = Resources.get(result); result = Resources.get(result);
} }
@ -200,10 +204,10 @@ class StencilShape extends Shape {
evaluateAttribute(node: Element, attribute: string, shape: Shape) { evaluateAttribute(node: Element, attribute: string, shape: Shape) {
let result = node.getAttribute(attribute); let result = node.getAttribute(attribute);
if (result == null) { if (!result) {
const text = getTextContent(node); const text = getTextContent(node);
if (text != null && StencilShape.allowEval) { if (text && StencilShape.allowEval) {
const funct = eval(text); const funct = eval(text);
if (typeof funct === 'function') { if (typeof funct === 'function') {
@ -221,7 +225,7 @@ class StencilShape extends Shape {
* Draws this stencil inside the given bounds. * Draws this stencil inside the given bounds.
*/ */
drawShape( drawShape(
canvas: mxSvgCanvas2D, canvas: AbstractCanvas2D,
shape: Shape, shape: Shape,
x: number, x: number,
y: number, y: number,
@ -240,34 +244,20 @@ class StencilShape extends Shape {
const aspect = this.computeAspect(shape, x, y, w, h, direction); const aspect = this.computeAspect(shape, x, y, w, h, direction);
const minScale = Math.min(aspect.width, aspect.height); const minScale = Math.min(aspect.width, aspect.height);
const sw = const sw =
this.strokeWidth == 'inherit' this.strokeWidthValue === 'inherit'
? Number(getNumber(shape.style, 'strokeWidth', 1)) ? Number(getNumber(shape.style, 'strokeWidth', 1))
: Number(this.strokeWidth) * minScale; : Number(this.strokeWidthValue) * minScale;
canvas.setStrokeWidth(sw); canvas.setStrokeWidth(sw);
// Draws a transparent rectangle for catching events // Draws a transparent rectangle for catching events
if ( if (shape.style?.pointerEvents ?? false) {
shape.style != null &&
getValue(shape.style, 'pointerEvents', '0') == '1'
) {
canvas.setStrokeColor(NONE); canvas.setStrokeColor(NONE);
canvas.rect(x, y, w, h); canvas.rect(x, y, w, h);
canvas.stroke(); canvas.stroke();
canvas.setStrokeColor(shape.stroke); canvas.setStrokeColor(shape.stroke);
} }
this.drawChildren( this.drawChildren(canvas, shape, x, y, w, h, this.bgNode, aspect, false, true);
canvas,
shape,
x,
y,
w,
h,
this.bgNode,
aspect,
false,
true
);
this.drawChildren( this.drawChildren(
canvas, canvas,
shape, shape,
@ -295,26 +285,26 @@ class StencilShape extends Shape {
* Draws this stencil inside the given bounds. * Draws this stencil inside the given bounds.
*/ */
drawChildren( drawChildren(
canvas: mxSvgCanvas2D, canvas: AbstractCanvas2D,
shape: Shape, shape: Shape,
x: number, x: number,
y: number, y: number,
w: number, w: number,
h: number, h: number,
node: Element | null, node: Element | null,
aspect: string, aspect: Rectangle,
disableShadow: boolean, disableShadow: boolean,
paint: boolean paint: boolean
) { ) {
if (node != null && w > 0 && h > 0) { if (node && w > 0 && h > 0) {
let tmp = node.firstChild; let tmp = node.firstChild as Element;
while (tmp != null) { while (tmp) {
if (tmp.nodeType === NODETYPE_ELEMENT) { if (tmp.nodeType === NODETYPE_ELEMENT) {
this.drawNode(canvas, shape, tmp, aspect, disableShadow, paint); this.drawNode(canvas, shape, tmp, aspect, disableShadow, paint);
} }
tmp = tmp.nextSibling; tmp = tmp.nextSibling as Element;
} }
} }
} }
@ -345,8 +335,7 @@ class StencilShape extends Shape {
let sx = w / this.w0; let sx = w / this.w0;
let sy = h / this.h0; let sy = h / this.h0;
const inverse = const inverse = direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH;
direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH;
if (inverse) { if (inverse) {
sy = w / this.h0; sy = w / this.h0;
@ -381,7 +370,7 @@ class StencilShape extends Shape {
* Draws this stencil inside the given bounds. * Draws this stencil inside the given bounds.
*/ */
drawNode( drawNode(
canvas: mxSvgCanvas2D, canvas: AbstractCanvas2D,
shape: Shape, shape: Shape,
node: Element, node: Element,
aspect: Rectangle, aspect: Rectangle,
@ -410,10 +399,10 @@ class StencilShape extends Shape {
const arcSize = Number(node.getAttribute('arcSize')); const arcSize = Number(node.getAttribute('arcSize'));
let pointCount = 0; let pointCount = 0;
const segs = []; const segs: Point[][] = [];
// Renders the elements inside the given path // Renders the elements inside the given path
let childNode = node.firstChild; let childNode = node.firstChild as Element;
while (childNode != null) { while (childNode != null) {
if (childNode.nodeType === NODETYPE_ELEMENT) { if (childNode.nodeType === NODETYPE_ELEMENT) {
@ -438,7 +427,7 @@ class StencilShape extends Shape {
} }
} }
childNode = childNode.nextSibling; childNode = childNode.nextSibling as Element;
} }
if (!parseRegularly && pointCount > 0) { if (!parseRegularly && pointCount > 0) {
@ -461,21 +450,14 @@ class StencilShape extends Shape {
if (parseRegularly) { if (parseRegularly) {
// Renders the elements inside the given path // Renders the elements inside the given path
let childNode = node.firstChild; let childNode = node.firstChild as Element;
while (childNode != null) { while (childNode) {
if (childNode.nodeType === NODETYPE_ELEMENT) { if (childNode.nodeType === NODETYPE_ELEMENT) {
this.drawNode( this.drawNode(canvas, shape, childNode, aspect, disableShadow, paint);
canvas,
shape,
childNode,
aspect,
disableShadow,
paint
);
} }
childNode = childNode.nextSibling; childNode = childNode.nextSibling as Element;
} }
} }
} else if (name === 'close') { } else if (name === 'close') {
@ -512,7 +494,7 @@ class StencilShape extends Shape {
Number(node.getAttribute('ry')) * sy, Number(node.getAttribute('ry')) * sy,
Number(node.getAttribute('x-axis-rotation')), Number(node.getAttribute('x-axis-rotation')),
Boolean(node.getAttribute('large-arc-flag')), Boolean(node.getAttribute('large-arc-flag')),
Number(node.getAttribute('sweep-flag')), Boolean(node.getAttribute('sweep-flag')),
x0 + Number(node.getAttribute('x')) * sx, x0 + Number(node.getAttribute('x')) * sx,
y0 + Number(node.getAttribute('y')) * sy y0 + Number(node.getAttribute('y')) * sy
); );
@ -552,7 +534,7 @@ class StencilShape extends Shape {
); );
} else if (name === 'image') { } else if (name === 'image') {
if (!shape.outline) { if (!shape.outline) {
const src = this.evaluateAttribute(node, 'src', shape); const src = this.evaluateAttribute(node, 'src', shape) as string;
canvas.image( canvas.image(
x0 + Number(node.getAttribute('x')) * sx, x0 + Number(node.getAttribute('x')) * sx,
@ -567,10 +549,10 @@ class StencilShape extends Shape {
} }
} else if (name === 'text') { } else if (name === 'text') {
if (!shape.outline) { if (!shape.outline) {
const str = this.evaluateTextAttribute(node, 'str', shape); const str = this.evaluateTextAttribute(node, 'str', shape) as string;
let rotation = node.getAttribute('vertical') == '1' ? -90 : 0; let rotation = node.getAttribute('vertical') == '1' ? -90 : 0;
if (node.getAttribute('align-shape') == '0') { if (node.getAttribute('align-shape') === '0') {
const dr = shape.rotation; const dr = shape.rotation;
// Depends on flipping // Depends on flipping
@ -586,7 +568,7 @@ class StencilShape extends Shape {
} }
} }
rotation -= node.getAttribute('rotation'); rotation -= Number(node.getAttribute('rotation'));
canvas.text( canvas.text(
x0 + Number(node.getAttribute('x')) * sx, x0 + Number(node.getAttribute('x')) * sx,
@ -594,19 +576,22 @@ class StencilShape extends Shape {
0, 0,
0, 0,
str, str,
node.getAttribute('align') || 'left', (node.getAttribute('align') as AlignValue) || ALIGN_LEFT,
node.getAttribute('valign') || 'top', (node.getAttribute('valign') as VAlignValue) || ALIGN_TOP,
false, false,
'', '',
null, 'auto',
false, false,
rotation rotation,
TEXT_DIRECTION_AUTO
); );
} }
} else if (name === 'include-shape') { } else if (name === 'include-shape') {
const stencil = StencilShapeRegistry.getStencil(node.getAttribute('name')); const stencil = StencilShapeRegistry.getStencil(
node.getAttribute('name') as string
);
if (stencil != null) { if (stencil) {
const x = x0 + Number(node.getAttribute('x')) * sx; const x = x0 + Number(node.getAttribute('x')) * sx;
const y = y0 + Number(node.getAttribute('y')) * sy; const y = y0 + Number(node.getAttribute('y')) * sy;
const w = Number(node.getAttribute('w')) * sx; const w = Number(node.getAttribute('w')) * sx;
@ -642,27 +627,27 @@ class StencilShape extends Shape {
canvas.setDashPattern(value); canvas.setDashPattern(value);
} }
} else if (name === 'strokecolor') { } else if (name === 'strokecolor') {
canvas.setStrokeColor(node.getAttribute('color')); canvas.setStrokeColor(node.getAttribute('color') as ColorValue);
} else if (name === 'linecap') { } else if (name === 'linecap') {
canvas.setLineCap(node.getAttribute('cap')); canvas.setLineCap(node.getAttribute('cap') as string);
} else if (name === 'linejoin') { } else if (name === 'linejoin') {
canvas.setLineJoin(node.getAttribute('join')); canvas.setLineJoin(node.getAttribute('join') as string);
} else if (name === 'miterlimit') { } else if (name === 'miterlimit') {
canvas.setMiterLimit(Number(node.getAttribute('limit'))); canvas.setMiterLimit(Number(node.getAttribute('limit')));
} else if (name === 'fillcolor') { } else if (name === 'fillcolor') {
canvas.setFillColor(node.getAttribute('color')); canvas.setFillColor(node.getAttribute('color') as ColorValue);
} else if (name === 'alpha') { } else if (name === 'alpha') {
canvas.setAlpha(node.getAttribute('alpha')); canvas.setAlpha(Number(node.getAttribute('alpha')));
} else if (name === 'fillalpha') { } else if (name === 'fillalpha') {
canvas.setAlpha(node.getAttribute('alpha')); canvas.setAlpha(Number(node.getAttribute('alpha')));
} else if (name === 'strokealpha') { } else if (name === 'strokealpha') {
canvas.setAlpha(node.getAttribute('alpha')); canvas.setAlpha(Number(node.getAttribute('alpha')));
} else if (name === 'fontcolor') { } else if (name === 'fontcolor') {
canvas.setFontColor(node.getAttribute('color')); canvas.setFontColor(node.getAttribute('color') as ColorValue);
} else if (name === 'fontstyle') { } else if (name === 'fontstyle') {
canvas.setFontStyle(node.getAttribute('style')); canvas.setFontStyle(Number(node.getAttribute('style')));
} else if (name === 'fontfamily') { } else if (name === 'fontfamily') {
canvas.setFontFamily(node.getAttribute('family')); canvas.setFontFamily(node.getAttribute('family') as string);
} else if (name === 'fontsize') { } else if (name === 'fontsize') {
canvas.setFontSize(Number(node.getAttribute('size')) * minScale); canvas.setFontSize(Number(node.getAttribute('size')) * minScale);
} }

View File

@ -15,7 +15,8 @@ import {
NONE, NONE,
RECTANGLE_ROUNDING_FACTOR, RECTANGLE_ROUNDING_FACTOR,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import utils from '../../../../util/Utils'; import { ColorValue } from 'packages/core/src/types';
import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D';
/** /**
* Extends {@link Shape} to implement a swimlane shape. * Extends {@link Shape} to implement a swimlane shape.
@ -32,12 +33,12 @@ import utils from '../../../../util/Utils';
* @extends {Shape} * @extends {Shape}
*/ */
class SwimlaneShape extends Shape { class SwimlaneShape extends Shape {
constructor(bounds, fill, stroke, strokewidth) { constructor(bounds: Rectangle, fill: ColorValue, stroke: ColorValue, strokeWidth = 1) {
super(); super();
this.bounds = bounds; this.bounds = bounds;
this.fill = fill; this.fill = fill;
this.stroke = stroke; this.stroke = stroke;
this.strokewidth = strokewidth != null ? strokewidth : 1; this.strokeWidth = strokeWidth;
} }
/** /**
@ -47,9 +48,10 @@ class SwimlaneShape extends Shape {
* @type {number} * @type {number}
* @default 16 * @default 16
*/ */
// imageSize: number;
imageSize = 16; imageSize = 16;
imageSrc: string | null = null;
/** /**
* Adds roundable support. * Adds roundable support.
* @param {mxAbstractCanvas2D} c * @param {mxAbstractCanvas2D} c
@ -59,33 +61,27 @@ class SwimlaneShape extends Shape {
* @param {number} h * @param {number} h
* @returns {boolean} * @returns {boolean}
*/ */
// isRoundable(c?: mxAbstractCanvas2D, x?: number, y?: number, w?: number, h?: number): boolean; isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
isRoundable(c, x, y, w, h) {
return true; return true;
} }
/** /**
* Returns the bounding box for the gradient box for this shape. * Returns the bounding box for the gradient box for this shape.
*/ */
// getTitleSize(): number;
getTitleSize() { getTitleSize() {
return Math.max( return Math.max(0, this.style?.startSize ?? DEFAULT_STARTSIZE);
0,
utils.getValue(this.style, 'startSize', DEFAULT_STARTSIZE)
);
} }
/** /**
* Returns the bounding box for the gradient box for this shape. * Returns the bounding box for the gradient box for this shape.
*/ */
// getLabelBounds(rect: mxRectangle): mxRectangle; getLabelBounds(rect: Rectangle) {
getLabelBounds(rect) {
const start = this.getTitleSize(); const start = this.getTitleSize();
const bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height); const bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height);
const horizontal = this.isHorizontal(); const horizontal = this.isHorizontal();
const flipH = utils.getValue(this.style, 'flipH', 0) == 1; const flipH = this.style?.flipH ?? false;
const flipV = utils.getValue(this.style, 'flipV', 0) == 1; const flipV = this.style?.flipV ?? false;
// East is default // East is default
const shapeVertical = const shapeVertical =
@ -94,14 +90,10 @@ class SwimlaneShape extends Shape {
const realFlipH = const realFlipH =
!realHorizontal && !realHorizontal &&
flipH != flipH !== (this.direction === DIRECTION_SOUTH || this.direction === DIRECTION_WEST);
(this.direction === DIRECTION_SOUTH ||
this.direction === DIRECTION_WEST);
const realFlipV = const realFlipV =
realHorizontal && realHorizontal &&
flipV != flipV !== (this.direction === DIRECTION_SOUTH || this.direction === DIRECTION_WEST);
(this.direction === DIRECTION_SOUTH ||
this.direction === DIRECTION_WEST);
// Shape is horizontal // Shape is horizontal
if (!shapeVertical) { if (!shapeVertical) {
@ -128,8 +120,7 @@ class SwimlaneShape extends Shape {
/** /**
* Returns the bounding box for the gradient box for this shape. * Returns the bounding box for the gradient box for this shape.
*/ */
// getGradientBounds(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): mxRectangle; getGradientBounds(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
getGradientBounds(c, x, y, w, h) {
let start = this.getTitleSize(); let start = this.getTitleSize();
if (this.isHorizontal()) { if (this.isHorizontal()) {
@ -145,22 +136,11 @@ class SwimlaneShape extends Shape {
* *
* Returns the arcsize for the swimlane. * Returns the arcsize for the swimlane.
*/ */
getSwimlaneArcSize(w, h, start) { getSwimlaneArcSize(w: number, h: number, start: number) {
if (utils.getValue(this.style, 'absoluteArcSize', 0) == '1') { if (this.style?.absoluteArcSize ?? false) {
return Math.min( return Math.min(w / 2, Math.min(h / 2, this.style?.arcSize ?? LINE_ARCSIZE / 2));
w / 2,
Math.min(
h / 2,
utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2
)
);
} }
const f = const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100;
utils.getValue(
this.style,
'arcSize',
RECTANGLE_ROUNDING_FACTOR * 100
) / 100;
return start * f * 3; return start * f * 3;
} }
@ -168,20 +148,17 @@ class SwimlaneShape extends Shape {
/** /**
* Paints the swimlane vertex shape. * Paints the swimlane vertex shape.
*/ */
// isHorizontal(): boolean;
isHorizontal() { isHorizontal() {
return utils.getValue(this.style, 'horizontal', 1) == 1; return this.style?.horizontal ?? true;
} }
/** /**
* Paints the swimlane vertex shape. * Paints the swimlane vertex shape.
*/ */
// paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
paintVertexShape(c, x, y, w, h) {
let start = this.getTitleSize(); let start = this.getTitleSize();
const fill = utils.getValue(this.style, 'swimlaneFillColor', NONE); const fill = this.style?.swimlaneFillColor ?? NONE;
const swimlaneLine = const swimlaneLine = this.style?.swimlaneLine ?? true;
utils.getValue(this.style, 'swimlaneLine', 1) == 1;
let r = 0; let r = 0;
if (this.isHorizontal()) { if (this.isHorizontal()) {
@ -200,17 +177,17 @@ class SwimlaneShape extends Shape {
this.paintRoundedSwimlane(c, x, y, w, h, start, r, fill, swimlaneLine); this.paintRoundedSwimlane(c, x, y, w, h, start, r, fill, swimlaneLine);
} }
const sep = utils.getValue(this.style, 'separatorColor', NONE); const sep = this.style?.separatorColor ?? NONE;
this.paintSeparator(c, x, y, w, h, start, sep); this.paintSeparator(c, x, y, w, h, start, sep);
if (this.image != null) { if (this.imageSrc) {
const bounds = this.getImageBounds(x, y, w, h); const bounds = this.getImageBounds(x, y, w, h);
c.image( c.image(
bounds.x - x, bounds.x - x,
bounds.y - y, bounds.y - y,
bounds.width, bounds.width,
bounds.height, bounds.height,
this.image, this.imageSrc,
false, false,
false, false,
false false
@ -228,16 +205,25 @@ class SwimlaneShape extends Shape {
* *
* Paints the swimlane vertex shape. * Paints the swimlane vertex shape.
*/ */
paintSwimlane(c, x, y, w, h, start, fill, swimlaneLine) { paintSwimlane(
c: AbstractCanvas2D,
x: number,
y: number,
w: number,
h: number,
start: number,
fill: ColorValue,
swimlaneLine: boolean
) {
c.begin(); c.begin();
let events = true; let events = true;
if (this.style != null) { if (this.style) {
events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; events = this.style.pointerEvents;
} }
if (!events && (this.fill == null || this.fill === NONE)) { if (!events && this.fill === NONE) {
c.pointerEvents = false; c.pointerEvents = false;
} }
@ -309,16 +295,26 @@ class SwimlaneShape extends Shape {
* *
* Paints the swimlane vertex shape. * Paints the swimlane vertex shape.
*/ */
paintRoundedSwimlane(c, x, y, w, h, start, r, fill, swimlaneLine) { paintRoundedSwimlane(
c: AbstractCanvas2D,
x: number,
y: number,
w: number,
h: number,
start: number,
r: number,
fill: ColorValue,
swimlaneLine: boolean
) {
c.begin(); c.begin();
let events = true; let events = true;
if (this.style != null) { if (this.style) {
events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; events = this.style.pointerEvents;
} }
if (!events && (this.fill == null || this.fill === NONE)) { if (!events && this.fill === NONE) {
c.pointerEvents = false; c.pointerEvents = false;
} }
@ -398,7 +394,15 @@ class SwimlaneShape extends Shape {
* *
* Paints the divider between swimlane title and content area. * Paints the divider between swimlane title and content area.
*/ */
paintDivider(c, x, y, w, h, start, shadow) { paintDivider(
c: AbstractCanvas2D,
x: number,
y: number,
w: number,
h: number,
start: number,
shadow: boolean
) {
if (!shadow) { if (!shadow) {
c.setShadow(false); c.setShadow(false);
} }
@ -421,7 +425,15 @@ class SwimlaneShape extends Shape {
* *
* Paints the vertical or horizontal separator line between swimlanes. * Paints the vertical or horizontal separator line between swimlanes.
*/ */
paintSeparator(c, x, y, w, h, start, color) { paintSeparator(
c: AbstractCanvas2D,
x: number,
y: number,
w: number,
h: number,
start: number,
color: ColorValue
) {
if (color !== NONE) { if (color !== NONE) {
c.setStrokeColor(color); c.setStrokeColor(color);
c.setDashed(true); c.setDashed(true);
@ -443,15 +455,9 @@ class SwimlaneShape extends Shape {
/** /**
* Paints the swimlane vertex shape. * Paints the swimlane vertex shape.
*/ */
// getImageBounds(x: number, y: number, w: number, h: number): mxRectangle; getImageBounds(x: number, y: number, w: number, h: number) {
getImageBounds(x, y, w, h) {
if (this.isHorizontal()) { if (this.isHorizontal()) {
return new Rectangle( return new Rectangle(x + w - this.imageSize, y, this.imageSize, this.imageSize);
x + w - this.imageSize,
y,
this.imageSize,
this.imageSize
);
} }
return new Rectangle(x, y, this.imageSize, this.imageSize); return new Rectangle(x, y, this.imageSize, this.imageSize);
} }

View File

@ -24,22 +24,31 @@ import {
LINE_HEIGHT, LINE_HEIGHT,
NONE, NONE,
TEXT_DIRECTION_AUTO, TEXT_DIRECTION_AUTO,
TEXT_DIRECTION_DEFAULT,
TEXT_DIRECTION_LTR, TEXT_DIRECTION_LTR,
TEXT_DIRECTION_RTL, TEXT_DIRECTION_RTL,
WORD_WRAP, WORD_WRAP,
} from '../../../../util/Constants'; } from '../../../../util/Constants';
import utils, { import { getAlignmentAsPoint, getBoundingBox } from '../../../../util/Utils';
getAlignmentAsPoint,
getBoundingBox,
getValue,
} from '../../../../util/Utils';
import Point from '../../Point'; import Point from '../../Point';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D';
import Shape from '../Shape'; import Shape from '../Shape';
import Rectangle from '../../Rectangle'; import Rectangle from '../../Rectangle';
import CellState from '../../../cell/datatypes/CellState'; import CellState from '../../../cell/datatypes/CellState';
import { htmlEntities, replaceTrailingNewlines, trim } from '../../../../util/StringUtils'; import {
htmlEntities,
replaceTrailingNewlines,
trim,
} from '../../../../util/StringUtils';
import { isNode } from '../../../../util/DomUtils'; import { isNode } from '../../../../util/DomUtils';
import {
AlignValue,
ColorValue,
OverflowValue,
TextDirectionValue,
VAlignValue,
} from 'packages/core/src/types';
import SvgCanvas2D from 'packages/core/src/util/canvas/SvgCanvas2D';
/** /**
* Extends mxShape to implement a text shape. * Extends mxShape to implement a text shape.
@ -56,28 +65,27 @@ class TextShape extends Shape {
constructor( constructor(
value: string, value: string,
bounds: Rectangle, bounds: Rectangle,
align: string = ALIGN_CENTER, align: AlignValue = ALIGN_CENTER,
valign: string | null = ALIGN_MIDDLE, valign: VAlignValue = ALIGN_MIDDLE,
color: string = 'black', color = 'black',
family: string = DEFAULT_FONTFAMILY, family = DEFAULT_FONTFAMILY,
size: number = DEFAULT_FONTSIZE, size = DEFAULT_FONTSIZE,
fontStyle: number = DEFAULT_FONTSTYLE, fontStyle = DEFAULT_FONTSTYLE,
spacing: number = 2, spacing = 2,
spacingTop: number = 0, spacingTop = 0,
spacingRight: number = 0, spacingRight = 0,
spacingBottom: number = 0, spacingBottom = 0,
spacingLeft: number = 0, spacingLeft = 0,
horizontal: boolean = true, horizontal = true,
background: string | null = null, background = NONE,
border: string | null = null, border = NONE,
wrap: boolean = false, wrap = false,
clipped: boolean = false, clipped = false,
overflow: string = 'visible', overflow: OverflowValue = 'visible',
labelPadding: number = 0, labelPadding = 0,
textDirection: string = DEFAULT_TEXT_DIRECTION textDirection: TextDirectionValue = DEFAULT_TEXT_DIRECTION
) { ) {
super(); super();
valign = valign != null ? valign : ALIGN_MIDDLE;
this.value = value; this.value = value;
this.bounds = bounds; this.bounds = bounds;
@ -87,14 +95,11 @@ class TextShape extends Shape {
this.family = family; this.family = family;
this.size = size; this.size = size;
this.fontStyle = fontStyle; this.fontStyle = fontStyle;
this.spacing = parseInt(String(spacing || 2)); this.spacing = spacing;
this.spacingTop = parseInt(String(spacing || 2)) + parseInt(String(spacingTop || 0)); this.spacingTop = spacing + spacingTop;
this.spacingRight = this.spacingRight = spacing + spacingRight;
parseInt(String(spacing || 2)) + parseInt(String(spacingRight || 0)); this.spacingBottom = spacing + spacingBottom;
this.spacingBottom = this.spacingLeft = spacing + spacingLeft;
parseInt(String(spacing || 2)) + parseInt(String(spacingBottom || 0));
this.spacingLeft =
parseInt(String(spacing || 2)) + parseInt(String(spacingLeft || 0));
this.horizontal = horizontal; this.horizontal = horizontal;
this.background = background; this.background = background;
this.border = border; this.border = border;
@ -110,29 +115,29 @@ class TextShape extends Shape {
// TODO: Document me! // TODO: Document me!
value: string | HTMLElement | SVGGElement | null; value: string | HTMLElement | SVGGElement | null;
bounds: Rectangle; bounds: Rectangle;
align: string = ALIGN_CENTER; align: AlignValue;
valign: string = ALIGN_MIDDLE; valign: VAlignValue;
color: string = 'black'; color: ColorValue;
family: string = DEFAULT_FONTFAMILY; family: string;
size: number = DEFAULT_FONTSIZE; size: number;
fontStyle: number = DEFAULT_FONTSTYLE; fontStyle: number;
spacing: number = 2; spacing: number;
spacingTop: number = 0; spacingTop: number;
spacingRight: number = 0; spacingRight: number;
spacingBottom: number = 0; spacingBottom: number;
spacingLeft: number = 0; spacingLeft: number;
horizontal: boolean = true; horizontal: boolean;
background: string | null = null; background: ColorValue;
border: string | null = null; border: ColorValue;
wrap: boolean = false; wrap: boolean;
clipped: boolean = false; clipped: boolean;
overflow: string = 'visible'; overflow: OverflowValue;
labelPadding: number = 0; labelPadding: number;
textDirection: string = DEFAULT_TEXT_DIRECTION; textDirection: TextDirectionValue;
margin: Point | null = null; margin: Point | null = null;
unrotatedBoundingBox: Rectangle | null = null; unrotatedBoundingBox: Rectangle | null = null;
flipH: boolean = false; flipH = false;
flipV: boolean = false; flipV = false;
/** /**
* Variable: baseSpacingTop * Variable: baseSpacingTop
@ -140,7 +145,7 @@ class TextShape extends Shape {
* Specifies the spacing to be added to the top spacing. Default is 0. Use the * Specifies the spacing to be added to the top spacing. Default is 0. Use the
* value 5 here to get the same label positions as in mxGraph 1.x. * value 5 here to get the same label positions as in mxGraph 1.x.
*/ */
baseSpacingTop: number = 0; baseSpacingTop = 0;
/** /**
* Variable: baseSpacingBottom * Variable: baseSpacingBottom
@ -148,21 +153,21 @@ class TextShape extends Shape {
* Specifies the spacing to be added to the bottom spacing. Default is 0. Use the * Specifies the spacing to be added to the bottom spacing. Default is 0. Use the
* value 1 here to get the same label positions as in mxGraph 1.x. * value 1 here to get the same label positions as in mxGraph 1.x.
*/ */
baseSpacingBottom: number = 0; baseSpacingBottom = 0;
/** /**
* Variable: baseSpacingLeft * Variable: baseSpacingLeft
* *
* Specifies the spacing to be added to the left spacing. Default is 0. * Specifies the spacing to be added to the left spacing. Default is 0.
*/ */
baseSpacingLeft: number = 0; baseSpacingLeft = 0;
/** /**
* Variable: baseSpacingRight * Variable: baseSpacingRight
* *
* Specifies the spacing to be added to the right spacing. Default is 0. * Specifies the spacing to be added to the right spacing. Default is 0.
*/ */
baseSpacingRight: number = 0; baseSpacingRight = 0;
/** /**
* Variable: replaceLinefeeds * Variable: replaceLinefeeds
@ -170,14 +175,14 @@ class TextShape extends Shape {
* Specifies if linefeeds in HTML labels should be replaced with BR tags. * Specifies if linefeeds in HTML labels should be replaced with BR tags.
* Default is true. * Default is true.
*/ */
replaceLinefeeds: boolean = true; replaceLinefeeds = true;
/** /**
* Variable: verticalTextRotation * Variable: verticalTextRotation
* *
* Rotation for vertical text. Default is -90 (bottom to top). * Rotation for vertical text. Default is -90 (bottom to top).
*/ */
verticalTextRotation: number = -90; verticalTextRotation = -90;
/** /**
* Variable: ignoreClippedStringSize * Variable: ignoreClippedStringSize
@ -187,7 +192,7 @@ class TextShape extends Shape {
* true, then the bounding box will be set to <bounds>. Default is true. * true, then the bounding box will be set to <bounds>. Default is true.
* <ignoreStringSize> has precedence over this switch. * <ignoreStringSize> has precedence over this switch.
*/ */
ignoreClippedStringSize: boolean = true; ignoreClippedStringSize = true;
/** /**
* Variable: ignoreStringSize * Variable: ignoreStringSize
@ -196,7 +201,7 @@ class TextShape extends Shape {
* boundingBox will not ignore the actual size of the string, otherwise * boundingBox will not ignore the actual size of the string, otherwise
* <bounds> will be used instead. Default is false. * <bounds> will be used instead. Default is false.
*/ */
ignoreStringSize: boolean = false; ignoreStringSize = false;
/** /**
* Variable: lastValue * Variable: lastValue
@ -210,14 +215,14 @@ class TextShape extends Shape {
* *
* Specifies if caching for HTML labels should be enabled. Default is true. * Specifies if caching for HTML labels should be enabled. Default is true.
*/ */
cacheEnabled: boolean = true; cacheEnabled = true;
/** /**
* Function: getSvgScreenOffset * Function: getSvgScreenOffset
* *
* Disables offset in IE9 for crisper image output. * Disables offset in IE9 for crisper image output.
*/ */
getSvgScreenOffset(): number { getSvgScreenOffset() {
return 0; return 0;
} }
@ -226,12 +231,12 @@ class TextShape extends Shape {
* *
* Returns true if the bounds are not null and all of its variables are numeric. * Returns true if the bounds are not null and all of its variables are numeric.
*/ */
checkBounds(): boolean { checkBounds() {
return ( return (
!isNaN(this.scale) && !isNaN(this.scale) &&
isFinite(this.scale) && isFinite(this.scale) &&
this.scale > 0 && this.scale > 0 &&
this.bounds != null && this.bounds &&
!isNaN(this.bounds.x) && !isNaN(this.bounds.x) &&
!isNaN(this.bounds.y) && !isNaN(this.bounds.y) &&
!isNaN(this.bounds.width) && !isNaN(this.bounds.width) &&
@ -244,7 +249,7 @@ class TextShape extends Shape {
* *
* Generic rendering code. * Generic rendering code.
*/ */
paint(c: mxSvgCanvas2D, update: boolean = false): void { paint(c: AbstractCanvas2D, update = false): void {
// Scale is passed-through to canvas // Scale is passed-through to canvas
const s = this.scale; const s = this.scale;
const x = this.bounds.x / s; const x = this.bounds.x / s;
@ -292,14 +297,14 @@ class TextShape extends Shape {
? (<string>val).replace(/\n/g, '<br/>') ? (<string>val).replace(/\n/g, '<br/>')
: val; : val;
let dir: string | null = this.textDirection; let dir: TextDirectionValue = this.textDirection;
if (dir === TEXT_DIRECTION_AUTO && !realHtml) { if (dir === TEXT_DIRECTION_AUTO && !realHtml) {
dir = this.getAutoDirection(); dir = this.getAutoDirection();
} }
if (dir !== TEXT_DIRECTION_LTR && dir !== TEXT_DIRECTION_RTL) { if (dir !== TEXT_DIRECTION_LTR && dir !== TEXT_DIRECTION_RTL) {
dir = null; dir = TEXT_DIRECTION_DEFAULT;
} }
c.text( c.text(
@ -333,19 +338,20 @@ class TextShape extends Shape {
this.lastValue === this.value && this.lastValue === this.value &&
(isNode(this.value) || this.dialect === DIALECT_STRICTHTML) (isNode(this.value) || this.dialect === DIALECT_STRICTHTML)
) { ) {
// @ts-ignore
if (this.node.nodeName === 'DIV') { if (this.node.nodeName === 'DIV') {
this.redrawHtmlShape(); this.redrawHtmlShape();
this.updateBoundingBox(); this.updateBoundingBox();
} else { } else {
const canvas = this.createCanvas(); const canvas = this.createCanvas();
// Specifies if events should be handled if (canvas) {
canvas.pointerEvents = this.pointerEvents; // Specifies if events should be handled
canvas.pointerEvents = this.pointerEvents;
this.paint(canvas, true); this.paint(canvas, true);
this.destroyCanvas(canvas); this.destroyCanvas(canvas);
this.updateBoundingBox(); this.updateBoundingBox();
}
} }
} else { } else {
super.redraw(); super.redraw();
@ -378,8 +384,8 @@ class TextShape extends Shape {
this.spacingBottom = 2; this.spacingBottom = 2;
this.spacingLeft = 2; this.spacingLeft = 2;
this.horizontal = true; this.horizontal = true;
this.background = null; this.background = NONE;
this.border = null; this.border = NONE;
this.textDirection = DEFAULT_TEXT_DIRECTION; this.textDirection = DEFAULT_TEXT_DIRECTION;
this.margin = null; this.margin = null;
} }
@ -397,31 +403,24 @@ class TextShape extends Shape {
const old = this.spacing; const old = this.spacing;
super.apply(state); super.apply(state);
if (this.style != null) { if (this.style) {
this.fontStyle = this.style.fontStyle || this.fontStyle; this.fontStyle = this.style.fontStyle;
this.family = getValue(this.style, 'fontFamily', this.family); this.family = this.style.fontFamily;
this.size = getValue(this.style, 'fontSize', this.size); this.size = this.style.fontSize;
this.color = getValue(this.style, 'fontColor', this.color); this.color = this.style.fontColor;
this.align = getValue(this.style, 'align', this.align); this.align = this.style.align;
this.valign = getValue(this.style, 'verticalAlign', this.valign); this.valign = this.style.verticalAlign;
this.spacing = parseInt(getValue(this.style, 'spacing', this.spacing)); this.spacing = this.style.spacing;
this.spacingTop = this.spacingTop = this.style.spacingTop;
parseInt(getValue(this.style, 'spacingTop', this.spacingTop - old)) + this.spacingRight = this.style.spacingRight;
this.spacing; this.spacingBottom = this.style.spacingBottom;
this.spacingRight = this.spacingLeft = this.style.spacingLeft;
parseInt(getValue(this.style, 'spacingRight', this.spacingRight - old)) + this.horizontal = this.style.horizontal;
this.spacing; this.background = this.style.backgroundColor;
this.spacingBottom = this.border = this.style.labelBorderColor;
parseInt(getValue(this.style, 'spacingBottom', this.spacingBottom - old)) + this.textDirection = this.style.textDirection;
this.spacing; this.opacity = this.style.textOpacity;
this.spacingLeft =
parseInt(getValue(this.style, 'spacingLeft', this.spacingLeft - old)) +
this.spacing;
this.horizontal = getValue(this.style, 'horizontal', this.horizontal);
this.background = getValue(this.style, 'backgroundColor', this.background);
this.border = getValue(this.style, 'labelBorderColor', this.border);
this.textDirection = getValue(this.style, 'textDirection', DEFAULT_TEXT_DIRECTION);
this.opacity = getValue(this.style, 'textOpacity', 100);
this.updateMargin(); this.updateMargin();
} }
@ -437,14 +436,14 @@ class TextShape extends Shape {
* depending on the contents of <value>. This is not invoked for HTML, wrapped * depending on the contents of <value>. This is not invoked for HTML, wrapped
* content or if <value> is a DOM node. * content or if <value> is a DOM node.
*/ */
getAutoDirection(): string { getAutoDirection() {
// Looks for strong (directional) characters // Looks for strong (directional) characters
const tmp = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec( const tmp = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec(
<string>this.value String(this.value)
); );
// Returns the direction defined by the character // Returns the direction defined by the character
return tmp != null && tmp.length > 0 && tmp[0] > 'z' return tmp && tmp.length > 0 && tmp[0] > 'z'
? TEXT_DIRECTION_RTL ? TEXT_DIRECTION_RTL
: TEXT_DIRECTION_LTR; : TEXT_DIRECTION_LTR;
} }
@ -457,9 +456,9 @@ class TextShape extends Shape {
getContentNode() { getContentNode() {
let result = this.node; let result = this.node;
if (result != null) { if (result) {
// Rendered with no foreignObject // Rendered with no foreignObject
if (result.ownerSVGElement == null) { if (!result.ownerSVGElement) {
// @ts-ignore // @ts-ignore
result = this.node.firstChild.firstChild; result = this.node.firstChild.firstChild;
} else { } else {
@ -468,6 +467,7 @@ class TextShape extends Shape {
result = result.firstChild.firstChild.firstChild.firstChild.firstChild; result = result.firstChild.firstChild.firstChild.firstChild.firstChild;
} }
} }
return result; return result;
} }
@ -476,21 +476,17 @@ class TextShape extends Shape {
* *
* Updates the <boundingBox> for this shape using the given node and position. * Updates the <boundingBox> for this shape using the given node and position.
*/ */
updateBoundingBox(): void { updateBoundingBox() {
let { node } = this; let { node } = this;
this.boundingBox = this.bounds.clone(); this.boundingBox = this.bounds.clone();
const rot = this.getTextRotation(); const rot = this.getTextRotation();
const h = const h = this.style?.labelPosition ?? ALIGN_CENTER;
this.style != null ? getValue(this.style, 'labelPosition', ALIGN_CENTER) : null; const v = this.style?.verticalLabelPosition ?? ALIGN_MIDDLE;
const v =
this.style != null
? getValue(this.style, 'verticalLabelPosition', ALIGN_MIDDLE)
: null;
if ( if (
!this.ignoreStringSize && !this.ignoreStringSize &&
node != null && node &&
this.overflow !== 'fill' && this.overflow !== 'fill' &&
(!this.clipped || (!this.clipped ||
!this.ignoreClippedStringSize || !this.ignoreClippedStringSize ||
@ -501,8 +497,8 @@ class TextShape extends Shape {
let oh = null; let oh = null;
if ( if (
node.firstChild != null && node.firstChild &&
node.firstChild.firstChild != null && node.firstChild.firstChild &&
node.firstChild.firstChild.nodeName === 'foreignObject' node.firstChild.firstChild.nodeName === 'foreignObject'
) { ) {
// Uses second inner DIV for font metrics // Uses second inner DIV for font metrics
@ -512,7 +508,6 @@ class TextShape extends Shape {
oh = node.offsetHeight * this.scale; oh = node.offsetHeight * this.scale;
if (this.overflow === 'width') { if (this.overflow === 'width') {
// @ts-ignore
ow = this.boundingBox.width; ow = this.boundingBox.width;
} else { } else {
// @ts-ignore // @ts-ignore
@ -523,7 +518,7 @@ class TextShape extends Shape {
const b = node.getBBox(); const b = node.getBBox();
// Workaround for bounding box of empty string // Workaround for bounding box of empty string
if (typeof this.value === 'string' && trim(this.value)?.length == 0) { if (typeof this.value === 'string' && trim(this.value)?.length === 0) {
this.boundingBox = null; this.boundingBox = null;
} else if (b.width === 0 && b.height === 0) { } else if (b.width === 0 && b.height === 0) {
this.boundingBox = null; this.boundingBox = null;
@ -537,12 +532,12 @@ class TextShape extends Shape {
} }
} }
if (ow != null && oh != null) { if (ow && oh) {
this.boundingBox = new Rectangle(this.bounds.x, this.bounds.y, ow, oh); this.boundingBox = new Rectangle(this.bounds.x, this.bounds.y, ow, oh);
} }
} }
if (this.boundingBox != null) { if (this.boundingBox) {
const margin = <Rectangle>this.margin; const margin = <Rectangle>this.margin;
if (rot !== 0) { if (rot !== 0) {
@ -581,7 +576,7 @@ class TextShape extends Shape {
* *
* Returns 0 to avoid using rotation in the canvas via updateTransform. * Returns 0 to avoid using rotation in the canvas via updateTransform.
*/ */
getShapeRotation(): number { getShapeRotation() {
return 0; return 0;
} }
@ -590,10 +585,8 @@ class TextShape extends Shape {
* *
* Returns the rotation for the text label of the corresponding shape. * Returns the rotation for the text label of the corresponding shape.
*/ */
getTextRotation(): number { getTextRotation() {
return this.state != null && this.state.shape != null return this.state && this.state.shape ? this.state.shape.getTextRotation() : 0;
? this.state.shape.getTextRotation()
: 0;
} }
/** /**
@ -602,13 +595,8 @@ class TextShape extends Shape {
* Inverts the bounds if <mxShape.isBoundsInverted> returns true or if the * Inverts the bounds if <mxShape.isBoundsInverted> returns true or if the
* horizontal style is false. * horizontal style is false.
*/ */
isPaintBoundsInverted(): boolean { isPaintBoundsInverted() {
return ( return !this.horizontal && !!this.state && this.state.cell.isVertex();
!this.horizontal &&
this.state != null &&
// @ts-ignore
this.state.cell.isVertex()
);
} }
/** /**
@ -616,7 +604,7 @@ class TextShape extends Shape {
* *
* Sets the state of the canvas for drawing the shape. * Sets the state of the canvas for drawing the shape.
*/ */
configureCanvas(c: mxSvgCanvas2D, x: number, y: number, w: number, h: number): void { configureCanvas(c: AbstractCanvas2D, x: number, y: number, w: number, h: number): void {
super.configureCanvas(c, x, y, w, h); super.configureCanvas(c, x, y, w, h);
c.setFontColor(this.color); c.setFontColor(this.color);
@ -691,7 +679,7 @@ class TextShape extends Shape {
* *
* Updates the HTML node(s) to reflect the latest bounds and scale. * Updates the HTML node(s) to reflect the latest bounds and scale.
*/ */
redrawHtmlShape(): void { redrawHtmlShape() {
const w = Math.max(0, Math.round(this.bounds.width / this.scale)); const w = Math.max(0, Math.round(this.bounds.width / this.scale));
const h = Math.max(0, Math.round(this.bounds.height / this.scale)); const h = Math.max(0, Math.round(this.bounds.height / this.scale));
const flex = const flex =
@ -699,9 +687,9 @@ class TextShape extends Shape {
`top: ${Math.round(this.bounds.y)}px; pointer-events: none; `; `top: ${Math.round(this.bounds.y)}px; pointer-events: none; `;
const block = this.getTextCss(); const block = this.getTextCss();
const margin = <Point>this.margin; const margin = <Point>this.margin;
const node = <SVGGElement>this.node; const node = this.node;
mxSvgCanvas2D.createCss( SvgCanvas2D.createCss(
w + 2, w + 2,
h, h,
this.align, this.align,
@ -709,8 +697,8 @@ class TextShape extends Shape {
this.wrap, this.wrap,
this.overflow, this.overflow,
this.clipped, this.clipped,
this.background != null ? htmlEntities(this.background, true) : null, this.background !== NONE ? htmlEntities(this.background, true) : null,
this.border != null ? htmlEntities(this.border, true) : null, this.border !== NONE ? htmlEntities(this.border, true) : null,
flex, flex,
block, block,
this.scale, this.scale,
@ -745,7 +733,7 @@ class TextShape extends Shape {
} }
} }
if (<number>this.opacity < 100) { if (this.opacity < 100) {
block += `opacity: ${<number>this.opacity / 100}; `; block += `opacity: ${<number>this.opacity / 100}; `;
} }
@ -756,7 +744,7 @@ class TextShape extends Shape {
this.value.outerHTML this.value.outerHTML
: this.getHtmlValue(); : this.getHtmlValue();
if (node.firstChild == null) { if (!node.firstChild) {
node.innerHTML = `<div><div>${html}</div></div>`; node.innerHTML = `<div><div>${html}</div></div>`;
} }
@ -773,7 +761,7 @@ class TextShape extends Shape {
* *
* Sets the inner HTML of the given element to the <value>. * Sets the inner HTML of the given element to the <value>.
*/ */
updateInnerHtml(elt: HTMLElement): void { updateInnerHtml(elt: HTMLElement) {
if (isNode(this.value)) { if (isNode(this.value)) {
// @ts-ignore // @ts-ignore
elt.innerHTML = this.value.outerHTML; elt.innerHTML = this.value.outerHTML;
@ -782,7 +770,7 @@ class TextShape extends Shape {
if (this.dialect !== DIALECT_STRICTHTML) { if (this.dialect !== DIALECT_STRICTHTML) {
// LATER: Can be cached in updateValue // LATER: Can be cached in updateValue
val = htmlEntities(<string>val, false); val = htmlEntities(val, false);
} }
// Handles trailing newlines to make sure they are visible in rendering output // Handles trailing newlines to make sure they are visible in rendering output
@ -799,8 +787,8 @@ class TextShape extends Shape {
* *
* Updates the HTML node(s) to reflect the latest bounds and scale. * Updates the HTML node(s) to reflect the latest bounds and scale.
*/ */
updateValue(): void { updateValue() {
const node = <SVGGElement>this.node; const node = this.node;
if (isNode(this.value)) { if (isNode(this.value)) {
node.innerHTML = ''; node.innerHTML = '';
@ -809,32 +797,31 @@ class TextShape extends Shape {
let val = this.value as string; let val = this.value as string;
if (this.dialect !== DIALECT_STRICTHTML) { if (this.dialect !== DIALECT_STRICTHTML) {
val = htmlEntities(<string>val, false); val = htmlEntities(val, false);
} }
// Handles trailing newlines to make sure they are visible in rendering output // Handles trailing newlines to make sure they are visible in rendering output
val = replaceTrailingNewlines(val, '<div><br></div>'); val = replaceTrailingNewlines(val, '<div><br></div>');
val = this.replaceLinefeeds ? val.replace(/\n/g, '<br/>') : val; val = this.replaceLinefeeds ? val.replace(/\n/g, '<br/>') : val;
const bg = const bg = this.background !== NONE ? this.background : null;
this.background != null && this.background !== NONE ? this.background : null; const bd = this.border !== NONE ? this.border : null;
const bd = this.border != null && this.border !== NONE ? this.border : null;
if (this.overflow === 'fill' || this.overflow === 'width') { if (this.overflow === 'fill' || this.overflow === 'width') {
if (bg != null) { if (bg) {
node.style.backgroundColor = bg; node.style.backgroundColor = bg;
} }
if (bd != null) { if (bd) {
node.style.border = `1px solid ${bd}`; node.style.border = `1px solid ${bd}`;
} }
} else { } else {
let css = ''; let css = '';
if (bg != null) { if (bg) {
css += `background-color:${htmlEntities(bg, true)};`; css += `background-color:${htmlEntities(bg, true)};`;
} }
if (bd != null) { if (bd) {
css += `border:1px solid ${htmlEntities(bd, true)};`; css += `border:1px solid ${htmlEntities(bd, true)};`;
} }
@ -873,7 +860,7 @@ class TextShape extends Shape {
* *
* Updates the HTML node(s) to reflect the latest bounds and scale. * Updates the HTML node(s) to reflect the latest bounds and scale.
*/ */
updateFont(node: HTMLElement | SVGGElement): void { updateFont(node: HTMLElement | SVGGElement) {
const { style } = node; const { style } = node;
// @ts-ignore // @ts-ignore
@ -923,7 +910,7 @@ class TextShape extends Shape {
* *
* Updates the HTML node(s) to reflect the latest bounds and scale. * Updates the HTML node(s) to reflect the latest bounds and scale.
*/ */
updateSize(node: HTMLElement, enableWrap: boolean = false): void { updateSize(node: HTMLElement, enableWrap = false) {
const w = Math.max(0, Math.round(this.bounds.width / this.scale)); const w = Math.max(0, Math.round(this.bounds.width / this.scale));
const h = Math.max(0, Math.round(this.bounds.height / this.scale)); const h = Math.max(0, Math.round(this.bounds.height / this.scale));
const { style } = node; const { style } = node;
@ -992,7 +979,7 @@ class TextShape extends Shape {
* *
* Returns the spacing as an <mxPoint>. * Returns the spacing as an <mxPoint>.
*/ */
updateMargin(): void { updateMargin() {
this.margin = getAlignmentAsPoint(this.align, this.valign); this.margin = getAlignmentAsPoint(this.align, this.valign);
} }
@ -1001,7 +988,7 @@ class TextShape extends Shape {
* *
* Returns the spacing as an <mxPoint>. * Returns the spacing as an <mxPoint>.
*/ */
getSpacing(): Point { getSpacing() {
let dx = 0; let dx = 0;
let dy = 0; let dy = 0;

View File

@ -7,9 +7,8 @@
import Point from '../../Point'; import Point from '../../Point';
import Actor from '../Actor'; import Actor from '../Actor';
import utils, { getValue } from '../../../../util/Utils';
import { LINE_ARCSIZE } from '../../../../util/Constants'; import { LINE_ARCSIZE } from '../../../../util/Constants';
import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D';
/** /**
* Implementation of the triangle shape. * Implementation of the triangle shape.
@ -25,7 +24,7 @@ class TriangleShape extends Actor {
* Adds roundable support. * Adds roundable support.
* @returns {boolean} * @returns {boolean}
*/ */
isRoundable(): boolean { isRoundable() {
return true; return true;
} }
@ -37,15 +36,8 @@ class TriangleShape extends Actor {
* @param {number} w * @param {number} w
* @param {number} h * @param {number} h
*/ */
redrawPath( redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {
c: mxSvgCanvas2D, const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2;
x: number,
y: number,
w: number,
h: number
): void {
const arcSize: number =
getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2;
this.addPoints( this.addPoints(
c, c,

View File

@ -1,6 +1,5 @@
import Graph from "../Graph"; import Graph from '../Graph';
import ImageBundle from "../../util/image/ImageBundle"; import ImageBundle from './ImageBundle';
import ImageBundle from "./ImageBundle";
class GraphImage { class GraphImage {
constructor(graph: Graph) { constructor(graph: Graph) {
@ -19,14 +18,14 @@ class GraphImage {
/** /**
* Adds the specified {@link ImageBundle}. * Adds the specified {@link ImageBundle}.
*/ */
addImageBundle(bundle: ImageBundle): void { addImageBundle(bundle: ImageBundle) {
this.imageBundles.push(bundle); this.imageBundles.push(bundle);
} }
/** /**
* Removes the specified {@link ImageBundle}. * Removes the specified {@link ImageBundle}.
*/ */
removeImageBundle(bundle: ImageBundle): void { removeImageBundle(bundle: ImageBundle) {
const tmp = []; const tmp = [];
for (let i = 0; i < this.imageBundles.length; i += 1) { for (let i = 0; i < this.imageBundles.length; i += 1) {
if (this.imageBundles[i] !== bundle) { if (this.imageBundles[i] !== bundle) {
@ -40,11 +39,11 @@ class GraphImage {
* Searches all {@link imageBundles} for the specified key and returns the value * Searches all {@link imageBundles} for the specified key and returns the value
* for the first match or null if the key is not found. * for the first match or null if the key is not found.
*/ */
getImageFromBundles(key: string): string { getImageFromBundles(key: string) {
if (key != null) { if (key) {
for (let i = 0; i < this.imageBundles.length; i += 1) { for (let i = 0; i < this.imageBundles.length; i += 1) {
const image = this.imageBundles[i].getImage(key); const image = this.imageBundles[i].getImage(key);
if (image != null) { if (image) {
return image; return image;
} }
} }

View File

@ -14,10 +14,8 @@
* *
* Constructs a new image. * Constructs a new image.
*/ */
class Image { class ImageBox {
constructor(src: string, constructor(src: string, width: number, height: number) {
width: number,
height: number) {
this.src = src; this.src = src;
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -28,21 +26,21 @@ class Image {
* *
* String that specifies the URL of the image. * String that specifies the URL of the image.
*/ */
src: string | null = null; src: string;
/** /**
* Variable: width * Variable: width
* *
* Integer that specifies the width of the image. * Integer that specifies the width of the image.
*/ */
width: number | null = null; width: number;
/** /**
* Variable: height * Variable: height
* *
* Integer that specifies the height of the image. * Integer that specifies the height of the image.
*/ */
height: number | null = null; height: number;
} }
export default Image; export default ImageBox;

View File

@ -5,6 +5,13 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
type ImageMap = {
[key: string]: {
value: string;
fallback: Function;
};
};
/** /**
* Class: mxImageBundle * Class: mxImageBundle
* *
@ -51,9 +58,9 @@
* all data URIs should be limited to 32 KB. * all data URIs should be limited to 32 KB.
*/ */
class ImageBundle { class ImageBundle {
constructor(alt) { constructor(alt = false) {
this.images = []; this.images = {};
this.alt = alt != null ? alt : false; this.alt = alt;
} }
/** /**
@ -61,14 +68,14 @@ class ImageBundle {
* *
* Maps from keys to images. * Maps from keys to images.
*/ */
images: { [key: string]: { value: string; fallback: Function } } | null = null; images: ImageMap;
/** /**
* Variable: alt * Variable: alt
* *
* Specifies if the fallback representation should be returned. * Specifies if the fallback representation should be returned.
*/ */
alt: boolean = null; alt: boolean;
/** /**
* Function: putImage * Function: putImage
@ -87,13 +94,13 @@ class ImageBundle {
* or fallback, depending on <alt>. The fallback is returned if * or fallback, depending on <alt>. The fallback is returned if
* <alt> is true, the value is returned otherwise. * <alt> is true, the value is returned otherwise.
*/ */
getImage(key: string): string { getImage(key: string) {
let result = null; let result = null;
if (key != null) { if (key) {
const img = this.images[key]; const img = this.images[key];
if (img != null) { if (img) {
result = this.alt ? img.fallback : img.value; result = this.alt ? img.fallback : img.value;
} }
} }

View File

@ -5,7 +5,7 @@
* Type definitions from the typed-mxgraph project * Type definitions from the typed-mxgraph project
*/ */
import mxAbstractCanvas2D from '../../util/canvas/mxAbstractCanvas2D'; import AbstractCanvas2D from '../../util/canvas/AbstractCanvas2D';
import CellState from '../cell/datatypes/CellState'; import CellState from '../cell/datatypes/CellState';
import Shape from '../geometry/shape/Shape'; import Shape from '../geometry/shape/Shape';
@ -41,13 +41,13 @@ class ImageExport {
/** /**
* Specifies if overlays should be included in the export. Default is false. * Specifies if overlays should be included in the export. Default is false.
*/ */
includeOverlays: boolean = false; includeOverlays = false;
/** /**
* Draws the given state and all its descendants to the given canvas. * Draws the given state and all its descendants to the given canvas.
*/ */
drawState(state: CellState, canvas: mxAbstractCanvas2D): void { drawState(state: CellState, canvas: AbstractCanvas2D): void {
if (state != null) { if (state) {
this.visitStatesRecursive(state, canvas, () => { this.visitStatesRecursive(state, canvas, () => {
this.drawCellState(state, canvas); this.drawCellState(state, canvas);
}); });
@ -66,8 +66,8 @@ class ImageExport {
* *
* Visits the given state and all its descendants to the given canvas recursively. * Visits the given state and all its descendants to the given canvas recursively.
*/ */
visitStatesRecursive(state: CellState, canvas: mxAbstractCanvas2D, visitor: Function) { visitStatesRecursive(state: CellState, canvas: AbstractCanvas2D, visitor: Function) {
if (state != null) { if (state) {
visitor(state, canvas); visitor(state, canvas);
const { graph } = state.view; const { graph } = state.view;
@ -83,18 +83,18 @@ class ImageExport {
/** /**
* Returns the link for the given cell state and canvas. This returns null. * Returns the link for the given cell state and canvas. This returns null.
*/ */
getLinkForCellState(state: CellState, canvas: mxAbstractCanvas2D): any { getLinkForCellState(state: CellState, canvas: AbstractCanvas2D): any {
return null; return null;
} }
/** /**
* Draws the given state to the given canvas. * Draws the given state to the given canvas.
*/ */
drawCellState(state: CellState, canvas: mxAbstractCanvas2D): void { drawCellState(state: CellState, canvas: AbstractCanvas2D): void {
// Experimental feature // Experimental feature
const link = this.getLinkForCellState(state, canvas); const link = this.getLinkForCellState(state, canvas);
if (link != null) { if (link) {
canvas.setLink(link); canvas.setLink(link);
} }
@ -102,7 +102,7 @@ class ImageExport {
this.drawShape(state, canvas); this.drawShape(state, canvas);
this.drawText(state, canvas); this.drawText(state, canvas);
if (link != null) { if (link) {
canvas.setLink(null); canvas.setLink(null);
} }
} }
@ -112,7 +112,7 @@ class ImageExport {
* *
* Draws the shape of the given state. * Draws the shape of the given state.
*/ */
drawShape(state: CellState, canvas: mxAbstractCanvas2D): void { drawShape(state: CellState, canvas: AbstractCanvas2D): void {
if (state.shape instanceof Shape && state.shape.checkBounds()) { if (state.shape instanceof Shape && state.shape.checkBounds()) {
canvas.save(); canvas.save();
@ -127,8 +127,8 @@ class ImageExport {
/** /**
* Draws the text of the given state. * Draws the text of the given state.
*/ */
drawText(state: CellState, canvas: mxAbstractCanvas2D): void { drawText(state: CellState, canvas: AbstractCanvas2D): void {
if (state.text != null && state.text.checkBounds()) { if (state.text && state.text.checkBounds()) {
canvas.save(); canvas.save();
state.text.beforePaint(canvas); state.text.beforePaint(canvas);
@ -145,7 +145,7 @@ class ImageExport {
* Draws the overlays for the given state. This is called if <includeOverlays> * Draws the overlays for the given state. This is called if <includeOverlays>
* is true. * is true.
*/ */
drawOverlays(state: CellState, canvas: mxAbstractCanvas2D): void { drawOverlays(state: CellState, canvas: AbstractCanvas2D): void {
if (state.overlays != null) { if (state.overlays != null) {
state.overlays.visit((id, shape) => { state.overlays.visit((id, shape) => {
if (shape instanceof Shape) { if (shape instanceof Shape) {

View File

@ -1,10 +1,10 @@
import Cell from "../cell/datatypes/Cell"; import Cell from '../cell/datatypes/Cell';
import CellOverlay from "../cell/CellOverlay"; import CellOverlay from '../cell/CellOverlay';
import EventObject from "../event/EventObject"; import EventObject from '../event/EventObject';
import InternalEvent from "../event/InternalEvent"; import InternalEvent from '../event/InternalEvent';
import Image from "../image/Image"; import Image from '../image/ImageBox';
import InternalMouseEvent from "../event/InternalMouseEvent"; import InternalMouseEvent from '../event/InternalMouseEvent';
import Graph from "../Graph"; import Graph from '../Graph';
class Overlays { class Overlays {
constructor(graph: Graph) { constructor(graph: Graph) {
@ -82,13 +82,7 @@ class Overlays {
} }
this.fireEvent( this.fireEvent(
new EventObject( new EventObject(InternalEvent.REMOVE_OVERLAY, 'cell', cell, 'overlay', overlay)
InternalEvent.REMOVE_OVERLAY,
'cell',
cell,
'overlay',
overlay
)
); );
} else { } else {
overlay = null; overlay = null;
@ -184,18 +178,18 @@ class Overlays {
img = img != null ? img : this.warningImage; img = img != null ? img : this.warningImage;
// Creates the overlay with the image and warning // Creates the overlay with the image and warning
const overlay = new CellOverlay( const overlay = new CellOverlay(img, `<font color=red>${warning}</font>`);
img,
`<font color=red>${warning}</font>`
);
// Adds a handler for single mouseclicks to select the cell // Adds a handler for single mouseclicks to select the cell
if (isSelect) { if (isSelect) {
overlay.addListener(InternalEvent.CLICK, (sender: any, evt: InternalMouseEvent) => { overlay.addListener(
if (this.isEnabled()) { InternalEvent.CLICK,
this.setSelectionCell(cell); (sender: any, evt: InternalMouseEvent) => {
if (this.isEnabled()) {
this.setSelectionCell(cell);
}
} }
}); );
} }
// Sets and returns the overlay in the graph // Sets and returns the overlay in the graph

View File

@ -72,7 +72,7 @@ class CellHighlight {
} }
// TODO: Document me!! // TODO: Document me!!
highlightColor: ColorValue = null; highlightColor: ColorValue | null = null;
strokeWidth: number = 0; strokeWidth: number = 0;
@ -119,7 +119,7 @@ class CellHighlight {
* *
* @param {string} color - String that represents the new highlight color. * @param {string} color - String that represents the new highlight color.
*/ */
setHighlightColor(color: ColorValue) { setHighlightColor(color: ColorValue | null) {
this.highlightColor = color; this.highlightColor = color;
if (this.shape) { if (this.shape) {
@ -134,15 +134,10 @@ class CellHighlight {
this.shape = this.createShape(); this.shape = this.createShape();
this.repaint(); this.repaint();
const node = this.shape?.node; const node = this.shape.node;
if (
!this.keepOnTop && if (!this.keepOnTop && node?.parentNode?.firstChild !== node) {
this.shape.node?.parentNode?.firstChild !== this.shape.node node.parentNode.insertBefore(node, node.parentNode.firstChild);
) {
this.shape.node.parentNode.insertBefore(
this.shape.node,
this.shape.node.parentNode.firstChild
);
} }
} }
@ -150,20 +145,20 @@ class CellHighlight {
* Creates and returns the highlight shape for the given state. * Creates and returns the highlight shape for the given state.
*/ */
createShape() { createShape() {
const shape = <Shape>( if (!this.state) return;
this.graph.cellRenderer.createShape(<CellState>this.state)
);
shape.svgStrokeTolerance = (<graph>this.graph).tolerance; const shape = this.graph.cellRenderer.createShape(this.state);
shape.points = (<CellState>this.state).absolutePoints;
shape.apply(<CellState>this.state); shape.svgStrokeTolerance = this.graph.tolerance;
shape.points = this.state.absolutePoints;
shape.apply(this.state);
shape.stroke = this.highlightColor; shape.stroke = this.highlightColor;
shape.opacity = this.opacity; shape.opacity = this.opacity;
shape.isDashed = this.dashed; shape.isDashed = this.dashed;
shape.isShadow = false; shape.isShadow = false;
shape.dialect = DIALECT_SVG; shape.dialect = DIALECT_SVG;
shape.init((<graph>this.graph).getView().getOverlayPane()); shape.init(this.graph.getView().getOverlayPane());
InternalEvent.redirectMouseEvents(shape.node, this.graph, this.state); InternalEvent.redirectMouseEvents(shape.node, this.graph, this.state);
if ((<graph>this.graph).dialect !== DIALECT_SVG) { if ((<graph>this.graph).dialect !== DIALECT_SVG) {
@ -191,7 +186,7 @@ class CellHighlight {
// @ts-ignore // @ts-ignore
if (this.graph.model.isEdge(this.state.cell)) { if (this.graph.model.isEdge(this.state.cell)) {
this.shape.strokewidth = this.getStrokeWidth(); this.shape.strokeWidth = this.getStrokeWidth();
this.shape.points = this.state.absolutePoints; this.shape.points = this.state.absolutePoints;
this.shape.outline = false; this.shape.outline = false;
} else { } else {
@ -202,8 +197,7 @@ class CellHighlight {
this.state.height + 2 * this.spacing this.state.height + 2 * this.spacing
); );
this.shape.rotation = Number(this.state.style.rotation || '0'); this.shape.rotation = Number(this.state.style.rotation || '0');
this.shape.strokewidth = this.shape.strokeWidth = <number>this.getStrokeWidth() / this.state.view.scale;
<number>this.getStrokeWidth() / this.state.view.scale;
this.shape.outline = true; this.shape.outline = true;
} }

View File

@ -1,17 +1,17 @@
import Cell from "../cell/datatypes/Cell"; import Cell from '../cell/datatypes/Cell';
import CellArray from "../cell/datatypes/CellArray"; import CellArray from '../cell/datatypes/CellArray';
import Rectangle from "../geometry/Rectangle"; import Rectangle from '../geometry/Rectangle';
import InternalMouseEvent from "../event/InternalMouseEvent"; import InternalMouseEvent from '../event/InternalMouseEvent';
import graph from "../Graph"; import graph from '../Graph';
import mxClient from "../../mxClient"; import mxClient from '../../mxClient';
import SelectionChange from "./SelectionChange"; import SelectionChange from './SelectionChange';
import UndoableEdit from "../model/UndoableEdit"; import UndoableEdit from '../model/UndoableEdit';
import EventObject from "../event/EventObject"; import EventObject from '../event/EventObject';
import InternalEvent from "../event/InternalEvent"; import InternalEvent from '../event/InternalEvent';
import EventSource from "../event/EventSource"; import EventSource from '../event/EventSource';
import Dictionary from "../../util/Dictionary"; import Dictionary from '../../util/Dictionary';
import RootChange from "../model/RootChange"; import RootChange from '../model/RootChange';
import ChildChange from "../model/ChildChange"; import ChildChange from '../model/ChildChange';
class GraphSelection extends EventSource { class GraphSelection extends EventSource {
constructor(graph: graph) { constructor(graph: graph) {
@ -50,25 +50,27 @@ class GraphSelection extends EventSource {
*/ */
singleSelection: boolean = false; singleSelection: boolean = false;
// TODO: Document me!!
selectionModel: GraphSelection | null = null;
/** /**
* Returns the {@link mxGraphSelectionModel} that contains the selection. * Returns the {@link mxGraphSelectionModel} that contains the selection.
*/ */
getSelectionModel(): mxGraphSelectionModel { getSelectionModel() {
return <mxGraphSelectionModel>this.selectionModel; return this.selectionModel;
} }
/** /**
* Sets the {@link mxSelectionModel} that contains the selection. * Sets the {@link mxSelectionModel} that contains the selection.
*/ */
setSelectionModel(selectionModel: mxGraphSelectionModel): void { setSelectionModel(selectionModel: GraphSelection) {
this.selectionModel = selectionModel; this.selectionModel = selectionModel;
} }
/** /**
* Returns {@link singleSelection} as a boolean. * Returns {@link singleSelection} as a boolean.
*/ */
isSingleSelection(): boolean { isSingleSelection() {
return this.singleSelection; return this.singleSelection;
} }
@ -78,7 +80,7 @@ class GraphSelection extends EventSource {
* @param {boolean} singleSelection Boolean that specifies the new value for * @param {boolean} singleSelection Boolean that specifies the new value for
* {@link singleSelection}. * {@link singleSelection}.
*/ */
setSingleSelection(singleSelection: boolean): void { setSingleSelection(singleSelection: boolean) {
this.singleSelection = singleSelection; this.singleSelection = singleSelection;
} }
@ -228,13 +230,19 @@ class GraphSelection extends EventSource {
* @param added Array of {@link Cell} to add to the selection. * @param added Array of {@link Cell} to add to the selection.
* @param remove Array of {@link Cell} to remove from the selection. * @param remove Array of {@link Cell} to remove from the selection.
*/ */
changeSelection(added: CellArray | null=null, changeSelection(
removed: CellArray | null=null): void { added: CellArray | null = null,
removed: CellArray | null = null
): void {
if ( if (
(added != null && added.length > 0 && added[0] != null) || (added != null && added.length > 0 && added[0] != null) ||
(removed != null && removed.length > 0 && removed[0] != null) (removed != null && removed.length > 0 && removed[0] != null)
) { ) {
const change = new SelectionChange(this, added || new CellArray(), removed || new CellArray()); const change = new SelectionChange(
this,
added || new CellArray(),
removed || new CellArray()
);
change.execute(); change.execute();
const edit = new UndoableEdit(this, false); const edit = new UndoableEdit(this, false);
edit.add(change); edit.add(change);
@ -491,8 +499,8 @@ class GraphSelection extends EventSource {
): void { ): void {
const cells = descendants const cells = descendants
? parent.filterDescendants((cell: Cell) => { ? parent.filterDescendants((cell: Cell) => {
return cell != parent && this.graph.getView().getState(cell) != null; return cell != parent && this.graph.getView().getState(cell) != null;
}) })
: parent.getChildren(); : parent.getChildren();
if (cells != null) { if (cells != null) {
@ -589,7 +597,6 @@ class GraphSelection extends EventSource {
} }
} }
/** /**
* Returns true if any sibling of the given cell is selected. * Returns true if any sibling of the given cell is selected.
*/ */
@ -646,10 +653,7 @@ class GraphSelection extends EventSource {
for (let i = 0; i < changes.length; i += 1) { for (let i = 0; i < changes.length; i += 1) {
const change = changes[i]; const change = changes[i];
if ( if (change.constructor !== RootChange && (ignoreFn == null || !ignoreFn(change))) {
change.constructor !== RootChange &&
(ignoreFn == null || !ignoreFn(change))
) {
let cell = null; let cell = null;
if (change instanceof ChildChange) { if (change instanceof ChildChange) {
@ -666,7 +670,6 @@ class GraphSelection extends EventSource {
return cells; return cells;
} }
/** /**
* Removes selection cells that are not in the model from the selection. * Removes selection cells that are not in the model from the selection.
*/ */
@ -695,5 +698,3 @@ class GraphSelection extends EventSource {
} }
export default GraphSelection; export default GraphSelection;

View File

@ -40,21 +40,16 @@ import InternalMouseEvent from '../event/InternalMouseEvent';
import StyleRegistry from '../style/StyleRegistry'; import StyleRegistry from '../style/StyleRegistry';
import graph from '../Graph'; import graph from '../Graph';
import Cell from '../cell/datatypes/Cell'; import Cell from '../cell/datatypes/Cell';
import Image from '../image/Image'; import Image from '../image/ImageBox';
import CurrentRootChange from './CurrentRootChange'; import CurrentRootChange from './CurrentRootChange';
import Model from '../model/Model'; import Model from '../model/Model';
import Shape from '../geometry/shape/Shape'; import Shape from '../geometry/shape/Shape';
import Geometry from '../geometry/Geometry'; import Geometry from '../geometry/Geometry';
import ConnectionConstraint from '../connection/ConnectionConstraint'; import ConnectionConstraint from '../connection/ConnectionConstraint';
import PopupMenuHandler from '../popups_menus/PopupMenuHandler'; import PopupMenuHandler from '../popups_menus/PopupMenuHandler';
import { import { getClientX, getClientY, getSource, isConsumed } from '../../util/EventUtils';
getClientX,
getClientY,
getSource,
isConsumed,
} from '../../util/EventUtils';
import { clone } from '../../util/CloneUtils'; import { clone } from '../../util/CloneUtils';
import CellArray from "../cell/datatypes/CellArray"; import CellArray from '../cell/datatypes/CellArray';
/** /**
* @class GraphView * @class GraphView
@ -135,8 +130,7 @@ class GraphView extends EventSource {
* being updated. If the resource for this key does not exist then the * being updated. If the resource for this key does not exist then the
* value is used as the status message. Default is 'updatingDocument'. * value is used as the status message. Default is 'updatingDocument'.
*/ */
updatingDocumentResource = updatingDocumentResource = mxClient.language !== 'none' ? 'updatingDocument' : '';
mxClient.language !== 'none' ? 'updatingDocument' : '';
/** /**
* Specifies if string values in cell styles should be evaluated using * Specifies if string values in cell styles should be evaluated using
@ -178,7 +172,7 @@ class GraphView extends EventSource {
*/ */
translate = new Point(); translate = new Point();
states = new Dictionary<CellState>(); states = new Dictionary<string, CellState>();
/** /**
* Specifies if the style should be updated in each validation step. If this * Specifies if the style should be updated in each validation step. If this
@ -243,13 +237,7 @@ class GraphView extends EventSource {
} }
} }
this.fireEvent( this.fireEvent(
new EventObject( new EventObject(InternalEvent.SCALE, 'scale', value, 'previousScale', previousScale)
InternalEvent.SCALE,
'scale',
value,
'previousScale',
previousScale
)
); );
} }
@ -289,13 +277,10 @@ class GraphView extends EventSource {
} }
this.fireEvent( this.fireEvent(
new EventObject( new EventObject(InternalEvent.TRANSLATE, {
InternalEvent.TRANSLATE, translate: this.translate,
{ previousTranslate: previousTranslate,
translate: this.translate, })
previousTranslate: previousTranslate,
}
)
); );
} }
@ -417,11 +402,7 @@ class GraphView extends EventSource {
const previousScale = this.scale; const previousScale = this.scale;
const previousTranslate = new Point(this.translate.x, this.translate.y); const previousTranslate = new Point(this.translate.x, this.translate.y);
if ( if (this.scale != scale || this.translate.x != dx || this.translate.y != dy) {
this.scale != scale ||
this.translate.x != dx ||
this.translate.y != dy
) {
this.scale = scale; this.scale = scale;
this.translate.x = dx; this.translate.x = dx;
@ -433,15 +414,12 @@ class GraphView extends EventSource {
} }
this.fireEvent( this.fireEvent(
new EventObject( new EventObject(InternalEvent.SCALE_AND_TRANSLATE, {
InternalEvent.SCALE_AND_TRANSLATE, scale: scale,
{ previousScale: previousScale,
scale: scale, translate: this.translate,
previousScale: previousScale, previousTranslate: previousTranslate,
translate: this.translate, })
previousTranslate: previousTranslate,
}
)
); );
} }
@ -559,8 +537,7 @@ class GraphView extends EventSource {
validate(cell: Cell | null = null): void { validate(cell: Cell | null = null): void {
const t0 = mxLog.enter('mxGraphView.validate'); const t0 = mxLog.enter('mxGraphView.validate');
window.status = window.status =
Resources.get(this.updatingDocumentResource) || Resources.get(this.updatingDocumentResource) || this.updatingDocumentResource;
this.updatingDocumentResource;
this.resetValidationState(); this.resetValidationState();
@ -579,9 +556,7 @@ class GraphView extends EventSource {
) )
); );
this.setGraphBounds( this.setGraphBounds(graphBounds != null ? graphBounds : this.getEmptyBounds());
graphBounds != null ? graphBounds : this.getEmptyBounds()
);
this.validateBackground(); this.validateBackground();
this.resetValidationState(); this.resetValidationState();
@ -595,10 +570,7 @@ class GraphView extends EventSource {
* {@link translate} with the size of 0 x 0. * {@link translate} with the size of 0 x 0.
*/ */
getEmptyBounds(): Rectangle { getEmptyBounds(): Rectangle {
return new Rectangle( return new Rectangle(this.translate.x * this.scale, this.translate.y * this.scale);
this.translate.x * this.scale,
this.translate.y * this.scale
);
} }
/** /**
@ -634,9 +606,7 @@ class GraphView extends EventSource {
const childCount = state.cell.getChildCount(); const childCount = state.cell.getChildCount();
for (let i = 0; i < childCount; i += 1) { for (let i = 0; i < childCount; i += 1) {
const bounds = this.getBoundingBox( const bounds = this.getBoundingBox(this.getState(state.cell.getChildAt(i)));
this.getState(state.cell.getChildAt(i))
);
if (bounds != null) { if (bounds != null) {
if (bbox == null) { if (bbox == null) {
@ -675,10 +645,7 @@ class GraphView extends EventSource {
const bg = (<graph>this.graph).getBackgroundImage(); const bg = (<graph>this.graph).getBackgroundImage();
if (bg != null) { if (bg != null) {
if ( if (this.backgroundImage == null || this.backgroundImage.imageSrc !== bg.src) {
this.backgroundImage == null ||
this.backgroundImage.image !== bg.src
) {
if (this.backgroundImage != null) { if (this.backgroundImage != null) {
this.backgroundImage.destroy(); this.backgroundImage.destroy();
} }
@ -734,15 +701,15 @@ class GraphView extends EventSource {
}, },
(evt: Event) => { (evt: Event) => {
// Hides the tooltip if mouse is outside container // Hides the tooltip if mouse is outside container
if ( if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
graph.tooltipHandler != null &&
graph.tooltipHandler.isHideOnHover()
) {
graph.tooltipHandler.hide(); graph.tooltipHandler.hide();
} }
if (graph.isMouseDown && !isConsumed(evt)) { if (graph.isMouseDown && !isConsumed(evt)) {
graph.fireMouseEvent(InternalEvent.MOUSE_MOVE, new InternalMouseEvent(evt)); graph.fireMouseEvent(
InternalEvent.MOUSE_MOVE,
new InternalMouseEvent(evt)
);
} }
}, },
(evt: Event) => { (evt: Event) => {
@ -859,9 +826,7 @@ class GraphView extends EventSource {
state.invalid = false; state.invalid = false;
if (state.style == null || state.invalidStyle) { if (state.style == null || state.invalidStyle) {
state.style = (<graph>this.graph).getCellStyle( state.style = (<graph>this.graph).getCellStyle(<Cell>state.cell);
<Cell>state.cell
);
state.invalidStyle = false; state.invalidStyle = false;
} }
@ -871,19 +836,13 @@ class GraphView extends EventSource {
state.setVisibleTerminalState( state.setVisibleTerminalState(
<CellState>( <CellState>(
this.validateCellState( this.validateCellState(<Cell>this.getVisibleTerminal(cell, true), false)
<Cell>this.getVisibleTerminal(cell, true),
false
)
), ),
true true
); );
state.setVisibleTerminalState( state.setVisibleTerminalState(
<CellState>( <CellState>(
this.validateCellState( this.validateCellState(<Cell>this.getVisibleTerminal(cell, false), false)
<Cell>this.getVisibleTerminal(cell, false),
false
)
), ),
false false
); );
@ -892,11 +851,7 @@ class GraphView extends EventSource {
// Repaint happens immediately after the cell is validated // Repaint happens immediately after the cell is validated
if (cell !== this.currentRoot && !state.invalid) { if (cell !== this.currentRoot && !state.invalid) {
(<graph>this.graph).cellRenderer.redraw( (<graph>this.graph).cellRenderer.redraw(state, false, this.isRendering());
state,
false,
this.isRendering()
);
// Handles changes to invertex paintbounds after update of rendering shape // Handles changes to invertex paintbounds after update of rendering shape
state.updateCachedBounds(); state.updateCachedBounds();
@ -943,9 +898,7 @@ class GraphView extends EventSource {
origin.y += (<Point>pState.origin).y; origin.y += (<Point>pState.origin).y;
} }
let offset = (<graph>this.graph).getChildOffsetForCell( let offset = (<graph>this.graph).getChildOffsetForCell(<Cell>state.cell);
<Cell>state.cell
);
if (offset != null) { if (offset != null) {
origin.x += offset.x; origin.x += offset.x;
@ -956,9 +909,7 @@ class GraphView extends EventSource {
if (geo != null) { if (geo != null) {
if (!state.cell.isEdge()) { if (!state.cell.isEdge()) {
offset = <Point>( offset = <Point>(geo.offset != null ? geo.offset : this.EMPTY_POINT);
(geo.offset != null ? geo.offset : this.EMPTY_POINT)
);
if (geo.relative && pState != null) { if (geo.relative && pState != null) {
if (pState.cell.isEdge()) { if (pState.cell.isEdge()) {
@ -966,13 +917,9 @@ class GraphView extends EventSource {
if (origin != null) { if (origin != null) {
origin.x += origin.x +=
origin.x / this.scale - origin.x / this.scale - (<Point>pState.origin).x - this.translate.x;
(<Point>pState.origin).x -
this.translate.x;
origin.y += origin.y +=
origin.y / this.scale - origin.y / this.scale - (<Point>pState.origin).y - this.translate.y;
(<Point>pState.origin).y -
this.translate.y;
} }
} else { } else {
origin.x += geo.x * <number>pState.unscaledWidth + offset.x; origin.x += geo.x * <number>pState.unscaledWidth + offset.x;
@ -1056,10 +1003,7 @@ class GraphView extends EventSource {
if ( if (
state.cell !== this.currentRoot && state.cell !== this.currentRoot &&
(pts == null || (pts == null || pts.length < 2 || pts[0] == null || pts[pts.length - 1] == null)
pts.length < 2 ||
pts[0] == null ||
pts[pts.length - 1] == null)
) { ) {
// This will remove edges with invalid points from the list of states in the view. // This will remove edges with invalid points from the list of states in the view.
// Happens if the one of the terminals and the corresponding terminal point is null. // Happens if the one of the terminals and the corresponding terminal point is null.
@ -1170,11 +1114,7 @@ class GraphView extends EventSource {
* @param source {@link mxCellState} which represents the source terminal. * @param source {@link mxCellState} which represents the source terminal.
* @param target {@link mxCellState} which represents the target terminal. * @param target {@link mxCellState} which represents the target terminal.
*/ */
updateFixedTerminalPoints( updateFixedTerminalPoints(edge: CellState, source: CellState, target: CellState): void {
edge: CellState,
source: CellState,
target: CellState
): void {
this.updateFixedTerminalPoint( this.updateFixedTerminalPoint(
edge, edge,
source, source,
@ -1234,11 +1174,7 @@ class GraphView extends EventSource {
let pt = null; let pt = null;
if (constraint != null) { if (constraint != null) {
pt = (<graph>this.graph).getConnectionPoint( pt = (<graph>this.graph).getConnectionPoint(terminal, constraint, false); // FIXME Rounding introduced bugs when calculating label positions -> , this.graph.isOrthogonal(edge));
terminal,
constraint,
false
); // FIXME Rounding introduced bugs when calculating label positions -> , this.graph.isOrthogonal(edge));
} }
if (pt == null && terminal == null) { if (pt == null && terminal == null) {
@ -1249,10 +1185,7 @@ class GraphView extends EventSource {
pt = geo.getTerminalPoint(source); pt = geo.getTerminalPoint(source);
if (pt != null) { if (pt != null) {
pt = new Point( pt = new Point(s * (tr.x + pt.x + orig.x), s * (tr.y + pt.y + orig.y));
s * (tr.x + pt.x + orig.x),
s * (tr.y + pt.y + orig.y)
);
} }
} }
@ -1325,21 +1258,11 @@ class GraphView extends EventSource {
// Restores previous bounds // Restores previous bounds
if (srcBounds != null) { if (srcBounds != null) {
src.setRect( src.setRect(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height);
srcBounds.x,
srcBounds.y,
srcBounds.width,
srcBounds.height
);
} }
if (trgBounds != null) { if (trgBounds != null) {
trg.setRect( trg.setRect(trgBounds.x, trgBounds.y, trgBounds.width, trgBounds.height);
trgBounds.x,
trgBounds.y,
trgBounds.width,
trgBounds.height
);
} }
} else if (points != null) { } else if (points != null) {
for (let i = 0; i < points.length; i += 1) { for (let i = 0; i < points.length; i += 1) {
@ -1388,16 +1311,8 @@ class GraphView extends EventSource {
source: CellState | null = null, source: CellState | null = null,
target: CellState | null = null target: CellState | null = null
): boolean { ): boolean {
const sc = (<graph>this.graph).getConnectionConstraint( const sc = (<graph>this.graph).getConnectionConstraint(edge, source, true);
edge, const tc = (<graph>this.graph).getConnectionConstraint(edge, target, false);
source,
true
);
const tc = (<graph>this.graph).getConnectionConstraint(
edge,
target,
false
);
if ( if (
(points == null || points.length < 2) && (points == null || points.length < 2) &&
@ -1516,16 +1431,9 @@ class GraphView extends EventSource {
let border = parseFloat(edge.style.perimeterSpacing || 0); let border = parseFloat(edge.style.perimeterSpacing || 0);
border += parseFloat( border += parseFloat(
edge.style[ edge.style[source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing'] || 0
source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing'
] || 0
);
let pt = this.getPerimeterPoint(
start,
<Point>next,
alpha === 0 && orth,
border
); );
let pt = this.getPerimeterPoint(start, <Point>next, alpha === 0 && orth, border);
if (alpha !== 0) { if (alpha !== 0) {
const cos = Math.cos(alpha); const cos = Math.cos(alpha);
@ -1553,10 +1461,7 @@ class GraphView extends EventSource {
const id = getValue(state.style, key); const id = getValue(state.style, key);
if (id != null) { if (id != null) {
const tmp = this.getState( const tmp = this.getState((<graph>this.graph).getModel().getCell(id), false);
(<graph>this.graph).getModel().getCell(id),
false
);
// Only uses ports where a cell state exists // Only uses ports where a cell state exists
if (tmp != null) { if (tmp != null) {
@ -1641,8 +1546,7 @@ class GraphView extends EventSource {
* Returns the x-coordinate of the center point for automatic routing. * Returns the x-coordinate of the center point for automatic routing.
*/ */
getRoutingCenterX(state: CellState): number { getRoutingCenterX(state: CellState): number {
const f = const f = state.style != null ? parseFloat(state.style.routingCenterX) || 0 : 0;
state.style != null ? parseFloat(state.style.routingCenterX) || 0 : 0;
return state.getCenterX() + f * state.width; return state.getCenterX() + f * state.width;
} }
@ -1650,8 +1554,7 @@ class GraphView extends EventSource {
* Returns the y-coordinate of the center point for automatic routing. * Returns the y-coordinate of the center point for automatic routing.
*/ */
getRoutingCenterY(state: CellState): number { getRoutingCenterY(state: CellState): number {
const f = const f = state.style != null ? parseFloat(state.style.routingCenterY) || 0 : 0;
state.style != null ? parseFloat(state.style.routingCenterY) || 0 : 0;
return state.getCenterY() + f * state.height; return state.getCenterY() + f * state.height;
} }
@ -1986,8 +1889,7 @@ class GraphView extends EventSource {
if (dotprod <= 0.0) { if (dotprod <= 0.0) {
projlenSq = 0; projlenSq = 0;
} else { } else {
projlenSq = projlenSq = (dotprod * dotprod) / (xSegment * xSegment + ySegment * ySegment);
(dotprod * dotprod) / (xSegment * xSegment + ySegment * ySegment);
} }
let projlen = Math.sqrt(projlenSq); let projlen = Math.sqrt(projlenSq);
@ -2139,11 +2041,7 @@ class GraphView extends EventSource {
* @param cell {@link mxCell} for which a new {@link CellState} should be created. * @param cell {@link mxCell} for which a new {@link CellState} should be created.
*/ */
createState(cell: Cell): CellState { createState(cell: Cell): CellState {
return new CellState( return new CellState(this, cell, (<graph>this.graph).getCellStyle(cell));
this,
cell,
(<graph>this.graph).getCellStyle(cell)
);
} }
/** /**
@ -2290,9 +2188,7 @@ class GraphView extends EventSource {
// Dispatches the drop event to the graph which // Dispatches the drop event to the graph which
// consumes and executes the source function // consumes and executes the source function
const pt = convertPoint(container, x, y); const pt = convertPoint(container, x, y);
state = (<GraphView>graph.view).getState( state = (<GraphView>graph.view).getState(graph.getCellAt(pt.x, pt.y));
graph.getCellAt(pt.x, pt.y)
);
} }
return state; return state;
@ -2312,10 +2208,7 @@ class GraphView extends EventSource {
this.moveHandler = (evt: Event) => { this.moveHandler = (evt: Event) => {
// Hides the tooltip if mouse is outside container // Hides the tooltip if mouse is outside container
if ( if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) {
graph.tooltipHandler != null &&
graph.tooltipHandler.isHideOnHover()
) {
graph.tooltipHandler.hide(); graph.tooltipHandler.hide();
} }
@ -2368,26 +2261,17 @@ class GraphView extends EventSource {
)); ));
// For background image // For background image
this.backgroundPane = document.createElementNS( this.backgroundPane = document.createElementNS('http://www.w3.org/2000/svg', 'g');
'http://www.w3.org/2000/svg',
'g'
);
canvas.appendChild(this.backgroundPane); canvas.appendChild(this.backgroundPane);
// Adds two layers (background is early feature) // Adds two layers (background is early feature)
this.drawPane = document.createElementNS('http://www.w3.org/2000/svg', 'g'); this.drawPane = document.createElementNS('http://www.w3.org/2000/svg', 'g');
canvas.appendChild(this.drawPane); canvas.appendChild(this.drawPane);
this.overlayPane = document.createElementNS( this.overlayPane = document.createElementNS('http://www.w3.org/2000/svg', 'g');
'http://www.w3.org/2000/svg',
'g'
);
canvas.appendChild(this.overlayPane); canvas.appendChild(this.overlayPane);
this.decoratorPane = document.createElementNS( this.decoratorPane = document.createElementNS('http://www.w3.org/2000/svg', 'g');
'http://www.w3.org/2000/svg',
'g'
);
canvas.appendChild(this.decoratorPane); canvas.appendChild(this.decoratorPane);
const root = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); const root = document.createElementNS('http://www.w3.org/2000/svg', 'svg');