UNPKG

@maxgraph/core

Version:

maxGraph is a fully client side JavaScript diagramming library that uses SVG and HTML for rendering.

471 lines (470 loc) 15.6 kB
import Rectangle from '../geometry/Rectangle.js'; import RectangleShape from '../shape/node/RectangleShape.js'; import EllipseShape from '../shape/node/EllipseShape.js'; import Point from '../geometry/Point.js'; import type { AbstractGraph } from '../AbstractGraph.js'; import CellState from '../cell/CellState.js'; import Image from '../image/ImageBox.js'; import type Cell from '../cell/Cell.js'; import type { CellHandle, Listenable, MouseListenerSet } from '../../types.js'; import Shape from '../shape/Shape.js'; import InternalMouseEvent from '../event/InternalMouseEvent.js'; import EdgeHandler from './EdgeHandler.js'; import EventSource from '../event/EventSource.js'; /** * Event handler for resizing cells. * * This handler is automatically created in {@link AbstractGraph.createHandler}. * * Some elements of this handler and its subclasses can be configured using {@link EdgeHandlerConfig}. */ declare class VertexHandler implements MouseListenerSet { escapeHandler: (sender: Listenable, evt: Event) => void; selectionBounds: Rectangle; bounds: Rectangle; selectionBorder: RectangleShape; /** * Reference to the enclosing {@link AbstractGraph}. */ graph: AbstractGraph; /** * Reference to the {@link CellState} being modified. */ state: CellState; sizers: Shape[]; /** * Specifies if only one sizer handle at the bottom, right corner should be used. * @default false */ singleSizer: boolean; /** * Holds the index of the current handle. */ index: number | null; /** * Specifies if the bounds of handles should be used for hit-detection in IE or if {@link tolerance} > 0. * @default true */ allowHandleBoundsCheck: boolean; /** * Optional {@link Image} to be used as handles. * @default null */ handleImage: Image | null; /** * If handles are currently visible. * @default true */ handlesVisible: boolean; /** * Optional tolerance for hit-detection in {@link getHandleForEvent}. * @default 0 */ tolerance: number; /** * Specifies if a rotation handle should be visible. * * This implementation returns {@link VertexHandlerConfig.rotationEnabled}. * @since 0.12.0 */ protected isRotationEnabled(): boolean; /** * Specifies if the parent should be highlighted if a child cell is selected. * @default false */ parentHighlightEnabled: boolean; /** * Specifies if rotation steps should be "rasterized" depending on the distance to the handle. * @default true */ rotationRaster: boolean; /** * Specifies the cursor for the rotation handle. * @default 'crosshair'. */ rotationCursor: string; /** * Specifies if resize should change the cell in-place. This is an experimental * feature for non-touch devices. * @default false */ livePreview: boolean; /** * Specifies if the live preview should be moved to the front. * @default false */ movePreviewToFront: boolean; /** * Specifies if sizers should be hidden and spaced if the vertex is small. * @default false */ manageSizers: boolean; /** * Specifies if the size of groups should be constrained by the children. * @default false */ constrainGroupByChildren: boolean; /** * Vertical spacing for rotation icon. * @default -16 */ rotationHandleVSpacing: number; /** * The horizontal offset for the handles. This is updated in {@link redrawHandles} * if {@link manageSizers} is `true` and the sizers are offset horizontally. */ horizontalOffset: number; /** * The horizontal offset for the handles. This is updated in <redrawHandles> * if {@link manageSizers} is true and the sizers are offset vertically. */ verticalOffset: number; minBounds: Rectangle | null; x0: number; y0: number; customHandles: CellHandle[]; inTolerance: boolean; startX: number; startY: number; rotationShape: Shape | null; currentAlpha: null | number; startAngle: number; startDist: number; ghostPreview: Shape | null; livePreviewActive: boolean; childOffsetX: number; childOffsetY: number; parentState: CellState | null; parentHighlight: RectangleShape | null; unscaledBounds: Rectangle | null; preview: Shape | null; labelShape: Shape | null; edgeHandlers: EdgeHandler[]; EMPTY_POINT: Point; /** * Constructs an event handler that allows to resize vertices and groups. * * @param state {@link CellState} of the cell to be resized. */ constructor(state: CellState); private getSelectionHandler; /** * Returns `true` if the rotation handle should be showing. */ isRotationHandleVisible(): boolean; /** * Returns `true` if the aspect ratio if the cell should be maintained. */ isConstrainedEvent(me: InternalMouseEvent): boolean; /** * Returns `true` if the center of the vertex should be maintained during the resize. */ isCenteredEvent(state: CellState, me: InternalMouseEvent): boolean; /** * Returns an array of custom handles. * * This implementation returns an empty array. */ createCustomHandles(): CellHandle[]; /** * Initializes the shapes required for this vertex handler. */ updateMinBounds(): void; /** * Returns the Rectangle that defines the bounds of the selection border. */ getSelectionBounds(state: CellState): Rectangle; /** * Creates the shape used to draw the selection border. */ createParentHighlightShape(bounds: Rectangle): RectangleShape; /** * Creates the shape used to draw the selection border. */ createSelectionShape(bounds: Rectangle): RectangleShape; /** * Returns {@link VertexHandlerConfig.selectionColor}. */ getSelectionColor(): string; /** * Returns {@link VertexHandlerConfig.selectionStrokeWidth}. */ getSelectionStrokeWidth(): number; /** * Returns {@link VertexHandlerConfig.selectionDashed}. */ isSelectionDashed(): boolean; /** * Creates a sizer handle for the specified cursor and index and returns * the new {@link RectangleShape} that represents the handle. */ createSizer(cursor: string, index: number, size?: number, fillColor?: string): RectangleShape | EllipseShape; /** * Returns `true` if the sizer for the given index is visible. * * This implementation returns `true` for all given indices. */ isSizerVisible(_index: number): boolean; /** * Creates the shape used for the sizer handle for the specified bounds an * index. Only images and rectangles should be returned if support for HTML * labels with not foreign objects is required. */ createSizerShape(bounds: Rectangle, index: number, fillColor?: string): RectangleShape | EllipseShape; /** * Helper method to create an {@link Rectangle} around the given center point * with a width and height of 2*s or 6, if no s is given. */ moveSizerTo(shape: Shape, x: number, y: number): void; /** * Returns the index of the handle for the given event. This returns the index * of the sizer from where the event originated or {@link InternalEvent.LABEL_HANDLE}. */ getHandleForEvent(me: InternalMouseEvent): number | null; /** * Returns `true` if the given event allows custom handles to be changed. * * This implementation returns `true`. */ isCustomHandleEvent(me: InternalMouseEvent): boolean; /** * Handles the event if a handle has been clicked. By consuming the * event all subsequent events of the gesture are redirected to this * handler. */ mouseDown(_sender: EventSource, me: InternalMouseEvent): void; /** * Called if {@link livePreview} is enabled to check if a border should be painted. * * This implementation returns `true` if the shape is transparent. */ isLivePreviewBorder(): boolean | null; /** * Starts the handling of the mouse gesture. */ start(x: number, y: number, index: number): void; /** * Starts the handling of the mouse gesture. */ createGhostPreview(): Shape; /** * Shortcut to {@link hideSizers}. */ setHandlesVisible(visible: boolean): void; /** * Hides all sizers except. * * Starts the handling of the mouse gesture. */ hideSizers(): void; /** * Checks if the coordinates for the given event are within the * {@link AbstractGraph.tolerance}. If the event is a mouse event then the tolerance is * ignored. */ checkTolerance(me: InternalMouseEvent): void; /** * Hook for subclasses do show details while the handler is active. */ updateHint(me: InternalMouseEvent): void; /** * Hooks for subclasses to hide details when the handler gets inactive. */ removeHint(): void; /** * Hook for rounding the angle. This uses {@link Math.round}. */ roundAngle(angle: number): number; /** * Hook for rounding the unscaled width or height. This uses {@link Math.round}. */ roundLength(length: number): number; /** * Handles the event by updating the preview. */ mouseMove(_sender: EventSource, me: InternalMouseEvent): void; /** * Returns `true` if a ghost preview should be used for custom handles. */ isGhostPreview(): boolean; /** * Moves the vertex. */ moveLabel(me: InternalMouseEvent): void; /** * Rotates the vertex. */ rotateVertex(me: InternalMouseEvent): void; /** * Resizes the vertex. */ resizeVertex(me: InternalMouseEvent): void; /** * Repaints the live preview. */ updateLivePreview(me: InternalMouseEvent): void; /** * Handles the event by applying the changes to the geometry. */ moveToFront(): void; /** * Handles the event by applying the changes to the geometry. */ mouseUp(_sender: EventSource, me: InternalMouseEvent): void; /** * Returns the `recursiveResize` status of the given state. * @param state the given {@link CellState}. This implementation takes the value of this state. * @param me the mouse event. */ isRecursiveResize(state: CellState, me: InternalMouseEvent): boolean; /** * Hook for subclasses to implement a single click on the rotation handle. * This code is executed as part of the model transaction. * * This implementation is empty. */ rotateClick(): void; /** * Rotates the given cell and its children by the given angle in degrees. * * @param cell {@link Cell} to be rotated. * @param angle Angle in degrees. * @param parent if set, consider the parent in the rotation computation. */ rotateCell(cell: Cell, angle: number, parent?: Cell): void; /** * Resets the state of this handler. */ reset(): void; /** * Uses the given vector to change the bounds of the given cell * in the graph using {@link AbstractGraph.resizeCell}. */ resizeCell(cell: Cell, dx: number, dy: number, index: number, gridEnabled: boolean, constrained: boolean, recurse: boolean): void; /** * Moves the children of the given cell by the given vector. */ moveChildren(cell: Cell, dx: number, dy: number): void; /** * Returns the union of the given bounds and location for the specified * handle index. * * To override this to limit the size of vertex via a minWidth/-Height style, * the following code can be used. * * ```javascript * const vertexHandlerUnion = union; * vertexHandler.union = (bounds, dx, dy, index, gridEnabled, scale, tr, constrained) => { * const result = vertexHandlerUnion.apply(this, arguments); * * result.width = Math.max(result.width, this.state.style.minWidth ?? 0)); * result.height = Math.max(result.height, this.state.style.minHeight ?? 0)); * * return result; * }; * ``` * * The minWidth/-Height style can then be used as follows: * * ```javascript * graph.insertVertex({ * parent, * value: 'Hello,', * position: [20, 20], * size: [80, 30], * style: { * minWidth: 100, * minHeight: 100, * }, * }); * ``` * * To override this to update the height for a wrapped text if the width of a vertex is * changed, the following can be used. * * ```javascript * const vertexHandlerUnion = union; * vertexHandler.union = (bounds, dx, dy, index, gridEnabled, scale, tr, constrained) => { * const result = vertexHandlerUnion.apply(this, arguments); * const s = this.state; * * if (this.graph.isHtmlLabel(s.cell) * && (index == 3 || index == 4) * && s.text != null && s.style.whiteSpace == 'wrap') { * const label = this.graph.getLabel(s.cell); * const fontSize = s.style.fontSize ?? constants.DEFAULT_FONTSIZE; * const ww = result.width / s.view.scale - s.text.spacingRight - s.text.spacingLeft * * result.height = styleUtils.getSizeForString(label, fontSize, s.style.fontFamily, ww).height; * } * * return result; * }; * ``` */ union(bounds: Rectangle, dx: number, dy: number, index: number, gridEnabled: boolean, scale: number, tr: Point, constrained: boolean, centered: boolean): Rectangle; /** * Redraws the handles and the preview. */ redraw(ignoreHandles?: boolean): void; /** * Returns the padding to be used for drawing handles for the current <bounds>. */ getHandlePadding(): Point; /** * Returns the bounds used to paint the resize handles. */ getSizerBounds(): Rectangle; /** * Redraws the handles. To hide certain handles the following code can be used. * * ```javascript * redrawHandles() * { * mxVertexHandlerRedrawHandles.apply(this, arguments); * * if (this.sizers != null && this.sizers.length > 7) * { * this.sizers[1].node.style.display = 'none'; * this.sizers[6].node.style.display = 'none'; * } * }; * ``` */ redrawHandles(): void; /** * Returns true if the given custom handle is visible. */ isCustomHandleVisible(handle: CellHandle): boolean; /** * Returns an {@link Point} that defines the rotation handle position. */ getRotationHandlePosition(): Point; /** * Returns `true` if the parent highlight should be visible. * * This implementation always returns `true`. */ isParentHighlightVisible(): boolean; /** * Updates the highlight of the parent if {@link parentHighlightEnabled} is `true`. */ updateParentHighlight(): void; /** * Redraws the preview. */ drawPreview(): void; /** * Returns the bounds for the selection border. */ getSelectionBorderBounds(): Rectangle; /** * Returns `true` if this handler was destroyed or not initialized. */ isDestroyed(): boolean; /** * Destroys the handler and all its resources and DOM nodes. */ onDestroy(): void; } export default VertexHandler;