diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4bde2df2c..a9a802250 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -207,5 +207,3 @@ export { default as CellStatePreview } from './view/cell/CellStatePreview'; export { default as TemporaryCellStates } from './view/cell/TemporaryCellStates'; export { default as ConnectionConstraint } from './view/connection/ConnectionConstraint'; export { default as Multiplicity } from './view/validation/Multiplicity'; - -import '../css/common.css'; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 961a0493f..1261fd939 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -98,6 +98,7 @@ export type CellStateStyles = { noLabel: boolean; opacity: number; orthogonal: boolean | null; + orthogonalLoop: boolean; overflow: OverflowValue; perimeter: Function | string | null; perimeterSpacing: number; diff --git a/packages/core/src/view/Graph.ts b/packages/core/src/view/Graph.ts index 9b4ac2402..504068885 100644 --- a/packages/core/src/view/Graph.ts +++ b/packages/core/src/view/Graph.ts @@ -152,12 +152,6 @@ class Graph extends EventSource { // @ts-ignore stylesheet: Stylesheet; - /** - * Holds the {@link CellEditor} that is used as the in-place editing. - */ - // @ts-ignore - cellEditor: CellEditor; - /** * Holds the {@link CellRenderer} for rendering the cells in the graph. */ @@ -1445,10 +1439,6 @@ class Graph extends EventSource { this.defaultParent = cell; } - getCellEditor() { - return this.cellEditor; - } - /** * Destroys the graph and all its resources. */ diff --git a/packages/core/src/view/GraphHandler.ts b/packages/core/src/view/GraphHandler.ts index 26cda3341..3fd9f384f 100644 --- a/packages/core/src/view/GraphHandler.ts +++ b/packages/core/src/view/GraphHandler.ts @@ -36,9 +36,10 @@ import EventSource from './event/EventSource'; import CellArray from './cell/datatypes/CellArray'; import CellState from './cell/datatypes/CellState'; import EventObject from './event/EventObject'; +import ConnectionHandler from './connection/ConnectionHandler'; +import CellEditor from './editing/CellEditor'; import type { ColorValue, GraphPlugin } from '../types'; -import ConnectionHandler from './connection/ConnectionHandler'; /** * Class: mxGraphHandler @@ -545,7 +546,8 @@ class GraphHandler implements GraphPlugin { if (!this.graph.isToggleEvent(me.getEvent()) || !isAltDown(me.getEvent())) { while (c) { if (selectionCellsHandler.isHandled(c)) { - return this.graph.cellEditor.getEditingCell() !== c; + const cellEditor = this.graph.getPlugin('CellEditor') as CellEditor; + return cellEditor.getEditingCell() !== c; } c = c.getParent(); diff --git a/packages/core/src/view/cell/CellRenderer.ts b/packages/core/src/view/cell/CellRenderer.ts index 8dad34c2b..9b3318512 100644 --- a/packages/core/src/view/cell/CellRenderer.ts +++ b/packages/core/src/view/cell/CellRenderer.ts @@ -52,7 +52,6 @@ import { equalEntries, equalPoints, getRotatedPoint, - getValue, mod, toRadians, } from '../../util/Utils'; @@ -1168,24 +1167,21 @@ class CellRenderer { * bounds - the rectangle to be rotated. */ rotateLabelBounds(state: CellState, bounds: Rectangle): void { - // @ts-ignore - bounds.y -= state.text.margin.y * bounds.height; - // @ts-ignore - bounds.x -= state.text.margin.x * bounds.width; + bounds.y -= state.text!.margin!.y * bounds.height; + bounds.x -= state.text!.margin!.x * bounds.width; if ( !this.legacySpacing || (state.style.overflow !== 'fill' && state.style.overflow !== 'width') ) { const s = state.view.scale; - // @ts-ignore - const spacing = state.text.getSpacing(); + const spacing = state.text!.getSpacing(); bounds.x += spacing.x * s; bounds.y += spacing.y * s; - const hpos = getValue(state.style, 'labelPosition', ALIGN_CENTER); - const vpos = getValue(state.style, 'verticalLabelPosition', ALIGN_MIDDLE); - const lw = getValue(state.style, 'labelWidth', null); + const hpos = state.style.labelPosition ?? ALIGN_CENTER; + const vpos = state.style.verticalLabelPosition ?? ALIGN_MIDDLE; + const lw = state.style.labelWidth ?? null; bounds.width = Math.max( 0, @@ -1246,7 +1242,7 @@ class CellRenderer { this.createCellOverlays(state); if (state.overlays != null) { - const rot = mod(getValue(state.style, 'rotation', 0), 90); + const rot = mod(state.style.rotation ?? 0, 90); const rad = toRadians(rot); const cos = Math.cos(rad); const sin = Math.sin(rad); @@ -1344,7 +1340,7 @@ class CellRenderer { let rot = state.shape.getShapeRotation(); if (this.legacyControlPosition) { - rot = getValue(state.style, 'rotation', 0); + rot = state.style.rotation ?? 0; } else if (state.shape.isPaintBoundsInverted()) { const t = (state.width - state.height) / 2; cx += t; diff --git a/packages/core/src/view/connection/ConnectionHandler.ts b/packages/core/src/view/connection/ConnectionHandler.ts index 388161a15..77a37d277 100644 --- a/packages/core/src/view/connection/ConnectionHandler.ts +++ b/packages/core/src/view/connection/ConnectionHandler.ts @@ -679,7 +679,7 @@ class ConnectionHandler extends EventSource implements GraphPlugin { } } - return new MyCellMarker(this.graph); + return new MyCellMarker(this.graph) as CellMarker; } /** diff --git a/packages/core/src/view/editing/GraphEditingMixin.ts b/packages/core/src/view/editing/GraphEditingMixin.ts index 2a6cef37f..e96c6b37d 100644 --- a/packages/core/src/view/editing/GraphEditingMixin.ts +++ b/packages/core/src/view/editing/GraphEditingMixin.ts @@ -5,6 +5,7 @@ import InternalEvent from '../event/InternalEvent'; import InternalMouseEvent from '../event/InternalMouseEvent'; import { Graph } from '../Graph'; import { mixInto } from '../../util/Utils'; +import CellEditor from './CellEditor'; declare module '../Graph' { interface Graph { @@ -25,7 +26,6 @@ declare module '../Graph' { type PartialGraph = Pick< Graph, - | 'getCellEditor' | 'convertValueToString' | 'batchUpdate' | 'getModel' @@ -35,6 +35,7 @@ type PartialGraph = Pick< | 'cellSizeUpdated' | 'getCurrentCellStyle' | 'isCellLocked' + | 'getPlugin' >; type PartialEditing = Pick< Graph, @@ -94,7 +95,10 @@ const GraphEditingMixin: PartialType = { this.fireEvent( new EventObject(InternalEvent.START_EDITING, 'cell', cell, 'event', evt) ); - this.getCellEditor().startEditing(cell, evt); + + const cellEditor = this.getPlugin('CellEditor') as CellEditor; + cellEditor.startEditing(cell, evt); + this.fireEvent( new EventObject(InternalEvent.EDITING_STARTED, 'cell', cell, 'event', evt) ); @@ -122,7 +126,9 @@ const GraphEditingMixin: PartialType = { * should be stored. */ stopEditing(cancel = false) { - this.getCellEditor().stopEditing(cancel); + const cellEditor = this.getPlugin('CellEditor') as CellEditor; + cellEditor.stopEditing(cancel); + this.fireEvent(new EventObject(InternalEvent.EDITING_STOPPED, 'cancel', cancel)); }, @@ -200,7 +206,8 @@ const GraphEditingMixin: PartialType = { * @param cell {@link mxCell} that should be checked. */ isEditing(cell = null) { - const editingCell = this.getCellEditor().getEditingCell(); + const cellEditor = this.getPlugin('CellEditor') as CellEditor; + const editingCell = cellEditor.getEditingCell(); return !cell ? !!editingCell : cell === editingCell; }, diff --git a/packages/core/src/view/geometry/shape/Shape.ts b/packages/core/src/view/geometry/shape/Shape.ts index 4500f31fd..724a7b56e 100644 --- a/packages/core/src/view/geometry/shape/Shape.ts +++ b/packages/core/src/view/geometry/shape/Shape.ts @@ -808,7 +808,7 @@ class Shape { paintVertexShape(c: AbstractCanvas2D, x: number, y: number, w: number, h: number) { this.paintBackground(c, x, y, w, h); - if (!this.outline || !this.style || this.style.backgroundOutline === 0) { + if (!this.outline || !this.style || (this.style.backgroundOutline ?? 0) === 0) { c.setShadow(false); this.paintForeground(c, x, y, w, h); } @@ -1049,23 +1049,23 @@ class Shape { this.style = state.style; if (this.style) { - 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.fill = this.style.fillColor ?? this.fill; + this.gradient = this.style.gradientColor ?? this.gradient; + this.gradientDirection = this.style.gradientDirection ?? this.gradientDirection; + this.opacity = this.style.opacity ?? this.opacity; + this.fillOpacity = this.style.fillOpacity ?? this.fillOpacity; + this.strokeOpacity = this.style.strokeOpacity ?? this.strokeOpacity; + this.stroke = this.style.strokeColor ?? this.stroke; + this.strokeWidth = this.style.strokeWidth ?? this.strokeWidth; + this.spacing = this.style.spacing ?? this.spacing; + this.startSize = this.style.startSize ?? this.startSize; + this.endSize = this.style.endSize ?? this.endSize; + this.startArrow = this.style.startArrow ?? this.startArrow; + this.endArrow = this.style.endArrow ?? this.endArrow; + this.rotation = this.style.rotation ?? this.rotation; + this.direction = this.style.direction ?? this.direction; + this.flipH = !!this.style.flipH; + this.flipV = !!this.style.flipV; if (this.direction === DIRECTION_NORTH || this.direction === DIRECTION_SOUTH) { const tmp = this.flipH; @@ -1073,10 +1073,10 @@ class Shape { this.flipV = tmp; } - this.isShadow = this.style.shadow; - this.isDashed = this.style.dashed; - this.isRounded = this.style.rounded; - this.glass = this.style.glass; + this.isShadow = this.style.shadow ?? this.isShadow; + this.isDashed = this.style.dashed ?? this.isDashed; + this.isRounded = this.style.rounded ?? this.isRounded; + this.glass = this.style.glass ?? this.glass; } } diff --git a/packages/core/src/view/geometry/shape/edge/Connector.ts b/packages/core/src/view/geometry/shape/edge/Connector.ts index 244640f54..d6d60aa0b 100644 --- a/packages/core/src/view/geometry/shape/edge/Connector.ts +++ b/packages/core/src/view/geometry/shape/edge/Connector.ts @@ -96,7 +96,8 @@ class Connector extends Polyline { const unitX = dx / dist; const unitY = dy / dist; - const size = source ? this.style.startSize : this.style.endSize; + const size = + (source ? this.style.startSize : this.style.endSize) ?? DEFAULT_MARKERSIZE; // Allow for stroke width in the end point used and the // orthogonal vectors describing the direction of the marker @@ -131,11 +132,11 @@ class Connector extends Polyline { let size = 0; if (this.style.startArrow !== NONE) { - size = getNumber(this.style, 'startSize', DEFAULT_MARKERSIZE) + 1; + size = (this.style.startSize ?? DEFAULT_MARKERSIZE) + 1; } if (this.style.endArrow !== NONE) { - size = Math.max(size, getNumber(this.style, 'endSize', DEFAULT_MARKERSIZE)) + 1; + size = Math.max(size, this.style.endSize ?? DEFAULT_MARKERSIZE) + 1; } bbox.grow(size * this.scale); diff --git a/packages/core/src/view/geometry/shape/node/TextShape.ts b/packages/core/src/view/geometry/shape/node/TextShape.ts index a47faa09f..97c59bc88 100644 --- a/packages/core/src/view/geometry/shape/node/TextShape.ts +++ b/packages/core/src/view/geometry/shape/node/TextShape.ts @@ -89,26 +89,27 @@ class TextShape extends Shape { this.value = value; this.bounds = bounds; - this.color = color; - this.align = align; - this.valign = valign; - this.family = family; - this.size = size; - this.fontStyle = fontStyle; - this.spacing = spacing; - this.spacingTop = spacing + spacingTop; - this.spacingRight = spacing + spacingRight; - this.spacingBottom = spacing + spacingBottom; - this.spacingLeft = spacing + spacingLeft; - this.horizontal = horizontal; + this.color = color ?? 'black'; + this.align = align ?? ALIGN_CENTER; + this.valign = valign ?? ALIGN_MIDDLE; + this.family = family ?? DEFAULT_FONTFAMILY; + this.size = size ?? DEFAULT_FONTSIZE; + this.fontStyle = fontStyle ?? DEFAULT_FONTSTYLE; + this.spacing = spacing ?? 2; + this.spacingTop = this.spacing + (spacingTop ?? 0); + this.spacingRight = this.spacing + (spacingRight ?? 0); + this.spacingBottom = this.spacing + (spacingBottom ?? 0); + this.spacingLeft = this.spacing + (spacingLeft ?? 0); + this.horizontal = horizontal ?? true; this.background = background; this.border = border; - this.wrap = wrap; - this.clipped = clipped; - this.overflow = overflow; - this.labelPadding = labelPadding; + this.wrap = wrap ?? false; + this.clipped = clipped ?? false; + this.overflow = overflow ?? 'visible'; + this.labelPadding = labelPadding ?? 0; this.textDirection = textDirection; this.rotation = 0; + this.updateMargin(); } @@ -403,22 +404,25 @@ class TextShape extends Shape { super.apply(state); 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.fontStyle = this.style.fontStyle ?? this.fontStyle; + this.family = this.style.fontFamily ?? this.family; + this.size = this.style.fontSize ?? this.size; + this.color = this.style.fontColor ?? this.color; + this.align = this.style.align ?? this.align; + this.valign = this.style.verticalAlign ?? this.valign; + this.spacing = this.style.spacing ?? this.spacing; + this.spacingTop = (this.style.spacingTop ?? this.spacingTop - old) + this.spacing; + this.spacingRight = + (this.style.spacingRight ?? this.spacingRight - old) + this.spacing; + this.spacingBottom = + (this.style.spacingBottom ?? this.spacingBottom - old) + this.spacing; + this.spacingLeft = + (this.style.spacingLeft ?? this.spacingLeft - old) + this.spacing; + this.horizontal = this.style.horizontal ?? this.horizontal; + this.background = this.style.backgroundColor ?? this.background; + this.border = this.style.labelBorderColor ?? this.border; + this.textDirection = this.style.textDirection ?? DEFAULT_TEXT_DIRECTION; + this.opacity = this.style.textOpacity ?? 100; this.updateMargin(); } diff --git a/packages/core/src/view/selection/CellHighlight.ts b/packages/core/src/view/selection/CellHighlight.ts index 8e3c07ad8..d13ffbd55 100644 --- a/packages/core/src/view/selection/CellHighlight.ts +++ b/packages/core/src/view/selection/CellHighlight.ts @@ -13,7 +13,7 @@ import { import InternalEvent from '../event/InternalEvent'; import Rectangle from '../geometry/Rectangle'; import CellState from '../cell/datatypes/CellState'; -import graph from '../Graph'; +import { Graph } from '../Graph'; import Shape from '../geometry/shape/Shape'; import { ColorValue } from '../../types'; @@ -28,7 +28,7 @@ import { ColorValue } from '../../types'; */ class CellHighlight { constructor( - graph: graph, + graph: Graph, highlightColor: ColorValue = DEFAULT_VALID_COLOR, strokeWidth = HIGHLIGHT_STROKEWIDTH, dashed = false @@ -94,7 +94,7 @@ class CellHighlight { * Reference to the enclosing {@link graph}. * @default true */ - graph: graph; + graph: Graph; /** * Reference to the {@link CellState}. @@ -163,7 +163,7 @@ class CellHighlight { shape.init(this.graph.getView().getOverlayPane()); InternalEvent.redirectMouseEvents(shape.node, this.graph, this.state); - if ((this.graph).dialect !== DIALECT_SVG) { + if ((this.graph).dialect !== DIALECT_SVG) { shape.pointerEvents = false; } else { shape.svgPointerEvents = 'stroke'; diff --git a/packages/core/src/view/view/GraphView.ts b/packages/core/src/view/view/GraphView.ts index 5a5c157f9..8cfc3ac82 100644 --- a/packages/core/src/view/view/GraphView.ts +++ b/packages/core/src/view/view/GraphView.ts @@ -26,7 +26,6 @@ import { getCurrentStyle, getOffset, getRotatedPoint, - getValue, ptSegDistSq, relativeCcw, toRadians, @@ -952,7 +951,7 @@ class GraphView extends EventSource { const pState = parent ? this.getState(parent) : null; if (geo.relative && pState && !pState.cell.isEdge()) { - const alpha = toRadians(pState.style.rotation); + const alpha = toRadians(pState.style.rotation ?? 0); if (alpha !== 0) { const cos = Math.cos(alpha); @@ -1013,10 +1012,10 @@ class GraphView extends EventSource { * @param state {@link mxCellState} whose absolute offset should be updated. */ updateVertexLabelOffset(state: CellState): void { - const h = getValue(state.style, 'labelPosition', ALIGN_CENTER); + const h = state.style.labelPosition ?? ALIGN_CENTER; if (h === ALIGN_LEFT) { - let lw = getValue(state.style, 'labelWidth', null); + let lw = state.style.labelWidth ?? null; if (lw != null) { lw *= this.scale; @@ -1030,11 +1029,11 @@ class GraphView extends EventSource { // @ts-ignore state.absoluteOffset.x += state.width; } else if (h === ALIGN_CENTER) { - const lw = getValue(state.style, 'labelWidth', null); + const lw = state.style.labelWidth ?? null; if (lw != null) { // Aligns text block with given width inside the vertex width - const align = getValue(state.style, 'align', ALIGN_CENTER); + const align = state.style.align ?? ALIGN_CENTER; let dx = 0; if (align === ALIGN_CENTER) { @@ -1050,7 +1049,7 @@ class GraphView extends EventSource { } } - const v = getValue(state.style, 'verticalLabelPosition', ALIGN_MIDDLE); + const v = state.style.verticalLabelPosition ?? ALIGN_MIDDLE; if (v === ALIGN_TOP) { // @ts-ignore @@ -1309,8 +1308,10 @@ class GraphView extends EventSource { if ( (points == null || points.length < 2) && - (!getValue(edge.style, 'orthogonalLoop', false) || - ((sc == null || sc.point == null) && (tc == null || tc.point == null))) + !( + (edge.style.orthogonalLoop ?? false) || + ((sc == null || sc.point == null) && (tc == null || tc.point == null)) + ) ) { return source != null && source === target; } @@ -1414,18 +1415,19 @@ class GraphView extends EventSource { let next = this.getNextPoint(edge, end, source); const orth = this.graph.isOrthogonal(edge); - const alpha = toRadians(start.style.rotation); + const alpha = toRadians(start.style.rotation ?? 0); const center = new Point(start.getCenterX(), start.getCenterY()); if (alpha !== 0) { const cos = Math.cos(-alpha); const sin = Math.sin(-alpha); + next = getRotatedPoint(next, cos, sin, center); } - let border = edge.style.perimeterSpacing; + let border = edge.style.perimeterSpacing ?? 0; border += - edge.style[source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing'] || 0; + edge.style[source ? 'sourcePerimeterSpacing' : 'targetPerimeterSpacing'] ?? 0; let pt = this.getPerimeterPoint(start, next, alpha === 0 && orth, border); if (alpha !== 0) { @@ -1497,14 +1499,8 @@ class GraphView extends EventSource { let flipV = false; if (terminal.cell.isVertex()) { - flipH = getValue(terminal.style, 'flipH', 0) == 1; - flipV = getValue(terminal.style, 'flipV', 0) == 1; - - // Legacy support for stencilFlipH/V - if (terminal.shape != null && terminal.shape.stencil != null) { - flipH = getValue(terminal.style, 'stencilFlipH', 0) == 1 || flipH; - flipV = getValue(terminal.style, 'stencilFlipV', 0) == 1 || flipV; - } + flipH = !!terminal.style.flipH; + flipV = !!terminal.style.flipV; if (flipH) { point.x = 2 * bounds.getCenterX() - point.x; @@ -1540,7 +1536,7 @@ class GraphView extends EventSource { * Returns the x-coordinate of the center point for automatic routing. */ getRoutingCenterX(state: CellState): number { - const f = state.style ? state.style.routingCenterX : 0; + const f = state.style ? state.style.routingCenterX ?? 0 : 0; return state.getCenterX() + f * state.width; } @@ -1548,7 +1544,7 @@ class GraphView extends EventSource { * Returns the y-coordinate of the center point for automatic routing. */ getRoutingCenterY(state: CellState): number { - const f = state.style ? state.style.routingCenterY : 0; + const f = state.style ? state.style.routingCenterY ?? 0 : 0; return state.getCenterY() + f * state.height; } @@ -1597,7 +1593,7 @@ class GraphView extends EventSource { border: number = 0 ): Rectangle | null { if (terminal) { - border += terminal.style.perimeterSpacing; + border += terminal.style.perimeterSpacing ?? 0; } return (terminal).getPerimeterBounds(border * this.scale); } diff --git a/packages/html/stories/HelloWorld.stories.js b/packages/html/stories/HelloWorld.stories.js index e8ed58c28..b3bdb030d 100644 --- a/packages/html/stories/HelloWorld.stories.js +++ b/packages/html/stories/HelloWorld.stories.js @@ -25,11 +25,11 @@ const Template = ({ label, ...args }) => { container.style.background = 'url(/images/grid.gif)'; container.style.cursor = 'default'; - // if (!args.contextMenu) InternalEvent.disableContextMenu(container); + if (!args.contextMenu) InternalEvent.disableContextMenu(container); const graph = new Graph(container); - // if (args.rubberBand) new RubberBand(graph); + if (args.rubberBand) new RubberBand(graph); const parent = graph.getDefaultParent();