- Converted Graph* classes into mixins.
- Created MaxGraph type to expose. - CellStateStyles is now more concrete. - More compiler errors are resolved.development
parent
49b307a557
commit
bc400a3ae3
|
@ -1,5 +1,9 @@
|
|||
import type Cell from './view/cell/datatypes/Cell';
|
||||
import Shape from './view/geometry/shape/Shape';
|
||||
import CellState from './view/cell/datatypes/CellState';
|
||||
import RectangleShape from './view/geometry/shape/node/RectangleShape';
|
||||
import type Shape from './view/geometry/shape/Shape';
|
||||
import type Graph from './view/Graph';
|
||||
import ImageBox from './view/image/ImageBox';
|
||||
|
||||
export type CellMap = {
|
||||
[id: string]: Cell;
|
||||
|
@ -27,20 +31,34 @@ export type CellStateStyles = {
|
|||
absoluteArcSize: number;
|
||||
align: AlignValue;
|
||||
arcSize: number;
|
||||
aspect: string;
|
||||
autosize: boolean;
|
||||
backgroundColor: ColorValue;
|
||||
backgroundOutline: number;
|
||||
bendable: boolean;
|
||||
cloneable: boolean;
|
||||
curved: boolean;
|
||||
dashed: boolean;
|
||||
dashPattern: string;
|
||||
defaultEdge: CellStateStyles;
|
||||
defaultVertex: CellStateStyles;
|
||||
deletable: boolean;
|
||||
direction: DirectionValue;
|
||||
edge: string;
|
||||
editable: boolean;
|
||||
endArrow: ArrowType;
|
||||
endFill: boolean;
|
||||
endSize: number;
|
||||
entryX: number;
|
||||
entryY: number;
|
||||
exitX: number;
|
||||
exitY: number;
|
||||
fillColor: ColorValue;
|
||||
fillOpacity: number;
|
||||
fixDash: boolean;
|
||||
flipH: boolean;
|
||||
flipV: boolean;
|
||||
foldable: boolean;
|
||||
fontColor: ColorValue;
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
|
@ -63,13 +81,28 @@ export type CellStateStyles = {
|
|||
indicatorWidth: number;
|
||||
labelBorderColor: ColorValue;
|
||||
labelPosition: AlignValue;
|
||||
loop: Function;
|
||||
margin: number;
|
||||
movable: boolean;
|
||||
noEdgeStyle: boolean;
|
||||
opacity: number;
|
||||
overflow: OverflowValue;
|
||||
perimeter: Function | string | null;
|
||||
perimeterSpacing: number;
|
||||
pointerEvents: boolean;
|
||||
resizeable: boolean;
|
||||
resizeHeight: boolean;
|
||||
resizeWidth: boolean;
|
||||
rotatable: boolean;
|
||||
rotation: number;
|
||||
rounded: boolean;
|
||||
routingCenterX: number;
|
||||
routingCenterY: number;
|
||||
separatorColor: ColorValue;
|
||||
shadow: boolean;
|
||||
shape: ShapeValue;
|
||||
sourcePerimeterSpacing: number;
|
||||
sourcePort: string;
|
||||
spacing: number;
|
||||
spacingBottom: number;
|
||||
spacingLeft: number;
|
||||
|
@ -83,10 +116,13 @@ export type CellStateStyles = {
|
|||
strokeWidth: number;
|
||||
swimlaneFillColor: ColorValue;
|
||||
swimlaneLine: boolean;
|
||||
targetPerimeterSpacing: number;
|
||||
targetPort: string;
|
||||
textDirection: TextDirectionValue;
|
||||
textOpacity: number;
|
||||
verticalAlign: VAlignValue;
|
||||
verticalLabelPosition: VAlignValue;
|
||||
whiteSpace: WhiteSpaceValue;
|
||||
};
|
||||
|
||||
export type ColorValue = string;
|
||||
|
@ -95,6 +131,7 @@ 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 WhiteSpaceValue = 'normal' | 'wrap' | 'nowrap' | 'pre';
|
||||
export type ArrowType =
|
||||
| 'none'
|
||||
| 'classic'
|
||||
|
@ -106,6 +143,23 @@ export type ArrowType =
|
|||
| 'oval'
|
||||
| 'diamond'
|
||||
| 'diamondThin';
|
||||
export type ShapeValue =
|
||||
| 'rectangle'
|
||||
| 'ellipse'
|
||||
| 'doubleEllipse'
|
||||
| 'rhombus'
|
||||
| 'line'
|
||||
| 'image'
|
||||
| 'arrow'
|
||||
| 'arrowConnector'
|
||||
| 'label'
|
||||
| 'cylinder'
|
||||
| 'swimlane'
|
||||
| 'connector'
|
||||
| 'actor'
|
||||
| 'cloud'
|
||||
| 'triangle'
|
||||
| 'hexagon';
|
||||
|
||||
export type CanvasState = {
|
||||
dx: number;
|
||||
|
@ -151,3 +205,37 @@ export interface Gradient extends SVGLinearGradientElement {
|
|||
export type GradientMap = {
|
||||
[k: string]: Gradient;
|
||||
};
|
||||
|
||||
export interface GraphPlugin {
|
||||
onInit: (graph: Graph) => void;
|
||||
onDestroy: () => void;
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
export type Listener = {
|
||||
name: string;
|
||||
f: EventListener;
|
||||
};
|
||||
|
||||
export type ListenerTarget = {
|
||||
mxListenerList?: Listener[];
|
||||
};
|
||||
|
||||
export type Listenable = (Node | Window) & ListenerTarget;
|
||||
|
||||
export type GestureEvent = Event &
|
||||
MouseEvent & {
|
||||
scale?: number;
|
||||
pointerId?: number;
|
||||
};
|
||||
|
||||
export type EventCache = GestureEvent[];
|
||||
|
||||
export interface CellHandle {
|
||||
state: CellState;
|
||||
cursor: string;
|
||||
image: ImageBox | null;
|
||||
shape: Shape | null;
|
||||
setVisible: (v: boolean) => void;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class Dictionary<T, U> {
|
|||
get(key: T) {
|
||||
const id = ObjectIdentity.get(key);
|
||||
|
||||
return this.map[id];
|
||||
return this.map[id] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,7 +68,7 @@ class Dictionary<T, U> {
|
|||
const previous = this.map[id];
|
||||
this.map[id] = value;
|
||||
|
||||
return previous;
|
||||
return previous ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +82,7 @@ class Dictionary<T, U> {
|
|||
const previous = this.map[id];
|
||||
delete this.map[id];
|
||||
|
||||
return previous;
|
||||
return previous ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1257,7 +1257,7 @@ export const getDocumentScrollOrigin = (doc: Document) => {
|
|||
* included. Default is true.
|
||||
*/
|
||||
export const getScrollOrigin = (
|
||||
node: HTMLElement | null,
|
||||
node: HTMLElement | null = null,
|
||||
includeAncestors = false,
|
||||
includeDocument = true
|
||||
) => {
|
||||
|
@ -1548,7 +1548,7 @@ export const relativeCcw = (
|
|||
* node - DOM node to set the opacity for.
|
||||
* value - Opacity in %. Possible values are between 0 and 100.
|
||||
*/
|
||||
export const setOpacity = (node: HTMLElement, value: number) => {
|
||||
export const setOpacity = (node: HTMLElement | SVGElement, value: number) => {
|
||||
node.style.opacity = String(value / 100);
|
||||
};
|
||||
|
||||
|
@ -1743,7 +1743,12 @@ 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: CellArray,
|
||||
key: string,
|
||||
value: any
|
||||
) => {
|
||||
if (cells.length > 0) {
|
||||
model.beginUpdate();
|
||||
try {
|
||||
|
@ -1842,7 +1847,7 @@ export const setStyle = (style: string | null, key: string, value: any) => {
|
|||
*/
|
||||
export const setCellStyleFlags = (
|
||||
model: Model,
|
||||
cells: Cell[],
|
||||
cells: CellArray,
|
||||
key: string,
|
||||
flag: number,
|
||||
value: boolean
|
||||
|
@ -1985,8 +1990,8 @@ export const getSizeForString = (
|
|||
text: string,
|
||||
fontSize = DEFAULT_FONTSIZE,
|
||||
fontFamily = DEFAULT_FONTFAMILY,
|
||||
textWidth: number,
|
||||
fontStyle: number
|
||||
textWidth: number | null = null,
|
||||
fontStyle: number | null = null
|
||||
) => {
|
||||
const div = document.createElement('div');
|
||||
|
||||
|
@ -1996,7 +2001,7 @@ export const getSizeForString = (
|
|||
div.style.lineHeight = `${Math.round(fontSize * LINE_HEIGHT)}px`;
|
||||
|
||||
// Sets the font style
|
||||
if (fontStyle != null) {
|
||||
if (fontStyle !== null) {
|
||||
if ((fontStyle & FONT_BOLD) === FONT_BOLD) {
|
||||
div.style.fontWeight = 'bold';
|
||||
}
|
||||
|
@ -2025,7 +2030,7 @@ export const getSizeForString = (
|
|||
div.style.visibility = 'hidden';
|
||||
div.style.display = 'inline-block';
|
||||
|
||||
if (textWidth != null) {
|
||||
if (textWidth !== null) {
|
||||
div.style.width = `${textWidth}px`;
|
||||
div.style.whiteSpace = 'normal';
|
||||
} else {
|
||||
|
@ -2342,4 +2347,19 @@ export const isNullish = (v: string | object | null | undefined | number) =>
|
|||
export const isNotNullish = (v: string | object | null | undefined | number) =>
|
||||
!isNullish(v);
|
||||
|
||||
// Mixins support
|
||||
export const applyMixins = (derivedCtor: any, constructors: any[]) => {
|
||||
constructors.forEach((baseCtor) => {
|
||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
|
||||
Object.defineProperty(
|
||||
derivedCtor.prototype,
|
||||
name,
|
||||
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) || Object.create(null)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const autoImplement = <T>(): new () => T => class {} as any;
|
||||
|
||||
export default utils;
|
||||
|
|
|
@ -17,12 +17,13 @@ import ConnectionHandler from './connection/ConnectionHandler';
|
|||
import GraphHandler from './GraphHandler';
|
||||
import PanningHandler from './panning/PanningHandler';
|
||||
import PopupMenuHandler from './popups_menus/PopupMenuHandler';
|
||||
import mxGraphSelectionModel from './selection/mxGraphSelectionModel';
|
||||
import GraphView from './view/GraphView';
|
||||
import CellRenderer from './cell/CellRenderer';
|
||||
import CellEditor from './editing/CellEditor';
|
||||
import Point from './geometry/Point';
|
||||
import {
|
||||
applyMixins,
|
||||
autoImplement,
|
||||
getBoundingBox,
|
||||
getCurrentStyle,
|
||||
getValue,
|
||||
|
@ -48,6 +49,90 @@ 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 GraphEvents from './event/GraphEvents';
|
||||
import GraphImage from './image/GraphImage';
|
||||
import GraphCells from './cell/GraphCells';
|
||||
import GraphSelection from './selection/GraphSelection';
|
||||
import GraphConnections from './connection/GraphConnections';
|
||||
import GraphEdge from './cell/edge/GraphEdge';
|
||||
import GraphVertex from './cell/vertex/GraphVertex';
|
||||
import GraphOverlays from './layout/GraphOverlays';
|
||||
import GraphEditing from './editing/GraphEditing';
|
||||
import GraphFolding from './folding/GraphFolding';
|
||||
import GraphLabel from './label/GraphLabel';
|
||||
import GraphValidation from './validation/GraphValidation';
|
||||
import GraphSnap from './snap/GraphSnap';
|
||||
|
||||
import type { GraphPlugin } from '../types';
|
||||
import GraphTooltip from './tooltip/GraphTooltip';
|
||||
import GraphTerminal from './terminal/GraphTerminal';
|
||||
|
||||
type PartialEvents = Pick<
|
||||
GraphEvents,
|
||||
| 'sizeDidChange'
|
||||
| 'isNativeDblClickEnabled'
|
||||
| 'dblClick'
|
||||
| 'fireMouseEvent'
|
||||
| 'isMouseDown'
|
||||
| 'fireGestureEvent'
|
||||
| 'addMouseListener'
|
||||
| 'removeMouseListener'
|
||||
| 'isGridEnabledEvent'
|
||||
| 'isIgnoreTerminalEvent'
|
||||
| 'isCloneEvent'
|
||||
| 'isToggleEvent'
|
||||
| 'getClickTolerance'
|
||||
>;
|
||||
type PartialSelection = Pick<
|
||||
GraphSelection,
|
||||
| 'clearSelection'
|
||||
| 'isCellSelected'
|
||||
| 'getSelectionCount'
|
||||
| 'selectCellForEvent'
|
||||
| 'setSelectionCell'
|
||||
>;
|
||||
type PartialCells = Pick<
|
||||
GraphCells,
|
||||
| 'removeStateForCell'
|
||||
| 'getCellStyle'
|
||||
| 'getCellAt'
|
||||
| 'isCellBendable'
|
||||
| 'isCellsCloneable'
|
||||
| 'cloneCell'
|
||||
| 'setCellStyles'
|
||||
>;
|
||||
type PartialConnections = Pick<
|
||||
GraphConnections,
|
||||
| 'getConnectionConstraint'
|
||||
| 'getConnectionPoint'
|
||||
| 'isCellDisconnectable'
|
||||
| 'getOutlineConstraint'
|
||||
| 'connectCell'
|
||||
>;
|
||||
type PartialEditing = Pick<GraphEditing, 'isEditing'>;
|
||||
type PartialTooltip = Pick<GraphTooltip, 'getTooltip'>;
|
||||
type PartialValidation = Pick<
|
||||
GraphValidation,
|
||||
'getEdgeValidationError' | 'validationAlert'
|
||||
>;
|
||||
type PartialLabel = Pick<GraphLabel, 'isLabelMovable'>;
|
||||
type PartialTerminal = Pick<GraphTerminal, 'isTerminalPointMovable'>;
|
||||
type PartialSnap = Pick<GraphSnap, 'snap' | 'getGridSize'>;
|
||||
type PartialEdge = Pick<GraphEdge, 'isAllowDanglingEdges' | 'isResetEdgesOnConnect'>;
|
||||
type PartialClass = PartialEvents &
|
||||
PartialSelection &
|
||||
PartialCells &
|
||||
PartialConnections &
|
||||
PartialEditing &
|
||||
PartialTooltip &
|
||||
PartialValidation &
|
||||
PartialLabel &
|
||||
PartialTerminal &
|
||||
PartialSnap &
|
||||
PartialEdge &
|
||||
EventSource;
|
||||
|
||||
export type MaxGraph = Graph & PartialClass;
|
||||
|
||||
/**
|
||||
* Extends {@link EventSource} to implement a graph component for
|
||||
|
@ -66,21 +151,19 @@ import ElbowEdgeHandler from './cell/edge/ElbowEdgeHandler';
|
|||
* @class graph
|
||||
* @extends {EventSource}
|
||||
*/
|
||||
class Graph extends EventSource {
|
||||
// @ts-ignore
|
||||
class Graph extends autoImplement<PartialClass>() {
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
model: Model,
|
||||
renderHint: string = DIALECT_SVG,
|
||||
plugins: GraphPlugin[] = [],
|
||||
stylesheet: Stylesheet | null = null
|
||||
) {
|
||||
super();
|
||||
|
||||
// Converts the renderHint into a dialect
|
||||
this.renderHint = renderHint;
|
||||
this.dialect = 'svg';
|
||||
|
||||
// Initializes the main members that do not require a container
|
||||
this.model = model != null ? model : new Model();
|
||||
this.container = container;
|
||||
this.model = model;
|
||||
this.plugins = plugins;
|
||||
this.cellRenderer = this.createCellRenderer();
|
||||
this.setSelectionModel(this.createSelectionModel());
|
||||
this.setStylesheet(stylesheet != null ? stylesheet : this.createStylesheet());
|
||||
|
@ -97,9 +180,7 @@ class Graph extends EventSource {
|
|||
this.createHandlers();
|
||||
|
||||
// Initializes the display if a container was specified
|
||||
if (container != null) {
|
||||
this.init(container);
|
||||
}
|
||||
this.init();
|
||||
|
||||
this.view.revalidate();
|
||||
}
|
||||
|
@ -109,9 +190,7 @@ class Graph extends EventSource {
|
|||
*
|
||||
* @param container DOM node that will contain the graph display.
|
||||
*/
|
||||
init(container: HTMLElement): void {
|
||||
this.container = container;
|
||||
|
||||
init() {
|
||||
// Initializes the in-place editor
|
||||
this.cellEditor = this.createCellEditor();
|
||||
|
||||
|
@ -122,27 +201,45 @@ class Graph extends EventSource {
|
|||
this.sizeDidChange();
|
||||
|
||||
// Hides tooltips and resets tooltip timer if mouse leaves container
|
||||
InternalEvent.addListener(container, 'mouseleave', (evt: Event) => {
|
||||
InternalEvent.addListener(this.container, 'mouseleave', (evt: Event) => {
|
||||
if (
|
||||
this.tooltipHandler != null &&
|
||||
this.tooltipHandler.div != null &&
|
||||
this.tooltipHandler.div != (<MouseEvent>evt).relatedTarget
|
||||
this.tooltipHandler.div &&
|
||||
this.tooltipHandler.div !== (<MouseEvent>evt).relatedTarget
|
||||
) {
|
||||
this.tooltipHandler.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Initiailzes plugins
|
||||
this.plugins.forEach((p) => p.onInit(this));
|
||||
}
|
||||
|
||||
// TODO: Document me!
|
||||
|
||||
// @ts-ignore
|
||||
container: HTMLElement;
|
||||
|
||||
getContainer = () => this.container;
|
||||
|
||||
destroyed: boolean = false;
|
||||
tooltipHandler: TooltipHandler | null = null;
|
||||
selectionCellsHandler: SelectionCellsHandler | null = null;
|
||||
popupMenuHandler: PopupMenuHandler | null = null;
|
||||
connectionHandler: ConnectionHandler | null = null;
|
||||
graphHandler: GraphHandler | null = null;
|
||||
|
||||
// Handlers
|
||||
// @ts-ignore Cannot be null.
|
||||
tooltipHandler: TooltipHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
selectionCellsHandler: SelectionCellsHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
popupMenuHandler: PopupMenuHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
connectionHandler: ConnectionHandler;
|
||||
// @ts-ignore Cannot be null.
|
||||
graphHandler: GraphHandler;
|
||||
|
||||
getTooltipHandler = () => this.tooltipHandler;
|
||||
getSelectionCellsHandler = () => this.selectionCellsHandler;
|
||||
getPopupMenuHandler = () => this.popupMenuHandler;
|
||||
getConnectionHandler = () => this.connectionHandler;
|
||||
getGraphHandler = () => this.graphHandler;
|
||||
|
||||
graphModelChangeListener: Function | null = null;
|
||||
paintBackground: Function | null = null;
|
||||
|
||||
|
@ -155,6 +252,8 @@ class Graph extends EventSource {
|
|||
*/
|
||||
model: Model;
|
||||
|
||||
plugins: GraphPlugin[];
|
||||
|
||||
/**
|
||||
* Holds the {@link GraphView} that caches the {@link CellState}s for the cells.
|
||||
*/
|
||||
|
@ -192,6 +291,10 @@ class Graph extends EventSource {
|
|||
*/
|
||||
cellRenderer: CellRenderer;
|
||||
|
||||
getCellRenderer() {
|
||||
return this.cellRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* RenderHint as it was passed to the constructor.
|
||||
*/
|
||||
|
@ -298,12 +401,16 @@ class Graph extends EventSource {
|
|||
*/
|
||||
exportEnabled: boolean = true;
|
||||
|
||||
isExportEnabled = () => this.exportEnabled;
|
||||
|
||||
/**
|
||||
* Specifies the return value for {@link canImportCell}.
|
||||
* @default true
|
||||
*/
|
||||
importEnabled: boolean = true;
|
||||
|
||||
isImportEnabled = () => this.importEnabled;
|
||||
|
||||
/**
|
||||
* Specifies if the graph should automatically scroll regardless of the
|
||||
* scrollbars. This will scroll the container using positive values for
|
||||
|
@ -424,18 +531,6 @@ class Graph extends EventSource {
|
|||
*/
|
||||
multigraph: boolean = true;
|
||||
|
||||
/**
|
||||
* Specifies if labels should be visible. This is used in {@link getLabel}. Default
|
||||
* is true.
|
||||
*/
|
||||
labelsVisible: boolean = true;
|
||||
|
||||
/**
|
||||
* Specifies the return value for {@link isHtmlLabel}.
|
||||
* @default false
|
||||
*/
|
||||
htmlLabels: boolean = false;
|
||||
|
||||
/**
|
||||
* Specifies the minimum scale to be applied in {@link fit}. Set this to `null` to allow any value.
|
||||
* @default 0.1
|
||||
|
@ -460,6 +555,10 @@ class Graph extends EventSource {
|
|||
16
|
||||
);
|
||||
|
||||
getWarningImage() {
|
||||
return this.warningImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the resource key for the error message to be displayed in
|
||||
* non-multigraphs when two vertices are already connected. If the resource
|
||||
|
@ -469,6 +568,8 @@ class Graph extends EventSource {
|
|||
alreadyConnectedResource: string =
|
||||
mxClient.language != 'none' ? 'alreadyConnected' : '';
|
||||
|
||||
getAlreadyConnectedResource = () => this.alreadyConnectedResource;
|
||||
|
||||
/**
|
||||
* Specifies the resource key for the warning message to be displayed when
|
||||
* a collapsed cell contains validation errors. If the resource for this
|
||||
|
@ -478,6 +579,8 @@ class Graph extends EventSource {
|
|||
containsValidationErrorsResource: string =
|
||||
mxClient.language != 'none' ? 'containsValidationErrors' : '';
|
||||
|
||||
getContainsValidationErrorsResource = () => this.containsValidationErrorsResource;
|
||||
|
||||
// TODO: Document me!!
|
||||
batchUpdate(fn: Function): void {
|
||||
(<Model>this.getModel()).beginUpdate();
|
||||
|
@ -505,7 +608,7 @@ class Graph extends EventSource {
|
|||
/**
|
||||
* Creates and returns a new {@link TooltipHandler} to be used in this graph.
|
||||
*/
|
||||
createTooltipHandler(): TooltipHandler {
|
||||
createTooltipHandler() {
|
||||
return new TooltipHandler(this);
|
||||
}
|
||||
|
||||
|
@ -554,7 +657,7 @@ class Graph extends EventSource {
|
|||
/**
|
||||
* Creates a new {@link GraphView} to be used in this graph.
|
||||
*/
|
||||
createGraphView(): GraphView {
|
||||
createGraphView() {
|
||||
return new GraphView(this);
|
||||
}
|
||||
|
||||
|
@ -575,28 +678,28 @@ class Graph extends EventSource {
|
|||
/**
|
||||
* Returns the {@link Model} that contains the cells.
|
||||
*/
|
||||
getModel(): Model {
|
||||
return <Model>this.model;
|
||||
getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GraphView} that contains the {@link mxCellStates}.
|
||||
*/
|
||||
getView(): GraphView {
|
||||
return <GraphView>this.view;
|
||||
getView() {
|
||||
return this.view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Stylesheet} that defines the style.
|
||||
*/
|
||||
getStylesheet(): Stylesheet | null {
|
||||
getStylesheet() {
|
||||
return this.stylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Stylesheet} that defines the style.
|
||||
*/
|
||||
setStylesheet(stylesheet: Stylesheet | null): void {
|
||||
setStylesheet(stylesheet: Stylesheet) {
|
||||
this.stylesheet = stylesheet;
|
||||
}
|
||||
|
||||
|
@ -627,7 +730,7 @@ class Graph extends EventSource {
|
|||
// Resets the view settings, removes all cells and clears
|
||||
// the selection if the root changes.
|
||||
if (change instanceof RootChange) {
|
||||
this.selection.clearSelection();
|
||||
this.clearSelection();
|
||||
this.setDefaultParent(null);
|
||||
this.cells.removeStateForCell(change.previous);
|
||||
|
||||
|
@ -1006,8 +1109,8 @@ class Graph extends EventSource {
|
|||
*
|
||||
* @param state {@link mxCellState} whose handler should be created.
|
||||
*/
|
||||
createHandler(state: CellState): mxEdgeHandler | VertexHandler | null {
|
||||
let result: mxEdgeHandler | VertexHandler | null = null;
|
||||
createHandler(state: CellState): EdgeHandler | VertexHandler | null {
|
||||
let result: EdgeHandler | VertexHandler | null = null;
|
||||
|
||||
if (state.cell.isEdge()) {
|
||||
const source = state.getVisibleTerminalState(true);
|
||||
|
@ -1041,7 +1144,7 @@ class Graph extends EventSource {
|
|||
*
|
||||
* @param state {@link mxCellState} to create the handler for.
|
||||
*/
|
||||
createEdgeHandler(state: CellState, edgeStyle: any): mxEdgeHandler {
|
||||
createEdgeHandler(state: CellState, edgeStyle: any): EdgeHandler {
|
||||
let result = null;
|
||||
if (
|
||||
edgeStyle == EdgeStyle.Loop ||
|
||||
|
@ -1056,7 +1159,7 @@ class Graph extends EventSource {
|
|||
) {
|
||||
result = this.createEdgeSegmentHandler(state);
|
||||
} else {
|
||||
result = new mxEdgeHandler(state);
|
||||
result = new EdgeHandler(state);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1120,7 +1223,7 @@ class Graph extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} that represents the root.
|
||||
*/
|
||||
getTranslateForRoot(cell: Cell): Point | null {
|
||||
getTranslateForRoot(cell: Cell | null): Point | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1534,6 +1637,10 @@ class Graph extends EventSource {
|
|||
this.defaultParent = cell;
|
||||
}
|
||||
|
||||
getCellEditor() {
|
||||
return this.cellEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the graph and all its resources.
|
||||
*/
|
||||
|
@ -1560,5 +1667,20 @@ class Graph extends EventSource {
|
|||
}
|
||||
}
|
||||
|
||||
applyMixins(Graph, [
|
||||
GraphEvents,
|
||||
GraphImage,
|
||||
GraphCells,
|
||||
GraphSelection,
|
||||
GraphConnections,
|
||||
GraphEdge,
|
||||
GraphVertex,
|
||||
GraphOverlays,
|
||||
GraphEditing,
|
||||
GraphFolding,
|
||||
GraphLabel,
|
||||
GraphValidation,
|
||||
GraphSnap,
|
||||
]);
|
||||
|
||||
export default Graph;
|
||||
// import("../../serialization/mxGraphCodec");
|
||||
|
|
|
@ -23,12 +23,8 @@ import {
|
|||
import Dictionary from '../util/Dictionary';
|
||||
import CellHighlight from './selection/CellHighlight';
|
||||
import Rectangle from './geometry/Rectangle';
|
||||
import {
|
||||
getClientX,
|
||||
getClientY,
|
||||
isAltDown,
|
||||
isMultiTouchEvent,
|
||||
} from '../util/EventUtils';
|
||||
import { getClientX, getClientY, isAltDown, isMultiTouchEvent } from '../util/EventUtils';
|
||||
import { MaxGraph } from './Graph';
|
||||
|
||||
/**
|
||||
* Class: mxGraphHandler
|
||||
|
@ -51,7 +47,7 @@ import {
|
|||
* graph - Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
class GraphHandler {
|
||||
constructor(graph) {
|
||||
constructor(graph: MaxGraph) {
|
||||
this.graph = graph;
|
||||
this.graph.addMouseListener(this);
|
||||
|
||||
|
@ -150,8 +146,7 @@ class GraphHandler {
|
|||
*
|
||||
* Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
// graph: mxGraph;
|
||||
graph = null;
|
||||
graph: MaxGraph;
|
||||
|
||||
/**
|
||||
* Variable: maxCells
|
||||
|
@ -484,8 +479,7 @@ class GraphHandler {
|
|||
!this.graph.isCellSelected(cell) &&
|
||||
!this.graph.isSwimlane(parent)) ||
|
||||
this.graph.isCellSelected(parent)) &&
|
||||
(this.graph.isToggleEvent(me.getEvent()) ||
|
||||
!this.graph.isCellSelected(parent))
|
||||
(this.graph.isToggleEvent(me.getEvent()) || !this.graph.isCellSelected(parent))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -570,10 +564,7 @@ class GraphHandler {
|
|||
if (me.isSource(state.control)) {
|
||||
this.graph.selectCellForEvent(cell, me.getEvent());
|
||||
} else {
|
||||
if (
|
||||
!this.graph.isToggleEvent(me.getEvent()) ||
|
||||
!isAltDown(me.getEvent())
|
||||
) {
|
||||
if (!this.graph.isToggleEvent(me.getEvent()) || !isAltDown(me.getEvent())) {
|
||||
const model = this.graph.getModel();
|
||||
let parent = cell.getParent();
|
||||
|
||||
|
@ -654,8 +645,7 @@ class GraphHandler {
|
|||
cell.getTerminal(true) == null ||
|
||||
cell.getTerminal(false) == null ||
|
||||
this.graph.allowDanglingEdges ||
|
||||
(this.graph.isCloneEvent(me.getEvent()) &&
|
||||
this.graph.isCellsCloneable()))
|
||||
(this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable()))
|
||||
) {
|
||||
this.start(cell, me.getX(), me.getY());
|
||||
} else if (this.delayedSelection) {
|
||||
|
@ -687,9 +677,7 @@ class GraphHandler {
|
|||
);
|
||||
};
|
||||
|
||||
return this.graph.view.getCellStates(
|
||||
model.filterDescendants(filter, parent)
|
||||
);
|
||||
return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -863,10 +851,7 @@ class GraphHandler {
|
|||
|
||||
// Uses connected states as guides
|
||||
const connected = new Dictionary();
|
||||
const opps = this.graph.getOpposites(
|
||||
this.graph.getEdges(this.cell),
|
||||
this.cell
|
||||
);
|
||||
const opps = this.graph.getOpposites(this.graph.getEdges(this.cell), this.cell);
|
||||
|
||||
for (let i = 0; i < opps.length; i += 1) {
|
||||
const state = this.graph.view.getState(opps[i]);
|
||||
|
@ -962,11 +947,7 @@ class GraphHandler {
|
|||
*/
|
||||
// getDelta(me: mxMouseEvent): mxPoint;
|
||||
getDelta(me) {
|
||||
const point = utils.convertPoint(
|
||||
this.graph.container,
|
||||
me.getX(),
|
||||
me.getY()
|
||||
);
|
||||
const point = utils.convertPoint(this.graph.container, me.getX(), me.getY());
|
||||
|
||||
return new point(
|
||||
point.x - this.first.x - this.graph.panDx,
|
||||
|
@ -1067,11 +1048,7 @@ class GraphHandler {
|
|||
) {
|
||||
// Highlight is used for highlighting drop targets
|
||||
if (this.highlight == null) {
|
||||
this.highlight = new CellHighlight(
|
||||
this.graph,
|
||||
DROP_TARGET_COLOR,
|
||||
3
|
||||
);
|
||||
this.highlight = new CellHighlight(this.graph, DROP_TARGET_COLOR, 3);
|
||||
}
|
||||
|
||||
const clone =
|
||||
|
@ -1113,8 +1090,7 @@ class GraphHandler {
|
|||
|
||||
if (state != null) {
|
||||
const error = graph.getEdgeValidationError(null, this.cell, cell);
|
||||
const color =
|
||||
error == null ? VALID_COLOR : INVALID_CONNECT_TARGET_COLOR;
|
||||
const color = error == null ? VALID_COLOR : INVALID_CONNECT_TARGET_COLOR;
|
||||
this.setHighlightColor(color);
|
||||
highlight = true;
|
||||
}
|
||||
|
@ -1131,13 +1107,7 @@ class GraphHandler {
|
|||
delta = this.guide.move(this.bounds, delta, gridEnabled, clone);
|
||||
hideGuide = false;
|
||||
} else {
|
||||
delta = this.graph.snapDelta(
|
||||
delta,
|
||||
this.bounds,
|
||||
!gridEnabled,
|
||||
false,
|
||||
false
|
||||
);
|
||||
delta = this.graph.snapDelta(delta, this.bounds, !gridEnabled, false, false);
|
||||
}
|
||||
|
||||
if (this.guide != null && hideGuide) {
|
||||
|
@ -1178,11 +1148,7 @@ class GraphHandler {
|
|||
) {
|
||||
let cursor = graph.getCursorForMouseEvent(me);
|
||||
|
||||
if (
|
||||
cursor == null &&
|
||||
graph.isEnabled() &&
|
||||
graph.isCellMovable(me.getCell())
|
||||
) {
|
||||
if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell())) {
|
||||
if (me.getCell().isEdge()) {
|
||||
cursor = CURSOR_MOVABLE_EDGE;
|
||||
} else {
|
||||
|
@ -1355,10 +1321,7 @@ class GraphHandler {
|
|||
|
||||
if (source == null || !this.isCellMoving(source.cell)) {
|
||||
const pt0 = pts[0];
|
||||
state.setAbsoluteTerminalPoint(
|
||||
new Point(pt0.x + dx, pt0.y + dy),
|
||||
true
|
||||
);
|
||||
state.setAbsoluteTerminalPoint(new Point(pt0.x + dx, pt0.y + dy), true);
|
||||
source = null;
|
||||
} else {
|
||||
state.view.updateFixedTerminalPoint(
|
||||
|
@ -1371,10 +1334,7 @@ class GraphHandler {
|
|||
|
||||
if (target == null || !this.isCellMoving(target.cell)) {
|
||||
const ptn = pts[pts.length - 1];
|
||||
state.setAbsoluteTerminalPoint(
|
||||
new Point(ptn.x + dx, ptn.y + dy),
|
||||
false
|
||||
);
|
||||
state.setAbsoluteTerminalPoint(new Point(ptn.x + dx, ptn.y + dy), false);
|
||||
target = null;
|
||||
} else {
|
||||
state.view.updateFixedTerminalPoint(
|
||||
|
@ -1411,9 +1371,7 @@ class GraphHandler {
|
|||
*/
|
||||
redrawHandles(states) {
|
||||
for (let i = 0; i < states.length; i += 1) {
|
||||
const handler = this.graph.selectionCellsHandler.getHandler(
|
||||
states[i][0].cell
|
||||
);
|
||||
const handler = this.graph.selectionCellsHandler.getHandler(states[i][0].cell);
|
||||
|
||||
if (handler != null) {
|
||||
handler.redraw(true);
|
||||
|
@ -1627,21 +1585,10 @@ class GraphHandler {
|
|||
me.getGraphY()
|
||||
);
|
||||
} else {
|
||||
this.moveCells(
|
||||
this.cells,
|
||||
dx,
|
||||
dy,
|
||||
clone,
|
||||
this.target,
|
||||
me.getEvent()
|
||||
);
|
||||
this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
this.isSelectEnabled() &&
|
||||
this.delayedSelection &&
|
||||
this.cell != null
|
||||
) {
|
||||
} else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null) {
|
||||
this.selectDelayed(me);
|
||||
}
|
||||
}
|
||||
|
@ -1707,9 +1654,7 @@ class GraphHandler {
|
|||
getClientX(evt),
|
||||
getClientY(evt)
|
||||
);
|
||||
const alpha = utils.toRadians(
|
||||
utils.getValue(pState.style, 'rotation') || 0
|
||||
);
|
||||
const alpha = utils.toRadians(utils.getValue(pState.style, 'rotation') || 0);
|
||||
|
||||
if (alpha !== 0) {
|
||||
const cos = Math.cos(-alpha);
|
||||
|
@ -1748,9 +1693,7 @@ class GraphHandler {
|
|||
}
|
||||
|
||||
// Cloning into locked cells is not allowed
|
||||
clone =
|
||||
clone &&
|
||||
!this.graph.isCellLocked(target || this.graph.getDefaultParent());
|
||||
clone = clone && !this.graph.isCellLocked(target || this.graph.getDefaultParent());
|
||||
|
||||
this.graph.getModel().beginUpdate();
|
||||
try {
|
||||
|
|
|
@ -16,7 +16,7 @@ import CellHighlight from '../selection/CellHighlight';
|
|||
import EventObject from '../event/EventObject';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import utils, { intersectsHotspot, isNumeric } from '../../util/Utils';
|
||||
import graph from '../Graph';
|
||||
import { MaxGraph } from '../Graph';
|
||||
import { ColorValue } from '../../types';
|
||||
import CellState from './datatypes/CellState';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
|
@ -64,7 +64,7 @@ import Cell from './datatypes/Cell';
|
|||
*/
|
||||
class CellMarker extends EventSource {
|
||||
constructor(
|
||||
graph: graph,
|
||||
graph: MaxGraph,
|
||||
validColor: ColorValue = DEFAULT_VALID_COLOR,
|
||||
invalidColor: ColorValue = DEFAULT_INVALID_COLOR,
|
||||
hotspot: number = DEFAULT_HOTSPOT
|
||||
|
@ -83,7 +83,7 @@ class CellMarker extends EventSource {
|
|||
*
|
||||
* Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
graph: graph;
|
||||
graph: MaxGraph;
|
||||
|
||||
/**
|
||||
* Variable: enabled
|
||||
|
@ -362,12 +362,15 @@ class CellMarker extends EventSource {
|
|||
* Uses <getCell>, <getStateToMark> and <intersects> to return the
|
||||
* <mxCellState> for the given <mxMouseEvent>.
|
||||
*/
|
||||
getState(me: InternalMouseEvent): CellState | null {
|
||||
getState(me: InternalMouseEvent) {
|
||||
const view = this.graph.getView();
|
||||
const cell = this.getCell(me);
|
||||
const state = this.getStateToMark(view.getState(cell));
|
||||
|
||||
return state != null && this.intersects(state, me) ? state : null;
|
||||
if (!cell) return null;
|
||||
|
||||
const state = this.getStateToMark(view.getState(cell) as CellState);
|
||||
|
||||
return this.intersects(state, me) ? state : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -386,7 +389,7 @@ class CellMarker extends EventSource {
|
|||
* Returns the <mxCellState> to be marked for the given <mxCellState> under
|
||||
* the mouse. This returns the given state.
|
||||
*/
|
||||
getStateToMark(state: CellState): CellState {
|
||||
getStateToMark(state: CellState) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
|
@ -1413,7 +1413,7 @@ class CellRenderer {
|
|||
state: CellState,
|
||||
node: HTMLElement | SVGElement | null,
|
||||
htmlNode: HTMLElement | SVGElement | null
|
||||
): void {
|
||||
) {
|
||||
const shapes = this.getShapesForState(state);
|
||||
|
||||
for (let i = 0; i < shapes.length; i += 1) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,7 +11,7 @@ import CellOverlay from '../CellOverlay';
|
|||
import { clone } from '../../../util/CloneUtils';
|
||||
import Point from '../../geometry/Point';
|
||||
import CellPath from './CellPath';
|
||||
import CellArray from "./CellArray";
|
||||
import CellArray from './CellArray';
|
||||
import { isNotNullish } from '../../../util/Utils';
|
||||
|
||||
import type { FilterFunction } from '../../../types';
|
||||
|
@ -88,7 +88,7 @@ class Cell {
|
|||
onInit: (() => void) | null = null;
|
||||
|
||||
// used by addCellOverlay() of mxGraph
|
||||
overlays: CellOverlay[] | null = [];
|
||||
overlays: CellOverlay[] = [];
|
||||
|
||||
/**
|
||||
* Holds the Id. Default is null.
|
||||
|
@ -755,9 +755,7 @@ class Cell {
|
|||
* incoming edges should be returned.
|
||||
* @param {Cell} ignoredEdge that represents an edge to be ignored.
|
||||
*/
|
||||
getDirectedEdgeCount(
|
||||
outgoing: boolean,
|
||||
ignoredEdge: Cell | null = null) {
|
||||
getDirectedEdgeCount(outgoing: boolean, ignoredEdge: Cell | null = null) {
|
||||
let count = 0;
|
||||
const edgeCount = this.getEdgeCount();
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ import Dictionary from '../../../util/Dictionary';
|
|||
import ObjectIdentity from '../../../util/ObjectIdentity';
|
||||
|
||||
class CellArray extends Array<Cell> {
|
||||
// @ts-ignore
|
||||
constructor(...items: Cell[] | CellArray) {
|
||||
constructor(...items: Cell[]) {
|
||||
super(...items);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ 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 { CellStateStyles } from 'packages/core/src/types';
|
||||
import RectangleShape from '../../geometry/shape/node/RectangleShape';
|
||||
|
||||
/**
|
||||
* Class: mxCellState
|
||||
|
@ -89,7 +89,7 @@ class CellState extends Rectangle {
|
|||
* Contains an array of key, value pairs that represent the style of the
|
||||
* cell.
|
||||
*/
|
||||
style: CellStateStyles; // TODO: Important - make the style type more strictly typed to allow for typescript checking of individual properties!!!
|
||||
style: CellStateStyles;
|
||||
|
||||
/**
|
||||
* Variable: invalidStyle
|
||||
|
@ -186,14 +186,16 @@ class CellState extends Rectangle {
|
|||
*
|
||||
* Holds the unscaled width of the state.
|
||||
*/
|
||||
unscaledWidth: number | null = null;
|
||||
unscaledWidth = 0;
|
||||
|
||||
/**
|
||||
* Variable: unscaledHeight
|
||||
*
|
||||
* Holds the unscaled height of the state.
|
||||
*/
|
||||
unscaledHeight: number | null = null;
|
||||
unscaledHeight = 0;
|
||||
|
||||
parentHighlight: RectangleShape | null = null;
|
||||
|
||||
/**
|
||||
* Function: getPerimeterBounds
|
||||
|
@ -313,7 +315,7 @@ class CellState extends Rectangle {
|
|||
* terminalState - <mxCellState> that represents the terminal.
|
||||
* source - Boolean that specifies if the source or target state should be set.
|
||||
*/
|
||||
setVisibleTerminalState(terminalState: CellState, source = false) {
|
||||
setVisibleTerminalState(terminalState: CellState | null, source = false) {
|
||||
if (source) {
|
||||
this.visibleSourceState = terminalState;
|
||||
} else {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,68 +1,82 @@
|
|||
import Cell from "../datatypes/Cell";
|
||||
import CellArray from "../datatypes/CellArray";
|
||||
import {
|
||||
findNearestSegment,
|
||||
removeDuplicates,
|
||||
} from "../../../util/Utils";
|
||||
import Geometry from "../../geometry/Geometry";
|
||||
import EventObject from "../../event/EventObject";
|
||||
import InternalEvent from "../../event/InternalEvent";
|
||||
import Dictionary from "../../../util/Dictionary";
|
||||
import Graph from "../../Graph";
|
||||
import Cell from '../datatypes/Cell';
|
||||
import CellArray from '../datatypes/CellArray';
|
||||
import { autoImplement, findNearestSegment, removeDuplicates } from '../../../util/Utils';
|
||||
import Geometry from '../../geometry/Geometry';
|
||||
import EventObject from '../../event/EventObject';
|
||||
import InternalEvent from '../../event/InternalEvent';
|
||||
import Dictionary from '../../../util/Dictionary';
|
||||
|
||||
class GraphEdge {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../../Graph';
|
||||
import type GraphCells from '../GraphCells';
|
||||
import type GraphConnections from '../../connection/GraphConnections';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<Graph, 'batchUpdate' | 'fireEvent' | 'getModel' | 'getView'>;
|
||||
type PartialCells = Pick<
|
||||
GraphCells,
|
||||
| 'cloneCell'
|
||||
| 'cellsMoved'
|
||||
| 'cellsAdded'
|
||||
| 'addCell'
|
||||
| 'isValidAncestor'
|
||||
| 'getChildCells'
|
||||
>;
|
||||
type PartialConnections = Pick<GraphConnections, 'cellConnected'>;
|
||||
type PartialClass = PartialGraph & PartialCells & PartialConnections;
|
||||
|
||||
// @ts-ignore recursive reference error
|
||||
class GraphEdge extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Specifies if edge control points should be reset after the resize of a
|
||||
* connected cell.
|
||||
* @default false
|
||||
*/
|
||||
resetEdgesOnResize: boolean = false;
|
||||
resetEdgesOnResize = false;
|
||||
|
||||
isResetEdgesOnResize = () => this.resetEdgesOnResize;
|
||||
|
||||
/**
|
||||
* Specifies if edge control points should be reset after the move of a
|
||||
* connected cell.
|
||||
* @default false
|
||||
*/
|
||||
resetEdgesOnMove: boolean = false;
|
||||
resetEdgesOnMove = false;
|
||||
|
||||
isResetEdgesOnMove = () => this.resetEdgesOnMove;
|
||||
|
||||
/**
|
||||
* Specifies if edge control points should be reset after the the edge has been
|
||||
* reconnected.
|
||||
* @default true
|
||||
*/
|
||||
resetEdgesOnConnect: boolean = true;
|
||||
resetEdgesOnConnect = true;
|
||||
|
||||
isResetEdgesOnConnect = () => this.resetEdgesOnConnect;
|
||||
|
||||
/**
|
||||
* Specifies if edges are connectable. This overrides the connectable field in edges.
|
||||
* @default false
|
||||
*/
|
||||
connectableEdges: boolean = false;
|
||||
connectableEdges = false;
|
||||
|
||||
/**
|
||||
* Specifies if edges with disconnected terminals are allowed in the graph.
|
||||
* @default true
|
||||
*/
|
||||
allowDanglingEdges: boolean = true;
|
||||
allowDanglingEdges = true;
|
||||
|
||||
/**
|
||||
* Specifies if edges that are cloned should be validated and only inserted
|
||||
* if they are valid.
|
||||
* @default true
|
||||
*/
|
||||
cloneInvalidEdges: boolean = false;
|
||||
cloneInvalidEdges = false;
|
||||
|
||||
/**
|
||||
* Specifies if edges should be disconnected from their terminals when they
|
||||
* are moved.
|
||||
* @default true
|
||||
*/
|
||||
disconnectOnMove: boolean = true;
|
||||
disconnectOnMove = true;
|
||||
|
||||
/**
|
||||
* Specifies the alternate edge style to be used if the main control point
|
||||
|
@ -75,7 +89,7 @@ class GraphEdge {
|
|||
* Specifies the return value for edges in {@link isLabelMovable}.
|
||||
* @default true
|
||||
*/
|
||||
edgeLabelsMovable: boolean = true;
|
||||
edgeLabelsMovable = true;
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Graph Behaviour
|
||||
|
@ -84,14 +98,14 @@ class GraphEdge {
|
|||
/**
|
||||
* Returns {@link edgeLabelsMovable}.
|
||||
*/
|
||||
isEdgeLabelsMovable(): boolean {
|
||||
isEdgeLabelsMovable() {
|
||||
return this.edgeLabelsMovable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link edgeLabelsMovable}.
|
||||
*/
|
||||
setEdgeLabelsMovable(value: boolean): void {
|
||||
setEdgeLabelsMovable(value: boolean) {
|
||||
this.edgeLabelsMovable = value;
|
||||
}
|
||||
|
||||
|
@ -101,14 +115,14 @@ class GraphEdge {
|
|||
*
|
||||
* @param value Boolean indicating if dangling edges are allowed.
|
||||
*/
|
||||
setAllowDanglingEdges(value: boolean): void {
|
||||
setAllowDanglingEdges(value: boolean) {
|
||||
this.allowDanglingEdges = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link allowDanglingEdges} as a boolean.
|
||||
*/
|
||||
isAllowDanglingEdges(): boolean {
|
||||
isAllowDanglingEdges() {
|
||||
return this.allowDanglingEdges;
|
||||
}
|
||||
|
||||
|
@ -117,14 +131,14 @@ class GraphEdge {
|
|||
*
|
||||
* @param value Boolean indicating if edges should be connectable.
|
||||
*/
|
||||
setConnectableEdges(value: boolean): void {
|
||||
setConnectableEdges(value: boolean) {
|
||||
this.connectableEdges = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link connectableEdges} as a boolean.
|
||||
*/
|
||||
isConnectableEdges(): boolean {
|
||||
isConnectableEdges() {
|
||||
return this.connectableEdges;
|
||||
}
|
||||
|
||||
|
@ -135,14 +149,14 @@ class GraphEdge {
|
|||
* @param value Boolean indicating if cloned invalid edges should be
|
||||
* inserted into the graph or ignored.
|
||||
*/
|
||||
setCloneInvalidEdges(value: boolean): void {
|
||||
setCloneInvalidEdges(value: boolean) {
|
||||
this.cloneInvalidEdges = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link cloneInvalidEdges} as a boolean.
|
||||
*/
|
||||
isCloneInvalidEdges(): boolean {
|
||||
isCloneInvalidEdges() {
|
||||
return this.cloneInvalidEdges;
|
||||
}
|
||||
|
||||
|
@ -176,20 +190,20 @@ class GraphEdge {
|
|||
* @param edge {@link mxCell} whose style should be changed.
|
||||
*/
|
||||
// flipEdge(edge: mxCell): mxCell;
|
||||
flipEdge(edge: Cell): Cell {
|
||||
if (this.alternateEdgeStyle != null) {
|
||||
this.graph.batchUpdate(() => {
|
||||
flipEdge(edge: Cell) {
|
||||
if (this.alternateEdgeStyle) {
|
||||
this.batchUpdate(() => {
|
||||
const style = edge.getStyle();
|
||||
|
||||
if (style == null || style.length === 0) {
|
||||
this.graph.model.setStyle(edge, this.alternateEdgeStyle);
|
||||
if (!style || style.length === 0) {
|
||||
this.getModel().setStyle(edge, this.alternateEdgeStyle);
|
||||
} else {
|
||||
this.graph.model.setStyle(edge, null);
|
||||
this.getModel().setStyle(edge, null);
|
||||
}
|
||||
|
||||
// Removes all existing control points
|
||||
this.resetEdge(edge);
|
||||
this.graph.fireEvent(new EventObject(InternalEvent.FLIP_EDGE, 'edge', edge));
|
||||
this.fireEvent(new EventObject(InternalEvent.FLIP_EDGE, 'edge', edge));
|
||||
});
|
||||
}
|
||||
return edge;
|
||||
|
@ -219,35 +233,35 @@ class GraphEdge {
|
|||
edge: Cell,
|
||||
cells: CellArray,
|
||||
newEdge: Cell,
|
||||
dx: number = 0,
|
||||
dy: number = 0,
|
||||
dx = 0,
|
||||
dy = 0,
|
||||
x: number,
|
||||
y: number,
|
||||
parent: Cell | null = null
|
||||
) {
|
||||
parent = parent != null ? parent : edge.getParent();
|
||||
parent = parent ?? edge.getParent();
|
||||
const source = edge.getTerminal(true);
|
||||
|
||||
this.graph.batchUpdate(() => {
|
||||
if (newEdge == null) {
|
||||
newEdge = <Cell>this.cloneCell(edge);
|
||||
this.batchUpdate(() => {
|
||||
if (!newEdge) {
|
||||
newEdge = this.cloneCell(edge);
|
||||
|
||||
// Removes waypoints before/after new cell
|
||||
const state = this.graph.view.getState(edge);
|
||||
let geo = newEdge.getGeometry();
|
||||
const state = this.getView().getState(edge);
|
||||
let geo: Geometry | null = newEdge.getGeometry();
|
||||
|
||||
if (geo != null && geo.points != null && state != null) {
|
||||
const t = this.graph.view.translate;
|
||||
const s = this.graph.view.scale;
|
||||
if (geo && state) {
|
||||
const t = this.getView().translate;
|
||||
const s = this.getView().scale;
|
||||
const idx = findNearestSegment(state, (dx + t.x) * s, (dy + t.y) * s);
|
||||
|
||||
geo.points = geo.points.slice(0, idx);
|
||||
geo = <Geometry>edge.getGeometry();
|
||||
geo = edge.getGeometry();
|
||||
|
||||
if (geo != null && geo.points != null) {
|
||||
geo = <Geometry>geo.clone();
|
||||
if (geo) {
|
||||
geo = geo.clone();
|
||||
geo.points = geo.points.slice(idx);
|
||||
this.graph.model.setGeometry(edge, geo);
|
||||
this.getModel().setGeometry(edge, geo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +269,7 @@ class GraphEdge {
|
|||
this.cellsMoved(cells, dx, dy, false, false);
|
||||
this.cellsAdded(
|
||||
cells,
|
||||
parent,
|
||||
parent as Cell,
|
||||
parent ? parent.getChildCount() : 0,
|
||||
null,
|
||||
null,
|
||||
|
@ -263,18 +277,15 @@ class GraphEdge {
|
|||
);
|
||||
this.cellsAdded(
|
||||
new CellArray(newEdge),
|
||||
parent,
|
||||
parent as Cell,
|
||||
parent ? parent.getChildCount() : 0,
|
||||
source,
|
||||
cells[0],
|
||||
false
|
||||
);
|
||||
this.cellConnected(edge, cells[0], true);
|
||||
this.graph.fireEvent(
|
||||
new EventObject(
|
||||
InternalEvent.SPLIT_EDGE,
|
||||
{ edge, cells, newEdge, dx, dy }
|
||||
)
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.SPLIT_EDGE, { edge, cells, newEdge, dx, dy })
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -294,8 +305,7 @@ class GraphEdge {
|
|||
* @param target {@link mxCell} that defines the target of the edge.
|
||||
* @param style Optional string that defines the cell style.
|
||||
*/
|
||||
// insertEdge(parent: mxCell, id: string | null, value: any, source: mxCell, target: mxCell, style?: string): mxCell;
|
||||
insertEdge(...args: any[]): Cell {
|
||||
insertEdge(...args: any[]) {
|
||||
let parent: Cell;
|
||||
let id: string = '';
|
||||
let value: any; // note me - can be a string or a class instance!!!
|
||||
|
@ -328,7 +338,6 @@ class GraphEdge {
|
|||
* are set when the edge is added to the model.
|
||||
*
|
||||
*/
|
||||
// createEdge(parent: mxCell, id: string | null, value: any, source: mxCell, target: mxCell, style?: string): mxCell;
|
||||
createEdge(
|
||||
parent: Cell | null = null,
|
||||
id: string,
|
||||
|
@ -363,7 +372,7 @@ class GraphEdge {
|
|||
source: Cell | null = null,
|
||||
target: Cell | null = null,
|
||||
index: number | null = null
|
||||
): Cell {
|
||||
) {
|
||||
return this.addCell(edge, parent, index, source, target);
|
||||
}
|
||||
|
||||
|
@ -375,7 +384,7 @@ class GraphEdge {
|
|||
* Returns an array with the given cells and all edges that are connected
|
||||
* to a cell or one of its descendants.
|
||||
*/
|
||||
addAllEdges(cells: CellArray): CellArray {
|
||||
addAllEdges(cells: CellArray) {
|
||||
const allCells = cells.slice();
|
||||
return new CellArray(...removeDuplicates(allCells.concat(this.getAllEdges(cells))));
|
||||
}
|
||||
|
@ -383,19 +392,20 @@ class GraphEdge {
|
|||
/**
|
||||
* Returns all edges connected to the given cells or its descendants.
|
||||
*/
|
||||
getAllEdges(cells: CellArray | null): CellArray {
|
||||
getAllEdges(cells: CellArray | null) {
|
||||
let edges: CellArray = new CellArray();
|
||||
if (cells != null) {
|
||||
|
||||
if (cells) {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
const edgeCount = cells[i].getEdgeCount();
|
||||
|
||||
for (let j = 0; j < edgeCount; j++) {
|
||||
edges.push(<Cell>cells[i].getEdgeAt(j));
|
||||
edges.push(cells[i].getEdgeAt(j));
|
||||
}
|
||||
|
||||
// Recurses
|
||||
const children = cells[i].getChildren();
|
||||
edges = edges.concat(this.getAllEdges(<CellArray>children));
|
||||
edges = edges.concat(this.getAllEdges(children));
|
||||
}
|
||||
}
|
||||
return edges;
|
||||
|
@ -410,8 +420,7 @@ class GraphEdge {
|
|||
* @param parent Optional parent of the opposite end for an edge to be
|
||||
* returned.
|
||||
*/
|
||||
getIncomingEdges(cell: Cell,
|
||||
parent: Cell | null = null): CellArray {
|
||||
getIncomingEdges(cell: Cell, parent: Cell | null = null) {
|
||||
return this.getEdges(cell, parent, true, false, false);
|
||||
}
|
||||
|
||||
|
@ -424,8 +433,7 @@ class GraphEdge {
|
|||
* @param parent Optional parent of the opposite end for an edge to be
|
||||
* returned.
|
||||
*/
|
||||
getOutgoingEdges(cell: Cell,
|
||||
parent: Cell | null = null): CellArray {
|
||||
getOutgoingEdges(cell: Cell, parent: Cell | null = null) {
|
||||
return this.getEdges(cell, parent, false, true, false);
|
||||
}
|
||||
|
||||
|
@ -456,11 +464,11 @@ class GraphEdge {
|
|||
getEdges(
|
||||
cell: Cell,
|
||||
parent: Cell | null = null,
|
||||
incoming: boolean = true,
|
||||
outgoing: boolean = true,
|
||||
includeLoops: boolean = true,
|
||||
recurse: boolean = false
|
||||
): CellArray {
|
||||
incoming = true,
|
||||
outgoing = true,
|
||||
includeLoops = true,
|
||||
recurse = false
|
||||
) {
|
||||
let edges: CellArray = new CellArray();
|
||||
const isCollapsed = cell.isCollapsed();
|
||||
const childCount = cell.getChildCount();
|
||||
|
@ -468,39 +476,33 @@ class GraphEdge {
|
|||
for (let i = 0; i < childCount; i += 1) {
|
||||
const child = cell.getChildAt(i);
|
||||
|
||||
if (isCollapsed || !(<Cell>child).isVisible()) {
|
||||
edges = edges.concat((<Cell>child).getEdges(incoming, outgoing));
|
||||
if (isCollapsed || !child.isVisible()) {
|
||||
edges = edges.concat(child.getEdges(incoming, outgoing));
|
||||
}
|
||||
}
|
||||
|
||||
edges = edges.concat(
|
||||
<CellArray>cell.getEdges(incoming, outgoing)
|
||||
);
|
||||
edges = edges.concat(cell.getEdges(incoming, outgoing));
|
||||
const result = new CellArray();
|
||||
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const state = this.getView().getState(edges[i]);
|
||||
|
||||
const source =
|
||||
state != null
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[i], true);
|
||||
const target =
|
||||
state != null
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[i], false);
|
||||
const source = state
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[i], true);
|
||||
const target = state
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[i], false);
|
||||
|
||||
if (
|
||||
(includeLoops && source == target) ||
|
||||
(source != target &&
|
||||
(includeLoops && source === target) ||
|
||||
(source !== target &&
|
||||
((incoming &&
|
||||
target == cell &&
|
||||
(parent == null ||
|
||||
this.isValidAncestor(<Cell>source, parent, recurse))) ||
|
||||
target === cell &&
|
||||
(!parent || this.isValidAncestor(<Cell>source, parent, recurse))) ||
|
||||
(outgoing &&
|
||||
source == cell &&
|
||||
(parent == null ||
|
||||
this.isValidAncestor(<Cell>target, parent, recurse)))))
|
||||
source === cell &&
|
||||
(!parent || this.isValidAncestor(<Cell>target, parent, recurse)))))
|
||||
) {
|
||||
result.push(edges[i]);
|
||||
}
|
||||
|
@ -517,11 +519,10 @@ class GraphEdge {
|
|||
*
|
||||
* @param parent {@link mxCell} whose child vertices should be returned.
|
||||
*/
|
||||
getChildEdges(parent: Cell): CellArray {
|
||||
getChildEdges(parent: Cell) {
|
||||
return this.getChildCells(parent, false, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the edges between the given source and target. This takes into
|
||||
* account collapsed and invisible cells and returns the connected edges
|
||||
|
@ -531,7 +532,7 @@ class GraphEdge {
|
|||
* target -
|
||||
* directed -
|
||||
*/
|
||||
getEdgesBetween(source: Cell, target: Cell, directed: boolean = false): CellArray {
|
||||
getEdgesBetween(source: Cell, target: Cell, directed = false) {
|
||||
const edges = this.getEdges(source);
|
||||
const result = new CellArray();
|
||||
|
||||
|
@ -540,18 +541,16 @@ class GraphEdge {
|
|||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const state = this.getView().getState(edges[i]);
|
||||
|
||||
const src =
|
||||
state != null
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[i], true);
|
||||
const trg =
|
||||
state != null
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[i], false);
|
||||
const src = state
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[i], true);
|
||||
const trg = state
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[i], false);
|
||||
|
||||
if (
|
||||
(src == source && trg == target) ||
|
||||
(!directed && src == target && trg == source)
|
||||
(src === source && trg === target) ||
|
||||
(!directed && src === target && trg === source)
|
||||
) {
|
||||
result.push(edges[i]);
|
||||
}
|
||||
|
@ -570,45 +569,39 @@ class GraphEdge {
|
|||
* @param cells Array of {@link Cell} for which the connected edges should be
|
||||
* reset.
|
||||
*/
|
||||
resetEdges(cells: CellArray): void {
|
||||
if (cells != null) {
|
||||
// Prepares faster cells lookup
|
||||
const dict = new Dictionary();
|
||||
resetEdges(cells: CellArray) {
|
||||
// Prepares faster cells lookup
|
||||
const dict = new Dictionary();
|
||||
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
dict.put(cells[i], true);
|
||||
}
|
||||
|
||||
this.getModel().beginUpdate();
|
||||
try {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
dict.put(cells[i], true);
|
||||
}
|
||||
const edges = cells[i].getEdges();
|
||||
|
||||
this.getModel().beginUpdate();
|
||||
try {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
const edges = cells[i].getEdges();
|
||||
for (let j = 0; j < edges.length; j++) {
|
||||
const state = this.getView().getState(edges[j]);
|
||||
|
||||
if (edges != null) {
|
||||
for (let j = 0; j < edges.length; j++) {
|
||||
const state = this.getView().getState(edges[j]);
|
||||
const source = state
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[j], true);
|
||||
const target = state
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[j], false);
|
||||
|
||||
const source =
|
||||
state != null
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[j], true);
|
||||
const target =
|
||||
state != null
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[j], false);
|
||||
|
||||
// Checks if one of the terminals is not in the given array
|
||||
if (!dict.get(source) || !dict.get(target)) {
|
||||
this.resetEdge(<Cell>edges[j]);
|
||||
}
|
||||
}
|
||||
// Checks if one of the terminals is not in the given array
|
||||
if (!dict.get(source) || !dict.get(target)) {
|
||||
this.resetEdge(edges[j]);
|
||||
}
|
||||
|
||||
this.resetEdges(cells[i].getChildren());
|
||||
}
|
||||
} finally {
|
||||
this.getModel().endUpdate();
|
||||
|
||||
this.resetEdges(cells[i].getChildren());
|
||||
}
|
||||
} finally {
|
||||
this.getModel().endUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -617,15 +610,16 @@ class GraphEdge {
|
|||
*
|
||||
* @param edge {@link mxCell} whose points should be reset.
|
||||
*/
|
||||
resetEdge(edge: Cell): Cell | null {
|
||||
resetEdge(edge: Cell) {
|
||||
let geo = edge.getGeometry();
|
||||
|
||||
// Resets the control points
|
||||
if (geo != null && geo.points != null && geo.points.length > 0) {
|
||||
geo = <Geometry>geo.clone();
|
||||
if (geo && geo.points.length > 0) {
|
||||
geo = geo.clone();
|
||||
geo.points = [];
|
||||
this.getModel().setGeometry(edge, geo);
|
||||
}
|
||||
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import Cell from "../datatypes/Cell";
|
||||
import Geometry from "../../geometry/Geometry";
|
||||
import CellArray from "../datatypes/CellArray";
|
||||
import Graph from '../../Graph';
|
||||
import Cell from '../datatypes/Cell';
|
||||
import Geometry from '../../geometry/Geometry';
|
||||
import CellArray from '../datatypes/CellArray';
|
||||
import { autoImplement } from 'packages/core/src/util/Utils';
|
||||
|
||||
class GraphVertex {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type GraphCells from '../GraphCells';
|
||||
|
||||
graph: Graph;
|
||||
type PartialCells = Pick<GraphCells, 'addCell' | 'getChildCells'>;
|
||||
type PartialClass = PartialCells;
|
||||
|
||||
class GraphVertex extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Specifies the return value for vertices in {@link isLabelMovable}.
|
||||
* @default false
|
||||
|
@ -95,18 +94,7 @@ class GraphVertex {
|
|||
geometryClass = params.geometryClass;
|
||||
} else {
|
||||
// Otherwise treat as arguments
|
||||
[
|
||||
parent,
|
||||
id,
|
||||
value,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
style,
|
||||
relative,
|
||||
geometryClass,
|
||||
] = args;
|
||||
[parent, id, value, x, y, width, height, style, relative, geometryClass] = args;
|
||||
}
|
||||
|
||||
const vertex = this.createVertex(
|
||||
|
@ -121,7 +109,7 @@ class GraphVertex {
|
|||
relative,
|
||||
geometryClass
|
||||
);
|
||||
return this.graph.cell.addCell(vertex, parent);
|
||||
return this.addCell(vertex, parent);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -160,7 +148,7 @@ class GraphVertex {
|
|||
* @param parent {@link mxCell} whose children should be returned.
|
||||
*/
|
||||
getChildVertices(parent: Cell): CellArray {
|
||||
return this.graph.cell.getChildCells(parent, true, false);
|
||||
return this.getChildCells(parent, true, false);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
|
||||
import utils, { getRotatedPoint, toRadians } from '../../../util/Utils';
|
||||
import { getRotatedPoint, toRadians } from '../../../util/Utils';
|
||||
import Point from '../../geometry/Point';
|
||||
import ImageShape from '../../geometry/shape/node/ImageShape';
|
||||
import Rectangle from '../../geometry/Rectangle';
|
||||
|
@ -21,60 +21,65 @@ import {
|
|||
import InternalEvent from '../../event/InternalEvent';
|
||||
import Shape from '../../geometry/shape/Shape';
|
||||
import InternalMouseEvent from '../../event/InternalMouseEvent';
|
||||
import Image from '../../image/ImageBox';
|
||||
import Graph from '../../Graph';
|
||||
import ImageBox from '../../image/ImageBox';
|
||||
import CellState from '../datatypes/CellState';
|
||||
import CellArray from '../datatypes/CellArray';
|
||||
|
||||
import type { MaxGraph } from '../../Graph';
|
||||
import type { CellHandle, CellStateStyles } from 'packages/core/src/types';
|
||||
|
||||
/**
|
||||
* Implements a single custom handle for vertices.
|
||||
*
|
||||
* @class VertexHandle
|
||||
*/
|
||||
class VertexHandle {
|
||||
class VertexHandle implements CellHandle {
|
||||
dependencies = ['snap', 'cells'];
|
||||
|
||||
constructor(
|
||||
state: CellState,
|
||||
cursor: string | null = 'default',
|
||||
image: Image | null = null,
|
||||
cursor: string = 'default',
|
||||
image: ImageBox | null = null,
|
||||
shape: Shape | null = null
|
||||
) {
|
||||
this.graph = state.view.graph;
|
||||
this.state = state;
|
||||
this.cursor = cursor != null ? cursor : this.cursor;
|
||||
this.image = image != null ? image : this.image;
|
||||
this.cursor = cursor;
|
||||
this.image = image;
|
||||
this.shape = shape;
|
||||
this.init();
|
||||
}
|
||||
|
||||
graph: Graph;
|
||||
graph: MaxGraph;
|
||||
state: CellState;
|
||||
shape: Shape | ImageShape | null;
|
||||
|
||||
/**
|
||||
* Specifies the cursor to be used for this handle. Default is 'default'.
|
||||
*/
|
||||
cursor: string = 'default';
|
||||
cursor = 'default';
|
||||
|
||||
/**
|
||||
* Specifies the <mxImage> to be used to render the handle. Default is null.
|
||||
*/
|
||||
image: Image | null = null;
|
||||
image: ImageBox | null = null;
|
||||
|
||||
/**
|
||||
* Default is false.
|
||||
*/
|
||||
ignoreGrid: boolean = false;
|
||||
ignoreGrid = false;
|
||||
|
||||
/**
|
||||
* Hook for subclassers to return the current position of the handle.
|
||||
*/
|
||||
getPosition(bounds: Rectangle) {}
|
||||
getPosition(bounds: Rectangle | null): Point {
|
||||
return new Point();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks for subclassers to update the style in the <state>.
|
||||
*/
|
||||
setPosition(bounds: Rectangle, pt: Point, me: InternalMouseEvent) {}
|
||||
setPosition(bounds: Rectangle | null, pt: Point, me: InternalMouseEvent) {}
|
||||
|
||||
/**
|
||||
* Hook for subclassers to execute the handle.
|
||||
|
@ -84,8 +89,8 @@ class VertexHandle {
|
|||
/**
|
||||
* Sets the cell style with the given name to the corresponding value in <state>.
|
||||
*/
|
||||
copyStyle(key: string): void {
|
||||
this.graph.setCellStyles(key, this.state.style[key], [this.state.cell]);
|
||||
copyStyle(key: keyof CellStateStyles) {
|
||||
this.graph.setCellStyles(key, this.state.style[key], new CellArray(this.state.cell));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,10 +99,7 @@ class VertexHandle {
|
|||
processEvent(me: InternalMouseEvent): void {
|
||||
const { scale } = this.graph.view;
|
||||
const tr = this.graph.view.translate;
|
||||
let pt = new Point(
|
||||
<number>me.getGraphX() / scale - tr.x,
|
||||
<number>me.getGraphY() / scale - tr.y
|
||||
);
|
||||
let pt = new Point(me.getGraphX() / scale - tr.x, me.getGraphY() / scale - tr.y);
|
||||
|
||||
// Center shape on mouse cursor
|
||||
if (this.shape != null && this.shape.bounds != null) {
|
||||
|
@ -112,7 +114,7 @@ class VertexHandle {
|
|||
this.rotatePoint(
|
||||
this.snapPoint(
|
||||
this.rotatePoint(pt, alpha1),
|
||||
this.ignoreGrid || !this.graph.snap.isGridEnabledEvent(me.getEvent())
|
||||
this.ignoreGrid || !this.graph.isGridEnabledEvent(me.getEvent())
|
||||
),
|
||||
alpha2
|
||||
)
|
||||
|
@ -161,16 +163,16 @@ class VertexHandle {
|
|||
/**
|
||||
* Creates and initializes the shapes required for this handle.
|
||||
*/
|
||||
init(): void {
|
||||
init() {
|
||||
const html = this.isHtmlRequired();
|
||||
|
||||
if (this.image != null) {
|
||||
if (this.image) {
|
||||
this.shape = new ImageShape(
|
||||
new Rectangle(0, 0, this.image.width, this.image.height),
|
||||
this.image.src
|
||||
);
|
||||
this.shape.preserveImageAspect = false;
|
||||
} else if (this.shape == null) {
|
||||
} else if (!this.shape) {
|
||||
this.shape = this.createShape(html);
|
||||
}
|
||||
|
||||
|
@ -180,7 +182,7 @@ class VertexHandle {
|
|||
/**
|
||||
* Creates and returns the shape for this handle.
|
||||
*/
|
||||
createShape(html: any): Shape {
|
||||
createShape(html: boolean): Shape {
|
||||
const bounds = new Rectangle(0, 0, HANDLE_SIZE, HANDLE_SIZE);
|
||||
return new RectangleShape(bounds, HANDLE_FILLCOLOR, HANDLE_STROKECOLOR);
|
||||
}
|
||||
|
@ -188,8 +190,8 @@ class VertexHandle {
|
|||
/**
|
||||
* Initializes <shape> and sets its cursor.
|
||||
*/
|
||||
initShape(html: any): void {
|
||||
const shape = <Shape>this.shape;
|
||||
initShape(html: boolean) {
|
||||
const shape = this.shape as Shape; // `this.shape` cannot be null.
|
||||
|
||||
if (html && shape.isHtmlAllowed()) {
|
||||
shape.dialect = DIALECT_STRICTHTML;
|
||||
|
@ -198,7 +200,7 @@ class VertexHandle {
|
|||
shape.dialect =
|
||||
this.graph.dialect !== DIALECT_SVG ? DIALECT_MIXEDHTML : DIALECT_SVG;
|
||||
|
||||
if (this.cursor != null) {
|
||||
if (this.cursor) {
|
||||
shape.init(this.graph.getView().getOverlayPane());
|
||||
}
|
||||
}
|
||||
|
@ -210,11 +212,11 @@ class VertexHandle {
|
|||
/**
|
||||
* Renders the shape for this handle.
|
||||
*/
|
||||
redraw(): void {
|
||||
if (this.shape != null && this.state.shape != null) {
|
||||
redraw() {
|
||||
if (this.shape && this.state.shape) {
|
||||
let pt = this.getPosition(this.state.getPaintBounds());
|
||||
|
||||
if (pt != null) {
|
||||
if (pt) {
|
||||
const alpha = toRadians(this.getTotalRotation());
|
||||
pt = this.rotatePoint(this.flipPoint(pt), alpha);
|
||||
|
||||
|
@ -235,16 +237,14 @@ class VertexHandle {
|
|||
* Returns true if this handle should be rendered in HTML. This returns true if
|
||||
* the text node is in the graph container.
|
||||
*/
|
||||
isHtmlRequired(): boolean {
|
||||
return (
|
||||
this.state.text != null && this.state.text.node.parentNode === this.graph.container
|
||||
);
|
||||
isHtmlRequired() {
|
||||
return !!this.state.text && this.state.text.node.parentNode === this.graph.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the point by the given angle.
|
||||
*/
|
||||
rotatePoint(pt: Point, alpha: number): Point {
|
||||
rotatePoint(pt: Point, alpha: number) {
|
||||
const bounds = <Rectangle>this.state.getCellBounds();
|
||||
const cx = new Point(bounds.getCenterX(), bounds.getCenterY());
|
||||
const cos = Math.cos(alpha);
|
||||
|
@ -256,8 +256,8 @@ class VertexHandle {
|
|||
/**
|
||||
* Flips the given point vertically and/or horizontally.
|
||||
*/
|
||||
flipPoint(pt: Point): Point {
|
||||
if (this.state.shape != null) {
|
||||
flipPoint(pt: Point) {
|
||||
if (this.state.shape) {
|
||||
const bounds = <Rectangle>this.state.getCellBounds();
|
||||
|
||||
if (this.state.shape.flipH) {
|
||||
|
@ -275,10 +275,10 @@ class VertexHandle {
|
|||
* Snaps the given point to the grid if ignore is false. This modifies
|
||||
* the given point in-place and also returns it.
|
||||
*/
|
||||
snapPoint(pt: Point, ignore: boolean): Point {
|
||||
snapPoint(pt: Point, ignore: boolean) {
|
||||
if (!ignore) {
|
||||
pt.x = this.graph.snap.snap(pt.x);
|
||||
pt.y = this.graph.snap.snap(pt.y);
|
||||
pt.x = this.graph.snap(pt.x);
|
||||
pt.y = this.graph.snap(pt.y);
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
@ -286,8 +286,8 @@ class VertexHandle {
|
|||
/**
|
||||
* Shows or hides this handle.
|
||||
*/
|
||||
setVisible(visible: boolean): void {
|
||||
if (this.shape != null && this.shape.node != null) {
|
||||
setVisible(visible: boolean) {
|
||||
if (this.shape && this.shape.node) {
|
||||
this.shape.node.style.display = visible ? '' : 'none';
|
||||
}
|
||||
}
|
||||
|
@ -295,9 +295,9 @@ class VertexHandle {
|
|||
/**
|
||||
* Resets the state of this handle by setting its visibility to true.
|
||||
*/
|
||||
reset(): void {
|
||||
reset() {
|
||||
this.setVisible(true);
|
||||
this.state.style = this.graph.cell.getCellStyle(this.state.cell);
|
||||
this.state.style = this.graph.getCellStyle(this.state.cell);
|
||||
this.positionChanged();
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ class VertexHandle {
|
|||
* Destroys this handle.
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.shape != null) {
|
||||
if (this.shape) {
|
||||
this.shape.destroy();
|
||||
this.shape = null;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import Point from "../geometry/Point";
|
||||
import CellState from "../cell/datatypes/CellState";
|
||||
import InternalMouseEvent from "../event/InternalMouseEvent";
|
||||
import ConnectionConstraint from "./ConnectionConstraint";
|
||||
import Rectangle from "../geometry/Rectangle";
|
||||
import {DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_WEST} from "../../util/Constants";
|
||||
import utils, {getRotatedPoint, getValue, toRadians} from "../../util/Utils";
|
||||
import Cell from "../cell/datatypes/Cell";
|
||||
import CellArray from "../cell/datatypes/CellArray";
|
||||
import EventObject from "../event/EventObject";
|
||||
import InternalEvent from "../event/InternalEvent";
|
||||
import Dictionary from "../../util/Dictionary";
|
||||
import Geometry from "../geometry/Geometry";
|
||||
import Graph from "../Graph";
|
||||
import ConnectionHandler from "./ConnectionHandler";
|
||||
import Point from '../geometry/Point';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import ConnectionConstraint from './ConnectionConstraint';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
import { DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_WEST } from '../../util/Constants';
|
||||
import utils, { getRotatedPoint, getValue, toRadians } from '../../util/Utils';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
import EventObject from '../event/EventObject';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import Dictionary from '../../util/Dictionary';
|
||||
import Geometry from '../geometry/Geometry';
|
||||
import Graph from '../Graph';
|
||||
import ConnectionHandler from './ConnectionHandler';
|
||||
|
||||
class GraphConnections {
|
||||
constructor(graph: Graph) {
|
||||
|
@ -34,9 +34,7 @@ class GraphConnections {
|
|||
me: InternalMouseEvent
|
||||
): ConnectionConstraint | null {
|
||||
if (terminalState.shape != null) {
|
||||
const bounds = <Rectangle>(
|
||||
this.graph.view.getPerimeterBounds(terminalState)
|
||||
);
|
||||
const bounds = <Rectangle>this.graph.view.getPerimeterBounds(terminalState);
|
||||
const direction = terminalState.style.direction;
|
||||
|
||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||
|
@ -68,16 +66,9 @@ class GraphConnections {
|
|||
let flipV = terminalState.style.flipV;
|
||||
|
||||
// Legacy support for stencilFlipH/V
|
||||
if (
|
||||
terminalState.shape != null &&
|
||||
terminalState.shape.stencil != null
|
||||
) {
|
||||
flipH =
|
||||
getValue(terminalState.style, 'stencilFlipH', 0) == 1 ||
|
||||
flipH;
|
||||
flipV =
|
||||
getValue(terminalState.style, 'stencilFlipV', 0) == 1 ||
|
||||
flipV;
|
||||
if (terminalState.shape != null && terminalState.shape.stencil != null) {
|
||||
flipH = getValue(terminalState.style, 'stencilFlipH', 0) == 1 || flipH;
|
||||
flipV = getValue(terminalState.style, 'stencilFlipV', 0) == 1 || flipV;
|
||||
}
|
||||
|
||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||
|
@ -128,11 +119,7 @@ class GraphConnections {
|
|||
terminal: CellState,
|
||||
source: boolean
|
||||
): ConnectionConstraint[] | null {
|
||||
if (
|
||||
terminal != null &&
|
||||
terminal.shape != null &&
|
||||
terminal.shape.stencil != null
|
||||
) {
|
||||
if (terminal != null && terminal.shape != null && terminal.shape.stencil != null) {
|
||||
return terminal.shape.stencil.constraints;
|
||||
}
|
||||
return null;
|
||||
|
@ -169,21 +156,13 @@ class GraphConnections {
|
|||
let dy = 0;
|
||||
|
||||
if (point != null) {
|
||||
perimeter = getValue(
|
||||
edge.style,
|
||||
source ? 'exitPerimeter' : 'entryPerimeter',
|
||||
true
|
||||
);
|
||||
perimeter = getValue(edge.style, source ? 'exitPerimeter' : 'entryPerimeter', true);
|
||||
|
||||
// Add entry/exit offset
|
||||
// @ts-ignore
|
||||
dx = parseFloat(
|
||||
<string>edge.style[source ? 'exitDx' : 'entryDx']
|
||||
);
|
||||
dx = parseFloat(<string>edge.style[source ? 'exitDx' : 'entryDx']);
|
||||
// @ts-ignore
|
||||
dy = parseFloat(
|
||||
<string>edge.style[source ? 'exitDy' : 'entryDy']
|
||||
);
|
||||
dy = parseFloat(<string>edge.style[source ? 'exitDy' : 'entryDy']);
|
||||
|
||||
dx = Number.isFinite(dx) ? dx : 0;
|
||||
dy = Number.isFinite(dy) ? dy : 0;
|
||||
|
@ -288,10 +267,7 @@ class GraphConnections {
|
|||
let r1 = 0;
|
||||
|
||||
// Bounds need to be rotated by 90 degrees for further computation
|
||||
if (
|
||||
direction != null &&
|
||||
getValue(vertex.style, 'anchorPointDirection', 1) == 1
|
||||
) {
|
||||
if (direction != null && getValue(vertex.style, 'anchorPointDirection', 1) == 1) {
|
||||
if (direction === DIRECTION_NORTH) {
|
||||
r1 += 270;
|
||||
} else if (direction === DIRECTION_WEST) {
|
||||
|
@ -308,12 +284,8 @@ class GraphConnections {
|
|||
|
||||
const { scale } = this.view;
|
||||
point = new point(
|
||||
bounds.x +
|
||||
constraint.point.x * bounds.width +
|
||||
<number>constraint.dx * scale,
|
||||
bounds.y +
|
||||
constraint.point.y * bounds.height +
|
||||
<number>constraint.dy * scale
|
||||
bounds.x + constraint.point.x * bounds.width + <number>constraint.dx * scale,
|
||||
bounds.y + constraint.point.y * bounds.height + <number>constraint.dy * scale
|
||||
);
|
||||
|
||||
// Rotation for direction before projection on perimeter
|
||||
|
@ -346,10 +318,8 @@ class GraphConnections {
|
|||
|
||||
// Legacy support for stencilFlipH/V
|
||||
if (vertex.shape != null && vertex.shape.stencil != null) {
|
||||
flipH =
|
||||
utils.getValue(vertex.style, 'stencilFlipH', 0) == 1 || flipH;
|
||||
flipV =
|
||||
utils.getValue(vertex.style, 'stencilFlipV', 0) == 1 || flipV;
|
||||
flipH = utils.getValue(vertex.style, 'stencilFlipH', 0) == 1 || flipH;
|
||||
flipV = utils.getValue(vertex.style, 'stencilFlipV', 0) == 1 || flipV;
|
||||
}
|
||||
|
||||
if (direction === DIRECTION_NORTH || direction === DIRECTION_SOUTH) {
|
||||
|
@ -515,9 +485,7 @@ class GraphConnections {
|
|||
|
||||
if (geo != null) {
|
||||
const state = this.graph.view.getState(cell);
|
||||
const pstate = <CellState>(
|
||||
this.graph.view.getState(cell.getParent())
|
||||
);
|
||||
const pstate = <CellState>this.graph.view.getState(cell.getParent());
|
||||
|
||||
if (state != null && pstate != null) {
|
||||
geo = geo.clone();
|
||||
|
@ -549,10 +517,7 @@ class GraphConnections {
|
|||
|
||||
let trg = cell.getTerminal(false);
|
||||
|
||||
if (
|
||||
trg != null &&
|
||||
this.isCellDisconnectable(cell, trg, false)
|
||||
) {
|
||||
if (trg != null && this.isCellDisconnectable(cell, trg, false)) {
|
||||
while (trg != null && !dict.get(trg)) {
|
||||
trg = trg.getParent();
|
||||
}
|
||||
|
@ -588,8 +553,7 @@ class GraphConnections {
|
|||
* @param parent Optional parent of the opposite end for a connection to be
|
||||
* returned.
|
||||
*/
|
||||
getConnections(cell: Cell,
|
||||
parent: Cell | null = null): CellArray {
|
||||
getConnections(cell: Cell, parent: Cell | null = null): CellArray {
|
||||
return this.getEdges(cell, parent, true, true, false);
|
||||
}
|
||||
|
||||
|
@ -698,12 +662,10 @@ class GraphConnections {
|
|||
*
|
||||
* @param cell {@link mxCell} that represents a possible source or null.
|
||||
*/
|
||||
isValidSource(cell: Cell): boolean {
|
||||
isValidSource(cell: Cell | null): boolean {
|
||||
return (
|
||||
(cell == null && this.allowDanglingEdges) ||
|
||||
(cell != null &&
|
||||
(!cell.isEdge() || this.connectableEdges) &&
|
||||
cell.isConnectable())
|
||||
(cell != null && (!cell.isEdge() || this.connectableEdges) && cell.isConnectable())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -713,7 +675,7 @@ class GraphConnections {
|
|||
*
|
||||
* @param cell {@link mxCell} that represents a possible target or null.
|
||||
*/
|
||||
isValidTarget(cell: Cell): boolean {
|
||||
isValidTarget(cell: Cell | null): boolean {
|
||||
return this.isValidSource(cell);
|
||||
}
|
||||
|
||||
|
@ -727,7 +689,7 @@ class GraphConnections {
|
|||
* @param source {@link mxCell} that represents the source cell.
|
||||
* @param target {@link mxCell} that represents the target cell.
|
||||
*/
|
||||
isValidConnection(source: Cell, target: Cell): boolean {
|
||||
isValidConnection(source: Cell | null, target: Cell | null): boolean {
|
||||
return this.isValidSource(source) && this.isValidTarget(target);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,33 @@
|
|||
import Cell from "../cell/datatypes/Cell";
|
||||
import {isMultiTouchEvent} from "../../util/EventUtils";
|
||||
import EventObject from "../event/EventObject";
|
||||
import InternalEvent from "../event/InternalEvent";
|
||||
import CellEditor from "./CellEditor";
|
||||
import InternalMouseEvent from "../event/InternalMouseEvent";
|
||||
import Graph from "../Graph";
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import { isMultiTouchEvent } from '../../util/EventUtils';
|
||||
import EventObject from '../event/EventObject';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphEditing {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type GraphSelection from '../selection/GraphSelection';
|
||||
import type GraphEvents from '../event/GraphEvents';
|
||||
import type Graph from '../Graph';
|
||||
import type GraphCells from '../cell/GraphCells';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
'getCellEditor' | 'convertValueToString' | 'batchUpdate' | 'getModel'
|
||||
>;
|
||||
type PartialSelection = Pick<GraphSelection, 'getSelectionCell'>;
|
||||
type PartialEvents = Pick<GraphEvents, 'fireEvent'>;
|
||||
type PartialCells = Pick<
|
||||
GraphCells,
|
||||
'isAutoSizeCell' | 'cellSizeUpdated' | 'getCurrentCellStyle' | 'isCellLocked'
|
||||
>;
|
||||
type PartialClass = PartialGraph & PartialSelection & PartialEvents & PartialCells;
|
||||
|
||||
class GraphEditing extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Specifies the return value for {@link isCellEditable}.
|
||||
* @default true
|
||||
*/
|
||||
cellsEditable: boolean = true;
|
||||
cellsEditable = true;
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Cell in-place editing
|
||||
|
@ -29,7 +39,7 @@ class GraphEditing {
|
|||
*
|
||||
* @param evt Optional mouse event that triggered the editing.
|
||||
*/
|
||||
startEditing(evt: MouseEvent): void {
|
||||
startEditing(evt: MouseEvent) {
|
||||
this.startEditingAtCell(null, evt);
|
||||
}
|
||||
|
||||
|
@ -41,21 +51,20 @@ class GraphEditing {
|
|||
* @param cell {@link mxCell} to start the in-place editor for.
|
||||
* @param evt Optional mouse event that triggered the editing.
|
||||
*/
|
||||
startEditingAtCell(cell: Cell | null = null, evt: MouseEvent): void {
|
||||
if (evt == null || !isMultiTouchEvent(evt)) {
|
||||
if (cell == null) {
|
||||
cell = this.graph.selection.getSelectionCell();
|
||||
if (cell != null && !this.isCellEditable(cell)) {
|
||||
startEditingAtCell(cell: Cell | null = null, evt: MouseEvent) {
|
||||
if (!evt || !isMultiTouchEvent(evt)) {
|
||||
if (!cell) {
|
||||
cell = this.getSelectionCell();
|
||||
|
||||
if (cell && !this.isCellEditable(cell)) {
|
||||
cell = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell != null) {
|
||||
this.graph.event.fireEvent(
|
||||
} else {
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.START_EDITING, 'cell', cell, 'event', evt)
|
||||
);
|
||||
(<CellEditor>this.graph.editing.cellEditor).startEditing(cell, evt);
|
||||
this.graph.event.fireEvent(
|
||||
this.getCellEditor().startEditing(cell, evt);
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.EDITING_STARTED, 'cell', cell, 'event', evt)
|
||||
);
|
||||
}
|
||||
|
@ -71,10 +80,7 @@ class GraphEditing {
|
|||
* @param cell {@link mxCell} for which the initial editing value should be returned.
|
||||
* @param evt Optional mouse event that triggered the editor.
|
||||
*/
|
||||
getEditingValue(
|
||||
cell: Cell,
|
||||
evt: EventObject | InternalMouseEvent
|
||||
): string | null {
|
||||
getEditingValue(cell: Cell, evt: EventObject) {
|
||||
return this.convertValueToString(cell);
|
||||
}
|
||||
|
||||
|
@ -84,11 +90,9 @@ class GraphEditing {
|
|||
* @param cancel Boolean that specifies if the current editing value
|
||||
* should be stored.
|
||||
*/
|
||||
stopEditing(cancel: boolean = false): void {
|
||||
(<CellEditor>this.graph.editing.cellEditor).stopEditing(cancel);
|
||||
this.graph.event.fireEvent(
|
||||
new EventObject(InternalEvent.EDITING_STOPPED, 'cancel', cancel)
|
||||
);
|
||||
stopEditing(cancel: boolean = false) {
|
||||
this.getCellEditor().stopEditing(cancel);
|
||||
this.fireEvent(new EventObject(InternalEvent.EDITING_STOPPED, 'cancel', cancel));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,24 +104,18 @@ class GraphEditing {
|
|||
* @param value New label to be assigned.
|
||||
* @param evt Optional event that triggered the change.
|
||||
*/
|
||||
// labelChanged(cell: mxCell, value: any, evt?: MouseEvent): mxCell;
|
||||
labelChanged(
|
||||
cell: Cell,
|
||||
value: any,
|
||||
evt: InternalMouseEvent | EventObject
|
||||
): Cell {
|
||||
this.graph.batchUpdate(() => {
|
||||
labelChanged(cell: Cell, value: any, evt: InternalMouseEvent | EventObject) {
|
||||
this.batchUpdate(() => {
|
||||
const old = cell.value;
|
||||
this.cellLabelChanged(cell, value, this.graph.cell.isAutoSizeCell(cell));
|
||||
this.graph.event.fireEvent(new EventObject(
|
||||
InternalEvent.LABEL_CHANGED,
|
||||
{
|
||||
this.cellLabelChanged(cell, value, this.isAutoSizeCell(cell));
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.LABEL_CHANGED, {
|
||||
cell: cell,
|
||||
value: value,
|
||||
old: old,
|
||||
event: evt,
|
||||
}
|
||||
));
|
||||
})
|
||||
);
|
||||
});
|
||||
return cell;
|
||||
}
|
||||
|
@ -149,12 +147,10 @@ class GraphEditing {
|
|||
* @param value New label to be assigned.
|
||||
* @param autoSize Boolean that specifies if {@link cellSizeUpdated} should be called.
|
||||
*/
|
||||
cellLabelChanged(cell: Cell,
|
||||
value: any,
|
||||
autoSize: boolean = false): void {
|
||||
cellLabelChanged(cell: Cell, value: any, autoSize: boolean = false) {
|
||||
this.batchUpdate(() => {
|
||||
this.getModel().setValue(cell, value);
|
||||
|
||||
this.graph.batchUpdate(() => {
|
||||
this.graph.model.setValue(cell, value);
|
||||
if (autoSize) {
|
||||
this.cellSizeUpdated(cell, false);
|
||||
}
|
||||
|
@ -172,12 +168,9 @@ class GraphEditing {
|
|||
*
|
||||
* @param cell {@link mxCell} that should be checked.
|
||||
*/
|
||||
isEditing(cell: Cell | null = null): boolean {
|
||||
if (this.cellEditor != null) {
|
||||
const editingCell = this.cellEditor.getEditingCell();
|
||||
return cell == null ? editingCell != null : cell === editingCell;
|
||||
}
|
||||
return false;
|
||||
isEditing(cell: Cell | null = null) {
|
||||
const editingCell = this.getCellEditor().getEditingCell();
|
||||
return !cell ? !!editingCell : cell === editingCell;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -187,20 +180,16 @@ class GraphEditing {
|
|||
*
|
||||
* @param cell {@link mxCell} whose editable state should be returned.
|
||||
*/
|
||||
isCellEditable(cell: Cell): boolean {
|
||||
const style = this.graph.cell.getCurrentCellStyle(cell);
|
||||
isCellEditable(cell: Cell) {
|
||||
const style = this.getCurrentCellStyle(cell);
|
||||
|
||||
return (
|
||||
this.isCellsEditable() &&
|
||||
!this.graph.cell.isCellLocked(cell) &&
|
||||
style.editable != 0
|
||||
);
|
||||
return this.isCellsEditable() && !this.isCellLocked(cell) && style.editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link cellsEditable}.
|
||||
*/
|
||||
isCellsEditable(): boolean {
|
||||
isCellsEditable() {
|
||||
return this.cellsEditable;
|
||||
}
|
||||
|
||||
|
@ -211,7 +200,7 @@ class GraphEditing {
|
|||
* @param value Boolean indicating if the graph should allow in-place
|
||||
* editing.
|
||||
*/
|
||||
setCellsEditable(value: boolean): void {
|
||||
setCellsEditable(value: boolean) {
|
||||
this.cellsEditable = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ class EventSource {
|
|||
*
|
||||
* Sets <eventSource>.
|
||||
*/
|
||||
setEventSource(value: EventSource) {
|
||||
setEventSource(value: EventSource | null) {
|
||||
this.eventSource = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +1,77 @@
|
|||
import InternalMouseEvent from "./InternalMouseEvent";
|
||||
import EventObject from "./EventObject";
|
||||
import InternalEvent from "./InternalEvent";
|
||||
import InternalMouseEvent from './InternalMouseEvent';
|
||||
import EventObject from './EventObject';
|
||||
import InternalEvent from './InternalEvent';
|
||||
import {
|
||||
getClientX,
|
||||
getClientY,
|
||||
isAltDown,
|
||||
isConsumed, isControlDown, isLeftMouseButton, isMetaDown,
|
||||
isMouseEvent, isMultiTouchEvent,
|
||||
isConsumed,
|
||||
isControlDown,
|
||||
isLeftMouseButton,
|
||||
isMetaDown,
|
||||
isMouseEvent,
|
||||
isMultiTouchEvent,
|
||||
isPenEvent,
|
||||
isPopupTrigger, isShiftDown, isTouchEvent
|
||||
} from "../../util/EventUtils";
|
||||
import CellState from "../cell/datatypes/CellState";
|
||||
import Cell from "../cell/datatypes/Cell";
|
||||
import PanningHandler from "../panning/PanningHandler";
|
||||
import ConnectionHandler from "../connection/ConnectionHandler";
|
||||
import Point from "../geometry/Point";
|
||||
import {convertPoint, getValue} from "../../util/Utils";
|
||||
import {NONE, SHAPE_SWIMLANE} from "../../util/Constants";
|
||||
import mxClient from "../../mxClient";
|
||||
import EventSource from "./EventSource";
|
||||
import CellEditor from "../editing/CellEditor";
|
||||
import Graph from "../Graph";
|
||||
isPopupTrigger,
|
||||
isShiftDown,
|
||||
isTouchEvent,
|
||||
} from '../../util/EventUtils';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import PanningHandler from '../panning/PanningHandler';
|
||||
import ConnectionHandler from '../connection/ConnectionHandler';
|
||||
import Point from '../geometry/Point';
|
||||
import { convertPoint, getValue, autoImplement } from '../../util/Utils';
|
||||
import { NONE, SHAPE_SWIMLANE } from '../../util/Constants';
|
||||
import mxClient from '../../mxClient';
|
||||
import EventSource from './EventSource';
|
||||
import CellEditor from '../editing/CellEditor';
|
||||
|
||||
class GraphEvents {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
import type Graph from '../Graph';
|
||||
import type GraphCells from '../cell/GraphCells';
|
||||
import type GraphSelection from '../selection/GraphSelection';
|
||||
import GraphEditing from '../editing/GraphEditing';
|
||||
import GraphSnap from '../snap/GraphSnap';
|
||||
|
||||
// Initializes the variable in case the prototype has been
|
||||
// modified to hold some listeners (which is possible because
|
||||
// the createHandlers call is executed regardless of the
|
||||
// arguments passed into the ctor).
|
||||
this.mouseListeners = null;
|
||||
}
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
| 'fireEvent'
|
||||
| 'isEnabled'
|
||||
| 'getView'
|
||||
| 'getGraphBounds'
|
||||
| 'getContainer'
|
||||
| 'paintBackground'
|
||||
>;
|
||||
type PartialCells = Pick<GraphCells, 'getCellAt' | 'getCursorForCell'>;
|
||||
type PartialSelection = Pick<
|
||||
GraphSelection,
|
||||
'isCellSelected' | 'selectCellForEvent' | 'clearSelection'
|
||||
>;
|
||||
type PartialEditing = Pick<
|
||||
GraphEditing,
|
||||
'isCellEditable' | 'isEditing' | 'startEditingAtCell' | 'stopEditing'
|
||||
>;
|
||||
type PartialSnap = Pick<GraphSnap, 'getGridSize' | 'snap'>;
|
||||
type PartialClass = PartialGraph &
|
||||
PartialCells &
|
||||
PartialSelection &
|
||||
PartialEditing &
|
||||
PartialSnap &
|
||||
EventSource;
|
||||
|
||||
graph: Graph;
|
||||
type MouseListener = {
|
||||
mouseDown: Function;
|
||||
mouseMove: Function;
|
||||
mouseUp: Function;
|
||||
};
|
||||
|
||||
// @ts-ignore recursive reference error
|
||||
class GraphEvents extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Holds the mouse event listeners. See {@link fireMouseEvent}.
|
||||
*/
|
||||
mouseListeners: any[] | null = null;
|
||||
|
||||
mouseListeners: MouseListener[] = [];
|
||||
|
||||
// TODO: Document me!
|
||||
lastTouchEvent: InternalMouseEvent | null = null;
|
||||
doubleClickCounter: number = 0;
|
||||
|
@ -83,13 +115,14 @@ class GraphEvents {
|
|||
*/
|
||||
isMouseDown: boolean = false;
|
||||
|
||||
|
||||
/**
|
||||
* Specifies if native double click events should be detected.
|
||||
* @default true
|
||||
*/
|
||||
nativeDblClickEnabled: boolean = true;
|
||||
|
||||
isNativeDblClickEnabled = () => this.nativeDblClickEnabled;
|
||||
|
||||
/**
|
||||
* Specifies if double taps on touch-based devices should be handled as a
|
||||
* double click.
|
||||
|
@ -166,6 +199,8 @@ class GraphEvents {
|
|||
*/
|
||||
tolerance: number = 4;
|
||||
|
||||
getClickTolerance = () => this.tolerance;
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Event processing
|
||||
*****************************************************************************/
|
||||
|
@ -176,7 +211,7 @@ class GraphEvents {
|
|||
* @param evt Mouseevent that represents the keystroke.
|
||||
*/
|
||||
escape(evt: InternalMouseEvent): void {
|
||||
this.graph.fireEvent(new EventObject(InternalEvent.ESCAPE, 'event', evt));
|
||||
this.fireEvent(new EventObject(InternalEvent.ESCAPE, 'event', evt));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,7 +240,7 @@ class GraphEvents {
|
|||
*
|
||||
* @param me {@link mxMouseEvent} that represents the single click.
|
||||
*/
|
||||
click(me: InternalMouseEvent): boolean {
|
||||
click(me: InternalMouseEvent) {
|
||||
const evt = me.getEvent();
|
||||
let cell = me.getCell();
|
||||
const mxe = new EventObject(InternalEvent.CLICK, 'event', evt, 'cell', cell);
|
||||
|
@ -214,38 +249,38 @@ class GraphEvents {
|
|||
mxe.consume();
|
||||
}
|
||||
|
||||
this.graph.fireEvent(mxe);
|
||||
this.fireEvent(mxe);
|
||||
|
||||
if (this.graph.isEnabled() && !isConsumed(evt) && !mxe.isConsumed()) {
|
||||
if (cell != null) {
|
||||
if (this.isEnabled() && !isConsumed(evt) && !mxe.isConsumed()) {
|
||||
if (cell) {
|
||||
if (this.isTransparentClickEvent(evt)) {
|
||||
let active = false;
|
||||
|
||||
const tmp = this.graph.cell.getCellAt(
|
||||
const tmp = this.getCellAt(
|
||||
me.graphX,
|
||||
me.graphY,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
(state: CellState): boolean => {
|
||||
const selected = this.graph.cell.isCellSelected(<Cell>state.cell);
|
||||
(state: CellState) => {
|
||||
const selected = this.isCellSelected(state.cell);
|
||||
active = active || selected;
|
||||
|
||||
return (
|
||||
!active ||
|
||||
selected ||
|
||||
(state.cell !== cell &&
|
||||
state.cell.isAncestor(cell))
|
||||
(state.cell !== cell && state.cell.isAncestor(cell))
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (tmp != null) {
|
||||
if (tmp) {
|
||||
cell = tmp;
|
||||
}
|
||||
}
|
||||
} else if (this.graph.swimlane.isSwimlaneSelectionEnabled()) {
|
||||
cell = this.graph.swimlane.getSwimlaneAt(me.getGraphX(), me.getGraphY());
|
||||
/* comment out swimlane for now... perhaps make it a plugin?
|
||||
} else if (this.swimlane.isSwimlaneSelectionEnabled()) {
|
||||
cell = this.swimlane.getSwimlaneAt(me.getGraphX(), me.getGraphY());
|
||||
|
||||
if (cell != null && (!this.isToggleEvent(evt) || !isAltDown(evt))) {
|
||||
let temp = cell;
|
||||
|
@ -253,9 +288,9 @@ class GraphEvents {
|
|||
|
||||
while (temp != null) {
|
||||
temp = temp.getParent();
|
||||
const state = this.graph.view.getState(temp);
|
||||
const state = this.getView().getState(temp);
|
||||
|
||||
if (this.graph.swimlane.isSwimlane(temp) && state != null) {
|
||||
if (this.swimlane.isSwimlane(temp) && state != null) {
|
||||
swimlanes.push(temp);
|
||||
}
|
||||
}
|
||||
|
@ -267,18 +302,18 @@ class GraphEvents {
|
|||
swimlanes.push(cell);
|
||||
|
||||
for (let i = 0; i < swimlanes.length - 1; i += 1) {
|
||||
if (this.graph.cell.isCellSelected(swimlanes[i])) {
|
||||
if (this.isCellSelected(swimlanes[i])) {
|
||||
cell = swimlanes[this.isToggleEvent(evt) ? i : i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (cell != null) {
|
||||
this.graph.selection.selectCellForEvent(cell, evt);
|
||||
if (cell) {
|
||||
this.selectCellForEvent(cell, evt);
|
||||
} else if (!this.isToggleEvent(evt)) {
|
||||
this.graph.selection.clearSelection();
|
||||
this.clearSelection();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -296,7 +331,7 @@ class GraphEvents {
|
|||
* graph.dblClick = function(evt, cell)
|
||||
* {
|
||||
* var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell', cell);
|
||||
* this.graph.fireEvent(mxe);
|
||||
* this.fireEvent(mxe);
|
||||
*
|
||||
* if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed())
|
||||
* {
|
||||
|
@ -320,21 +355,20 @@ class GraphEvents {
|
|||
* @param evt Mouseevent that represents the doubleclick.
|
||||
* @param cell Optional {@link Cell} under the mousepointer.
|
||||
*/
|
||||
dblClick(evt: MouseEvent,
|
||||
cell?: Cell): void {
|
||||
const mxe = new EventObject(InternalEvent.DOUBLE_CLICK, {event: evt, cell: cell});
|
||||
this.graph.fireEvent(mxe);
|
||||
dblClick(evt: MouseEvent, cell?: Cell): void {
|
||||
const mxe = new EventObject(InternalEvent.DOUBLE_CLICK, { event: evt, cell: cell });
|
||||
this.fireEvent(mxe);
|
||||
|
||||
// Handles the event if it has not been consumed
|
||||
if (
|
||||
this.graph.isEnabled() &&
|
||||
this.isEnabled() &&
|
||||
!isConsumed(evt) &&
|
||||
!mxe.isConsumed() &&
|
||||
cell != null &&
|
||||
this.graph.cell.isCellEditable(cell) &&
|
||||
!this.graph.editing.isEditing(cell)
|
||||
this.isCellEditable(cell) &&
|
||||
!this.isEditing(cell)
|
||||
) {
|
||||
this.graph.editing.startEditingAtCell(cell, evt);
|
||||
this.startEditingAtCell(cell, evt);
|
||||
InternalEvent.consume(evt);
|
||||
}
|
||||
}
|
||||
|
@ -354,11 +388,11 @@ class GraphEvents {
|
|||
'cell',
|
||||
me.getCell()
|
||||
);
|
||||
const panningHandler = <PanningHandler>this.graph.panning.panningHandler;
|
||||
const connectionHandler = <ConnectionHandler>this.graph.connectionHandler;
|
||||
const panningHandler = <PanningHandler>this.panning.panningHandler;
|
||||
const connectionHandler = <ConnectionHandler>this.connectionHandler;
|
||||
|
||||
// LATER: Check if event should be consumed if me is consumed
|
||||
this.graph.fireEvent(mxe);
|
||||
this.fireEvent(mxe);
|
||||
|
||||
if (mxe.isConsumed()) {
|
||||
// Resets the state of the panning handler
|
||||
|
@ -367,18 +401,15 @@ class GraphEvents {
|
|||
|
||||
// Handles the event if it has not been consumed
|
||||
if (
|
||||
this.graph.isEnabled() &&
|
||||
this.isEnabled() &&
|
||||
!isConsumed(evt) &&
|
||||
!mxe.isConsumed() &&
|
||||
connectionHandler.isEnabled()
|
||||
) {
|
||||
const state = this.graph.view.getState(
|
||||
connectionHandler.marker.getCell(me)
|
||||
);
|
||||
const state = this.getView().getState(connectionHandler.marker.getCell(me));
|
||||
|
||||
if (state != null) {
|
||||
connectionHandler.marker.currentColor =
|
||||
connectionHandler.marker.validColor;
|
||||
connectionHandler.marker.currentColor = connectionHandler.marker.validColor;
|
||||
connectionHandler.marker.markedState = state;
|
||||
connectionHandler.marker.mark();
|
||||
|
||||
|
@ -404,10 +435,7 @@ class GraphEvents {
|
|||
* @param listener Listener to be added to the graph event listeners.
|
||||
*/
|
||||
// addMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
|
||||
addMouseListener(listener: any): void {
|
||||
if (this.mouseListeners == null) {
|
||||
this.mouseListeners = [];
|
||||
}
|
||||
addMouseListener(listener: MouseListener): void {
|
||||
this.mouseListeners.push(listener);
|
||||
}
|
||||
|
||||
|
@ -417,13 +445,11 @@ class GraphEvents {
|
|||
* @param listener Listener to be removed from the graph event listeners.
|
||||
*/
|
||||
// removeMouseListener(listener: { [key: string]: (sender: mxEventSource, me: mxMouseEvent) => void }): void;
|
||||
removeMouseListener(listener: any) {
|
||||
if (this.mouseListeners != null) {
|
||||
for (let i = 0; i < this.mouseListeners.length; i += 1) {
|
||||
if (this.mouseListeners[i] === listener) {
|
||||
this.mouseListeners.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
removeMouseListener(listener: MouseListener) {
|
||||
for (let i = 0; i < this.mouseListeners.length; i += 1) {
|
||||
if (this.mouseListeners[i] === listener) {
|
||||
this.mouseListeners.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -435,14 +461,12 @@ class GraphEvents {
|
|||
* @param me {@link mxMouseEvent} to be updated.
|
||||
* @param evtName Name of the mouse event.
|
||||
*/
|
||||
updateMouseEvent(me: InternalMouseEvent,
|
||||
evtName: string): InternalMouseEvent {
|
||||
|
||||
updateMouseEvent(me: InternalMouseEvent, evtName: string): InternalMouseEvent {
|
||||
if (me.graphX == null || me.graphY == null) {
|
||||
const pt = convertPoint(this.graph.container, me.getX(), me.getY());
|
||||
const pt = convertPoint(this.getContainer(), me.getX(), me.getY());
|
||||
|
||||
me.graphX = pt.x - this.graph.panning.panDx;
|
||||
me.graphY = pt.y - this.graph.panning.panDy;
|
||||
me.graphX = pt.x - this.panning.panDx;
|
||||
me.graphY = pt.y - this.panning.panDy;
|
||||
|
||||
// Searches for rectangles using method if native hit detection is disabled on shape
|
||||
if (
|
||||
|
@ -450,7 +474,7 @@ class GraphEvents {
|
|||
this.isMouseDown &&
|
||||
evtName === InternalEvent.MOUSE_MOVE
|
||||
) {
|
||||
me.state = this.graph.view.getState(
|
||||
me.state = this.getView().getState(
|
||||
this.getCellAt(pt.x, pt.y, null, true, true, (state: CellState) => {
|
||||
return (
|
||||
state.shape == null ||
|
||||
|
@ -476,9 +500,9 @@ class GraphEvents {
|
|||
|
||||
// Dispatches the drop event to the graph which
|
||||
// consumes and executes the source function
|
||||
const pt = convertPoint(this.graph.container, x, y);
|
||||
const pt = convertPoint(this.getContainer(), x, y);
|
||||
|
||||
return this.graph.view.getState(this.graph.cell.getCellAt(pt.x, pt.y));
|
||||
return this.getView().getState(this.getCellAt(pt.x, pt.y));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -499,21 +523,19 @@ class GraphEvents {
|
|||
// Installs event listeners to capture the complete gesture from the event source
|
||||
// for non-MS touch events as a workaround for all events for the same geture being
|
||||
// fired from the event source even if that was removed from the DOM.
|
||||
if (this.eventSource != null && evtName !== InternalEvent.MOUSE_MOVE) {
|
||||
const eventSource = this.getEventSource();
|
||||
|
||||
if (eventSource && evtName !== InternalEvent.MOUSE_MOVE) {
|
||||
InternalEvent.removeGestureListeners(
|
||||
this.eventSource,
|
||||
eventSource,
|
||||
null,
|
||||
this.mouseMoveRedirect,
|
||||
this.mouseUpRedirect
|
||||
);
|
||||
this.mouseMoveRedirect = null;
|
||||
this.mouseUpRedirect = null;
|
||||
this.eventSource = null;
|
||||
} else if (
|
||||
!mxClient.IS_GC &&
|
||||
this.eventSource != null &&
|
||||
me.getSource() !== this.eventSource
|
||||
) {
|
||||
this.setEventSource(null);
|
||||
} else if (!mxClient.IS_GC && eventSource && me.getSource() !== eventSource) {
|
||||
result = true;
|
||||
} else if (
|
||||
mxClient.IS_TOUCH &&
|
||||
|
@ -521,7 +543,7 @@ class GraphEvents {
|
|||
!mouseEvent &&
|
||||
!isPenEvent(me.getEvent())
|
||||
) {
|
||||
this.eventSource = me.getSource();
|
||||
this.setEventSource(me.getSource());
|
||||
|
||||
this.mouseMoveRedirect = (evt: InternalMouseEvent) => {
|
||||
this.fireMouseEvent(
|
||||
|
@ -537,7 +559,7 @@ class GraphEvents {
|
|||
};
|
||||
|
||||
InternalEvent.addGestureListeners(
|
||||
this.eventSource,
|
||||
eventSource,
|
||||
null,
|
||||
this.mouseMoveRedirect,
|
||||
this.mouseUpRedirect
|
||||
|
@ -566,7 +588,7 @@ class GraphEvents {
|
|||
this.isMouseDown = true;
|
||||
this.isMouseTrigger = mouseEvent;
|
||||
}
|
||||
// Drops mouse events that are fired during touch gestures as a workaround for Webkit
|
||||
// Drops mouse events that are fired during touch gestures as a workaround for Webkit
|
||||
// and mouse events that are not in sync with the current internal button state
|
||||
else if (
|
||||
!result &&
|
||||
|
@ -591,20 +613,12 @@ class GraphEvents {
|
|||
* Hook for ignoring synthetic mouse events after touchend in Firefox.
|
||||
*/
|
||||
// isSyntheticEventIgnored(evtName: string, me: mxMouseEvent, sender: mxEventSource): boolean;
|
||||
isSyntheticEventIgnored(
|
||||
evtName: string,
|
||||
me: InternalMouseEvent,
|
||||
sender: any
|
||||
): boolean {
|
||||
isSyntheticEventIgnored(evtName: string, me: InternalMouseEvent, sender: any): boolean {
|
||||
let result = false;
|
||||
const mouseEvent = isMouseEvent(me.getEvent());
|
||||
|
||||
// LATER: This does not cover all possible cases that can go wrong in FF
|
||||
if (
|
||||
this.ignoreMouseEvents &&
|
||||
mouseEvent &&
|
||||
evtName !== InternalEvent.MOUSE_MOVE
|
||||
) {
|
||||
if (this.ignoreMouseEvents && mouseEvent && evtName !== InternalEvent.MOUSE_MOVE) {
|
||||
this.ignoreMouseEvents = evtName !== InternalEvent.MOUSE_UP;
|
||||
result = true;
|
||||
} else if (mxClient.IS_FF && !mouseEvent && evtName === InternalEvent.MOUSE_UP) {
|
||||
|
@ -625,8 +639,7 @@ class GraphEvents {
|
|||
isEventSourceIgnored(evtName: string, me: InternalMouseEvent): boolean {
|
||||
const source = me.getSource();
|
||||
const name = source.nodeName != null ? source.nodeName.toLowerCase() : '';
|
||||
const candidate =
|
||||
!isMouseEvent(me.getEvent()) || isLeftMouseButton(me.getEvent());
|
||||
const candidate = !isMouseEvent(me.getEvent()) || isLeftMouseButton(me.getEvent());
|
||||
|
||||
return (
|
||||
evtName === InternalEvent.MOUSE_DOWN &&
|
||||
|
@ -662,19 +675,20 @@ class GraphEvents {
|
|||
* @param me {@link mxMouseEvent} to be fired.
|
||||
* @param sender Optional sender argument. Default is `this`.
|
||||
*/
|
||||
fireMouseEvent(evtName: string,
|
||||
me: InternalMouseEvent,
|
||||
sender: EventSource = this): void {
|
||||
|
||||
fireMouseEvent(
|
||||
evtName: string,
|
||||
me: InternalMouseEvent,
|
||||
sender: EventSource = this
|
||||
): void {
|
||||
if (this.isEventSourceIgnored(evtName, me)) {
|
||||
if (this.graph.tooltipHandler != null) {
|
||||
this.graph.tooltipHandler.hide();
|
||||
if (this.tooltipHandler != null) {
|
||||
this.tooltipHandler.hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender == null) {
|
||||
sender = this.graph;
|
||||
sender = this;
|
||||
}
|
||||
|
||||
// Updates the graph coordinates in the event
|
||||
|
@ -706,10 +720,7 @@ class GraphEvents {
|
|||
let doubleClickFired = false;
|
||||
|
||||
if (evtName === InternalEvent.MOUSE_UP) {
|
||||
if (
|
||||
me.getCell() === this.lastTouchCell &&
|
||||
this.lastTouchCell !== null
|
||||
) {
|
||||
if (me.getCell() === this.lastTouchCell && this.lastTouchCell !== null) {
|
||||
this.lastTouchTime = 0;
|
||||
const cell = this.lastTouchCell;
|
||||
this.lastTouchCell = null;
|
||||
|
@ -726,10 +737,7 @@ class GraphEvents {
|
|||
InternalEvent.consume(me.getEvent());
|
||||
return;
|
||||
}
|
||||
} else if (
|
||||
this.lastTouchEvent == null ||
|
||||
this.lastTouchEvent !== me.getEvent()
|
||||
) {
|
||||
} else if (this.lastTouchEvent == null || this.lastTouchEvent !== me.getEvent()) {
|
||||
this.lastTouchCell = me.getCell();
|
||||
this.lastTouchX = me.getX();
|
||||
this.lastTouchY = me.getY();
|
||||
|
@ -769,21 +777,11 @@ class GraphEvents {
|
|||
if (!this.isEventIgnored(evtName, me, sender)) {
|
||||
// Updates the event state via getEventState
|
||||
me.state = this.getEventState(me.getState());
|
||||
this.graph.fireEvent(
|
||||
new EventObject(
|
||||
InternalEvent.FIRE_MOUSE_EVENT,
|
||||
'eventName',
|
||||
evtName,
|
||||
'event',
|
||||
me
|
||||
)
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.FIRE_MOUSE_EVENT, 'eventName', evtName, 'event', me)
|
||||
);
|
||||
|
||||
if (
|
||||
mxClient.IS_SF ||
|
||||
mxClient.IS_GC ||
|
||||
me.getEvent().target !== this.container
|
||||
) {
|
||||
if (mxClient.IS_SF || mxClient.IS_GC || me.getEvent().target !== this.container) {
|
||||
const container = <HTMLElement>this.container;
|
||||
|
||||
if (
|
||||
|
@ -792,20 +790,16 @@ class GraphEvents {
|
|||
this.autoScroll &&
|
||||
!isMultiTouchEvent(me.getEvent)
|
||||
) {
|
||||
this.scrollPointToVisible(
|
||||
me.getGraphX(),
|
||||
me.getGraphY(),
|
||||
this.autoExtend
|
||||
);
|
||||
this.scrollPointToVisible(me.getGraphX(), me.getGraphY(), this.autoExtend);
|
||||
} else if (
|
||||
evtName === InternalEvent.MOUSE_UP &&
|
||||
this.ignoreScrollbars &&
|
||||
this.translateToScrollPosition &&
|
||||
(container.scrollLeft !== 0 || container.scrollTop !== 0)
|
||||
) {
|
||||
const s = this.graph.view.scale;
|
||||
const tr = this.graph.view.translate;
|
||||
this.graph.view.setTranslate(
|
||||
const s = this.getView().scale;
|
||||
const tr = this.getView().translate;
|
||||
this.getView().setTranslate(
|
||||
tr.x - container.scrollLeft / s,
|
||||
tr.y - container.scrollTop / s
|
||||
);
|
||||
|
@ -813,23 +807,21 @@ class GraphEvents {
|
|||
container.scrollTop = 0;
|
||||
}
|
||||
|
||||
if (this.mouseListeners != null) {
|
||||
const args = [sender, me];
|
||||
const mouseListeners = this.mouseListeners;
|
||||
const args = [sender, me];
|
||||
const mouseListeners = this.mouseListeners;
|
||||
|
||||
// Does not change returnValue in Opera
|
||||
if (!me.getEvent().preventDefault) {
|
||||
me.getEvent().returnValue = true;
|
||||
}
|
||||
// Does not change returnValue in Opera
|
||||
if (!me.getEvent().preventDefault) {
|
||||
me.getEvent().returnValue = true;
|
||||
}
|
||||
|
||||
for (const l of mouseListeners) {
|
||||
if (evtName === InternalEvent.MOUSE_DOWN) {
|
||||
l.mouseDown.apply(l, args);
|
||||
} else if (evtName === InternalEvent.MOUSE_MOVE) {
|
||||
l.mouseMove.apply(l, args);
|
||||
} else if (evtName === InternalEvent.MOUSE_UP) {
|
||||
l.mouseUp.apply(l, args);
|
||||
}
|
||||
for (const l of mouseListeners) {
|
||||
if (evtName === InternalEvent.MOUSE_DOWN) {
|
||||
l.mouseDown.apply(l, args);
|
||||
} else if (evtName === InternalEvent.MOUSE_MOVE) {
|
||||
l.mouseMove.apply(l, args);
|
||||
} else if (evtName === InternalEvent.MOUSE_UP) {
|
||||
l.mouseUp.apply(l, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -863,10 +855,7 @@ class GraphEvents {
|
|||
window.clearTimeout(this.tapAndHoldThread);
|
||||
}
|
||||
|
||||
this.tapAndHoldThread = window.setTimeout(
|
||||
handler,
|
||||
this.tapAndHoldDelay
|
||||
);
|
||||
this.tapAndHoldThread = window.setTimeout(handler, this.tapAndHoldDelay);
|
||||
this.tapAndHoldValid = true;
|
||||
} else if (evtName === InternalEvent.MOUSE_UP) {
|
||||
this.tapAndHoldInProgress = false;
|
||||
|
@ -893,9 +882,7 @@ class GraphEvents {
|
|||
/**
|
||||
* Consumes the given {@link InternalMouseEvent} if it's a touchStart event.
|
||||
*/
|
||||
consumeMouseEvent(evtName: string,
|
||||
me: InternalMouseEvent,
|
||||
sender: any = this): void {
|
||||
consumeMouseEvent(evtName: string, me: InternalMouseEvent, sender: any = this): void {
|
||||
// Workaround for duplicate click in Windows 8 with Chrome/FF/Opera with touch
|
||||
if (evtName === InternalEvent.MOUSE_DOWN && isTouchEvent(me.getEvent())) {
|
||||
me.consume(false);
|
||||
|
@ -933,13 +920,10 @@ class GraphEvents {
|
|||
* @param evt Gestureend event that represents the gesture.
|
||||
* @param cell Optional {@link Cell} associated with the gesture.
|
||||
*/
|
||||
fireGestureEvent(evt: MouseEvent,
|
||||
cell: Cell | null = null): void {
|
||||
fireGestureEvent(evt: MouseEvent, cell: Cell | null = null): void {
|
||||
// Resets double tap event handling when gestures take place
|
||||
this.lastTouchTime = 0;
|
||||
this.graph.fireEvent(
|
||||
new EventObject(InternalEvent.GESTURE, 'event', evt, 'cell', cell)
|
||||
);
|
||||
this.fireEvent(new EventObject(InternalEvent.GESTURE, 'event', evt, 'cell', cell));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -948,63 +932,56 @@ class GraphEvents {
|
|||
* SVG-bases browsers.
|
||||
*/
|
||||
sizeDidChange(): void {
|
||||
const bounds = this.graph.getGraphBounds();
|
||||
const bounds = this.getGraphBounds();
|
||||
|
||||
if (this.graph.container != null) {
|
||||
const border = this.graph.getBorder();
|
||||
const border = this.getBorder();
|
||||
|
||||
let width = Math.max(0, bounds.x) + bounds.width + 2 * border;
|
||||
let height = Math.max(0, bounds.y) + bounds.height + 2 * border;
|
||||
let width = Math.max(0, bounds.x) + bounds.width + 2 * border;
|
||||
let height = Math.max(0, bounds.y) + bounds.height + 2 * border;
|
||||
|
||||
if (this.graph.minimumContainerSize != null) {
|
||||
width = Math.max(width, this.graph.minimumContainerSize.width);
|
||||
height = Math.max(height, this.graph.minimumContainerSize.height);
|
||||
}
|
||||
|
||||
if (this.graph.resizeContainer) {
|
||||
this.graph.doResizeContainer(width, height);
|
||||
}
|
||||
|
||||
if (this.preferPageSize || this.pageVisible) {
|
||||
const size = this.getPreferredPageSize(
|
||||
bounds,
|
||||
Math.max(1, width),
|
||||
Math.max(1, height)
|
||||
);
|
||||
|
||||
if (size != null) {
|
||||
width = size.width * this.graph.view.scale;
|
||||
height = size.height * this.graph.view.scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.graph.minimumGraphSize != null) {
|
||||
width = Math.max(
|
||||
width,
|
||||
this.graph.minimumGraphSize.width * this.graph.view.scale
|
||||
);
|
||||
height = Math.max(
|
||||
height,
|
||||
this.graph.minimumGraphSize.height * this.graph.view.scale
|
||||
);
|
||||
}
|
||||
|
||||
width = Math.ceil(width);
|
||||
height = Math.ceil(height);
|
||||
|
||||
// @ts-ignore
|
||||
const root = this.graph.view.getDrawPane().ownerSVGElement;
|
||||
|
||||
if (root != null) {
|
||||
root.style.minWidth = `${Math.max(1, width)}px`;
|
||||
root.style.minHeight = `${Math.max(1, height)}px`;
|
||||
root.style.width = '100%';
|
||||
root.style.height = '100%';
|
||||
}
|
||||
|
||||
this.graph.pageBreaks.updatePageBreaks(this.graph.pageBreaksVisible, width, height);
|
||||
if (this.minimumContainerSize != null) {
|
||||
width = Math.max(width, this.minimumContainerSize.width);
|
||||
height = Math.max(height, this.minimumContainerSize.height);
|
||||
}
|
||||
this.graph.fireEvent(new EventObject(InternalEvent.SIZE, 'bounds', bounds));
|
||||
|
||||
if (this.resizeContainer) {
|
||||
this.doResizeContainer(width, height);
|
||||
}
|
||||
|
||||
if (this.preferPageSize || this.pageVisible) {
|
||||
const size = this.getPreferredPageSize(
|
||||
bounds,
|
||||
Math.max(1, width),
|
||||
Math.max(1, height)
|
||||
);
|
||||
|
||||
if (size != null) {
|
||||
width = size.width * this.getView().scale;
|
||||
height = size.height * this.getView().scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.minimumGraphSize != null) {
|
||||
width = Math.max(width, this.minimumGraphSize.width * this.getView().scale);
|
||||
height = Math.max(height, this.minimumGraphSize.height * this.getView().scale);
|
||||
}
|
||||
|
||||
width = Math.ceil(width);
|
||||
height = Math.ceil(height);
|
||||
|
||||
// @ts-ignore
|
||||
const root = this.getView().getDrawPane().ownerSVGElement;
|
||||
|
||||
if (root != null) {
|
||||
root.style.minWidth = `${Math.max(1, width)}px`;
|
||||
root.style.minHeight = `${Math.max(1, height)}px`;
|
||||
root.style.width = '100%';
|
||||
root.style.height = '100%';
|
||||
}
|
||||
|
||||
this.pageBreaks.updatePageBreaks(this.pageBreaksVisible, width, height);
|
||||
|
||||
this.fireEvent(new EventObject(InternalEvent.SIZE, 'bounds', bounds));
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -1068,13 +1045,13 @@ class GraphEvents {
|
|||
* offset by half of the {@link gridSize}. Default is `true`.
|
||||
*/
|
||||
getPointForEvent(evt: InternalMouseEvent, addOffset: boolean = true): Point {
|
||||
const p = convertPoint(this.graph.container, getClientX(evt), getClientY(evt));
|
||||
const s = this.graph.view.scale;
|
||||
const tr = this.graph.view.translate;
|
||||
const off = addOffset ? this.graph.snap.gridSize / 2 : 0;
|
||||
const p = convertPoint(this.getContainer(), getClientX(evt), getClientY(evt));
|
||||
const s = this.getView().scale;
|
||||
const tr = this.getView().translate;
|
||||
const off = addOffset ? this.getGridSize() / 2 : 0;
|
||||
|
||||
p.x = this.graph.snap.snap(p.x / s - tr.x - off);
|
||||
p.y = this.graph.snap.snap(p.y / s - tr.y - off);
|
||||
p.x = this.snap(p.x / s - tr.x - off);
|
||||
p.y = this.snap(p.y / s - tr.y - off);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -1137,7 +1114,7 @@ class GraphEvents {
|
|||
* @param me {@link mxMouseEvent} whose cursor should be returned.
|
||||
*/
|
||||
getCursorForMouseEvent(me: InternalMouseEvent): string | null {
|
||||
return this.graph.cell.getCursorForCell(me.getCell());
|
||||
return this.getCursorForCell(me.getCell());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,25 +9,7 @@ import mxClient from '../../mxClient';
|
|||
import { isConsumed, isMouseEvent } from '../../util/EventUtils';
|
||||
import graph from '../Graph';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
|
||||
type Listener = {
|
||||
name: string;
|
||||
f: EventListener;
|
||||
};
|
||||
|
||||
type ListenerTarget = {
|
||||
mxListenerList?: Listener[];
|
||||
};
|
||||
|
||||
type Listenable = (Node | Window) & ListenerTarget;
|
||||
|
||||
type GestureEvent = Event &
|
||||
MouseEvent & {
|
||||
scale?: number;
|
||||
pointerId?: number;
|
||||
};
|
||||
|
||||
type EventCache = GestureEvent[];
|
||||
import { EventCache, GestureEvent, Listenable } from '../../types';
|
||||
|
||||
// Checks if passive event listeners are supported
|
||||
// see https://github.com/Modernizr/Modernizr/issues/1894
|
||||
|
@ -69,11 +51,7 @@ class InternalEvent {
|
|||
* to a given execution scope.
|
||||
*/
|
||||
// static addListener(element: Node | Window, eventName: string, funct: Function): void;
|
||||
static addListener(
|
||||
element: Listenable,
|
||||
eventName: string,
|
||||
funct: EventListener
|
||||
) {
|
||||
static addListener(element: Listenable, eventName: string, funct: EventListener) {
|
||||
element.addEventListener(
|
||||
eventName,
|
||||
funct,
|
||||
|
@ -92,11 +70,7 @@ class InternalEvent {
|
|||
* Removes the specified listener from the given element.
|
||||
*/
|
||||
// static removeListener(element: Node | Window, eventName: string, funct: Function): void;
|
||||
static removeListener(
|
||||
element: Listenable,
|
||||
eventName: string,
|
||||
funct: EventListener
|
||||
) {
|
||||
static removeListener(element: Listenable, eventName: string, funct: EventListener) {
|
||||
element.removeEventListener(eventName, funct, false);
|
||||
|
||||
if (element.mxListenerList) {
|
||||
|
@ -264,7 +238,7 @@ class InternalEvent {
|
|||
} else if (!isConsumed(evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_DOWN,
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -274,7 +248,7 @@ class InternalEvent {
|
|||
} else if (!isConsumed(evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_MOVE,
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -284,7 +258,7 @@ class InternalEvent {
|
|||
} else if (!isConsumed(evt)) {
|
||||
graph.fireMouseEvent(
|
||||
InternalEvent.MOUSE_UP,
|
||||
new InternalMouseEvent(evt, getState(evt))
|
||||
new InternalMouseEvent(evt as MouseEvent, getState(evt))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -348,13 +322,7 @@ class InternalEvent {
|
|||
* https://www.chromestatus.com/features/6662647093133312.
|
||||
*/
|
||||
static addMouseWheelListener(
|
||||
funct: (
|
||||
event: Event,
|
||||
up: boolean,
|
||||
force?: boolean,
|
||||
cx?: number,
|
||||
cy?: number
|
||||
) => void,
|
||||
funct: (event: Event, up: boolean, force?: boolean, cx?: number, cy?: number) => void,
|
||||
target: Listenable
|
||||
) {
|
||||
if (funct != null) {
|
||||
|
@ -430,11 +398,9 @@ class InternalEvent {
|
|||
ty > InternalEvent.PINCH_THRESHOLD
|
||||
) {
|
||||
const cx =
|
||||
evtCache[0].clientX +
|
||||
(evtCache[1].clientX - evtCache[0].clientX) / 2;
|
||||
evtCache[0].clientX + (evtCache[1].clientX - evtCache[0].clientX) / 2;
|
||||
const cy =
|
||||
evtCache[0].clientY +
|
||||
(evtCache[1].clientY - evtCache[0].clientY) / 2;
|
||||
evtCache[0].clientY + (evtCache[1].clientY - evtCache[0].clientY) / 2;
|
||||
|
||||
funct(evtCache[0], tx > ty ? dx > dx0 : dy > dy0, true, cx, cy);
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
import { isAncestorNode } from '../../util/DomUtils';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import Shape from '../geometry/shape/Shape';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
|
||||
/**
|
||||
* Class: mxMouseEvent
|
||||
|
@ -51,10 +50,15 @@ import Cell from '../cell/datatypes/Cell';
|
|||
*
|
||||
*/
|
||||
class InternalMouseEvent {
|
||||
constructor(evt: MouseEvent, state?: CellState) {
|
||||
constructor(evt: MouseEvent, state: CellState | null = null) {
|
||||
this.evt = evt;
|
||||
this.state = state;
|
||||
this.sourceState = state;
|
||||
|
||||
// graphX and graphY are updated right after this constructor is executed,
|
||||
// so let them default to 0 and make them not nullable.
|
||||
this.graphX = 0;
|
||||
this.graphY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +66,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Holds the consumed state of this event.
|
||||
*/
|
||||
consumed: boolean = false;
|
||||
consumed = false;
|
||||
|
||||
/**
|
||||
* Variable: evt
|
||||
|
@ -77,7 +81,7 @@ class InternalMouseEvent {
|
|||
* Holds the x-coordinate of the event in the graph. This value is set in
|
||||
* <mxGraph.fireMouseEvent>.
|
||||
*/
|
||||
graphX?: number;
|
||||
graphX: number;
|
||||
|
||||
/**
|
||||
* Variable: graphY
|
||||
|
@ -85,14 +89,14 @@ class InternalMouseEvent {
|
|||
* Holds the y-coordinate of the event in the graph. This value is set in
|
||||
* <mxGraph.fireMouseEvent>.
|
||||
*/
|
||||
graphY?: number;
|
||||
graphY: number;
|
||||
|
||||
/**
|
||||
* Variable: state
|
||||
*
|
||||
* Holds the optional <mxCellState> associated with this event.
|
||||
*/
|
||||
state?: CellState;
|
||||
state: CellState | null;
|
||||
|
||||
/**
|
||||
* Variable: sourceState
|
||||
|
@ -100,14 +104,14 @@ class InternalMouseEvent {
|
|||
* Holds the <mxCellState> that was passed to the constructor. This can be
|
||||
* different from <state> depending on the result of <mxGraph.getEventState>.
|
||||
*/
|
||||
sourceState?: CellState;
|
||||
sourceState: CellState | null;
|
||||
|
||||
/**
|
||||
* Function: getEvent
|
||||
*
|
||||
* Returns <evt>.
|
||||
*/
|
||||
getEvent(): MouseEvent {
|
||||
getEvent() {
|
||||
return this.evt;
|
||||
}
|
||||
|
||||
|
@ -116,7 +120,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns the target DOM element using <mxEvent.getSource> for <evt>.
|
||||
*/
|
||||
getSource(): Element {
|
||||
getSource() {
|
||||
return getSource(this.evt);
|
||||
}
|
||||
|
||||
|
@ -125,7 +129,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns true if the given <mxShape> is the source of <evt>.
|
||||
*/
|
||||
isSource(shape: Shape) {
|
||||
isSource(shape: Shape | null) {
|
||||
return shape ? isAncestorNode(shape.node, this.getSource()) : false;
|
||||
}
|
||||
|
||||
|
@ -152,7 +156,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns <graphX>.
|
||||
*/
|
||||
getGraphX(): number | undefined {
|
||||
getGraphX() {
|
||||
return this.graphX;
|
||||
}
|
||||
|
||||
|
@ -161,7 +165,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns <graphY>.
|
||||
*/
|
||||
getGraphY(): number | undefined {
|
||||
getGraphY() {
|
||||
return this.graphY;
|
||||
}
|
||||
|
||||
|
@ -170,7 +174,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns <state>.
|
||||
*/
|
||||
getState(): CellState | undefined {
|
||||
getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
|
@ -179,12 +183,9 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns the <mxCell> in <state> is not null.
|
||||
*/
|
||||
getCell(): Cell | null {
|
||||
getCell() {
|
||||
const state = this.getState();
|
||||
if (state != null) {
|
||||
return state.cell;
|
||||
}
|
||||
return null;
|
||||
return state ? state.cell : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +193,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns true if the event is a popup trigger.
|
||||
*/
|
||||
isPopupTrigger(): boolean {
|
||||
isPopupTrigger() {
|
||||
return isPopupTrigger(this.getEvent());
|
||||
}
|
||||
|
||||
|
@ -201,7 +202,7 @@ class InternalMouseEvent {
|
|||
*
|
||||
* Returns <consumed>.
|
||||
*/
|
||||
isConsumed(): boolean {
|
||||
isConsumed() {
|
||||
return this.consumed;
|
||||
}
|
||||
|
||||
|
@ -218,11 +219,10 @@ class InternalMouseEvent {
|
|||
* preventDefault - Specifies if the native event should be canceled. Default
|
||||
* is true.
|
||||
*/
|
||||
consume(preventDefault?: boolean): void {
|
||||
preventDefault =
|
||||
preventDefault != null
|
||||
? preventDefault
|
||||
: this.evt.touches != null || isMouseEvent(this.evt);
|
||||
consume(preventDefault?: boolean) {
|
||||
preventDefault = preventDefault
|
||||
? preventDefault
|
||||
: this.evt instanceof TouchEvent || isMouseEvent(this.evt);
|
||||
|
||||
if (preventDefault && this.evt.preventDefault) {
|
||||
this.evt.preventDefault();
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
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 { autoImplement, getValue, toRadians } from '../../util/Utils';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
|
||||
import type Graph from '../Graph';
|
||||
import type GraphCells from '../cell/GraphCells';
|
||||
import type GraphSelection from '../selection/GraphSelection';
|
||||
import type GraphEditing from '../editing/GraphEditing';
|
||||
|
||||
/**
|
||||
* GraphFoldingOptions
|
||||
*
|
||||
|
@ -31,22 +35,26 @@ type GraphFoldingOptions = {
|
|||
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,
|
||||
}
|
||||
) {
|
||||
this.graph = graph;
|
||||
this.options = options;
|
||||
}
|
||||
type PartialGraph = Pick<Graph, 'getModel' | 'fireEvent'>;
|
||||
type PartialCells = Pick<
|
||||
GraphCells,
|
||||
| 'getCurrentCellStyle'
|
||||
| 'isExtendParent'
|
||||
| 'extendParent'
|
||||
| 'constrainChild'
|
||||
| 'getPreferredSizeForCell'
|
||||
>;
|
||||
type PartialSelection = Pick<GraphSelection, 'getSelectionCells'>;
|
||||
type PartialEditing = Pick<GraphEditing, 'stopEditing'>;
|
||||
type PartialClass = PartialGraph & PartialCells & PartialSelection & PartialEditing;
|
||||
|
||||
graph: Graph;
|
||||
options: GraphFoldingOptions;
|
||||
class GraphFolding extends autoImplement<PartialClass>() {
|
||||
options: GraphFoldingOptions = {
|
||||
foldingEnabled: true,
|
||||
collapsedImage: new Image(`${mxClient.imageBasePath}/collapsed.gif`, 9, 9),
|
||||
expandedImage: new Image(`${mxClient.imageBasePath}/expanded.gif`, 9, 9),
|
||||
collapseToPreferredSize: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the resource key for the tooltip on the collapse/expand icon.
|
||||
|
@ -56,6 +64,8 @@ class GraphFolding {
|
|||
*/
|
||||
collapseExpandResource: string = mxClient.language != 'none' ? 'collapse-expand' : '';
|
||||
|
||||
getCollapseExpandResource = () => this.collapseExpandResource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @default true
|
||||
|
@ -65,7 +75,7 @@ class GraphFolding {
|
|||
* Returns the cells which are movable in the given array of cells.
|
||||
*/
|
||||
getFoldableCells(cells: CellArray, collapse: boolean = false): CellArray | null {
|
||||
return this.graph.model.filterCells(cells, (cell: Cell) => {
|
||||
return this.getModel().filterCells(cells, (cell: Cell) => {
|
||||
return this.isCellFoldable(cell, collapse);
|
||||
});
|
||||
}
|
||||
|
@ -80,7 +90,7 @@ class GraphFolding {
|
|||
// isCellFoldable(cell: mxCell, collapse: boolean): boolean;
|
||||
isCellFoldable(cell: Cell, collapse: boolean = false): boolean {
|
||||
const style = this.getCurrentCellStyle(cell);
|
||||
return cell.getChildCount() > 0 && style.foldable != 0;
|
||||
return cell.getChildCount() > 0 && style.foldable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,7 +141,7 @@ class GraphFolding {
|
|||
|
||||
this.stopEditing(false);
|
||||
|
||||
this.graph.model.beginUpdate();
|
||||
this.getModel().beginUpdate();
|
||||
try {
|
||||
this.cellsFolded(cells, collapse, recurse, checkFoldable);
|
||||
this.fireEvent(
|
||||
|
@ -146,7 +156,7 @@ class GraphFolding {
|
|||
)
|
||||
);
|
||||
} finally {
|
||||
this.graph.model.endUpdate();
|
||||
this.getModel().endUpdate();
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
@ -171,14 +181,14 @@ class GraphFolding {
|
|||
checkFoldable: boolean = false
|
||||
): void {
|
||||
if (cells != null && cells.length > 0) {
|
||||
this.graph.model.beginUpdate();
|
||||
this.getModel().beginUpdate();
|
||||
try {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (
|
||||
(!checkFoldable || this.isCellFoldable(cells[i], collapse)) &&
|
||||
collapse !== cells[i].isCollapsed()
|
||||
) {
|
||||
this.graph.model.setCollapsed(cells[i], collapse);
|
||||
this.getModel().setCollapsed(cells[i], collapse);
|
||||
this.swapBounds(cells[i], collapse);
|
||||
|
||||
if (this.isExtendParent(cells[i])) {
|
||||
|
@ -194,7 +204,7 @@ class GraphFolding {
|
|||
}
|
||||
}
|
||||
|
||||
this.graph.fireEvent(
|
||||
this.fireEvent(
|
||||
new EventObject(
|
||||
InternalEvent.CELLS_FOLDED,
|
||||
'cells',
|
||||
|
@ -206,7 +216,7 @@ class GraphFolding {
|
|||
)
|
||||
);
|
||||
} finally {
|
||||
this.graph.model.endUpdate();
|
||||
this.getModel().endUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +237,7 @@ class GraphFolding {
|
|||
this.updateAlternateBounds(cell, geo, willCollapse);
|
||||
geo.swap();
|
||||
|
||||
this.graph.model.setGeometry(cell, geo);
|
||||
this.getModel().setGeometry(cell, geo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ class Shape {
|
|||
*
|
||||
* container - DOM node that will contain the shape.
|
||||
*/
|
||||
init(container: SVGElement) {
|
||||
init(container: HTMLElement | SVGElement) {
|
||||
if (!this.node.parentNode) {
|
||||
container.appendChild(this.node);
|
||||
}
|
||||
|
|
|
@ -323,7 +323,7 @@ class StencilShape extends Shape {
|
|||
* direction - Optional direction of the shape to be darwn.
|
||||
*/
|
||||
computeAspect(
|
||||
shape: Shape,
|
||||
shape: Shape | null = null,
|
||||
x: number,
|
||||
y: number,
|
||||
w: number,
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import Graph from '../Graph';
|
||||
import ImageBundle from './ImageBundle';
|
||||
|
||||
class GraphImage {
|
||||
constructor(graph: Graph) {
|
||||
this.imageBundles = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the list of image bundles.
|
||||
*/
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
type ImageMap = {
|
||||
[key: string]: {
|
||||
value: string;
|
||||
fallback: Function;
|
||||
fallback: string;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ class ImageBundle {
|
|||
* Adds the specified entry to the map. The entry is an object with a value and
|
||||
* fallback property as specified in the arguments.
|
||||
*/
|
||||
putImage(key: string, value: string, fallback: Function): void {
|
||||
putImage(key: string, value: string, fallback: string) {
|
||||
this.images[key] = { value, fallback };
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
import Cell from "../cell/datatypes/Cell";
|
||||
import {getValue} from "../../util/Utils";
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import { autoImplement, getValue } from '../../util/Utils';
|
||||
|
||||
class GraphLabel {
|
||||
import type Graph from '../Graph';
|
||||
import type GraphCells from '../cell/GraphCells';
|
||||
import type GraphEdge from '../cell/edge/GraphEdge';
|
||||
import type GraphVertex from '../cell/vertex/GraphVertex';
|
||||
|
||||
type PartialGraph = Pick<Graph, 'convertValueToString'>;
|
||||
type PartialCells = Pick<GraphCells, 'getCurrentCellStyle' | 'isCellLocked'>;
|
||||
type PartialEdge = Pick<GraphEdge, 'isEdgeLabelsMovable'>;
|
||||
type PartialVertex = Pick<GraphVertex, 'isVertexLabelsMovable'>;
|
||||
type PartialClass = PartialGraph & PartialCells & PartialEdge & PartialVertex;
|
||||
|
||||
class GraphLabel extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Returns a string or DOM node that represents the label for the given
|
||||
* cell. This implementation uses {@link convertValueToString} if {@link labelsVisible}
|
||||
|
@ -53,7 +64,7 @@ class GraphLabel {
|
|||
getLabel(cell: Cell): string | Node | null {
|
||||
let result: string | null = '';
|
||||
|
||||
if (this.labelsVisible && cell != null) {
|
||||
if (this.isLabelsVisible() && cell != null) {
|
||||
const style = this.getCurrentCellStyle(cell);
|
||||
|
||||
if (!getValue(style, 'noLabel', false)) {
|
||||
|
@ -73,6 +84,20 @@ class GraphLabel {
|
|||
return this.isHtmlLabels();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if labels should be visible. This is used in {@link getLabel}. Default
|
||||
* is true.
|
||||
*/
|
||||
labelsVisible: boolean = true;
|
||||
|
||||
isLabelsVisible = () => this.labelsVisible;
|
||||
|
||||
/**
|
||||
* Specifies the return value for {@link isHtmlLabel}.
|
||||
* @default false
|
||||
*/
|
||||
htmlLabels: boolean = false;
|
||||
|
||||
/**
|
||||
* Returns {@link htmlLabels}.
|
||||
*/
|
||||
|
@ -154,8 +179,8 @@ class GraphLabel {
|
|||
isLabelMovable(cell: Cell): boolean {
|
||||
return (
|
||||
!this.isCellLocked(cell) &&
|
||||
((cell.isEdge() && this.edgeLabelsMovable) ||
|
||||
(cell.isVertex() && this.vertexLabelsMovable))
|
||||
((cell.isEdge() && this.isEdgeLabelsMovable()) ||
|
||||
(cell.isVertex() && this.isVertexLabelsMovable()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,24 @@ import EventObject from '../event/EventObject';
|
|||
import InternalEvent from '../event/InternalEvent';
|
||||
import Image from '../image/ImageBox';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import Graph from '../Graph';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class Overlays {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../Graph';
|
||||
import type GraphSelection from '../selection/GraphSelection';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
| 'getView'
|
||||
| 'fireEvent'
|
||||
| 'getModel'
|
||||
| 'isEnabled'
|
||||
| 'getWarningImage'
|
||||
| 'getCellRenderer'
|
||||
>;
|
||||
type PartialSelection = Pick<GraphSelection, 'setSelectionCell'>;
|
||||
type PartialClass = PartialGraph & PartialSelection;
|
||||
|
||||
class GraphOverlays extends autoImplement<PartialClass>() {
|
||||
/*****************************************************************************
|
||||
* Group: Overlays
|
||||
*****************************************************************************/
|
||||
|
@ -25,15 +34,13 @@ class Overlays {
|
|||
* @param overlay {@link mxCellOverlay} to be added for the cell.
|
||||
*/
|
||||
addCellOverlay(cell: Cell, overlay: CellOverlay): CellOverlay {
|
||||
if (cell.overlays == null) {
|
||||
cell.overlays = [];
|
||||
}
|
||||
cell.overlays.push(overlay);
|
||||
|
||||
// Immediately update the cell display if the state exists
|
||||
const state = this.getView().getState(cell);
|
||||
if (state != null) {
|
||||
this.cellRenderer.redraw(state);
|
||||
|
||||
if (state) {
|
||||
this.getCellRenderer().redraw(state);
|
||||
}
|
||||
|
||||
this.fireEvent(
|
||||
|
@ -48,7 +55,7 @@ class Overlays {
|
|||
*
|
||||
* @param cell {@link mxCell} whose overlays should be returned.
|
||||
*/
|
||||
getCellOverlays(cell: Cell): CellOverlay[] | null {
|
||||
getCellOverlays(cell: Cell) {
|
||||
return cell.overlays;
|
||||
}
|
||||
|
||||
|
@ -61,24 +68,20 @@ class Overlays {
|
|||
* @param overlay Optional {@link CellOverlay} to be removed.
|
||||
*/
|
||||
// removeCellOverlay(cell: mxCell, overlay: mxCellOverlay): mxCellOverlay;
|
||||
removeCellOverlay(cell: Cell, overlay: CellOverlay | null = null): any {
|
||||
if (overlay == null) {
|
||||
removeCellOverlay(cell: Cell, overlay: CellOverlay | null = null) {
|
||||
if (!overlay) {
|
||||
this.removeCellOverlays(cell);
|
||||
} else {
|
||||
const index = cell.overlays ? cell.overlays.indexOf(overlay) : -1;
|
||||
const index = cell.overlays.indexOf(overlay);
|
||||
|
||||
if (index >= 0) {
|
||||
(<CellOverlay[]>cell.overlays).splice(index, 1);
|
||||
|
||||
if ((<CellOverlay[]>cell.overlays).length === 0) {
|
||||
cell.overlays = null;
|
||||
}
|
||||
cell.overlays.splice(index, 1);
|
||||
|
||||
// Immediately updates the cell display if the state exists
|
||||
const state = this.getView().getState(cell);
|
||||
|
||||
if (state != null) {
|
||||
this.cellRenderer.redraw(state);
|
||||
if (state) {
|
||||
this.getCellRenderer().redraw(state);
|
||||
}
|
||||
|
||||
this.fireEvent(
|
||||
|
@ -99,33 +102,31 @@ class Overlays {
|
|||
*
|
||||
* @param cell {@link mxCell} whose overlays should be removed
|
||||
*/
|
||||
removeCellOverlays(cell: Cell): CellOverlay[] {
|
||||
removeCellOverlays(cell: Cell) {
|
||||
const { overlays } = cell;
|
||||
|
||||
if (overlays != null) {
|
||||
cell.overlays = null;
|
||||
cell.overlays = [];
|
||||
|
||||
// Immediately updates the cell display if the state exists
|
||||
const state = this.getView().getState(cell);
|
||||
// Immediately updates the cell display if the state exists
|
||||
const state = this.getView().getState(cell);
|
||||
|
||||
if (state != null) {
|
||||
this.cellRenderer.redraw(state);
|
||||
}
|
||||
|
||||
for (let i = 0; i < overlays.length; i += 1) {
|
||||
this.fireEvent(
|
||||
new EventObject(
|
||||
InternalEvent.REMOVE_OVERLAY,
|
||||
'cell',
|
||||
cell,
|
||||
'overlay',
|
||||
overlays[i]
|
||||
)
|
||||
);
|
||||
}
|
||||
if (state) {
|
||||
this.getCellRenderer().redraw(state);
|
||||
}
|
||||
|
||||
return <CellOverlay[]>overlays;
|
||||
for (let i = 0; i < overlays.length; i += 1) {
|
||||
this.fireEvent(
|
||||
new EventObject(
|
||||
InternalEvent.REMOVE_OVERLAY,
|
||||
'cell',
|
||||
cell,
|
||||
'overlay',
|
||||
overlays[i]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return overlays;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,15 +138,19 @@ class Overlays {
|
|||
* @param cell Optional {@link Cell} that represents the root of the subtree to
|
||||
* remove the overlays from. Default is the root in the model.
|
||||
*/
|
||||
clearCellOverlays(cell: Cell = <Cell>this.getModel().getRoot()): void {
|
||||
this.removeCellOverlays(<Cell>cell);
|
||||
clearCellOverlays(cell: Cell | null = null) {
|
||||
cell = cell ?? this.getModel().getRoot();
|
||||
|
||||
if (!cell) return;
|
||||
|
||||
this.removeCellOverlays(cell);
|
||||
|
||||
// Recursively removes all overlays from the children
|
||||
const childCount = cell.getChildCount();
|
||||
|
||||
for (let i = 0; i < childCount; i += 1) {
|
||||
const child = cell.getChildAt(i);
|
||||
this.clearCellOverlays(<Cell>child); // recurse
|
||||
this.clearCellOverlays(child); // recurse
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,12 +176,10 @@ class Overlays {
|
|||
setCellWarning(
|
||||
cell: Cell,
|
||||
warning: string | null = null,
|
||||
img: Image | null = null,
|
||||
isSelect: boolean = false
|
||||
): CellOverlay | null {
|
||||
if (warning != null && warning.length > 0) {
|
||||
img = img != null ? img : this.warningImage;
|
||||
|
||||
img: Image = this.getWarningImage(),
|
||||
isSelect = false
|
||||
) {
|
||||
if (warning && warning.length > 0) {
|
||||
// Creates the overlay with the image and warning
|
||||
const overlay = new CellOverlay(img, `<font color=red>${warning}</font>`);
|
||||
|
||||
|
@ -201,4 +204,4 @@ class Overlays {
|
|||
}
|
||||
}
|
||||
|
||||
export default Overlays;
|
||||
export default GraphOverlays;
|
|
@ -8,7 +8,7 @@ import EventSource from '../event/EventSource';
|
|||
import UndoableEdit from './UndoableEdit';
|
||||
import CellPath from '../cell/datatypes/CellPath';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import utils, {isNumeric} from '../../util/Utils';
|
||||
import utils, { isNumeric } from '../../util/Utils';
|
||||
import EventObject from '../event/EventObject';
|
||||
import InternalEvent from '../event/InternalEvent';
|
||||
import Point from '../geometry/Point';
|
||||
|
@ -20,8 +20,8 @@ import StyleChange from '../style/StyleChange';
|
|||
import TerminalChange from '../cell/edge/TerminalChange';
|
||||
import ValueChange from '../cell/ValueChange';
|
||||
import VisibleChange from '../style/VisibleChange';
|
||||
import Geometry from "../geometry/Geometry";
|
||||
import CellArray from "../cell/datatypes/CellArray";
|
||||
import Geometry from '../geometry/Geometry';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
|
||||
import type { CellMap, FilterFunction, UndoableChange } from '../../types';
|
||||
|
||||
|
@ -207,7 +207,7 @@ import type { CellMap, FilterFunction, UndoableChange } from '../../types';
|
|||
* @class Model
|
||||
*/
|
||||
class Model extends EventSource {
|
||||
constructor(root: Cell | null=null) {
|
||||
constructor(root: Cell | null = null) {
|
||||
super();
|
||||
this.currentEdit = this.createUndoableEdit();
|
||||
|
||||
|
@ -320,16 +320,15 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {string} id A string representing the Id of the cell.
|
||||
*/
|
||||
getCell(id: string): Cell | null {
|
||||
return this.cells != null ? this.cells[id] : null;
|
||||
getCell(id: string) {
|
||||
return this.cells ? this.cells[id] : null;
|
||||
}
|
||||
|
||||
filterCells(cells: CellArray,
|
||||
filter: FilterFunction): CellArray | null {
|
||||
filterCells(cells: CellArray, filter: FilterFunction) {
|
||||
return new CellArray(...cells).filterCells(filter);
|
||||
}
|
||||
|
||||
getRoot(cell: Cell | null = null): Cell | null {
|
||||
getRoot(cell: Cell | null = null) {
|
||||
return cell ? cell.getRoot() : this.root;
|
||||
}
|
||||
|
||||
|
@ -349,7 +348,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} root that specifies the new root.
|
||||
*/
|
||||
setRoot(root: Cell | null): Cell | null {
|
||||
setRoot(root: Cell | null) {
|
||||
this.execute(new RootChange(this, root));
|
||||
return root;
|
||||
}
|
||||
|
@ -360,7 +359,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} root that specifies the new root.
|
||||
*/
|
||||
rootChanged(root: Cell | null): Cell | null {
|
||||
rootChanged(root: Cell | null) {
|
||||
const oldRoot = this.root;
|
||||
this.root = root;
|
||||
|
||||
|
@ -378,7 +377,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell that represents the possible root.
|
||||
*/
|
||||
isRoot(cell: Cell | null=null): boolean {
|
||||
isRoot(cell: Cell | null = null) {
|
||||
return cell != null && this.root === cell;
|
||||
}
|
||||
|
||||
|
@ -387,7 +386,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell that represents the possible layer.
|
||||
*/
|
||||
isLayer(cell: Cell): boolean {
|
||||
isLayer(cell: Cell) {
|
||||
return this.isRoot(cell.getParent());
|
||||
}
|
||||
|
||||
|
@ -396,7 +395,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell that specifies the cell.
|
||||
*/
|
||||
contains(cell: Cell): boolean {
|
||||
contains(cell: Cell) {
|
||||
return (<Cell>this.root).isAncestor(cell);
|
||||
}
|
||||
|
||||
|
@ -410,10 +409,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} child that specifies the child to be inserted.
|
||||
* @param index Optional integer that specifies the index of the child.
|
||||
*/
|
||||
add(parent: Cell | null,
|
||||
child: Cell | null,
|
||||
index: number | null=null): Cell | null {
|
||||
|
||||
add(parent: Cell | null, child: Cell | null, index: number | null = null) {
|
||||
if (child !== parent && parent != null && child != null) {
|
||||
// Appends the child if no index was specified
|
||||
if (index == null) {
|
||||
|
@ -450,7 +446,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell that specifies the cell that has been added.
|
||||
*/
|
||||
cellAdded(cell: Cell | null): void {
|
||||
cellAdded(cell: Cell | null) {
|
||||
if (cell != null) {
|
||||
// Creates an Id for the cell if not Id exists
|
||||
if (cell.getId() == null && this.createIds) {
|
||||
|
@ -497,7 +493,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell to create the Id for.
|
||||
*/
|
||||
createId(cell: Cell): string {
|
||||
createId(cell: Cell) {
|
||||
const id = this.nextId;
|
||||
this.nextId++;
|
||||
return this.prefix + id + this.postfix;
|
||||
|
@ -507,9 +503,7 @@ class Model extends EventSource {
|
|||
* Updates the parent for all edges that are connected to cell or one of
|
||||
* its descendants using {@link updateEdgeParent}.
|
||||
*/
|
||||
updateEdgeParents(cell: Cell,
|
||||
root: Cell=<Cell>this.getRoot(cell)): void {
|
||||
|
||||
updateEdgeParents(cell: Cell, root: Cell = <Cell>this.getRoot(cell)) {
|
||||
// Updates edges on children first
|
||||
const childCount = cell.getChildCount();
|
||||
|
||||
|
@ -545,9 +539,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} edge that specifies the edge.
|
||||
* @param {Cell} root that represents the current root of the model.
|
||||
*/
|
||||
updateEdgeParent(edge: Cell,
|
||||
root: Cell): void {
|
||||
|
||||
updateEdgeParent(edge: Cell, root: Cell): void {
|
||||
let source = edge.getTerminal(true);
|
||||
let target = edge.getTerminal(false);
|
||||
let cell = null;
|
||||
|
@ -583,7 +575,8 @@ class Model extends EventSource {
|
|||
if (
|
||||
cell != null &&
|
||||
(cell.getParent() !== this.root || cell.isAncestor(edge)) &&
|
||||
edge && edge.getParent() !== cell
|
||||
edge &&
|
||||
edge.getParent() !== cell
|
||||
) {
|
||||
let geo = edge.getGeometry();
|
||||
|
||||
|
@ -651,10 +644,7 @@ class Model extends EventSource {
|
|||
* @param index Optional integer that defines the index of the child
|
||||
* in the parent's child array.
|
||||
*/
|
||||
parentForCellChanged(cell: Cell,
|
||||
parent: Cell | null,
|
||||
index: number): Cell {
|
||||
|
||||
parentForCellChanged(cell: Cell, parent: Cell | null, index: number): Cell {
|
||||
const previous = <Cell>cell.getParent();
|
||||
|
||||
if (parent != null) {
|
||||
|
@ -690,10 +680,7 @@ class Model extends EventSource {
|
|||
* target terminal of the edge.
|
||||
*/
|
||||
// setTerminal(edge: mxCell, terminal: mxCell, isSource: boolean): mxCell;
|
||||
setTerminal(edge: Cell,
|
||||
terminal: Cell | null,
|
||||
isSource: boolean): Cell | null {
|
||||
|
||||
setTerminal(edge: Cell, terminal: Cell | null, isSource: boolean): Cell | null {
|
||||
const terminalChanged = terminal !== edge.getTerminal(isSource);
|
||||
this.execute(new TerminalChange(this, edge, terminal, isSource));
|
||||
|
||||
|
@ -712,10 +699,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} target that specifies the new target terminal.
|
||||
*/
|
||||
// setTerminals(edge: mxCell, source: mxCell, target: mxCell): void;
|
||||
setTerminals(edge: Cell,
|
||||
source: Cell | null,
|
||||
target: Cell | null): void {
|
||||
|
||||
setTerminals(edge: Cell, source: Cell | null, target: Cell | null): void {
|
||||
this.beginUpdate();
|
||||
try {
|
||||
this.setTerminal(edge, source, true);
|
||||
|
@ -735,10 +719,11 @@ class Model extends EventSource {
|
|||
* target terminal of the edge.
|
||||
*/
|
||||
// terminalForCellChanged(edge: mxCell, terminal: mxCell, isSource: boolean): mxCell;
|
||||
terminalForCellChanged(edge: Cell,
|
||||
terminal: Cell | null,
|
||||
isSource: boolean=false): Cell | null {
|
||||
|
||||
terminalForCellChanged(
|
||||
edge: Cell,
|
||||
terminal: Cell | null,
|
||||
isSource: boolean = false
|
||||
): Cell | null {
|
||||
const previous = edge.getTerminal(isSource);
|
||||
if (terminal != null) {
|
||||
terminal.insertEdge(edge, isSource);
|
||||
|
@ -760,10 +745,7 @@ class Model extends EventSource {
|
|||
* @param directed Optional boolean that specifies if the direction of the
|
||||
* edge should be taken into account. Default is false.
|
||||
*/
|
||||
getEdgesBetween(source: Cell,
|
||||
target: Cell,
|
||||
directed: boolean=false): CellArray {
|
||||
|
||||
getEdgesBetween(source: Cell, target: Cell, directed: boolean = false): CellArray {
|
||||
const tmp1 = source.getEdgeCount();
|
||||
const tmp2 = target.getEdgeCount();
|
||||
|
||||
|
@ -803,8 +785,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell whose user object should be changed.
|
||||
* @param value Object that defines the new user object.
|
||||
*/
|
||||
setValue(cell: Cell,
|
||||
value: any): any {
|
||||
setValue(cell: Cell, value: any): any {
|
||||
this.execute(new ValueChange(this, cell, value));
|
||||
return value;
|
||||
}
|
||||
|
@ -827,8 +808,7 @@ class Model extends EventSource {
|
|||
* };
|
||||
* ```
|
||||
*/
|
||||
valueForCellChanged(cell: Cell,
|
||||
value: any): any {
|
||||
valueForCellChanged(cell: Cell, value: any): any {
|
||||
return cell.valueChanged(value);
|
||||
}
|
||||
|
||||
|
@ -840,9 +820,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell whose geometry should be changed.
|
||||
* @param {Geometry} geometry that defines the new geometry.
|
||||
*/
|
||||
setGeometry(cell: Cell,
|
||||
geometry: Geometry): Geometry {
|
||||
|
||||
setGeometry(cell: Cell, geometry: Geometry): Geometry {
|
||||
if (geometry !== cell.getGeometry()) {
|
||||
this.execute(new GeometryChange(this, cell, geometry));
|
||||
}
|
||||
|
@ -853,9 +831,7 @@ class Model extends EventSource {
|
|||
* Inner callback to update the {@link Geometry} of the given {@link Cell} using
|
||||
* <mxCell.setGeometry> and return the previous {@link Geometry}.
|
||||
*/
|
||||
geometryForCellChanged(cell: Cell,
|
||||
geometry: Geometry | null): Geometry | null {
|
||||
|
||||
geometryForCellChanged(cell: Cell, geometry: Geometry | null): Geometry | null {
|
||||
const previous = cell.getGeometry();
|
||||
cell.setGeometry(geometry);
|
||||
return previous;
|
||||
|
@ -869,13 +845,10 @@ class Model extends EventSource {
|
|||
* @param style String of the form [stylename;|key=value;] to specify
|
||||
* the new cell style.
|
||||
*/
|
||||
setStyle(cell: Cell,
|
||||
style: string): string {
|
||||
|
||||
setStyle(cell: Cell, style: string | null) {
|
||||
if (style !== cell.getStyle()) {
|
||||
this.execute(new StyleChange(this, cell, style));
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -886,9 +859,7 @@ class Model extends EventSource {
|
|||
* @param style String of the form [stylename;|key=value;] to specify
|
||||
* the new cell style.
|
||||
*/
|
||||
styleForCellChanged(cell: Cell,
|
||||
style: string | null): string | null {
|
||||
|
||||
styleForCellChanged(cell: Cell, style: string | null) {
|
||||
const previous = cell.getStyle();
|
||||
cell.setStyle(style);
|
||||
return previous;
|
||||
|
@ -901,9 +872,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell whose collapsed state should be changed.
|
||||
* @param collapsed Boolean that specifies the new collpased state.
|
||||
*/
|
||||
setCollapsed(cell: Cell,
|
||||
collapsed: boolean): boolean {
|
||||
|
||||
setCollapsed(cell: Cell, collapsed: boolean): boolean {
|
||||
if (collapsed !== cell.isCollapsed()) {
|
||||
this.execute(new CollapseChange(this, cell, collapsed));
|
||||
}
|
||||
|
@ -918,9 +887,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell that specifies the cell to be updated.
|
||||
* @param collapsed Boolean that specifies the new collpased state.
|
||||
*/
|
||||
collapsedStateForCellChanged(cell: Cell,
|
||||
collapsed: boolean): boolean {
|
||||
|
||||
collapsedStateForCellChanged(cell: Cell, collapsed: boolean): boolean {
|
||||
const previous = cell.isCollapsed();
|
||||
cell.setCollapsed(collapsed);
|
||||
return previous;
|
||||
|
@ -933,9 +900,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell whose visible state should be changed.
|
||||
* @param visible Boolean that specifies the new visible state.
|
||||
*/
|
||||
setVisible(cell: Cell,
|
||||
visible: boolean): boolean {
|
||||
|
||||
setVisible(cell: Cell, visible: boolean): boolean {
|
||||
if (visible !== cell.isVisible()) {
|
||||
this.execute(new VisibleChange(this, cell, visible));
|
||||
}
|
||||
|
@ -950,8 +915,7 @@ class Model extends EventSource {
|
|||
* @param {Cell} cell that specifies the cell to be updated.
|
||||
* @param visible Boolean that specifies the new visible state.
|
||||
*/
|
||||
visibleStateForCellChanged(cell: Cell,
|
||||
visible: boolean): boolean {
|
||||
visibleStateForCellChanged(cell: Cell, visible: boolean): boolean {
|
||||
const previous = cell.isVisible();
|
||||
cell.setVisible(visible);
|
||||
return previous;
|
||||
|
@ -1045,9 +1009,7 @@ class Model extends EventSource {
|
|||
|
||||
if (!this.endingUpdate) {
|
||||
this.endingUpdate = this.updateLevel === 0;
|
||||
this.fireEvent(
|
||||
new EventObject(InternalEvent.END_UPDATE, 'edit', this.currentEdit)
|
||||
);
|
||||
this.fireEvent(new EventObject(InternalEvent.END_UPDATE, 'edit', this.currentEdit));
|
||||
|
||||
try {
|
||||
if (this.endingUpdate && !this.currentEdit.isEmpty()) {
|
||||
|
@ -1073,7 +1035,7 @@ class Model extends EventSource {
|
|||
* @param significant Optional boolean that specifies if the edit to be created is
|
||||
* significant. Default is true.
|
||||
*/
|
||||
createUndoableEdit(significant: boolean=true): UndoableEdit {
|
||||
createUndoableEdit(significant: boolean = true): UndoableEdit {
|
||||
const edit = new UndoableEdit(this, significant);
|
||||
|
||||
edit.notify = () => {
|
||||
|
@ -1100,10 +1062,7 @@ class Model extends EventSource {
|
|||
* source edges.
|
||||
*/
|
||||
// mergeChildren(from: Transactions, to: Transactions, cloneAllEdges?: boolean): void;
|
||||
mergeChildren(from: Cell,
|
||||
to: Cell,
|
||||
cloneAllEdges: boolean=true): void {
|
||||
|
||||
mergeChildren(from: Cell, to: Cell, cloneAllEdges: boolean = true): void {
|
||||
this.beginUpdate();
|
||||
try {
|
||||
const mapping: any = {};
|
||||
|
@ -1140,11 +1099,7 @@ class Model extends EventSource {
|
|||
* that was inserted into this model.
|
||||
*/
|
||||
// mergeChildrenImpl(from: Transactions, to: Transactions, cloneAllEdges: boolean, mapping: any): void;
|
||||
mergeChildrenImpl(from: Cell,
|
||||
to: Cell,
|
||||
cloneAllEdges: boolean,
|
||||
mapping: any={}) {
|
||||
|
||||
mergeChildrenImpl(from: Cell, to: Cell, cloneAllEdges: boolean, mapping: any = {}) {
|
||||
this.beginUpdate();
|
||||
try {
|
||||
const childCount = from.getChildCount();
|
||||
|
@ -1155,9 +1110,7 @@ class Model extends EventSource {
|
|||
if (typeof cell.getId === 'function') {
|
||||
const id: string = <string>cell.getId();
|
||||
let target =
|
||||
id != null && (!cell.isEdge() || !cloneAllEdges)
|
||||
? this.getCell(id)
|
||||
: null;
|
||||
id != null && (!cell.isEdge() || !cloneAllEdges) ? this.getCell(id) : null;
|
||||
|
||||
// Clones and adds the child if no cell exists for the id
|
||||
if (target == null) {
|
||||
|
@ -1198,8 +1151,7 @@ class Model extends EventSource {
|
|||
*
|
||||
* @param {Cell} cell to be cloned.
|
||||
*/
|
||||
cloneCell(cell: Cell | null,
|
||||
includeChildren: boolean): Cell | null {
|
||||
cloneCell(cell: Cell | null, includeChildren: boolean): Cell | null {
|
||||
if (cell != null) {
|
||||
return new CellArray(cell).cloneCells(includeChildren)[0];
|
||||
}
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
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 { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphSelection extends EventSource {
|
||||
constructor(graph: graph) {
|
||||
super();
|
||||
import type GraphCells from '../cell/GraphCells';
|
||||
import type Graph from '../Graph';
|
||||
import type GraphEvents from '../event/GraphEvents';
|
||||
import type EventSource from '../event/EventSource';
|
||||
|
||||
this.graph = graph;
|
||||
this.cells = new CellArray();
|
||||
}
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
'fireEvent' | 'getDefaultParent' | 'getView' | 'getCurrentRoot' | 'getModel'
|
||||
>;
|
||||
type PartialCells = Pick<GraphCells, 'isCellSelectable' | 'getCells'>;
|
||||
type PartialEvents = Pick<GraphEvents, 'isToggleEvent'>;
|
||||
type PartialClass = PartialGraph & PartialCells & PartialEvents & EventSource;
|
||||
|
||||
// @ts-ignore recursive reference error
|
||||
class GraphSelection extends autoImplement<PartialClass>() {
|
||||
// TODO: Document me!!
|
||||
cells: CellArray;
|
||||
cells: CellArray = new CellArray();
|
||||
|
||||
/**
|
||||
* Specifies the resource key for the status message after a long operation.
|
||||
|
@ -39,11 +44,6 @@ class GraphSelection extends EventSource {
|
|||
updatingSelectionResource: string =
|
||||
mxClient.language !== 'none' ? 'updatingSelection' : '';
|
||||
|
||||
/**
|
||||
* Reference to the enclosing {@link graph}.
|
||||
*/
|
||||
graph: graph;
|
||||
|
||||
/**
|
||||
* Specifies if only one selected item at a time is allowed.
|
||||
* Default is false.
|
||||
|
@ -87,17 +87,14 @@ class GraphSelection extends EventSource {
|
|||
/**
|
||||
* Returns true if the given {@link Cell} is selected.
|
||||
*/
|
||||
isSelected(cell: Cell): boolean {
|
||||
if (cell != null) {
|
||||
return this.cells.indexOf(cell) >= 0;
|
||||
}
|
||||
return false;
|
||||
isSelected(cell: Cell) {
|
||||
return this.cells.indexOf(cell) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if no cells are currently selected.
|
||||
*/
|
||||
isEmpty(): boolean {
|
||||
isEmpty() {
|
||||
return this.cells.length === 0;
|
||||
}
|
||||
|
||||
|
@ -105,7 +102,7 @@ class GraphSelection extends EventSource {
|
|||
* Clears the selection and fires a {@link change} event if the selection was not
|
||||
* empty.
|
||||
*/
|
||||
clear(): void {
|
||||
clear() {
|
||||
this.changeSelection(null, this.cells);
|
||||
}
|
||||
|
||||
|
@ -114,10 +111,8 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to be selected.
|
||||
*/
|
||||
setCell(cell: Cell | null): void {
|
||||
if (cell != null) {
|
||||
this.setCells(new CellArray(cell));
|
||||
}
|
||||
setCell(cell: Cell) {
|
||||
this.setCells(new CellArray(cell));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,32 +121,29 @@ class GraphSelection extends EventSource {
|
|||
* @param cells Array of {@link Cell} to be selected.
|
||||
*/
|
||||
setCells(cells: CellArray): void {
|
||||
if (cells != null) {
|
||||
if (this.singleSelection) {
|
||||
cells = new CellArray(<Cell>this.getFirstSelectableCell(cells));
|
||||
}
|
||||
|
||||
const tmp = new CellArray();
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if ((<graph>this.graph).isCellSelectable(cells[i])) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
}
|
||||
this.changeSelection(tmp, this.cells);
|
||||
if (this.singleSelection) {
|
||||
cells = new CellArray(<Cell>this.getFirstSelectableCell(cells));
|
||||
}
|
||||
|
||||
const tmp = new CellArray();
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (this.isCellSelectable(cells[i])) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
}
|
||||
this.changeSelection(tmp, this.cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first selectable cell in the given array of cells.
|
||||
*/
|
||||
getFirstSelectableCell(cells: CellArray): Cell | null {
|
||||
if (cells != null) {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if ((<graph>this.graph).isCellSelectable(cells[i])) {
|
||||
return cells[i];
|
||||
}
|
||||
getFirstSelectableCell(cells: CellArray) {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (this.isCellSelectable(cells[i])) {
|
||||
return cells[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -160,10 +152,8 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to add to the selection.
|
||||
*/
|
||||
addCell(cell: Cell | null = null): void {
|
||||
if (cell != null) {
|
||||
this.addCells(new CellArray(cell));
|
||||
}
|
||||
addCell(cell: Cell) {
|
||||
this.addCells(new CellArray(cell));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,26 +162,24 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cells Array of {@link Cell} to add to the selection.
|
||||
*/
|
||||
addCells(cells: CellArray): void {
|
||||
if (cells != null) {
|
||||
let remove = null;
|
||||
if (this.singleSelection) {
|
||||
remove = this.cells;
|
||||
cells = new CellArray(<Cell>this.getFirstSelectableCell(cells));
|
||||
}
|
||||
addCells(cells: CellArray) {
|
||||
let remove = null;
|
||||
if (this.singleSelection) {
|
||||
remove = this.cells;
|
||||
|
||||
const tmp = new CellArray();
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (
|
||||
!this.isSelected(cells[i]) &&
|
||||
(<graph>this.graph).isCellSelectable(cells[i])
|
||||
) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
}
|
||||
const selectableCell = this.getFirstSelectableCell(cells);
|
||||
|
||||
this.changeSelection(tmp, remove);
|
||||
cells = selectableCell ? new CellArray(selectableCell) : new CellArray();
|
||||
}
|
||||
|
||||
const tmp = new CellArray();
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (!this.isSelected(cells[i]) && this.isCellSelectable(cells[i])) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.changeSelection(tmp, remove);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,10 +188,8 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to remove from the selection.
|
||||
*/
|
||||
removeCell(cell: Cell | null = null): void {
|
||||
if (cell != null) {
|
||||
this.removeCells(new CellArray(cell));
|
||||
}
|
||||
removeCell(cell: Cell) {
|
||||
this.removeCells(new CellArray(cell));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,16 +198,16 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cells {@link mxCell}s to remove from the selection.
|
||||
*/
|
||||
removeCells(cells: CellArray | null = null): void {
|
||||
if (cells != null) {
|
||||
const tmp = new CellArray();
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (this.isSelected(cells[i])) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
removeCells(cells: CellArray) {
|
||||
const tmp = new CellArray();
|
||||
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
if (this.isSelected(cells[i])) {
|
||||
tmp.push(cells[i]);
|
||||
}
|
||||
this.changeSelection(null, tmp);
|
||||
}
|
||||
|
||||
this.changeSelection(null, tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,13 +216,10 @@ 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) {
|
||||
if (
|
||||
(added != null && added.length > 0 && added[0] != null) ||
|
||||
(removed != null && removed.length > 0 && removed[0] != null)
|
||||
(added && added.length > 0 && added[0]) ||
|
||||
(removed && removed.length > 0 && removed[0])
|
||||
) {
|
||||
const change = new SelectionChange(
|
||||
this,
|
||||
|
@ -258,8 +241,8 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to add to the selection.
|
||||
*/
|
||||
cellAdded(cell: Cell): void {
|
||||
if (cell != null && !this.isSelected(cell)) {
|
||||
cellAdded(cell: Cell) {
|
||||
if (!this.isSelected(cell)) {
|
||||
this.cells.push(cell);
|
||||
}
|
||||
}
|
||||
|
@ -270,12 +253,10 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to remove from the selection.
|
||||
*/
|
||||
cellRemoved(cell: Cell): void {
|
||||
if (cell != null) {
|
||||
const index = this.cells.indexOf(cell);
|
||||
if (index >= 0) {
|
||||
this.cells.splice(index, 1);
|
||||
}
|
||||
cellRemoved(cell: Cell) {
|
||||
const index = this.cells.indexOf(cell);
|
||||
if (index >= 0) {
|
||||
this.cells.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,42 +269,42 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} for which the selection state should be returned.
|
||||
*/
|
||||
isCellSelected(cell: Cell): boolean {
|
||||
isCellSelected(cell: Cell) {
|
||||
return this.isSelected(cell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selection is empty.
|
||||
*/
|
||||
isSelectionEmpty(): boolean {
|
||||
isSelectionEmpty() {
|
||||
return this.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the selection using {@link mxGraphSelectionModel.clear}.
|
||||
*/
|
||||
clearSelection(): void {
|
||||
return this.clear();
|
||||
clearSelection() {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of selected cells.
|
||||
*/
|
||||
getSelectionCount(): number {
|
||||
getSelectionCount() {
|
||||
return this.cells.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first cell from the array of selected {@link Cell}.
|
||||
*/
|
||||
getSelectionCell(): Cell {
|
||||
getSelectionCell() {
|
||||
return this.cells[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of selected {@link Cell}.
|
||||
*/
|
||||
getSelectionCells(): CellArray {
|
||||
getSelectionCells() {
|
||||
return this.cells.slice();
|
||||
}
|
||||
|
||||
|
@ -332,7 +313,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to be selected.
|
||||
*/
|
||||
setSelectionCell(cell: Cell | null): void {
|
||||
setSelectionCell(cell: Cell) {
|
||||
this.setCell(cell);
|
||||
}
|
||||
|
||||
|
@ -341,7 +322,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cells Array of {@link Cell} to be selected.
|
||||
*/
|
||||
setSelectionCells(cells: CellArray): void {
|
||||
setSelectionCells(cells: CellArray) {
|
||||
this.setCells(cells);
|
||||
}
|
||||
|
||||
|
@ -350,7 +331,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to be add to the selection.
|
||||
*/
|
||||
addSelectionCell(cell: Cell): void {
|
||||
addSelectionCell(cell: Cell) {
|
||||
this.addCell(cell);
|
||||
}
|
||||
|
||||
|
@ -359,7 +340,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cells Array of {@link Cell} to be added to the selection.
|
||||
*/
|
||||
addSelectionCells(cells: CellArray): void {
|
||||
addSelectionCells(cells: CellArray) {
|
||||
this.addCells(cells);
|
||||
}
|
||||
|
||||
|
@ -368,7 +349,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cell {@link mxCell} to be removed from the selection.
|
||||
*/
|
||||
removeSelectionCell(cell: Cell): void {
|
||||
removeSelectionCell(cell: Cell) {
|
||||
this.removeCell(cell);
|
||||
}
|
||||
|
||||
|
@ -377,7 +358,7 @@ class GraphSelection extends EventSource {
|
|||
*
|
||||
* @param cells Array of {@link Cell} to be removed from the selection.
|
||||
*/
|
||||
removeSelectionCells(cells: CellArray): void {
|
||||
removeSelectionCells(cells: CellArray) {
|
||||
this.removeCells(cells);
|
||||
}
|
||||
|
||||
|
@ -389,8 +370,8 @@ class GraphSelection extends EventSource {
|
|||
* @param evt Mouseevent that triggered the selection.
|
||||
*/
|
||||
// selectRegion(rect: mxRectangle, evt: Event): mxCellArray;
|
||||
selectRegion(rect: Rectangle, evt: InternalMouseEvent): CellArray | null {
|
||||
const cells = this.graph.getCells(rect.x, rect.y, rect.width, rect.height);
|
||||
selectRegion(rect: Rectangle, evt: MouseEvent) {
|
||||
const cells = this.getCells(rect.x, rect.y, rect.width, rect.height);
|
||||
this.selectCellsForEvent(cells, evt);
|
||||
return cells;
|
||||
}
|
||||
|
@ -398,28 +379,28 @@ class GraphSelection extends EventSource {
|
|||
/**
|
||||
* Selects the next cell.
|
||||
*/
|
||||
selectNextCell(): void {
|
||||
selectNextCell() {
|
||||
this.selectCell(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the previous cell.
|
||||
*/
|
||||
selectPreviousCell(): void {
|
||||
selectPreviousCell() {
|
||||
this.selectCell();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the parent cell.
|
||||
*/
|
||||
selectParentCell(): void {
|
||||
selectParentCell() {
|
||||
this.selectCell(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the first child cell.
|
||||
*/
|
||||
selectChildCell(): void {
|
||||
selectChildCell() {
|
||||
this.selectCell(false, false, true);
|
||||
}
|
||||
|
||||
|
@ -431,36 +412,29 @@ class GraphSelection extends EventSource {
|
|||
* @param isParent Boolean indicating if the parent cell should be selected.
|
||||
* @param isChild Boolean indicating if the first child cell should be selected.
|
||||
*/
|
||||
selectCell(
|
||||
isNext: boolean = false,
|
||||
isParent: boolean = false,
|
||||
isChild: boolean = false
|
||||
): void {
|
||||
selectCell(isNext = false, isParent = false, isChild = false) {
|
||||
const cell = this.cells.length > 0 ? this.cells[0] : null;
|
||||
|
||||
if (this.cells.length > 1) {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
const parent = <Cell>(
|
||||
(cell != null ? cell.getParent() : this.graph.getDefaultParent())
|
||||
);
|
||||
|
||||
const parent = cell ? cell.getParent() : this.getDefaultParent();
|
||||
const childCount = parent.getChildCount();
|
||||
|
||||
if (cell == null && childCount > 0) {
|
||||
if (!cell && childCount > 0) {
|
||||
const child = parent.getChildAt(0);
|
||||
this.setSelectionCell(child);
|
||||
} else if (
|
||||
parent &&
|
||||
(cell == null || isParent) &&
|
||||
this.graph.getView().getState(parent) != null &&
|
||||
parent.getGeometry() != null
|
||||
(!cell || isParent) &&
|
||||
this.getView().getState(parent) &&
|
||||
parent.getGeometry()
|
||||
) {
|
||||
if (this.graph.getCurrentRoot() != parent) {
|
||||
if (this.getCurrentRoot() !== parent) {
|
||||
this.setSelectionCell(parent);
|
||||
}
|
||||
} else if (cell != null && isChild) {
|
||||
} else if (cell && isChild) {
|
||||
const tmp = cell.getChildCount();
|
||||
|
||||
if (tmp > 0) {
|
||||
|
@ -468,7 +442,7 @@ class GraphSelection extends EventSource {
|
|||
this.setSelectionCell(child);
|
||||
}
|
||||
} else if (childCount > 0) {
|
||||
let i = (<Cell>parent).getIndex(cell);
|
||||
let i = parent.getIndex(cell);
|
||||
|
||||
if (isNext) {
|
||||
i++;
|
||||
|
@ -493,32 +467,27 @@ class GraphSelection extends EventSource {
|
|||
* @param descendants Optional boolean specifying whether all descendants should be
|
||||
* selected. Default is `false`.
|
||||
*/
|
||||
selectAll(
|
||||
parent: Cell = this.graph.getDefaultParent(),
|
||||
descendants: boolean = false
|
||||
): void {
|
||||
selectAll(parent: Cell = this.getDefaultParent(), descendants: boolean = false) {
|
||||
const cells = descendants
|
||||
? parent.filterDescendants((cell: Cell) => {
|
||||
return cell != parent && this.graph.getView().getState(cell) != null;
|
||||
return cell !== parent && !!this.getView().getState(cell);
|
||||
})
|
||||
: parent.getChildren();
|
||||
|
||||
if (cells != null) {
|
||||
this.setSelectionCells(cells);
|
||||
}
|
||||
this.setSelectionCells(cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all vertices inside the given parent or the default parent.
|
||||
*/
|
||||
selectVertices(parent: Cell, selectGroups: boolean = false): void {
|
||||
selectVertices(parent: Cell, selectGroups = false) {
|
||||
this.selectCells(true, false, parent, selectGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all vertices inside the given parent or the default parent.
|
||||
*/
|
||||
selectEdges(parent: Cell): void {
|
||||
selectEdges(parent: Cell) {
|
||||
this.selectCells(false, true, parent);
|
||||
}
|
||||
|
||||
|
@ -536,27 +505,25 @@ class GraphSelection extends EventSource {
|
|||
* selected. Default is `false`.
|
||||
*/
|
||||
selectCells(
|
||||
vertices: boolean = false,
|
||||
edges: boolean = false,
|
||||
parent: Cell = this.graph.getDefaultParent(),
|
||||
selectGroups: boolean = false
|
||||
): void {
|
||||
vertices = false,
|
||||
edges = false,
|
||||
parent: Cell = this.getDefaultParent(),
|
||||
selectGroups = false
|
||||
) {
|
||||
const filter = (cell: Cell) => {
|
||||
return (
|
||||
this.graph.getView().getState(cell) != null &&
|
||||
(((selectGroups || cell.getChildCount() == 0) &&
|
||||
this.getView().getState(cell) &&
|
||||
(((selectGroups || cell.getChildCount() === 0) &&
|
||||
cell.isVertex() &&
|
||||
vertices &&
|
||||
cell.getParent() &&
|
||||
!(<Cell>cell.getParent()).isEdge()) ||
|
||||
!cell.getParent().isEdge()) ||
|
||||
(cell.isEdge() && edges))
|
||||
);
|
||||
};
|
||||
|
||||
const cells = parent.filterDescendants(filter);
|
||||
if (cells != null) {
|
||||
this.setSelectionCells(cells);
|
||||
}
|
||||
this.setSelectionCells(cells);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -567,16 +534,16 @@ class GraphSelection extends EventSource {
|
|||
* @param cell {@link mxCell} to be selected.
|
||||
* @param evt Optional mouseevent that triggered the selection.
|
||||
*/
|
||||
selectCellForEvent(cell: Cell, evt: InternalMouseEvent): void {
|
||||
selectCellForEvent(cell: Cell, evt: MouseEvent) {
|
||||
const isSelected = this.isCellSelected(cell);
|
||||
|
||||
if (this.graph.isToggleEvent(evt)) {
|
||||
if (this.isToggleEvent(evt)) {
|
||||
if (isSelected) {
|
||||
this.removeSelectionCell(cell);
|
||||
} else {
|
||||
this.addSelectionCell(cell);
|
||||
}
|
||||
} else if (!isSelected || this.getSelectionCount() != 1) {
|
||||
} else if (!isSelected || this.getSelectionCount() !== 1) {
|
||||
this.setSelectionCell(cell);
|
||||
}
|
||||
}
|
||||
|
@ -589,8 +556,8 @@ class GraphSelection extends EventSource {
|
|||
* @param cells Array of {@link Cell} to be selected.
|
||||
* @param evt Optional mouseevent that triggered the selection.
|
||||
*/
|
||||
selectCellsForEvent(cells: CellArray, evt: InternalMouseEvent): void {
|
||||
if (this.graph.isToggleEvent(evt)) {
|
||||
selectCellsForEvent(cells: CellArray, evt: MouseEvent) {
|
||||
if (this.isToggleEvent(evt)) {
|
||||
this.addSelectionCells(cells);
|
||||
} else {
|
||||
this.setSelectionCells(cells);
|
||||
|
@ -600,16 +567,17 @@ class GraphSelection extends EventSource {
|
|||
/**
|
||||
* Returns true if any sibling of the given cell is selected.
|
||||
*/
|
||||
isSiblingSelected(cell: Cell): boolean {
|
||||
const parent = <Cell>cell.getParent();
|
||||
isSiblingSelected(cell: Cell) {
|
||||
const parent = cell.getParent();
|
||||
const childCount = parent.getChildCount();
|
||||
|
||||
for (let i = 0; i < childCount; i += 1) {
|
||||
const child = <Cell>parent.getChildAt(i);
|
||||
const child = parent.getChildAt(i);
|
||||
if (cell !== child && this.isCellSelected(child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -628,10 +596,7 @@ class GraphSelection extends EventSource {
|
|||
* change should be ignored.
|
||||
*
|
||||
*/
|
||||
getSelectionCellsForChanges(
|
||||
changes: any[],
|
||||
ignoreFn: Function | null = null
|
||||
): CellArray {
|
||||
getSelectionCellsForChanges(changes: any[], ignoreFn: Function | null = null) {
|
||||
const dict = new Dictionary();
|
||||
const cells: CellArray = new CellArray();
|
||||
|
||||
|
@ -644,7 +609,7 @@ class GraphSelection extends EventSource {
|
|||
const childCount = cell.getChildCount();
|
||||
|
||||
for (let i = 0; i < childCount; i += 1) {
|
||||
addCell(<Cell>cell.getChildAt(i));
|
||||
addCell(cell.getChildAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,16 +618,16 @@ 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 || !ignoreFn(change))) {
|
||||
let cell = null;
|
||||
|
||||
if (change instanceof ChildChange) {
|
||||
cell = change.child;
|
||||
} else if (change.cell != null && change.cell instanceof Cell) {
|
||||
} else if (change.cell && change.cell instanceof Cell) {
|
||||
cell = change.cell;
|
||||
}
|
||||
|
||||
if (cell != null) {
|
||||
if (cell) {
|
||||
addCell(cell);
|
||||
}
|
||||
}
|
||||
|
@ -673,7 +638,7 @@ class GraphSelection extends EventSource {
|
|||
/**
|
||||
* Removes selection cells that are not in the model from the selection.
|
||||
*/
|
||||
updateSelection(): void {
|
||||
updateSelection() {
|
||||
const cells = this.getSelectionCells();
|
||||
const removed = new CellArray();
|
||||
|
||||
|
@ -683,7 +648,7 @@ class GraphSelection extends EventSource {
|
|||
} else {
|
||||
let par = cell.getParent();
|
||||
|
||||
while (par != null && par !== this.view.currentRoot) {
|
||||
while (par && par !== this.getView().currentRoot) {
|
||||
if (par.isCollapsed() || !par.isVisible()) {
|
||||
removed.push(cell);
|
||||
break;
|
||||
|
@ -693,7 +658,7 @@ class GraphSelection extends EventSource {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.selection.removeSelectionCells(removed);
|
||||
this.removeSelectionCells(removed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,28 +18,32 @@ import Rectangle from '../geometry/Rectangle';
|
|||
import { isAltDown, isMultiTouchEvent } from '../../util/EventUtils';
|
||||
import { clearSelection } from '../../util/DomUtils';
|
||||
import Graph from '../Graph';
|
||||
import { GraphPlugin } from '../../types';
|
||||
import EventObject from '../event/EventObject';
|
||||
|
||||
/**
|
||||
* Event handler that selects rectangular regions.
|
||||
* This is not built-into [mxGraph].
|
||||
* To enable rubberband selection in a graph, use the following code.
|
||||
*/
|
||||
class RubberBand {
|
||||
forceRubberbandHandler: Function;
|
||||
panHandler: Function;
|
||||
gestureHandler: Function;
|
||||
graph: Graph;
|
||||
class RubberBand implements GraphPlugin {
|
||||
forceRubberbandHandler?: Function;
|
||||
panHandler?: Function;
|
||||
gestureHandler?: Function;
|
||||
graph?: Graph;
|
||||
first: Point | null = null;
|
||||
destroyed: boolean = false;
|
||||
dragHandler?: Function;
|
||||
dropHandler?: Function;
|
||||
|
||||
constructor(graph: Graph) {
|
||||
constructor() {}
|
||||
|
||||
onInit(graph: Graph) {
|
||||
this.graph = graph;
|
||||
this.graph.addMouseListener(this);
|
||||
|
||||
// Handles force rubberband event
|
||||
this.forceRubberbandHandler = (sender, evt) => {
|
||||
this.forceRubberbandHandler = (sender: any, evt: EventObject) => {
|
||||
const evtName = evt.getProperty('eventName');
|
||||
const me = evt.getProperty('event');
|
||||
|
||||
|
@ -379,7 +383,7 @@ class RubberBand {
|
|||
* normally not need to be called, it is called automatically when the
|
||||
* window unloads.
|
||||
*/
|
||||
destroy() {
|
||||
onDestroy() {
|
||||
if (!this.destroyed) {
|
||||
this.destroyed = true;
|
||||
this.graph.removeMouseListener(this);
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import Point from "../geometry/Point";
|
||||
import Rectangle from "../geometry/Rectangle";
|
||||
import Graph from '../Graph';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
import Point from '../geometry/Point';
|
||||
import Rectangle from '../geometry/Rectangle';
|
||||
|
||||
class GraphSnap {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../Graph';
|
||||
|
||||
type PartialGraph = Pick<Graph, 'getView'>;
|
||||
type PartialClass = PartialGraph;
|
||||
|
||||
class GraphSnap extends autoImplement<PartialClass>() {
|
||||
// TODO: Document me!
|
||||
tolerance: number | null = null;
|
||||
graph: Graph;
|
||||
tolerance: number = 0;
|
||||
|
||||
/**
|
||||
* Specifies the grid size.
|
||||
* @default 10
|
||||
*/
|
||||
gridSize: number = 10;
|
||||
gridSize = 10;
|
||||
|
||||
/**
|
||||
* Specifies if the grid is enabled. This is used in {@link snap}.
|
||||
* @default true
|
||||
*/
|
||||
gridEnabled: boolean = true;
|
||||
gridEnabled = true;
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Graph display
|
||||
|
@ -32,7 +32,7 @@ class GraphSnap {
|
|||
*
|
||||
* @param value Numeric value to be snapped to the grid.
|
||||
*/
|
||||
snap(value: number): number {
|
||||
snap(value: number) {
|
||||
if (this.gridEnabled) {
|
||||
value = Math.round(value / this.gridSize) * this.gridSize;
|
||||
}
|
||||
|
@ -47,12 +47,12 @@ class GraphSnap {
|
|||
snapDelta(
|
||||
delta: Point,
|
||||
bounds: Rectangle,
|
||||
ignoreGrid: boolean = false,
|
||||
ignoreHorizontal: boolean = false,
|
||||
ignoreVertical: boolean = false
|
||||
): Point {
|
||||
const t = this.graph.view.translate;
|
||||
const s = this.graph.view.scale;
|
||||
ignoreGrid = false,
|
||||
ignoreHorizontal = false,
|
||||
ignoreVertical = false
|
||||
) {
|
||||
const t = this.getView().translate;
|
||||
const s = this.getView().scale;
|
||||
|
||||
if (!ignoreGrid && this.gridEnabled) {
|
||||
const tol = this.gridSize * s * 0.5;
|
||||
|
@ -109,7 +109,7 @@ class GraphSnap {
|
|||
/**
|
||||
* Returns {@link gridEnabled} as a boolean.
|
||||
*/
|
||||
isGridEnabled(): boolean {
|
||||
isGridEnabled() {
|
||||
return this.gridEnabled;
|
||||
}
|
||||
|
||||
|
@ -118,36 +118,35 @@ class GraphSnap {
|
|||
*
|
||||
* @param value Boolean indicating if the grid should be enabled.
|
||||
*/
|
||||
setGridEnabled(value: boolean): void {
|
||||
setGridEnabled(value: boolean) {
|
||||
this.gridEnabled = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@link gridSize}.
|
||||
*/
|
||||
getGridSize(): number {
|
||||
getGridSize() {
|
||||
return this.gridSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link gridSize}.
|
||||
*/
|
||||
setGridSize(value: number): void {
|
||||
setGridSize(value: number) {
|
||||
this.gridSize = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link tolerance}.
|
||||
*/
|
||||
getTolerance(): number | null {
|
||||
getTolerance() {
|
||||
return this.tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link tolerance}.
|
||||
*/
|
||||
setTolerance(value: number): void {
|
||||
setTolerance(value: number) {
|
||||
this.tolerance = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import {Style} from "util";
|
||||
|
||||
class StyleMap {
|
||||
class StyleMap implements Record<string, any> {
|
||||
defaultVertex?: StyleMap;
|
||||
defaultEdge?: StyleMap;
|
||||
|
||||
|
@ -160,7 +158,6 @@ class StyleMap {
|
|||
*/
|
||||
exitY: any;
|
||||
|
||||
|
||||
/**
|
||||
* Variable: STYLE_EXIT_DX
|
||||
*
|
||||
|
|
|
@ -10,12 +10,13 @@ import {
|
|||
ARROW_CLASSIC,
|
||||
NONE,
|
||||
SHAPE_CONNECTOR,
|
||||
SHAPE_RECTANGLE
|
||||
SHAPE_RECTANGLE,
|
||||
} from '../../util/Constants';
|
||||
import Perimeter from './Perimeter';
|
||||
import utils from '../../util/Utils';
|
||||
import { isNumeric } from '../../util/Utils';
|
||||
import { clone } from '../../util/CloneUtils';
|
||||
import StyleMap from "./StyleMap";
|
||||
|
||||
import type { CellStateStyles } from '../../types';
|
||||
|
||||
/**
|
||||
* @class Stylesheet
|
||||
|
@ -68,7 +69,7 @@ import StyleMap from "./StyleMap";
|
|||
*/
|
||||
class Stylesheet {
|
||||
constructor() {
|
||||
this.styles = new StyleMap();
|
||||
this.styles = {} as CellStateStyles;
|
||||
|
||||
this.putDefaultVertexStyle(this.createDefaultVertexStyle());
|
||||
this.putDefaultEdgeStyle(this.createDefaultEdgeStyle());
|
||||
|
@ -78,13 +79,13 @@ class Stylesheet {
|
|||
* Maps from names to cell styles. Each cell style is a map of key,
|
||||
* value pairs.
|
||||
*/
|
||||
styles: StyleMap;
|
||||
styles: CellStateStyles;
|
||||
|
||||
/**
|
||||
* Creates and returns the default vertex style.
|
||||
*/
|
||||
createDefaultVertexStyle(): StyleMap {
|
||||
const style = new StyleMap();
|
||||
createDefaultVertexStyle() {
|
||||
const style = {} as CellStateStyles;
|
||||
style.shape = SHAPE_RECTANGLE;
|
||||
style.perimeter = Perimeter.RectanglePerimeter;
|
||||
style.verticalAlign = ALIGN_MIDDLE;
|
||||
|
@ -98,8 +99,8 @@ class Stylesheet {
|
|||
/**
|
||||
* Creates and returns the default edge style.
|
||||
*/
|
||||
createDefaultEdgeStyle(): StyleMap {
|
||||
const style = new StyleMap();
|
||||
createDefaultEdgeStyle() {
|
||||
const style = {} as CellStateStyles;
|
||||
style.shape = SHAPE_CONNECTOR;
|
||||
style.endArrow = ARROW_CLASSIC;
|
||||
style.verticalAlign = ALIGN_MIDDLE;
|
||||
|
@ -114,29 +115,29 @@ class Stylesheet {
|
|||
* stylename.
|
||||
* @param style Key, value pairs that define the style.
|
||||
*/
|
||||
putDefaultVertexStyle(style: StyleMap): void {
|
||||
putDefaultVertexStyle(style: CellStateStyles) {
|
||||
this.putCellStyle('defaultVertex', style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default style for edges using defaultEdge as the stylename.
|
||||
*/
|
||||
putDefaultEdgeStyle(style: StyleMap): void {
|
||||
putDefaultEdgeStyle(style: CellStateStyles) {
|
||||
this.putCellStyle('defaultEdge', style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default style for vertices.
|
||||
*/
|
||||
getDefaultVertexStyle(): StyleMap {
|
||||
return <StyleMap>this.styles.defaultVertex;
|
||||
getDefaultVertexStyle() {
|
||||
return this.styles.defaultVertex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default style for edges.
|
||||
*/
|
||||
getDefaultEdgeStyle(): StyleMap {
|
||||
return <StyleMap>this.styles.defaultEdge;
|
||||
getDefaultEdgeStyle() {
|
||||
return this.styles.defaultEdge;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,8 +173,11 @@ class Stylesheet {
|
|||
* @param name Name for the style to be stored.
|
||||
* @param style Key, value pairs that define the style.
|
||||
*/
|
||||
putCellStyle(name: string, style: StyleMap): void {
|
||||
this.styles[name] = style;
|
||||
putCellStyle(
|
||||
name: keyof CellStateStyles,
|
||||
style: CellStateStyles[keyof CellStateStyles]
|
||||
) {
|
||||
(this.styles[name] as any) = style;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,18 +187,16 @@ class Stylesheet {
|
|||
* @param name String of the form [(stylename|key=value);] that represents the style.
|
||||
* @param defaultStyle Default style to be returned if no style can be found.
|
||||
*/
|
||||
getCellStyle(name: string,
|
||||
defaultStyle: StyleMap=new StyleMap()): StyleMap {
|
||||
|
||||
getCellStyle(name: string, defaultStyle: CellStateStyles) {
|
||||
let style = defaultStyle;
|
||||
|
||||
if (name != null && name.length > 0) {
|
||||
if (name.length > 0) {
|
||||
const pairs = name.split(';');
|
||||
|
||||
if (style != null && name.charAt(0) !== ';') {
|
||||
if (style && name.charAt(0) !== ';') {
|
||||
style = clone(style);
|
||||
} else {
|
||||
style = new StyleMap();
|
||||
style = {} as CellStateStyles;
|
||||
}
|
||||
|
||||
// Parses each key, value pair into the existing style
|
||||
|
@ -202,23 +204,24 @@ class Stylesheet {
|
|||
const pos = tmp.indexOf('=');
|
||||
|
||||
if (pos >= 0) {
|
||||
const key = tmp.substring(0, pos);
|
||||
const key = tmp.substring(0, pos) as keyof CellStateStyles;
|
||||
const value = tmp.substring(pos + 1);
|
||||
|
||||
if (value === NONE) {
|
||||
delete style[key];
|
||||
} else if (utils.isNumeric(value)) {
|
||||
style[key] = parseFloat(value);
|
||||
} else if (isNumeric(value)) {
|
||||
(style[key] as any) = parseFloat(value);
|
||||
} else {
|
||||
style[key] = value;
|
||||
(style[key] as any) = value;
|
||||
}
|
||||
} else {
|
||||
// Merges the entries from a named style
|
||||
const tmpStyle = this.styles[tmp];
|
||||
const tmpStyle = this.styles[tmp as keyof CellStateStyles] as CellStateStyles;
|
||||
|
||||
if (tmpStyle != null) {
|
||||
if (tmpStyle && typeof tmpStyle === 'object') {
|
||||
for (const key in tmpStyle) {
|
||||
style[key] = tmpStyle[key];
|
||||
const k = key as keyof CellStateStyles;
|
||||
(style[k] as any) = tmpStyle[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,4 +232,3 @@ class Stylesheet {
|
|||
}
|
||||
|
||||
export default Stylesheet;
|
||||
// import('../../../serialization/mxStylesheetCodec');
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import CellArray from "../cell/datatypes/CellArray";
|
||||
import Cell from "../cell/datatypes/Cell";
|
||||
import Dictionary from "../../util/Dictionary";
|
||||
import Graph from '../Graph';
|
||||
import CellArray from '../cell/datatypes/CellArray';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import Dictionary from '../../util/Dictionary';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphTerminal {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../Graph';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<Graph, 'getView'>;
|
||||
type PartialClass = PartialGraph;
|
||||
|
||||
class GraphTerminal extends autoImplement<PartialClass>() {
|
||||
/*****************************************************************************
|
||||
* Group: Graph behaviour
|
||||
*****************************************************************************/
|
||||
|
@ -24,7 +23,7 @@ class GraphTerminal {
|
|||
* @param cell {@link mxCell} whose terminal point should be moved.
|
||||
* @param source Boolean indicating if the source or target terminal should be moved.
|
||||
*/
|
||||
isTerminalPointMovable(cell: Cell, source: boolean): boolean {
|
||||
isTerminalPointMovable(cell: Cell, source: boolean) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -48,48 +47,36 @@ class GraphTerminal {
|
|||
getOpposites(
|
||||
edges: CellArray,
|
||||
terminal: Cell | null = null,
|
||||
sources: boolean = true,
|
||||
targets: boolean = true
|
||||
sources = true,
|
||||
targets = true
|
||||
): CellArray {
|
||||
const terminals = new CellArray();
|
||||
|
||||
// Fast lookup to avoid duplicates in terminals array
|
||||
const dict = new Dictionary();
|
||||
const dict = new Dictionary<Cell, boolean>();
|
||||
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
const state = this.graph.view.getState(edges[i]);
|
||||
const state = this.getView().getState(edges[i]);
|
||||
|
||||
const source =
|
||||
state != null
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.graph.view.getVisibleTerminal(edges[i], true);
|
||||
const target =
|
||||
state != null
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.graph.view.getVisibleTerminal(edges[i], false);
|
||||
const source = state
|
||||
? state.getVisibleTerminal(true)
|
||||
: this.getView().getVisibleTerminal(edges[i], true);
|
||||
const target = state
|
||||
? state.getVisibleTerminal(false)
|
||||
: this.getView().getVisibleTerminal(edges[i], false);
|
||||
|
||||
// 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 && target !== terminal && targets) {
|
||||
if (!dict.get(target)) {
|
||||
dict.put(target, true);
|
||||
terminals.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the terminal is the taget of the edge and if the
|
||||
// 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 && source !== terminal && sources) {
|
||||
if (!dict.get(source)) {
|
||||
dict.put(source, true);
|
||||
terminals.push(source);
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import CellState from "../cell/datatypes/CellState";
|
||||
import {htmlEntities} from "../../util/StringUtils";
|
||||
import Resources from "../../util/Resources";
|
||||
import Shape from "../geometry/shape/Shape";
|
||||
import SelectionCellsHandler from "../selection/SelectionCellsHandler";
|
||||
import Cell from "../cell/datatypes/Cell";
|
||||
import TooltipHandler from "./TooltipHandler";
|
||||
import Graph from '../Graph';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import { htmlEntities } from '../../util/StringUtils';
|
||||
import Resources from '../../util/Resources';
|
||||
import Shape from '../geometry/shape/Shape';
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphTooltip {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
import type Graph from '../Graph';
|
||||
import type GraphFolding from '../folding/GraphFolding';
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
'getSelectionCellsHandler' | 'convertValueToString' | 'getTooltipHandler'
|
||||
>;
|
||||
type PartialFolding = Pick<GraphFolding, 'getCollapseExpandResource'>;
|
||||
type PartialClass = PartialGraph & PartialFolding;
|
||||
|
||||
class GraphTooltip extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* Returns the string or DOM node that represents the tooltip for the given
|
||||
* state, node and coordinate pair. This implementation checks if the given
|
||||
|
@ -29,58 +31,38 @@ class GraphTooltip {
|
|||
* @param x X-coordinate of the mouse.
|
||||
* @param y Y-coordinate of the mouse.
|
||||
*/
|
||||
getTooltip(
|
||||
state: CellState,
|
||||
node: HTMLElement,
|
||||
x: number,
|
||||
y: number
|
||||
): string | null {
|
||||
let tip: string | null = null;
|
||||
getTooltip(state: CellState, node: HTMLElement | SVGElement, x: number, y: number) {
|
||||
let tip: HTMLElement | string | null = null;
|
||||
|
||||
if (state != null) {
|
||||
// Checks if the mouse is over the folding icon
|
||||
if (
|
||||
state.control != null &&
|
||||
// @ts-ignore
|
||||
(node === state.control.node || node.parentNode === state.control.node)
|
||||
) {
|
||||
tip = this.graph.collapseExpandResource;
|
||||
tip = htmlEntities(Resources.get(tip) || tip, true).replace(
|
||||
/\\n/g,
|
||||
'<br>'
|
||||
);
|
||||
}
|
||||
// Checks if the mouse is over the folding icon
|
||||
if (
|
||||
state.control &&
|
||||
(node === state.control.node || node.parentNode === state.control.node)
|
||||
) {
|
||||
tip = this.getCollapseExpandResource();
|
||||
tip = htmlEntities(Resources.get(tip) || tip, true).replace(/\\n/g, '<br>');
|
||||
}
|
||||
|
||||
if (tip == null && state.overlays != null) {
|
||||
state.overlays.visit((id: string, shape: Shape) => {
|
||||
// LATER: Exit loop if tip is not null
|
||||
if (
|
||||
tip == null &&
|
||||
// @ts-ignore
|
||||
(node === shape.node || node.parentNode === shape.node)
|
||||
) {
|
||||
// @ts-ignore
|
||||
tip = shape.overlay.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (tip == null) {
|
||||
const handler = (<SelectionCellsHandler>(
|
||||
this.graph.selectionCellsHandler
|
||||
)).getHandler(<Cell>state.cell);
|
||||
if (
|
||||
handler != null &&
|
||||
typeof handler.getTooltipForNode === 'function'
|
||||
) {
|
||||
tip = handler.getTooltipForNode(node);
|
||||
if (!tip && state.overlays) {
|
||||
state.overlays.visit((id: string, shape: Shape) => {
|
||||
// LATER: Exit loop if tip is not null
|
||||
if (!tip && (node === shape.node || node.parentNode === shape.node)) {
|
||||
tip = shape.overlay ? shape.overlay.toString() ?? null : null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (tip == null) {
|
||||
tip = this.getTooltipForCell(<Cell>state.cell);
|
||||
if (!tip) {
|
||||
const handler = this.getSelectionCellsHandler().getHandler(state.cell);
|
||||
if (handler && typeof handler.getTooltipForNode === 'function') {
|
||||
tip = handler.getTooltipForNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tip) {
|
||||
tip = this.getTooltipForCell(state.cell);
|
||||
}
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
||||
|
@ -102,15 +84,16 @@ class GraphTooltip {
|
|||
*
|
||||
* @param cell {@link mxCell} whose tooltip should be returned.
|
||||
*/
|
||||
getTooltipForCell(cell: Cell): string | null {
|
||||
getTooltipForCell(cell: Cell) {
|
||||
let tip = null;
|
||||
|
||||
if (cell != null && 'getTooltip' in cell) {
|
||||
// @ts-ignore
|
||||
if (cell && 'getTooltip' in cell) {
|
||||
// @ts-ignore getTooltip() must exists.
|
||||
tip = cell.getTooltip();
|
||||
} else {
|
||||
tip = this.graph.convertValueToString(cell);
|
||||
tip = this.convertValueToString(cell);
|
||||
}
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
||||
|
@ -124,10 +107,9 @@ class GraphTooltip {
|
|||
*
|
||||
* @param enabled Boolean indicating if tooltips should be enabled.
|
||||
*/
|
||||
setTooltips(enabled: boolean): void {
|
||||
(<TooltipHandler>this.graph.tooltipHandler).setEnabled(enabled);
|
||||
setTooltips(enabled: boolean) {
|
||||
this.getTooltipHandler().setEnabled(enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GraphTooltip;
|
||||
|
|
|
@ -9,9 +9,10 @@ import { fit, getScrollOrigin } from '../../util/Utils';
|
|||
import { TOOLTIP_VERTICAL_OFFSET } from '../../util/Constants';
|
||||
import { getSource, isMouseEvent } from '../../util/EventUtils';
|
||||
import { isNode } from '../../util/DomUtils';
|
||||
import Graph from '../Graph';
|
||||
import Graph, { MaxGraph } from '../Graph';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import InternalMouseEvent from '../event/InternalMouseEvent';
|
||||
import { Listenable } from '../../types';
|
||||
|
||||
/**
|
||||
* Class: mxTooltipHandler
|
||||
|
@ -38,41 +39,42 @@ import InternalMouseEvent from '../event/InternalMouseEvent';
|
|||
* delay - Optional delay in milliseconds.
|
||||
*/
|
||||
class TooltipHandler {
|
||||
constructor(graph: Graph | null, delay: number) {
|
||||
if (graph != null) {
|
||||
this.graph = graph;
|
||||
this.delay = delay || 500;
|
||||
this.graph.addMouseListener(this);
|
||||
}
|
||||
constructor(graph: MaxGraph, delay: number = 500) {
|
||||
this.graph = graph;
|
||||
this.delay = delay;
|
||||
this.graph.addMouseListener(this);
|
||||
}
|
||||
|
||||
// @ts-ignore Cannot be null.
|
||||
div: HTMLElement;
|
||||
|
||||
/**
|
||||
* Variable: zIndex
|
||||
*
|
||||
* Specifies the zIndex for the tooltip and its shadow. Default is 10005.
|
||||
*/
|
||||
zIndex: number = 10005;
|
||||
zIndex = 10005;
|
||||
|
||||
/**
|
||||
* Variable: graph
|
||||
*
|
||||
* Reference to the enclosing <mxGraph>.
|
||||
*/
|
||||
graph: Graph = null;
|
||||
graph: Graph;
|
||||
|
||||
/**
|
||||
* Variable: delay
|
||||
*
|
||||
* Delay to show the tooltip in milliseconds. Default is 500.
|
||||
*/
|
||||
delay: number = null;
|
||||
delay: number;
|
||||
|
||||
/**
|
||||
* Variable: ignoreTouchEvents
|
||||
*
|
||||
* Specifies if touch and pen events should be ignored. Default is true.
|
||||
*/
|
||||
ignoreTouchEvents: boolean = true;
|
||||
ignoreTouchEvents = true;
|
||||
|
||||
/**
|
||||
* Variable: hideOnHover
|
||||
|
@ -80,21 +82,28 @@ class TooltipHandler {
|
|||
* Specifies if the tooltip should be hidden if the mouse is moved over the
|
||||
* current cell. Default is false.
|
||||
*/
|
||||
hideOnHover: boolean = false;
|
||||
hideOnHover = false;
|
||||
|
||||
/**
|
||||
* Variable: destroyed
|
||||
*
|
||||
* True if this handler was destroyed using <destroy>.
|
||||
*/
|
||||
destroyed: boolean = false;
|
||||
destroyed = false;
|
||||
|
||||
lastX: number = 0;
|
||||
lastY: number = 0;
|
||||
state: CellState | null = null;
|
||||
stateSource: boolean = false;
|
||||
node: any;
|
||||
thread: number | null = null;
|
||||
|
||||
/**
|
||||
* Variable: enabled
|
||||
*
|
||||
* Specifies if events are handled. Default is true.
|
||||
*/
|
||||
enabled: boolean = true;
|
||||
enabled = true;
|
||||
|
||||
/**
|
||||
* Function: isEnabled
|
||||
|
@ -102,7 +111,7 @@ class TooltipHandler {
|
|||
* Returns true if events are handled. This implementation
|
||||
* returns <enabled>.
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
|
@ -112,7 +121,7 @@ class TooltipHandler {
|
|||
* Enables or disables event handling. This implementation
|
||||
* updates <enabled>.
|
||||
*/
|
||||
setEnabled(enabled: boolean): void {
|
||||
setEnabled(enabled: boolean) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
|
@ -121,7 +130,7 @@ class TooltipHandler {
|
|||
*
|
||||
* Returns <hideOnHover>.
|
||||
*/
|
||||
isHideOnHover(): boolean {
|
||||
isHideOnHover() {
|
||||
return this.hideOnHover;
|
||||
}
|
||||
|
||||
|
@ -130,7 +139,7 @@ class TooltipHandler {
|
|||
*
|
||||
* Sets <hideOnHover>.
|
||||
*/
|
||||
setHideOnHover(value: boolean): void {
|
||||
setHideOnHover(value: boolean) {
|
||||
this.hideOnHover = value;
|
||||
}
|
||||
|
||||
|
@ -139,22 +148,20 @@ class TooltipHandler {
|
|||
*
|
||||
* Initializes the DOM nodes required for this tooltip handler.
|
||||
*/
|
||||
init(): void {
|
||||
if (document.body != null) {
|
||||
this.div = document.createElement('div');
|
||||
this.div.className = 'mxTooltip';
|
||||
this.div.style.visibility = 'hidden';
|
||||
init() {
|
||||
this.div = document.createElement('div');
|
||||
this.div.className = 'mxTooltip';
|
||||
this.div.style.visibility = 'hidden';
|
||||
|
||||
document.body.appendChild(this.div);
|
||||
document.body.appendChild(this.div);
|
||||
|
||||
InternalEvent.addGestureListeners(this.div, (evt) => {
|
||||
const source = getSource(evt);
|
||||
InternalEvent.addGestureListeners(this.div, (evt) => {
|
||||
const source = getSource(evt);
|
||||
|
||||
if (source.nodeName !== 'A') {
|
||||
this.hideTooltip();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (source.nodeName !== 'A') {
|
||||
this.hideTooltip();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,7 +169,7 @@ class TooltipHandler {
|
|||
*
|
||||
* Returns the <mxCellState> to be used for showing a tooltip for this event.
|
||||
*/
|
||||
getStateForEvent(me: MouseEvent): CellState {
|
||||
getStateForEvent(me: InternalMouseEvent) {
|
||||
return me.getState();
|
||||
}
|
||||
|
||||
|
@ -173,7 +180,7 @@ class TooltipHandler {
|
|||
* event all subsequent events of the gesture are redirected to this
|
||||
* handler.
|
||||
*/
|
||||
mouseDown(sender: any, me: InternalMouseEvent): void {
|
||||
mouseDown(sender: any, me: InternalMouseEvent) {
|
||||
this.reset(me, false);
|
||||
this.hideTooltip();
|
||||
}
|
||||
|
@ -183,7 +190,7 @@ class TooltipHandler {
|
|||
*
|
||||
* Handles the event by updating the rubberband selection.
|
||||
*/
|
||||
mouseMove(sender: any, me: InternalMouseEvent): void {
|
||||
mouseMove(sender: Listenable, me: InternalMouseEvent) {
|
||||
if (me.getX() !== this.lastX || me.getY() !== this.lastY) {
|
||||
this.reset(me, true);
|
||||
const state = this.getStateForEvent(me);
|
||||
|
@ -222,7 +229,7 @@ class TooltipHandler {
|
|||
* Resets the timer.
|
||||
*/
|
||||
resetTimer(): void {
|
||||
if (this.thread != null) {
|
||||
if (this.thread !== null) {
|
||||
window.clearTimeout(this.thread);
|
||||
this.thread = null;
|
||||
}
|
||||
|
@ -233,16 +240,16 @@ class TooltipHandler {
|
|||
*
|
||||
* Resets and/or restarts the timer to trigger the display of the tooltip.
|
||||
*/
|
||||
reset(me: InternalMouseEvent, restart: boolean, state: CellState): void {
|
||||
reset(me: InternalMouseEvent, restart: boolean, state: CellState | null = null) {
|
||||
if (!this.ignoreTouchEvents || isMouseEvent(me.getEvent())) {
|
||||
this.resetTimer();
|
||||
state = state != null ? state : this.getStateForEvent(me);
|
||||
state = state ?? this.getStateForEvent(me);
|
||||
|
||||
if (
|
||||
restart &&
|
||||
this.isEnabled() &&
|
||||
state != null &&
|
||||
(this.div == null || this.div.style.visibility === 'hidden')
|
||||
state &&
|
||||
this.div.style.visibility === 'hidden'
|
||||
) {
|
||||
const node = me.getSource();
|
||||
const x = me.getX();
|
||||
|
@ -251,6 +258,7 @@ class TooltipHandler {
|
|||
|
||||
this.thread = window.setTimeout(() => {
|
||||
if (
|
||||
state &&
|
||||
!this.graph.isEditing() &&
|
||||
!this.graph.popupMenuHandler.isMenuShowing() &&
|
||||
!this.graph.isMouseDown
|
||||
|
@ -274,7 +282,7 @@ class TooltipHandler {
|
|||
*
|
||||
* Hides the tooltip and resets the timer.
|
||||
*/
|
||||
hide(): void {
|
||||
hide() {
|
||||
this.resetTimer();
|
||||
this.hideTooltip();
|
||||
}
|
||||
|
@ -284,11 +292,9 @@ class TooltipHandler {
|
|||
*
|
||||
* Hides the tooltip.
|
||||
*/
|
||||
hideTooltip(): void {
|
||||
if (this.div != null) {
|
||||
this.div.style.visibility = 'hidden';
|
||||
this.div.innerHTML = '';
|
||||
}
|
||||
hideTooltip() {
|
||||
this.div.style.visibility = 'hidden';
|
||||
this.div.innerHTML = '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,24 +303,19 @@ class TooltipHandler {
|
|||
* Shows the tooltip for the specified cell and optional index at the
|
||||
* specified location (with a vertical offset of 10 pixels).
|
||||
*/
|
||||
show(tip: string, x: number, y: number): void {
|
||||
if (!this.destroyed && tip != null && tip.length > 0) {
|
||||
// Initializes the DOM nodes if required
|
||||
if (this.div == null) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
show(tip: HTMLElement | string | null, x: number, y: number) {
|
||||
if (!this.destroyed && tip && tip !== '') {
|
||||
const origin = getScrollOrigin();
|
||||
|
||||
this.div.style.zIndex = this.zIndex;
|
||||
this.div.style.zIndex = String(this.zIndex);
|
||||
this.div.style.left = `${x + origin.x}px`;
|
||||
this.div.style.top = `${y + TOOLTIP_VERTICAL_OFFSET + origin.y}px`;
|
||||
|
||||
if (!isNode(tip)) {
|
||||
this.div.innerHTML = tip.replace(/\n/g, '<br>');
|
||||
this.div.innerHTML = (tip as string).replace(/\n/g, '<br>');
|
||||
} else {
|
||||
this.div.innerHTML = '';
|
||||
this.div.appendChild(tip);
|
||||
this.div.appendChild(tip as HTMLElement);
|
||||
}
|
||||
|
||||
this.div.style.visibility = '';
|
||||
|
@ -327,17 +328,16 @@ class TooltipHandler {
|
|||
*
|
||||
* Destroys the handler and all its resources and DOM nodes.
|
||||
*/
|
||||
destroy(): void {
|
||||
destroy() {
|
||||
if (!this.destroyed) {
|
||||
this.graph.removeMouseListener(this);
|
||||
InternalEvent.release(this.div);
|
||||
|
||||
if (this.div != null && this.div.parentNode != null) {
|
||||
if (this.div.parentNode != null) {
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
}
|
||||
|
||||
this.destroyed = true;
|
||||
this.div = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,36 @@
|
|||
import Cell from "../cell/datatypes/Cell";
|
||||
import Resources from "../../util/Resources";
|
||||
import {isNode} from "../../util/DomUtils";
|
||||
import CellState from "../cell/datatypes/CellState";
|
||||
import Multiplicity from "./Multiplicity";
|
||||
import Graph from "../Graph";
|
||||
import Cell from '../cell/datatypes/Cell';
|
||||
import Resources from '../../util/Resources';
|
||||
import { isNode } from '../../util/DomUtils';
|
||||
import CellState from '../cell/datatypes/CellState';
|
||||
import Multiplicity from './Multiplicity';
|
||||
import { autoImplement } from '../../util/Utils';
|
||||
|
||||
class GraphValidation {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
import type Graph from '../Graph';
|
||||
import type GraphEdge from '../cell/edge/GraphEdge';
|
||||
import type GraphConnections from '../connection/GraphConnections';
|
||||
import type GraphOverlays from '../layout/GraphOverlays';
|
||||
|
||||
this.multiplicities = [];
|
||||
}
|
||||
|
||||
graph: Graph;
|
||||
type PartialGraph = Pick<
|
||||
Graph,
|
||||
| 'getModel'
|
||||
| 'isAllowLoops'
|
||||
| 'isMultigraph'
|
||||
| 'getView'
|
||||
| 'isValidRoot'
|
||||
| 'getContainsValidationErrorsResource'
|
||||
| 'getAlreadyConnectedResource'
|
||||
>;
|
||||
type PartialEdge = Pick<GraphEdge, 'isAllowDanglingEdges'>;
|
||||
type PartialConnections = Pick<GraphConnections, 'isValidConnection'>;
|
||||
type PartialOverlays = Pick<GraphOverlays, 'setCellWarning'>;
|
||||
type PartialClass = PartialGraph & PartialEdge & PartialConnections & PartialOverlays;
|
||||
|
||||
class GraphValidation extends autoImplement<PartialClass>() {
|
||||
/**
|
||||
* An array of {@link Multiplicity} describing the allowed
|
||||
* connections in a graph.
|
||||
*/
|
||||
multiplicities: Multiplicity[] | null = null;
|
||||
multiplicities: Multiplicity[] = [];
|
||||
|
||||
/*****************************************************************************
|
||||
* Group: Validation
|
||||
|
@ -28,7 +40,7 @@ class GraphValidation {
|
|||
* Displays the given validation error in a dialog. This implementation uses
|
||||
* mxUtils.alert.
|
||||
*/
|
||||
validationAlert(message: any): void {
|
||||
validationAlert(message: string) {
|
||||
alert(message);
|
||||
}
|
||||
|
||||
|
@ -40,8 +52,8 @@ class GraphValidation {
|
|||
* @param source {@link mxCell} that represents the source terminal.
|
||||
* @param target {@link mxCell} that represents the target terminal.
|
||||
*/
|
||||
isEdgeValid(edge: Cell, source: Cell, target: Cell): boolean {
|
||||
return this.getEdgeValidationError(edge, source, target) == null;
|
||||
isEdgeValid(edge: Cell, source: Cell, target: Cell) {
|
||||
return !this.getEdgeValidationError(edge, source, target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,45 +98,37 @@ class GraphValidation {
|
|||
source: Cell | null = null,
|
||||
target: Cell | null = null
|
||||
): string | null {
|
||||
if (
|
||||
edge != null &&
|
||||
!this.isAllowDanglingEdges() &&
|
||||
(source == null || target == null)
|
||||
) {
|
||||
if (edge && !this.isAllowDanglingEdges() && (!source || !target)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (
|
||||
edge != null &&
|
||||
edge.getTerminal(true) == null &&
|
||||
edge.getTerminal(false) == null
|
||||
) {
|
||||
if (edge && !edge.getTerminal(true) && !edge.getTerminal(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Checks if we're dealing with a loop
|
||||
if (!this.allowLoops && source === target && source != null) {
|
||||
if (!this.isAllowLoops() && source === target && source) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Checks if the connection is generally allowed
|
||||
if (!this.isValidConnection(<Cell>source, <Cell>target)) {
|
||||
if (!this.isValidConnection(source, target)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (source != null && target != null) {
|
||||
if (source && target) {
|
||||
let error = '';
|
||||
|
||||
// Checks if the cells are already connected
|
||||
// and adds an error message if required
|
||||
if (!this.multigraph) {
|
||||
if (!this.isMultigraph()) {
|
||||
const tmp = this.getModel().getEdgesBetween(source, target, true);
|
||||
|
||||
// Checks if the source and target are not connected by another edge
|
||||
if (tmp.length > 1 || (tmp.length === 1 && tmp[0] !== edge)) {
|
||||
error += `${
|
||||
Resources.get(this.alreadyConnectedResource) ||
|
||||
this.alreadyConnectedResource
|
||||
Resources.get(this.getAlreadyConnectedResource()) ||
|
||||
this.getAlreadyConnectedResource()
|
||||
}\n`;
|
||||
}
|
||||
}
|
||||
|
@ -136,20 +140,18 @@ class GraphValidation {
|
|||
const targetIn = target.getDirectedEdgeCount(false, edge);
|
||||
|
||||
// Checks the change against each multiplicity rule
|
||||
if (this.multiplicities != null) {
|
||||
for (const multiplicity of this.multiplicities) {
|
||||
const err = multiplicity.check(
|
||||
this,
|
||||
<Cell>edge,
|
||||
source,
|
||||
target,
|
||||
sourceOut,
|
||||
targetIn
|
||||
);
|
||||
for (const multiplicity of this.multiplicities) {
|
||||
const err = multiplicity.check(
|
||||
<Graph>(<unknown>this), // needs to cast to Graph
|
||||
<Cell>edge,
|
||||
source,
|
||||
target,
|
||||
sourceOut,
|
||||
targetIn
|
||||
);
|
||||
|
||||
if (err != null) {
|
||||
error += err;
|
||||
}
|
||||
if (err != null) {
|
||||
error += err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +163,7 @@ class GraphValidation {
|
|||
return error.length > 0 ? error : null;
|
||||
}
|
||||
|
||||
return this.allowDanglingEdges ? null : '';
|
||||
return this.isAllowDanglingEdges() ? null : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,17 +193,20 @@ class GraphValidation {
|
|||
* the graph root.
|
||||
* @param context Object that represents the global validation state.
|
||||
*/
|
||||
validateGraph(
|
||||
cell: Cell = <Cell>this.graph.model.getRoot(),
|
||||
context: any
|
||||
): string | null {
|
||||
context = context != null ? context : {};
|
||||
validateGraph(cell: Cell | null, context: any): string | null {
|
||||
cell = cell ?? this.getModel().getRoot();
|
||||
|
||||
if (!cell) {
|
||||
return 'The root does not exist!';
|
||||
}
|
||||
|
||||
context = context ?? {};
|
||||
|
||||
let isValid = true;
|
||||
const childCount = cell.getChildCount();
|
||||
|
||||
for (let i = 0; i < childCount; i += 1) {
|
||||
const tmp = <Cell>cell.getChildAt(i);
|
||||
const tmp = cell.getChildAt(i);
|
||||
let ctx = context;
|
||||
|
||||
if (this.isValidRoot(tmp)) {
|
||||
|
@ -210,7 +215,7 @@ class GraphValidation {
|
|||
|
||||
const warn = this.validateGraph(tmp, ctx);
|
||||
|
||||
if (warn != null) {
|
||||
if (warn) {
|
||||
this.setCellWarning(tmp, warn.replace(/\n/g, '<br>'));
|
||||
} else {
|
||||
this.setCellWarning(tmp, null);
|
||||
|
@ -224,8 +229,8 @@ class GraphValidation {
|
|||
// Adds error for invalid children if collapsed (children invisible)
|
||||
if (cell && cell.isCollapsed() && !isValid) {
|
||||
warning += `${
|
||||
Resources.get(this.containsValidationErrorsResource) ||
|
||||
this.containsValidationErrorsResource
|
||||
Resources.get(this.getContainsValidationErrorsResource()) ||
|
||||
this.getContainsValidationErrorsResource()
|
||||
}\n`;
|
||||
}
|
||||
|
||||
|
@ -265,31 +270,30 @@ class GraphValidation {
|
|||
*
|
||||
* @param cell {@link mxCell} for which the multiplicities should be checked.
|
||||
*/
|
||||
getCellValidationError(cell: Cell): string | null {
|
||||
getCellValidationError(cell: Cell) {
|
||||
const outCount = cell.getDirectedEdgeCount(true);
|
||||
const inCount = cell.getDirectedEdgeCount(false);
|
||||
const value = cell.getValue();
|
||||
let error = '';
|
||||
|
||||
if (this.multiplicities != null) {
|
||||
for (let i = 0; i < this.multiplicities.length; i += 1) {
|
||||
const rule = this.multiplicities[i];
|
||||
for (let i = 0; i < this.multiplicities.length; i += 1) {
|
||||
const rule = this.multiplicities[i];
|
||||
|
||||
if (
|
||||
rule.source &&
|
||||
isNode(value, rule.type, rule.attr, rule.value) &&
|
||||
(outCount > rule.max || outCount < rule.min)
|
||||
) {
|
||||
error += `${rule.countError}\n`;
|
||||
} else if (
|
||||
!rule.source &&
|
||||
isNode(value, rule.type, rule.attr, rule.value) &&
|
||||
(inCount > rule.max || inCount < rule.min)
|
||||
) {
|
||||
error += `${rule.countError}\n`;
|
||||
}
|
||||
if (
|
||||
rule.source &&
|
||||
isNode(value, rule.type, rule.attr, rule.value) &&
|
||||
(outCount > rule.max || outCount < rule.min)
|
||||
) {
|
||||
error += `${rule.countError}\n`;
|
||||
} else if (
|
||||
!rule.source &&
|
||||
isNode(value, rule.type, rule.attr, rule.value) &&
|
||||
(inCount > rule.max || inCount < rule.min)
|
||||
) {
|
||||
error += `${rule.countError}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return error.length > 0 ? error : null;
|
||||
}
|
||||
|
||||
|
@ -301,8 +305,7 @@ class GraphValidation {
|
|||
* @param cell {@link mxCell} that represents the cell to validate.
|
||||
* @param context Object that represents the global validation state.
|
||||
*/
|
||||
// validateCell(cell: mxCell, context: any): string | null;
|
||||
validateCell(cell: Cell, context: CellState): void | null {
|
||||
validateCell(cell: Cell, context: CellState): string | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ import type { UndoableChange } from '../../types';
|
|||
* Action to change the current root in a view.
|
||||
*/
|
||||
class CurrentRootChange implements UndoableChange {
|
||||
view: mxGraphView;
|
||||
view: GraphView;
|
||||
root: Cell | null;
|
||||
previous: Cell | null;
|
||||
isUp: boolean;
|
||||
|
||||
constructor(view: mxGraphView, root: Cell | null) {
|
||||
constructor(view: GraphView, root: Cell | null) {
|
||||
this.view = view;
|
||||
this.root = root;
|
||||
this.previous = root;
|
||||
|
@ -44,9 +44,7 @@ class CurrentRootChange implements UndoableChange {
|
|||
this.view.currentRoot = this.previous;
|
||||
this.previous = tmp;
|
||||
|
||||
const translate = this.view.graph.getTranslateForRoot(
|
||||
this.view.currentRoot
|
||||
);
|
||||
const translate = this.view.graph.getTranslateForRoot(this.view.currentRoot);
|
||||
|
||||
if (translate) {
|
||||
this.view.translate = new Point(-translate.x, -translate.y);
|
||||
|
@ -62,13 +60,7 @@ class CurrentRootChange implements UndoableChange {
|
|||
const name = this.isUp ? InternalEvent.UP : InternalEvent.DOWN;
|
||||
|
||||
this.view.fireEvent(
|
||||
new EventObject(
|
||||
name,
|
||||
'root',
|
||||
this.view.currentRoot,
|
||||
'previous',
|
||||
this.previous
|
||||
)
|
||||
new EventObject(name, 'root', this.view.currentRoot, 'previous', this.previous)
|
||||
);
|
||||
|
||||
this.isUp = !this.isUp;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue