@maxgraph/core
Version:
maxGraph is a fully client side JavaScript diagramming library that uses SVG and HTML for rendering.
638 lines (637 loc) • 24.4 kB
TypeScript
import EventSource from './event/EventSource.js';
import UndoableEdit from './undoable_changes/UndoableEdit.js';
import Cell from './cell/Cell.js';
import Geometry from './geometry/Geometry.js';
import type { CellStyle, FilterFunction } from '../types.js';
/**
* Extends {@link EventSource} to implement a graph model. The graph model acts as
* a wrapper around the cells which are in charge of storing the actual graph
* data structure. The model acts as a transactional wrapper with event
* notification for all changes, whereas the cells contain the atomic
* operations for updating the actual data structure.
*
* ### Layers
*
* The cell hierarchy in the model must have a top-level root cell which
* contains the layers (typically one default layer), which in turn contain the
* top-level cells of the layers. This means each cell is contained in a layer.
* If no layers are required, then all new cells should be added to the default
* layer.
*
* Layers are useful for hiding and showing groups of cells, or for placing
* groups of cells on top of other cells in the display. To identify a layer,
* the {@link isLayer} function is used. It returns true if the parent of the given
* cell is the root of the model.
*
* ### Events
*
* See events section for more details. There is a new set of events for
* tracking transactional changes as they happen. The events are called
* startEdit for the initial beginUpdate, executed for each executed change
* and endEdit for the terminal endUpdate. The executed event contains a
* property called change which represents the change after execution.
*
* ### Encoding the model
*
* #### To encode a graph model, use the following code:
*
* ```javascript
* var enc = new Codec();
* var node = enc.encode(graph.getDataModel());
* ```
*
* This will create an XML node that contains all the model information.
*
* #### Encoding and decoding changes:
*
* For the encoding of changes, a graph model listener is required that encodes
* each change from the given array of changes.
*
* ```javascript
* model.addListener(mxEvent.CHANGE, function(sender, evt)
* {
* var changes = evt.getProperty('edit').changes;
* var nodes = [];
* var codec = new Codec();
*
* for (var i = 0; i < changes.length; i++)
* {
* nodes.push(codec.encode(changes[i]));
* }
* // do something with the nodes
* });
* ```
*
* For the decoding and execution of changes, the codec needs a lookup function
* that allows it to resolve cell IDs as follows:
*
* ```javascript
* var codec = new Codec();
* codec.lookup(id)
* {
* return model.getCell(id);
* }
* ```
*
* For each encoded change (represented by a node), the following code can be
* used to carry out the decoding and create a change object.
*
* ```javascript
* var changes = [];
* var change = codec.decode(node);
* change.model = model;
* change.execute();
* changes.push(change);
* ```
*
* The changes can then be dispatched using the model as follows.
*
* ```javascript
* var edit = new mxUndoableEdit(model, false);
* edit.changes = changes;
*
* edit.notify()
* {
* edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
* 'edit', edit, 'changes', edit.changes));
* edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
* 'edit', edit, 'changes', edit.changes));
* }
*
* model.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
* model.fireEvent(new mxEventObject(mxEvent.CHANGE,
* 'edit', edit, 'changes', changes));
* ```
*
* Event: mxEvent.CHANGE
*
* Fires when an undoable edit is dispatched. The `edit` property
* contains the {@link UndoableEdit}. The `changes` property contains
* the array of atomic changes inside the undoable edit. The changes property
* is **deprecated**, please use edit.changes instead.
*
* ### Example
*
* For finding newly inserted cells, the following code can be used:
*
* ```javascript
* graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
* {
* var changes = evt.getProperty('edit').changes;
*
* for (var i = 0; i < changes.length; i++)
* {
* var change = changes[i];
*
* if (change instanceof mxChildChange &&
* change.change.previous == null)
* {
* graph.startEditingAtCell(change.child);
* break;
* }
* }
* });
* ```
*
* Event: mxEvent.NOTIFY
*
* Same as {@link Event#CHANGE}, this event can be used for classes that need to
* implement a sync mechanism between this model and, say, a remote model. In
* such a setup, only local changes should trigger a notify event and all
* changes should trigger a change event.
*
* Event: mxEvent.EXECUTE
*
* Fires between begin- and endUpdate and after an atomic change was executed
* in the model. The `change` property contains the atomic change
* that was executed.
*
* Event: mxEvent.EXECUTED
*
* Fires between START_EDIT and END_EDIT after an atomic change was executed.
* The `change` property contains the change that was executed.
*
* Event: mxEvent.BEGIN_UPDATE
*
* Fires after the {@link updateLevel} was incremented in {@link beginUpdate}. This event
* contains no properties.
*
* Event: mxEvent.START_EDIT
*
* Fires after the {@link updateLevel} was changed from 0 to 1. This event
* contains no properties.
*
* Event: mxEvent.END_UPDATE
*
* Fires after the {@link updateLevel} was decreased in {@link endUpdate} but before any
* notification or change dispatching. The `edit` property contains
* the {@link currentEdit}.
*
* Event: mxEvent.END_EDIT
*
* Fires after the {@link updateLevel} was changed from 1 to 0. This event
* contains no properties.
*
* Event: mxEvent.BEFORE_UNDO
*
* Fires before the change is dispatched after the update level has reached 0
* in {@link endUpdate}. The `edit` property contains the {@link currentEdit}.
*
* Event: mxEvent.UNDO
*
* Fires after the change was dispatched in {@link endUpdate}. The `edit`
* property contains the {@link currentEdit}.
*
* @class GraphDataModel
*/
export declare class GraphDataModel extends EventSource {
/**
* Holds the root cell, which in turn contains the cells that represent the
* layers of the diagram as child cells. That is, the actual elements of the
* diagram are supposed to live in the third generation of cells and below.
*/
root: Cell | null;
/**
* Maps from Ids to cells.
*/
cells: {
[key: string]: Cell;
} | null;
/**
* Specifies if edges should automatically be moved into the nearest common ancestor of their terminals.
* @default true
*/
maintainEdgeParent: boolean;
/**
* Specifies if relative edge parents should be ignored for finding the nearest common ancestors of an edge's terminals.
* @default true
*/
ignoreRelativeEdgeParent: boolean;
/**
* Specifies if the model should automatically create Ids for new cells.
* @default true.
*/
createIds: boolean;
/**
* Defines the prefix of new Ids. Default is an empty string.
* @default ''
*/
prefix: string;
/**
* Defines the postfix of new Ids.
* @default ''
*/
postfix: string;
/**
* Specifies the next Id to be created. Initial value is 0.
*/
nextId: number;
/**
* Holds the changes for the current transaction. If the transaction is
* closed then a new object is created for this variable using
* {@link createUndoableEdit}.
*/
currentEdit: any;
/**
* Counter for the depth of nested transactions. Each call to {@link beginUpdate}
* will increment this number and each call to {@link endUpdate} will decrement
* it. When the counter reaches 0, the transaction is closed and the
* respective events are fired. Initial value is 0.
*/
updateLevel: number;
/**
* True if the program flow is currently inside endUpdate.
*/
endingUpdate: boolean;
constructor(root?: Cell | null);
/**
* Sets a new root using {@link createRoot}.
*/
clear(): void;
/**
* Returns {@link createIds}.
*/
isCreateIds(): boolean;
/**
* Sets {@link createIds}.
*/
setCreateIds(value: boolean): void;
/**
* Creates a new root cell with a default layer (child 0).
*/
createRoot(): Cell;
/**
* Returns the {@link Cell} for the specified Id or null if no cell can be
* found for the given Id.
*
* @param {string} id A string representing the Id of the cell.
*/
getCell(id: string): Cell | null;
filterCells(cells: Cell[], filter: FilterFunction): Cell[];
getRoot(cell?: Cell | null): Cell | null;
/**
* Sets the {@link root} of the model using {@link RootChange} and adds the change to
* the current transaction. This resets all datastructures in the model and
* is the preferred way of clearing an existing model. Returns the new
* root.
*
* Example:
*
* ```javascript
* const root = new Cell();
* root.insert(new Cell());
* model.setRoot(root);
* ```
*
* @param {Cell} root that specifies the new root.
*/
setRoot(root: Cell | null): Cell | null;
/**
* Inner callback to change the root of the model and update the internal
* datastructures, such as {@link cells} and {@link nextId}. Returns the previous root.
*
* @param {Cell} root that specifies the new root.
*/
rootChanged(root: Cell | null): Cell | null;
/**
* Returns true if the given cell is the root of the model and a non-null
* value.
*
* @param {Cell} cell that represents the possible root.
*/
isRoot(cell?: Cell | null): boolean;
/**
* Returns true if {@link isRoot} returns true for the parent of the given cell.
*
* @param cell that represents the possible layer.
*/
isLayer(cell: Cell | null): boolean;
/**
* Returns true if the model contains the given {@link Cell}.
*
* @param {Cell} cell that specifies the cell.
*/
contains(cell: Cell): boolean;
/**
* Adds the specified child to the parent at the given index using
* {@link ChildChange} and adds the change to the current transaction. If no
* index is specified then the child is appended to the parent's array of
* children. Returns the inserted child.
*
* @param {Cell} parent that specifies the parent to contain the child.
* @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): Cell | null;
/**
* Inner callback to update {@link cells} when a cell has been added. This
* implementation resolves collisions by creating new Ids. To change the
* ID of a cell after it was inserted into the model, use the following
* code:
*
* (code
* delete model.cells[cell.getId()];
* cell.setId(newId);
* model.cells[cell.getId()] = cell;
* ```
*
* If the change of the ID should be part of the command history, then the
* cell should be removed from the model and a clone with the new ID should
* be reinserted into the model instead.
*
* @param {Cell} cell that specifies the cell that has been added.
*/
cellAdded(cell: Cell | null): void;
/**
* Hook method to create an Id for the specified cell. This implementation
* concatenates {@link prefix}, id and {@link postfix} to create the Id and increments
* {@link nextId}. The cell is ignored by this implementation, but can be used in
* overridden methods to prefix the Ids with eg. the cell type.
*
* @param {Cell} cell to create the Id for.
*/
createId(cell: Cell): string;
/**
* Updates the parent for all edges that are connected to cell or one of
* its descendants using {@link updateEdgeParent}.
*/
updateEdgeParents(cell: Cell, root?: Cell): void;
/**
* Inner callback to update the parent of the specified {@link Cell} to the
* nearest-common-ancestor of its two terminals.
*
* @param {Cell} edge that specifies the edge.
* @param {Cell} root that represents the current root of the model.
*/
updateEdgeParent(edge: Cell, root: Cell): void;
/**
* Removes the specified cell from the model using {@link ChildChange} and adds
* the change to the current transaction. This operation will remove the
* cell and all of its children from the model. Returns the removed cell.
*
* @param {Cell} cell that should be removed.
*/
remove(cell: Cell): Cell;
/**
* Inner callback to update {@link cells} when a cell has been removed.
*
* @param {Cell} cell that specifies the cell that has been removed.
*/
cellRemoved(cell: Cell): void;
/**
* Inner callback to update the parent of a cell using {@link Cell#insert}
* on the parent and return the previous parent.
*
* @param {Cell} cell to update the parent for.
* @param {Cell} parent that specifies the new parent of the cell.
* @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;
/**
* Sets the source or target terminal of the given {@link Cell} using
* {@link TerminalChange} and adds the change to the current transaction.
* This implementation updates the parent of the edge using {@link updateEdgeParent}
* if required.
*
* @param {Cell} edge that specifies the edge.
* @param {Cell} terminal that specifies the new terminal.
* @param isSource Boolean indicating if the terminal is the new source or
* target terminal of the edge.
*/
setTerminal(edge: Cell, terminal: Cell | null, isSource: boolean): Cell | null;
/**
* Sets the source and target {@link Cell} of the given {@link Cell} in a single
* transaction using {@link setTerminal} for each end of the edge.
*
* @param {Cell} edge that specifies the edge.
* @param {Cell} source that specifies the new source terminal.
* @param {Cell} target that specifies the new target terminal.
*/
setTerminals(edge: Cell, source: Cell | null, target: Cell | null): void;
/**
* Inner helper function to update the terminal of the edge using
* {@link Cell#insertEdge} and return the previous terminal.
*
* @param {Cell} edge that specifies the edge to be updated.
* @param {Cell} terminal that specifies the new terminal.
* @param isSource Boolean indicating if the terminal is the new source or
* target terminal of the edge.
*/
terminalForCellChanged(edge: Cell, terminal: Cell | null, isSource?: boolean): Cell | null;
/**
* Returns all edges between the given source and target pair. If directed
* is true, then only edges from the source to the target are returned,
* otherwise, all edges between the two cells are returned.
*
* @param {Cell} source that defines the source terminal of the edge to be
* returned.
* @param {Cell} target that defines the target terminal of the edge to be
* returned.
* @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): Cell[];
/**
* Sets the user object of then given {@link Cell} using {@link ValueChange}
* and adds the change to the current transaction.
*
* @param {Cell} cell whose user object should be changed.
* @param value Object that defines the new user object.
*/
setValue(cell: Cell, value: any): any;
/**
* Inner callback to update the user object of the given {@link Cell}
* using {@link Cell#valueChanged} and return the previous value,
* that is, the return value of {@link Cell#valueChanged}.
*
* To change a specific attribute in an XML node, the following code can be
* used.
*
* ```javascript
* graph.getDataModel().valueForCellChanged(cell, value)
* {
* var previous = cell.value.getAttribute('label');
* cell.value.setAttribute('label', value);
*
* return previous;
* };
* ```
*/
valueForCellChanged(cell: Cell, value: any): any;
/**
* Sets the {@link Geometry} of the given {@link Cell}. The actual update
* of the cell is carried out in {@link geometryForCellChanged}. The
* {@link GeometryChange} action is used to encapsulate the change.
*
* @param {Cell} cell whose geometry should be changed.
* @param {Geometry} geometry that defines the new geometry.
*/
setGeometry(cell: Cell, geometry: Geometry): Geometry;
/**
* Inner callback to update the {@link Geometry} of the given {@link Cell} using
* {@link Cell#setGeometry} and return the previous {@link Geometry}.
*/
geometryForCellChanged(cell: Cell, geometry: Geometry | null): Geometry | null;
/**
* Sets the style of the given {@link Cell} using {@link StyleChange} and adds the change to the current transaction.
*
* **IMPORTANT**: Do not pass {@link Cell.getStyle} as value of the `style` parameter. Otherwise, no style change is performed, so the view won't be updated.
* Always get a clone of the style of the cell with {@link Cell.getClonedStyle}, then update it and pass the updated style to this method.
*
* @param cell whose style should be changed.
* @param style the new cell style to set.
*/
setStyle(cell: Cell, style: CellStyle): void;
/**
* Inner callback to update the style of the given {@link Cell} using {@link Cell#setStyle} and return the previous style.
*
* **IMPORTANT**: to fully work, this method should not receive `cell.getStyle` as value of the `style` parameter. See {@link setStyle} for more information.
*
* @param cell whose style should be changed.
* @param style the new cell style to set.
*/
styleForCellChanged(cell: Cell, style: CellStyle): CellStyle;
/**
* Sets the collapsed state of the given {@link Cell} using {@link CollapseChange}
* and adds the change to the current transaction.
*
* @param {Cell} cell whose collapsed state should be changed.
* @param collapsed Boolean that specifies the new collpased state.
*/
setCollapsed(cell: Cell, collapsed: boolean): boolean;
/**
* Inner callback to update the collapsed state of the
* given {@link Cell} using {@link Cell#setCollapsed} and return
* the previous collapsed state.
*
* @param {Cell} cell that specifies the cell to be updated.
* @param collapsed Boolean that specifies the new collapsed state.
*/
collapsedStateForCellChanged(cell: Cell, collapsed: boolean): boolean;
/**
* Sets the visible state of the given {@link Cell} using {@link VisibleChange} and
* adds the change to the current transaction.
*
* @param {Cell} cell whose visible state should be changed.
* @param visible Boolean that specifies the new visible state.
*/
setVisible(cell: Cell, visible: boolean): boolean;
/**
* Inner callback to update the visible state of the
* given {@link Cell} using {@link Cell#setCollapsed} and return
* the previous visible state.
*
* @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;
/**
* Executes the given edit and fires events if required. The edit object
* requires an execute function which is invoked. The edit is added to the
* {@link currentEdit} between {@link beginUpdate} and {@link endUpdate} calls, so that
* events will be fired if this execute is an individual transaction, that
* is, if no previous {@link beginUpdate} calls have been made without calling
* {@link endUpdate}. This implementation fires an {@link execute} event before
* executing the given change.
*
* @param change Object that described the change.
*/
execute(change: any): void;
/**
* Updates the model in a transaction.
* This is a shortcut to the usage of {@link beginUpdate} and the {@link endUpdate} methods.
*
* ```javascript
* const model = graph.getDataModel();
* const parent = graph.getDefaultParent();
* const index = model.getChildCount(parent);
* model.batchUpdate(() => {
* model.add(parent, v1, index);
* model.add(parent, v2, index+1);
* });
* ```
*
* @param fn the update to be performed in the transaction.
*/
batchUpdate(fn: () => void): void;
/**
* Increments the {@link updateLevel} by one. The event notification
* is queued until {@link updateLevel} reaches 0 by use of
* {@link endUpdate}.
*
* All changes on {@link GraphDataModel} are transactional,
* that is, they are executed in a single undoable change
* on the model (without transaction isolation).
* Therefore, if you want to combine any
* number of changes into a single undoable change,
* you should group any two or more API calls that
* modify the graph model between {@link beginUpdate}
* and {@link endUpdate} calls as shown here:
*
* ```javascript
* const model = graph.getDataModel();
* const parent = graph.getDefaultParent();
* const index = model.getChildCount(parent);
* model.beginUpdate();
* try
* {
* model.add(parent, v1, index);
* model.add(parent, v2, index+1);
* }
* finally
* {
* model.endUpdate();
* }
* ```
*
* Of course there is a shortcut for appending a
* sequence of cells into the default parent:
*
* ```javascript
* graph.addCells([v1, v2]).
* ```
*/
beginUpdate(): void;
/**
* Decrements the {@link updateLevel} by one and fires an {@link undo}
* event if the {@link updateLevel} reaches 0. This function
* indirectly fires a {@link change} event by invoking the notify
* function on the {@link currentEdit} und then creates a new
* {@link currentEdit} using {@link createUndoableEdit}.
*
* The {@link undo} event is fired only once per edit, whereas
* the {@link change} event is fired whenever the notify
* function is invoked, that is, on undo and redo of
* the edit.
*/
endUpdate(): void;
/**
* Creates a new {@link UndoableEdit} that implements the
* notify function to fire a {@link change} and {@link notify} event
* through the {@link UndoableEdit}'s source.
*
* @param significant Optional boolean that specifies if the edit to be created is
* significant. Default is true.
*/
createUndoableEdit(significant?: boolean): UndoableEdit;
/**
* Merges the children of the given cell into the given target cell inside
* this model. All cells are cloned unless there is a corresponding cell in
* the model with the same id, in which case the source cell is ignored and
* all edges are connected to the corresponding cell in this model. Edges
* are considered to have no identity and are always cloned unless the
* cloneAllEdges flag is set to false, in which case edges with the same
* id in the target model are reconnected to reflect the terminals of the
* source edges.
*/
mergeChildren(from: Cell, to: Cell, cloneAllEdges?: boolean): void;
/**
* Clones the children of the source cell into the given target cell in
* this model and adds an entry to the mapping that maps from the source
* cell to the target cell with the same id or the clone of the source cell
* that was inserted into this model.
*/
mergeChildrenImpl(from: Cell, to: Cell, cloneAllEdges: boolean, mapping?: any): void;
}
export default GraphDataModel;