diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9a6964bd9..f037fe939 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -119,8 +119,8 @@ import mxAnimation from './util/animate/mxAnimation'; import mxEffects from './util/animate/mxEffects'; import mxMorphing from './util/animate/mxMorphing'; -import mxAbstractCanvas2D from './util/canvas/mxAbstractCanvas2D'; -import mxSvgCanvas2D from './util/canvas/mxSvgCanvas2D'; +import mxAbstractCanvas2D from './util/canvas/AbstractCanvas2D'; +import mxSvgCanvas2D from './util/canvas/SvgCanvas2D'; import mxXmlCanvas2D from './util/canvas/mxXmlCanvas2D'; import Dictionary from './util/Dictionary'; @@ -151,7 +151,7 @@ import mxPopupMenu from './util/gui/mxPopupMenu'; import mxToolbar from './util/gui/mxToolbar'; 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 ImageExport from './view/image/ImageExport'; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 22919a4b3..d37f85c4b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,4 +1,5 @@ import type Cell from './view/cell/datatypes/Cell'; +import Shape from './view/geometry/shape/Shape'; export type CellMap = { [id: string]: Cell; @@ -23,19 +24,88 @@ export type Properties = { }; 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 DirectionValue = 'north' | 'south' | 'east' | 'west' | null; -export type AlignValue = - | 'left' - | 'center' - | 'right' - | 'top' - | 'middle' - | 'bottom' - | null; +export type ColorValue = string; +export type DirectionValue = 'north' | 'south' | 'east' | 'west'; +export type TextDirectionValue = '' | 'ltr' | 'rtl' | 'auto'; +export type AlignValue = 'left' | 'center' | 'right'; +export type VAlignValue = 'top' | 'middle' | 'bottom'; +export type OverflowValue = 'fill' | 'width' | 'auto' | 'hidden' | 'scroll' | 'visible'; +export type ArrowType = + | 'none' + | 'classic' + | 'classicThin' + | 'block' + | 'blockThin' + | 'open' + | 'openThin' + | 'oval' + | 'diamond' + | 'diamondThin'; export type CanvasState = { dx: number; @@ -48,9 +118,9 @@ export type CanvasState = { gradientFillAlpha: number; gradientColor: ColorValue; gradientAlpha: number; - gradientDirection: string | null; + gradientDirection: DirectionValue; strokeColor: ColorValue; - strokeWidth: number | null; + strokeWidth: number; dashed: boolean; dashPattern: string; fixDash: boolean; diff --git a/packages/core/src/util/Dictionary.ts b/packages/core/src/util/Dictionary.ts index 130a6506b..f43208c6e 100644 --- a/packages/core/src/util/Dictionary.ts +++ b/packages/core/src/util/Dictionary.ts @@ -5,13 +5,15 @@ * Type definitions from the typed-mxgraph project */ -import mxObjectIdentity from './mxObjectIdentity'; +import ObjectIdentity from './ObjectIdentity'; //type Dictionary = { // [key: string]: U; //}; -type Visitor = (key: string, value: U) => void; +type MapKey = string; + +type Visitor = (key: MapKey, value: U) => void; /** * Class: mxDictionary @@ -33,7 +35,7 @@ class Dictionary { * * Stores the (key, value) pairs in this dictionary. */ - map: Dictionary = {}; + map: Record = {}; /** * Function: clear @@ -50,7 +52,7 @@ class Dictionary { * Returns the value for the given key. */ get(key: T) { - const id = mxObjectIdentity.get(key); + const id = ObjectIdentity.get(key); return this.map[id]; } @@ -62,7 +64,7 @@ class Dictionary { * value for that key. */ put(key: T, value: U) { - const id = mxObjectIdentity.get(key); + const id = ObjectIdentity.get(key); const previous = this.map[id]; this.map[id] = value; @@ -76,7 +78,7 @@ class Dictionary { * has been removed. */ remove(key: T) { - const id = mxObjectIdentity.get(key); + const id = ObjectIdentity.get(key); const previous = this.map[id]; delete this.map[id]; @@ -124,7 +126,7 @@ class Dictionary { * * visitor - A function that takes the key and value as arguments. */ - visit(visitor: Visitor) { + visit(visitor: Visitor) { for (const key in this.map) { visitor(key, this.map[key]); } diff --git a/packages/core/src/util/mxObjectIdentity.ts b/packages/core/src/util/ObjectIdentity.ts similarity index 88% rename from packages/core/src/util/mxObjectIdentity.ts rename to packages/core/src/util/ObjectIdentity.ts index d835b15f1..7c95368db 100644 --- a/packages/core/src/util/mxObjectIdentity.ts +++ b/packages/core/src/util/ObjectIdentity.ts @@ -29,7 +29,7 @@ type IdentityFunction = { * * 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 * mxObjectId. @@ -48,9 +48,9 @@ class mxObjectIdentity { if (isNullish(obj[FIELD_NAME])) { if (typeof obj === 'object') { const ctor = getFunctionName(obj.constructor); - obj[FIELD_NAME] = `${ctor}#${mxObjectIdentity.counter++}`; + obj[FIELD_NAME] = `${ctor}#${ObjectIdentity.counter++}`; } 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; diff --git a/packages/core/src/util/Utils.ts b/packages/core/src/util/Utils.ts index f5c3d5d9d..7fe726110 100644 --- a/packages/core/src/util/Utils.ts +++ b/packages/core/src/util/Utils.ts @@ -41,13 +41,8 @@ import Cell from '../view/cell/datatypes/Cell'; import Model from '../view/model/Model'; import graph from '../view/Graph'; -import type { - CellStateStyles, - Properties, - StyleProperties, - StyleValue, -} from '../types'; -import CellArray from "../view/cell/datatypes/CellArray"; +import type { CellStateStyles, Properties, StyleProperties, StyleValue } from '../types'; +import CellArray from '../view/cell/datatypes/CellArray'; /** * Class: mxUtils @@ -164,11 +159,7 @@ export const parseCssNumber = (value: string) => { * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%'); * (end) */ -export const setPrefixedStyle = ( - style: StyleProperties, - name: string, - value: string -) => { +export const setPrefixedStyle = (style: StyleProperties, name: string, value: string) => { let prefix = null; if (mxClient.IS_SF || mxClient.IS_GC) { @@ -343,11 +334,7 @@ export const getValue = (array: any, key: string, defaultValue?: any) => { return value; }; -export const getStringValue = ( - array: any, - key: string, - defaultValue: string -) => { +export const getStringValue = (array: any, key: string, defaultValue: string) => { let value = array != null ? array[key] : null; if (value == null) { value = defaultValue; @@ -455,10 +442,7 @@ export const equalEntries = (a: Properties | null, b: Properties | null) => { for (var key in a) { count--; - if ( - (!Number.isNaN(a[key]) || !Number.isNaN(b[key])) && - a[key] !== b[key] - ) { + if ((!Number.isNaN(a[key]) || !Number.isNaN(b[key])) && a[key] !== b[key]) { return false; } } @@ -588,10 +572,7 @@ export const arcToCurves = ( } sds = - seif * - Math.sqrt( - (r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd) - ); + seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd)); } const txd = (sds * r1 * ryd) / r2; @@ -614,8 +595,7 @@ export const arcToCurves = ( const sse = (dr * 2) / Math.PI; const seg = Math.ceil(sse < 0 ? -1 * sse : sse); const segr = dr / seg; - const t = - ((8 / 3) * Math.sin(segr / 4) * Math.sin(segr / 4)) / Math.sin(segr / 2); + const t = ((8 / 3) * Math.sin(segr / 4) * Math.sin(segr / 4)) / Math.sin(segr / 2); const cpsir1 = cpsi * r1; const cpsir2 = cpsi * r2; const spsir1 = spsi * r1; @@ -679,10 +659,7 @@ export const getBoundingBox = ( const cos = Math.cos(rad); const sin = Math.sin(rad); - cx = - cx != null - ? cx - : new Point(rect.x + rect.width / 2, rect.y + rect.height / 2); + cx = cx != null ? cx : new Point(rect.x + rect.width / 2, rect.y + rect.height / 2); let p1 = new Point(rect.x, 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. */ -export const getRotatedPoint = ( - pt: Point, - cos: number, - sin: number, - c = new Point() -) => { +export const getRotatedPoint = (pt: Point, cos: number, sin: number, c = new Point()) => { const x = pt.x - c.x; const y = pt.y - c.y; @@ -745,11 +717,7 @@ export const getPortConstraints = ( const value = getValue( terminal.style, 'portConstraint', - getValue( - edge.style, - source ? 'sourcePortConstraint' : 'targetPortConstraint', - null - ) + getValue(edge.style, source ? 'sourcePortConstraint' : 'targetPortConstraint', null) ); if (isNullish(value)) { @@ -758,11 +726,7 @@ export const getPortConstraints = ( const directions = value.toString(); let returnValue = DIRECTION_MASK_NONE; - const constraintRotationEnabled = getValue( - terminal.style, - 'portConstraintRotation', - 0 - ); + const constraintRotationEnabled = getValue(terminal.style, 'portConstraintRotation', 0); let rotation = 0; 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 * the specified coordinate pair. */ -export const findNearestSegment = ( - state: CellState, - x: number, - y: number -) => { +export const findNearestSegment = (state: CellState, x: number, y: number) => { let index = -1; if (state.absolutePoints.length > 0) { @@ -979,11 +939,7 @@ export const getDirectedBounds = ( * Returns the intersection between the polygon defined by the array of * points and the line between center and point. */ -export const getPerimeterPoint = ( - pts: Point[], - center: Point, - point: Point -) => { +export const getPerimeterPoint = (pts: Point[], center: Point, point: Point) => { let min = null; for (let i = 0; i < pts.length - 1; i += 1) { @@ -1023,11 +979,7 @@ export const getPerimeterPoint = ( * p1 - that represents the first point of the segment. * p2 - that represents the second point of the segment. */ -export const rectangleIntersectsSegment = ( - bounds: Rectangle, - p1: Point, - p2: Point -) => { +export const rectangleIntersectsSegment = (bounds: Rectangle, p1: Point, p2: Point) => { const top = bounds.y; const left = bounds.x; const bottom = top + bounds.height; @@ -1286,8 +1238,7 @@ export const getDocumentScrollOrigin = (doc: Document) => { const y = wnd != null && window.pageYOffset !== undefined ? window.pageYOffset - : (document.documentElement || document.body.parentNode || document.body) - .scrollTop; + : (document.documentElement || document.body.parentNode || document.body).scrollTop; 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. */ -export const isNumeric = (n: string) => { +export const isNumeric = (n: any) => { return ( !Number.isNaN(parseFloat(n)) && isFinite(+n) && @@ -1792,12 +1743,7 @@ export const removeAllStylenames = (style: string) => { * key - Key of the style to be changed. * value - New value for the given key. */ -export const setCellStyles = ( - model: Model, - cells: Cell[], - key: string, - value: any -) => { +export const setCellStyles = (model: Model, cells: Cell[], key: string, value: any) => { if (cells.length > 0) { model.beginUpdate(); try { @@ -1842,8 +1788,7 @@ export const setStyle = (style: string | null, key: string, value: any) => { if (isValue) { style = `${key}=${value}${next < 0 ? ';' : style.substring(next)}`; } else { - style = - next < 0 || next == style.length - 1 ? '' : style.substring(next + 1); + style = next < 0 || next == style.length - 1 ? '' : style.substring(next + 1); } } else { const index = style.indexOf(`;${key}=`); @@ -1861,8 +1806,7 @@ export const setStyle = (style: string | null, key: string, value: any) => { next < 0 ? ';' : style.substring(next) }`; } else { - style = - style.substring(0, index) + (next < 0 ? ';' : style.substring(next)); + style = style.substring(0, index) + (next < 0 ? ';' : style.substring(next)); } } } @@ -2128,9 +2072,7 @@ export const getScaleForPageCount = ( } pageFormat = - pageFormat != null - ? pageFormat - : new Rectangle(...PAGE_FORMAT_A4_PORTRAIT); + pageFormat != null ? pageFormat : new Rectangle(...PAGE_FORMAT_A4_PORTRAIT); const availablePageWidth = pageFormat.width - border * 2; const availablePageHeight = pageFormat.height - border * 2; @@ -2202,8 +2144,7 @@ export const getScaleForPageCount = ( roundRowDownProportion = Math.floor(numRowPages - 1) / numRowPages; } if (roundColumnDownProportion == 1) { - roundColumnDownProportion = - Math.floor(numColumnPages - 1) / numColumnPages; + roundColumnDownProportion = Math.floor(numColumnPages - 1) / numColumnPages; } // 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); if (w == null) { - w = - Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x); + w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x); } if (h == null) { - h = - Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y); + h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y); } doc.writeln(''); @@ -2322,7 +2261,7 @@ export const show = ( div.style.position = 'absolute'; div.style.left = `${dx}px`; div.style.top = `${dy}px`; - + if (graph.container && graph.view.drawPane) { let node = graph.container.firstChild; let svg: SVGElement | null = null; diff --git a/packages/core/src/util/canvas/mxAbstractCanvas2D.ts b/packages/core/src/util/canvas/AbstractCanvas2D.ts similarity index 84% rename from packages/core/src/util/canvas/mxAbstractCanvas2D.ts rename to packages/core/src/util/canvas/AbstractCanvas2D.ts index 9472fcdb5..399980595 100644 --- a/packages/core/src/util/canvas/mxAbstractCanvas2D.ts +++ b/packages/core/src/util/canvas/AbstractCanvas2D.ts @@ -8,6 +8,7 @@ import { arcToCurves, getRotatedPoint } from '../Utils'; import { DEFAULT_FONTFAMILY, DEFAULT_FONTSIZE, + DIRECTION_EAST, NONE, SHADOWCOLOR, SHADOW_OFFSET_X, @@ -18,7 +19,15 @@ import mxUrlConverter from '../network/mxUrlConverter'; import Point from '../../view/geometry/Point'; import { clone } from '../CloneUtils'; -import type { CanvasState, ColorValue } from '../../types'; +import type { + AlignValue, + CanvasState, + ColorValue, + DirectionValue, + OverflowValue, + TextDirectionValue, + VAlignValue, +} from '../../types'; /** * Class: mxAbstractCanvas2D @@ -30,7 +39,7 @@ import type { CanvasState, ColorValue } from '../../types'; * * Constructs a new abstract canvas. */ -class mxAbstractCanvas2D { +class AbstractCanvas2D { constructor() { /** * Variable: converter @@ -127,6 +136,9 @@ class mxAbstractCanvas2D { */ pointerEvents = false; + // from Polyline (maybe from other shapes also) + pointerEventsValue: string | null = null; + /** * Function: createUrlConverter * @@ -159,12 +171,12 @@ class mxAbstractCanvas2D { alpha: 1, fillAlpha: 1, strokeAlpha: 1, - fillColor: null, + fillColor: NONE, gradientFillAlpha: 1, - gradientColor: null, + gradientColor: NONE, gradientAlpha: 1, - gradientDirection: null, - strokeColor: null, + gradientDirection: DIRECTION_EAST, + strokeColor: NONE, strokeWidth: 1, dashed: false, dashPattern: '3 3', @@ -173,8 +185,8 @@ class mxAbstractCanvas2D { lineJoin: 'miter', miterLimit: 10, fontColor: '#000000', - fontBackgroundColor: null, - fontBorderColor: null, + fontBackgroundColor: NONE, + fontBorderColor: NONE, fontSize: DEFAULT_FONTSIZE, fontFamily: DEFAULT_FONTFAMILY, fontStyle: 0, @@ -291,13 +303,7 @@ class mxAbstractCanvas2D { * * Rotates the current state. */ - rotate( - theta: number, - flipH: boolean, - flipV: boolean, - cx: number, - cy: number - ) { + rotate(theta: number, flipH: boolean, flipV: boolean, cx: number, cy: number) { // nop } @@ -334,10 +340,8 @@ class mxAbstractCanvas2D { * Sets the current fill color. */ setFillColor(value: ColorValue) { - const v = value === NONE ? null : value; - - this.state.fillColor = v; - this.state.gradientColor = null; + this.state.fillColor = value; + this.state.gradientColor = NONE; } /** @@ -352,7 +356,7 @@ class mxAbstractCanvas2D { y: number, w: number, h: number, - direction: string | null, + direction: DirectionValue, alpha1 = 1, alpha2: number = 1 ) { @@ -370,8 +374,7 @@ class mxAbstractCanvas2D { * Sets the current stroke color. */ setStrokeColor(value: ColorValue) { - const v = value === NONE ? null : value; - this.state.strokeColor = v; + this.state.strokeColor = value; } /** @@ -379,7 +382,7 @@ class mxAbstractCanvas2D { * * Sets the current stroke width. */ - setStrokeWidth(value: number | null) { + setStrokeWidth(value: number) { this.state.strokeWidth = value; } @@ -435,8 +438,7 @@ class mxAbstractCanvas2D { * Sets the current font color. */ setFontColor(value: ColorValue) { - const v = value === NONE ? null : value; - this.state.fontColor = v; + this.state.fontColor = value; } /** @@ -445,8 +447,7 @@ class mxAbstractCanvas2D { * Sets the current font background color. */ setFontBackgroundColor(value: ColorValue) { - const v = value === NONE ? null : value; - this.state.fontBackgroundColor = v; + this.state.fontBackgroundColor = value; } /** @@ -455,8 +456,7 @@ class mxAbstractCanvas2D { * Sets the current font border color. */ setFontBorderColor(value: ColorValue) { - const v = value === NONE ? null : value; - this.state.fontBorderColor = v; + this.state.fontBorderColor = value; } /** @@ -501,8 +501,7 @@ class mxAbstractCanvas2D { * Enables or disables and configures the current shadow. */ setShadowColor(value: ColorValue) { - const v = value === NONE ? null : value; - this.state.shadowColor = v; + this.state.shadowColor = value; } /** @@ -567,14 +566,7 @@ class mxAbstractCanvas2D { * * Adds a bezier curve to the current path. */ - curveTo( - x1: number, - y1: number, - x2: number, - y2: number, - x3: number, - y3: number - ) { + curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) { this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3); } @@ -624,14 +616,7 @@ class mxAbstractCanvas2D { * * Closes the current path. */ - close( - x1?: number, - y1?: number, - x2?: number, - y2?: number, - x3?: number, - y3?: number - ) { + close(x1?: number, y1?: number, x2?: number, y2?: number, x3?: number, y3?: number) { this.addOp(this.closeOp); } @@ -641,6 +626,59 @@ class mxAbstractCanvas2D { * Empty implementation for backwards compatibility. This will be removed. */ 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; diff --git a/packages/core/src/util/canvas/mxSvgCanvas2D.ts b/packages/core/src/util/canvas/SvgCanvas2D.ts similarity index 80% rename from packages/core/src/util/canvas/mxSvgCanvas2D.ts rename to packages/core/src/util/canvas/SvgCanvas2D.ts index 8f8a40e56..c21221d46 100644 --- a/packages/core/src/util/canvas/mxSvgCanvas2D.ts +++ b/packages/core/src/util/canvas/SvgCanvas2D.ts @@ -5,7 +5,7 @@ * Type definitions from the typed-mxgraph project */ -import { getAlignmentAsPoint, isNotNullish } from '../Utils'; +import { getAlignmentAsPoint, isNotNullish, mod } from '../Utils'; import mxClient from '../../mxClient'; import { ABSOLUTE_LINE_HEIGHT, @@ -31,15 +31,19 @@ import { WORD_WRAP, } from '../Constants'; import Rectangle from '../../view/geometry/Rectangle'; -import mxAbstractCanvas2D from './mxAbstractCanvas2D'; -import { parseXml } from '../XmlUtils'; +import AbstractCanvas2D from './AbstractCanvas2D'; +import { getXml, parseXml } from '../XmlUtils'; import { importNodeImplementation, isNode, write } from '../DomUtils'; import { htmlEntities, trim } from '../StringUtils'; import { AlignValue, ColorValue, DirectionValue, + Gradient, GradientMap, + OverflowValue, + TextDirectionValue, + VAlignValue, } from '../../types'; // Activates workaround for gradient ID resolution if base tag is used. @@ -87,7 +91,7 @@ const useAbsoluteIds = * ``` * Or set the respective attribute in the SVG element directly. */ -class mxSvgCanvas2D extends mxAbstractCanvas2D { +class SvgCanvas2D extends AbstractCanvas2D { constructor(root: SVGElement, styleEnabled: boolean) { super(); @@ -123,17 +127,17 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Adds optional defs section for export if (root.ownerDocument !== document) { - let node = root; + let node: HTMLElement | SVGElement | null = root; // Finds owner SVG element in XML DOM - while (node != null && node.nodeName !== 'svg') { - node = node.parentNode; + while (node && node.nodeName !== 'svg') { + node = node.parentElement; } svg = node; } - if (svg != null) { + if (svg) { // Tries to get existing defs section const tmp = svg.getElementsByTagName('defs'); @@ -142,8 +146,8 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } // Adds defs section if none exists - if (this.defs == null) { - this.defs = this.createElement('defs'); + if (!this.defs) { + this.defs = this.createElement('defs') as SVGDefsElement; if (svg.firstChild != null) { svg.insertBefore(this.defs, svg.firstChild); @@ -159,7 +163,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } } - root: SVGElement; + root: SVGElement | null; gradients: GradientMap; @@ -257,6 +261,8 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { */ cacheOffsetSize = true; + originalRoot: SVGElement | null = null; + /** * Updates existing DOM nodes for text rendering. */ @@ -268,8 +274,8 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { wrap: boolean, overflow: string, clip: boolean, - bg: ColorValue, - border: ColorValue, + bg: ColorValue | null, + border: ColorValue | null, flex: string, block: string, scale: number, @@ -399,7 +405,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Private helper function to create SVG elements */ createElement(tagName: string, namespace?: string) { - return this.root.ownerDocument.createElementNS( + return this.root?.ownerDocument.createElementNS( namespace || NS_SVG, tagName ) as SVGElement; @@ -418,12 +424,12 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { h: number, str: Element | string, align: AlignValue, - valign: string, + valign: VAlignValue, wrap: boolean, - format, - overflow: boolean, + format: string, + overflow: OverflowValue, clip: boolean, - rotation + rotation: number ) { return isNotNullish(str) ? this.foAltText : null; } @@ -434,19 +440,19 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Returns the alternate content for the given foreignObject. */ createAlternateContent( - fo, - x, - y, - w, - h, - str, + fo: SVGForeignObjectElement, + x: number, + y: number, + w: number, + h: number, + str: string, align: AlignValue, - valign: AlignValue, - wrap, - format, - overflow, - clip, - rotation + valign: VAlignValue, + wrap: boolean, + format: string, + overflow: OverflowValue, + clip: boolean, + rotation: number ) { const text = this.getAlternateText( fo, @@ -468,11 +474,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { if (isNotNullish(text) && s.fontSize > 0) { const dy = valign === ALIGN_TOP ? 1 : valign === ALIGN_BOTTOM ? 0 : 0.3; const anchor = - align === ALIGN_RIGHT - ? 'end' - : align === ALIGN_LEFT - ? 'start' - : 'middle'; + align === ALIGN_RIGHT ? 'end' : align === ALIGN_LEFT ? 'start' : 'middle'; const alt = this.createElement('text'); alt.setAttribute('x', String(Math.round(x + s.dx))); @@ -565,42 +567,43 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Private helper function to create SVG elements */ - // getSvgGradient(start: string, end: string, alpha1: string, alpha2: string, direction: string): string; - getSvgGradient(start, end, alpha1, alpha2, direction) { - const id = this.createGradientId(start, end, alpha1, alpha2, direction); - let gradient = this.gradients[id]; + getSvgGradient( + start: string, + end: string, + alpha1: number, + alpha2: number, + direction: DirectionValue + ) { + if (!this.root) return; - if (gradient == null) { + const id = this.createGradientId(start, end, alpha1, alpha2, direction); + let gradient: Gradient | null = this.gradients[id]; + + if (!gradient) { const svg = this.root.ownerSVGElement; let counter = 0; let tmpId = `${id}-${counter}`; - if (svg != null) { - gradient = svg.ownerDocument.getElementById(tmpId); + if (svg) { + gradient = (svg.ownerDocument.getElementById(tmpId)); - while (gradient != null && gradient.ownerSVGElement !== svg) { + while (gradient && gradient.ownerSVGElement !== svg) { tmpId = `${id}-${counter++}`; - gradient = svg.ownerDocument.getElementById(tmpId); + gradient = (svg.ownerDocument.getElementById(tmpId)); } } else { // Uses shorter IDs for export tmpId = `id${++this.refCount}`; } - if (gradient == null) { - gradient = this.createSvgGradient( - start, - end, - alpha1, - alpha2, - direction - ); + if (!gradient) { + gradient = this.createSvgGradient(start, end, alpha1, alpha2, direction); gradient.setAttribute('id', tmpId); - if (this.defs != null) { + if (this.defs) { this.defs.appendChild(gradient); - } else { + } else if (svg) { svg.appendChild(gradient); } } @@ -614,9 +617,14 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Creates the given SVG gradient. */ - // createSvgGradient(start: string, end: string, alpha1: string, alpha2: string, direction: string): Element; - createSvgGradient(start, end, alpha1, alpha2, direction) { - const gradient = this.createElement('linearGradient'); + createSvgGradient( + start: string, + end: string, + alpha1: number, + alpha2: number, + direction: DirectionValue + ) { + const gradient = this.createElement('linearGradient'); gradient.setAttribute('x1', '0%'); gradient.setAttribute('y1', '0%'); gradient.setAttribute('x2', '0%'); @@ -652,8 +660,9 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Private helper function to create SVG elements */ - // addNode(filled: boolean, stroked: boolean): void; - addNode(filled, stroked) { + addNode(filled: boolean, stroked: boolean) { + if (!this.root) return; + const { node } = this; const s = this.state; @@ -730,25 +739,26 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Transfers the stroke attributes from to . */ - // updateFill(): void; updateFill() { + if (!this.node) return; + const s = this.state; if (s.alpha < 1 || s.fillAlpha < 1) { - this.node.setAttribute('fill-opacity', s.alpha * s.fillAlpha); + this.node.setAttribute('fill-opacity', String(s.alpha * s.fillAlpha)); } - if (s.fillColor != null) { - if (s.gradientColor != null) { + if (s.fillColor) { + if (s.gradientColor) { const id = this.getSvgGradient( - String(s.fillColor), - String(s.gradientColor), + s.fillColor, + s.gradientColor, s.gradientFillAlpha, s.gradientAlpha, s.gradientDirection ); - if (this.root.ownerDocument === document && useAbsoluteIds) { + if (this.root?.ownerDocument === document && useAbsoluteIds) { // Workaround for no fill with base tag in page (escape brackets) const base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1'); this.node.setAttribute('fill', `url(${base}#${id})`); @@ -756,7 +766,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { this.node.setAttribute('fill', `url(#${id})`); } } else { - this.node.setAttribute('fill', String(s.fillColor).toLowerCase()); + this.node.setAttribute('fill', s.fillColor.toLowerCase()); } } } @@ -775,20 +785,21 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Transfers the stroke attributes from {@link mxAbstractCanvas2D.state} to {@link node}. */ - // updateStroke(): void; updateStroke() { + if (!this.node) return; + const s = this.state; - this.node.setAttribute('stroke', String(s.strokeColor).toLowerCase()); + if (s.strokeColor) this.node.setAttribute('stroke', s.strokeColor.toLowerCase()); if (s.alpha < 1 || s.strokeAlpha < 1) { - this.node.setAttribute('stroke-opacity', s.alpha * s.strokeAlpha); + this.node.setAttribute('stroke-opacity', String(s.alpha * s.strokeAlpha)); } const sw = this.getCurrentStrokeWidth(); if (sw !== 1) { - this.node.setAttribute('stroke-width', sw); + this.node.setAttribute('stroke-width', String(sw)); } if (this.node.nodeName === 'path') { @@ -806,16 +817,17 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Transfers the stroke attributes from {@link mxAbstractCanvas2D.state} to {@link node}. */ - // updateStrokeAttributes(): void; updateStrokeAttributes() { + if (!this.node) return; + const s = this.state; // Linejoin miter is default in SVG - if (s.lineJoin != null && s.lineJoin !== 'miter') { + if (s.lineJoin && s.lineJoin !== 'miter') { this.node.setAttribute('stroke-linejoin', s.lineJoin); } - if (s.lineCap != null) { + if (s.lineCap) { // flat is called butt in SVG let value = s.lineCap; @@ -831,15 +843,14 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Miterlimit 10 is default in our document if (s.miterLimit != null && (!this.styleEnabled || s.miterLimit !== 10)) { - this.node.setAttribute('stroke-miterlimit', s.miterLimit); + this.node.setAttribute('stroke-miterlimit', String(s.miterLimit)); } } /** * Creates the SVG dash pattern for the given state. */ - // createDashPattern(scale: number): string; - createDashPattern(scale) { + createDashPattern(scale: number) { const pat = []; if (typeof this.state.dashPattern === 'string') { @@ -859,14 +870,13 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Creates a hit detection tolerance shape for the given node. */ // createTolerance(node: Element): Element; - createTolerance(node) { - const tol = node.cloneNode(true); - const sw = - parseFloat(tol.getAttribute('stroke-width') || 1) + this.strokeTolerance; + createTolerance(node: SVGElement) { + const tol = node.cloneNode(true) as SVGElement; + const sw = parseFloat(tol.getAttribute('stroke-width') || '1') + this.strokeTolerance; tol.setAttribute('pointer-events', 'stroke'); tol.setAttribute('visibility', 'hidden'); tol.removeAttribute('stroke-dasharray'); - tol.setAttribute('stroke-width', sw); + tol.setAttribute('stroke-width', String(sw)); tol.setAttribute('fill', 'none'); tol.setAttribute('stroke', 'white'); return tol; @@ -875,9 +885,8 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Creates a shadow for the given node. */ - // createShadow(node: Element): Element; - createShadow(node) { - const shadow = node.cloneNode(true); + createShadow(node: SVGElement) { + const shadow = node.cloneNode(true) as SVGElement; const s = this.state; // Firefox uses transparent for no fill in ellipses @@ -885,10 +894,10 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { shadow.getAttribute('fill') !== 'none' && (!mxClient.IS_FF || shadow.getAttribute('fill') !== 'transparent') ) { - shadow.setAttribute('fill', s.shadowColor); + shadow.setAttribute('fill', String(s.shadowColor)); } - if (shadow.getAttribute('stroke') !== 'none') { + if (shadow.getAttribute('stroke') !== 'none' && s.shadowColor) { shadow.setAttribute('stroke', s.shadowColor); } @@ -898,7 +907,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { s.shadowDy * s.scale )})${s.transform || ''}` ); - shadow.setAttribute('opacity', s.shadowAlpha); + shadow.setAttribute('opacity', String(s.shadowAlpha)); return shadow; } @@ -906,9 +915,10 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Experimental implementation for hyperlinks. */ - // setLink(link: string): void; - setLink(link) { - if (link == null) { + setLink(link: string) { + if (!this.root) return; + + if (!link) { this.root = this.originalRoot; } else { this.originalRoot = this.root; @@ -931,8 +941,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Sets the rotation of the canvas. Note that rotation cannot be concatenated. */ - // rotate(theta: number, flipH: boolean, flipV: boolean, cx: number, cy: number): void; - rotate(theta, flipH, flipV, cx, cy) { + rotate(theta: number, flipH: boolean, flipV: boolean, cx: number, cy: number) { if (theta !== 0 || flipH || flipV) { const s = this.state; cx += s.dx; @@ -965,9 +974,9 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } if (theta !== 0) { - s.transform += `rotate(${this.format(theta)},${this.format( - cx - )},${this.format(cy)})`; + s.transform += `rotate(${this.format(theta)},${this.format(cx)},${this.format( + cy + )})`; } s.rotation += theta; @@ -988,14 +997,13 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Private helper function to create SVG elements */ - // rect(x: number, y: number, w: number, h: number): void; - rect(x, y, w, h) { + rect(x: number, y: number, w: number, h: number) { const s = this.state; const n = this.createElement('rect'); - n.setAttribute('x', this.format((x + s.dx) * s.scale)); - n.setAttribute('y', this.format((y + s.dy) * s.scale)); - n.setAttribute('width', this.format(w * s.scale)); - n.setAttribute('height', this.format(h * s.scale)); + n.setAttribute('x', String(this.format((x + s.dx) * s.scale))); + n.setAttribute('y', String(this.format((y + s.dy) * s.scale))); + n.setAttribute('width', String(this.format(w * s.scale))); + n.setAttribute('height', String(this.format(h * s.scale))); this.node = n; } @@ -1003,31 +1011,31 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Private helper function to create SVG elements */ - // roundrect(x: number, y: number, w: number, h: number, dx: number, dy: number): void; - roundrect(x, y, w, h, dx, dy) { + roundrect(x: number, y: number, w: number, h: number, dx: number, dy: number) { + if (!this.node) return; + this.rect(x, y, w, h); if (dx > 0) { - this.node.setAttribute('rx', this.format(dx * this.state.scale)); + this.node.setAttribute('rx', String(this.format(dx * this.state.scale))); } if (dy > 0) { - this.node.setAttribute('ry', this.format(dy * this.state.scale)); + this.node.setAttribute('ry', String(this.format(dy * this.state.scale))); } } /** * Private helper function to create SVG elements */ - // ellipse(x: number, y: number, w: number, h: number): void; - ellipse(x, y, w, h) { + ellipse(x: number, y: number, w: number, h: number) { const s = this.state; const n = this.createElement('ellipse'); // No rounding for consistent output with 1.x - n.setAttribute('cx', this.format((x + w / 2 + s.dx) * s.scale)); - n.setAttribute('cy', this.format((y + h / 2 + s.dy) * s.scale)); - n.setAttribute('rx', (w / 2) * s.scale); - n.setAttribute('ry', (h / 2) * s.scale); + n.setAttribute('cx', String(this.format((x + w / 2 + s.dx) * s.scale))); + n.setAttribute('cy', String(this.format((y + h / 2 + s.dy) * s.scale))); + n.setAttribute('rx', String((w / 2) * s.scale)); + n.setAttribute('ry', String((h / 2) * s.scale)); this.node = n; } @@ -1036,26 +1044,32 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * * Private helper function to create SVG elements */ - image(x, y, w, h, src, aspect, flipH, flipV) { - src = this.converter.convert(src); + image( + x: number, + y: number, + w: number, + h: number, + src: string, + aspect = true, + flipH = false, + flipV = false + ) { + if (!this.root) return; - // LATER: Add option for embedding images as base64. - aspect = aspect != null ? aspect : true; - flipH = flipH != null ? flipH : false; - flipV = flipV != null ? flipV : false; + src = this.converter.convert(src); const s = this.state; x += s.dx; y += s.dy; const node = this.createElement('image'); - node.setAttribute('x', this.format(x * s.scale) + this.imageOffset); - node.setAttribute('y', this.format(y * s.scale) + this.imageOffset); - node.setAttribute('width', this.format(w * s.scale)); - node.setAttribute('height', this.format(h * s.scale)); + node.setAttribute('x', String(this.format(x * s.scale) + this.imageOffset)); + node.setAttribute('y', String(this.format(y * s.scale) + this.imageOffset)); + node.setAttribute('width', String(this.format(w * s.scale))); + node.setAttribute('height', String(this.format(h * s.scale))); // Workaround for missing namespace support - if (node.setAttributeNS == null) { + if (!node.setAttributeNS) { node.setAttribute('xlink:href', src); } else { node.setAttributeNS(NS_XLINK, 'xlink:href', src); @@ -1066,7 +1080,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } if (s.alpha < 1 || s.fillAlpha < 1) { - node.setAttribute('opacity', s.alpha * s.fillAlpha); + node.setAttribute('opacity', String(s.alpha * s.fillAlpha)); } let tr = this.state.transform || ''; @@ -1105,8 +1119,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Converts the given HTML string to XHTML. */ - // convertHtml(val: string): string; - convertHtml(val) { + convertHtml(val: string) { const doc = new DOMParser().parseFromString(val, 'text/html'); if (doc != null) { @@ -1129,41 +1142,41 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Private helper function to create SVG elements * Note: signature changed in mxgraph 4.1.0 */ - // createDiv(str: string): HTMLElement; - createDiv(str) { + createDiv(str: string | HTMLElement) { + if (!this.root) return; + let val = str; if (!isNode(val)) { - val = `
${this.convertHtml(val)}
`; + val = `
${this.convertHtml(val as string)}
`; } if (document.createElementNS) { - const div = document.createElementNS( - 'http://www.w3.org/1999/xhtml', - 'div' - ); + const div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); if (isNode(val)) { + const n = val as HTMLElement; + const div2 = document.createElement('div'); const div3 = div2.cloneNode(false); // Creates a copy for export if (this.root.ownerDocument !== document) { - div2.appendChild(val.cloneNode(true)); + div2.appendChild(n.cloneNode(true)); } else { - div2.appendChild(val); + div2.appendChild(n); } div3.appendChild(div2); div.appendChild(div3); } else { - div.innerHTML = val; + div.innerHTML = val as string; } return div; } if (isNode(val)) { - val = `
${mxUtils.getXml(val)}
`; + val = `
${getXml(val)}
`; } val = `
${val}
`; @@ -1175,12 +1188,20 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Updates existing DOM nodes for text rendering. LATER: Merge common parts with text function below. */ - updateText(x, y, w, h, align, valign, wrap, overflow, clip, rotation, node) { - if ( - node != null && - node.firstChild != null && - node.firstChild.firstChild != null - ) { + updateText( + x: number, + y: number, + w: number, + h: number, + align: AlignValue, + valign: VAlignValue, + wrap: boolean, + overflow: OverflowValue, + clip: boolean, + rotation: number, + node: SVGElement + ) { + if (node && node.firstChild && node.firstChild.firstChild) { this.updateTextNodes( x, y, @@ -1192,7 +1213,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { overflow, clip, rotation, - node.firstChild + node.firstChild as SVGElement ); } } @@ -1203,24 +1224,24 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Creates a foreignObject for the given string and adds it to the given root. */ addForeignObject( - x, - y, - w, - h, - str, - align, - valign, - wrap, - format, - overflow, - clip, - rotation, - dir, - div, - root + x: number, + y: number, + w: number, + h: number, + str: string, + align: AlignValue, + valign: VAlignValue, + wrap: boolean, + format: string, + overflow: OverflowValue, + clip: boolean, + rotation: number, + dir: TextDirectionValue, + div: HTMLElement, + root: SVGElement ) { const group = this.createElement('g'); - const fo = this.createElement('foreignObject'); + const fo = this.createElement('foreignObject') as SVGForeignObjectElement; // Workarounds for print clipping and static position in Safari fo.setAttribute('style', 'overflow: visible; text-align: left;'); @@ -1249,7 +1270,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { ); // Alternate content if foreignObject not supported - if (this.root.ownerDocument !== document) { + if (this.root?.ownerDocument !== document) { const alt = this.createAlternateContent( fo, x, @@ -1285,21 +1306,21 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Updates existing DOM nodes for text rendering. */ updateTextNodes( - x, - y, - w, - h, - align, - valign, - wrap, - overflow, - clip, - rotation, - g + x: number, + y: number, + w: number, + h: number, + align: AlignValue, + valign: VAlignValue, + wrap: boolean, + overflow: OverflowValue, + clip: boolean, + rotation: number, + g: SVGElement ) { const s = this.state.scale; - mxSvgCanvas2D.createCss( + SvgCanvas2D.createCss( w + 2, h, align, @@ -1307,9 +1328,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { wrap, overflow, clip, - this.state.fontBackgroundColor != null - ? this.state.fontBackgroundColor - : null, + this.state.fontBackgroundColor != null ? this.state.fontBackgroundColor : null, this.state.fontBorderColor != null ? this.state.fontBorderColor : null, `display: flex; align-items: unsafe ${ valign === ALIGN_TOP @@ -1331,17 +1350,15 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { x += this.state.dx; y += this.state.dy; - const fo = g.firstChild; - const div = fo.firstChild; - const box = div.firstChild; - const text = box.firstChild; + const fo = g.firstChild as SVGElement; + const div = fo.firstChild as SVGElement; + const box = div.firstChild as SVGElement; + const text = box.firstChild as SVGElement; const r = - (this.rotateHtml ? this.state.rotation : 0) + - (rotation != null ? rotation : 0); + (this.rotateHtml ? this.state.rotation : 0) + (rotation != null ? rotation : 0); let t = - (this.foOffset !== 0 - ? `translate(${this.foOffset} ${this.foOffset})` - : '') + (s !== 1 ? `scale(${s})` : ''); + (this.foOffset !== 0 ? `translate(${this.foOffset} ${this.foOffset})` : '') + + (s !== 1 ? `scale(${s})` : ''); text.setAttribute('style', block); box.setAttribute('style', item); @@ -1360,16 +1377,13 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Margin-top is ignored in Safari and no negative values allowed // for padding. if (yp < 0) { - fo.setAttribute('y', yp); + fo.setAttribute('y', String(yp)); } else { fo.removeAttribute('y'); flex += `padding-top: ${yp}px; `; } - div.setAttribute( - 'style', - `${flex}margin-left: ${Math.round(x + dx)}px;` - ); + div.setAttribute('style', `${flex}margin-left: ${Math.round(x + dx)}px;`); t += r !== 0 ? `rotate(${r} ${x} ${y})` : ''; // Output allows for reflow but Safari cannot use absolute position, @@ -1381,7 +1395,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } if (this.state.alpha !== 1) { - g.setAttribute('opacity', this.state.alpha); + g.setAttribute('opacity', String(this.state.alpha)); } else { g.removeAttribute('opacity'); } @@ -1440,20 +1454,22 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * does currently not support HTML text as part of shapes.) */ text( - x, - y, - w, - h, - str, - align, - valign, - wrap, - format, - overflow, - clip, - rotation, - dir + 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 ) { + if (!this.root) return; + if (this.textEnabled && str != null) { rotation = rotation != null ? rotation : 0; @@ -1506,8 +1522,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Creates a clip for the given coordinates. */ - // createClip(x: number, y: number, w: number, h: number): Element; - createClip(x, y, w, h) { + createClip(x: number, y: number, w: number, h: number) { x = Math.round(x); y = Math.round(y); w = Math.round(w); @@ -1527,10 +1542,10 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { clip.setAttribute('id', tmp); const rect = this.createElement('rect'); - rect.setAttribute('x', x); - rect.setAttribute('y', y); - rect.setAttribute('width', w); - rect.setAttribute('height', h); + rect.setAttribute('x', String(x)); + rect.setAttribute('y', String(y)); + rect.setAttribute('width', String(w)); + rect.setAttribute('height', String(h)); clip.appendChild(rect); @@ -1544,20 +1559,21 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * plain text and html for HTML markup. */ plainText( - x, - y, - w, - h, - str, - align, - valign, - wrap, - overflow, - clip, - rotation, - dir + x: number, + y: number, + w: number, + h: number, + str: string, + align: AlignValue, + valign: VAlignValue, + wrap: boolean, + overflow: OverflowValue, + clip: boolean, + rotation = 0, + dir: TextDirectionValue ) { - rotation = rotation != null ? rotation : 0; + if (!this.root) return; + const s = this.state; const size = s.fontSize; const node = this.createElement('g'); @@ -1571,9 +1587,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Non-rotated text if (rotation !== 0) { - tr += `rotate(${rotation},${this.format(x * s.scale)},${this.format( - y * s.scale - )})`; + tr += `rotate(${rotation},${this.format(x * s.scale)},${this.format(y * s.scale)})`; } if (dir != null) { @@ -1628,11 +1642,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Default is left const anchor = - align === ALIGN_RIGHT - ? 'end' - : align === ALIGN_CENTER - ? 'middle' - : 'start'; + align === ALIGN_RIGHT ? 'end' : align === ALIGN_CENTER ? 'middle' : 'start'; // Text-anchor start is default in SVG if (anchor !== 'start') { @@ -1648,7 +1658,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { } if (s.alpha < 1) { - node.setAttribute('opacity', s.alpha); + node.setAttribute('opacity', String(s.alpha)); } const lines = str.split('\n'); @@ -1672,22 +1682,22 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { cy -= h; } else { const dy = - this.matchHtmlAlignment && clip && h > 0 - ? Math.min(textHeight, h) - : textHeight; + this.matchHtmlAlignment && clip && h > 0 ? Math.min(textHeight, h) : textHeight; cy -= dy + 1; } } for (let i = 0; i < lines.length; i += 1) { + const line = trim(lines[i]); + // Workaround for bounding box of empty lines and spaces - if (lines[i].length > 0 && trim(lines[i]).length > 0) { + if (line) { const text = this.createElement('text'); // LATER: Match horizontal HTML alignment - text.setAttribute('x', this.format(x * s.scale) + this.textOffset); - text.setAttribute('y', this.format(cy * s.scale) + this.textOffset); + text.setAttribute('x', String(this.format(x * s.scale) + this.textOffset)); + text.setAttribute('y', String(this.format(cy * s.scale) + this.textOffset)); - write(text, lines[i]); + write(text, line); node.appendChild(text); } @@ -1712,11 +1722,10 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * Updates the text properties for the given node. (NOTE: For this to work in * IE, the given node must be a text or tspan element.) */ - // updateFont(node: Element): void; - updateFont(node) { + updateFont(node: SVGElement) { const s = this.state; - node.setAttribute('fill', s.fontColor); + if (s.fontColor) node.setAttribute('fill', s.fontColor); if (!this.styleEnabled || s.fontFamily !== DEFAULT_FONTFAMILY) { node.setAttribute('font-family', s.fontFamily); @@ -1750,7 +1759,17 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { * * Background color and border */ - addTextBackground(node, str, x, y, w, h, align, valign, overflow) { + addTextBackground( + node: SVGElement, + str: string, + x: number, + y: number, + w: number, + h: number, + align: AlignValue, + valign: VAlignValue, + overflow: OverflowValue + ) { const s = this.state; if (s.fontBackgroundColor != null || s.fontBorderColor != null) { @@ -1775,16 +1794,13 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { (w - 2) * s.scale, (h + 2) * s.scale ); + // @ts-ignore check for getBBox } else if (node.getBBox != null && this.root.ownerDocument === document) { // Uses getBBox only if inside document for correct size try { + // @ts-ignore getBBox exists bbox = node.getBBox(); - bbox = new Rectangle( - bbox.x, - bbox.y + 1, - bbox.width, - bbox.height + 0 - ); + bbox = new Rectangle(bbox.x, bbox.y + 1, bbox.width, bbox.height + 0); } catch (e) { // Ignores NS_ERROR_FAILURE in FF if container display is none. } @@ -1797,7 +1813,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { // Wrapping and clipping can be ignored here div.style.lineHeight = ABSOLUTE_LINE_HEIGHT ? `${s.fontSize * LINE_HEIGHT}px` - : LINE_HEIGHT; + : String(LINE_HEIGHT); div.style.fontSize = `${s.fontSize}px`; div.style.fontFamily = s.fontFamily; div.style.whiteSpace = 'nowrap'; @@ -1819,7 +1835,7 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { document.body.appendChild(div); const w = div.offsetWidth; const h = div.offsetHeight; - div.parentNode.removeChild(div); + document.body.removeChild(div); if (align === ALIGN_CENTER) { x -= w / 2; @@ -1845,17 +1861,16 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { const n = this.createElement('rect'); n.setAttribute('fill', s.fontBackgroundColor || 'none'); n.setAttribute('stroke', s.fontBorderColor || 'none'); - n.setAttribute('x', Math.floor(bbox.x - 1)); - n.setAttribute('y', Math.floor(bbox.y - 1)); - n.setAttribute('width', Math.ceil(bbox.width + 2)); - n.setAttribute('height', Math.ceil(bbox.height)); + n.setAttribute('x', String(Math.floor(bbox.x - 1))); + n.setAttribute('y', String(Math.floor(bbox.y - 1))); + n.setAttribute('width', String(Math.ceil(bbox.width + 2))); + n.setAttribute('height', String(Math.ceil(bbox.height))); - const sw = - s.fontBorderColor != null ? Math.max(1, this.format(s.scale)) : 0; - n.setAttribute('stroke-width', sw); + const sw = s.fontBorderColor ? Math.max(1, this.format(s.scale)) : 0; + n.setAttribute('stroke-width', String(sw)); // Workaround for crisp rendering - only required if not exporting - if (this.root.ownerDocument === document && mxUtils.mod(sw, 2) === 1) { + if (this.root?.ownerDocument === document && mod(sw, 2) === 1) { n.setAttribute('transform', 'translate(0.5, 0.5)'); } @@ -1867,7 +1882,6 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Paints the outline of the current path. */ - // stroke(): void; stroke() { this.addNode(false, true); } @@ -1875,7 +1889,6 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Fills the current path. */ - // fill(): void; fill() { this.addNode(true, false); } @@ -1883,10 +1896,9 @@ class mxSvgCanvas2D extends mxAbstractCanvas2D { /** * Fills and paints the outline of the current path. */ - // fillAndStroke(): void; fillAndStroke() { this.addNode(true, true); } } -export default mxSvgCanvas2D; +export default SvgCanvas2D; diff --git a/packages/core/src/util/canvas/mxXmlCanvas2D.js b/packages/core/src/util/canvas/mxXmlCanvas2D.js index 3b7c9aa58..733b59016 100644 --- a/packages/core/src/util/canvas/mxXmlCanvas2D.js +++ b/packages/core/src/util/canvas/mxXmlCanvas2D.js @@ -4,7 +4,7 @@ * Updated to ES9 syntax by David Morrissey 2021 * Type definitions from the typed-mxgraph project */ -import mxAbstractCanvas2D from './mxAbstractCanvas2D'; +import mxAbstractCanvas2D from './AbstractCanvas2D'; import { DEFAULT_FONTFAMILY, 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. * dir - Optional string that specifies the text direction. Possible values are rtl and lrt. */ - text( - x, - y, - w, - h, - str, - align, - valign, - wrap, - format, - overflow, - clip, - rotation, - dir - ) { + text(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) { if (this.textEnabled && str != null) { if (isNode(str)) { str = getOuterHtml(str); diff --git a/packages/core/src/view/Graph.ts b/packages/core/src/view/Graph.ts index 929c655b1..49cf7aeb3 100644 --- a/packages/core/src/view/Graph.ts +++ b/packages/core/src/view/Graph.ts @@ -5,7 +5,7 @@ * Type definitions from the typed-mxgraph project */ -import Image from './image/Image'; +import Image from './image/ImageBox'; import EventObject from './event/EventObject'; import EventSource from './event/EventSource'; import InternalEvent from './event/InternalEvent'; @@ -23,16 +23,16 @@ import CellRenderer from './cell/CellRenderer'; import CellEditor from './editing/CellEditor'; import Point from './geometry/Point'; import { - getBoundingBox, getCurrentStyle, - getValue, hasScrollbars, parseCssNumber, + getBoundingBox, + getCurrentStyle, + getValue, + hasScrollbars, + parseCssNumber, } from '../util/Utils'; import Cell from './cell/datatypes/Cell'; import Model from './model/Model'; import Stylesheet from './style/Stylesheet'; -import { - DIALECT_SVG, - PAGE_FORMAT_A4_PORTRAIT, -} from '../util/Constants'; +import { DIALECT_SVG, PAGE_FORMAT_A4_PORTRAIT } from '../util/Constants'; import ChildChange from './model/ChildChange'; import GeometryChange from './geometry/GeometryChange'; @@ -42,12 +42,12 @@ import TerminalChange from './cell/edge/TerminalChange'; import ValueChange from './cell/ValueChange'; import CellState from './cell/datatypes/CellState'; import { isNode } from '../util/DomUtils'; -import CellArray from "./cell/datatypes/CellArray"; -import EdgeStyle from "./style/EdgeStyle"; -import EdgeHandler from "./cell/edge/EdgeHandler"; -import VertexHandler from "./cell/vertex/VertexHandler"; -import EdgeSegmentHandler from "./cell/edge/EdgeSegmentHandler"; -import ElbowEdgeHandler from "./cell/edge/ElbowEdgeHandler"; +import CellArray from './cell/datatypes/CellArray'; +import EdgeStyle from './style/EdgeStyle'; +import EdgeHandler from './cell/edge/EdgeHandler'; +import VertexHandler from './cell/vertex/VertexHandler'; +import EdgeSegmentHandler from './cell/edge/EdgeSegmentHandler'; +import ElbowEdgeHandler from './cell/edge/ElbowEdgeHandler'; /** * 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.cellRenderer = this.createCellRenderer(); this.setSelectionModel(this.createSelectionModel()); - this.setStylesheet( - stylesheet != null ? stylesheet : this.createStylesheet() - ); + this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet()); this.view = this.createGraphView(); // 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 // recreated from scratch, it only needs to be invalidated. - else if ( - change instanceof TerminalChange || - change instanceof GeometryChange - ) { + else if (change instanceof TerminalChange || change instanceof GeometryChange) { // Checks if the geometry has changed to avoid unnessecary revalidation if ( change instanceof TerminalChange || @@ -807,21 +802,13 @@ class Graph extends EventSource { return new Rectangle( parseCssNumber(css.paddingLeft) + - (css.borderLeftStyle != 'none' - ? parseCssNumber(css.borderLeftWidth) - : 0), + (css.borderLeftStyle != 'none' ? parseCssNumber(css.borderLeftWidth) : 0), parseCssNumber(css.paddingTop) + - (css.borderTopStyle != 'none' - ? parseCssNumber(css.borderTopWidth) - : 0), + (css.borderTopStyle != 'none' ? parseCssNumber(css.borderTopWidth) : 0), parseCssNumber(css.paddingRight) + - (css.borderRightStyle != 'none' - ? parseCssNumber(css.borderRightWidth) - : 0), + (css.borderRightStyle != 'none' ? parseCssNumber(css.borderRightWidth) : 0), parseCssNumber(css.paddingBottom) + - (css.borderBottomStyle != 'none' - ? parseCssNumber(css.borderBottomWidth) - : 0) + (css.borderBottomStyle != 'none' ? parseCssNumber(css.borderBottomWidth) : 0) ); } @@ -909,8 +896,7 @@ class Graph extends EventSource { if (this.container != null) { // Adds spacing and border from css const cssBorder = this.getBorderSizes(); - let w1: number = - this.container.offsetWidth - cssBorder.x - cssBorder.width - 1; + let w1: number = this.container.offsetWidth - cssBorder.x - cssBorder.width - 1; let h1: number = maxHeight != null ? maxHeight @@ -962,19 +948,13 @@ class Graph extends EventSource { const x0 = bounds.x != null ? Math.floor( - this.view.translate.x - - bounds.x / s + - border / s2 + - margin / 2 + this.view.translate.x - bounds.x / s + border / s2 + margin / 2 ) : border; const y0 = bounds.y != null ? Math.floor( - this.view.translate.y - - bounds.y / s + - border / s2 + - margin / 2 + this.view.translate.y - bounds.y / s + border / s2 + margin / 2 ) : border; @@ -1026,9 +1006,7 @@ class Graph extends EventSource { * * @param state {@link mxCellState} whose handler should be created. */ - createHandler( - state: CellState - ): mxEdgeHandler | VertexHandler | null { + createHandler(state: CellState): mxEdgeHandler | VertexHandler | null { let result: mxEdgeHandler | VertexHandler | null = null; if (state.cell.isEdge()) { @@ -1242,7 +1220,6 @@ class Graph extends EventSource { cx: number = 0.5, cy: number = 0.5 ): void { - const container = this.container; const _hasScrollbars = hasScrollbars(this.container); const padding = 2 * this.getBorder(); diff --git a/packages/core/src/view/Outline.ts b/packages/core/src/view/Outline.ts index cc016c8cb..775db88d2 100644 --- a/packages/core/src/view/Outline.ts +++ b/packages/core/src/view/Outline.ts @@ -19,10 +19,10 @@ import graph from './Graph'; import ImageShape from './geometry/shape/node/ImageShape'; import InternalEvent from './event/InternalEvent'; import utils from '../util/Utils'; -import Image from './image/Image'; +import Image from './image/ImageBox'; import EventObject from './event/EventObject'; import { getSource, isMouseEvent } from '../util/EventUtils'; -import EventSource from "./event/EventSource"; +import EventSource from './event/EventSource'; /** * @class Outline @@ -510,10 +510,8 @@ class Outline { ); // Adds the scrollbar offset to the finder - this.bounds.x += - (this.source.container.scrollLeft * navView.scale) / scale; - this.bounds.y += - (this.source.container.scrollTop * navView.scale) / scale; + this.bounds.x += (this.source.container.scrollLeft * navView.scale) / scale; + this.bounds.y += (this.source.container.scrollTop * navView.scale) / scale; const selectionBorder = this.selectionBorder; let b = selectionBorder.bounds; @@ -567,12 +565,7 @@ class Outline { const tol = !isMouseEvent(me.getEvent()) ? this.source.tolerance : 0; const hit = tol > 0 - ? new Rectangle( - me.getGraphX() - tol, - me.getGraphY() - tol, - 2 * tol, - 2 * tol - ) + ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null; this.zoom = me.isSource(this.sizer) || @@ -691,10 +684,7 @@ class Outline { * ``` */ getTranslateForEvent(me: InternalMouseEvent): Point { - return new Point( - me.getX() - this.startX, - me.getY() - this.startY - ); + return new Point(me.getX() - this.startX, me.getY() - this.startY); } /** @@ -713,10 +703,7 @@ class Outline { if (!this.zoom) { // Applies the new translation if the source // has no scrollbars - if ( - !source.useScrollbarsForPanning || - !utils.hasScrollbars(source.container) - ) { + if (!source.useScrollbarsForPanning || !utils.hasScrollbars(source.container)) { source.panGraph(0, 0); dx /= outline.getView().scale; dy /= outline.getView().scale; @@ -727,10 +714,7 @@ class Outline { // Applies the new zoom const w = (selectionBorder.bounds).width; const { scale } = source.getView(); - source.zoomTo( - Math.max(this.minScale, scale - (dx * scale) / w), - false - ); + source.zoomTo(Math.max(this.minScale, scale - (dx * scale) / w), false); } this.update(); @@ -752,11 +736,7 @@ class Outline { this.source.removeListener(this.refreshHandler); this.source.getModel().removeListener(this.updateHandler); this.source.getView().removeListener(this.updateHandler); - InternalEvent.removeListener( - this.source.container, - 'scroll', - this.updateHandler - ); + InternalEvent.removeListener(this.source.container, 'scroll', this.updateHandler); // @ts-ignore this.source = null; } diff --git a/packages/core/src/view/cell/CellMarker.ts b/packages/core/src/view/cell/CellMarker.ts index a0916df00..e6a7a8b81 100644 --- a/packages/core/src/view/cell/CellMarker.ts +++ b/packages/core/src/view/cell/CellMarker.ts @@ -15,7 +15,7 @@ import { import CellHighlight from '../selection/CellHighlight'; import EventObject from '../event/EventObject'; import InternalEvent from '../event/InternalEvent'; -import utils, { intersectsHotspot } from '../../util/Utils'; +import utils, { intersectsHotspot, isNumeric } from '../../util/Utils'; import graph from '../Graph'; import { ColorValue } from '../../types'; import CellState from './datatypes/CellState'; @@ -274,9 +274,9 @@ class CellMarker extends EventSource { * Sets and marks the current valid state. */ setCurrentState( - state: CellState, + state: CellState | null, me: InternalMouseEvent, - color: ColorValue = null + color: ColorValue | null = null ) { const isValid = state ? this.isValidState(state) : false; 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 if no color is specified. */ - markCell(cell: Cell, - color: ColorValue) { + markCell(cell: Cell, color: ColorValue) { const state = this.graph.getView().getState(cell); if (state) { @@ -353,9 +352,7 @@ class CellMarker extends EventSource { * Returns the valid- or invalidColor depending on the value of isValid. * The given is ignored by this implementation. */ - getMarkerColor(evt: Event, - state: CellState, - isValid: boolean): string { + getMarkerColor(evt: Event, state: CellState | null, isValid: boolean) { return isValid ? this.validColor : this.invalidColor; } @@ -379,7 +376,7 @@ class CellMarker extends EventSource { * Returns the for the given event and cell. This returns the * given cell. */ - getCell(me: InternalMouseEvent): Cell { + getCell(me: InternalMouseEvent) { return me.getCell(); } @@ -400,17 +397,21 @@ class CellMarker extends EventSource { * This returns true if the is 0 or the coordinates are inside * the hotspot for the given cell state. */ - intersects(state: CellState, me: InternalMouseEvent): boolean { - if (this.hotspotEnabled) { + intersects(state: CellState, me: InternalMouseEvent) { + const x = me.getGraphX(); + const y = me.getGraphY(); + + if (this.hotspotEnabled && isNumeric(x) && isNumeric(y)) { return intersectsHotspot( state, - me.getGraphX(), - me.getGraphY(), + x as number, + y as number, this.hotspot, MIN_HOTSPOT_SIZE, MAX_HOTSPOT_SIZE ); } + return true; } @@ -419,9 +420,7 @@ class CellMarker extends EventSource { * * Destroys the handler and all its resources and DOM nodes. */ - destroy(): void { - this.graph.getView().removeListener(this.resetHandler); - this.graph.getModel().removeListener(this.resetHandler); + destroy() { this.highlight.destroy(); } } diff --git a/packages/core/src/view/cell/CellOverlay.ts b/packages/core/src/view/cell/CellOverlay.ts index 62f63ef08..1de22533c 100644 --- a/packages/core/src/view/cell/CellOverlay.ts +++ b/packages/core/src/view/cell/CellOverlay.ts @@ -8,8 +8,8 @@ import Point from '../geometry/Point'; import Rectangle from '../geometry/Rectangle'; import EventSource from '../event/EventSource'; -import Image from '../image/Image'; -import CellState from "./datatypes/CellState"; +import ImageBox from '../image/ImageBox'; +import CellState from './datatypes/CellState'; /** * Class: mxCellOverlay @@ -66,12 +66,14 @@ import CellState from "./datatypes/CellState"; * (default). */ class CellOverlay extends EventSource { - constructor(image: Image, - tooltip: string | null=null, - align: string='right', - verticalAlign: string='bottom', - offset: Point=new Point(), - cursor: string='help') { + constructor( + image: ImageBox, + tooltip: string | null = null, + align: string = 'right', + verticalAlign: string = 'bottom', + offset: Point = new Point(), + cursor: string = 'help' + ) { super(); this.image = image; @@ -85,14 +87,14 @@ class CellOverlay extends EventSource { * * Holds the to be used as the icon. */ - image: Image | null = null; + image: ImageBox; /** * Variable: tooltip * * Holds the optional string to be used as the tooltip. */ - tooltip?: string | null = null; + tooltip?: string | null; /** * Variable: align @@ -118,14 +120,14 @@ class CellOverlay extends EventSource { * Holds the offset as an . The offset will be scaled according to the * current scale. */ - offset: Point = new Point(); + offset = new Point(); /** * Variable: cursor * * Holds the cursor for the overlay. Default is 'help'. */ - cursor: string = 'help'; + cursor = 'help'; /** * Variable: defaultOverlap @@ -133,7 +135,7 @@ class CellOverlay extends EventSource { * 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. */ - defaultOverlap: number = 0.5; + defaultOverlap = 0.5; /** * Function: getBounds @@ -173,7 +175,7 @@ class CellOverlay extends EventSource { const s = state.view.scale; let pt = null; - const image = this.image; + const image = this.image; const w = image.width; const h = image.height; diff --git a/packages/core/src/view/cell/CellRenderer.ts b/packages/core/src/view/cell/CellRenderer.ts index 729b4550f..a488cf324 100644 --- a/packages/core/src/view/cell/CellRenderer.ts +++ b/packages/core/src/view/cell/CellRenderer.ts @@ -286,7 +286,7 @@ class CellRenderer { if (shape) { shape.apply(state); - shape.image = state.getImage(); + shape.imageSrc = state.getImage(); shape.indicatorColor = state.getIndicatorColor(); shape.indicatorStrokeColor = state.style.indicatorStrokeColor; shape.indicatorGradientColor = state.getIndicatorGradientColor(); @@ -371,11 +371,7 @@ class CellRenderer { } else if (value === 'indicated' && state.shape != null) { // @ts-ignore shape[field] = state.shape.indicatorColor; - } else if ( - key !== 'fillColor' && - value === 'fillColor' && - state.shape != null - ) { + } else if (key !== 'fillColor' && value === 'fillColor' && state.shape != null) { // @ts-ignore shape[field] = state.style.fillColor; } else if ( @@ -465,9 +461,7 @@ class CellRenderer { ); state.text.opacity = state.style.textOpacity == null ? 100 : state.style.textOpacity; - state.text.dialect = isForceHtml - ? DIALECT_STRICTHTML - : state.view.graph.dialect; + state.text.dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect; state.text.style = state.style; state.text.state = state; this.initializeLabel(state, state.text); @@ -489,9 +483,7 @@ class CellRenderer { // Dispatches the drop event to the graph which // consumes and executes the source function const pt = convertPoint(graph.container, x, y); - result = ( - graph.view.getState(graph.getCellAt(pt.x, pt.y)) - ); + result = graph.view.getState(graph.getCellAt(pt.x, pt.y)); } return result; }; @@ -506,8 +498,7 @@ class CellRenderer { new InternalMouseEvent(evt, state) ); forceGetCell = - graph.dialect !== DIALECT_SVG && - getSource(evt).nodeName === 'IMG'; + graph.dialect !== DIALECT_SVG && getSource(evt).nodeName === 'IMG'; } }, (evt: MouseEvent) => { @@ -576,8 +567,7 @@ class CellRenderer { dict = new Dictionary(); for (let i = 0; i < overlays.length; i += 1) { - const shape = - state.overlays != null ? state.overlays.remove(overlays[i]) : null; + const shape = state.overlays != null ? state.overlays.remove(overlays[i]) : null; if (shape == null) { const tmp = new ImageShape( @@ -645,7 +635,7 @@ class CellRenderer { } 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); }, (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) { InternalEvent.addListener(shape.node, 'touchend', (evt: Event) => { 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 // it is obscured by the HTML label that overlaps the cell. const isForceHtml = - graph.isHtmlLabel(state.cell) && - mxClient.NO_FO && - graph.dialect === DIALECT_SVG; + graph.isHtmlLabel(state.cell) && mxClient.NO_FO && graph.dialect === DIALECT_SVG; if (isForceHtml) { control.dialect = DIALECT_PREFERHTML; @@ -791,7 +782,10 @@ class CellRenderer { ); }, (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); } ); @@ -833,8 +827,7 @@ class CellRenderer { * state - whose shape fired the event. * evt - Mouse event which was fired. */ - isShapeEvent(state: CellState, - evt: InternalMouseEvent | MouseEvent): boolean { + isShapeEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean { return true; } @@ -849,8 +842,7 @@ class CellRenderer { * state - whose label fired the event. * evt - Mouse event which was fired. */ - isLabelEvent(state: CellState, - evt: InternalMouseEvent | MouseEvent): boolean { + isLabelEvent(state: CellState, evt: InternalMouseEvent | MouseEvent): boolean { return true; } @@ -938,15 +930,13 @@ class CellRenderer { * * state - whose label should be redrawn. */ - redrawLabel(state: CellState, - forced: boolean): void { + redrawLabel(state: CellState, forced: boolean): void { const { graph } = state.view; const value = this.getLabelValue(state); const wrapping = graph.isWrapping(state.cell); const clipping = graph.isLabelClipped(state.cell); const isForceHtml = - state.view.graph.isHtmlLabel(state.cell) || - (value != null && isNode(value)); + state.view.graph.isHtmlLabel(state.cell) || (value != null && isNode(value)); const dialect = isForceHtml ? DIALECT_STRICTHTML : state.view.graph.dialect; const overflow = state.style.overflow || 'visible'; @@ -961,11 +951,7 @@ class CellRenderer { state.text = null; } - if ( - state.text == null && - value != null && - (isNode(value) || value.length > 0) - ) { + if (state.text == null && value != null && (isNode(value) || value.length > 0)) { this.createLabel(state, value); } else if (state.text != null && (value == null || value.length == 0)) { state.text.destroy(); @@ -977,10 +963,7 @@ class CellRenderer { // result in getLabelBounds we apply the new style to the shape if (forced) { // Checks if a full repaint is needed - if ( - state.text.lastValue != null && - this.isTextShapeInvalid(state, state.text) - ) { + if (state.text.lastValue != null && this.isTextShapeInvalid(state, state.text)) { // Forces a full repaint state.text.lastValue = null; } @@ -1035,8 +1018,7 @@ class CellRenderer { * state - whose label should be checked. * shape - shape to be checked. */ - isTextShapeInvalid(state: CellState, - shape: TextShape): boolean { + isTextShapeInvalid(state: CellState, shape: TextShape): boolean { function check(property: string, stylename: string, defaultValue: any) { let result = false; @@ -1049,8 +1031,7 @@ class CellRenderer { ) { result = // @ts-ignore - parseFloat(String(shape[property])) - - parseFloat(String(shape.spacing)) !== + parseFloat(String(shape[property])) - parseFloat(String(shape.spacing)) !== (state.style[stylename] || defaultValue); } else { // @ts-ignore @@ -1119,10 +1100,7 @@ class CellRenderer { const { graph } = state.view; const { scale } = state.view; const isEdge = state.cell.isEdge(); - let bounds = new Rectangle( - state.absoluteOffset.x, - state.absoluteOffset.y - ); + let bounds = new Rectangle(state.absoluteOffset.x, state.absoluteOffset.y); if (isEdge) { // @ts-ignore @@ -1273,8 +1251,7 @@ class CellRenderer { * * state - whose overlays should be redrawn. */ - redrawCellOverlays(state: CellState, - forced: boolean = false): void { + redrawCellOverlays(state: CellState, forced: boolean = false): void { this.createCellOverlays(state); if (state.overlays != null) { @@ -1329,8 +1306,7 @@ class CellRenderer { * * state - whose control should be redrawn. */ - redrawControl(state: CellState, - forced: boolean = false): void { + redrawControl(state: CellState, forced: boolean = false): void { const image = state.view.graph.getFoldingImage(state); 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 * given state. */ - getControlBounds( - state: CellState, - w: number, - h: number - ): Rectangle | null { + getControlBounds(state: CellState, w: number, h: number): Rectangle | null { if (state.control != null) { const s = state.view.scale; let cx = state.getCenterX(); @@ -1471,10 +1443,7 @@ class CellRenderer { if (shapeNode.parentNode === state.view.graph.container) { let { canvas } = state.view; - while ( - canvas != null && - canvas.parentNode !== state.view.graph.container - ) { + while (canvas != null && canvas.parentNode !== state.view.graph.container) { // @ts-ignore canvas = canvas.parentNode; } @@ -1482,10 +1451,7 @@ class CellRenderer { if (canvas != null && canvas.nextSibling != null) { if (canvas.nextSibling !== shapeNode) { // @ts-ignore - shapeNode.parentNode.insertBefore( - shapeNode, - canvas.nextSibling - ); + shapeNode.parentNode.insertBefore(shapeNode, canvas.nextSibling); } } else { // @ts-ignore @@ -1497,10 +1463,7 @@ class CellRenderer { shapeNode.parentNode.firstChild != shapeNode ) { // Inserts the node as the first child of the parent to implement the order - shapeNode.parentNode.insertBefore( - shapeNode, - shapeNode.parentNode.firstChild - ); + shapeNode.parentNode.insertBefore(shapeNode, shapeNode.parentNode.firstChild); } } @@ -1527,9 +1490,7 @@ class CellRenderer { * * state - whose shapes should be returned. */ - getShapesForState( - state: CellState - ): [Shape | null, TextShape | null, Shape | null] { + getShapesForState(state: CellState): [Shape | null, TextShape | null, Shape | null] { 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 * will not be called on the shape. */ - redraw( - state: CellState, - force: boolean = false, - rendering: boolean = true - ): void { + redraw(state: CellState, force: boolean = false, rendering: boolean = true): void { const shapeChanged = this.redrawShape(state, force, rendering); if (state.shape != null && rendering) { @@ -1628,8 +1585,7 @@ class CellRenderer { // Updates indicator shape if ( state.shape != null && - state.shape.indicatorShape != - this.getShape(state.getIndicatorShape()) + state.shape.indicatorShape != this.getShape(state.getIndicatorShape()) ) { if (state.shape.indicator != null) { state.shape.indicator.destroy(); @@ -1658,12 +1614,7 @@ class CellRenderer { state.shape.bounds = null; } else { state.shape.points = null; - state.shape.bounds = new Rectangle( - state.x, - state.y, - state.width, - state.height - ); + state.shape.bounds = new Rectangle(state.x, state.y, state.width, state.height); } state.shape.scale = state.view.scale; @@ -1700,8 +1651,7 @@ class CellRenderer { shape.bounds == null || shape.scale !== state.view.scale || (state.absolutePoints == null && !shape.bounds.equals(state)) || - (state.absolutePoints != null && - !equalPoints(shape.points, state.absolutePoints)) + (state.absolutePoints != null && !equalPoints(shape.points, state.absolutePoints)) ); } diff --git a/packages/core/src/view/cell/TemporaryCellStates.ts b/packages/core/src/view/cell/TemporaryCellStates.ts index 63a33a263..6e6fdf3ac 100644 --- a/packages/core/src/view/cell/TemporaryCellStates.ts +++ b/packages/core/src/view/cell/TemporaryCellStates.ts @@ -13,16 +13,17 @@ import Dictionary from '../../util/Dictionary'; import GraphView from '../view/GraphView'; import Cell from './datatypes/Cell'; import CellState from './datatypes/CellState'; -import Shape from "../geometry/shape/Shape"; -import graph from "../Graph"; +import Shape from '../geometry/shape/Shape'; +import graph from '../Graph'; +import CellArray from './datatypes/CellArray'; class TemporaryCellStates { constructor( - view: GraphView, - scale: number = 1, - cells: Cell[], - isCellVisibleFn: Function | null = null, - getLinkForCellState: Function | null = null + view: GraphView, + scale: number = 1, + cells: CellArray, + isCellVisibleFn: Function | null = null, + getLinkForCellState: Function | null = null ) { this.view = view; @@ -31,17 +32,17 @@ class TemporaryCellStates { this.oldBounds = view.getGraphBounds(); this.oldStates = view.getStates(); this.oldScale = view.getScale(); - this.oldDoRedrawShape = (view.graph).cellRenderer.doRedrawShape; + this.oldDoRedrawShape = view.graph.cellRenderer.doRedrawShape; const self = this; // Overrides doRedrawShape and paint shape to add links on shapes if (getLinkForCellState != null) { - (view.graph).cellRenderer.doRedrawShape = (state: CellState) => { + view.graph.cellRenderer.doRedrawShape = (state: CellState) => { const shape = state?.shape; const oldPaint = shape.paint; - shape.paint = c => { + shape.paint = (c) => { const link = getLinkForCellState(state); if (link != null) { c.setLink(link); @@ -52,7 +53,7 @@ class TemporaryCellStates { } }; - (self.oldDoRedrawShape).apply((view.graph).cellRenderer, [state]); + (self.oldDoRedrawShape).apply(view.graph.cellRenderer, [state]); shape.paint = oldPaint; }; } @@ -66,70 +67,53 @@ class TemporaryCellStates { }; // Creates space for new states - view.setStates(new mxDictionary()); + view.setStates(new Dictionary()); view.setScale(scale); - if (cells != null) { - view.resetValidationState(); - let bbox = null; + view.resetValidationState(); + let bbox = null; - // Validates the vertices and edges without adding them to - // the model so that the original cells are not modified - for (const cell of cells) { - const bounds = view.getBoundingBox( - view.validateCellState(view.validateCell(cell)) - ); - if (bbox == null) { - bbox = bounds; - } else { - bbox.add(bounds); - } + // Validates the vertices and edges without adding them to + // the model so that the original cells are not modified + for (const cell of cells) { + const bounds = view.getBoundingBox( + view.validateCellState(view.validateCell(cell)) + ); + if (bbox == null) { + bbox = bounds; + } else { + bbox.add(bounds); } - view.setGraphBounds(bbox || new Rectangle()); } + view.setGraphBounds(bbox || new Rectangle()); } oldValidateCellState: Function | null; oldDoRedrawShape: Function | null; - /** - * Holds the width of the rectangle. - * @default 0 - */ - // view: number; - view: GraphView | null = null; + view: GraphView; /** - * Holds the height of the rectangle. - * @default 0 + * Holds the states of the rectangle. */ - // oldStates: number; - oldStates: Dictionary | null = null; + oldStates: Dictionary; /** - * Holds the height of the rectangle. - * @default 0 + * Holds the bounds of the rectangle. */ - // oldBounds: number; - oldBounds: Rectangle | null = null; + oldBounds: Rectangle; /** - * Holds the height of the rectangle. - * @default 0 + * Holds the scale of the rectangle. */ - oldScale: number = 0; + oldScale: number; - /** - * Holds the height of the rectangle. - * @default 0 - */ - // destroy(): void; destroy(): void { - const view = this.view; + const view = this.view; view.setScale(this.oldScale); view.setStates(this.oldStates); - view.setGraphBounds(this.oldBounds); + view.setGraphBounds(this.oldBounds); // @ts-ignore view.validateCellState = this.oldValidateCellState; // @ts-ignore diff --git a/packages/core/src/view/cell/datatypes/CellArray.ts b/packages/core/src/view/cell/datatypes/CellArray.ts index 2a646b123..05219d533 100644 --- a/packages/core/src/view/cell/datatypes/CellArray.ts +++ b/packages/core/src/view/cell/datatypes/CellArray.ts @@ -1,6 +1,6 @@ -import Cell from "./Cell"; -import Dictionary from "../../../util/Dictionary"; -import mxObjectIdentity from "../../../util/mxObjectIdentity"; +import Cell from './Cell'; +import Dictionary from '../../../util/Dictionary'; +import ObjectIdentity from '../../../util/ObjectIdentity'; class CellArray extends Array { // @ts-ignore @@ -25,12 +25,12 @@ class CellArray extends Array { // @ts-ignore map(arg0: any, ...args: any): CellArray { - return new CellArray(...super.map(arg0, ...args)); + return new CellArray(...(super.map(arg0, ...args))); } // @ts-ignore filter(arg0: any, ...args: any): CellArray { - return new CellArray(...super.filter(arg0, ...args)); + return new CellArray(...(super.filter(arg0, ...args))); } /** @@ -59,10 +59,11 @@ class CellArray extends Array { * @param targets Boolean that specifies if target terminals should be contained * in the result. Default is true. */ - getOpposites(terminal: Cell, - sources: boolean=true, - targets: boolean=true): CellArray { - + getOpposites( + terminal: Cell, + sources: boolean = true, + targets: boolean = true + ): CellArray { const terminals = new CellArray(); for (let i = 0; i < this.length; i += 1) { @@ -72,24 +73,14 @@ class CellArray extends Array { // Checks if the terminal is the source of // the edge and if the target should be // stored in the result - if ( - source === terminal && - target != null && - target !== terminal && - targets - ) { + if (source === terminal && target != null && target !== terminal && targets) { terminals.push(target); } - // Checks if the terminal is the taget of - // the edge and if the source should be + // Checks if the terminal is the taget of + // the edge and if the source should be // stored in the result - else if ( - target === terminal && - source != null && - source !== terminal && - sources - ) { + else if (target === terminal && source != null && source !== terminal && sources) { terminals.push(source); } } @@ -157,9 +148,7 @@ class CellArray extends Array { * with all descendants. * @param mapping Optional mapping for existing clones. */ - cloneCells(includeChildren: boolean=true, - mapping: any={}): CellArray { - + cloneCells(includeChildren: boolean = true, mapping: any = {}): CellArray { const clones: CellArray = new CellArray(); for (const cell of this) { @@ -179,11 +168,8 @@ class CellArray extends Array { * * @private */ - cloneCellImpl(cell: Cell, - mapping: any={}, - includeChildren: boolean): Cell { - - const ident = mxObjectIdentity.get(cell); + cloneCellImpl(cell: Cell, mapping: any = {}, includeChildren: boolean): Cell { + const ident = ObjectIdentity.get(cell); let clone = mapping ? mapping[ident] : null; if (clone == null) { @@ -194,11 +180,7 @@ class CellArray extends Array { const childCount = cell.getChildCount(); for (let i = 0; i < childCount; i += 1) { - const cloneChild = this.cloneCellImpl( - cell.getChildAt(i), - mapping, - true - ); + const cloneChild = this.cloneCellImpl(cell.getChildAt(i), mapping, true); clone.insert(cloneChild); } } @@ -212,14 +194,11 @@ class CellArray extends Array { * * @private */ - restoreClone(clone: Cell, - cell: Cell, - mapping: any): void { - + restoreClone(clone: Cell, cell: Cell, mapping: any): void { const source = cell.getTerminal(true); if (source != null) { - const tmp = mapping[mxObjectIdentity.get(source)]; + const tmp = mapping[ObjectIdentity.get(source)]; if (tmp != null) { tmp.insertEdge(clone, true); } @@ -227,7 +206,7 @@ class CellArray extends Array { const target = cell.getTerminal(false); if (target != null) { - const tmp = mapping[mxObjectIdentity.get(target)]; + const tmp = mapping[ObjectIdentity.get(target)]; if (tmp != null) { tmp.insertEdge(clone, false); } @@ -235,11 +214,7 @@ class CellArray extends Array { const childCount = clone.getChildCount(); for (let i = 0; i < childCount; i += 1) { - this.restoreClone( - clone.getChildAt(i), - cell.getChildAt(i), - mapping - ); + this.restoreClone(clone.getChildAt(i), cell.getChildAt(i), mapping); } } } diff --git a/packages/core/src/view/cell/datatypes/CellState.ts b/packages/core/src/view/cell/datatypes/CellState.ts index 545f93981..e1f045b32 100644 --- a/packages/core/src/view/cell/datatypes/CellState.ts +++ b/packages/core/src/view/cell/datatypes/CellState.ts @@ -12,11 +12,9 @@ import GraphView from '../../view/GraphView'; import Shape from '../../geometry/shape/Shape'; import TextShape from '../../geometry/shape/node/TextShape'; import Dictionary from '../../../util/Dictionary'; +import { NONE } from '../../../util/Constants'; import type { CellStateStyles } from '../../../types'; -import Image from "../../image/Image"; -import {ALIGN_MIDDLE, NONE} from "../../../util/Constants"; -import {getValue} from "../../../util/Utils"; /** * Class: mxCellState @@ -210,20 +208,11 @@ class CellState extends Rectangle { */ getPerimeterBounds( border: number = 0, - bounds: Rectangle = new Rectangle( - this.x, - this.y, - this.width, - this.height - ) + bounds: Rectangle = new Rectangle(this.x, this.y, this.width, this.height) ) { - if ( - this.shape && - this.shape.stencil && - this.shape.stencil.aspect === 'fixed' - ) { + if (this.shape?.stencil?.aspect === 'fixed') { const aspect = this.shape.stencil.computeAspect( - this.style, + this.shape, bounds.x, bounds.y, bounds.width, @@ -455,6 +444,7 @@ class CellState extends Rectangle { isLoop(state: CellState) { const src = this.getVisibleTerminalState(true); const trg = this.getVisibleTerminalState(false); + return src && src === trg; } @@ -470,10 +460,8 @@ class CellState extends Rectangle { * @param state {@link mxCellState} whose vertical alignment should be * returned. */ - getVerticalAlign(): string | null { - return this.style != null - ? this.style.verticalAlign || ALIGN_MIDDLE - : null; + getVerticalAlign() { + return this.style.verticalAlign; } /** @@ -481,11 +469,14 @@ class CellState extends Rectangle { * * @param state {@link mxCellState} to check. */ - isTransparentState(): boolean { + isTransparentState() { let result = false; - const stroke = getValue(this.style, 'strokeColor', NONE); - const fill = getValue(this.style, 'fillColor', NONE); - result = stroke === NONE && fill === NONE && this.getImage(state) == null; + + const stroke = this.style.strokeColor; + const fill = this.style.fillColor; + + result = stroke === NONE && fill === NONE && !this.getImageSrc(); + return result; } @@ -496,10 +487,8 @@ class CellState extends Rectangle { * * @param state {@link mxCellState} whose image URL should be returned. */ - getImage(): Image | null { - return this.style != null - ? this.style.image - : null; + getImageSrc() { + return this.style.image; } /** @@ -510,10 +499,8 @@ class CellState extends Rectangle { * @param state {@link mxCellState} whose indicator color should be * returned. */ - getIndicatorColor(): string | null { - return this.style != null - ? this.style.indicatorColor - : null; + getIndicatorColor() { + return this.style.indicatorColor; } /** @@ -524,10 +511,8 @@ class CellState extends Rectangle { * @param state {@link mxCellState} whose indicator gradient color should be * returned. */ - getIndicatorGradientColor(): string | null { - return this.style != null - ? this.style.gradientColor - : null; + getIndicatorGradientColor() { + return this.style.gradientColor; } /** @@ -537,10 +522,8 @@ class CellState extends Rectangle { * * @param state {@link mxCellState} whose indicator shape should be returned. */ - getIndicatorShape(): string | null { - return this.style != null - ? this.style.indicatorShape - : null; + getIndicatorShape() { + return this.style.indicatorShape; } /** @@ -550,10 +533,8 @@ class CellState extends Rectangle { * * @param state {@link mxCellState} whose indicator image should be returned. */ - getIndicatorImage(): Image | null { - return this.style != null - ? this.style.indicatorImage - : null; + getIndicatorImageSrc() { + return this.style.indicatorImage; } } diff --git a/packages/core/src/view/cell/edge/EdgeHandler.ts b/packages/core/src/view/cell/edge/EdgeHandler.ts index 93b63589e..fb8d67d7b 100644 --- a/packages/core/src/view/cell/edge/EdgeHandler.ts +++ b/packages/core/src/view/cell/edge/EdgeHandler.ts @@ -77,11 +77,7 @@ class EdgeHandler { this.reset(); if (dirty) { - this.graph.cellRenderer.redraw( - this.state, - false, - state.view.isRendering() - ); + this.graph.cellRenderer.redraw(this.state, false, state.view.isRendering()); } }; @@ -329,10 +325,7 @@ class EdgeHandler { this.parentHighlight.redraw(); } } else { - if ( - pstate != null && - pstate.parentHighlight === this.parentHighlight - ) { + if (pstate != null && pstate.parentHighlight === this.parentHighlight) { pstate.parentHighlight = null; } @@ -340,18 +333,12 @@ class EdgeHandler { this.parentHighlight = null; } } else if (this.parentHighlightEnabled && visible) { - if ( - parent.isVertex() && - pstate != null && - pstate.parentHighlight == null - ) { + if (parent.isVertex() && pstate != null && pstate.parentHighlight == null) { this.parentHighlight = this.createParentHighlightShape(pstate); // VML dialect required here for event transparency in IE this.parentHighlight.dialect = DIALECT_SVG; this.parentHighlight.pointerEvents = false; - this.parentHighlight.rotation = Number( - pstate.style.rotation || '0' - ); + this.parentHighlight.rotation = Number(pstate.style.rotation || '0'); this.parentHighlight.init(this.graph.getView().getOverlayPane()); this.parentHighlight.redraw(); @@ -390,8 +377,7 @@ class EdgeHandler { // Updates preferHtml this.preferHtml = - this.state.text != null && - this.state.text.node.parentNode === this.graph.container; + this.state.text != null && this.state.text.node.parentNode === this.graph.container; if (!this.preferHtml) { // Checks source terminal @@ -429,10 +415,7 @@ class EdgeHandler { } // Adds a rectangular handle for the label position - this.label = new Point( - this.state.absoluteOffset.x, - this.state.absoluteOffset.y - ); + this.label = new Point(this.state.absoluteOffset.x, this.state.absoluteOffset.y); this.labelShape = this.createLabelHandleShape(); this.initBend(this.labelShape); this.labelShape.setCursor(CURSOR_LABEL_HANDLE); @@ -525,7 +508,7 @@ class EdgeHandler { null, this.getSelectionColor() ); - shape.strokewidth = this.getSelectionStrokeWidth(); + shape.strokeWidth = this.getSelectionStrokeWidth(); shape.isDashed = this.isSelectionDashed(); return shape; @@ -616,10 +599,7 @@ class EdgeHandler { let cell = super.getCell(me); // Checks for cell at preview point (with grid) - if ( - (cell === self.state.cell || cell == null) && - self.currentPoint != null - ) { + if ((cell === self.state.cell || cell == null) && self.currentPoint != null) { cell = self.graph.getCellAt(self.currentPoint.x, self.currentPoint.y); } @@ -722,9 +702,7 @@ class EdgeHandler { }); if (this.isHandleEnabled(i)) { - bend.setCursor( - terminal ? CURSOR_TERMINAL_HANDLE : CURSOR_BEND_HANDLE - ); + bend.setCursor(terminal ? CURSOR_TERMINAL_HANDLE : CURSOR_BEND_HANDLE); } bends.push(bend); @@ -842,12 +820,7 @@ class EdgeHandler { createLabelHandleShape() { if (this.labelHandleImage != null) { const shape = new ImageShape( - new Rectangle( - 0, - 0, - this.labelHandleImage.width, - this.labelHandleImage.height - ), + new Rectangle(0, 0, this.labelHandleImage.width, this.labelHandleImage.height), this.labelHandleImage.src ); @@ -879,8 +852,7 @@ class EdgeHandler { bend.dialect = DIALECT_STRICTHTML; bend.init(this.graph.container); } else { - bend.dialect = - this.graph.dialect !== DIALECT_SVG ? DIALECT_MIXEDHTML : DIALECT_SVG; + bend.dialect = this.graph.dialect !== DIALECT_SVG ? DIALECT_MIXEDHTML : DIALECT_SVG; bend.init(this.graph.getView().getOverlayPane()); } @@ -913,12 +885,7 @@ class EdgeHandler { const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1; const hit = this.allowHandleBoundsCheck && tol > 0 - ? new Rectangle( - me.getGraphX() - tol, - me.getGraphY() - tol, - 2 * tol, - 2 * tol - ) + ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null; let minDistSq = null; @@ -928,8 +895,7 @@ class EdgeHandler { shape.node != null && shape.node.style.display !== 'none' && shape.node.style.visibility !== 'hidden' && - (me.isSource(shape) || - (hit != null && utils.intersects(shape.bounds, hit))) + (me.isSource(shape) || (hit != null && utils.intersects(shape.bounds, hit))) ) { const dx = me.getGraphX() - shape.bounds.getCenterX(); const dy = me.getGraphY() - shape.bounds.getCenterY(); @@ -1019,11 +985,7 @@ class EdgeHandler { this.snapPoint = new Point(b.getCenterX(), b.getCenterY()); } - if ( - this.addEnabled && - handle == null && - this.isAddPointEvent(me.getEvent()) - ) { + if (this.addEnabled && handle == null && this.isAddPointEvent(me.getEvent())) { this.addPoint(this.state, me.getEvent()); me.consume(); } else if (handle != null && !me.isConsumed() && this.graph.isEnabled()) { @@ -1058,8 +1020,7 @@ class EdgeHandler { this.startY = y; this.isSource = this.bends == null ? false : index === 0; - this.isTarget = - this.bends == null ? false : index === this.bends.length - 1; + this.isTarget = this.bends == null ? false : index === this.bends.length - 1; this.isLabel = index === InternalEvent.LABEL_HANDLE; if (this.isSource || this.isTarget) { @@ -1067,8 +1028,7 @@ class EdgeHandler { const terminal = cell.getTerminal(this.isSource); if ( - (terminal == null && - this.graph.isTerminalPointMovable(cell, this.isSource)) || + (terminal == null && this.graph.isTerminalPointMovable(cell, this.isSource)) || (terminal != null && this.graph.isCellDisconnectable(cell, terminal, this.isSource)) ) { @@ -1189,10 +1149,7 @@ class EdgeHandler { const snapToTerminal = (terminal) => { if (terminal != null) { snapToPoint( - new point( - view.getRoutingCenterX(terminal), - view.getRoutingCenterY(terminal) - ) + new point(view.getRoutingCenterX(terminal), view.getRoutingCenterY(terminal)) ); } }; @@ -1245,8 +1202,7 @@ class EdgeHandler { if ( this.marker.highlight != null && this.marker.highlight.state != null && - this.marker.highlight.state.cell === - this.constraintHandler.currentFocus.cell + this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell ) { // Direct repaint needed if cell already highlighted if (this.marker.highlight.shape.stroke !== 'transparent') { @@ -1254,10 +1210,7 @@ class EdgeHandler { this.marker.highlight.repaint(); } } else { - this.marker.markCell( - this.constraintHandler.currentFocus.cell, - 'transparent' - ); + this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent'); } const model = this.graph.getModel(); @@ -1267,12 +1220,8 @@ class EdgeHandler { !this.isSource ); const otherCell = other != null ? other.cell : null; - const source = this.isSource - ? this.constraintHandler.currentFocus.cell - : otherCell; - const target = this.isSource - ? otherCell - : this.constraintHandler.currentFocus.cell; + const source = this.isSource ? this.constraintHandler.currentFocus.cell : otherCell; + const target = this.isSource ? otherCell : this.constraintHandler.currentFocus.cell; // Updates the error message of the handler this.error = this.validateConnection(source, target); @@ -1282,10 +1231,7 @@ class EdgeHandler { result = this.constraintHandler.currentFocus; } - if ( - this.error != null || - (result != null && !this.isCellEnabled(result.cell)) - ) { + if (this.error != null || (result != null && !this.isCellEnabled(result.cell))) { this.constraintHandler.reset(); } @@ -1367,11 +1313,7 @@ class EdgeHandler { const src = this.state.getVisibleTerminalState(true); if (src != null) { - const c = this.graph.getConnectionConstraint( - this.state, - src, - true - ); + const c = this.graph.getConnectionConstraint(this.state, src, true); // Checks if point is not fixed if (c == null || this.graph.getConnectionPoint(src, c) == null) { @@ -1385,11 +1327,7 @@ class EdgeHandler { const trg = this.state.getVisibleTerminalState(false); if (trg != null) { - const c = this.graph.getConnectionConstraint( - this.state, - trg, - false - ); + const c = this.graph.getConnectionConstraint(this.state, trg, false); // Checks if point is not fixed 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 top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); - const gridX = - this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; - const gridY = - this.currentPoint.y - this.graph.container.scrollTop + offset.y - top; + const gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; + const gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top; return ( this.outlineConnect && @@ -1484,16 +1420,8 @@ class EdgeHandler { ? terminalState : this.state.getVisibleTerminalState(false); - let sourceConstraint = this.graph.getConnectionConstraint( - edge, - sourceState, - true - ); - let targetConstraint = this.graph.getConnectionConstraint( - edge, - targetState, - false - ); + let sourceConstraint = this.graph.getConnectionConstraint(edge, sourceState, true); + let targetConstraint = this.graph.getConnectionConstraint(edge, targetState, false); let constraint = this.constraintHandler.currentConstraint; @@ -1528,13 +1456,11 @@ class EdgeHandler { this.marker.highlight.shape.stroke = outline ? OUTLINE_HIGHLIGHT_COLOR : 'transparent'; - this.marker.highlight.shape.strokewidth = - OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s; + this.marker.highlight.shape.strokewidth = OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s; this.marker.highlight.repaint(); } else if (this.marker.hasValidState()) { this.marker.highlight.shape.stroke = - me.getCell().isConnectable() && - this.marker.getValidState() !== me.getState() + me.getCell().isConnectable() && this.marker.getValidState() !== me.getState() ? 'transparent' : DEFAULT_VALID_COLOR; this.marker.highlight.shape.strokewidth = HIGHLIGHT_STROKEWIDTH / s / s; @@ -1550,10 +1476,8 @@ class EdgeHandler { if (this.isSource || this.isTarget) { if (constraint != null && constraint.point != null) { - edge.style[this.isSource ? 'exitX' : 'entryX'] = - constraint.point.x; - edge.style[this.isSource ? 'exitY' : 'entryY'] = - constraint.point.y; + edge.style[this.isSource ? 'exitX' : 'entryX'] = constraint.point.x; + edge.style[this.isSource ? 'exitY' : 'entryY'] = constraint.point.y; } else { delete edge.style[this.isSource ? 'exitX' : 'entryX']; delete edge.style[this.isSource ? 'exitY' : 'entryY']; @@ -1564,21 +1488,11 @@ class EdgeHandler { edge.setVisibleTerminalState(targetState, false); if (!this.isSource || sourceState != null) { - edge.view.updateFixedTerminalPoint( - edge, - sourceState, - true, - sourceConstraint - ); + edge.view.updateFixedTerminalPoint(edge, sourceState, true, sourceConstraint); } if (!this.isTarget || targetState != null) { - edge.view.updateFixedTerminalPoint( - edge, - targetState, - false, - targetConstraint - ); + edge.view.updateFixedTerminalPoint(edge, targetState, false, targetConstraint); } if ((this.isSource || this.isTarget) && terminalState == null) { @@ -1625,12 +1539,8 @@ class EdgeHandler { this.index > InternalEvent.VIRTUAL_HANDLE ) { if (this.customHandles != null) { - this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent( - me - ); - this.customHandles[ - InternalEvent.CUSTOM_HANDLE - this.index - ].positionChanged(); + this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent(me); + this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].positionChanged(); if (this.shape != null && this.shape.node != null) { this.shape.node.style.display = 'none'; @@ -1642,9 +1552,7 @@ class EdgeHandler { } else { this.points = this.getPreviewPoints(this.currentPoint, me); let terminalState = - this.isSource || this.isTarget - ? this.getPreviewTerminalState(me) - : null; + this.isSource || this.isTarget ? this.getPreviewTerminalState(me) : null; if ( this.constraintHandler.currentConstraint != null && @@ -1655,9 +1563,7 @@ class EdgeHandler { } else if (this.outlineConnect) { // Need to check outline before cloning terminal state const outline = - this.isSource || this.isTarget - ? this.isOutlineConnectEvent(me) - : false; + this.isSource || this.isTarget ? this.isOutlineConnectEvent(me) : false; if (outline) { terminalState = this.marker.highlight.state; @@ -1693,9 +1599,7 @@ class EdgeHandler { // Sets the color of the preview to valid or invalid, updates the // points of the preview and redraws const color = - this.error == null - ? this.marker.validColor - : this.marker.invalidColor; + this.error == null ? this.marker.validColor : this.marker.invalidColor; this.setPreviewColor(color); this.abspoints = clone.absolutePoints; this.active = true; @@ -1813,9 +1717,7 @@ class EdgeHandler { model.endUpdate(); } } else if (this.graph.isAllowDanglingEdges()) { - const pt = this.abspoints[ - this.isSource ? 0 : this.abspoints.length - 1 - ]; + const pt = this.abspoints[this.isSource ? 0 : this.abspoints.length - 1]; pt.x = this.roundLength( pt.x / this.graph.view.scale - this.graph.view.translate.x ); @@ -2074,12 +1976,7 @@ class EdgeHandler { geo = geo.clone(); geo.setTerminalPoint(point, isSource); model.setGeometry(edge, geo); - this.graph.connectCell( - edge, - null, - isSource, - new ConnectionConstraint() - ); + this.graph.connectCell(edge, null, isSource, new ConnectionConstraint()); } } finally { model.endUpdate(); @@ -2130,11 +2027,7 @@ class EdgeHandler { */ // addPoint(state: mxCellState, evt: Event): void; addPoint(state, evt) { - const pt = utils.convertPoint( - this.graph.container, - getClientX(evt), - getClientY(evt) - ); + const pt = utils.convertPoint(this.graph.container, getClientX(evt), getClientY(evt)); const gridEnabled = this.graph.isGridEnabledEvent(evt); this.convertPoint(pt, gridEnabled); this.addPointAt(state, pt.x, pt.y); @@ -2215,8 +2108,7 @@ class EdgeHandler { let color = HANDLE_FILLCOLOR; if ( - (terminal != null && - !this.graph.isCellDisconnectable(cell, terminal, isSource)) || + (terminal != null && !this.graph.isCellDisconnectable(cell, terminal, isSource)) || (terminal == null && !this.graph.isTerminalPointMovable(cell, isSource)) ) { color = LOCKED_HANDLE_FILLCOLOR; @@ -2278,10 +2170,7 @@ class EdgeHandler { // Updates the handle for the label position let b = this.labelShape.bounds; - this.label = new Point( - this.state.absoluteOffset.x, - this.state.absoluteOffset.y - ); + this.label = new Point(this.state.absoluteOffset.x, this.state.absoluteOffset.y); this.labelShape.bounds = new Rectangle( Math.round(this.label.x - b.width / 2), Math.round(this.label.y - b.height / 2), @@ -2378,9 +2267,7 @@ class EdgeHandler { this.customHandles[i].shape.node.style.display = temp; // Hides custom handles during text editing - this.customHandles[ - i - ].shape.node.style.visibility = this.isCustomHandleVisible( + this.customHandles[i].shape.node.style.visibility = this.isCustomHandleVisible( this.customHandles[i] ) ? '' @@ -2395,9 +2282,7 @@ class EdgeHandler { * Returns true if the given custom handle is visible. */ isCustomHandleVisible(handle) { - return ( - !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1 - ); + return !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1; } /** @@ -2527,16 +2412,13 @@ class EdgeHandler { } } - if ( - this.shape != null && - !utils.equalPoints(this.shape.points, this.abspoints) - ) { + if (this.shape != null && !utils.equalPoints(this.shape.points, this.abspoints)) { this.shape.apply(this.state); this.shape.points = this.abspoints.slice(); this.shape.scale = this.state.view.scale; this.shape.isDashed = this.isSelectionDashed(); this.shape.stroke = this.getSelectionColor(); - this.shape.strokewidth = + this.shape.strokeWidth = this.getSelectionStrokeWidth() / this.shape.scale / this.shape.scale; this.shape.isShadow = false; this.shape.redraw(); diff --git a/packages/core/src/view/cell/vertex/VertexHandle.ts b/packages/core/src/view/cell/vertex/VertexHandle.ts index 19e4ebe1c..16c721c59 100644 --- a/packages/core/src/view/cell/vertex/VertexHandle.ts +++ b/packages/core/src/view/cell/vertex/VertexHandle.ts @@ -21,7 +21,7 @@ import { import InternalEvent from '../../event/InternalEvent'; import Shape from '../../geometry/shape/Shape'; import InternalMouseEvent from '../../event/InternalMouseEvent'; -import Image from '../../image/Image'; +import Image from '../../image/ImageBox'; import Graph from '../../Graph'; import CellState from '../datatypes/CellState'; @@ -33,11 +33,12 @@ import CellState from '../datatypes/CellState'; class VertexHandle { dependencies = ['snap', 'cells']; - constructor(state: CellState, - cursor: string | null = 'default', - image: Image | null = null, - shape: Shape | null = null) { - + constructor( + state: CellState, + cursor: string | null = 'default', + image: Image | null = null, + shape: Shape | null = null + ) { this.graph = state.view.graph; this.state = state; this.cursor = cursor != null ? cursor : this.cursor; @@ -221,12 +222,8 @@ class VertexHandle { const tr = this.graph.view.translate; const shapeBounds = this.shape.bounds; - shapeBounds.x = Math.floor( - (pt.x + tr.x) * scale - shapeBounds.width / 2 - ); - shapeBounds.y = Math.floor( - (pt.y + tr.y) * scale - shapeBounds.height / 2 - ); + shapeBounds.x = Math.floor((pt.x + tr.x) * scale - shapeBounds.width / 2); + shapeBounds.y = Math.floor((pt.y + tr.y) * scale - shapeBounds.height / 2); // Needed to force update of text bounds this.shape.redraw(); @@ -240,8 +237,7 @@ class VertexHandle { */ isHtmlRequired(): boolean { return ( - this.state.text != null && - this.state.text.node.parentNode === this.graph.container + this.state.text != null && this.state.text.node.parentNode === this.graph.container ); } diff --git a/packages/core/src/view/cell/vertex/VertexHandler.ts b/packages/core/src/view/cell/vertex/VertexHandler.ts index 2fe2ba36a..050615a3c 100644 --- a/packages/core/src/view/cell/vertex/VertexHandler.ts +++ b/packages/core/src/view/cell/vertex/VertexHandler.ts @@ -30,7 +30,7 @@ import mxClient from '../../../mxClient'; import { isMouseEvent, isShiftDown } from '../../../util/EventUtils'; import Graph from '../../Graph'; import CellState from '../datatypes/CellState'; -import Image from '../../image/Image'; +import Image from '../../image/ImageBox'; import Cell from '../datatypes/Cell'; /** @@ -238,15 +238,9 @@ class VertexHandler { // VML dialect required here for event transparency in IE this.selectionBorder.dialect = DIALECT_SVG; this.selectionBorder.pointerEvents = false; - this.selectionBorder.rotation = Number( - this.state.style.rotation || '0' - ); + this.selectionBorder.rotation = Number(this.state.style.rotation || '0'); this.selectionBorder.init(this.graph.getView().getOverlayPane()); - InternalEvent.redirectMouseEvents( - this.selectionBorder.node, - this.graph, - this.state - ); + InternalEvent.redirectMouseEvents(this.selectionBorder.node, this.graph, this.state); if (this.graph.isCellMovable(this.state.cell)) { this.selectionBorder.setCursor(CURSOR_MOVABLE_VERTEX); @@ -357,9 +351,7 @@ class VertexHandler { */ // isConstrainedEvent(me: mxMouseEvent): boolean; isConstrainedEvent(me) { - return ( - isShiftDown(me.getEvent()) || this.state.style.aspect === 'fixed' - ); + return isShiftDown(me.getEvent()) || this.state.style.aspect === 'fixed'; } /** @@ -448,7 +440,7 @@ class VertexHandler { null, this.getSelectionColor() ); - shape.strokewidth = this.getSelectionStrokeWidth(); + shape.strokeWidth = this.getSelectionStrokeWidth(); shape.isDashed = this.isSelectionDashed(); return shape; @@ -560,17 +552,9 @@ class VertexHandler { return shape; } if (index === InternalEvent.ROTATION_HANDLE) { - return new EllipseShape( - bounds, - fillColor || HANDLE_FILLCOLOR, - HANDLE_STROKECOLOR - ); + return new EllipseShape(bounds, fillColor || HANDLE_FILLCOLOR, HANDLE_STROKECOLOR); } - return new RectangleShape( - bounds, - fillColor || HANDLE_FILLCOLOR, - HANDLE_STROKECOLOR - ); + return new RectangleShape(bounds, fillColor || HANDLE_FILLCOLOR, HANDLE_STROKECOLOR); } /** @@ -604,19 +588,12 @@ class VertexHandler { const tol = !isMouseEvent(me.getEvent()) ? this.tolerance : 1; const hit = this.allowHandleBoundsCheck && tol > 0 - ? new Rectangle( - me.getGraphX() - tol, - me.getGraphY() - tol, - 2 * tol, - 2 * tol - ) + ? new Rectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol) : null; const checkShape = (shape) => { const st = - shape != null && - shape.constructor !== ImageShape && - this.allowHandleBoundsCheck + shape != null && shape.constructor !== ImageShape && this.allowHandleBoundsCheck ? shape.strokewidth + shape.svgStrokeTolerance : null; const real = @@ -720,8 +697,7 @@ class VertexHandler { // start(x: number, y: number, index: number): void; start(x, y, index) { if (this.selectionBorder != null) { - this.livePreviewActive = - this.livePreview && this.state.cell.getChildCount() === 0; + this.livePreviewActive = this.livePreview && this.state.cell.getChildCount() === 0; this.inTolerance = true; this.childOffsetX = 0; this.childOffsetY = 0; @@ -752,10 +728,7 @@ class VertexHandler { this.preview = this.createSelectionShape(this.bounds); 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.node.parentNode === this.graph.container ) { @@ -774,8 +747,7 @@ class VertexHandler { const dx = pos.x - this.state.getCenterX(); const dy = pos.y - this.state.getCenterY(); - this.startAngle = - dx !== 0 ? (Math.atan(dy / dx) * 180) / Math.PI + 90 : 0; + this.startAngle = dx !== 0 ? (Math.atan(dy / dx) * 180) / Math.PI + 90 : 0; this.startDist = Math.sqrt(dx * dx + dy * dy); } @@ -789,10 +761,7 @@ class VertexHandler { this.labelShape.node.style.display = ''; } else if (this.sizers != null && this.sizers[index] != null) { this.sizers[index].node.style.display = ''; - } else if ( - index <= InternalEvent.CUSTOM_HANDLE && - this.customHandles != null - ) { + } else if (index <= InternalEvent.CUSTOM_HANDLE && this.customHandles != null) { this.customHandles[InternalEvent.CUSTOM_HANDLE - index].setVisible(true); } @@ -801,9 +770,7 @@ class VertexHandler { this.edgeHandlers = []; for (let i = 0; i < edges.length; i += 1) { - const handler = this.graph.selectionCellsHandler.getHandler( - edges[i] - ); + const handler = this.graph.selectionCellsHandler.getHandler(edges[i]); if (handler != null) { this.edgeHandlers.push(handler); @@ -933,12 +900,8 @@ class VertexHandler { if (!this.inTolerance) { if (this.index <= InternalEvent.CUSTOM_HANDLE) { if (this.customHandles != null) { - this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent( - me - ); - this.customHandles[ - InternalEvent.CUSTOM_HANDLE - this.index - ].active = true; + this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].processEvent(me); + this.customHandles[InternalEvent.CUSTOM_HANDLE - this.index].active = true; if (this.ghostPreview != null) { this.ghostPreview.apply(this.state); @@ -1010,9 +973,7 @@ class VertexHandler { } const index = - this.rotationShape != null - ? this.sizers.length - 2 - : this.sizers.length - 1; + this.rotationShape != null ? this.sizers.length - 2 : this.sizers.length - 1; this.moveSizerTo(this.sizers[index], point.x, point.y); } @@ -1148,26 +1109,14 @@ class VertexHandler { this.unscaledBounds.y = max.y; } - if ( - this.unscaledBounds.x + this.unscaledBounds.width > - max.x + max.width - ) { + if (this.unscaledBounds.x + this.unscaledBounds.width > max.x + max.width) { this.unscaledBounds.width -= - this.unscaledBounds.x + - this.unscaledBounds.width - - max.x - - max.width; + this.unscaledBounds.x + this.unscaledBounds.width - max.x - max.width; } - if ( - this.unscaledBounds.y + this.unscaledBounds.height > - max.y + max.height - ) { + if (this.unscaledBounds.y + this.unscaledBounds.height > max.y + max.height) { this.unscaledBounds.height -= - this.unscaledBounds.y + - this.unscaledBounds.height - - max.y - - max.height; + this.unscaledBounds.y + this.unscaledBounds.height - max.y - max.height; } } } @@ -1211,12 +1160,8 @@ class VertexHandler { this.bounds.y += dy3; // Rounds unscaled bounds to int - this.unscaledBounds.x = this.roundLength( - this.unscaledBounds.x + dx3 / scale - ); - this.unscaledBounds.y = this.roundLength( - this.unscaledBounds.y + dy3 / scale - ); + this.unscaledBounds.x = this.roundLength(this.unscaledBounds.x + dx3 / scale); + this.unscaledBounds.y = this.roundLength(this.unscaledBounds.y + dy3 / scale); this.unscaledBounds.width = this.roundLength(this.unscaledBounds.width); this.unscaledBounds.height = this.roundLength(this.unscaledBounds.height); @@ -1370,15 +1315,12 @@ class VertexHandler { this.customHandles[InternalEvent.CUSTOM_HANDLE - index] != null ) { this.state.style = style; - this.customHandles[ - InternalEvent.CUSTOM_HANDLE - index - ].positionChanged(); + this.customHandles[InternalEvent.CUSTOM_HANDLE - index].positionChanged(); } } } else if (index === InternalEvent.ROTATION_HANDLE) { if (this.currentAlpha != null) { - const delta = - this.currentAlpha - (this.state.style.rotation || 0); + const delta = this.currentAlpha - (this.state.style.rotation || 0); if (delta !== 0) { this.rotateCell(this.state.cell, delta); @@ -1388,9 +1330,7 @@ class VertexHandler { } } else { const gridEnabled = this.graph.isGridEnabledEvent(me.getEvent()); - const alpha = utils.toRadians( - this.state.style.rotation || '0' - ); + const alpha = utils.toRadians(this.state.style.rotation || '0'); const cos = Math.cos(-alpha); const sin = Math.sin(-alpha); @@ -1579,20 +1519,14 @@ class VertexHandler { if (geo != null) { if (index === InternalEvent.LABEL_HANDLE) { - const alpha = -utils.toRadians( - this.state.style.rotation || '0' - ); + const alpha = -utils.toRadians(this.state.style.rotation || '0'); const cos = Math.cos(alpha); const sin = Math.sin(alpha); const { scale } = this.graph.view; const pt = utils.getRotatedPoint( new Point( - Math.round( - (this.labelShape.bounds.getCenterX() - this.startX) / scale - ), - Math.round( - (this.labelShape.bounds.getCenterY() - this.startY) / scale - ) + Math.round((this.labelShape.bounds.getCenterX() - this.startX) / scale), + Math.round((this.labelShape.bounds.getCenterY() - this.startY) / scale) ), cos, sin @@ -1811,12 +1745,7 @@ class VertexHandler { height = Math.abs(height); } - const result = new Rectangle( - left + tr.x * scale, - top + tr.y * scale, - width, - height - ); + const result = new Rectangle(left + tr.x * scale, top + tr.y * scale, width, height); if (this.minBounds != null) { result.width = Math.max( @@ -1924,18 +1853,13 @@ class VertexHandler { // Hides custom handles during text editing this.customHandles[i].shape.node.style.visibility = - this.handlesVisible && - this.isCustomHandleVisible(this.customHandles[i]) + this.handlesVisible && this.isCustomHandleVisible(this.customHandles[i]) ? '' : 'hidden'; } } - if ( - this.sizers != null && - this.sizers.length > 0 && - this.sizers[0] != null - ) { + if (this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null) { if (this.index == null && this.manageSizers && this.sizers.length >= 8) { // KNOWN: Tolerance depends on event type (eg. 0 for mouse events) const padding = this.getHandlePadding(); @@ -1990,9 +1914,7 @@ class VertexHandler { 'w-resize', ]; - const alpha = utils.toRadians( - this.state.style.rotation || '0' - ); + const alpha = utils.toRadians(this.state.style.rotation || '0'); const cos = Math.cos(alpha); const sin = Math.sin(alpha); const da = Math.round((alpha * 4) / Math.PI); @@ -2062,36 +1984,25 @@ class VertexHandler { if (this.rotationShape != null) { const alpha = utils.toRadians( - this.currentAlpha != null - ? this.currentAlpha - : this.state.style.rotation || '0' + this.currentAlpha != null ? this.currentAlpha : this.state.style.rotation || '0' ); const cos = Math.cos(alpha); const sin = Math.sin(alpha); const ct = new Point(this.state.getCenterX(), this.state.getCenterY()); - const pt = utils.getRotatedPoint( - this.getRotationHandlePosition(), - cos, - sin, - ct - ); + const pt = utils.getRotatedPoint(this.getRotationHandlePosition(), cos, sin, ct); if (this.rotationShape.node != null) { this.moveSizerTo(this.rotationShape, pt.x, pt.y); // Hides rotation handle during text editing this.rotationShape.node.style.visibility = - this.state.view.graph.isEditing() || !this.handlesVisible - ? 'hidden' - : ''; + this.state.view.graph.isEditing() || !this.handlesVisible ? 'hidden' : ''; } } if (this.selectionBorder != null) { - this.selectionBorder.rotation = Number( - this.state.style.rotation || '0' - ); + this.selectionBorder.rotation = Number(this.state.style.rotation || '0'); } if (this.edgeHandlers != null) { @@ -2107,9 +2018,7 @@ class VertexHandler { * Returns true if the given custom handle is visible. */ isCustomHandleVisible(handle) { - return ( - !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1 - ); + return !this.graph.isEditing() && this.state.view.graph.getSelectionCount() === 1; } /** @@ -2162,10 +2071,7 @@ class VertexHandler { this.parentHighlight.redraw(); } } else { - if ( - pstate != null && - pstate.parentHighlight === this.parentHighlight - ) { + if (pstate != null && pstate.parentHighlight === this.parentHighlight) { pstate.parentHighlight = null; } @@ -2173,18 +2079,12 @@ class VertexHandler { this.parentHighlight = null; } } else if (this.parentHighlightEnabled && visible) { - if ( - parent.isVertex() && - pstate != null && - pstate.parentHighlight == null - ) { + if (parent.isVertex() && pstate != null && pstate.parentHighlight == null) { this.parentHighlight = this.createParentHighlightShape(pstate); // VML dialect required here for event transparency in IE this.parentHighlight.dialect = DIALECT_SVG; this.parentHighlight.pointerEvents = false; - this.parentHighlight.rotation = Number( - pstate.style.rotation || '0' - ); + this.parentHighlight.rotation = Number(pstate.style.rotation || '0'); this.parentHighlight.init(this.graph.getView().getOverlayPane()); this.parentHighlight.redraw(); @@ -2207,10 +2107,7 @@ class VertexHandler { if (this.preview.node.parentNode === this.graph.container) { this.preview.bounds.width = Math.max(0, this.preview.bounds.width - 1); - this.preview.bounds.height = Math.max( - 0, - this.preview.bounds.height - 1 - ); + this.preview.bounds.height = Math.max(0, this.preview.bounds.height - 1); } this.preview.rotation = Number(this.state.style.rotation || '0'); diff --git a/packages/core/src/view/connection/ConnectionHandler.ts b/packages/core/src/view/connection/ConnectionHandler.ts index bc84bb53d..7154e7d1c 100644 --- a/packages/core/src/view/connection/ConnectionHandler.ts +++ b/packages/core/src/view/connection/ConnectionHandler.ts @@ -19,7 +19,13 @@ import { TOOLTIP_VERTICAL_OFFSET, VALID_COLOR, } 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 ImageShape from '../geometry/shape/node/ImageShape'; import CellMarker from '../cell/CellMarker'; @@ -36,7 +42,7 @@ import { isShiftDown, } from '../../util/EventUtils'; import graph from '../Graph'; -import Image from '../image/Image'; +import Image from '../image/ImageBox'; import CellState from '../cell/datatypes/CellState'; import Graph from '../Graph'; import ConnectionConstraint from './ConnectionConstraint'; @@ -201,8 +207,7 @@ type FactoryMethod = (source: Cell, target: Cell, style?: string) => Cell; * the that represents the new edge. */ class ConnectionHandler extends EventSource { - constructor(graph: Graph, - factoryMethod: FactoryMethod | null = null) { + constructor(graph: Graph, factoryMethod: FactoryMethod | null = null) { super(); this.graph = graph; @@ -660,7 +665,7 @@ class ConnectionHandler extends EventSource { } return cell; - }; + } // Sets the highlight color according to validateConnection isValidState(state: CellState) { @@ -696,11 +701,7 @@ class ConnectionHandler extends EventSource { * * Starts a new connection for the given state and coordinates. */ - start(state: CellState, - x: number, - y: number, - edgeState: CellState): void { - + start(state: CellState, x: number, y: number, edgeState: CellState): void { this.previous = state; this.first = new Point(x, y); this.edgeState = edgeState != null ? edgeState : this.createEdgeState(null); @@ -733,8 +734,7 @@ class ConnectionHandler extends EventSource { * cell - that represents the source terminal. * me - that is associated with this call. */ - isValidSource(cell: Cell, - me: InternalMouseEvent): boolean { + isValidSource(cell: Cell, me: InternalMouseEvent): boolean { return this.graph.isValidSource(cell); } @@ -765,8 +765,7 @@ class ConnectionHandler extends EventSource { * source - that represents the source terminal. * target - that represents the target terminal. */ - validateConnection(source: Cell, - target: Cell): string { + validateConnection(source: Cell, target: Cell): string { if (!this.isValidTarget(target)) { return ''; } @@ -798,10 +797,7 @@ class ConnectionHandler extends EventSource { * state - whose connect icons should be returned. */ isMoveIconToFrontForState(state: CellState): boolean { - if ( - state.text != null && - state.text.node.parentNode === this.graph.container - ) { + if (state.text != null && state.text.node.parentNode === this.graph.container) { return true; } return this.moveIconFront; @@ -841,10 +837,7 @@ class ConnectionHandler extends EventSource { // Move the icon back in the overlay pane if (this.moveIconBack && icon.node.previousSibling != null) { - icon.node.parentNode.insertBefore( - icon.node, - icon.node.parentNode.firstChild - ); + icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild); } } @@ -886,8 +879,7 @@ class ConnectionHandler extends EventSource { * * icons - Optional array of to be redrawn. */ - redrawIcons(icons?: ImageShape[] | null, - state?: CellState): void { + redrawIcons(icons?: ImageShape[] | null, state?: CellState): void { if (icons != null && icons[0] != null && state != null) { const pos = this.getIconPosition(icons[0], state); icons[0].bounds.x = pos.x; @@ -897,8 +889,7 @@ class ConnectionHandler extends EventSource { } // TODO: Document me! =========================================================================================================== - getIconPosition(icon: ImageShape, - state: CellState): Point { + getIconPosition(icon: ImageShape, state: CellState): Point { const { scale } = this.graph.getView(); let cx = state.getCenterX(); let cy = state.getCenterY(); @@ -909,9 +900,7 @@ class ConnectionHandler extends EventSource { cx = size.width !== 0 ? state.x + (size.width * scale) / 2 : cx; cy = size.height !== 0 ? state.y + (size.height * scale) / 2 : cy; - const alpha = toRadians( - getValue(state.style, 'rotation') || 0 - ); + const alpha = toRadians(getValue(state.style, 'rotation') || 0); if (alpha !== 0) { const cos = Math.cos(alpha); @@ -1066,10 +1055,8 @@ class ConnectionHandler extends EventSource { const left = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0); const top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); - const gridX = - this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; - const gridY = - this.currentPoint.y - this.graph.container.scrollTop + offset.y - top; + const gridX = this.currentPoint.x - this.graph.container.scrollLeft + offset.x - left; + const gridY = this.currentPoint.y - this.graph.container.scrollTop + offset.y - top; return ( this.outlineConnect && @@ -1089,15 +1076,12 @@ class ConnectionHandler extends EventSource { * Updates the current state for a given mouse move event by using * the . */ - updateCurrentState(me: InternalMouseEvent, - point: Point): void { + updateCurrentState(me: InternalMouseEvent, point: Point): void { this.constraintHandler.update( me, this.first == null, false, - this.first == null || me.isSource(this.marker.highlight.shape) - ? null - : point + this.first == null || me.isSource(this.marker.highlight.shape) ? null : point ); if ( @@ -1109,8 +1093,7 @@ class ConnectionHandler extends EventSource { if ( this.marker.highlight != null && this.marker.highlight.state != null && - this.marker.highlight.state.cell === - this.constraintHandler.currentFocus.cell + this.marker.highlight.state.cell === this.constraintHandler.currentFocus.cell ) { // Direct repaint needed if cell already highlighted if (this.marker.highlight.shape.stroke !== 'transparent') { @@ -1118,10 +1101,7 @@ class ConnectionHandler extends EventSource { this.marker.highlight.repaint(); } } else { - this.marker.markCell( - this.constraintHandler.currentFocus.cell, - 'transparent' - ); + this.marker.markCell(this.constraintHandler.currentFocus.cell, 'transparent'); } // Updates validation state @@ -1137,8 +1117,7 @@ class ConnectionHandler extends EventSource { if ( this.error != null || - (this.currentState != null && - !this.isCellEnabled(this.currentState.cell)) + (this.currentState != null && !this.isCellEnabled(this.currentState.cell)) ) { this.constraintHandler.reset(); } @@ -1152,10 +1131,7 @@ class ConnectionHandler extends EventSource { this.currentState = this.marker.getValidState(); } - if ( - this.currentState != null && - !this.isCellEnabled(this.currentState.cell) - ) { + if (this.currentState != null && !this.isCellEnabled(this.currentState.cell)) { this.constraintHandler.reset(); this.marker.reset(); this.currentState = null; @@ -1170,21 +1146,14 @@ class ConnectionHandler extends EventSource { point = new point(me.getGraphX(), me.getGraphY()); } - const constraint = this.graph.getOutlineConstraint( - point, - this.currentState, - me - ); + const constraint = this.graph.getOutlineConstraint(point, this.currentState, me); this.constraintHandler.setFocus(me, this.currentState, false); this.constraintHandler.currentConstraint = constraint; this.constraintHandler.currentPoint = point; } if (this.outlineConnect) { - if ( - this.marker.highlight != null && - this.marker.highlight.shape != null - ) { + if (this.marker.highlight != null && this.marker.highlight.shape != null) { const s = this.graph.view.scale; if ( @@ -1192,7 +1161,7 @@ class ConnectionHandler extends EventSource { this.constraintHandler.currentFocus != null ) { this.marker.highlight.shape.stroke = OUTLINE_HIGHLIGHT_COLOR; - this.marker.highlight.shape.strokewidth = + this.marker.highlight.shape.strokeWidth = OUTLINE_HIGHLIGHT_STROKEWIDTH / s / s; this.marker.highlight.repaint(); } 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.strokewidth = - HIGHLIGHT_STROKEWIDTH / s / s; + this.marker.highlight.shape.strokeWidth = HIGHLIGHT_STROKEWIDTH / s / s; 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 * first point of the preview if alt is not pressed. */ - snapToPreview(me: MouseEvent, - point: Point): void { + snapToPreview(me: MouseEvent, point: Point): void { if (!isAltDown(me.getEvent()) && this.previous != null) { const tol = (this.graph.gridSize * this.graph.view.scale) / 2; const tmp = @@ -1271,8 +1238,7 @@ class ConnectionHandler extends EventSource { * Handles the event by updating the preview edge or by highlighting * a possible source or target terminal. */ - mouseMove(sender: MouseEvent, - me: InternalMouseEvent): void { + mouseMove(sender: MouseEvent, me: InternalMouseEvent): void { if ( !me.isConsumed() && (this.ignoreMouseDown || this.first != null || !this.graph.isMouseDown) @@ -1344,10 +1310,7 @@ class ConnectionHandler extends EventSource { const h = this.selectedIcon.bounds.height; if (this.currentState != null && this.targetConnectImage) { - const pos = this.getIconPosition( - this.selectedIcon, - this.currentState - ); + const pos = this.getIconPosition(this.selectedIcon, this.currentState); this.selectedIcon.bounds.x = pos.x; this.selectedIcon.bounds.y = pos.y; } else { @@ -1402,10 +1365,7 @@ class ConnectionHandler extends EventSource { if (this.currentState == null && this.movePreviewAway) { let tmp = pt2; - if ( - this.edgeState != null && - this.edgeState.absolutePoints.length >= 2 - ) { + if (this.edgeState != null && this.edgeState.absolutePoints.length >= 2) { const tmp2 = this.edgeState.absolutePoints[ this.edgeState.absolutePoints.length - 2 ]; @@ -1476,10 +1436,7 @@ class ConnectionHandler extends EventSource { me.consume(); } else if (!this.isEnabled() || !this.graph.isEnabled()) { this.constraintHandler.reset(); - } else if ( - this.previous !== this.currentState && - this.edgeState == null - ) { + } else if (this.previous !== this.currentState && this.edgeState == null) { this.destroyIcons(); // Sets the cursor on the current shape @@ -1507,18 +1464,13 @@ class ConnectionHandler extends EventSource { me.consume(); } - if ( - !this.graph.isMouseDown && - this.currentState != null && - this.icons != null - ) { + if (!this.graph.isMouseDown && this.currentState != null && this.icons != null) { let hitsIcon = false; const target = me.getSource(); for (let i = 0; i < this.icons.length && !hitsIcon; i += 1) { hitsIcon = - target === this.icons[i].node || - target.parentNode === this.icons[i].node; + target === this.icons[i].node || target.parentNode === this.icons[i].node; } if (!hitsIcon) { @@ -1535,8 +1487,7 @@ class ConnectionHandler extends EventSource { * * Updates . */ - updateEdgeState(current: CellState, - constraint: CellState): void { + updateEdgeState(current: CellState, constraint: CellState): void { // TODO: Use generic method for writing constraint to style if (this.sourceConstraint != null && this.sourceConstraint.point != null) { this.edgeState.style.exitX = this.sourceConstraint.point.x; @@ -1551,10 +1502,7 @@ class ConnectionHandler extends EventSource { delete this.edgeState.style.entryY; } - this.edgeState.absolutePoints = [ - null, - this.currentState != null ? null : current, - ]; + this.edgeState.absolutePoints = [null, this.currentState != null ? null : current]; this.graph.view.updateFixedTerminalPoint( this.edgeState, this.previous, @@ -1616,8 +1564,7 @@ class ConnectionHandler extends EventSource { * state - that represents the target cell state. * me - that represents the mouse move. */ - getTargetPerimeterPoint(state: CellState, - me: MouseEvent): Point { + getTargetPerimeterPoint(state: CellState, me: MouseEvent): Point { let result = null; const { view } = state; const targetPerimeter = view.getPerimeterFunction(state); @@ -1656,9 +1603,7 @@ class ConnectionHandler extends EventSource { * next - that represents the next point along the previewed edge. * me - that represents the mouse move. */ - getSourcePerimeterPoint(state: CellState, - next: Point, - me: MouseEvent): Point { + getSourcePerimeterPoint(state: CellState, next: Point, me: MouseEvent): Point { let result = null; const { view } = state; const sourcePerimeter = view.getPerimeterFunction(state); @@ -1677,12 +1622,7 @@ class ConnectionHandler extends EventSource { ); } - let tmp = sourcePerimeter( - view.getPerimeterBounds(state), - state, - next, - false - ); + let tmp = sourcePerimeter(view.getPerimeterBounds(state), state, next, false); if (tmp != null) { if (theta !== 0) { @@ -1715,9 +1655,7 @@ class ConnectionHandler extends EventSource { * icons - Array of currently displayed icons. * me - that contains the mouse event. */ - updateIcons(state: CellState, - icons: string[], - me: InternalMouseEvent): void { + updateIcons(state: CellState, icons: string[], me: InternalMouseEvent): void { // empty } @@ -1739,11 +1677,7 @@ class ConnectionHandler extends EventSource { * Adds the waypoint for the given event to . */ addWaypointForEvent(me: InternalMouseEvent): void { - let point = convertPoint( - this.graph.container, - me.getX(), - me.getY() - ); + let point = convertPoint(this.graph.container, me.getX(), me.getY()); const dx = Math.abs(point.x - this.first.x); const dy = Math.abs(point.y - this.first.y); const addPoint = @@ -1790,8 +1724,7 @@ class ConnectionHandler extends EventSource { * * Handles the event by inserting the new connection. */ - mouseUp(sender: InternalMouseEvent, - me: InternalMouseEvent): void { + mouseUp(sender: InternalMouseEvent, me: InternalMouseEvent): void { if (!me.isConsumed() && this.isConnecting()) { if (this.waypointsEnabled && !this.isStopEvent(me)) { this.addWaypointForEvent(me); @@ -1907,7 +1840,7 @@ class ConnectionHandler extends EventSource { * returned. */ updatePreview(valid: boolean): void { - this.shape.strokewidth = this.getEdgeWidth(valid); + this.shape.strokeWidth = this.getEdgeWidth(valid); this.shape.stroke = this.getEdgeColor(valid); } @@ -1955,15 +1888,8 @@ class ConnectionHandler extends EventSource { * dropTarget - that represents the cell under the mouse when it was * released. */ - connect(source: Cell, - target: Cell, - evt: MouseEvent, - dropTarget: Cell): void { - if ( - target != null || - this.isCreateTarget(evt) || - this.graph.allowDanglingEdges - ) { + connect(source: Cell, target: Cell, evt: MouseEvent, dropTarget: Cell): void { + if (target != null || this.isCreateTarget(evt) || this.graph.allowDanglingEdges) { // Uses the common parent of source and target or // the default parent to insert the edge const model = this.graph.getModel(); @@ -2036,12 +1962,7 @@ class ConnectionHandler extends EventSource { if (edge != null) { // Updates the connection constraints - this.graph.setConnectionConstraint( - edge, - source, - true, - this.sourceConstraint - ); + this.graph.setConnectionConstraint(edge, source, true, this.sourceConstraint); this.graph.setConnectionConstraint( edge, target, @@ -2070,11 +1991,7 @@ class ConnectionHandler extends EventSource { tmp = tmp.getParent(); } - if ( - tmp != null && - tmp.parent != null && - tmp.parent === edge.parent - ) { + if (tmp != null && tmp.parent != null && tmp.parent === edge.parent) { model.add(parent, edge, tmp.parent.getIndex(tmp)); } } @@ -2110,10 +2027,7 @@ class ConnectionHandler extends EventSource { this.originalPoint.x / s - t.x, this.originalPoint.y / s - t.y ) - : new Point( - this.currentPoint.x / s - t.x, - this.currentPoint.y / s - t.y - ); + : new Point(this.currentPoint.x / s - t.x, this.currentPoint.y / s - t.y); pt.x -= this.graph.panDx / this.graph.view.scale; pt.y -= this.graph.panDy / this.graph.view.scale; geo.setTerminalPoint(pt, false); @@ -2154,8 +2068,7 @@ class ConnectionHandler extends EventSource { * Selects the given edge after adding a new connection. The target argument * contains the target vertex if one has been inserted. */ - selectCells(edge: Cell, - target: Cell): void { + selectCells(edge: Cell, target: Cell): void { this.graph.setSelectionCell(edge); } @@ -2166,13 +2079,14 @@ class ConnectionHandler extends EventSource { * implementation does only use if is defined, * otherwise will be used. */ - insertEdge(parent: Cell, - id: string, - value: any, - source: Cell, - target: Cell, - style: string): Cell { - + insertEdge( + parent: Cell, + id: string, + value: any, + source: Cell, + target: Cell, + style: string + ): Cell { if (this.factoryMethod == null) { 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. * source - that represents the source terminal. */ - createTargetVertex(evt: MouseEvent, - source: Cell): Cell { + createTargetVertex(evt: MouseEvent, source: Cell): Cell { // Uses the first non-relative source let geo = source.getGeometry(); @@ -2267,10 +2180,7 @@ class ConnectionHandler extends EventSource { * target - that represents the target terminal. * style - Optional style from the preview edge. */ - createEdge(value?: any, - source?: Cell, - target?: Cell, - style?: string): Cell { + createEdge(value?: any, source?: Cell, target?: Cell, style?: string): Cell { let edge = null; // Creates a new edge using the factoryMethod diff --git a/packages/core/src/view/connection/ConstraintHandler.ts b/packages/core/src/view/connection/ConstraintHandler.ts index 82556b2e9..76d8222dc 100644 --- a/packages/core/src/view/connection/ConstraintHandler.ts +++ b/packages/core/src/view/connection/ConstraintHandler.ts @@ -4,7 +4,7 @@ * Updated to ES9 syntax by David Morrissey 2021 * Type definitions from the typed-mxgraph project */ -import Image from '../image/Image'; +import Image from '../image/ImageBox'; import mxClient from '../../mxClient'; import { DEFAULT_VALID_COLOR, @@ -240,11 +240,7 @@ class ConstraintHandler { this.reset(); }; - InternalEvent.addListener( - this.graph.container, - 'mouseleave', - this.resetHandler - ); + InternalEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler); } const tol = this.getTolerance(me); @@ -296,12 +292,7 @@ class ConstraintHandler { if ( (this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null && - this.intersects( - this.focusIcons[i], - grid, - source, - existingEdge - ))) && + this.intersects(this.focusIcons[i], grid, source, existingEdge))) && (minDistSq == null || tmp < minDistSq) ) { this.currentConstraint = this.constraints[i]; @@ -358,12 +349,7 @@ class ConstraintHandler { ) { const state = this.graph.view.getState(this.currentFocus.cell); this.currentFocus = state; - this.currentFocusArea = new Rectangle( - state.x, - state.y, - state.width, - state.height - ); + this.currentFocusArea = new Rectangle(state.x, state.y, state.width, state.height); for (let i = 0; i < this.constraints.length; i += 1) { 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, state, source) { this.constraints = - state != null && - !this.isStateIgnored(state, source) && - state.cell.isConnectable() + state != null && !this.isStateIgnored(state, source) && state.cell.isConnectable() ? this.isEnabled() ? this.graph.getAllConnectionConstraints(state, source) || [] : [] @@ -402,12 +386,7 @@ class ConstraintHandler { // Only uses cells which have constraints if (this.constraints != null) { this.currentFocus = state; - this.currentFocusArea = new Rectangle( - state.x, - state.y, - state.width, - state.height - ); + this.currentFocusArea = new Rectangle(state.x, state.y, state.width, state.height); if (this.focusIcons != null) { for (let i = 0; i < this.focusIcons.length; i += 1) { @@ -440,10 +419,7 @@ class ConstraintHandler { // Move the icon behind all other overlays if (icon.node.previousSibling != null) { - icon.node.parentNode.insertBefore( - icon.node, - icon.node.parentNode.firstChild - ); + icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild); } const getState = () => { diff --git a/packages/core/src/view/event/EventObject.ts b/packages/core/src/view/event/EventObject.ts index 45001690b..620d169cf 100644 --- a/packages/core/src/view/event/EventObject.ts +++ b/packages/core/src/view/event/EventObject.ts @@ -5,6 +5,8 @@ * Type definitions from the typed-mxgraph project */ +type EventProperties = Record; + /** * Class: mxEventObject * @@ -29,9 +31,9 @@ * (end) */ class EventObject { - constructor(name: string, ...args: any[]) { + constructor(name = '', ...args: any[]) { this.name = name; - this.properties = []; + this.properties = {}; if (!!args[0] && args[0].constructor === Object) { // A literal object ({}) @@ -41,7 +43,7 @@ class EventObject { } else { // two-values [key, value, key, value, ...] 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]; } } @@ -53,14 +55,14 @@ class EventObject { * * Holds the name. */ - name: string = ''; + name: string; /** * Variable: properties * * Holds the properties as an associative array. */ - properties: any = null; + properties: EventProperties; /** * Variable: consumed @@ -74,7 +76,7 @@ class EventObject { * * Returns . */ - getName(): string { + getName() { return this.name; } @@ -83,7 +85,7 @@ class EventObject { * * Returns . */ - getProperties(): any { + getProperties() { return this.properties; } @@ -92,7 +94,7 @@ class EventObject { * * Returns the property for the given key. */ - getProperty(key: string): any { + getProperty(key: string) { return this.properties[key]; } @@ -101,7 +103,7 @@ class EventObject { * * Returns true if the event has been consumed. */ - isConsumed(): boolean { + isConsumed() { return this.consumed; } @@ -110,7 +112,7 @@ class EventObject { * * Consumes the event. */ - consume(): void { + consume() { this.consumed = true; } } diff --git a/packages/core/src/view/event/EventSource.ts b/packages/core/src/view/event/EventSource.ts index c051c89df..f2349c332 100644 --- a/packages/core/src/view/event/EventSource.ts +++ b/packages/core/src/view/event/EventSource.ts @@ -7,6 +7,11 @@ import EventObject from './EventObject'; +type EventListener = { + funct: Function; + name: string; +}; + /** * Class: mxEventSource * @@ -30,7 +35,7 @@ import EventObject from './EventObject'; * Constructs a new event source. */ class EventSource { - constructor(eventSource: EventSource | null=null) { + constructor(eventSource: EventSource | null = null) { this.eventSource = eventSource; } @@ -41,14 +46,14 @@ class EventSource { * contains the event name followed by the respective listener for each * registered listener. */ - eventListeners: ({funct: Function, name: string})[] = []; + eventListeners: EventListener[] = []; /** * Variable: eventsEnabled * * Specifies if events can be fired. Default is true. */ - eventsEnabled: boolean = true; + eventsEnabled = true; /** * Variable: eventSource @@ -62,7 +67,7 @@ class EventSource { * * Returns . */ - isEventsEnabled(): boolean { + isEventsEnabled() { return this.eventsEnabled; } @@ -71,7 +76,7 @@ class EventSource { * * Sets . */ - setEventsEnabled(value: boolean): void { + setEventsEnabled(value: boolean) { this.eventsEnabled = value; } @@ -80,7 +85,7 @@ class EventSource { * * Returns . */ - getEventSource(): EventSource | null { + getEventSource() { return this.eventSource; } @@ -89,7 +94,7 @@ class EventSource { * * Sets . */ - setEventSource(value: EventSource): void { + setEventSource(value: EventSource) { this.eventSource = value; } @@ -101,13 +106,8 @@ class EventSource { * * The parameters of the listener are the sender and an . */ - addListener(name: string, - funct: (...args: any[]) => any): void { - - if (this.eventListeners == null) { - this.eventListeners = []; - } - this.eventListeners.push({name, funct}); + addListener(name: string, funct: (...args: any[]) => any) { + this.eventListeners.push({ name, funct }); } /** @@ -115,16 +115,14 @@ class EventSource { * * Removes all occurrences of the given listener from . */ - removeListener(funct: (...args: any[]) => any): void { - if (this.eventListeners != null) { - let i = 0; + removeListener(funct: (...args: any[]) => any) { + let i = 0; - while (i < this.eventListeners.length) { - if (this.eventListeners[i].funct === funct) { - this.eventListeners.splice(i, 1); - } else { - i += 1; - } + while (i < this.eventListeners.length) { + if (this.eventListeners[i].funct === funct) { + this.eventListeners.splice(i, 1); + } else { + i += 1; } } } @@ -148,21 +146,21 @@ class EventSource { * sender - Optional sender to be passed to the listener. Default value is * the return value of . */ - fireEvent(evt: EventObject, sender: any = null): void { - if (this.eventListeners != null && this.isEventsEnabled()) { - if (evt == null) { + fireEvent(evt: EventObject, sender: any = null) { + if (this.isEventsEnabled()) { + if (!evt) { evt = new EventObject(''); } - if (sender == null) { + if (!sender) { sender = this.getEventSource(); } - if (sender == null) { + if (!sender) { sender = this; } 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]); } } diff --git a/packages/core/src/view/event/InternalMouseEvent.ts b/packages/core/src/view/event/InternalMouseEvent.ts index d39334da6..cc3fd3169 100644 --- a/packages/core/src/view/event/InternalMouseEvent.ts +++ b/packages/core/src/view/event/InternalMouseEvent.ts @@ -4,7 +4,13 @@ * Updated to ES9 syntax by David Morrissey 2021 * 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 CellState from '../cell/datatypes/CellState'; import Shape from '../geometry/shape/Shape'; @@ -119,11 +125,8 @@ class InternalMouseEvent { * * Returns true if the given is the source of . */ - isSource(shape: Shape): boolean { - if (shape != null) { - return isAncestorNode(shape.node, this.getSource()); - } - return false; + isSource(shape: Shape) { + return shape ? isAncestorNode(shape.node, this.getSource()) : false; } /** @@ -131,7 +134,7 @@ class InternalMouseEvent { * * Returns . */ - getX(): number { + getX() { return getClientX(this.getEvent()); } @@ -140,7 +143,7 @@ class InternalMouseEvent { * * Returns . */ - getY(): number { + getY() { return getClientY(this.getEvent()); } diff --git a/packages/core/src/view/folding/GraphFolding.ts b/packages/core/src/view/folding/GraphFolding.ts index af540b7cc..5e9d59942 100644 --- a/packages/core/src/view/folding/GraphFolding.ts +++ b/packages/core/src/view/folding/GraphFolding.ts @@ -1,14 +1,14 @@ -import Image from "../image/Image"; -import mxClient from "../../mxClient"; -import Graph from "../Graph"; -import CellState from "../cell/datatypes/CellState"; -import Cell from "../cell/datatypes/Cell"; -import CellArray from "../cell/datatypes/CellArray"; -import EventObject from "../event/EventObject"; -import InternalEvent from "../event/InternalEvent"; -import Geometry from "../geometry/Geometry"; -import {getValue, toRadians} from "../../util/Utils"; -import Rectangle from "../geometry/Rectangle"; +import Image from '../image/ImageBox'; +import mxClient from '../../mxClient'; +import Graph from '../Graph'; +import CellState from '../cell/datatypes/CellState'; +import Cell from '../cell/datatypes/Cell'; +import CellArray from '../cell/datatypes/CellArray'; +import EventObject from '../event/EventObject'; +import InternalEvent from '../event/InternalEvent'; +import Geometry from '../geometry/Geometry'; +import { getValue, toRadians } from '../../util/Utils'; +import Rectangle from '../geometry/Rectangle'; /** * GraphFoldingOptions @@ -25,21 +25,22 @@ import Rectangle from "../geometry/Rectangle"; * a cell is first collapsed. */ type GraphFoldingOptions = { - foldingEnabled: boolean, - collapsedImage: Image, - expandedImage: Image, - collapseToPreferredSize: boolean, + foldingEnabled: boolean; + collapsedImage: Image; + expandedImage: Image; + collapseToPreferredSize: boolean; }; class GraphFolding { - constructor(graph: Graph, - options: GraphFoldingOptions = { - foldingEnabled: true, - collapsedImage: new Image(`${mxClient.imageBasePath}/collapsed.gif`, 9, 9), - expandedImage: new Image(`${mxClient.imageBasePath}/expanded.gif`, 9, 9), - collapseToPreferredSize: true, - }) { - + constructor( + graph: Graph, + options: GraphFoldingOptions = { + foldingEnabled: true, + collapsedImage: new Image(`${mxClient.imageBasePath}/collapsed.gif`, 9, 9), + expandedImage: new Image(`${mxClient.imageBasePath}/expanded.gif`, 9, 9), + collapseToPreferredSize: true, + } + ) { this.graph = graph; this.options = options; } @@ -53,8 +54,7 @@ class GraphFolding { * the tooltip. * @default 'collapse-expand' */ - collapseExpandResource: string = - mxClient.language != 'none' ? 'collapse-expand' : ''; + collapseExpandResource: string = mxClient.language != 'none' ? 'collapse-expand' : ''; /** * @@ -64,10 +64,7 @@ class GraphFolding { /** * Returns the cells which are movable in the given array of cells. */ - getFoldableCells( - cells: CellArray, - collapse: boolean = false - ): CellArray | null { + getFoldableCells(cells: CellArray, collapse: boolean = false): CellArray | null { return this.graph.model.filterCells(cells, (cell: Cell) => { return this.isCellFoldable(cell, collapse); }); @@ -272,12 +269,7 @@ class GraphFolding { } } - geo.alternateBounds = new Rectangle( - 0, - 0, - bounds.width, - bounds.height - ); + geo.alternateBounds = new Rectangle(0, 0, bounds.width, bounds.height); } if (geo.alternateBounds != null) { diff --git a/packages/core/src/view/geometry/Geometry.ts b/packages/core/src/view/geometry/Geometry.ts index a69f66039..02a7fd4a7 100644 --- a/packages/core/src/view/geometry/Geometry.ts +++ b/packages/core/src/view/geometry/Geometry.ts @@ -7,7 +7,7 @@ import Point from './Point'; import Rectangle from './Rectangle'; -import utils, { equalPoints, getRotatedPoint, toRadians } from '../../util/Utils'; +import { equalPoints, getRotatedPoint, toRadians } from '../../util/Utils'; 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. */ class Geometry extends Rectangle { - constructor( - x: number = 0, - y: number = 0, - width: number = 0, - height: number = 0 - ) { + constructor(x = 0, y = 0, width = 0, height = 0) { super(x, y, width, height); } diff --git a/packages/core/src/view/geometry/shape/Actor.ts b/packages/core/src/view/geometry/shape/Actor.ts index 100dfc8b9..8f66e8cb3 100644 --- a/packages/core/src/view/geometry/shape/Actor.ts +++ b/packages/core/src/view/geometry/shape/Actor.ts @@ -6,7 +6,9 @@ */ import Rectangle from '../Rectangle'; 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 @@ -34,27 +36,21 @@ import mxSvgCanvas2D from '../../../util/canvas/mxSvgCanvas2D'; class Actor extends Shape { constructor( bounds: Rectangle | null = null, - fill: string | null = null, - stroke: string | null = null, - strokewidth: number = 1 + fill: ColorValue = NONE, + stroke: ColorValue = NONE, + strokeWidth: number = 1 ) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokeWidth; } /** * Redirects to redrawPath for subclasses to work. */ - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { + paintVertexShape(c: SvgCanvas2D, x: number, y: number, w: number, h: number) { c.translate(x, y); c.begin(); this.redrawPath(c, x, y, w, h); @@ -64,13 +60,7 @@ class Actor extends Shape { /** * Draws the path for this shape. */ - redrawPath( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { + redrawPath(c: SvgCanvas2D, x: number, y: number, w: number, h: number) { const width = w / 3; c.moveTo(0, h); c.curveTo(0, (3 * h) / 5, 0, (2 * h) / 5, w / 2, (2 * h) / 5); diff --git a/packages/core/src/view/geometry/shape/Shape.ts b/packages/core/src/view/geometry/shape/Shape.ts index 8d338f276..fa8053b61 100644 --- a/packages/core/src/view/geometry/shape/Shape.ts +++ b/packages/core/src/view/geometry/shape/Shape.ts @@ -8,7 +8,6 @@ import Rectangle from '../Rectangle'; import { getBoundingBox, getDirectedBounds, - getValue, isNotNullish, mod, } from '../../../util/Utils'; @@ -24,30 +23,22 @@ import { SHADOW_OFFSET_Y, } from '../../../util/Constants'; 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 mxClient from '../../../mxClient'; import CellState from '../../cell/datatypes/CellState'; import StencilShape from './node/StencilShape'; import CellOverlay from '../../cell/CellOverlay'; +import ImageBox from '../../image/ImageBox'; import type { + ArrowType, CellStateStyles, ColorValue, DirectionValue, GradientMap, } 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. @@ -94,9 +85,14 @@ const toBool = (i: any) => { */ class Shape { constructor(stencil: StencilShape | null = null) { + // `stencil` is not null when instantiated directly, + // but can be null when instantiated through a child class. if (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. */ - init(container: SVGElement | null = null) { - if (!this.node) { - this.node = this.create(); - - if (container) { - container.appendChild(this.node); - } + init(container: SVGElement) { + if (!this.node.parentNode) { + container.appendChild(this.node); } } @@ -125,7 +117,7 @@ class Shape { * Sets the styles to their default values. */ initStyles() { - this.strokewidth = 1; + this.strokeWidth = 1; this.rotation = 0; this.opacity = 100; this.fillOpacity = 100; @@ -146,31 +138,31 @@ class Shape { opacity = 100; 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; - 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; @@ -184,7 +176,7 @@ class Shape { cursor = ''; - verticalTextRotation: number | null = null; + verticalTextRotation = 0; oldGradients: GradientMap = {}; @@ -238,7 +230,7 @@ class Shape { * * Holds the outermost DOM node that represents this shape. */ - node: SVGGElement | null = null; + node: SVGGElement; /** * Variable: state @@ -332,15 +324,15 @@ class Shape { */ 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 @@ -357,11 +349,11 @@ class Shape { * * Returns 0, or 0.5 if % 2 == 1. */ - getSvgScreenOffset() { + getSvgScreenOffset(): number { const sw = - this.stencil && this.stencil.strokewidth !== 'inherit' - ? Number(this.stencil.strokewidth) - : this.strokewidth ?? 0; + this.stencil && this.stencil.strokeWidthValue !== 'inherit' + ? Number(this.stencil.strokeWidthValue) + : this.strokeWidth ?? 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. */ redraw() { - if (!this.node) return; - this.updateBoundsFromPoints(); if (this.visible && this.checkBounds()) { @@ -419,8 +409,6 @@ class Shape { * Removes all child nodes and resets all CSS. */ clear() { - if (!this.node) return; - while (this.node.lastChild) { this.node.removeChild(this.node.lastChild); } @@ -435,18 +423,11 @@ class Shape { const pts = this.points; if (pts.length > 0 && pts[0]) { - this.bounds = new Rectangle( - Math.round(pts[0].x), - Math.round(pts[0].y), - 1, - 1 - ); + this.bounds = new Rectangle(Math.round(pts[0].x), Math.round(pts[0].y), 1, 1); for (const pt of pts) { if (pt) { - this.bounds.add( - new Rectangle(Math.round(pt.x), Math.round(pt.y), 1, 1) - ); + this.bounds.add(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. */ getLabelBounds(rect: Rectangle) { - const d = getValue(this.style, 'direction', DIRECTION_EAST); + const d = this.style?.direction ?? DIRECTION_EAST; let bounds = rect.clone(); // Normalizes argument for getLabelMargins hook @@ -480,15 +461,11 @@ class Shape { if (labelMargins) { labelMargins = labelMargins.clone(); - let flipH = toBool(getValue(this.style, 'flipH', false)); - let flipV = toBool(getValue(this.style, 'flipV', false)); + let flipH = this.style?.flipH ?? false; + let flipV = this.style?.flipV ?? false; // Handles special case for vertical labels - if ( - this.state && - this.state.text && - this.state.text.isPaintBoundsInverted() - ) { + if (this.state && this.state.text && this.state.text.isPaintBoundsInverted()) { const tmp = labelMargins.x; labelMargins.x = labelMargins.height; labelMargins.height = labelMargins.width; @@ -540,8 +517,6 @@ class Shape { * Updates the SVG or VML shape. */ redrawShape() { - if (!this.node) return; - const canvas = this.createCanvas(); if (canvas) { @@ -552,7 +527,7 @@ class Shape { this.paint(canvas); this.afterPaint(canvas); - if (this.node !== canvas.root) { + if (this.node !== canvas.root && canvas.root) { // Forces parsing in IE8 standards mode - slow! avoid this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML); } @@ -570,7 +545,7 @@ class Shape { const canvas = this.createSvgCanvas(); if (canvas && this.outline) { - canvas.setStrokeWidth(this.strokewidth); + canvas.setStrokeWidth(this.strokeWidth); canvas.setStrokeColor(this.stroke); if (this.isDashed) { @@ -596,7 +571,7 @@ class Shape { createSvgCanvas() { 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.pointerEventsValue = this.svgPointerEvents; @@ -626,9 +601,9 @@ class Shape { * Destroys the given canvas which was used for drawing. This implementation * increments the reference counts on all shared gradients used in the canvas. */ - destroyCanvas(canvas: mxSvgCanvas2D) { + destroyCanvas(canvas: AbstractCanvas2D) { // Manages reference counts - if (canvas instanceof mxSvgCanvas2D) { + if (canvas instanceof SvgCanvas2D) { // Increments ref counts for (const key in canvas.gradients) { const gradient = canvas.gradients[key]; @@ -648,19 +623,19 @@ class Shape { * * Invoked before paint is called. */ - beforePaint(c: mxSvgCanvas2D) {} + beforePaint(c: AbstractCanvas2D) {} /** * Function: afterPaint * * Invokes after paint was called. */ - afterPaint(c: mxSvgCanvas2D) {} + afterPaint(c: AbstractCanvas2D) {} /** * Generic rendering code. */ - paint(c: mxSvgCanvas2D) { + paint(c: AbstractCanvas2D) { let strokeDrawn = false; if (c && this.outline) { @@ -705,20 +680,13 @@ class Shape { let bg = null; if ( - (!this.stencil && - this.points.length === 0 && - this.shapePointerEvents) || + (!this.stencil && this.points.length === 0 && this.shapePointerEvents) || (this.stencil && this.stencilPointerEvents) ) { const bb = this.createBoundingBox(); if (bb && this.node) { - bg = this.createTransparentSvgRectangle( - bb.x, - bb.y, - bb.width, - bb.height - ); + bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height); this.node.appendChild(bg); } } @@ -727,7 +695,7 @@ class Shape { this.stencil.drawShape(c, this, x, y, w, h); } else { // Stencils have separate strokewidth - c.setStrokeWidth(this.strokewidth); + c.setStrokeWidth(this.strokeWidth); if (this.points.length > 0) { // Paints edge shape @@ -763,49 +731,30 @@ class 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: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { - let dash = null; + configureCanvas(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + let dash = NONE; - if (this.style != null) { + if (this.style) { dash = this.style.dashPattern; } - c.setAlpha(this.opacity / 100); - c.setFillAlpha(this.fillOpacity / 100); - c.setStrokeAlpha(this.strokeOpacity / 100); + c.setAlpha(this.opacity / 100); + c.setFillAlpha(this.fillOpacity / 100); + c.setStrokeAlpha(this.strokeOpacity / 100); // Sets alpha, colors and gradients - if (this.isShadow != null) { + if (this.isShadow) { c.setShadow(this.isShadow); } // Dash pattern - if (this.isDashed != null) { - c.setDashed( - this.isDashed, - this.style != null - ? toBool(getValue(this.style, 'fixDash', false)) - : false - ); + if (this.isDashed) { + c.setDashed(this.isDashed, this.style?.fixDash ?? false); } - if (dash != null) { - c.setDashPattern(dash); - } + c.setDashPattern(dash); - if ( - this.fill != null && - this.fill !== NONE && - this.gradient && - this.gradient !== NONE - ) { + if (this.fill !== NONE && this.gradient !== NONE) { const b = this.getGradientBounds(c, x, y, w, h); c.setGradient( this.fill, @@ -828,14 +777,7 @@ class 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: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + getGradientBounds(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { return new Rectangle(x, y, w, h); } @@ -844,25 +786,12 @@ class Shape { * * Sets the scale and rotation on the given canvas. */ - // updateTransform(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - updateTransform( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + updateTransform(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { // NOTE: Currently, scale is implemented in state and canvas. This will // 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. c.scale(this.scale); - c.rotate( - this.getShapeRotation(), - this.flipH, - this.flipV, - x + w / 2, - y + h / 2 - ); + c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2); } /** @@ -870,21 +799,10 @@ class Shape { * * Paints the vertex shape. */ - // paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { this.paintBackground(c, x, y, w, h); - if ( - !this.outline || - this.style == null || - toBool(getValue(this.style, 'backgroundOutline', 0) === false) - ) { + if (!this.outline || !this.style || this.style.backgroundOutline === 0) { c.setShadow(false); this.paintForeground(c, x, y, w, h); } @@ -895,55 +813,32 @@ class Shape { * * Hook for subclassers. This implementation is empty. */ - // paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintBackground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) {} + paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {} /** * Hook for subclassers. This implementation is empty. */ - // paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintForeground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) {} + paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) {} /** * Function: paintEdgeShape * * Hook for subclassers. This implementation is empty. */ - // paintEdgeShape(c: mxAbstractCanvas2D, pts: mxPoint[]): void; - paintEdgeShape(c: mxSvgCanvas2D, pts: Point[]): void {} + paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) {} /** * Function: getArcSize * * Returns the arc size for the given dimension. */ - // getArcSize(w: number, h: number): number; - getArcSize(w: number, h: number): number { + getArcSize(w: number, h: number) { let r = 0; - if (toBool(getValue(this.style, 'absoluteArcSize', 0))) { - r = Math.min( - w / 2, - Math.min(h / 2, getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2) - ); + if (this.style?.absoluteArcSize === 0) { + r = Math.min(w / 2, Math.min(h / 2, (this.style?.arcSize ?? LINE_ARCSIZE) / 2)); } else { - const f = parseFloat( - String( - getValue(this.style, 'arcSize', RECTANGLE_ROUNDING_FACTOR * 100) / 100 - ) - ); + const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100; r = Math.min(w * f, h * f); } return r; @@ -954,16 +849,15 @@ class Shape { * * Paints the glass gradient effect. */ - // paintGlassEffect(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number, arc: number): void; paintGlassEffect( - c: mxSvgCanvas2D, + c: AbstractCanvas2D, x: number, y: number, w: number, h: number, arc: number ) { - const sw = Math.ceil((this.strokewidth ?? 0) / 2); + const sw = Math.ceil((this.strokeWidth ?? 0) / 2); const size = 0.4; 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. */ addPoints( - c: mxSvgCanvas2D, + c: AbstractCanvas2D, pts: Point[], rounded: boolean = false, arcSize: number, @@ -1009,10 +903,7 @@ class Shape { if (close && rounded) { pts = pts.slice(); const p0 = pts[0]; - const wp = new Point( - pe.x + (p0.x - pe.x) / 2, - pe.y + (p0.y - pe.y) / 2 - ); + const wp = new Point(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2); pts.splice(0, 0, wp); } @@ -1096,15 +987,15 @@ class Shape { this.spacing = 0; - this.fill = null; - this.gradient = null; - this.gradientDirection = null; - this.stroke = null; - this.startSize = null; - this.endSize = null; - this.startArrow = null; - this.endArrow = null; - this.direction = null; + this.fill = NONE; + this.gradient = NONE; + this.gradientDirection = DIRECTION_EAST; + this.stroke = NONE; + this.startSize = 1; + this.endSize = 1; + this.startArrow = NONE; + this.endArrow = NONE; + this.direction = DIRECTION_EAST; this.isShadow = false; this.isDashed = false; @@ -1151,81 +1042,35 @@ class Shape { this.state = state; this.style = state.style; - const ifNotNullElse = (value: any, defaultValue: any) => { - if (isNotNullish(value)) { - return value; - } - - return defaultValue; - }; - if (this.style) { - this.fill = ifNotNullElse(this.style.fillColor, this.fill); - this.gradient = ifNotNullElse(this.style.gradientColor, this.gradient); - this.gradientDirection = ifNotNullElse( - this.style.gradientDirection, - this.gradientDirection - ); - this.opacity = ifNotNullElse(this.style.opacity, this.opacity); - this.fillOpacity = ifNotNullElse( - this.style.fillOpacity, - this.fillOpacity - ); - this.strokeOpacity = ifNotNullElse( - this.style.strokeOpacity, - this.strokeOpacity - ); - this.stroke = ifNotNullElse(this.style.strokeColor, this.stroke); - this.strokewidth = ifNotNullElse( - 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.fill = this.style.fillColor; + this.gradient = this.style.gradientColor; + this.gradientDirection = this.style.gradientDirection; + this.opacity = this.style.opacity; + this.fillOpacity = this.style.fillOpacity; + this.strokeOpacity = this.style.strokeOpacity; + this.stroke = this.style.strokeColor; + this.strokeWidth = this.style.strokeWidth; + this.spacing = this.style.spacing; + this.startSize = this.style.startSize; + this.endSize = this.style.endSize; + this.startArrow = this.style.startArrow; + this.endArrow = this.style.endArrow; + this.rotation = this.style.rotation; + this.direction = this.style.direction; + this.flipH = this.style.flipH; + this.flipV = this.style.flipV; - this.flipH = toBool(ifNotNullElse(this.style.flipH, 0)); - 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 - ) { + if (this.direction === DIRECTION_NORTH || this.direction === DIRECTION_SOUTH) { const tmp = this.flipH; this.flipH = this.flipV; this.flipV = tmp; } - this.isShadow = toBool(ifNotNullElse(this.style.shadow, this.isShadow)); - this.isDashed = toBool(ifNotNullElse(this.style.dashed, this.isDashed)); - this.isRounded = toBool( - ifNotNullElse(this.style.rounded, this.isRounded) - ); - 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; - } + this.isShadow = this.style.shadow; + this.isDashed = this.style.dashed; + this.isRounded = this.style.rounded; + this.glass = this.style.glass; } } @@ -1240,10 +1085,7 @@ class Shape { */ setCursor(cursor: string) { this.cursor = cursor; - - if (this.node) { - this.node.style.cursor = cursor; - } + this.node.style.cursor = cursor; } /** @@ -1258,7 +1100,7 @@ class Shape { /** * 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; } @@ -1271,7 +1113,7 @@ class Shape { updateBoundingBox() { // Tries to get bounding box from SVG subsystem // LATER: Use getBoundingClientRect for fallback in VML - if (this.useSvgBoundingBox && this.node && this.node.ownerSVGElement) { + if (this.useSvgBoundingBox && this.node.ownerSVGElement) { try { const b = this.node.getBBox(); @@ -1279,7 +1121,7 @@ class Shape { this.boundingBox = new Rectangle(b.x, b.y, b.width, b.height); // Adds strokeWidth - this.boundingBox.grow(((this.strokewidth ?? 0) * this.scale) / 2); + this.boundingBox.grow(((this.strokeWidth ?? 0) * this.scale) / 2); return; } @@ -1316,8 +1158,7 @@ class Shape { const bb = this.bounds.clone(); if ( (this.stencil && - (this.direction === DIRECTION_NORTH || - this.direction === DIRECTION_SOUTH)) || + (this.direction === DIRECTION_NORTH || this.direction === DIRECTION_SOUTH)) || this.isPaintBoundsInverted() ) { bb.rotate90(); @@ -1334,8 +1175,9 @@ class Shape { bbox.width += Math.ceil(SHADOW_OFFSET_X * this.scale); bbox.height += Math.ceil(SHADOW_OFFSET_Y * this.scale); } + // 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() { let rot = this.getRotation(); - if (!toBool(getValue(this.style, 'horizontal', 1))) { + if (!(this.style?.horizontal ?? true)) { rot += this.verticalTextRotation || -90; // WARNING WARNING!!!! =============================================================================================== } @@ -1383,14 +1225,12 @@ class Shape { getShapeRotation() { let rot = this.getRotation(); - if (this.direction) { - if (this.direction === DIRECTION_NORTH) { - rot += 270; - } else if (this.direction === DIRECTION_WEST) { - rot += 180; - } else if (this.direction === DIRECTION_SOUTH) { - rot += 90; - } + if (this.direction === DIRECTION_NORTH) { + rot += 270; + } else if (this.direction === DIRECTION_WEST) { + rot += 180; + } else if (this.direction === DIRECTION_SOUTH) { + rot += 90; } return rot; @@ -1413,6 +1253,8 @@ class Shape { return rect; } + redrawHtmlShape() {} + /** * Function: setTransparentBackgroundImage * @@ -1450,16 +1292,14 @@ class Shape { * node associated with the shape using . */ destroy() { - if (this.node) { - InternalEvent.release(this.node); + InternalEvent.release(this.node); - if (this.node.parentNode) { - this.node.parentNode.removeChild(this.node); - } - - this.node = null; + if (this.node.parentNode) { + this.node.parentNode.removeChild(this.node); } + this.node.innerHTML = ''; + // Decrements refCount and removes unused this.releaseSvgGradients(this.oldGradients); this.oldGradients = {}; diff --git a/packages/core/src/view/geometry/shape/edge/Arrow.ts b/packages/core/src/view/geometry/shape/edge/Arrow.ts index 31cb3ddb9..102120880 100644 --- a/packages/core/src/view/geometry/shape/edge/Arrow.ts +++ b/packages/core/src/view/geometry/shape/edge/Arrow.ts @@ -7,8 +7,9 @@ import Shape from '../Shape'; import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH } from '../../../../util/Constants'; import Rectangle from '../../Rectangle'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; 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. @@ -16,31 +17,41 @@ import Point from '../../Point'; * This shape is registered under {@link mxConstants.SHAPE_ARROW} in {@link mxCellRenderer}. */ 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(); this.points = points; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth != null ? strokewidth : 1; - this.arrowWidth = arrowWidth != null ? arrowWidth : ARROW_WIDTH; - this.spacing = spacing != null ? spacing : ARROW_SPACING; - this.endSize = endSize != null ? endSize : ARROW_SIZE; + this.strokeWidth = strokeWidth; + this.arrowWidth = arrowWidth; + this.spacing = spacing; + this.endSize = endSize; } + arrowWidth: number; + /** * Augments the bounding box with the edge width and markers. */ - augmentBoundingBox(bbox: Rectangle): void { + augmentBoundingBox(bbox: Rectangle) { super.augmentBoundingBox(bbox); 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. */ - paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { + paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) { // Geometry of arrow const spacing = ARROW_SPACING; const width = ARROW_WIDTH; diff --git a/packages/core/src/view/geometry/shape/edge/ArrowConnector.ts b/packages/core/src/view/geometry/shape/edge/ArrowConnector.ts index 9f2b69be1..1ecea57c9 100644 --- a/packages/core/src/view/geometry/shape/edge/ArrowConnector.ts +++ b/packages/core/src/view/geometry/shape/edge/ArrowConnector.ts @@ -6,11 +6,12 @@ */ import Shape from '../Shape'; import { ARROW_SIZE, ARROW_SPACING, ARROW_WIDTH, NONE } from '../../../../util/Constants'; -import utils, { getNumber, getValue, relativeCcw } from '../../../../util/Utils'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import { relativeCcw } from '../../../../util/Utils'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Point from '../../Point'; import Rectangle from '../../Rectangle'; 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 @@ -19,37 +20,48 @@ import CellState from '../../../cell/datatypes/CellState'; * This shape is registered under {@link mxConstants.SHAPE_ARROW_CONNECTOR} in {@link mxCellRenderer}. */ 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(); this.points = points; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth != null ? strokewidth : 1; - this.arrowWidth = arrowWidth != null ? arrowWidth : ARROW_WIDTH; - this.arrowSpacing = spacing != null ? spacing : ARROW_SPACING; + this.strokeWidth = strokeWidth; + this.arrowWidth = arrowWidth; + this.arrowSpacing = spacing; 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. * @defaultValue `false` for performance reasons. */ - useSvgBoundingBox: boolean = true; + useSvgBoundingBox = true; /** * Function: isRoundable * * Hook for subclassers. */ - isRoundable(): boolean { + isRoundable() { return true; } /** * Overrides mxShape to reset spacing. */ - resetStyles(): void { + resetStyles() { super.resetStyles(); this.arrowSpacing = ARROW_SPACING; } @@ -60,9 +72,9 @@ class ArrowConnector extends Shape { apply(state: CellState): void { super.apply(state); - if (this.style != null) { - this.startSize = getNumber(this.style, 'startSize', ARROW_SIZE / 5) * 3; - this.endSize = getNumber(this.style, 'endSize', ARROW_SIZE / 5) * 3; + if (this.style) { + this.startSize = this.style.startSize * 3; + this.endSize = this.style.endSize * 3; } } @@ -82,21 +94,18 @@ class ArrowConnector extends Shape { 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. */ - paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { + paintEdgeShape(c: AbstractCanvas2D, pts: Point[]): void { // Geometry of arrow - let strokeWidth = this.strokewidth; + let strokeWidth = this.strokeWidth; if (this.outline) { - strokeWidth = Math.max( - 1, - utils.getNumber(this.style, 'strokeWidth', this.strokewidth) - ); + strokeWidth = Math.max(1, this.style?.strokeWidth ?? 0); } 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 const strokeWidthFactor = Math.max( tmp, - Math.min(this.strokewidth / 200 + 0.04, 0.35) + Math.min(this.strokeWidth / 200 + 0.04, 0.35) ); const angleFactor = pos !== 0 && isRounded @@ -387,7 +396,18 @@ class ArrowConnector extends Shape { * * 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 orthx = (edgeWidth * ny) / 2; const orthy = (-edgeWidth * nx) / 2; @@ -416,50 +436,50 @@ class ArrowConnector extends Shape { /** * @returns whether the arrow is rounded */ - isArrowRounded(): boolean { + isArrowRounded() { return this.isRounded; } /** * @returns the width of the start arrow */ - getStartArrowWidth(): number { + getStartArrowWidth() { return ARROW_WIDTH; } /** * @returns the width of the end arrow */ - getEndArrowWidth(): number { + getEndArrowWidth() { return ARROW_WIDTH; } /** * @returns the width of the body of the edge */ - getEdgeWidth(): number { + getEdgeWidth() { return ARROW_WIDTH / 3; } /** * @returns whether the ends of the shape are drawn */ - isOpenEnded(): boolean { + isOpenEnded() { return false; } /** * @returns whether the start marker is drawn */ - isMarkerStart(): boolean { - return getValue(this.style, 'startArrow', NONE) !== NONE; + isMarkerStart() { + return (this.style?.startArrow ?? NONE) !== NONE; } /** * @returns whether the end marker is drawn */ - isMarkerEnd(): boolean { - return getValue(this.style, 'endArrow', NONE) !== NONE; + isMarkerEnd() { + return (this.style?.endArrow ?? NONE) !== NONE; } } diff --git a/packages/core/src/view/geometry/shape/edge/Connector.ts b/packages/core/src/view/geometry/shape/edge/Connector.ts index 70b388dad..fa1dc31a7 100644 --- a/packages/core/src/view/geometry/shape/edge/Connector.ts +++ b/packages/core/src/view/geometry/shape/edge/Connector.ts @@ -6,11 +6,12 @@ */ import { DEFAULT_MARKERSIZE, NONE } from '../../../../util/Constants'; import Polyline from './Polyline'; -import utils, { getNumber, getValue } from '../../../../util/Utils'; +import { getNumber } from '../../../../util/Utils'; import Marker from './Marker'; import Point from '../../Point'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Rectangle from '../../Rectangle'; +import { ColorValue } from 'packages/core/src/types'; /** * Extends {@link mxShape} to implement a connector shape. @@ -21,7 +22,7 @@ import Rectangle from '../../Rectangle'; * @extends {Polyline} */ class Connector extends Polyline { - constructor(points: Point[], stroke: string, strokewidth: number) { + constructor(points: Point[], stroke: ColorValue, strokewidth: number) { super(points, stroke, strokewidth); } @@ -29,15 +30,15 @@ class Connector extends Polyline { * Updates the for this shape using * and augmentBoundingBox and stores the result in . */ - updateBoundingBox(): void { - this.useSvgBoundingBox = this.style != null && this.style.curved === 1; + updateBoundingBox() { + this.useSvgBoundingBox = !!this.style?.curved; super.updateBoundingBox(); } /** * 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 // order to apply the offsets before painting the line and // paint the markers after painting the line. @@ -51,11 +52,11 @@ class Connector extends Polyline { c.setShadow(false); c.setDashed(false); - if (sourceMarker != null) { + if (sourceMarker) { sourceMarker(); } - if (targetMarker != null) { + if (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. */ - createMarker(c: mxAbstractCanvas2D, pts: Point[], source: boolean): Marker { + createMarker(c: AbstractCanvas2D, pts: Point[], source: boolean) { + if (!this.style) return null; + let result = null; 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]; 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; // Uses next non-overlapping point @@ -92,15 +96,11 @@ class Connector extends Polyline { const unitX = dx / dist; const unitY = dy / dist; - const size = getNumber( - this.style, - source ? 'startSize' : 'endSize', - DEFAULT_MARKERSIZE - ); + const size = source ? this.style.startSize : this.style.endSize; // Allow for stroke width in the end point used and the // 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( c, @@ -111,7 +111,7 @@ class Connector extends Polyline { unitY, size, source, - this.strokewidth, + this.strokeWidth, filled ); } @@ -122,17 +122,19 @@ class Connector extends Polyline { /** * Augments the bounding box with the strokewidth and shadow offsets. */ - augmentBoundingBox(bbox: Rectangle): void { + augmentBoundingBox(bbox: Rectangle) { super.augmentBoundingBox(bbox); + if (!this.style) return; + // Adds marker sizes let size = 0; - if (getValue(this.style, 'startArrow', NONE) !== NONE) { + if (this.style.startArrow !== NONE) { 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; } diff --git a/packages/core/src/view/geometry/shape/edge/Line.ts b/packages/core/src/view/geometry/shape/edge/Line.ts index 02d5cec4c..8bdfdd0fc 100644 --- a/packages/core/src/view/geometry/shape/edge/Line.ts +++ b/packages/core/src/view/geometry/shape/edge/Line.ts @@ -5,8 +5,9 @@ * Type definitions from the typed-mxgraph project */ import Shape from '../Shape'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Rectangle from '../../Rectangle'; +import { ColorValue } from 'packages/core/src/types'; /** * Extends {@link Shape} to implement a horizontal line shape. @@ -15,12 +16,12 @@ import Rectangle from '../../Rectangle'; * @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(); this.bounds = bounds; this.stroke = stroke; - this.strokewidth = strokewidth != null ? strokewidth : 1; - this.vertical = vertical != null ? vertical : this.vertical; + this.strokeWidth = strokeWidth; + this.vertical = vertical; } /** @@ -28,23 +29,17 @@ class Line extends Shape { * * Whether to paint a vertical line. */ - vertical = false; + vertical: boolean; /** * Redirects to redrawPath for subclasses to work. - * @param {mxAbstractCanvas2D} c + * @param {AbstractCanvas2D} c * @param {number} x * @param {number} y * @param {number} w * @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) { c.begin(); if (this.vertical) { diff --git a/packages/core/src/view/geometry/shape/edge/Marker.ts b/packages/core/src/view/geometry/shape/edge/Marker.ts index ac32b9425..6051e85dd 100644 --- a/packages/core/src/view/geometry/shape/edge/Marker.ts +++ b/packages/core/src/view/geometry/shape/edge/Marker.ts @@ -4,11 +4,15 @@ * Updated to ES9 syntax by David Morrissey 2021 * Type definitions from the typed-mxgraph project */ +import { ArrowType } from 'packages/core/src/types'; +import AbstractCanvas2D from 'packages/core/src/util/canvas/AbstractCanvas2D'; import { ARROW_CLASSIC, ARROW_CLASSIC_THIN, ARROW_DIAMOND, } 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. @@ -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 */ - // static markers: object; - static markers = []; + static markers: Record = {}; /** * Adds a factory method that updates a given endpoint and returns a * function to paint the marker onto the given canvas. */ - // static addMarker(type: string, funct: Function): void; - static addMarker(type, funct) { + static addMarker(type: string, funct: Function) { Marker.markers[type] = funct; } @@ -38,9 +40,20 @@ class 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]; - return funct != null + return funct ? funct(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) : null; } @@ -50,10 +63,19 @@ class Marker { * Adds the classic and block marker factory method. */ (() => { - function createArrow(widthFactor) { - widthFactor = widthFactor != null ? widthFactor : 2; - - return (canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) => { + function createArrow(widthFactor = 2) { + return ( + 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 // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for // only half the strokewidth is processed ). @@ -103,10 +125,19 @@ class Marker { Marker.addMarker('block', createArrow(2)); Marker.addMarker('blockThin', createArrow(3)); - function createOpenArrow(widthFactor) { - widthFactor = widthFactor != null ? widthFactor : 2; - - return (canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) => { + function createOpenArrow(widthFactor = 2) { + return ( + 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 // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for // only half the strokewidth is processed ). @@ -144,7 +175,18 @@ class Marker { Marker.addMarker( '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 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 // 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. diff --git a/packages/core/src/view/geometry/shape/edge/Polyline.ts b/packages/core/src/view/geometry/shape/edge/Polyline.ts index 29e367eb1..fd2368e4f 100644 --- a/packages/core/src/view/geometry/shape/edge/Polyline.ts +++ b/packages/core/src/view/geometry/shape/edge/Polyline.ts @@ -6,9 +6,9 @@ */ import Shape from '../Shape'; import { LINE_ARCSIZE } from '../../../../util/Constants'; -import utils, { getValue } from '../../../../util/Utils'; 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 @@ -31,42 +31,42 @@ import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; * 1. This is stored in . */ class Polyline extends Shape { - constructor(points: Point[], stroke: string, strokewidth: number) { + constructor(points: Point[], stroke: ColorValue, strokeWidth = 1) { super(); this.points = points; this.stroke = stroke; - this.strokewidth = strokewidth != null ? strokewidth : 1; + this.strokeWidth = strokeWidth; } /** * Returns 0. */ - getRotation(): number { + getRotation() { return 0; } /** * Returns 0. */ - getShapeRotation(): number { + getShapeRotation() { return 0; } /** * Returns false. */ - isPaintBoundsInverted(): boolean { + isPaintBoundsInverted() { return false; } /** * Paints the line shape. */ - paintEdgeShape(c: mxAbstractCanvas2D, pts: Point[]): void { + paintEdgeShape(c: AbstractCanvas2D, pts: Point[]) { const prev = c.pointerEventsValue; c.pointerEventsValue = 'stroke'; - if (this.style == null || this.style.curved != 1) { + if (!this.style || !this.style.curved) { this.paintLine(c, pts, this.isRounded); } else { this.paintCurvedLine(c, pts); @@ -77,8 +77,9 @@ class Polyline extends Shape { /** * Paints the line shape. */ - paintLine(c: mxAbstractCanvas2D, pts: Point[], rounded?: boolean): void { - const arcSize = getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; + paintLine(c: AbstractCanvas2D, pts: Point[], rounded?: boolean) { + const arcSize = this.style?.arcSize ?? LINE_ARCSIZE; + c.begin(); this.addPoints(c, pts, rounded, arcSize, false); c.stroke(); @@ -87,7 +88,7 @@ class Polyline extends Shape { /** * Paints the line shape. */ - paintCurvedLine(c: mxAbstractCanvas2D, pts: Point[]): void { + paintCurvedLine(c: AbstractCanvas2D, pts: Point[]) { c.begin(); const pt = pts[0]; diff --git a/packages/core/src/view/geometry/shape/node/CloudShape.ts b/packages/core/src/view/geometry/shape/node/CloudShape.ts index 896ada028..d99a73ffc 100644 --- a/packages/core/src/view/geometry/shape/node/CloudShape.ts +++ b/packages/core/src/view/geometry/shape/node/CloudShape.ts @@ -5,7 +5,7 @@ * Type definitions from the typed-mxgraph project */ import Actor from '../Actor'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Rectangle from '../../Rectangle'; /** @@ -14,30 +14,18 @@ import Rectangle from '../../Rectangle'; * This shape is registered under {@link mxConstants.SHAPE_CLOUD} in {@link cellRenderer}. */ class CloudShape extends Actor { - constructor( - bounds: Rectangle, - fill: string, - stroke: string, - strokewidth: number = 1 - ) { + constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokeWidth; } /** * Draws the path for this shape. */ - // redrawPath(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - redrawPath( - c: mxAbstractCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { 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, 0.66 * h, 0.18 * w, 0.9 * h, 0.31 * w, 0.8 * h); diff --git a/packages/core/src/view/geometry/shape/node/CylinderShape.ts b/packages/core/src/view/geometry/shape/node/CylinderShape.ts index f92a992b4..bc74aa637 100644 --- a/packages/core/src/view/geometry/shape/node/CylinderShape.ts +++ b/packages/core/src/view/geometry/shape/node/CylinderShape.ts @@ -5,10 +5,9 @@ * Type definitions from the typed-mxgraph project */ import Shape from '../Shape'; -import utils from '../../../../util/Utils'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; 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 @@ -17,52 +16,34 @@ import Rectangle from '../../Rectangle'; * This shape is registered under {@link mxConstants.SHAPE_CYLINDER} in {@link cellRenderer}. */ class CylinderShape extends Shape { - constructor( - bounds: Rectangle, - fill: string, - stroke: string, - strokewidth: number = 1 - ) { + constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokeWidth; } /** * Defines the maximum height of the top and bottom part of the cylinder shape. */ - // maxHeight: number; maxHeight = 40; /** * Sets stroke tolerance to 0 for SVG. */ - // svgStrokeTolerance: number; svgStrokeTolerance = 0; /** * Redirects to redrawPath for subclasses to work. */ - // paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { c.translate(x, y); c.begin(); this.redrawPath(c, x, y, w, h, false); c.fillAndStroke(); - if ( - !this.outline || - this.style == null || - utils.getValue(this.style, 'backgroundOutline', 0) == 0 - ) { + if (!this.outline || !this.style || this.style.backgroundOutline === 0) { c.setShadow(false); c.begin(); this.redrawPath(c, x, y, w, h, true); @@ -73,17 +54,15 @@ class CylinderShape extends Shape { /** * 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): number { + getCylinderSize(x: number, y: number, w: number, h: number) { return Math.min(this.maxHeight, Math.round(h / 5)); } /** * Draws the path for this shape. */ - // redrawPath(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number, isForeground: boolean): void; redrawPath( - c: mxSvgCanvas2D, + c: AbstractCanvas2D, x: number, y: number, w: number, @@ -92,10 +71,7 @@ class CylinderShape extends Shape { ): void { const dy = this.getCylinderSize(x, y, w, h); - if ( - (isForeground && this.fill != null) || - (!isForeground && this.fill == null) - ) { + if ((isForeground && this.fill !== NONE) || (!isForeground && this.fill === NONE)) { c.moveTo(0, dy); c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); diff --git a/packages/core/src/view/geometry/shape/node/DoubleEllipseShape.ts b/packages/core/src/view/geometry/shape/node/DoubleEllipseShape.ts index a565c4ae6..6fed746c1 100644 --- a/packages/core/src/view/geometry/shape/node/DoubleEllipseShape.ts +++ b/packages/core/src/view/geometry/shape/node/DoubleEllipseShape.ts @@ -7,8 +7,7 @@ import Rectangle from '../../Rectangle'; import Shape from '../Shape'; -import utils from '../../../../util/Utils'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; /** * Extends {@link Shape} to implement a double ellipse shape. @@ -39,32 +38,18 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; * ``` */ class DoubleEllipseShape extends Shape { - strokewidth: number; - - constructor( - bounds: Rectangle, - fill: string, - stroke: string, - strokewidth: number = 1 - ) { + constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokeWidth; } /** * Paints the background. */ - // paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintBackground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { c.ellipse(x, y, w, h); c.fillAndStroke(); } @@ -72,20 +57,11 @@ class DoubleEllipseShape extends Shape { /** * Paints the foreground. */ - // paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintForeground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { if (!this.outline) { - const margin = utils.getValue( - this.style, - 'margin', - Math.min(3 + this.strokewidth, Math.min(w / 5, h / 5)) - ); + const margin = + this.style?.margin ?? Math.min(3 + this.strokeWidth, Math.min(w / 5, h / 5)); + x += margin; y += margin; w -= 2 * margin; @@ -103,16 +79,12 @@ class DoubleEllipseShape extends Shape { /** * @returns the bounds for the label. */ - // getLabelBounds(rect: mxRectangle): mxRectangle; getLabelBounds(rect: Rectangle) { const margin = - utils.getValue( - this.style, - 'margin', - Math.min( - 3 + this.strokewidth, - Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale) - ) + this.style?.margin ?? + Math.min( + 3 + this.strokeWidth, + Math.min(rect.width / 5 / this.scale, rect.height / 5 / this.scale) ) * this.scale; return new Rectangle( diff --git a/packages/core/src/view/geometry/shape/node/EllipseShape.ts b/packages/core/src/view/geometry/shape/node/EllipseShape.ts index c18d3f041..7a446f3b8 100644 --- a/packages/core/src/view/geometry/shape/node/EllipseShape.ts +++ b/packages/core/src/view/geometry/shape/node/EllipseShape.ts @@ -5,7 +5,7 @@ * Type definitions from the typed-mxgraph project */ import Shape from '../Shape'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Rectangle from '../../Rectangle'; /** @@ -13,29 +13,18 @@ import Rectangle from '../../Rectangle'; * This shape is registered under mxConstants.SHAPE_ELLIPSE in mxCellRenderer. */ class EllipseShape extends Shape { - constructor( - bounds: Rectangle, - fill: string, - stroke: string, - strokewidth: number = 1 - ) { + constructor(bounds: Rectangle, fill: string, stroke: string, strokeWidth = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokeWidth; } /** * Paints the ellipse shape. */ - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { c.ellipse(x, y, w, h); c.fillAndStroke(); } diff --git a/packages/core/src/view/geometry/shape/node/HexagonShape.ts b/packages/core/src/view/geometry/shape/node/HexagonShape.ts index a8e7af293..c001e6105 100644 --- a/packages/core/src/view/geometry/shape/node/HexagonShape.ts +++ b/packages/core/src/view/geometry/shape/node/HexagonShape.ts @@ -6,9 +6,8 @@ */ import Actor from '../Actor'; import Point from '../../Point'; -import utils, { getValue } from '../../../../util/Utils'; import { LINE_ARCSIZE } from '../../../../util/Constants'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; /** * Implementation of the hexagon shape. @@ -28,8 +27,9 @@ class HexagonShape extends Actor { * @param {number} w * @param {number} h */ - redrawPath(c: mxSvgCanvas2D, x: number, y: number, w: number, h: number): void { - const arcSize = getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; + redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2; + this.addPoints( c, [ diff --git a/packages/core/src/view/geometry/shape/node/ImageShape.ts b/packages/core/src/view/geometry/shape/node/ImageShape.ts index 15010a9ae..1c6bbd103 100644 --- a/packages/core/src/view/geometry/shape/node/ImageShape.ts +++ b/packages/core/src/view/geometry/shape/node/ImageShape.ts @@ -5,12 +5,12 @@ * Type definitions from the typed-mxgraph project */ -import utils from '../../../../util/Utils'; import RectangleShape from './RectangleShape'; import Rectangle from '../../Rectangle'; import CellState from '../../../cell/datatypes/CellState'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/SvgCanvas2D'; import CellOverlay from '../../../cell/CellOverlay'; +import { NONE } from 'packages/core/src/util/Constants'; /** * Extends {@link mxShape} to implement an image shape. @@ -22,24 +22,21 @@ import CellOverlay from '../../../cell/CellOverlay'; class ImageShape extends RectangleShape { constructor( bounds: Rectangle, - image: string, + imageSrc: string, fill: string = '#FFFFFF', stroke: string = '#000000', - strokewidth: number = 1 + strokeWidth: number = 1 ) { - super(); - this.bounds = bounds; - this.image = image; - this.fill = fill; - this.stroke = stroke; - this.strokewidth = strokewidth; + super(bounds, fill, stroke, strokeWidth); + + this.imageSrc = imageSrc; this.shadow = false; } // TODO: Document me!! shadow: boolean; - image: string; + imageSrc: string; // Used in mxCellRenderer overlay: CellOverlay | null = null; @@ -54,8 +51,7 @@ class ImageShape extends RectangleShape { /** * Disables offset in IE9 for crisper image output. */ - // getSvgScreenOffset(): number; - getSvgScreenOffset(): number { + getSvgScreenOffset() { return 0; } @@ -76,19 +72,12 @@ class ImageShape extends RectangleShape { apply(state: CellState) { super.apply(state); - this.fill = null; - this.stroke = null; - this.gradient = null; + this.fill = NONE; + this.stroke = NONE; + this.gradient = NONE; - if (this.style != null) { - this.preserveImageAspect = - 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; + if (this.style) { + this.preserveImageAspect = this.style.imageAspect; } } @@ -96,8 +85,7 @@ class ImageShape extends RectangleShape { * Returns true if HTML is allowed for this shape. This implementation always * returns false. */ - // isHtmlAllowed(): boolean; - isHtmlAllowed(): boolean { + isHtmlAllowed() { return !this.preserveImageAspect; } @@ -106,8 +94,7 @@ class ImageShape extends RectangleShape { * this shape. This implementation falls back to * so that the HTML creation is optional. */ - // createHtml(): HTMLElement; - createHtml(): HTMLElement { + createHtml() { const node = document.createElement('div'); node.style.position = 'absolute'; return node; @@ -116,33 +103,19 @@ class ImageShape extends RectangleShape { /** * Disables inherited roundable support. */ - // isRoundable(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): boolean; - isRoundable( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): boolean { + isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { return false; } /** * Generic background painting implementation. */ - // paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - 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); + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + if (this.imageSrc && this.style) { + const fill = this.style.imageBackground; + const stroke = this.style.imageBorder; - if (fill != null) { + if (fill !== NONE) { // Stroke rendering required for shadow c.setFillColor(fill); c.setStrokeColor(stroke); @@ -151,9 +124,7 @@ class ImageShape extends RectangleShape { } // FlipH/V are implicit via mxShape.updateTransform - c.image(x, y, w, h, this.image, this.preserveImageAspect, false, false); - - stroke = utils.getValue(this.style, 'imageBorder', null); + c.image(x, y, w, h, this.imageSrc, this.preserveImageAspect, false, false); if (stroke != null) { c.setShadow(false); diff --git a/packages/core/src/view/geometry/shape/node/LabelShape.ts b/packages/core/src/view/geometry/shape/node/LabelShape.ts index ea0d1778d..dd54ddfd1 100644 --- a/packages/core/src/view/geometry/shape/node/LabelShape.ts +++ b/packages/core/src/view/geometry/shape/node/LabelShape.ts @@ -13,9 +13,11 @@ import { ALIGN_RIGHT, ALIGN_TOP, DEFAULT_IMAGESIZE, + NONE, } from '../../../../util/Constants'; 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 @@ -37,24 +39,29 @@ import utils from '../../../../util/Utils'; * strokewidth - Optional integer that defines the stroke width. Default is * 1. This is stored in . */ -class Label extends RectangleShape { - constructor(bounds, fill, stroke, strokewidth) { - super(bounds, fill, stroke, strokewidth); +class LabelShape extends RectangleShape { + constructor( + bounds: Rectangle, + fill: ColorValue, + stroke: ColorValue, + strokeWidth: number + ) { + super(bounds, fill, stroke, strokeWidth); } /** * Default width and height for the image. * @default mxConstants.DEFAULT_IMAGESIZE */ - // imageSize: number; imageSize = DEFAULT_IMAGESIZE; + imageSrc: string | null = null; + /** * Default value for image spacing * @type {number} * @default 2 */ - // spacing: number; spacing = 2; /** @@ -62,7 +69,6 @@ class Label extends RectangleShape { * @type {number} * @default 10 */ - // indicatorSize: number; indicatorSize = 10; /** @@ -70,17 +76,17 @@ class Label extends RectangleShape { * @default 2 * @type {number} */ - // indicatorSpacing: number; indicatorSpacing = 2; + indicatorImageSrc: string | null = null; + /** * Initializes the shape and the . */ - // init(container: HTMLElement): void; - init(container) { + init(container: SVGElement) { super.init(container); - if (this.indicatorShape != null) { + if (this.indicatorShape) { this.indicator = new this.indicatorShape(); this.indicator.dialect = this.dialect; this.indicator.init(this.node); @@ -91,9 +97,8 @@ class Label extends RectangleShape { * Reconfigures this shape. This will update the colors of the indicator * and reconfigure it if required. */ - // redraw(): void; redraw() { - if (this.indicator != null) { + if (this.indicator) { this.indicator.fill = this.indicatorColor; this.indicator.stroke = this.indicatorStrokeColor; 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 * no indicator shape. */ - // isHtmlAllowed(): boolean; isHtmlAllowed() { - return ( - super.isHtmlAllowed() && - this.indicatorColor == null && - this.indicatorShape == null - ); + return super.isHtmlAllowed() && this.indicatorColor === NONE && !!this.indicatorShape; } /** @@ -124,8 +124,7 @@ class Label extends RectangleShape { * @param {number} w * @param {number} h */ - // paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintForeground(c, x, y, w, h) { + paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { this.paintImage(c, x, y, w, h); this.paintIndicator(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} h */ - // paintImage(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintImage(c, x, y, w, h) { - if (this.image != null) { + paintImage(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + if (this.imageSrc) { const bounds = this.getImageBounds(x, y, w, h); c.image( bounds.x, bounds.y, bounds.width, bounds.height, - this.image, + this.imageSrc, false, false, false @@ -163,26 +161,12 @@ class Label extends RectangleShape { * @param {number} w * @param {number} h */ - // getImageBounds(x: number, y: number, w: number, h: number): mxRectangle; - getImageBounds(x, y, w, h) { - const align = utils.getValue(this.style, 'imageAlign', ALIGN_LEFT); - const valign = utils.getValue( - this.style, - 'verticalAlign', - 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; + getImageBounds(x: number, y: number, w: number, h: number) { + const align = this.style?.imageAlign ?? ALIGN_LEFT; + const valign = this.style?.verticalAlign ?? ALIGN_MIDDLE; + const width = this.style?.imageWidth ?? DEFAULT_IMAGESIZE; + const height = this.style?.imageHeight ?? DEFAULT_IMAGESIZE; + const spacing = this.style?.spacing ?? this.spacing + 5; if (align === ALIGN_CENTER) { x += (w - width) / 2; @@ -213,19 +197,18 @@ class Label extends RectangleShape { * @param {number} w * @param {number} h */ - // paintIndicator(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintIndicator(c, x, y, w, h) { - if (this.indicator != null) { + paintIndicator(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + if (this.indicator) { this.indicator.bounds = this.getIndicatorBounds(x, y, w, h); this.indicator.paint(c); - } else if (this.indicatorImage != null) { + } else if (this.indicatorImageSrc) { const bounds = this.getIndicatorBounds(x, y, w, h); c.image( bounds.x, bounds.y, bounds.width, bounds.height, - this.indicatorImage, + this.indicatorImageSrc, false, false, false @@ -241,24 +224,11 @@ class Label extends RectangleShape { * @param {number} h * @returns {Rectangle} */ - // getIndicatorBounds(x: number, y: number, w: number, h: number): mxRectangle; - getIndicatorBounds(x, y, w, h) { - const align = utils.getValue(this.style, 'imageAlign', ALIGN_LEFT); - const valign = utils.getValue( - this.style, - 'verticalAlign', - ALIGN_MIDDLE - ); - const width = utils.getNumber( - this.style, - 'indicatorWidth', - this.indicatorSize - ); - const height = utils.getNumber( - this.style, - 'indicatorHeight', - this.indicatorSize - ); + getIndicatorBounds(x: number, y: number, w: number, h: number) { + const align = this.style?.imageAlign ?? ALIGN_LEFT; + const valign = this.style?.verticalAlign ?? ALIGN_MIDDLE; + const width = this.style?.indicatorWidth ?? this.indicatorSize; + const height = this.style?.indicatorHeight ?? this.indicatorSize; const spacing = this.spacing + 5; if (align === ALIGN_RIGHT) { @@ -285,16 +255,15 @@ class Label extends RectangleShape { /** * Generic background painting implementation. */ - // redrawHtmlShape(): void; redrawHtmlShape() { super.redrawHtmlShape(); // Removes all children 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'); node.style.position = 'relative'; node.setAttribute('border', '0'); @@ -313,11 +282,11 @@ class Label extends RectangleShape { node.style.width = `${Math.round(bounds.width)}px`; node.style.height = `${Math.round(bounds.height)}px`; - node.src = this.image; + node.src = this.imageSrc; this.node.appendChild(node); } } } -export default Label; +export default LabelShape; diff --git a/packages/core/src/view/geometry/shape/node/RectangleShape.ts b/packages/core/src/view/geometry/shape/node/RectangleShape.ts index 93dd88b6c..cc653b234 100644 --- a/packages/core/src/view/geometry/shape/node/RectangleShape.ts +++ b/packages/core/src/view/geometry/shape/node/RectangleShape.ts @@ -10,11 +10,10 @@ import { NONE, RECTANGLE_ROUNDING_FACTOR, } from '../../../../util/Constants'; -import utils from '../../../../util/Utils'; import Shape from '../Shape'; -import mxAbstractCanvas2D from '../../../../util/canvas/mxAbstractCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; 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. @@ -24,84 +23,58 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; */ class RectangleShape extends Shape { constructor( - bounds: Rectangle | null = null, - fill: string | null = '#FFFFFF', - stroke: string | null = '#000000', - strokewidth: number = 1 + bounds: Rectangle, + fill: ColorValue, + stroke: ColorValue, + strokeWidth: number = 1 ) { super(); this.bounds = bounds; this.fill = fill; 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. */ - // isHtmlAllowed(): boolean; - isHtmlAllowed(): boolean { + isHtmlAllowed() { let events = true; - if (this.style != null) { - events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; + if (this.style) { + events = this.style.pointerEvents; } return ( !this.isRounded && !this.glass && this.rotation === 0 && - (events || (this.fill != null && this.fill !== NONE)) + (events || this.fill !== NONE) ); } /** * Generic background painting implementation. */ - // paintBackground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintBackground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { + paintBackground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { let events = true; - if (this.style != null) { - events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; + if (this.style) { + events = this.style.pointerEvents; } - if ( - events || - (this.fill != null && this.fill !== NONE) || - (this.stroke != null && this.stroke !== NONE) - ) { - if (!events && (this.fill == null || this.fill === NONE)) { + if (events || this.fill !== NONE || this.stroke !== NONE) { + if (!events && this.fill === NONE) { c.pointerEvents = false; } if (this.isRounded) { let r = 0; - if (utils.getValue(this.style, 'absoluteArcSize', 0) == '1') { - r = Math.min( - w / 2, - Math.min( - h / 2, - utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2 - ) - ); + if (this.style?.absoluteArcSize ?? false) { + r = Math.min(w / 2, Math.min(h / 2, (this.style?.arcSize ?? LINE_ARCSIZE) / 2)); } else { - const f = - utils.getValue( - this.style, - 'arcSize', - RECTANGLE_ROUNDING_FACTOR * 100 - ) / 100; + const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100; r = Math.min(w * f, h * f); } @@ -117,41 +90,22 @@ class RectangleShape extends Shape { /** * Adds roundable support. */ - // isRoundable(c?: mxAbstractCanvas2D, x?: number, y?: number, w?: number, h?: number): boolean; - isRoundable( - c: mxAbstractCanvas2D, - x: number, - y: number, - w: number, - h: number - ): boolean { + isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { return true; } /** * Generic background painting implementation. */ - // paintForeground(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintForeground( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { - if ( - this.glass && - !this.outline && - this.fill != null && - this.fill !== NONE - ) { + paintForeground(c: AbstractCanvas2D, x: number, y: number, w: number, h: number): void { + if (this.glass && !this.outline && this.fill !== NONE) { this.paintGlassEffect( c, x, y, w, h, - this.getArcSize(w + this.strokewidth, h + this.strokewidth) + this.getArcSize(w + this.strokeWidth, h + this.strokeWidth) ); } } diff --git a/packages/core/src/view/geometry/shape/node/RhombusShape.ts b/packages/core/src/view/geometry/shape/node/RhombusShape.ts index 95054a252..314cf3de4 100644 --- a/packages/core/src/view/geometry/shape/node/RhombusShape.ts +++ b/packages/core/src/view/geometry/shape/node/RhombusShape.ts @@ -6,10 +6,9 @@ */ import Shape from '../Shape'; import Point from '../../Point'; -import utils from '../../../../util/Utils'; import { LINE_ARCSIZE } from '../../../../util/Constants'; 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. @@ -18,17 +17,12 @@ import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; * @extends {Shape} */ class RhombusShape extends Shape { - constructor( - bounds: Rectangle, - fill: string, - stroke: string, - strokewidth: number = 1 - ) { + constructor(bounds: Rectangle, fill: string, stroke: string, strokewidth: number = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth; + this.strokeWidth = strokewidth; } /** @@ -47,19 +41,12 @@ class RhombusShape extends Shape { * @param {number} w * @param {number} h */ - // paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintVertexShape( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ) { + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { const hw = w / 2; const hh = h / 2; - const arcSize = - utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; + const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2; + c.begin(); this.addPoints( c, diff --git a/packages/core/src/view/geometry/shape/node/StencilShape.ts b/packages/core/src/view/geometry/shape/node/StencilShape.ts index 52f299e16..bc9148bf0 100644 --- a/packages/core/src/view/geometry/shape/node/StencilShape.ts +++ b/packages/core/src/view/geometry/shape/node/StencilShape.ts @@ -9,18 +9,22 @@ import ConnectionConstraint from '../../../connection/ConnectionConstraint'; import Rectangle from '../../Rectangle'; import Shape from '../Shape'; import Resources from '../../../../util/Resources'; -import utils, { getNumber, getValue, isNotNullish } from '../../../../util/Utils'; +import { getNumber, getValue, isNotNullish } from '../../../../util/Utils'; import { + ALIGN_LEFT, + ALIGN_TOP, DIRECTION_NORTH, DIRECTION_SOUTH, NODETYPE_ELEMENT, NONE, RECTANGLE_ROUNDING_FACTOR, + TEXT_DIRECTION_AUTO, } from '../../../../util/Constants'; import StencilShapeRegistry from './StencilShapeRegistry'; import { getChildNodes, getTextContent } from '../../../../util/DomUtils'; 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. @@ -107,7 +111,7 @@ class StencilShape extends Shape { * * Holds the strokewidth direction from the description. */ - strokeWidth: string | null = null; + strokeWidthValue: string | null = null; /** * Function: parseDescription @@ -132,7 +136,7 @@ class StencilShape extends Shape { // user-defined stroke-width). Note that the strokewidth is scaled // by the minimum scaling that is used to draw the shape (sx, sy). 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() { const conns = this.desc.getElementsByTagName('connections')[0]; - if (conns != null) { + if (conns) { const tmp = getChildNodes(conns); - if (tmp != null && tmp.length > 0) { + if (tmp.length > 0) { this.constraints = []; for (let i = 0; i < tmp.length; i += 1) { @@ -165,7 +169,7 @@ class StencilShape extends Shape { parseConstraint(node: Element) { const x = Number(node.getAttribute('x')); const y = Number(node.getAttribute('y')); - const perimeter = node.getAttribute('perimeter') == '1'; + const perimeter = node.getAttribute('perimeter') === '1'; const name = node.getAttribute('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); const loc = node.getAttribute('localized'); - if ((StencilShape.defaultLocalized && loc == null) || loc == '1') { + if ((StencilShape.defaultLocalized && !loc) || loc === '1') { result = Resources.get(result); } @@ -200,10 +204,10 @@ class StencilShape extends Shape { evaluateAttribute(node: Element, attribute: string, shape: Shape) { let result = node.getAttribute(attribute); - if (result == null) { + if (!result) { const text = getTextContent(node); - if (text != null && StencilShape.allowEval) { + if (text && StencilShape.allowEval) { const funct = eval(text); if (typeof funct === 'function') { @@ -221,7 +225,7 @@ class StencilShape extends Shape { * Draws this stencil inside the given bounds. */ drawShape( - canvas: mxSvgCanvas2D, + canvas: AbstractCanvas2D, shape: Shape, x: number, y: number, @@ -240,34 +244,20 @@ class StencilShape extends Shape { const aspect = this.computeAspect(shape, x, y, w, h, direction); const minScale = Math.min(aspect.width, aspect.height); const sw = - this.strokeWidth == 'inherit' + this.strokeWidthValue === 'inherit' ? Number(getNumber(shape.style, 'strokeWidth', 1)) - : Number(this.strokeWidth) * minScale; + : Number(this.strokeWidthValue) * minScale; canvas.setStrokeWidth(sw); // Draws a transparent rectangle for catching events - if ( - shape.style != null && - getValue(shape.style, 'pointerEvents', '0') == '1' - ) { + if (shape.style?.pointerEvents ?? false) { canvas.setStrokeColor(NONE); canvas.rect(x, y, w, h); canvas.stroke(); canvas.setStrokeColor(shape.stroke); } - this.drawChildren( - canvas, - shape, - x, - y, - w, - h, - this.bgNode, - aspect, - false, - true - ); + this.drawChildren(canvas, shape, x, y, w, h, this.bgNode, aspect, false, true); this.drawChildren( canvas, shape, @@ -295,26 +285,26 @@ class StencilShape extends Shape { * Draws this stencil inside the given bounds. */ drawChildren( - canvas: mxSvgCanvas2D, + canvas: AbstractCanvas2D, shape: Shape, x: number, y: number, w: number, h: number, node: Element | null, - aspect: string, + aspect: Rectangle, disableShadow: boolean, paint: boolean ) { - if (node != null && w > 0 && h > 0) { - let tmp = node.firstChild; + if (node && w > 0 && h > 0) { + let tmp = node.firstChild as Element; - while (tmp != null) { + while (tmp) { if (tmp.nodeType === NODETYPE_ELEMENT) { 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 sy = h / this.h0; - const inverse = - direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH; + const inverse = direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH; if (inverse) { sy = w / this.h0; @@ -381,7 +370,7 @@ class StencilShape extends Shape { * Draws this stencil inside the given bounds. */ drawNode( - canvas: mxSvgCanvas2D, + canvas: AbstractCanvas2D, shape: Shape, node: Element, aspect: Rectangle, @@ -410,10 +399,10 @@ class StencilShape extends Shape { const arcSize = Number(node.getAttribute('arcSize')); let pointCount = 0; - const segs = []; + const segs: Point[][] = []; // Renders the elements inside the given path - let childNode = node.firstChild; + let childNode = node.firstChild as Element; while (childNode != null) { 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) { @@ -461,21 +450,14 @@ class StencilShape extends Shape { if (parseRegularly) { // 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) { - this.drawNode( - canvas, - shape, - childNode, - aspect, - disableShadow, - paint - ); + this.drawNode(canvas, shape, childNode, aspect, disableShadow, paint); } - childNode = childNode.nextSibling; + childNode = childNode.nextSibling as Element; } } } else if (name === 'close') { @@ -512,7 +494,7 @@ class StencilShape extends Shape { Number(node.getAttribute('ry')) * sy, Number(node.getAttribute('x-axis-rotation')), Boolean(node.getAttribute('large-arc-flag')), - Number(node.getAttribute('sweep-flag')), + Boolean(node.getAttribute('sweep-flag')), x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy ); @@ -552,7 +534,7 @@ class StencilShape extends Shape { ); } else if (name === 'image') { if (!shape.outline) { - const src = this.evaluateAttribute(node, 'src', shape); + const src = this.evaluateAttribute(node, 'src', shape) as string; canvas.image( x0 + Number(node.getAttribute('x')) * sx, @@ -567,10 +549,10 @@ class StencilShape extends Shape { } } else if (name === 'text') { 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; - if (node.getAttribute('align-shape') == '0') { + if (node.getAttribute('align-shape') === '0') { const dr = shape.rotation; // Depends on flipping @@ -586,7 +568,7 @@ class StencilShape extends Shape { } } - rotation -= node.getAttribute('rotation'); + rotation -= Number(node.getAttribute('rotation')); canvas.text( x0 + Number(node.getAttribute('x')) * sx, @@ -594,19 +576,22 @@ class StencilShape extends Shape { 0, 0, str, - node.getAttribute('align') || 'left', - node.getAttribute('valign') || 'top', + (node.getAttribute('align') as AlignValue) || ALIGN_LEFT, + (node.getAttribute('valign') as VAlignValue) || ALIGN_TOP, false, '', - null, + 'auto', false, - rotation + rotation, + TEXT_DIRECTION_AUTO ); } } 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 y = y0 + Number(node.getAttribute('y')) * sy; const w = Number(node.getAttribute('w')) * sx; @@ -642,27 +627,27 @@ class StencilShape extends Shape { canvas.setDashPattern(value); } } else if (name === 'strokecolor') { - canvas.setStrokeColor(node.getAttribute('color')); + canvas.setStrokeColor(node.getAttribute('color') as ColorValue); } else if (name === 'linecap') { - canvas.setLineCap(node.getAttribute('cap')); + canvas.setLineCap(node.getAttribute('cap') as string); } else if (name === 'linejoin') { - canvas.setLineJoin(node.getAttribute('join')); + canvas.setLineJoin(node.getAttribute('join') as string); } else if (name === 'miterlimit') { canvas.setMiterLimit(Number(node.getAttribute('limit'))); } else if (name === 'fillcolor') { - canvas.setFillColor(node.getAttribute('color')); + canvas.setFillColor(node.getAttribute('color') as ColorValue); } else if (name === 'alpha') { - canvas.setAlpha(node.getAttribute('alpha')); + canvas.setAlpha(Number(node.getAttribute('alpha'))); } else if (name === 'fillalpha') { - canvas.setAlpha(node.getAttribute('alpha')); + canvas.setAlpha(Number(node.getAttribute('alpha'))); } else if (name === 'strokealpha') { - canvas.setAlpha(node.getAttribute('alpha')); + canvas.setAlpha(Number(node.getAttribute('alpha'))); } else if (name === 'fontcolor') { - canvas.setFontColor(node.getAttribute('color')); + canvas.setFontColor(node.getAttribute('color') as ColorValue); } else if (name === 'fontstyle') { - canvas.setFontStyle(node.getAttribute('style')); + canvas.setFontStyle(Number(node.getAttribute('style'))); } else if (name === 'fontfamily') { - canvas.setFontFamily(node.getAttribute('family')); + canvas.setFontFamily(node.getAttribute('family') as string); } else if (name === 'fontsize') { canvas.setFontSize(Number(node.getAttribute('size')) * minScale); } diff --git a/packages/core/src/view/geometry/shape/node/SwimlaneShape.ts b/packages/core/src/view/geometry/shape/node/SwimlaneShape.ts index 2d36e73f2..85079b9ff 100644 --- a/packages/core/src/view/geometry/shape/node/SwimlaneShape.ts +++ b/packages/core/src/view/geometry/shape/node/SwimlaneShape.ts @@ -15,7 +15,8 @@ import { NONE, RECTANGLE_ROUNDING_FACTOR, } 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. @@ -32,12 +33,12 @@ import utils from '../../../../util/Utils'; * @extends {Shape} */ class SwimlaneShape extends Shape { - constructor(bounds, fill, stroke, strokewidth) { + constructor(bounds: Rectangle, fill: ColorValue, stroke: ColorValue, strokeWidth = 1) { super(); this.bounds = bounds; this.fill = fill; this.stroke = stroke; - this.strokewidth = strokewidth != null ? strokewidth : 1; + this.strokeWidth = strokeWidth; } /** @@ -47,9 +48,10 @@ class SwimlaneShape extends Shape { * @type {number} * @default 16 */ - // imageSize: number; imageSize = 16; + imageSrc: string | null = null; + /** * Adds roundable support. * @param {mxAbstractCanvas2D} c @@ -59,33 +61,27 @@ class SwimlaneShape extends Shape { * @param {number} h * @returns {boolean} */ - // isRoundable(c?: mxAbstractCanvas2D, x?: number, y?: number, w?: number, h?: number): boolean; - isRoundable(c, x, y, w, h) { + isRoundable(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { return true; } /** * Returns the bounding box for the gradient box for this shape. */ - // getTitleSize(): number; getTitleSize() { - return Math.max( - 0, - utils.getValue(this.style, 'startSize', DEFAULT_STARTSIZE) - ); + return Math.max(0, this.style?.startSize ?? DEFAULT_STARTSIZE); } /** * Returns the bounding box for the gradient box for this shape. */ - // getLabelBounds(rect: mxRectangle): mxRectangle; - getLabelBounds(rect) { + getLabelBounds(rect: Rectangle) { const start = this.getTitleSize(); const bounds = new Rectangle(rect.x, rect.y, rect.width, rect.height); const horizontal = this.isHorizontal(); - const flipH = utils.getValue(this.style, 'flipH', 0) == 1; - const flipV = utils.getValue(this.style, 'flipV', 0) == 1; + const flipH = this.style?.flipH ?? false; + const flipV = this.style?.flipV ?? false; // East is default const shapeVertical = @@ -94,14 +90,10 @@ class SwimlaneShape extends Shape { const realFlipH = !realHorizontal && - flipH != - (this.direction === DIRECTION_SOUTH || - this.direction === DIRECTION_WEST); + flipH !== (this.direction === DIRECTION_SOUTH || this.direction === DIRECTION_WEST); const realFlipV = realHorizontal && - flipV != - (this.direction === DIRECTION_SOUTH || - this.direction === DIRECTION_WEST); + flipV !== (this.direction === DIRECTION_SOUTH || this.direction === DIRECTION_WEST); // Shape is horizontal if (!shapeVertical) { @@ -128,8 +120,7 @@ class SwimlaneShape extends 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, x, y, w, h) { + getGradientBounds(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { let start = this.getTitleSize(); if (this.isHorizontal()) { @@ -145,22 +136,11 @@ class SwimlaneShape extends Shape { * * Returns the arcsize for the swimlane. */ - getSwimlaneArcSize(w, h, start) { - if (utils.getValue(this.style, 'absoluteArcSize', 0) == '1') { - return Math.min( - w / 2, - Math.min( - h / 2, - utils.getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2 - ) - ); + getSwimlaneArcSize(w: number, h: number, start: number) { + if (this.style?.absoluteArcSize ?? false) { + return Math.min(w / 2, Math.min(h / 2, this.style?.arcSize ?? LINE_ARCSIZE / 2)); } - const f = - utils.getValue( - this.style, - 'arcSize', - RECTANGLE_ROUNDING_FACTOR * 100 - ) / 100; + const f = (this.style?.arcSize ?? RECTANGLE_ROUNDING_FACTOR * 100) / 100; return start * f * 3; } @@ -168,20 +148,17 @@ class SwimlaneShape extends Shape { /** * Paints the swimlane vertex shape. */ - // isHorizontal(): boolean; isHorizontal() { - return utils.getValue(this.style, 'horizontal', 1) == 1; + return this.style?.horizontal ?? true; } /** * Paints the swimlane vertex shape. */ - // paintVertexShape(c: mxAbstractCanvas2D, x: number, y: number, w: number, h: number): void; - paintVertexShape(c, x, y, w, h) { + paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { let start = this.getTitleSize(); - const fill = utils.getValue(this.style, 'swimlaneFillColor', NONE); - const swimlaneLine = - utils.getValue(this.style, 'swimlaneLine', 1) == 1; + const fill = this.style?.swimlaneFillColor ?? NONE; + const swimlaneLine = this.style?.swimlaneLine ?? true; let r = 0; if (this.isHorizontal()) { @@ -200,17 +177,17 @@ class SwimlaneShape extends Shape { 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); - if (this.image != null) { + if (this.imageSrc) { const bounds = this.getImageBounds(x, y, w, h); c.image( bounds.x - x, bounds.y - y, bounds.width, bounds.height, - this.image, + this.imageSrc, false, false, false @@ -228,16 +205,25 @@ class SwimlaneShape extends 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(); let events = true; - if (this.style != null) { - events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; + if (this.style) { + events = this.style.pointerEvents; } - if (!events && (this.fill == null || this.fill === NONE)) { + if (!events && this.fill === NONE) { c.pointerEvents = false; } @@ -309,16 +295,26 @@ class SwimlaneShape extends 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(); let events = true; - if (this.style != null) { - events = utils.getValue(this.style, 'pointerEvents', '1') == '1'; + if (this.style) { + events = this.style.pointerEvents; } - if (!events && (this.fill == null || this.fill === NONE)) { + if (!events && this.fill === NONE) { c.pointerEvents = false; } @@ -398,7 +394,15 @@ class SwimlaneShape extends Shape { * * 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) { c.setShadow(false); } @@ -421,7 +425,15 @@ class SwimlaneShape extends Shape { * * 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) { c.setStrokeColor(color); c.setDashed(true); @@ -443,15 +455,9 @@ class SwimlaneShape extends Shape { /** * Paints the swimlane vertex shape. */ - // getImageBounds(x: number, y: number, w: number, h: number): mxRectangle; - getImageBounds(x, y, w, h) { + getImageBounds(x: number, y: number, w: number, h: number) { if (this.isHorizontal()) { - return new Rectangle( - x + w - this.imageSize, - y, - this.imageSize, - this.imageSize - ); + return new Rectangle(x + w - this.imageSize, y, this.imageSize, this.imageSize); } return new Rectangle(x, y, this.imageSize, this.imageSize); } diff --git a/packages/core/src/view/geometry/shape/node/TextShape.ts b/packages/core/src/view/geometry/shape/node/TextShape.ts index 4ea92ba0d..775bd67bc 100644 --- a/packages/core/src/view/geometry/shape/node/TextShape.ts +++ b/packages/core/src/view/geometry/shape/node/TextShape.ts @@ -24,22 +24,31 @@ import { LINE_HEIGHT, NONE, TEXT_DIRECTION_AUTO, + TEXT_DIRECTION_DEFAULT, TEXT_DIRECTION_LTR, TEXT_DIRECTION_RTL, WORD_WRAP, } from '../../../../util/Constants'; -import utils, { - getAlignmentAsPoint, - getBoundingBox, - getValue, -} from '../../../../util/Utils'; +import { getAlignmentAsPoint, getBoundingBox } from '../../../../util/Utils'; import Point from '../../Point'; -import mxSvgCanvas2D from '../../../../util/canvas/mxSvgCanvas2D'; +import AbstractCanvas2D from '../../../../util/canvas/AbstractCanvas2D'; import Shape from '../Shape'; import Rectangle from '../../Rectangle'; 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 { + 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. @@ -56,28 +65,27 @@ class TextShape extends Shape { constructor( value: string, bounds: Rectangle, - align: string = ALIGN_CENTER, - valign: string | null = ALIGN_MIDDLE, - color: string = 'black', - family: string = DEFAULT_FONTFAMILY, - size: number = DEFAULT_FONTSIZE, - fontStyle: number = DEFAULT_FONTSTYLE, - spacing: number = 2, - spacingTop: number = 0, - spacingRight: number = 0, - spacingBottom: number = 0, - spacingLeft: number = 0, - horizontal: boolean = true, - background: string | null = null, - border: string | null = null, - wrap: boolean = false, - clipped: boolean = false, - overflow: string = 'visible', - labelPadding: number = 0, - textDirection: string = DEFAULT_TEXT_DIRECTION + align: AlignValue = ALIGN_CENTER, + valign: VAlignValue = ALIGN_MIDDLE, + color = 'black', + family = DEFAULT_FONTFAMILY, + size = DEFAULT_FONTSIZE, + fontStyle = DEFAULT_FONTSTYLE, + spacing = 2, + spacingTop = 0, + spacingRight = 0, + spacingBottom = 0, + spacingLeft = 0, + horizontal = true, + background = NONE, + border = NONE, + wrap = false, + clipped = false, + overflow: OverflowValue = 'visible', + labelPadding = 0, + textDirection: TextDirectionValue = DEFAULT_TEXT_DIRECTION ) { super(); - valign = valign != null ? valign : ALIGN_MIDDLE; this.value = value; this.bounds = bounds; @@ -87,14 +95,11 @@ class TextShape extends Shape { this.family = family; this.size = size; this.fontStyle = fontStyle; - this.spacing = parseInt(String(spacing || 2)); - this.spacingTop = parseInt(String(spacing || 2)) + parseInt(String(spacingTop || 0)); - this.spacingRight = - parseInt(String(spacing || 2)) + parseInt(String(spacingRight || 0)); - this.spacingBottom = - parseInt(String(spacing || 2)) + parseInt(String(spacingBottom || 0)); - this.spacingLeft = - parseInt(String(spacing || 2)) + parseInt(String(spacingLeft || 0)); + this.spacing = spacing; + this.spacingTop = spacing + spacingTop; + this.spacingRight = spacing + spacingRight; + this.spacingBottom = spacing + spacingBottom; + this.spacingLeft = spacing + spacingLeft; this.horizontal = horizontal; this.background = background; this.border = border; @@ -110,29 +115,29 @@ class TextShape extends Shape { // TODO: Document me! value: string | HTMLElement | SVGGElement | null; bounds: Rectangle; - align: string = ALIGN_CENTER; - valign: string = ALIGN_MIDDLE; - color: string = 'black'; - family: string = DEFAULT_FONTFAMILY; - size: number = DEFAULT_FONTSIZE; - fontStyle: number = DEFAULT_FONTSTYLE; - spacing: number = 2; - spacingTop: number = 0; - spacingRight: number = 0; - spacingBottom: number = 0; - spacingLeft: number = 0; - horizontal: boolean = true; - background: string | null = null; - border: string | null = null; - wrap: boolean = false; - clipped: boolean = false; - overflow: string = 'visible'; - labelPadding: number = 0; - textDirection: string = DEFAULT_TEXT_DIRECTION; + align: AlignValue; + valign: VAlignValue; + color: ColorValue; + family: string; + size: number; + fontStyle: number; + spacing: number; + spacingTop: number; + spacingRight: number; + spacingBottom: number; + spacingLeft: number; + horizontal: boolean; + background: ColorValue; + border: ColorValue; + wrap: boolean; + clipped: boolean; + overflow: OverflowValue; + labelPadding: number; + textDirection: TextDirectionValue; margin: Point | null = null; unrotatedBoundingBox: Rectangle | null = null; - flipH: boolean = false; - flipV: boolean = false; + flipH = false; + flipV = false; /** * 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 * value 5 here to get the same label positions as in mxGraph 1.x. */ - baseSpacingTop: number = 0; + baseSpacingTop = 0; /** * 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 * value 1 here to get the same label positions as in mxGraph 1.x. */ - baseSpacingBottom: number = 0; + baseSpacingBottom = 0; /** * Variable: baseSpacingLeft * * Specifies the spacing to be added to the left spacing. Default is 0. */ - baseSpacingLeft: number = 0; + baseSpacingLeft = 0; /** * Variable: baseSpacingRight * * Specifies the spacing to be added to the right spacing. Default is 0. */ - baseSpacingRight: number = 0; + baseSpacingRight = 0; /** * Variable: replaceLinefeeds @@ -170,14 +175,14 @@ class TextShape extends Shape { * Specifies if linefeeds in HTML labels should be replaced with BR tags. * Default is true. */ - replaceLinefeeds: boolean = true; + replaceLinefeeds = true; /** * Variable: verticalTextRotation * * Rotation for vertical text. Default is -90 (bottom to top). */ - verticalTextRotation: number = -90; + verticalTextRotation = -90; /** * Variable: ignoreClippedStringSize @@ -187,7 +192,7 @@ class TextShape extends Shape { * true, then the bounding box will be set to . Default is true. * has precedence over this switch. */ - ignoreClippedStringSize: boolean = true; + ignoreClippedStringSize = true; /** * Variable: ignoreStringSize @@ -196,7 +201,7 @@ class TextShape extends Shape { * boundingBox will not ignore the actual size of the string, otherwise * will be used instead. Default is false. */ - ignoreStringSize: boolean = false; + ignoreStringSize = false; /** * Variable: lastValue @@ -210,14 +215,14 @@ class TextShape extends Shape { * * Specifies if caching for HTML labels should be enabled. Default is true. */ - cacheEnabled: boolean = true; + cacheEnabled = true; /** * Function: getSvgScreenOffset * * Disables offset in IE9 for crisper image output. */ - getSvgScreenOffset(): number { + getSvgScreenOffset() { 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. */ - checkBounds(): boolean { + checkBounds() { return ( !isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 && - this.bounds != null && + this.bounds && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) && !isNaN(this.bounds.width) && @@ -244,7 +249,7 @@ class TextShape extends Shape { * * Generic rendering code. */ - paint(c: mxSvgCanvas2D, update: boolean = false): void { + paint(c: AbstractCanvas2D, update = false): void { // Scale is passed-through to canvas const s = this.scale; const x = this.bounds.x / s; @@ -292,14 +297,14 @@ class TextShape extends Shape { ? (val).replace(/\n/g, '
') : val; - let dir: string | null = this.textDirection; + let dir: TextDirectionValue = this.textDirection; if (dir === TEXT_DIRECTION_AUTO && !realHtml) { dir = this.getAutoDirection(); } if (dir !== TEXT_DIRECTION_LTR && dir !== TEXT_DIRECTION_RTL) { - dir = null; + dir = TEXT_DIRECTION_DEFAULT; } c.text( @@ -333,19 +338,20 @@ class TextShape extends Shape { this.lastValue === this.value && (isNode(this.value) || this.dialect === DIALECT_STRICTHTML) ) { - // @ts-ignore if (this.node.nodeName === 'DIV') { this.redrawHtmlShape(); this.updateBoundingBox(); } else { const canvas = this.createCanvas(); - // Specifies if events should be handled - canvas.pointerEvents = this.pointerEvents; + if (canvas) { + // Specifies if events should be handled + canvas.pointerEvents = this.pointerEvents; - this.paint(canvas, true); - this.destroyCanvas(canvas); - this.updateBoundingBox(); + this.paint(canvas, true); + this.destroyCanvas(canvas); + this.updateBoundingBox(); + } } } else { super.redraw(); @@ -378,8 +384,8 @@ class TextShape extends Shape { this.spacingBottom = 2; this.spacingLeft = 2; this.horizontal = true; - this.background = null; - this.border = null; + this.background = NONE; + this.border = NONE; this.textDirection = DEFAULT_TEXT_DIRECTION; this.margin = null; } @@ -397,31 +403,24 @@ class TextShape extends Shape { const old = this.spacing; super.apply(state); - if (this.style != null) { - this.fontStyle = this.style.fontStyle || this.fontStyle; - this.family = getValue(this.style, 'fontFamily', this.family); - this.size = getValue(this.style, 'fontSize', this.size); - this.color = getValue(this.style, 'fontColor', this.color); - this.align = getValue(this.style, 'align', this.align); - this.valign = getValue(this.style, 'verticalAlign', this.valign); - this.spacing = parseInt(getValue(this.style, 'spacing', this.spacing)); - this.spacingTop = - parseInt(getValue(this.style, 'spacingTop', this.spacingTop - old)) + - this.spacing; - this.spacingRight = - parseInt(getValue(this.style, 'spacingRight', this.spacingRight - old)) + - this.spacing; - this.spacingBottom = - parseInt(getValue(this.style, 'spacingBottom', this.spacingBottom - old)) + - this.spacing; - 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); + if (this.style) { + this.fontStyle = this.style.fontStyle; + this.family = this.style.fontFamily; + this.size = this.style.fontSize; + this.color = this.style.fontColor; + this.align = this.style.align; + this.valign = this.style.verticalAlign; + this.spacing = this.style.spacing; + this.spacingTop = this.style.spacingTop; + this.spacingRight = this.style.spacingRight; + this.spacingBottom = this.style.spacingBottom; + this.spacingLeft = this.style.spacingLeft; + this.horizontal = this.style.horizontal; + this.background = this.style.backgroundColor; + this.border = this.style.labelBorderColor; + this.textDirection = this.style.textDirection; + this.opacity = this.style.textOpacity; + this.updateMargin(); } @@ -437,14 +436,14 @@ class TextShape extends Shape { * depending on the contents of . This is not invoked for HTML, wrapped * content or if is a DOM node. */ - getAutoDirection(): string { + getAutoDirection() { // Looks for strong (directional) characters const tmp = /[A-Za-z\u05d0-\u065f\u066a-\u06ef\u06fa-\u07ff\ufb1d-\ufdff\ufe70-\ufefc]/.exec( - this.value + String(this.value) ); // 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_LTR; } @@ -457,9 +456,9 @@ class TextShape extends Shape { getContentNode() { let result = this.node; - if (result != null) { + if (result) { // Rendered with no foreignObject - if (result.ownerSVGElement == null) { + if (!result.ownerSVGElement) { // @ts-ignore result = this.node.firstChild.firstChild; } else { @@ -468,6 +467,7 @@ class TextShape extends Shape { result = result.firstChild.firstChild.firstChild.firstChild.firstChild; } } + return result; } @@ -476,21 +476,17 @@ class TextShape extends Shape { * * Updates the for this shape using the given node and position. */ - updateBoundingBox(): void { + updateBoundingBox() { let { node } = this; this.boundingBox = this.bounds.clone(); const rot = this.getTextRotation(); - const h = - this.style != null ? getValue(this.style, 'labelPosition', ALIGN_CENTER) : null; - const v = - this.style != null - ? getValue(this.style, 'verticalLabelPosition', ALIGN_MIDDLE) - : null; + const h = this.style?.labelPosition ?? ALIGN_CENTER; + const v = this.style?.verticalLabelPosition ?? ALIGN_MIDDLE; if ( !this.ignoreStringSize && - node != null && + node && this.overflow !== 'fill' && (!this.clipped || !this.ignoreClippedStringSize || @@ -501,8 +497,8 @@ class TextShape extends Shape { let oh = null; if ( - node.firstChild != null && - node.firstChild.firstChild != null && + node.firstChild && + node.firstChild.firstChild && node.firstChild.firstChild.nodeName === 'foreignObject' ) { // Uses second inner DIV for font metrics @@ -512,7 +508,6 @@ class TextShape extends Shape { oh = node.offsetHeight * this.scale; if (this.overflow === 'width') { - // @ts-ignore ow = this.boundingBox.width; } else { // @ts-ignore @@ -523,7 +518,7 @@ class TextShape extends Shape { const b = node.getBBox(); // 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; } else if (b.width === 0 && b.height === 0) { 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); } } - if (this.boundingBox != null) { + if (this.boundingBox) { const margin = this.margin; if (rot !== 0) { @@ -581,7 +576,7 @@ class TextShape extends Shape { * * Returns 0 to avoid using rotation in the canvas via updateTransform. */ - getShapeRotation(): number { + getShapeRotation() { return 0; } @@ -590,10 +585,8 @@ class TextShape extends Shape { * * Returns the rotation for the text label of the corresponding shape. */ - getTextRotation(): number { - return this.state != null && this.state.shape != null - ? this.state.shape.getTextRotation() - : 0; + getTextRotation() { + return this.state && this.state.shape ? this.state.shape.getTextRotation() : 0; } /** @@ -602,13 +595,8 @@ class TextShape extends Shape { * Inverts the bounds if returns true or if the * horizontal style is false. */ - isPaintBoundsInverted(): boolean { - return ( - !this.horizontal && - this.state != null && - // @ts-ignore - this.state.cell.isVertex() - ); + isPaintBoundsInverted() { + return !this.horizontal && !!this.state && this.state.cell.isVertex(); } /** @@ -616,7 +604,7 @@ class TextShape extends 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); c.setFontColor(this.color); @@ -691,7 +679,7 @@ class TextShape extends Shape { * * 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 h = Math.max(0, Math.round(this.bounds.height / this.scale)); const flex = @@ -699,9 +687,9 @@ class TextShape extends Shape { `top: ${Math.round(this.bounds.y)}px; pointer-events: none; `; const block = this.getTextCss(); const margin = this.margin; - const node = this.node; + const node = this.node; - mxSvgCanvas2D.createCss( + SvgCanvas2D.createCss( w + 2, h, this.align, @@ -709,8 +697,8 @@ class TextShape extends Shape { this.wrap, this.overflow, this.clipped, - this.background != null ? htmlEntities(this.background, true) : null, - this.border != null ? htmlEntities(this.border, true) : null, + this.background !== NONE ? htmlEntities(this.background, true) : null, + this.border !== NONE ? htmlEntities(this.border, true) : null, flex, block, this.scale, @@ -745,7 +733,7 @@ class TextShape extends Shape { } } - if (this.opacity < 100) { + if (this.opacity < 100) { block += `opacity: ${this.opacity / 100}; `; } @@ -756,7 +744,7 @@ class TextShape extends Shape { this.value.outerHTML : this.getHtmlValue(); - if (node.firstChild == null) { + if (!node.firstChild) { node.innerHTML = `
${html}
`; } @@ -773,7 +761,7 @@ class TextShape extends Shape { * * Sets the inner HTML of the given element to the . */ - updateInnerHtml(elt: HTMLElement): void { + updateInnerHtml(elt: HTMLElement) { if (isNode(this.value)) { // @ts-ignore elt.innerHTML = this.value.outerHTML; @@ -782,7 +770,7 @@ class TextShape extends Shape { if (this.dialect !== DIALECT_STRICTHTML) { // LATER: Can be cached in updateValue - val = htmlEntities(val, false); + val = htmlEntities(val, false); } // 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. */ - updateValue(): void { - const node = this.node; + updateValue() { + const node = this.node; if (isNode(this.value)) { node.innerHTML = ''; @@ -809,32 +797,31 @@ class TextShape extends Shape { let val = this.value as string; if (this.dialect !== DIALECT_STRICTHTML) { - val = htmlEntities(val, false); + val = htmlEntities(val, false); } // Handles trailing newlines to make sure they are visible in rendering output val = replaceTrailingNewlines(val, '

'); val = this.replaceLinefeeds ? val.replace(/\n/g, '
') : val; - const bg = - this.background != null && this.background !== NONE ? this.background : null; - const bd = this.border != null && this.border !== NONE ? this.border : null; + const bg = this.background !== NONE ? this.background : null; + const bd = this.border !== NONE ? this.border : null; if (this.overflow === 'fill' || this.overflow === 'width') { - if (bg != null) { + if (bg) { node.style.backgroundColor = bg; } - if (bd != null) { + if (bd) { node.style.border = `1px solid ${bd}`; } } else { let css = ''; - if (bg != null) { + if (bg) { css += `background-color:${htmlEntities(bg, true)};`; } - if (bd != null) { + if (bd) { 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. */ - updateFont(node: HTMLElement | SVGGElement): void { + updateFont(node: HTMLElement | SVGGElement) { const { style } = node; // @ts-ignore @@ -923,7 +910,7 @@ class TextShape extends Shape { * * 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 h = Math.max(0, Math.round(this.bounds.height / this.scale)); const { style } = node; @@ -992,7 +979,7 @@ class TextShape extends Shape { * * Returns the spacing as an . */ - updateMargin(): void { + updateMargin() { this.margin = getAlignmentAsPoint(this.align, this.valign); } @@ -1001,7 +988,7 @@ class TextShape extends Shape { * * Returns the spacing as an . */ - getSpacing(): Point { + getSpacing() { let dx = 0; let dy = 0; diff --git a/packages/core/src/view/geometry/shape/node/TriangleShape.ts b/packages/core/src/view/geometry/shape/node/TriangleShape.ts index e7f745bc3..36a303e15 100644 --- a/packages/core/src/view/geometry/shape/node/TriangleShape.ts +++ b/packages/core/src/view/geometry/shape/node/TriangleShape.ts @@ -7,9 +7,8 @@ import Point from '../../Point'; import Actor from '../Actor'; -import utils, { getValue } from '../../../../util/Utils'; 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. @@ -25,7 +24,7 @@ class TriangleShape extends Actor { * Adds roundable support. * @returns {boolean} */ - isRoundable(): boolean { + isRoundable() { return true; } @@ -37,15 +36,8 @@ class TriangleShape extends Actor { * @param {number} w * @param {number} h */ - redrawPath( - c: mxSvgCanvas2D, - x: number, - y: number, - w: number, - h: number - ): void { - const arcSize: number = - getValue(this.style, 'arcSize', LINE_ARCSIZE) / 2; + redrawPath(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { + const arcSize = (this.style?.arcSize ?? LINE_ARCSIZE) / 2; this.addPoints( c, diff --git a/packages/core/src/view/image/GraphImage.ts b/packages/core/src/view/image/GraphImage.ts index 127abcb65..91f601fab 100644 --- a/packages/core/src/view/image/GraphImage.ts +++ b/packages/core/src/view/image/GraphImage.ts @@ -1,6 +1,5 @@ -import Graph from "../Graph"; -import ImageBundle from "../../util/image/ImageBundle"; -import ImageBundle from "./ImageBundle"; +import Graph from '../Graph'; +import ImageBundle from './ImageBundle'; class GraphImage { constructor(graph: Graph) { @@ -19,14 +18,14 @@ class GraphImage { /** * Adds the specified {@link ImageBundle}. */ - addImageBundle(bundle: ImageBundle): void { + addImageBundle(bundle: ImageBundle) { this.imageBundles.push(bundle); } /** * Removes the specified {@link ImageBundle}. */ - removeImageBundle(bundle: ImageBundle): void { + removeImageBundle(bundle: ImageBundle) { const tmp = []; for (let i = 0; i < this.imageBundles.length; i += 1) { if (this.imageBundles[i] !== bundle) { @@ -40,11 +39,11 @@ class GraphImage { * Searches all {@link imageBundles} for the specified key and returns the value * for the first match or null if the key is not found. */ - getImageFromBundles(key: string): string { - if (key != null) { + getImageFromBundles(key: string) { + if (key) { for (let i = 0; i < this.imageBundles.length; i += 1) { const image = this.imageBundles[i].getImage(key); - if (image != null) { + if (image) { return image; } } diff --git a/packages/core/src/view/image/Image.ts b/packages/core/src/view/image/ImageBox.ts similarity index 75% rename from packages/core/src/view/image/Image.ts rename to packages/core/src/view/image/ImageBox.ts index e5fc663a0..f98e46e54 100644 --- a/packages/core/src/view/image/Image.ts +++ b/packages/core/src/view/image/ImageBox.ts @@ -14,10 +14,8 @@ * * Constructs a new image. */ -class Image { - constructor(src: string, - width: number, - height: number) { +class ImageBox { + constructor(src: string, width: number, height: number) { this.src = src; this.width = width; this.height = height; @@ -28,21 +26,21 @@ class Image { * * String that specifies the URL of the image. */ - src: string | null = null; + src: string; /** * Variable: width * * Integer that specifies the width of the image. */ - width: number | null = null; + width: number; /** * Variable: height * * Integer that specifies the height of the image. */ - height: number | null = null; + height: number; } -export default Image; +export default ImageBox; diff --git a/packages/core/src/view/image/ImageBundle.ts b/packages/core/src/view/image/ImageBundle.ts index 71ca240e9..9340d56ac 100644 --- a/packages/core/src/view/image/ImageBundle.ts +++ b/packages/core/src/view/image/ImageBundle.ts @@ -5,6 +5,13 @@ * Type definitions from the typed-mxgraph project */ +type ImageMap = { + [key: string]: { + value: string; + fallback: Function; + }; +}; + /** * Class: mxImageBundle * @@ -51,9 +58,9 @@ * all data URIs should be limited to 32 KB. */ class ImageBundle { - constructor(alt) { - this.images = []; - this.alt = alt != null ? alt : false; + constructor(alt = false) { + this.images = {}; + this.alt = alt; } /** @@ -61,14 +68,14 @@ class ImageBundle { * * Maps from keys to images. */ - images: { [key: string]: { value: string; fallback: Function } } | null = null; + images: ImageMap; /** * Variable: alt * * Specifies if the fallback representation should be returned. */ - alt: boolean = null; + alt: boolean; /** * Function: putImage @@ -87,13 +94,13 @@ class ImageBundle { * or fallback, depending on . The fallback is returned if * is true, the value is returned otherwise. */ - getImage(key: string): string { + getImage(key: string) { let result = null; - if (key != null) { + if (key) { const img = this.images[key]; - if (img != null) { + if (img) { result = this.alt ? img.fallback : img.value; } } diff --git a/packages/core/src/view/image/ImageExport.ts b/packages/core/src/view/image/ImageExport.ts index dfa8433c0..5b2a86a8d 100644 --- a/packages/core/src/view/image/ImageExport.ts +++ b/packages/core/src/view/image/ImageExport.ts @@ -5,7 +5,7 @@ * 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 Shape from '../geometry/shape/Shape'; @@ -41,13 +41,13 @@ class ImageExport { /** * 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. */ - drawState(state: CellState, canvas: mxAbstractCanvas2D): void { - if (state != null) { + drawState(state: CellState, canvas: AbstractCanvas2D): void { + if (state) { this.visitStatesRecursive(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. */ - visitStatesRecursive(state: CellState, canvas: mxAbstractCanvas2D, visitor: Function) { - if (state != null) { + visitStatesRecursive(state: CellState, canvas: AbstractCanvas2D, visitor: Function) { + if (state) { visitor(state, canvas); const { graph } = state.view; @@ -83,18 +83,18 @@ class ImageExport { /** * 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; } /** * Draws the given state to the given canvas. */ - drawCellState(state: CellState, canvas: mxAbstractCanvas2D): void { + drawCellState(state: CellState, canvas: AbstractCanvas2D): void { // Experimental feature const link = this.getLinkForCellState(state, canvas); - if (link != null) { + if (link) { canvas.setLink(link); } @@ -102,7 +102,7 @@ class ImageExport { this.drawShape(state, canvas); this.drawText(state, canvas); - if (link != null) { + if (link) { canvas.setLink(null); } } @@ -112,7 +112,7 @@ class ImageExport { * * 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()) { canvas.save(); @@ -127,8 +127,8 @@ class ImageExport { /** * Draws the text of the given state. */ - drawText(state: CellState, canvas: mxAbstractCanvas2D): void { - if (state.text != null && state.text.checkBounds()) { + drawText(state: CellState, canvas: AbstractCanvas2D): void { + if (state.text && state.text.checkBounds()) { canvas.save(); state.text.beforePaint(canvas); @@ -145,7 +145,7 @@ class ImageExport { * Draws the overlays for the given state. This is called if * is true. */ - drawOverlays(state: CellState, canvas: mxAbstractCanvas2D): void { + drawOverlays(state: CellState, canvas: AbstractCanvas2D): void { if (state.overlays != null) { state.overlays.visit((id, shape) => { if (shape instanceof Shape) { diff --git a/packages/core/src/view/layout/Overlays.ts b/packages/core/src/view/layout/Overlays.ts index 43d77e4fa..956a1b8aa 100644 --- a/packages/core/src/view/layout/Overlays.ts +++ b/packages/core/src/view/layout/Overlays.ts @@ -1,10 +1,10 @@ -import Cell from "../cell/datatypes/Cell"; -import CellOverlay from "../cell/CellOverlay"; -import EventObject from "../event/EventObject"; -import InternalEvent from "../event/InternalEvent"; -import Image from "../image/Image"; -import InternalMouseEvent from "../event/InternalMouseEvent"; -import Graph from "../Graph"; +import Cell from '../cell/datatypes/Cell'; +import CellOverlay from '../cell/CellOverlay'; +import EventObject from '../event/EventObject'; +import InternalEvent from '../event/InternalEvent'; +import Image from '../image/ImageBox'; +import InternalMouseEvent from '../event/InternalMouseEvent'; +import Graph from '../Graph'; class Overlays { constructor(graph: Graph) { @@ -82,13 +82,7 @@ class Overlays { } this.fireEvent( - new EventObject( - InternalEvent.REMOVE_OVERLAY, - 'cell', - cell, - 'overlay', - overlay - ) + new EventObject(InternalEvent.REMOVE_OVERLAY, 'cell', cell, 'overlay', overlay) ); } else { overlay = null; @@ -184,18 +178,18 @@ class Overlays { img = img != null ? img : this.warningImage; // Creates the overlay with the image and warning - const overlay = new CellOverlay( - img, - `${warning}` - ); + const overlay = new CellOverlay(img, `${warning}`); // Adds a handler for single mouseclicks to select the cell if (isSelect) { - overlay.addListener(InternalEvent.CLICK, (sender: any, evt: InternalMouseEvent) => { - if (this.isEnabled()) { - this.setSelectionCell(cell); + overlay.addListener( + InternalEvent.CLICK, + (sender: any, evt: InternalMouseEvent) => { + if (this.isEnabled()) { + this.setSelectionCell(cell); + } } - }); + ); } // Sets and returns the overlay in the graph diff --git a/packages/core/src/view/selection/CellHighlight.ts b/packages/core/src/view/selection/CellHighlight.ts index f654a13ec..bd37935e3 100644 --- a/packages/core/src/view/selection/CellHighlight.ts +++ b/packages/core/src/view/selection/CellHighlight.ts @@ -72,7 +72,7 @@ class CellHighlight { } // TODO: Document me!! - highlightColor: ColorValue = null; + highlightColor: ColorValue | null = null; strokeWidth: number = 0; @@ -119,7 +119,7 @@ class CellHighlight { * * @param {string} color - String that represents the new highlight color. */ - setHighlightColor(color: ColorValue) { + setHighlightColor(color: ColorValue | null) { this.highlightColor = color; if (this.shape) { @@ -134,15 +134,10 @@ class CellHighlight { this.shape = this.createShape(); this.repaint(); - const node = this.shape?.node; - if ( - !this.keepOnTop && - this.shape.node?.parentNode?.firstChild !== this.shape.node - ) { - this.shape.node.parentNode.insertBefore( - this.shape.node, - this.shape.node.parentNode.firstChild - ); + const node = this.shape.node; + + if (!this.keepOnTop && node?.parentNode?.firstChild !== node) { + node.parentNode.insertBefore(node, node.parentNode.firstChild); } } @@ -150,20 +145,20 @@ class CellHighlight { * Creates and returns the highlight shape for the given state. */ createShape() { - const shape = ( - this.graph.cellRenderer.createShape(this.state) - ); + if (!this.state) return; - shape.svgStrokeTolerance = (this.graph).tolerance; - shape.points = (this.state).absolutePoints; - shape.apply(this.state); + const shape = this.graph.cellRenderer.createShape(this.state); + + shape.svgStrokeTolerance = this.graph.tolerance; + shape.points = this.state.absolutePoints; + shape.apply(this.state); shape.stroke = this.highlightColor; shape.opacity = this.opacity; shape.isDashed = this.dashed; shape.isShadow = false; shape.dialect = DIALECT_SVG; - shape.init((this.graph).getView().getOverlayPane()); + shape.init(this.graph.getView().getOverlayPane()); InternalEvent.redirectMouseEvents(shape.node, this.graph, this.state); if ((this.graph).dialect !== DIALECT_SVG) { @@ -191,7 +186,7 @@ class CellHighlight { // @ts-ignore 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.outline = false; } else { @@ -202,8 +197,7 @@ class CellHighlight { this.state.height + 2 * this.spacing ); this.shape.rotation = Number(this.state.style.rotation || '0'); - this.shape.strokewidth = - this.getStrokeWidth() / this.state.view.scale; + this.shape.strokeWidth = this.getStrokeWidth() / this.state.view.scale; this.shape.outline = true; } diff --git a/packages/core/src/view/selection/GraphSelection.ts b/packages/core/src/view/selection/GraphSelection.ts index 81c52ad4c..04f813144 100644 --- a/packages/core/src/view/selection/GraphSelection.ts +++ b/packages/core/src/view/selection/GraphSelection.ts @@ -1,17 +1,17 @@ -import Cell from "../cell/datatypes/Cell"; -import CellArray from "../cell/datatypes/CellArray"; -import Rectangle from "../geometry/Rectangle"; -import InternalMouseEvent from "../event/InternalMouseEvent"; -import graph from "../Graph"; -import mxClient from "../../mxClient"; -import SelectionChange from "./SelectionChange"; -import UndoableEdit from "../model/UndoableEdit"; -import EventObject from "../event/EventObject"; -import InternalEvent from "../event/InternalEvent"; -import EventSource from "../event/EventSource"; -import Dictionary from "../../util/Dictionary"; -import RootChange from "../model/RootChange"; -import ChildChange from "../model/ChildChange"; +import Cell from '../cell/datatypes/Cell'; +import CellArray from '../cell/datatypes/CellArray'; +import Rectangle from '../geometry/Rectangle'; +import InternalMouseEvent from '../event/InternalMouseEvent'; +import graph from '../Graph'; +import mxClient from '../../mxClient'; +import SelectionChange from './SelectionChange'; +import UndoableEdit from '../model/UndoableEdit'; +import EventObject from '../event/EventObject'; +import InternalEvent from '../event/InternalEvent'; +import EventSource from '../event/EventSource'; +import Dictionary from '../../util/Dictionary'; +import RootChange from '../model/RootChange'; +import ChildChange from '../model/ChildChange'; class GraphSelection extends EventSource { constructor(graph: graph) { @@ -50,25 +50,27 @@ class GraphSelection extends EventSource { */ singleSelection: boolean = false; + // TODO: Document me!! + selectionModel: GraphSelection | null = null; /** * Returns the {@link mxGraphSelectionModel} that contains the selection. */ - getSelectionModel(): mxGraphSelectionModel { - return this.selectionModel; + getSelectionModel() { + return this.selectionModel; } /** * Sets the {@link mxSelectionModel} that contains the selection. */ - setSelectionModel(selectionModel: mxGraphSelectionModel): void { + setSelectionModel(selectionModel: GraphSelection) { this.selectionModel = selectionModel; } /** * Returns {@link singleSelection} as a boolean. */ - isSingleSelection(): boolean { + isSingleSelection() { return this.singleSelection; } @@ -78,7 +80,7 @@ class GraphSelection extends EventSource { * @param {boolean} singleSelection Boolean that specifies the new value for * {@link singleSelection}. */ - setSingleSelection(singleSelection: boolean): void { + setSingleSelection(singleSelection: boolean) { this.singleSelection = singleSelection; } @@ -228,13 +230,19 @@ class GraphSelection extends EventSource { * @param added Array of {@link Cell} to add to the selection. * @param remove Array of {@link Cell} to remove from the selection. */ - changeSelection(added: CellArray | null=null, - removed: CellArray | null=null): void { + changeSelection( + added: CellArray | null = null, + removed: CellArray | null = null + ): void { if ( (added != null && added.length > 0 && added[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(); const edit = new UndoableEdit(this, false); edit.add(change); @@ -491,8 +499,8 @@ class GraphSelection extends EventSource { ): void { const cells = descendants ? parent.filterDescendants((cell: Cell) => { - return cell != parent && this.graph.getView().getState(cell) != null; - }) + return cell != parent && this.graph.getView().getState(cell) != null; + }) : parent.getChildren(); if (cells != null) { @@ -589,7 +597,6 @@ class GraphSelection extends EventSource { } } - /** * 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) { const change = changes[i]; - if ( - change.constructor !== RootChange && - (ignoreFn == null || !ignoreFn(change)) - ) { + if (change.constructor !== RootChange && (ignoreFn == null || !ignoreFn(change))) { let cell = null; if (change instanceof ChildChange) { @@ -666,7 +670,6 @@ class GraphSelection extends EventSource { return cells; } - /** * Removes selection cells that are not in the model from the selection. */ @@ -695,5 +698,3 @@ class GraphSelection extends EventSource { } export default GraphSelection; - - diff --git a/packages/core/src/view/view/GraphView.ts b/packages/core/src/view/view/GraphView.ts index 25d7b196e..02b1f8b2b 100644 --- a/packages/core/src/view/view/GraphView.ts +++ b/packages/core/src/view/view/GraphView.ts @@ -40,21 +40,16 @@ import InternalMouseEvent from '../event/InternalMouseEvent'; import StyleRegistry from '../style/StyleRegistry'; import graph from '../Graph'; import Cell from '../cell/datatypes/Cell'; -import Image from '../image/Image'; +import Image from '../image/ImageBox'; import CurrentRootChange from './CurrentRootChange'; import Model from '../model/Model'; import Shape from '../geometry/shape/Shape'; import Geometry from '../geometry/Geometry'; import ConnectionConstraint from '../connection/ConnectionConstraint'; import PopupMenuHandler from '../popups_menus/PopupMenuHandler'; -import { - getClientX, - getClientY, - getSource, - isConsumed, -} from '../../util/EventUtils'; +import { getClientX, getClientY, getSource, isConsumed } from '../../util/EventUtils'; import { clone } from '../../util/CloneUtils'; -import CellArray from "../cell/datatypes/CellArray"; +import CellArray from '../cell/datatypes/CellArray'; /** * @class GraphView @@ -135,8 +130,7 @@ class GraphView extends EventSource { * being updated. If the resource for this key does not exist then the * value is used as the status message. Default is 'updatingDocument'. */ - updatingDocumentResource = - mxClient.language !== 'none' ? 'updatingDocument' : ''; + updatingDocumentResource = mxClient.language !== 'none' ? 'updatingDocument' : ''; /** * Specifies if string values in cell styles should be evaluated using @@ -178,7 +172,7 @@ class GraphView extends EventSource { */ translate = new Point(); - states = new Dictionary(); + states = new Dictionary(); /** * Specifies if the style should be updated in each validation step. If this @@ -243,13 +237,7 @@ class GraphView extends EventSource { } } this.fireEvent( - new EventObject( - InternalEvent.SCALE, - 'scale', - value, - 'previousScale', - previousScale - ) + new EventObject(InternalEvent.SCALE, 'scale', value, 'previousScale', previousScale) ); } @@ -289,13 +277,10 @@ class GraphView extends EventSource { } this.fireEvent( - new EventObject( - InternalEvent.TRANSLATE, - { - translate: this.translate, - previousTranslate: previousTranslate, - } - ) + new EventObject(InternalEvent.TRANSLATE, { + translate: this.translate, + previousTranslate: previousTranslate, + }) ); } @@ -417,11 +402,7 @@ class GraphView extends EventSource { const previousScale = this.scale; const previousTranslate = new Point(this.translate.x, this.translate.y); - if ( - this.scale != scale || - this.translate.x != dx || - this.translate.y != dy - ) { + if (this.scale != scale || this.translate.x != dx || this.translate.y != dy) { this.scale = scale; this.translate.x = dx; @@ -433,15 +414,12 @@ class GraphView extends EventSource { } this.fireEvent( - new EventObject( - InternalEvent.SCALE_AND_TRANSLATE, - { - scale: scale, - previousScale: previousScale, - translate: this.translate, - previousTranslate: previousTranslate, - } - ) + new EventObject(InternalEvent.SCALE_AND_TRANSLATE, { + scale: scale, + previousScale: previousScale, + translate: this.translate, + previousTranslate: previousTranslate, + }) ); } @@ -559,8 +537,7 @@ class GraphView extends EventSource { validate(cell: Cell | null = null): void { const t0 = mxLog.enter('mxGraphView.validate'); window.status = - Resources.get(this.updatingDocumentResource) || - this.updatingDocumentResource; + Resources.get(this.updatingDocumentResource) || this.updatingDocumentResource; this.resetValidationState(); @@ -579,9 +556,7 @@ class GraphView extends EventSource { ) ); - this.setGraphBounds( - graphBounds != null ? graphBounds : this.getEmptyBounds() - ); + this.setGraphBounds(graphBounds != null ? graphBounds : this.getEmptyBounds()); this.validateBackground(); this.resetValidationState(); @@ -595,10 +570,7 @@ class GraphView extends EventSource { * {@link translate} with the size of 0 x 0. */ getEmptyBounds(): Rectangle { - return new Rectangle( - this.translate.x * this.scale, - this.translate.y * this.scale - ); + return new Rectangle(this.translate.x * this.scale, this.translate.y * this.scale); } /** @@ -634,9 +606,7 @@ class GraphView extends EventSource { const childCount = state.cell.getChildCount(); for (let i = 0; i < childCount; i += 1) { - const bounds = this.getBoundingBox( - this.getState(state.cell.getChildAt(i)) - ); + const bounds = this.getBoundingBox(this.getState(state.cell.getChildAt(i))); if (bounds != null) { if (bbox == null) { @@ -675,10 +645,7 @@ class GraphView extends EventSource { const bg = (this.graph).getBackgroundImage(); if (bg != null) { - if ( - this.backgroundImage == null || - this.backgroundImage.image !== bg.src - ) { + if (this.backgroundImage == null || this.backgroundImage.imageSrc !== bg.src) { if (this.backgroundImage != null) { this.backgroundImage.destroy(); } @@ -734,15 +701,15 @@ class GraphView extends EventSource { }, (evt: Event) => { // Hides the tooltip if mouse is outside container - if ( - graph.tooltipHandler != null && - graph.tooltipHandler.isHideOnHover() - ) { + if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) { graph.tooltipHandler.hide(); } if (graph.isMouseDown && !isConsumed(evt)) { - graph.fireMouseEvent(InternalEvent.MOUSE_MOVE, new InternalMouseEvent(evt)); + graph.fireMouseEvent( + InternalEvent.MOUSE_MOVE, + new InternalMouseEvent(evt) + ); } }, (evt: Event) => { @@ -859,9 +826,7 @@ class GraphView extends EventSource { state.invalid = false; if (state.style == null || state.invalidStyle) { - state.style = (this.graph).getCellStyle( - state.cell - ); + state.style = (this.graph).getCellStyle(state.cell); state.invalidStyle = false; } @@ -871,19 +836,13 @@ class GraphView extends EventSource { state.setVisibleTerminalState( ( - this.validateCellState( - this.getVisibleTerminal(cell, true), - false - ) + this.validateCellState(this.getVisibleTerminal(cell, true), false) ), true ); state.setVisibleTerminalState( ( - this.validateCellState( - this.getVisibleTerminal(cell, false), - false - ) + this.validateCellState(this.getVisibleTerminal(cell, false), false) ), false ); @@ -892,11 +851,7 @@ class GraphView extends EventSource { // Repaint happens immediately after the cell is validated if (cell !== this.currentRoot && !state.invalid) { - (this.graph).cellRenderer.redraw( - state, - false, - this.isRendering() - ); + (this.graph).cellRenderer.redraw(state, false, this.isRendering()); // Handles changes to invertex paintbounds after update of rendering shape state.updateCachedBounds(); @@ -943,9 +898,7 @@ class GraphView extends EventSource { origin.y += (pState.origin).y; } - let offset = (this.graph).getChildOffsetForCell( - state.cell - ); + let offset = (this.graph).getChildOffsetForCell(state.cell); if (offset != null) { origin.x += offset.x; @@ -956,9 +909,7 @@ class GraphView extends EventSource { if (geo != null) { if (!state.cell.isEdge()) { - offset = ( - (geo.offset != null ? geo.offset : this.EMPTY_POINT) - ); + offset = (geo.offset != null ? geo.offset : this.EMPTY_POINT); if (geo.relative && pState != null) { if (pState.cell.isEdge()) { @@ -966,13 +917,9 @@ class GraphView extends EventSource { if (origin != null) { origin.x += - origin.x / this.scale - - (pState.origin).x - - this.translate.x; + origin.x / this.scale - (pState.origin).x - this.translate.x; origin.y += - origin.y / this.scale - - (pState.origin).y - - this.translate.y; + origin.y / this.scale - (pState.origin).y - this.translate.y; } } else { origin.x += geo.x * pState.unscaledWidth + offset.x; @@ -1056,10 +1003,7 @@ class GraphView extends EventSource { if ( state.cell !== this.currentRoot && - (pts == null || - pts.length < 2 || - pts[0] == null || - pts[pts.length - 1] == null) + (pts == 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. // 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 target {@link mxCellState} which represents the target terminal. */ - updateFixedTerminalPoints( - edge: CellState, - source: CellState, - target: CellState - ): void { + updateFixedTerminalPoints(edge: CellState, source: CellState, target: CellState): void { this.updateFixedTerminalPoint( edge, source, @@ -1234,11 +1174,7 @@ class GraphView extends EventSource { let pt = null; if (constraint != null) { - pt = (this.graph).getConnectionPoint( - terminal, - constraint, - false - ); // FIXME Rounding introduced bugs when calculating label positions -> , this.graph.isOrthogonal(edge)); + pt = (this.graph).getConnectionPoint(terminal, constraint, false); // FIXME Rounding introduced bugs when calculating label positions -> , this.graph.isOrthogonal(edge)); } if (pt == null && terminal == null) { @@ -1249,10 +1185,7 @@ class GraphView extends EventSource { pt = geo.getTerminalPoint(source); if (pt != null) { - pt = new Point( - s * (tr.x + pt.x + orig.x), - s * (tr.y + pt.y + orig.y) - ); + pt = new Point(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 if (srcBounds != null) { - src.setRect( - srcBounds.x, - srcBounds.y, - srcBounds.width, - srcBounds.height - ); + src.setRect(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height); } if (trgBounds != null) { - trg.setRect( - trgBounds.x, - trgBounds.y, - trgBounds.width, - trgBounds.height - ); + trg.setRect(trgBounds.x, trgBounds.y, trgBounds.width, trgBounds.height); } } else if (points != null) { for (let i = 0; i < points.length; i += 1) { @@ -1388,16 +1311,8 @@ class GraphView extends EventSource { source: CellState | null = null, target: CellState | null = null ): boolean { - const sc = (this.graph).getConnectionConstraint( - edge, - source, - true - ); - const tc = (this.graph).getConnectionConstraint( - edge, - target, - false - ); + const sc = (this.graph).getConnectionConstraint(edge, source, true); + const tc = (this.graph).getConnectionConstraint(edge, target, false); if ( (points == null || points.length < 2) && @@ -1516,16 +1431,9 @@ class GraphView extends EventSource { let border = parseFloat(edge.style.perimeterSpacing || 0); border += parseFloat( - edge.style[ - source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing' - ] || 0 - ); - let pt = this.getPerimeterPoint( - start, - next, - alpha === 0 && orth, - border + edge.style[source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing'] || 0 ); + let pt = this.getPerimeterPoint(start, next, alpha === 0 && orth, border); if (alpha !== 0) { const cos = Math.cos(alpha); @@ -1553,10 +1461,7 @@ class GraphView extends EventSource { const id = getValue(state.style, key); if (id != null) { - const tmp = this.getState( - (this.graph).getModel().getCell(id), - false - ); + const tmp = this.getState((this.graph).getModel().getCell(id), false); // Only uses ports where a cell state exists if (tmp != null) { @@ -1641,8 +1546,7 @@ class GraphView extends EventSource { * Returns the x-coordinate of the center point for automatic routing. */ getRoutingCenterX(state: CellState): number { - const f = - state.style != null ? parseFloat(state.style.routingCenterX) || 0 : 0; + const f = state.style != null ? parseFloat(state.style.routingCenterX) || 0 : 0; 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. */ getRoutingCenterY(state: CellState): number { - const f = - state.style != null ? parseFloat(state.style.routingCenterY) || 0 : 0; + const f = state.style != null ? parseFloat(state.style.routingCenterY) || 0 : 0; return state.getCenterY() + f * state.height; } @@ -1986,8 +1889,7 @@ class GraphView extends EventSource { if (dotprod <= 0.0) { projlenSq = 0; } else { - projlenSq = - (dotprod * dotprod) / (xSegment * xSegment + ySegment * ySegment); + projlenSq = (dotprod * dotprod) / (xSegment * xSegment + ySegment * ySegment); } 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. */ createState(cell: Cell): CellState { - return new CellState( - this, - cell, - (this.graph).getCellStyle(cell) - ); + return new CellState(this, cell, (this.graph).getCellStyle(cell)); } /** @@ -2290,9 +2188,7 @@ class GraphView extends EventSource { // Dispatches the drop event to the graph which // consumes and executes the source function const pt = convertPoint(container, x, y); - state = (graph.view).getState( - graph.getCellAt(pt.x, pt.y) - ); + state = (graph.view).getState(graph.getCellAt(pt.x, pt.y)); } return state; @@ -2312,10 +2208,7 @@ class GraphView extends EventSource { this.moveHandler = (evt: Event) => { // Hides the tooltip if mouse is outside container - if ( - graph.tooltipHandler != null && - graph.tooltipHandler.isHideOnHover() - ) { + if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) { graph.tooltipHandler.hide(); } @@ -2368,26 +2261,17 @@ class GraphView extends EventSource { )); // For background image - this.backgroundPane = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'g' - ); + this.backgroundPane = document.createElementNS('http://www.w3.org/2000/svg', 'g'); canvas.appendChild(this.backgroundPane); // Adds two layers (background is early feature) this.drawPane = document.createElementNS('http://www.w3.org/2000/svg', 'g'); canvas.appendChild(this.drawPane); - this.overlayPane = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'g' - ); + this.overlayPane = document.createElementNS('http://www.w3.org/2000/svg', 'g'); canvas.appendChild(this.overlayPane); - this.decoratorPane = document.createElementNS( - 'http://www.w3.org/2000/svg', - 'g' - ); + this.decoratorPane = document.createElementNS('http://www.w3.org/2000/svg', 'g'); canvas.appendChild(this.decoratorPane); const root = document.createElementNS('http://www.w3.org/2000/svg', 'svg');