/*! JointJS React v4.3.6 (2026-09-04) - React bindings and hooks for JointJS to build interactive diagrams and graphs. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { RefObject, ReactNode, PropsWithChildren, CSSProperties } from 'react'; import { dia, attributes, mvc, g } from '@joint/core'; import { Element as Element$1, Link, Cell, Graph, Point, Size } from '@joint/core/types/dia'; import { DOMElement } from '@joint/core/types/internal'; interface IncrementalChangeBase { readonly type: 'change' | 'add'; readonly data: Item; } interface IncrementalChangeRemove { readonly type: 'remove'; readonly data?: Item; } interface IncrementalChangeReset { readonly type: 'reset'; readonly data: Item[]; } /** Discriminated union describing a single incremental change to a collection. */ type IncrementalChange = IncrementalChangeBase | IncrementalChangeRemove | IncrementalChangeReset; /** * Optional shape that a `dia.Cell` subclass can implement to opt into * joint-react's portal rendering. Cells without a `portalSelector` are * skipped by default (unless a paper-level override is supplied). * * {@link ElementModel} and {@link LinkModel} satisfy this structurally; custom cell * classes can too: * * ```ts * import type { PortalHostCell } from '@joint/react'; * * class MyShape extends dia.Element implements PortalHostCell { * portalSelector = 'root'; * } * ``` * @group Types */ interface PortalHostCell { /** Selector of the node inside the cell view where React content mounts. */ readonly portalSelector?: string; } /** * Context passed to a {@link PortalSelector} callback. * @expand * @group Types */ interface PortalSelectorParams { /** The cell model. Has a `portalSelector` field when it opts into portal rendering. */ readonly model: dia.Cell & PortalHostCell; /** The paper instance. */ readonly paper: dia.Paper; /** The graph instance. */ readonly graph: dia.Graph; } /** * Resolves the JointJS selector used to find the React portal target node * inside a cell view. * * - A **string** is used directly as the selector for `cellView.findNode()`. * - **`null`** disables all portal rendering. * - A **function** receives a {@link PortalSelectorParams} and returns: * - a **selector string**, look up that node, * - an **`Element`**, use that DOM node directly, * - **`null`**, skip rendering for this cell, * - **`undefined`** (or no return), fall back to joint-react's default selector. * @group Types */ type PortalSelector = string | null | ((context: PortalSelectorParams) => string | Element | null | undefined); /** * Options for creating a PaperView instance with lifecycle callbacks. */ interface PaperViewOptions extends dia.Paper.Options { /** Called when cell views mount or unmount, with the per-cell incremental changes. */ readonly onViewMountChange?: (changes: Map>) => void; /** * Selector used to locate the React portal target node inside a cell view. * * By default, only cells whose markup contains the `'__portal__'` selector * (i.e. {@link ElementModel}) are rendered via `renderElement`. * Set this to a different selector (e.g. `'root'`) to render into * built-in or custom JointJS shapes. * * A function receives the cell view and the default selector, and returns * a selector string or `null` to skip rendering for that cell. */ readonly portalSelector?: PortalSelector; } /** * The `type` value `@joint/react` stamps on every {@link ElementModel} * (`'element'`). Match it against a cell's `type` to single out React elements * when iterating the graph. * @group MVC */ declare const ELEMENT_MODEL_TYPE = "element"; /** * Selector for the `` element used as the React portal target inside ElementModel markup. * @internal */ declare const PORTAL_SELECTOR = "__portal__"; /** * The element class `@joint/react` registers and uses by default for every * element you add to the graph. Its markup carries a dedicated `` group (the * `'__portal__'` selector) where your {@link RenderElement} output is mounted, so * React content renders beneath the element's ports and highlighters. Extend it * to customize the markup or default attributes, or supply any `dia.Element` * subclass that implements {@link PortalHostCell} to host React content yourself. * @group MVC * @example * ```ts * import { ElementModel } from '@joint/react'; * * const element = new ElementModel({ * id: '1', * position: { x: 10, y: 20 }, * size: { width: 100, height: 50 }, * }); * ``` */ declare class ElementModel extends dia.Element implements PortalHostCell { /** * Selector of the node in this cell's view where `@joint/react` mounts your * {@link RenderElement} content, the `'__portal__'` `` group. */ portalSelector: string; /** * Markup with a single `` group (`'__portal__'`) that hosts the React * portal. JointJS appends ports and highlighters after this group, so they * paint on top of your React content. */ markup: dia.MarkupJSON; /** * Default attributes applied to every ElementModel: the `'element'` type, a * 0x0 size, and an empty `data` object. * @returns The default attributes. */ defaults(): Attributes; } /** * The `type` value `@joint/react` stamps on every {@link LinkModel} (`'link'`). * Match it against a cell's `type` to single out React links when iterating the * graph. * @group MVC */ declare const LINK_MODEL_TYPE = "link"; /** * The link class `@joint/react` registers and uses by default for every link you * add to the graph. Its markup has two paths, a wide transparent `wrapper` that * widens the pointer hit area and the visible `line`, and it mounts the * experimental {@link RenderLink} output into the link's root ``. Extend it to * customize the markup or default attributes, or supply any `dia.Link` subclass * that implements {@link PortalHostCell} to host React content yourself. * @group MVC * @example * ```ts * import { LinkModel } from '@joint/react'; * * const link = new LinkModel({ * id: 'link-1', * source: { id: '1' }, * target: { id: '2' }, * }); * ``` */ declare class LinkModel extends dia.Link implements PortalHostCell { /** * Selector of the node in this cell's view where `@joint/react` mounts React * content, the link's root `` (`'root'`). The markup keeps no dedicated * portal group so React-less links stay lean; the experimental * {@link RenderLink} mounts here when enabled. */ portalSelector: string; /** * Markup with two paths: a wide, transparent `wrapper` that widens the pointer * hit area, and the visible `line`. React content (if any) mounts into the * link's root `` via {@link RenderLink}. */ markup: dia.MarkupJSON; /** * Default attributes for every LinkModel: the `'link'` type, the default link * styling, and an empty `data` object. The styling sets `connection: true` on * the `line`/`wrapper` paths, which JointJS needs to compute the link path. * @returns The default attributes. */ defaults(): Attributes; } /** * Identifies which paper a paper-targeting hook should resolve: a registered * paper id, a React ref to a paper, or a `dia.Paper` instance directly. These * hooks never throw — an unresolved target falls back to the current Paper * context or the default paper. * @see {@link useOnPaperEvents} * @see {@link useOnElementsMeasured} * @group Types */ type PaperTarget = string | RefObject | dia.Paper; /** * Union of known string literals plus arbitrary strings while preserving * intellisense for the known members. */ type LiteralUnion = T | (string & Record); /** Strips the index signature from `T`, leaving only explicitly declared keys. */ type RemoveIndexSignature = { [K in keyof T as string extends K ? never : K]: T[K]; }; /** Like `Omit`, but first removes any index signature from `T`. */ type OmitWithoutIndexSignature = Omit, K>; /** * A type that makes all properties of T nullable. */ type Nullable = { [K in keyof T]: T[K] | null; }; /** * A link endpoint marker, an SVG complex-marker JSON plus an optional `length`. * Attach one to a {@link LinkStyle}'s `sourceMarker` / `targetMarker`. The * built-in `linkMarker*` factories return this shape, or you can hand-write one. * @group Types */ interface LinkMarkerRecord extends dia.SVGComplexMarkerJSON { /** * The marker's visual length along the link, in px. Connection-point math pulls * the line tip back by this much so the line meets the marker instead of poking * through it. Omit it to apply no offset (treated as `0`). */ readonly length?: number; } /** * Sizing, color, and stroke options shared by every built-in `linkMarker*` * factory. Build a marker, then attach it to a {@link LinkStyle}. * @group Types * @example * ```ts * import { linkStyle, linkMarkerArrow, linkMarkerCircle } from '@joint/react'; * * const attrs = linkStyle({ * sourceMarker: linkMarkerCircle({ scale: 1.2 }), * targetMarker: linkMarkerArrow({ fill: 'none' }), * }); * ``` */ interface LinkMarkerOptions { /** Uniform scale factor applied to the marker geometry. @default 1 */ readonly scale?: number; /** Fill color. Defaults to inheriting the link's stroke; use `'none'` for an outline-only marker. @default 'inherit' */ readonly fill?: string; /** Stroke (outline) color. Defaults to inheriting the link's stroke. @default 'inherit' */ readonly stroke?: string; /** Stroke width, in px. @default 2 */ readonly strokeWidth?: number; /** Optional CSS class added to the marker root. */ readonly className?: string; } /** * Filled triangle marker for link endpoints, the classic directed-edge arrow. * @returns A marker record for a {@link LinkStyle}'s `sourceMarker` / `targetMarker` * @example * ```ts * import { linkStyle, linkMarkerArrow } from '@joint/react'; * * const attrs = linkStyle({ targetMarker: linkMarkerArrow() }); * ``` * @group Presets */ declare function linkMarkerArrow(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Open chevron marker for link endpoints, two strokes meeting at a point, * no fill (no back edge). * @group Presets */ declare function linkMarkerArrowOpen(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Filled arrow marker with a concave (sunken) back edge, sharper, slimmer * silhouette than the plain {@link linkMarkerArrow}. * @group Presets */ declare function linkMarkerArrowSunken(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Filled arrow marker with a split/quill back, the back edges form an open * V instead of meeting in a single point. * @group Presets */ declare function linkMarkerArrowQuill(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Double arrow marker, two stacked triangles drawn one behind the other along * the link, useful for "fast-forward" or "strong direction" semantics. * @group Presets */ declare function linkMarkerArrowDouble(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Circle marker for link endpoints. Pass `fill: 'none'` for an outline ring. * @group Presets */ declare function linkMarkerCircle(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Diamond marker for link endpoints, used in UML for aggregation/composition. * Pass `fill: 'none'` for an outline-only diamond (aggregation). * @group Presets */ declare function linkMarkerDiamond(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Vertical bar marker at the link endpoint, a neutral terminator that adds * a visual stop without implying direction. * @group Presets */ declare function linkMarkerLine(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Cross (X) marker centered at the link endpoint, typically used to mark * a forbidden or "no entry" connection. * @group Presets */ declare function linkMarkerCross(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Fork marker, a reversed triangle, useful as a "return" or back-edge cap. * @group Presets */ declare function linkMarkerFork(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Fork marker with a closing vertical bar at the tip, a fork that * terminates on a solid wall. * @group Presets */ declare function linkMarkerForkClose(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Crow's foot marker for ER diagrams, denotes "many" cardinality on the * relation's end. * @group Presets */ declare function linkMarkerMany(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Crow's foot with circle marker for ER diagrams, denotes "many optional" * (zero-or-many) cardinality. * @group Presets */ declare function linkMarkerManyOptional(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Vertical bar marker for ER diagrams, denotes "one" (exactly-one) cardinality. * @group Presets */ declare function linkMarkerOne(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Vertical bar with circle marker for ER diagrams, denotes "one optional" * (zero-or-one) cardinality. * @group Presets */ declare function linkMarkerOneOptional(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Crow's foot with vertical bar marker for ER diagrams, denotes "one or * many" (at-least-one) cardinality. * @group Presets */ declare function linkMarkerOneOrMany(options?: LinkMarkerOptions): LinkMarkerRecord; /** * Built-in marker shapes for links. */ declare const namedLinkMarkers: { readonly none: null; readonly arrow: LinkMarkerRecord; readonly 'arrow-open': LinkMarkerRecord; readonly 'arrow-sunken': LinkMarkerRecord; readonly circle: LinkMarkerRecord; readonly diamond: LinkMarkerRecord; }; /** * The names of the built-in link markers you can pass to a {@link LinkStyle}: * `'arrow'`, `'arrow-open'`, `'arrow-sunken'`, `'circle'`, `'diamond'`, or * `'none'`. * @group Types */ type LinkMarkerName = keyof typeof namedLinkMarkers; /** * A link endpoint marker, either a built-in {@link LinkMarkerName} or a custom * {@link LinkMarkerRecord}. * @group Types */ type LinkMarker = LinkMarkerName | LinkMarkerRecord; /** * Normalizes a {@link LinkMarker} into a concrete {@link LinkMarkerRecord}. Looks * up a built-in name (`'arrow'`, `'circle'`, …) in the marker registry, passes a * custom record through unchanged, and returns `null` for `'none'`, `undefined`, * or an unknown name, meaning "draw no marker". * @param marker - a marker name, a custom marker record, `'none'`, or `undefined` * @returns The resolved marker record, or `null` when no marker should be drawn * @example * ```ts * import { resolveLinkMarker } from '@joint/react'; * * resolveLinkMarker('arrow'); // built-in arrow record * resolveLinkMarker('none'); // null * ``` * @group Presets */ declare function resolveLinkMarker(marker: LinkMarker | undefined): LinkMarkerRecord | null; /** * Visual styling for a link, the visible line plus its invisible pointer * hit-area wrapper. Hand it to {@link linkStyle} (or a link record's `style` * field); empty-string values fall back to the CSS variables in `theme.css`. * @group Types */ interface LinkStyle { /** Stroke color of the visible line. Any CSS color, including CSS variables. Empty inherits the theme default. @default '' */ color?: string; /** Stroke width of the visible line, a number (px) or CSS length string. Empty inherits the theme default. @default '' */ width?: number | string; /** Marker at the source end: a {@link LinkMarkerName}, a {@link LinkMarkerRecord}, or `'none'` for no marker. @default 'none' */ sourceMarker?: LinkMarker; /** Marker at the target end: a {@link LinkMarkerName}, a {@link LinkMarkerRecord}, or `'none'` for no marker. @default 'none' */ targetMarker?: LinkMarker; /** Extra CSS class added to the visible line. @default '' */ className?: string; /** Dash pattern in SVG `stroke-dasharray` syntax, e.g. `'5,5'` for a dashed line. @default '' */ dasharray?: string; /** Stroke line cap of the line ends. @default '' */ linecap?: LiteralUnion<'butt' | 'round' | 'square'>; /** Stroke line join at the line's corners. @default '' */ linejoin?: LiteralUnion<'miter' | 'round' | 'bevel'>; /** Stroke width, in px, of the transparent wrapper that widens the pointer hit area. @default 10 */ wrapperWidth?: number; /** Stroke color of the wrapper. Usually transparent, set it to make the hit area visible while debugging. @default 'transparent' */ wrapperColor?: string; /** Extra CSS class added to the wrapper. @default '' */ wrapperClassName?: string; } /** * Builds the SVG `attrs` for a link's visible `line` from a {@link LinkStyle}: * stroke color and width, source/target markers, dash pattern, and the line's * CSS class. This is the `line` half of {@link linkStyle}; reach for it when you * style the line and wrapper selectors separately. * @param style - link style to convert to SVG attributes * @returns SVG attributes for the `line` selector * @example * ```ts * import { LinkModel, linkStyleLine, linkStyleWrapper } from '@joint/react'; * * const style = { color: '#333', width: 2 }; * const link = new LinkModel({ * source: { id: 'a' }, target: { id: 'b' }, * attrs: { line: linkStyleLine(style), wrapper: linkStyleWrapper(style) }, * }); * ``` * @group Presets */ declare function linkStyleLine(style?: LinkStyle): Nullable; /** * Builds the SVG `attrs` for a link's `wrapper`, the wide invisible path around * the visible line that catches pointer events, from a {@link LinkStyle}. This is * the `wrapper` half of {@link linkStyle}; reach for it when you style the line * and wrapper selectors separately. * @param style - link style to convert to SVG attributes * @returns SVG attributes for the `wrapper` selector * @example * ```ts * import { LinkModel, linkStyleLine, linkStyleWrapper } from '@joint/react'; * * const style = { color: '#333', width: 2 }; * const link = new LinkModel({ * source: { id: 'a' }, target: { id: 'b' }, * attrs: { line: linkStyleLine(style), wrapper: linkStyleWrapper(style) }, * }); * ``` * @group Presets */ declare function linkStyleWrapper(style?: LinkStyle): Nullable; /** * Converts a {@link LinkStyle} into the JointJS SVG `attrs` object a link needs, * keyed by the `line` and `wrapper` selectors. Use it to set a link's `attrs` * directly, or rely on the `style` shorthand handled by {@link linkAttributes}. * @param style - link style to convert to SVG attributes * @returns An `attrs` object with `line` and `wrapper` entries * @example * ```ts * import { LinkModel, linkStyle } from '@joint/react'; * * const link = new LinkModel({ * source: { id: 'a' }, * target: { id: 'b' }, * attrs: linkStyle({ color: '#333', width: 2, targetMarker: 'arrow' }), * }); * ``` * @group Presets */ declare function linkStyle(style?: LinkStyle): Record>; /** * A simplified link label, text plus optional styling, that {@link linkLabel} * expands into the raw `dia.Link.Label` markup and attrs JointJS expects. * @group Types */ interface LinkLabel { /** The text shown on the link. */ text: string; /** Where the label sits along the link: 0–1 is a fraction of the path, values above 1 are an absolute distance in px. @default 0.5 */ position?: number; /** Shift the label off the path. A number nudges it perpendicular to the line; `{ x, y }` moves it freely. */ offset?: number | { x: number; y: number; }; /** Text color. Empty inherits the theme default. @default '' */ color?: string; /** Fill color of the label background. Empty inherits the theme default. @default '' */ backgroundColor?: string; /** * Padding between the text and the edge of its background. A number applies to * both axes; an object sets each axis. @default `{ horizontal: 4, vertical: 2 }` */ backgroundPadding?: number | { horizontal?: number; vertical?: number; }; /** Font size of the label text, in px. Empty inherits the theme default. @default '' */ fontSize?: number; /** Font family of the label text. Empty inherits the theme default. @default '' */ fontFamily?: string; /** Extra CSS class added to the label text element. @default '' */ className?: string; /** Outline (stroke) color of the label background. Empty for no outline. @default '' */ backgroundOutline?: string; /** Outline (stroke) width of the label background, in px. @default '' */ backgroundOutlineWidth?: number; /** Corner radius of the label background, in px. Applies to the `'rect'` shape. @default 4 */ backgroundBorderRadius?: number; /** Opacity of the label background, from 0 (transparent) to 1 (opaque). */ backgroundOpacity?: number; /** Extra CSS class added to the label background element. @default '' */ backgroundClassName?: string; /** Background outline shape: `'rect'`, `'ellipse'`, or a raw SVG path `d` string. @default 'rect' */ backgroundShape?: LiteralUnion<'rect' | 'ellipse'>; } /** * Converts a simplified {@link LinkLabel} (text, color, position, …) into the * `dia.Link.Label` JSON JointJS expects in a link's `labels` array. * @param label - The simplified link label to convert. * @returns A JointJS label entry ready to drop into `link.labels`. * @example * ```ts * import { LinkModel, linkLabel } from '@joint/react'; * * const link = new LinkModel({ * source: { id: 'a' }, * target: { id: 'b' }, * labels: [linkLabel({ text: 'flows to', color: '#333', position: 0.5 })], * }); * ``` * @group Presets */ declare function linkLabel(label: LinkLabel): dia.Link.Label; /** * Converts a keyed map of {@link LinkLabel} definitions into the array of JointJS * labels a link expects, running each through {@link linkLabel}. The map key * becomes the label's stable `id`, so you can address a label later without * relying on array order. * @param labels - The map of label id to its simplified definition. * @param labelStyle - Shared styling merged into every label before its own values. * @returns The labels array — each entry is a JointJS label whose `id` is its map key. * @example * ```ts * import { LinkModel, linkLabels } from '@joint/react'; * * const link = new LinkModel({ * source: { id: 'a' }, * target: { id: 'b' }, * labels: linkLabels( * { name: { text: 'orders' }, card: { text: '1..*', position: 0.9 } }, * { fontSize: 12 } // applied to every label * ), * }); * ``` * @group Presets */ declare function linkLabels(labels: Record, labelStyle?: Partial): Array; /** * Extra declarative link fields the React presets understand on top of a native * `dia.Link.Attributes`: `style`, `labelMap`, and `labelStyle`. * @group Types */ interface LinkPresetAttributes { /** Visual styling for the link's line and markers, expanded into `attrs` by {@link linkStyle}. */ style?: LinkStyle; /** Labels keyed by id; each value is a {@link LinkLabel} expanded by {@link linkLabels}, and its key becomes the label's stable `id`. */ labelMap?: Record; /** Shared {@link LinkLabel} styling merged into every `labelMap` entry before that entry's own values. */ labelStyle?: Partial; } /** * A link description for {@link linkAttributes}: native `dia.Link.Attributes` * plus the React preset shorthands `style`, `labelMap`, and `labelStyle`. No * `type` is required. * @expand * @group Types */ interface LinkAttributes extends dia.Link.Attributes, LinkPresetAttributes { } /** * Expands the declarative React link shorthand (`style`, `labelMap`) into the * native attributes JointJS understands, so you can describe a link with the * friendly preset fields instead of raw `attrs` and `labels`. * * - `style` → SVG `attrs` via {@link linkStyle}. * - `labelMap` → a native `labels` array via {@link linkLabels}. * - `labels` (array) → passed through unchanged. * @param link - The link record to convert. * @returns JointJS-compatible cell attributes. * @throws When `link` is not an object, or when both `labelMap` and `labels` are set on the same link. * @example * Expand the declarative preset input into native attributes and hand them to * a cell model: * ```tsx * import { LinkModel, linkAttributes } from '@joint/react'; * * const link = new LinkModel( * linkAttributes({ * source: { id: 'a' }, * target: { id: 'b' }, * style: { color: '#333', width: 2 }, // preset shorthand * labelMap: { mid: { text: 'edge', position: 0.5 } }, * }) * ); * ``` * @group Presets */ declare function linkAttributes(link: LinkAttributes): dia.Link.Attributes; /** * Shape of a port's body. * - `'ellipse'` renders an ellipse. * - `'rect'` renders a rectangle. * - Any other string is used directly as the SVG path `d` attribute. * @group Types */ type ElementPortShape = LiteralUnion<'ellipse' | 'rect'>; /** * Declarative port description for {@link elementPort} and {@link elementPorts}. * Captures the common port styling and label options in a flat shape, which the * presets expand into a full `dia.Element.Port`. * @group Types */ interface ElementPort { /** * Horizontal position of the port, relative to the element, for absolute * placement. Accepts a number or a `calc()` expression such as `'calc(w)'`. * Omit to let the port group position the port instead. */ cx?: number | string; /** * Vertical position of the port, relative to the element, for absolute * placement. Accepts a number or a `calc()` expression such as `'calc(h)'`. * Omit to let the port group position the port instead. */ cy?: number | string; /** Width of the port shape, in pixels. @default 8 */ width?: number; /** Height of the port shape, in pixels. @default 8 */ height?: number; /** Fill color of the port shape. Any CSS color; when empty the port inherits the `jj-port` stylesheet fill. @default '' */ color?: string; /** Shape of the port body. @default 'ellipse' */ shape?: ElementPortShape; /** Outline (stroke) color of the port shape. Any CSS color; when empty the stroke comes from the stylesheet. @default '' */ outline?: string; /** Outline (stroke) width of the port shape, in pixels. Empty leaves the stroke width unset. @default '' */ outlineWidth?: number; /** Extra CSS class added to the port shape alongside the built-in `jj-port` class. @default '' */ className?: string; /** Restricts the port to being a link target only; links cannot be started from it. @default false */ passive?: boolean; /** Text label rendered next to the port. Omit for an unlabeled port. */ label?: string; /** Placement of the label relative to the port, e.g. `'outside'`, `'inside'`, or a side name. @default 'outside' */ labelPosition?: string; /** Color of the label text. Any CSS color; when empty the color comes from the stylesheet. @default '' */ labelColor?: string; /** Font size of the label text, in pixels. When empty the size comes from the stylesheet. @default '' */ labelFontSize?: number; /** Font family of the label text. When empty the family comes from the stylesheet. @default '' */ labelFontFamily?: string; /** Extra CSS class added to the label alongside the built-in `jj-port-label` class. @default '' */ labelClassName?: string; /** Horizontal offset of the label from its computed position, in pixels. */ labelOffsetX?: number; /** Vertical offset of the label from its computed position, in pixels. */ labelOffsetY?: number; } /** * Builds a full `dia.Element.Port` from a declarative {@link ElementPort}. * When `cx`/`cy` are set the port is placed absolutely; when they are omitted the * port relies on the positioning of whatever group it is placed in. * @param port - The declarative port description to expand. * @returns A JointJS port object ready to drop into an element's `ports.items`. * @example * ```tsx * import { elementPort } from '@joint/react'; * * // Absolute placement via calc() expressions, relative to the element size. * const outlet = elementPort({ cx: 'calc(w)', cy: 'calc(h/2)', color: 'red' }); * * // No cx/cy: the port group decides where it sits. * const inlet = elementPort({ shape: 'rect', width: 12, height: 12 }); * ``` * @group Presets */ declare function elementPort(port: ElementPort): dia.Element.Port; /** * Expands a map of declarative {@link ElementPort}s into a full JointJS `ports` * object (a `groups` definition plus the `items` array). Every port is placed * absolutely under a single `'main'` group, keyed by its map id. * @param ports - Map of port id to its {@link ElementPort} description. * @param portStyle - Shared defaults merged under each port; per-port values win. * @returns The `ports` object to assign to an element's attributes. * @example * ```tsx * import { elementPorts } from '@joint/react'; * * // One output port on the right edge, vertically centered. * const ports = elementPorts({ out: { cx: 'calc(w)', cy: 'calc(h/2)' } }); * ``` * @group Presets */ declare function elementPorts(ports: Record, portStyle?: Partial): { groups: Record; items: dia.Element.Port[]; }; /** * React-side declarative fields the preset adds on top of `dia.Element.Attributes`. * Composed orthogonally into both `ElementAttributes` (preset input) and * `ElementJSONInit` (record/mapper boundary). * @group Types */ interface ElementPresetAttributes { /** Ports keyed by id; each value is an {@link ElementPort} expanded into native `ports` by {@link elementPorts}, and its key becomes the port id. */ portMap?: Record; /** Shared {@link ElementPort} styling merged into every `portMap` entry before that entry's own values. */ portStyle?: Partial; } /** * Loose preset input, no `type` required. `dia.Element.Attributes` plus the * React preset extras (`portMap`, `portStyle`). * @expand * @group Types */ interface ElementAttributes extends dia.Element.Attributes, ElementPresetAttributes { } /** * Normalizes a declarative element description into JointJS cell attributes. * The `portMap` shorthand is expanded into native `ports` via {@link elementPorts} * (and kept on the result as `portMap`); a native `ports` value is passed through * untouched. Use it when feeding a {@link ElementModel} or building a model's * `defaults()`. * @param element - The declarative element description to convert. * @returns Attributes ready to pass to an element model. * @throws TypeError when `element` is not a plain object. * @throws Error when both `portMap` and `ports` are supplied. * @example * Feed an element model: * ```tsx * import { ElementModel, elementAttributes } from '@joint/react'; * * const element = new ElementModel( * elementAttributes({ * type: 'standard.Rectangle', * position: { x: 10, y: 20 }, * size: { width: 120, height: 40 }, * portMap: { in: { color: '#fff', cx: 0, cy: 0.5 } }, // preset shorthand * }) * ); * ``` * @example * Build a custom model's defaults: * ```tsx * import { ElementModel, elementAttributes } from '@joint/react'; * * class RectShape extends ElementModel { * defaults() { * return { * ...super.defaults(), * ...elementAttributes({ * type: 'standard.Rectangle', * size: { width: 120, height: 40 }, * }), * }; * } * } * ``` * @group Presets */ declare function elementAttributes(element: ElementAttributes): dia.Element.Attributes; /** * Loose element shape accepted at the record/mapper boundary: a `dia.Element` * JSON init (optional `id`, plus `type` and visual attrs) with the React preset * extras and an optional typed `data` payload. */ interface ElementJSONInit extends Element$1.JSONInit, ElementPresetAttributes { data?: unknown; } /** * Loose link shape accepted at the record/mapper boundary: a `dia.Link` JSON * init (optional `id`, plus `type` and visual attrs) with the React preset * extras and an optional typed `data` payload. */ interface LinkJSONInit extends Link.JSONInit, LinkPresetAttributes { data?: unknown; } type PickRequired = T & { [P in K]-?: T[P]; }; /** Known cell type names. */ type KnownCellType = typeof ELEMENT_MODEL_TYPE | typeof LINK_MODEL_TYPE; interface WithType { readonly type: Type; } type WithData = unknown extends Data ? { readonly data?: unknown; } : { readonly data: Data; }; /** * Plain-object description of one element: your custom `data` plus the visual * fields JointJS understands (`position`, `size`, `angle`, `attrs`, ports, and * the `portMap`/`portStyle` preset extras). Use it for `initialCells` entries * and when adding or updating cells; reading hooks hand back its * `Computed` form (fields the store always populates are required). * * `Type` defaults to `'element'` so `cell.type === 'element'` narrows the * {@link CellRecord} union; set it to a shape name (e.g. `'standard.Rectangle'`) * for a built-in or custom shape. * @template ElementData - shape of the custom `data` payload carried on the element * @template Type - the `type` discriminator literal * @group Types */ type ElementRecord = ElementJSONInit & WithType & WithData; /** * Internal element record shape, what the store holds after JointJS / * {@link elementAttributes} defaults are applied. Reach via {@link Computed} * (`Computed>`); kept private so the public surface is * a single utility. * * Always populated by the framework: * - `position`, `dia.Element` defaults to `{ x: 0, y: 0 }`. * - `size`, `dia.Element` defaults to `{ width: 1, height: 1 }`. * - `angle`, `dia.Element` defaults to `0`. * - `data`, {@link elementAttributes} defaults to `{} as ElementData`. */ type InternalElementRecord = PickRequired, 'id' | 'type' | 'position' | 'size' | 'angle' | 'data'>; /** * Plain-object description of one link: your custom `data` plus the visual * fields JointJS understands (`source`, `target`, `attrs`, labels, and the * `style`/`labelMap`/`labelStyle` preset extras). Use it for `initialCells` * entries and when adding or updating cells; reading hooks hand back its * `Computed` form (fields the store always populates are required). * * `Type` defaults to `'link'` so `cell.type === 'link'` narrows the * {@link CellRecord} union; set it to a shape name (e.g. `'standard.Link'`) for * a built-in or custom shape. * @template LinkData - shape of the custom `data` payload carried on the link * @template Type - the `type` discriminator literal * @group Types */ type LinkRecord = LinkJSONInit & WithType & WithData; /** * Internal link record shape, what the store holds after JointJS / * {@link linkAttributes} defaults are applied. Reach via {@link Computed} * (`Computed>`); kept private so the public surface is a * single utility. * * Always populated by the framework: * - `source`, `dia.Link` defaults to `{}`. * - `target`, `dia.Link` defaults to `{}`. * - `data`, {@link linkAttributes} defaults to `{} as LinkData`. */ type InternalLinkRecord = PickRequired, 'id' | 'type' | 'source' | 'target' | 'data'>; /** * One cell — either an {@link ElementRecord} or a {@link LinkRecord} — as a * discriminated union on `type`: * - `type === 'element'` → {@link ElementRecord} * - `type === 'link'` → {@link LinkRecord} * * Because it discriminates on `type`, `if (cell.type === 'element')` narrows * correctly inside arrays and hooks. For mixed built-in shape arrays with typed * data, build the union yourself: * `ElementRecord | LinkRecord`. * @template ElementData - shape of the custom `data` payload on elements * @template LinkData - shape of the custom `data` payload on links * @template ElementType - the element `type` discriminator literal * @template LinkType - the link `type` discriminator literal * @group Types */ type CellRecord = ElementRecord | LinkRecord; /** * The most permissive {@link CellRecord}: `data` is `unknown` and `type` is any * string. Reach for it when you don't need the default `'element'` / `'link'` * discrimination — for example an `initialCells` array mixing built-in shape * types, or a generic upper bound in a custom hook. * @group Types */ type AnyCellRecord = CellRecord; /** * Resolves any input cell shape to its internal store form, the variant with * framework-populated fields (`id`, `position`, `size`, `angle`, `data` for * elements; `id`, `source`, `target`, `data` for links) required. * * Distributes over unions, so a single utility covers every input flavor: * * | Input | Result | * |------------------------------------|-----------------------------------| * | `Computed>` | element with required fields | * | `Computed>` | link with required fields | * | `Computed>` | resolved element or resolved link | * * To keep a custom record's exact shape, compose it OUTSIDE the wrapper, e.g. * `Computed | MyCustomRecord`. Passing a custom element- or * link-shaped record (any object with a `type` field) directly through * `Computed` re-maps it to the internal element/link record, because it * structurally matches the same branch as {@link ElementRecord} / * {@link LinkRecord}. * * Reading hooks ({@link useCell}, {@link useCells}) yield the `Computed` variant so * consumers don't need `?? {}` / `?? 0` fallbacks for fields the store * always populates. * @template T - the input cell shape (record or union) to resolve * @example * ```ts * import { useCell } from '@joint/react'; * import type { Computed, ElementRecord } from '@joint/react'; * * interface MyData { * label: string; * } * * const label = useCell((el: Computed>) => el.data.label); * ``` * @group Types */ type Computed = T extends ElementRecord ? InternalElementRecord : T extends LinkRecord ? InternalLinkRecord : T extends ElementJSONInit ? InternalElementRecord : T extends LinkJSONInit ? InternalLinkRecord : T; /** * Short alias for cell ids; same as `dia.Cell.ID`. * @group Types */ type CellId = Cell.ID; /** * An element's top-left position, `{ x, y }`. Alias for `dia.Point`. * @group Types */ type ElementPosition = Point; /** * An element's bounding-box size, `{ width, height }`. Alias for `dia.Size`. * @group Types */ type ElementSize = Size; /** * Flat element layout used internally by the size observer and transform callbacks. * @internal */ interface ElementLayout { readonly x: number; readonly y: number; readonly width: number; readonly height: number; readonly angle: number; } /** * Resolved geometry of one link on a specific paper: the source and target * endpoint coordinates plus the rendered SVG path. Returned by * {@link useLinkLayout} so you can draw or measure alongside a link. * @group Types */ interface LinkLayout { /** X coordinate of the link's source endpoint, in paper coordinates. */ readonly sourceX: number; /** Y coordinate of the link's source endpoint, in paper coordinates. */ readonly sourceY: number; /** X coordinate of the link's target endpoint, in paper coordinates. */ readonly targetX: number; /** Y coordinate of the link's target endpoint, in paper coordinates. */ readonly targetY: number; /** SVG path data (the `d` attribute) for the link's rendered route. */ readonly d: string; } /** * What you may pass when handing a cell to the library: either a plain element * or link record, or a live `dia.Cell` instance. Accepted by every * cell-mutation entry point — `initialCells`, `resetCells`, and the cell setters. * @template Element - element record shape * @template Link - link record shape * @group Types */ type CellInput = Element | Link | Cell; /** * A reference to a cell — either its {@link CellId} or the `dia.Cell` instance * itself. Alias for JointJS core's `dia.Graph.CellRef`. * @group Types */ type CellRef = Graph.CellRef; declare const Paper: typeof dia.Paper; /** Well-known paper ID used when no explicit `id` is provided to ``. */ declare const DEFAULT_PAPER_ID = "default-paper"; /** * Extended Paper class that manages React view lifecycle. * * PaperView centralizes view management by: * - Emitting view mount/unmount callbacks for graph-store snapshot sync * - Hiding links until their source/target elements have rendered */ declare class PaperView extends Paper { viewChanges: Map>; onViewMountChange: (changes: Map>) => void; private readonly shouldPreserveHostElementOnRemove; private readonly portalSelector; private pendingLinks; private portalObserver; private observedPortalNodes; private pendingEndpointIds; constructor(options: PaperViewOptions); /** * Preserves externally managed host elements (e.g. React refs) on cleanup. */ protected _removeElement(): void; getElementView(id: CellId): dia.ElementView | undefined; getLinkView(id: CellId): dia.LinkView | undefined; /** * Mounts the paper DOM element into the provided host element. * This is used by React wrappers ({@link Paper}, `PaperScroller`) to control where * JointJS paper DOM is attached. * @param element - The host element where paper should be rendered. * @returns The same PaperView instance for chaining. */ render(element?: HTMLElement | SVGElement): this; /** * Resolves the portal target node from a cell view. * * Resolution order: * 1. Paper-level `portalSelector` option if set. * 2. The cell's own `portalSelector` field ({@link ElementModel} → `'__portal__'`, * {@link LinkModel} → `'root'`). Cells without the field are skipped. * @param cellView - The cell view to resolve the portal node for. * @returns The portal DOM node, or null if not found / skipped. */ getCellViewPortalNode(cellView: dia.CellView): SVGElement | HTMLElement | null; /** * Check if an element view has rendered its React content. * @param elementId - The element identifier to check. * @returns True if the element view has rendered content. */ private isElementReady; /** * Check whether a link end can be rendered immediately. * @param end - The link end JSON descriptor. * @returns True if the link end's target cell is ready. */ private isLinkEndReady; private bumpPendingEndpoint; /** * Observe one parked endpoint's portal node, so content mounted by a * child-only React update still triggers a recheck. O(1) per call — a * park observes its own two endpoints, an element mount observes itself. */ private observeEndpoint; private disconnectPortalObserver; /** * Check pending links and show them if their source/target are ready. */ checkPendingLinks(): void; onViewMountChangeFlush(): void; /** * Notify graph-store that a mounted view has been unmounted. * @param cell - The cell whose view was unmounted. */ private notifyViewUnmount; /** * Called when a view is mounted into the DOM. * @param view - The cell view being inserted. * @param isInitialInsert - Whether this is the initial insert during rendering. */ insertView(view: dia.CellView, isInitialInsert: boolean): void; /** * Called when a cell is deleted from the graph. * @param cell - The cell being removed. * @returns The removed cell view. */ removeView(cell: dia.Cell): dia.CellView; remove(): this; /** * Bit flag for marking views that have been measured by {@link useMeasureElement} * and are awaiting size/position updates. * * Bit 27 is chosen to sit immediately below joint-core's reserved view * flags without colliding with them: * - `FLAG_INSERT = 1 << 30` * - `FLAG_REMOVE = 1 << 29` * - `FLAG_INIT = 1 << 28` * (see `mvc/View.mjs` in @joint/core). Cell-view subclasses use the * low bits (0..N) for their own dirty flags, so 1 << 27 keeps us clear * of both ends of the bitfield. */ FLAG_MEASURE: number; /** * Removes the `MEASURING_CLASS_NAME` class from element views once * `updateView` has applied the latest size and position written to the * model. Removing the class here (rather than when the ResizeObserver * fires) avoids the flash at the pre-update position that happens when * size lands on the model before position. * @param view - The view being updated. * @param flagIn - Bitmask of pending update flags. * @param opt - Update options forwarded to `view.confirmUpdate`. * @returns Leftover flag count, as returned by the base `updateView`. */ updateView(view: mvc.View, flagIn: number, opt?: dia.Paper.UpdateViewOptions): number; /** * Called when a view is hidden (viewport culling). * @internal * @param cellView - The cell view being hidden. */ _hideCellView(cellView: dia.CellView): void; } /** * One end (source or target) of a link: the cell it attaches to and the exact * port or magnet within that cell. Shared by {@link ValidateConnectionParams} * and emitted on `link:connect` / `link:disconnect` events, so validation and * event payloads describe a connection the same way. * @group Types * @expand */ interface ConnectionEnd { /** ID of the cell this end attaches to. */ readonly id: dia.Cell.ID; /** The cell model (element or link) this end attaches to. */ readonly model: dia.Cell; /** ID of the port the end attaches to, or `null` when it attaches to the cell body. */ readonly port: string | null; /** The SVG magnet node the end attaches to, or `null` when attaching to the cell's root. */ readonly magnet: Element | null; /** Value of the magnet's `joint-selector` attribute, or `null` when it has none. */ readonly selector: string | null; } /** * Context handed to a {@link ValidateConnection} callback (and to the `validate` * option of {@link CanConnectOptions}) while the user drags a link end. Describes * both ends of the pending connection along with the paper and graph it lives in. * @group Types * @expand */ interface ValidateConnectionParams { /** The source end of the pending connection. */ readonly source: ConnectionEnd; /** The target end of the pending connection. */ readonly target: ConnectionEnd; /** Which end the user is dragging: `'source'` or `'target'`. */ readonly endType: dia.LinkEnd; /** The paper the link is being drawn on. */ readonly paper: dia.Paper; /** The graph the link belongs to. */ readonly graph: dia.Graph; } /** * Decides whether a link may connect its source end to its target end. Return * `true` to allow the connection, `false` to reject it. Pass it (or a * {@link CanConnectOptions} object) to the `validateConnection` prop of ``; * the callback receives a structured {@link ValidateConnectionParams} context. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { ValidateConnection } from '@joint/react'; * * // Only accept links that end on a port named "in". * const validate: ValidateConnection = ({ target }) => target.port === 'in'; * * * } /> * ; * ``` */ type ValidateConnection = (context: ValidateConnectionParams) => boolean; /** * Options for the `validateConnection` prop of `` — declarative rules * that toggle the common connection constraints (self-loops, link-to-link, * duplicate links, root connections) and optionally layer your own check on top. * Reach for this object instead of a {@link ValidateConnection} callback when the * built-in rules already cover what you need. * @group Types * @expand * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * * * target.port === 'in', * }} * renderElement={() => } * /> * ; * ``` */ interface CanConnectOptions { /** Allow a cell to connect to itself. @default false */ readonly allowSelfLoops?: boolean; /** Allow links to start or end on another link, not just on elements. @default false */ readonly allowLinkToLink?: boolean; /** * How many links may connect the same source+port and target+port. Matching * is port/magnet aware, so links on different ports never collide. * - `'none'` — no limit; any number of links, in either direction. * - `'one-per-direction'` — one link each way: a second `A→B` is blocked, but * the reverse `B→A` is allowed. * - `'one-per-pair'` — one link per element pair: `A→B` blocks both another * `A→B` and the reverse `B→A`. * @default 'one-per-direction' */ readonly linkLimit?: 'none' | 'one-per-direction' | 'one-per-pair'; /** * Whether a link may attach to an element's root (its body) instead of a port or magnet. * - `true` — always allow root connections. * - `false` — never allow them; require a port or magnet. * - `'auto'` — allow a root connection only when the element has no ports. @default 'auto' */ readonly allowRootConnection?: boolean | 'auto'; /** Extra check run only after every built-in rule passes; receives the same {@link ValidateConnectionParams} context as {@link ValidateConnection}. */ readonly validate?: (context: ValidateConnectionParams) => boolean; } /** * Context handed to a {@link ValidateEmbedding} callback while an element is * dragged over a candidate parent. Use it to compare the dragged `child` against * the `parent` it would drop into. * @group Types * @expand */ interface ValidateEmbeddingParams { /** The element being dragged (the would-be child). */ readonly child: { readonly id: dia.Cell.ID; readonly model: dia.Element; }; /** The element it would be embedded into (the would-be parent). */ readonly parent: { readonly id: dia.Cell.ID; readonly model: dia.Element; }; /** The paper the elements live on. */ readonly paper: dia.Paper; /** The graph the elements belong to. */ readonly graph: dia.Graph; } /** * Context handed to a {@link ValidateUnembedding} callback when an embedded * element is dragged out of its parent. * @group Types * @expand */ interface ValidateUnembeddingParams { /** The embedded element being dragged out of its parent. */ readonly child: { readonly id: dia.Cell.ID; readonly model: dia.Element; }; /** The paper the element lives on. */ readonly paper: dia.Paper; /** The graph the element belongs to. */ readonly graph: dia.Graph; } /** * Decides whether a dragged element may be embedded into a parent element. * Return `true` to allow the drop, `false` to reject it. Pass it to the * `validateEmbedding` prop of ``; the callback receives a structured * {@link ValidateEmbeddingParams} context. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { ValidateEmbedding } from '@joint/react'; * * // Only "container" elements may accept children. * const validate: ValidateEmbedding = ({ parent }) => parent.model.get('type') === 'container'; * * * } /> * ; * ``` */ type ValidateEmbedding = (context: ValidateEmbeddingParams) => boolean; /** * Decides whether an embedded element may be detached from its parent. Return * `true` to allow detaching, `false` to keep it embedded. Pass it to the * `validateUnembedding` prop of ``; the callback receives a structured * {@link ValidateUnembeddingParams} context. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { ValidateUnembedding } from '@joint/react'; * * // Keep "locked" elements embedded; everything else can be dragged out. * const validate: ValidateUnembedding = ({ child }) => !child.model.get('locked'); * * * } /> * ; * ``` */ type ValidateUnembedding = (context: ValidateUnembeddingParams) => boolean; /** * Context handed to a {@link ConnectionStrategy} `customize` callback after a * link end is dropped. Describes where the end landed (cell, magnet, drop point) * so the callback can return the final end definition. * @group Types * @expand */ interface ConnectionStrategyParams { /** The end definition to return, pre-filled by the selected pin mode (or the dropped end when pinning is off). */ readonly end: dia.Link.EndJSON; /** The cell the end was dropped on. */ readonly model: dia.Cell; /** The magnet element the end was dropped on. */ readonly magnet: Element; /** Paper-space point where the end was dropped. */ readonly dropPoint: dia.Point; /** Which end was dropped: `'source'` or `'target'`. */ readonly endType: dia.LinkEnd; /** The link being reconnected. */ readonly link: dia.Link; /** The paper the link is drawn on. */ readonly paper: dia.Paper; /** The graph the link belongs to. */ readonly graph: dia.Graph; } /** * How a dropped link end is anchored to its target: * - `'none'` — keep the JointJS defaults (attach to the cell/port, no fixed anchor). * - `'absolute'` — pin the end at a fixed pixel offset within the target. * - `'relative'` — pin the end at an offset given as a percentage of the target's size, so it tracks resizing. * @group Types */ type ConnectionStrategyPin = 'none' | 'absolute' | 'relative'; /** * Options for the `connectionStrategy` prop of ``: pick how a dropped link * end is pinned and, optionally, post-process the resulting end definition. * @group Types * @expand * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * * // Pin dropped ends at a relative (%) offset of their target. * * } /> * ; * ``` */ interface ConnectionStrategyOptions { /** How to anchor the dropped end, see {@link ConnectionStrategyPin}. @default 'none' */ readonly pin?: ConnectionStrategyPin; /** Runs after pinning; receives the already-pinned end (or the original when `pin` is `'none'`) and returns the final end definition. */ readonly customize?: ConnectionStrategy; } /** * Computes the final end definition stored on a link after one of its ends is * dropped. Pass it (or a {@link ConnectionStrategyOptions} object) to the * `connectionStrategy` prop of ``; the callback receives a structured * {@link ConnectionStrategyParams} context and returns the end JSON to save. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { ConnectionStrategy } from '@joint/react'; * * // Snap the dropped end exactly to the pointer position. * const strategy: ConnectionStrategy = ({ end, dropPoint }) => ({ ...end, x: dropPoint.x, y: dropPoint.y }); * * * } /> * ; * ``` */ type ConnectionStrategy = (context: ConnectionStrategyParams) => dia.Link.EndJSON; /** * Context handed to a {@link CellVisibility} callback each time the paper decides * whether to render a cell. * @group Types * @expand */ interface CellVisibilityParams { /** The cell whose visibility is being decided. */ readonly model: dia.Cell; /** Whether the cell currently has a view mounted in the paper. */ readonly isMounted: boolean; /** The paper rendering the cell. */ readonly paper: dia.Paper; /** The graph the cell belongs to. */ readonly graph: dia.Graph; } /** * Decides whether a cell is rendered on the paper. Return `false` to skip * rendering it (handy for viewport culling or hiding cells by state), `true` to * render it. Pass it to the `cellVisibility` prop of ``; the callback * receives a structured {@link CellVisibilityParams} context instead of the * native positional arguments. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { CellVisibility } from '@joint/react'; * * // Hide every cell flagged as collapsed. * const cellVisibility: CellVisibility = ({ model }) => !model.get('collapsed'); * * * } /> * ; * ``` */ type CellVisibility = (context: CellVisibilityParams) => boolean; /** Name of a cell interactivity feature (e.g. `'elementMove'`, `'labelMove'`, `'linkMove'`). */ type CellInteraction = keyof dia.CellView.InteractivityOptions; /** * Context handed to the function form of {@link CellInteractivity}. JointJS calls * the callback once per cell with that cell, then reads the specific interactivity * flag it needs from the value you return. In practice only `model` is reliably * populated — see the individual fields. * @group Types * @expand */ interface CellInteractivityParams { /** The cell whose interactivity is being resolved. */ readonly model: dia.Cell; /** * Always `undefined` at runtime: JointJS does not pass an interaction name to the * callback. It reads the relevant flag from the value you return instead. */ readonly interaction: CellInteraction; /** * The cell's paper. JointJS binds the callback to the cell view's options rather * than to the `dia.Paper`, so this is not guaranteed to be the paper; capture the * `dia.Paper` from your own scope if you need it. */ readonly paper: dia.Paper; /** * The graph the cell belongs to. Derived from `paper`, so it carries the same * caveat; capture the `dia.Graph` from your own scope if you need it. */ readonly graph: dia.Graph; } /** * Function form of the `interactive` Paper prop. Receives a structured * context (instead of the native positional `(cellView)` form) and * returns either a boolean or the native `InteractivityOptions` object. * @group Types */ type CellInteractivityCallback = (context: CellInteractivityParams) => boolean | dia.CellView.InteractivityOptions; /** * Controls which pointer interactions (moving, linking, label dragging, …) are * enabled on cells. Accepts a boolean to switch everything on or off, an * `InteractivityOptions` object to toggle individual interactions, or a function * that returns either form per cell from a {@link CellInteractivityParams} * context. Pass it to the `interactive` prop of ``. By default, label * dragging (`labelMove`) and moving links (`linkMove`) stay disabled; link * endpoints (`arrowheadMove`) remain draggable. * @group Types * @example * ```tsx * import { GraphProvider, Paper } from '@joint/react'; * import type { CellInteractivity } from '@joint/react'; * * // Lock cells flagged as readonly; leave the rest interactive. * const interactive: CellInteractivity = ({ model }) => !model.get('readonly'); * * * } /> * ; * ``` */ type CellInteractivity = boolean | dia.CellView.InteractivityOptions | CellInteractivityCallback; /** * Chooses which side of an element or port a link attaches to when using the * mid-side anchor. `'auto'` picks the side nearest the other end; `'horizontal'` * and `'vertical'` lock to left/right or top/bottom; the `'prefer-*'` variants * favor one axis but fall back to the other; and the directional pairs * (`'top-bottom'`, `'left-right'`, etc.) pin the source and target to opposite * sides. * @group Types */ type LinkMode = 'prefer-horizontal' | 'prefer-vertical' | 'horizontal' | 'vertical' | 'auto' | 'top-bottom' | 'bottom-top' | 'left-right' | 'right-left'; /** * Bundle of paper-level link defaults (router, connector, anchor, connection point) * produced by a routing preset like {@link linkRoutingStraight} or * {@link linkRoutingOrthogonal}. * @group Types */ interface LinkRouting { /** Paper-level default router this preset installs for links that don't set their own. */ readonly defaultRouter?: dia.Paper.Options['defaultRouter']; /** Paper-level default connector that draws the routed points into the link's path. */ readonly defaultConnector?: dia.Paper.Options['defaultConnector']; /** Paper-level default anchor that decides where on an element each link end attaches. */ readonly defaultAnchor?: dia.Paper.Options['defaultAnchor']; /** Paper-level default connection point where the link meets the element boundary. */ readonly defaultConnectionPoint?: dia.Paper.Options['defaultConnectionPoint']; } interface BaseLinkOptions { /** Which side of an element or port each link end attaches to; see {@link LinkMode} for how each value behaves. @default 'auto' */ readonly mode?: LinkMode; /** Offset (in px) applied to the connection point at the source end. @default 0 */ readonly sourceOffset?: number; /** Offset (in px) applied to the connection point at the target end. @default 0 */ readonly targetOffset?: number; /** Fall back to a straight line while either end is still unconnected (e.g. mid-drag). @default true */ readonly straightWhenDisconnected?: boolean; /** The attrs selector that holds the marker definitions. @default 'line' */ readonly markerSelector?: string; } /** * Options for {@link linkRoutingStraight}. * @remarks The inherited `mode` and `straightWhenDisconnected` options have no * effect on straight routing; they apply only to {@link linkRoutingOrthogonal} * and {@link linkRoutingSmooth}. * @group Types * @expand */ interface LinkRoutingStraightOptions extends BaseLinkOptions { /** Corner style applied at manual vertices. @default 'point' */ readonly cornerType?: 'point' | 'cubic' | 'line' | 'gap'; /** Corner radius at vertices, in px. @default 0 */ readonly cornerRadius?: number; /** Anchor links perpendicular to the element edge instead of at its center. @default false */ readonly perpendicular?: boolean; } /** * Straight-line routing: links are drawn as a direct line from source to target, * with no obstacle avoidance. The simplest, lowest-overhead routing. * * Returns a `LinkRouting` bundle for the {@link Paper} `linkRouting` prop that * sets the paper's router, connector, anchor, and connection point in one step. * For other looks, reach for {@link linkRoutingOrthogonal} (right-angle segments * that steer around elements) or {@link linkRoutingSmooth} (curved links). * @param options - overrides for corner style, anchor, and connection-point offsets * @returns Paper link defaults for straight routing * @example * ```tsx * import { Paper, linkRoutingStraight } from '@joint/react'; * * * ``` * @group Presets */ declare function linkRoutingStraight(options?: LinkRoutingStraightOptions): LinkRouting; /** * Options for {@link linkRoutingOrthogonal}. * @group Types * @expand */ interface LinkRoutingOrthogonalOptions extends BaseLinkOptions { /** Corner style at each bend. @default 'cubic' */ readonly cornerType?: 'point' | 'cubic' | 'line' | 'gap'; /** Corner radius of the rounded bends, in px. @default 8 */ readonly cornerRadius?: number; /** Distance, in px, the route keeps clear of elements as it steers around them. @default 20 */ readonly margin?: number; /** Smallest distance, in px, the router travels before it can turn. @default margin / 4 */ readonly minPathMargin?: number; } /** * Orthogonal routing: links travel in horizontal and vertical segments only and * steer around elements, the right-angle look common in flowcharts and ER * diagrams. * * Returns a `LinkRouting` bundle for the {@link Paper} `linkRouting` prop. * @param options - overrides for corner style/radius, routing margins, and anchors * @returns Paper link defaults for orthogonal routing * @example * ```tsx * import { Paper, linkRoutingOrthogonal } from '@joint/react'; * * // round the bends and keep links 24px clear of elements * * ``` * @group Presets */ declare function linkRoutingOrthogonal(options?: LinkRoutingOrthogonalOptions): LinkRouting; /** * Options for {@link linkRoutingSmooth}. * @group Types * @expand */ interface LinkRoutingSmoothOptions extends BaseLinkOptions { } /** * Smooth routing: links are drawn as soft bezier curves instead of straight or * right-angle segments, for a more organic look. * * Returns a `LinkRouting` bundle for the {@link Paper} `linkRouting` prop. * @param options - overrides for anchor and connection-point offsets * @returns Paper link defaults for smooth routing * @example * ```tsx * import { Paper, linkRoutingSmooth } from '@joint/react'; * * * ``` * @group Presets */ declare function linkRoutingSmooth(options?: LinkRoutingSmoothOptions): LinkRouting; interface BaseContext { readonly paper: dia.Paper; readonly graph: dia.Graph; } interface CellContext { readonly id: dia.Cell.ID; readonly model: dia.Cell; readonly view: dia.CellView; } interface ElementContext { readonly id: dia.Cell.ID; readonly model: dia.Element; readonly view: dia.ElementView; } interface LinkContext { readonly id: dia.Cell.ID; readonly model: dia.Link; readonly view: dia.LinkView; } type CellEventParams = BaseContext & CellContext; type ElementEventParams = BaseContext & ElementContext; type LinkEventParams = BaseContext & LinkContext; type WithPointer = Params & { readonly event: dia.Event; readonly x: number; readonly y: number; }; type WithHover = Params & { readonly event: dia.Event; }; type WithWheel = WithPointer & { readonly delta: number; }; /** Pointer-style cell-level payload (down/move/up/click/dblclick/contextmenu). */ type PointerCellEventParams = WithPointer; /** Pointer-style element-level payload. */ type PointerElementEventParams = WithPointer; /** Pointer-style link-level payload. */ type PointerLinkEventParams = WithPointer; /** Pointer-style blank-area payload — event + coords on empty paper area. */ type PointerBlankEventParams = WithPointer; /** Hover-style cell-level payload (mouseenter/leave/over/out). */ type HoverCellEventParams = WithHover; /** Hover-style element-level payload. */ type HoverElementEventParams = WithHover; /** Hover-style link-level payload. */ type HoverLinkEventParams = WithHover; /** Hover-style blank-area payload — event only on empty paper area. */ type HoverBlankEventParams = WithHover; /** Wheel cell-level payload (mousewheel) — pointer + delta. */ type WheelCellEventParams = WithWheel; /** Wheel element-level payload. */ type WheelElementEventParams = WithWheel; /** Wheel link-level payload. */ type WheelLinkEventParams = WithWheel; /** Wheel blank-area payload — pointer + delta on empty paper area. */ type WheelBlankEventParams = WithWheel; /** Magnet payload — element-only, pointer + magnet SVG node + port/selector. */ type MagnetEventParams = WithPointer & { readonly magnet: DOMElement; /** The port ID, or `null` if the magnet is not on a port. */ readonly port: string | null; /** The `joint-selector` attribute of the magnet, or `null`. */ readonly selector: string | null; }; /** Paper-edge hover payload (`paper:mouseenter` / `paper:mouseleave`). */ type PaperHoverEventParams = BaseContext & { readonly event: dia.Event; }; /** Paper-level pan payload — `paper:pan` from touchpad / wheel pan. */ type PaperPanEventParams = BaseContext & { readonly event: dia.Event; readonly deltaX: number; readonly deltaY: number; }; /** Paper-level pinch payload — `paper:pinch` from touchpad pinch gesture. */ type PaperPinchEventParams = BaseContext & { readonly event: dia.Event; readonly x: number; readonly y: number; readonly scale: number; }; /** `translate` payload — paper translation. */ type TranslateEventParams = BaseContext & { readonly translateX: number; readonly translateY: number; readonly options: unknown; }; /** `scale` payload — paper scale. */ type ScaleEventParams = BaseContext & { readonly scaleX: number; readonly scaleY: number; readonly options: unknown; }; /** `resize` payload — paper dimensions. */ type ResizeEventParams = BaseContext & { readonly width: number; readonly height: number; readonly options: unknown; }; /** `transform` payload — paper SVG transform matrix. */ type TransformEventParams = BaseContext & { readonly matrix: SVGMatrix; readonly options: unknown; }; /** * `link:connect` / `link:disconnect` payload, the link + the cell at the * (dis)connected end as a {@link ConnectionEnd} (same shape used by * `validateConnection`, so the two stay symmetric). * @group Types */ interface LinkConnectEventParams extends LinkEventParams { readonly event: dia.Event; /** Which end of the link was (dis)connected. */ readonly end: 'source' | 'target'; /** Cell at the (dis)connected end. Always present, these events fire only on actual cells. */ readonly endCell: ConnectionEnd; } /** * Camel-cased, params-object handlers for the most common `dia.Paper` events. * Mixable with raw `'element:pointerclick'` keys in the same handlers map. * Paper-level events that stay raw: `'resize'`, `'transform'`, `'scale'`, * `'translate'`, `'render:done'`, `'render:idle'`, `'cell:highlight'`, * `'cell:unhighlight'`, `'cell:highlight:invalid'`, `'link:snap:connect'`, * `'link:snap:disconnect'`. * @group Types */ interface PaperEventHandlers { readonly onCellPointerDown?: (params: PointerCellEventParams) => void; readonly onCellPointerMove?: (params: PointerCellEventParams) => void; readonly onCellPointerUp?: (params: PointerCellEventParams) => void; readonly onCellPointerClick?: (params: PointerCellEventParams) => void; readonly onCellPointerDblClick?: (params: PointerCellEventParams) => void; readonly onCellContextMenu?: (params: PointerCellEventParams) => void; readonly onElementPointerDown?: (params: PointerElementEventParams) => void; readonly onElementPointerMove?: (params: PointerElementEventParams) => void; readonly onElementPointerUp?: (params: PointerElementEventParams) => void; readonly onElementPointerClick?: (params: PointerElementEventParams) => void; readonly onElementPointerDblClick?: (params: PointerElementEventParams) => void; readonly onElementContextMenu?: (params: PointerElementEventParams) => void; readonly onLinkPointerDown?: (params: PointerLinkEventParams) => void; readonly onLinkPointerMove?: (params: PointerLinkEventParams) => void; readonly onLinkPointerUp?: (params: PointerLinkEventParams) => void; readonly onLinkPointerClick?: (params: PointerLinkEventParams) => void; readonly onLinkPointerDblClick?: (params: PointerLinkEventParams) => void; readonly onLinkContextMenu?: (params: PointerLinkEventParams) => void; readonly onCellMouseEnter?: (params: HoverCellEventParams) => void; readonly onCellMouseLeave?: (params: HoverCellEventParams) => void; readonly onCellMouseOver?: (params: HoverCellEventParams) => void; readonly onCellMouseOut?: (params: HoverCellEventParams) => void; readonly onElementMouseEnter?: (params: HoverElementEventParams) => void; readonly onElementMouseLeave?: (params: HoverElementEventParams) => void; readonly onElementMouseOver?: (params: HoverElementEventParams) => void; readonly onElementMouseOut?: (params: HoverElementEventParams) => void; readonly onLinkMouseEnter?: (params: HoverLinkEventParams) => void; readonly onLinkMouseLeave?: (params: HoverLinkEventParams) => void; readonly onLinkMouseOver?: (params: HoverLinkEventParams) => void; readonly onLinkMouseOut?: (params: HoverLinkEventParams) => void; readonly onCellMouseWheel?: (params: WheelCellEventParams) => void; readonly onElementMouseWheel?: (params: WheelElementEventParams) => void; readonly onLinkMouseWheel?: (params: WheelLinkEventParams) => void; readonly onElementMagnetPointerClick?: (params: MagnetEventParams) => void; readonly onElementMagnetPointerDblClick?: (params: MagnetEventParams) => void; readonly onElementMagnetContextMenu?: (params: MagnetEventParams) => void; readonly onLinkConnect?: (params: LinkConnectEventParams) => void; readonly onLinkDisconnect?: (params: LinkConnectEventParams) => void; readonly onLinkSnapConnect?: (params: LinkConnectEventParams) => void; readonly onLinkSnapDisconnect?: (params: LinkConnectEventParams) => void; readonly onBlankPointerDown?: (params: PointerBlankEventParams) => void; readonly onBlankPointerMove?: (params: PointerBlankEventParams) => void; readonly onBlankPointerUp?: (params: PointerBlankEventParams) => void; readonly onBlankPointerClick?: (params: PointerBlankEventParams) => void; readonly onBlankPointerDblClick?: (params: PointerBlankEventParams) => void; readonly onBlankContextMenu?: (params: PointerBlankEventParams) => void; readonly onBlankMouseEnter?: (params: HoverBlankEventParams) => void; readonly onBlankMouseLeave?: (params: HoverBlankEventParams) => void; readonly onBlankMouseOver?: (params: HoverBlankEventParams) => void; readonly onBlankMouseOut?: (params: HoverBlankEventParams) => void; readonly onBlankMouseWheel?: (params: WheelBlankEventParams) => void; readonly onPaperMouseEnter?: (params: PaperHoverEventParams) => void; readonly onPaperMouseLeave?: (params: PaperHoverEventParams) => void; readonly onPaperPan?: (params: PaperPanEventParams) => void; readonly onPaperPinch?: (params: PaperPinchEventParams) => void; readonly onTranslate?: (params: TranslateEventParams) => void; readonly onScale?: (params: ScaleEventParams) => void; readonly onResize?: (params: ResizeEventParams) => void; readonly onTransform?: (params: TransformEventParams) => void; } /** * The handler signature for a single paper event, looked up by its camelCase * name. For example `PaperEventHandler<'onCellPointerDown'>` resolves to * `(params: PointerCellEventParams) => void`. Handy for typing a standalone * handler before adding it to a {@link PaperEventMap}. * @template T - The event key to look up (a camelCase `on*` key of the paper's React event handlers, see {@link PaperEventMap}). * @group Types */ type PaperEventHandler = NonNullable; /** * Every paper-event handler accepted by {@link useOnPaperEvents}: the typed * camelCase `on*` handlers (each delivering a single params object) plus any raw * native event name with its positional arguments. Mix both freely in one map. * @group Types */ type PaperEventMap = Partial & PaperEventHandlers; /** * Viewport transform accepted by the `` `transform` prop: either a CSS * transform string (e.g. `'scale(0.5)'`, `'translate(10px, 20px) rotate(15deg)'`) * or a `DOMMatrix`. Strings are parsed with the native `DOMMatrix` constructor. * @group Types */ type PaperTransform = string | DOMMatrix; /** * Context handed to a {@link DefaultLink} factory while the user drags a new * connection from a port or element. * @expand * @group Types */ interface DefaultLinkParams { /** The source end of the connection being created. */ readonly source: ConnectionEnd; /** The paper instance. */ readonly paper: dia.Paper; /** The graph instance. */ readonly graph: dia.Graph; } /** * Defines the link created when the user drags a new connection from a port or * element. Either a factory receiving {@link DefaultLinkParams} (returning a * `dia.Link` or a partial {@link LinkRecord}) or a static partial * {@link LinkRecord}. * @group Types */ type DefaultLink = ((context: DefaultLinkParams) => dia.Link | Partial) | Partial; /** * Raw `dia.Paper.Options` accepted by the {@link PaperProps} `options` escape * hatch, for any native option joint-react does not expose as a dedicated prop. * `cellVisibility` is excluded: use the dedicated `cellVisibility` prop instead * (it is also managed by feature ownership, e.g. a virtual-rendering scroller). * @group Types */ type PaperOptions = Omit; /** * Officially supported Paper options. Pass-through props inherit their exact * native types via indexed access (`dia.Paper.Options['name']`), so any * type-level change in JointJS propagates automatically. Anything not listed * here is reachable via the `options` escape hatch, never implicitly exposed. */ interface PaperSupportedOptions { /** * Defines the link created when the user starts dragging from a port or element. * * Can be a factory function receiving connection context, a static {@link LinkRecord}, * or a `dia.Link` instance. */ readonly defaultLink?: DefaultLink; /** * Validates whether a connection between two elements/ports is allowed. * * - **Function**: custom validation with built-in rules (no self-loops, no link-to-link, no multi-links). * Receives `{ source, target, endType, paper, graph }`. * - **Object**: {@link CanConnectOptions} with built-in rules and optional `validate` callback. * * When omitted, defaults to `canConnect()` (no self-loops, no link-to-link, no multi-links). */ readonly validateConnection?: CanConnectOptions | ValidateConnection; /** * Decides how the end JSON is stored when the user drops a link end. * * - **Function**: receives `{ end, model, magnet, dropPoint, endType, link, paper, graph }` * and returns the modified `EndJSON`. * - **Object**: {@link ConnectionStrategyOptions} with `pin` preset and/or `customize` callback. */ readonly connectionStrategy?: ConnectionStrategyOptions | ConnectionStrategy; /** * Validates whether an element can be embedded into another element. * Receives `{ child, parent, paper, graph }`. */ readonly validateEmbedding?: ValidateEmbedding; /** * Validates whether an element can be unembedded from its parent. * Receives `{ child, paper, graph }`. */ readonly validateUnembedding?: ValidateUnembedding; /** Unique identifier used by joint-react to track the paper instance. */ readonly id?: string; /** * Draws a grid pattern on the paper background. Pass `true` for the default * grid or an object to style it (e.g. `{ color: 'red', thickness: 2 }`). * @default true */ readonly drawGrid?: dia.Paper.Options['drawGrid']; /** * Spacing of the rendered grid lines in px. Falls back to `gridSize` when not * set. * @default matches `gridSize` */ readonly drawGridSize?: dia.Paper.Options['drawGridSize']; /** * Grid step in px that element positions snap to while dragging. * @default 10 */ readonly gridSize?: dia.Paper.Options['gridSize']; /** * Paper background color, image, or pattern. Pass an object such as * `{ color: 'lightblue', image: '/bg.png', repeat: 'flip-xy' }`. * @default false */ readonly background?: dia.Paper.Options['background']; /** * Renders link labels into a dedicated top layer (so they are not occluded by * later cells). Pass `true`, or a layer name to target a specific layer. * @default false */ readonly labelsLayer?: dia.Paper.Options['labelsLayer']; /** * Lets cell content spill outside the paper viewport instead of being clipped. * @default false */ readonly overflow?: dia.Paper.Options['overflow']; /** * Which pointer interactions are enabled on cells. Accepts a boolean to toggle * everything, an `InteractivityOptions` object for granular control per * interaction kind, or a {@link CellInteractivity} callback returning either * form per cell. The native `(cellView, event)` callback is reachable via the * `options` escape hatch. * @default { labelMove: false, linkMove: false } */ readonly interactive?: CellInteractivity; /** * Highlighter definitions keyed by highlight type (connecting, embedding, * magnet/element availability). Override to restyle these visual cues. * @default joint-react's themed highlighters */ readonly highlighting?: dia.Paper.Options['highlighting']; /** * Snaps a dragged link label to the closest point on the link path. * @default false */ readonly snapLabels?: dia.Paper.Options['snapLabels']; /** * Snaps a dragged link end to nearby ports/elements. Pass `{ radius }` to set * the snapping distance in px. * @default { radius: 15 } */ readonly snapLinks?: dia.Paper.Options['snapLinks']; /** * Allows a link end to snap to its own source/target element. * @default false */ readonly snapLinksSelf?: dia.Paper.Options['snapLinksSelf']; /** * Highlights valid drop targets (magnets and elements) while a link is being * dragged. * @default true */ readonly markAvailable?: dia.Paper.Options['markAvailable']; /** * Allows dropping a link end on blank paper, pinning it to a fixed point * instead of requiring an element/port. * @default false */ readonly linkPinning?: dia.Paper.Options['linkPinning']; /** * Maximum pointer travel (in px) still treated as a click rather than a drag. * @default 5 */ readonly clickThreshold?: dia.Paper.Options['clickThreshold']; /** * Pointer travel (in px) required before `pointermove` events start firing. * @default 0 */ readonly moveThreshold?: dia.Paper.Options['moveThreshold']; /** * Pointer travel (in px) before a link is created from a magnet, or * `'onleave'` to create it once the pointer leaves the magnet. * @default 'onleave' */ readonly magnetThreshold?: dia.Paper.Options['magnetThreshold']; /** * Suppresses the browser context menu over the paper so `contextmenu` events * can drive your own UI. * @default true */ readonly preventContextMenu?: dia.Paper.Options['preventContextMenu']; /** * Prevents the browser default action on cell pointer events. * @default true */ readonly preventDefaultViewAction?: dia.Paper.Options['preventDefaultViewAction']; /** * Prevents the browser default action on blank-area pointer events. * @default false */ readonly preventDefaultBlankAction?: dia.Paper.Options['preventDefaultBlankAction']; /** * Enables embedding: dropping an element onto another re-parents it (the * child then moves with its parent). Pair with the `validateEmbedding` prop to * control which parents are allowed. * @default false */ readonly embeddingMode?: dia.Paper.Options['embeddingMode']; /** * When embedding, only the frontmost element under the pointer is considered a * parent; otherwise candidates are tested front-to-back. * @default true */ readonly frontParentOnly?: dia.Paper.Options['frontParentOnly']; /** * Predicate deciding whether a cell should be rendered. Receives * `{ model, isMounted, paper, graph }`; return `false` to hide the cell. * Native positional form is reachable via the `options` escape hatch. */ readonly cellVisibility?: CellVisibility; /** * Namespace of cell-view constructors used to resolve a cell's view by type. * @default JointJS built-in cell views */ readonly cellViewNamespace?: dia.Paper.Options['cellViewNamespace']; /** * Namespace of layer-view constructors used to resolve custom paper layers. * @default JointJS built-in layer views */ readonly layerViewNamespace?: dia.Paper.Options['layerViewNamespace']; /** * Namespace used to resolve router names referenced by links. * @default JointJS built-in `routers` */ readonly routerNamespace?: dia.Paper.Options['routerNamespace']; /** * Namespace used to resolve connector names referenced by links. * @default JointJS built-in `connectors` */ readonly connectorNamespace?: dia.Paper.Options['connectorNamespace']; /** * Namespace used to resolve highlighter names referenced by `highlighting`. * @default JointJS built-in highlighters plus joint-react's magnet highlighter */ readonly highlighterNamespace?: dia.Paper.Options['highlighterNamespace']; /** * Namespace used to resolve element anchor names. * @default JointJS built-in `anchors` */ readonly anchorNamespace?: dia.Paper.Options['anchorNamespace']; /** * Namespace used to resolve link anchor names. * @default JointJS built-in `linkAnchors` */ readonly linkAnchorNamespace?: dia.Paper.Options['linkAnchorNamespace']; /** * Namespace used to resolve connection point names. * @default JointJS built-in `connectionPoints` */ readonly connectionPointNamespace?: dia.Paper.Options['connectionPointNamespace']; /** * Bundle of link routing defaults (router, connector, anchor, connection * point). Use a preset ({@link linkRoutingStraight}, {@link linkRoutingOrthogonal}, * {@link linkRoutingSmooth}) or pass a custom object of the same shape. * * `defaultLinkAnchor` is reachable via the `options` escape hatch. * * Values inside `options` override matching keys here. * @example * ```tsx * import { linkRoutingOrthogonal } from '@joint/react'; * * * ``` */ readonly linkRouting?: LinkRouting; /** * Raw `dia.Paper.Options` passthrough for anything joint-react doesn't * expose as a dedicated prop (e.g. `allowLink`, `validateMagnet`, * `restrictTranslate`, `onViewPostponed`). * * Values set here override top-level props of the same name, treat this * as the authoritative form for users who need direct access to the raw * JointJS API. Avoid overriding joint-react-controlled options * (`async`, `sorting`, `viewManagement`, `frozen`, `autoFreeze`), the * portal rendering depends on their set values. */ readonly options?: PaperOptions; } /** * Render function for elements. Receives the element's `data` slice only, so * the renderer re-runs ONLY when `data` changes, not when `position`, * `size`, `angle`, or other cell attributes update. Position and size are * applied by JointJS's view layer without touching React at all (SVG mode) * or by a thin wrapper div that doesn't invoke the renderer (HTML mode). * * Rendered as JSX (``) so wrapping it in * `React.memo` actually short-circuits on prop equality. * * The framework guarantees `data` is at least `{}` at this boundary, even * for built-in JointJS shapes that ship without a `data` field. * * If the renderer needs the id, position, size, or other slices, use the * context hooks: {@link useCellId}(), {@link useCell}() (with optional selector), or * `useCell(c => c.position / c.size / ...)`. * @group Types */ type RenderElement = (data: ElementData) => ReactNode; /** * Render function for links. Receives the link's `data` slice only, same * performance rationale as {@link RenderElement}. Use {@link useCell}() (with an * optional selector) inside the renderer when source / target / id are * needed. * * The framework guarantees `data` is at least `{}` at this boundary. * @group Types */ type RenderLink = (data: LinkData) => ReactNode; /** * Props for {@link Paper} — the React-friendly surface over * `dia.Paper.Options`, plus joint-react extras such as custom cell rendering, a * controlled viewport `transform`, and portal targeting. * * Paper events are exposed directly as props * (`onBlankContextMenu`, `onElementPointerClick`, `onLinkMouseEnter`, …). * Each handler receives a single params * object, e.g. `onBlankContextMenu={({ paper, event, x, y }) => …}`. * * Handlers are **always-latest**: the paper subscribes once and each event * reads the current handler, so inline arrows * (`onBlankContextMenu={() => …}`) are fine, no `useCallback` needed and no * re-subscription on render. For raw native event names or events without an * `on*` form (`render:done`, `cell:highlight`, …), use the {@link useOnPaperEvents} * hook. * @see [`dia.Paper`](https://docs.jointjs.com/api/dia/Paper) * @expand * @group Types */ interface PaperProps extends PaperSupportedOptions, PropsWithChildren, PaperEventHandlers { /** * Renders each element from its `data` slice. * * Note: JointJS works with SVG by default, so `renderElement` is appended inside an SVG node. * To render HTML elements, use the experimental `useHTMLOverlay` prop or an SVG `foreignObject`. * * Receives the element's `data` slice only. Derive its type from your cells * with `InferElement['data']`. * @example Global component * ```tsx * type NodeData = InferElement['data'] * // HTML content lives inside so it renders correctly in SVG mode. * function RenderElement(data: NodeData) { * return {data.label} * } * ``` * @example Local component * ```tsx * type NodeData = InferElement['data'] * const renderElement: RenderElement = useCallback( * (data) => {data.label}, * [] * ) * ``` */ readonly renderElement?: RenderElement; /** * Renders each link's content from its `data` slice. Re-runs when the link's * `data` changes. * * Note: JointJS works with SVG by default, so `renderLink` content is appended inside an SVG node. * To render HTML elements, use an SVG `foreignObject`. * * Receives the link's `data` slice only. Derive its type from your cells with * `InferLink['data']`. When you need the source, target, or id, * read them with {@link useCell}() from inside the renderer. * @experimental - this feature is experimental and may have limitations or issues. Use at your own risk. * @example Global component * ```tsx * type LinkData = InferLink['data'] * function RenderLink(data: LinkData) { * return {data.label}; * } * ``` * @example Local component * ```tsx * type LinkData = InferLink['data'] * const renderLink: RenderLink = useCallback( * (data) => {data.label}, * [] * ) * ``` * @example Reading the id alongside the data slice * ```tsx * type LinkData = InferLink['data'] * function RenderLink(data: LinkData) { * // source / target / id live on the context, not the data slice * const id = useCellId(); * return {data.label} ({id}); * } * ``` */ readonly renderLink?: RenderLink; /** * Inline styles applied to the paper host element. Use `style.width` and * `style.height` (or CSS via `className`) to size the paper, Paper does * not expose dedicated width/height props. */ readonly style?: CSSProperties; /** * CSS classes applied to the paper host element. Combine with width / * height rules to size the paper. */ readonly className?: string; /** * Sets the paper's viewport transform via `paper.matrix(...)`. Accepts * either a CSS transform string (e.g. `'scale(0.5)'`, * `'translate(10px, 20px) rotate(15deg)'`) or a `DOMMatrix`. Useful for * zoom, minimap, and arbitrary viewport transforms. * @example * ```tsx * * * ``` */ readonly transform?: PaperTransform; /** * Maximum pointer travel (in px) still treated as a click rather than a drag. * Moving farther than this between press and release suppresses the * `pointerclick` event. * @default 5 */ readonly clickThreshold?: number; /** * Renders elements as real HTML in an overlay instead of inside an SVG node. * By default `renderElement` output is mounted in SVG, so plain HTML needs a * `foreignObject` (or an {@link HTMLBox}); enable this to skip that wrapping. * @experimental Known issues with HTML element rendering — use at your own risk. * @default false */ readonly useHTMLOverlay?: boolean; /** * Paper-level override for the React portal target selector. * * By default, each cell uses its own `portalSelector` field. * {@link ElementModel} renders into its `'__portal__'` group, {@link LinkModel} into its * root ``. Built-in JointJS shapes have no `portalSelector` field and * are skipped. Set this prop to force a single selector or a dynamic one * across all cells. * * A function receives `{ model, paper, graph }` and may return: * - a **selector string**, look up that node, * - an **`Element`**, use that DOM node directly, * - **`null`**, skip rendering for this cell, * - **`undefined`** (or no return), fall back to the cell's own `portalSelector`. * @example * ```tsx * // Render into the 'root' selector of all cells * * ``` * @example * ```tsx * // Route built-in shapes to 'root'; let ElementModel cells use their default * { * if (model.get('type') === 'standard.Rectangle') return 'root'; * // implicit: use the cell's own portalSelector * }} renderElement={...} /> * ``` */ readonly portalSelector?: PortalSelector; /** * Pre-created paper instance to adopt. * When provided, the Paper component wraps this paper instead of creating a new one. * The paper's DOM is assumed to be managed externally (e.g. by a stencil). * * `PaperView` is an internally-managed instance, not a public, importable type; * you normally receive it from another joint-react construct (such as a stencil) * rather than constructing it yourself. */ readonly paper?: PaperView; } /** * A registered feature instance with lifecycle cleanup. * @internal */ interface Feature { readonly id: string; readonly instance: T; readonly clean?: () => void; /** * Optional hook invoked when the `` `cellVisibility` prop changes * while this feature owns the option (see `PaperStore.claimCellVisibility`). * Lets the owner re-apply the callback without depending on its own * component re-rendering. */ readonly onCellVisibilityChange?: (cellVisibility: dia.Paper.Options['cellVisibility']) => void; } /** * Options for adding a new paper instance to the graph store. */ interface AddPaperOptions { /** JointJS Paper configuration options */ readonly paperOptions: dia.Paper.Options; /** Optional initial transform for the paper (CSS string or `DOMMatrix`). */ readonly transform?: PaperTransform; /** Optional custom renderer for elements */ readonly renderElement?: RenderElement; /** Optional custom renderer for links */ readonly renderLink?: RenderLink; /** Optional selector for locating React portal targets within cell views */ readonly portalSelector?: PortalSelector; /** * Pre-created PaperView instance to adopt. * When provided, PaperStore wraps this paper instead of creating a new one. */ readonly paper?: PaperView; } /** * Options for creating a PaperStore instance. * Extends AddPaperOptions with required graph store and ID. */ interface PaperStoreOptions extends AddPaperOptions { /** The graph store instance this paper belongs to */ readonly graphStore: GraphStore; /** Unique identifier for this paper instance */ readonly id: string; } /** * Store for managing a single Paper instance and its associated state. * * Each Paper component creates a PaperStore instance that: * - Manages the JointJS Paper instance * - Tracks element views for rendering * - Coordinates with the GraphStore for state updates */ declare class PaperStore { /** The underlying JointJS Paper instance with React-specific properties */ paper: PaperView; /** Unique identifier for this paper instance */ paperId: string; /** Optional custom element renderer */ renderElement?: RenderElement; /** Optional custom link renderer */ renderLink?: RenderLink; features: Record; /** * True when this store adopted a pre-created paper (e.g. ``'s drag * paper) instead of creating its own. The adopting wrapper does not own the * paper's lifecycle, so it must not remove it on `destroy()`. */ private readonly isAdoptedPaper; /** * The native `cellVisibility` callback resolved from the `` * `cellVisibility` prop. Kept current on every prop change regardless of * ownership, so a feature that claims the option (see * {@link claimCellVisibility}) always has access to the latest value. */ nativeCellVisibility: dia.Paper.Options['cellVisibility']; /** * Feature id that currently owns `paper.options.cellVisibility`, or `null` * when no feature owns it. While owned, the Paper component stops writing * `cellVisibility` onto the paper and instead routes * {@link nativeCellVisibility} to the owner. Generic, the store has no * knowledge of which feature claims it. */ private cellVisibilityOwner; /** Link changes pending flush, populated by clearView, flushed in afterRender. */ private pendingLinkChanges; constructor(options: PaperStoreOptions); /** * Queues link changes for flush after the next JointJS render cycle. * @param changes - Link changes to queue */ addPendingLinkChanges(changes: Map>): void; /** * Flushes pending link changes via setPaperViews so React re-reads correct link layout. * Called from afterRender when JointJS has finished rendering. * @param graphStore */ private flushPendingLinkChanges; getElementView(id: CellId): dia.ElementView | undefined; getLinkView(id: CellId): dia.LinkView | undefined; /** Whether some feature currently owns `paper.options.cellVisibility`. */ get isCellVisibilityOwned(): boolean; /** * Claim ownership of `paper.options.cellVisibility` for a feature. Clears * the option on the paper so a feature (e.g. a virtual-rendering scroller) * can install its own callback without conflicting with the Paper * component's write. Idempotent for the same owner; a different owner takes * over. Generic, no knowledge of the claiming feature. * @param ownerId - The claiming feature's id. */ claimCellVisibility(ownerId: string): void; /** * Release ownership previously taken via {@link claimCellVisibility}. * Restores the paper's `cellVisibility` to the latest resolved native * callback so the Paper component resumes managing it. No-op when a * different feature owns it. * @param ownerId - The releasing feature's id. */ releaseCellVisibility(ownerId: string): void; /** * Notify the owning feature that the native `cellVisibility` callback * changed (e.g. the `` prop updated), so it can re-apply it. * Routed through the owner's {@link Feature.onCellVisibilityChange} hook. * no separate listener registry. No-op when unowned or the owner provides * no hook. * @param cb - The refreshed native callback. */ notifyCellVisibilityOwner(cb: dia.Paper.Options['cellVisibility']): void; /** * Cleans up the paper instance and all associated resources. * Should be called when the paper is being removed from the graph store. */ destroy: () => void; } /** * Element size observer with stack-based multi-hook support. * * Tracks DOM element sizes via ResizeObserver and syncs them to the graph. * Multiple {@link useMeasureElement} hooks can target the same cell ID, only the * most recently added (active) node is observed. When it unmounts, the * previous node in the stack becomes active again. * * Internal data structures: * - `observedStacksByCellId`, `Map` (last = active) * - `activeObservedElementByDomNode`, `WeakMap` for O(1) lookup in the ResizeObserver callback */ /** Element layout where width/height are required but x/y may be omitted. */ type ElementLayoutOptionalXY = Pick & Partial>; /** * The element's measurement, passed to a {@link TransformElementLayout} callback. * Carries the element's current `x`, `y`, and `angle` together with the freshly * measured `width` and `height`, plus the underlying model and cell id. * @expand * @group Types */ interface TransformElementLayoutParams extends Required { /** The JointJS `dia.Element` instance being measured. */ readonly model: dia.Element; /** Id of the cell being measured. */ readonly id: CellId; } /** * Adjusts a measured element layout before it is written to the graph. Receives * the element's current geometry plus its newly measured size, and returns the * `width`/`height` (and optionally `x`/`y`) to apply — use it to clamp sizes, * snap to a grid, or reposition while auto-sizing. Pass it via the `transform` * option of {@link useMeasureElement}. * @example * ```tsx * import type { TransformElementLayout } from '@joint/react'; * * // Never let a measured element shrink below 80px wide. * const transform: TransformElementLayout = ({ width, height }) => ({ * width: Math.max(width, 80), * height, * }); * ``` * @see {@link TransformElementLayoutParams} * @group Types */ type TransformElementLayout = (params: TransformElementLayoutParams) => ElementLayoutOptionalXY; /** * Options for registering an element to be measured for size changes. * @group Types */ interface SetMeasuredNodeOptions { /** The DOM node (HTML or SVG) to observe for size changes */ readonly node: HTMLElement | SVGElement; /** Optional callback to handle size updates before they're applied */ readonly transform?: TransformElementLayout; /** The ID of the cell in the graph that corresponds to this DOM node */ readonly id: CellId; } /** * Update payload for array-shaped state, replace or transform-from-previous. * `Input` defaults to `T` (same type for read and write). Override it to * widen the write side, e.g. `ArrayUpdate`. */ type ArrayUpdate = readonly Input[] | ((previous: readonly T[]) => readonly Input[]); /** Read-only view of a cell container, supports reads, lookups, and subscriptions. */ interface ReadonlyContainer { getVersion: () => number; getAll: () => readonly Cell[]; get: (id: CellId) => Cell | undefined; has: (id: CellId) => boolean; getSize: () => number; subscribe: (id: CellId, listener: () => void) => () => void; /** Notifies on membership changes (ids added / removed), even when the net count is unchanged. */ subscribeToSize: (listener: () => void) => () => void; subscribeToAll: (listener: () => void) => () => void; } /** Updater for atom values — always receives the current value (never undefined). */ type AtomUpdate = ((previous: T) => T) | T; /** Return type of {@link createAtom}. */ interface Atom { /** Get the current value. */ readonly get: () => T; /** Alias for `get`, compatible with `useSyncExternalStore`. */ readonly getSnapshot: () => T; /** Update the value. Accepts a new value or an updater function. Pass `sync` to notify synchronously. */ readonly set: (update: AtomUpdate, sync?: boolean) => void; /** Alias for `set`, matches the old `createState` API during migration. */ readonly setState: (update: AtomUpdate, sync?: boolean) => void; /** Subscribe to value changes. Returns an unsubscribe function. */ readonly subscribe: (listener: () => void) => () => void; /** Remove all listeners. */ readonly clean: () => void; } /** * Creates a simple atomic value container with get/set/subscribe. * Items are immutable, the container replaces the value on each update. * @param initialValue - The initial value of the atom. */ declare function createAtom(initialValue: T): Atom; /** * Options for applying a React-driven cells snapshot to the graph. * * A single unified `cells` stream replaces the earlier `elements` / `links` * split. Each record is routed internally by `type`: * - {@link ELEMENT_MODEL_TYPE} → mapped via the element mapper * - {@link LINK_MODEL_TYPE} → mapped via the link mapper * - anything else → passed through as raw attributes */ interface UpdateGraphOptions { /** Cell records to sync. If omitted, the current graph cells are preserved untouched. */ readonly cells?: ReadonlyArray; readonly flag?: 'updateFromReact'; /** Extra options forwarded verbatim into the `graph.syncCells` event opt. */ readonly metadata?: Record; } /** * A batch of cell changes reported after each graph update, delivered to the * `onIncrementalCellsChange` callback of {@link GraphProviderProps}. Lets you * apply just the delta to an external store instead of diffing the whole graph. * @template Element - Shape of the element cells stored in the graph. * @template Link - Shape of the link cells stored in the graph. * @expand * @group Types */ interface IncrementalCellsChange { /** Cells added since the last commit, keyed by cell id. */ readonly added: Map; /** Cells whose attributes changed since the last commit, keyed by cell id. */ readonly changed: Map; /** Ids of cells removed since the last commit (including a removed element's links). */ readonly removed: Set; } /** * Callback type for incremental cell changes. Emitted after each graph change batch * @group Types */ type OnIncrementalCellsChange = (changes: IncrementalCellsChange) => void; /** Options for {@link graphProjection}. */ interface GraphProjectionState { readonly graph: dia.Graph; readonly onIncrementalCellsChange?: OnIncrementalCellsChange; readonly onElementsSizeChange?: (id: CellId, size: dia.Size) => void; } /** * Project a JointJS graph into a reactive cells container, keeping the two in * sync. Subscribes to graph changes and mirrors add / change / remove events * (including connected-link sweeps) into the container, optionally emitting an * incremental change set after each commit. * @param options - graph to project plus optional change/size callbacks * @returns controller exposing the readonly cells container and sync/update/destroy methods */ declare function graphProjection(options: GraphProjectionState): { cells: ReadonlyContainer; syncFromGraph: () => void; updateGraph(update: UpdateGraphOptions): void; destroy(): void; }; /** * Controller returned by {@link graphProjection}. * @group Types */ type GraphProjection = ReturnType>; declare const DEFAULT_CELL_NAMESPACE: Record; /** * `dia.Cell.set()` option key used to mark writes that originate from the * auto-size / measurement pipeline. `change:size` listeners can read it to * distinguish measurement writes from external ones (controlled-mode sync, * `cell.resize`, interactive tools) and avoid feedback loops. Exported for * plugin authors and `@joint/react-plus` via `@joint/react/internal`. */ declare const AUTO_SIZE_OPTION = "autoSize"; /** * Paper snapshot is a simple version counter. * Incremented on every view mount/unmount change to trigger React re-renders. * @group Types */ interface PaperStoreState { readonly version: number; readonly featuresState?: Record; } /** * Full internal snapshot of the graph store. * @group Types */ interface GraphStoreInternalSnapshot { readonly papers: Record; readonly resetVersion: number; readonly graphFeaturesVersion: number; } /** * Reference point that stays fixed when an auto-sized element's measured size * changes. Mirrors CSS `transform-origin` semantics. * * - `'top-left'` (default): element grows right/down, top-left stays put. * - `'center'`: element grows symmetrically, geometric center stays put. * * Only affects writes from the {@link useMeasureElement} pipeline. Manual `cell.resize()`, * interactive resize tools, and direct `cell.set('size', ...)` calls are unaffected. * @group Types */ type AutoSizeOrigin = 'top-left' | 'center'; /** * Options for constructing a {@link GraphStore}: an optional existing `dia.Graph`, * cell namespace/model overrides, the auto-size origin, and `initialCells` used to * seed the graph once on creation. * @group Types */ interface GraphStoreOptions { readonly graph?: dia.Graph; readonly cellNamespace?: unknown; readonly cellModel?: typeof dia.Cell; /** * Reference point that stays fixed when an auto-sized element's measured size * changes. See {@link AutoSizeOrigin}. * @default 'top-left' */ readonly autoSizeOrigin?: AutoSizeOrigin; readonly initialCells?: ReadonlyArray>; } /** * Central store for managing graph state, synchronization, and paper instances. */ declare class GraphStore { readonly config: GraphStoreOptions; readonly graphProjection: GraphProjection; readonly internalState: Atom; readonly measureState: Atom; readonly graph: dia.Graph; readonly autoSizeOrigin: AutoSizeOrigin; paperStores: Map; features: Record; private observer; private onIncrementalCellsChange?; private warnAutoSizeResize?; constructor(config: GraphStoreOptions); setOnIncrementalCellsChange: (callback: OnIncrementalCellsChange) => void; /** * Apply a controlled cells snapshot (called by GraphProvider when the * parent-owned `cells` prop changes). Equivalent to `graphProjection.updateGraph` * with the react-origin flag set. * @param cells - new cells snapshot from the parent * @param metadata - extra options forwarded to the underlying `graph.syncCells` opt */ applyControlled(cells: ReadonlyArray, metadata?: Record): void; /** * Type guard: does this cell record resolve to an element? * * Reads `cell.type` and classifies it via the graph's type registry. Falls * back to `graph.getTypeConstructor(type).prototype.isElement()` when the * type is not our default {@link ElementModel}, so any `dia.Element` subclass * registered in the cell namespace (`standard.Rectangle`, custom shapes, * etc.) is correctly recognised. * @param cell - the cell record to classify * @returns `true` when the resolved type extends `dia.Element` */ isElement: (cell: Element | Link) => cell is Element; /** * Type guard: does this cell record resolve to a link? * * Reads `cell.type` and classifies it via the graph's type registry. Falls * back to `graph.getTypeConstructor(type).prototype.isLink()` when the type * is not our default {@link LinkModel}, so any `dia.Link` subclass registered in * the cell namespace is correctly recognised. * @param cell - the cell record to classify * @returns `true` when the resolved type extends `dia.Link` */ isLink: (cell: Element | Link) => cell is Link; destroy: (isGraphExternal: boolean) => void; updatePaperSnapshot(paperId: string, updater: (previous: PaperStoreState) => PaperStoreState, sync?: boolean): void; private bumpPaperVersion; private bumpGraphFeaturesVersion; setGraphFeature(feature: Feature, sync?: boolean): void; removeGraphFeature(featureId: string, sync?: boolean): void; setPaperFeature(paperId: string, feature: Feature, sync?: boolean): void; removePaperFeature(paperId: string, featureId: string, sync?: boolean): void; setPaperViews(paperId: string, changes: Map>): void; private removePaper; addPaper: (id: string, paperOptions: AddPaperOptions) => { paperStore: PaperStore; remove: () => void; }; setMeasuredNode: (options: SetMeasuredNodeOptions) => () => void; getPaperStore: (id: string) => PaperStore | undefined; /** * Clear the cached view for an element and its connected links on a paper. * Forces re-rendering after layout might have changed. * @param options - element id, optional link filter, target paper * @param options.cellId - id of the element whose view to clear * @param options.onValidateLink - optional filter for which connected links to clear * @param options.paper - target paper instance */ clearViewForElementAndLinks: (options: { readonly cellId: CellId; readonly onValidateLink?: (link: dia.Link) => boolean; readonly paper: dia.Paper; }) => void; } /** * Resolves a paper ID from any {@link PaperTarget}, handling the ref-timing problem. * For string IDs and `dia.Paper` instances resolution is synchronous. For * `RefObject` targets, a layout effect re-resolves the ID once * `useImperativeHandle` has set the ref. * @param paperTarget - The paper target to resolve. * @returns The paper ID string, or `undefined` when not yet available. * @internal */ declare function useResolvePaperId(paperTarget: PaperTarget | undefined): string | undefined; /** * Returns the active paper store from context, by ID, or via the default paper. * * A paper store is view-access: it exists only once a `` has mounted, so * the result is `PaperStore | undefined` and this hook never throws. * * Resolution order (no explicit id): * 1. `PaperStoreContext` (when called inside a `` subtree) * 2. `DEFAULT_PAPER_ID` lookup (when a single `` exists without an explicit `id`) * @param paperId - An explicit paper id, or omitted for the context/default paper. * @returns The resolved paper store, or `undefined` when no paper is mounted yet. * @internal */ declare function usePaperStore(paperId?: string): PaperStore | undefined; /** * Result of {@link usePaper}, the paper instance and imperative actions. * @expand * @group Types */ interface PaperApi { /** Resolved JointJS paper instance, or `null` until a `` has mounted. */ readonly paper: PaperView | null; /** * Trigger a render pass on the paper. Forwards to `paper.wakeUp()`. * No-op when the paper isn't resolved yet. * @see [`paper.wakeUp()`](https://docs.jointjs.com/api/dia/Paper#wakeup) */ readonly wakeUp: () => void; /** * Suspend view updates so edits don't repaint until {@link PaperApi.unfreeze}. * Forwards to `paper.freeze()`. No-op when the paper isn't resolved yet. * @see [`paper.freeze()`](https://docs.jointjs.com/api/dia/Paper#freeze) */ readonly freeze: () => void; /** * Resume view updates and flush everything queued while frozen. Forwards to * `paper.unfreeze()`. No-op when the paper isn't resolved yet. * @see [`paper.unfreeze()`](https://docs.jointjs.com/api/dia/Paper#unfreeze) */ readonly unfreeze: () => void; } /** * Access the JointJS paper (`dia.Paper`) instance for the surrounding ``, * a specific paper by id, or the default paper. Use it to drive the paper * imperatively, scale, fit content, or wake it up, from anywhere under a * {@link GraphProvider}. * * The returned object is referentially stable for a given resolved paper; its * `paper` is `null` until the `` view has mounted, so guard calls with * `paper?.`. * @param paperId - An explicit paper id, or omitted for the context/default paper. * @returns The {@link PaperApi}: the resolved `paper` (or `null`) plus `wakeUp`, * `freeze`, and `unfreeze` actions. * @see [Paper quickstart](https://docs.jointjs.com/learn/quickstart/paper) * @group Hooks * @example * ```tsx * import { GraphProvider, Paper, usePaper } from '@joint/react'; * * function FitButton() { * const { paper } = usePaper(); * // `paper` is null until the view has mounted. * return ; * } * * function App() { * return ( * * * * * ); * } * ``` */ declare function usePaper(paperId?: string): PaperApi; /** * Options for {@link useMeasureElement}, controlling how the measured DOM size is * turned into the graph element's size. * @group Types * @expand */ interface MeasureElementOptions { /** * Adjusts the measured size before it is written to the graph element, e.g. to * add padding or reserve space for a header. Receives the measured dimensions * plus the element's current layout ({@link TransformElementLayoutParams}) and * returns the `width`/`height` (and optionally `x`/`y`) to apply. * @default When omitted, the measured `width`/`height` are applied to the element unchanged. * @example * ```tsx * const transform = ({ width, height }) => ({ * width: width + 20, // 10px padding on each side * height: height + 20, * }); * useMeasureElement(nodeRef, { transform }); * ``` */ readonly transform?: TransformElementLayout; } /** * Measures a rendered DOM node and keeps the graph element's size in sync with * it. Point `nodeRef` at the HTML or SVG node that defines the element's size; * whenever that node resizes, the matching graph element is resized to match * (optionally adjusted by a `transform`, see {@link MeasureElementOptions}), and * the element's current `width`/`height` are returned for your own layout math. * * Reach for it when an element's size is driven by its rendered content rather * than fixed up front, e.g. text that wraps or a list that grows. * @remarks * - Call this inside a `renderElement` callback (or a component rendered from * one); it reads the current cell from context and throws otherwise. * - When several `useMeasureElement` calls target the same element, the most * recently mounted one wins. When it unmounts, the previous one takes over * again. * - Do not also read the size back with the {@link selectElementSize} selector * (or `useCell((cell) => cell.size)`) in the same component. This hook already * syncs the size and returns the live `width`/`height`; reading it again only * adds a redundant subscription and an extra render. * @param nodeRef - Ref to the HTML or SVG node to measure. It must be mounted in * the DOM while the hook runs. * @param options - Optional {@link MeasureElementOptions}; the main option is a * `transform` that adjusts the measured size before it is applied. * @returns The graph element's current `width` and `height` (always defined). * @throws If used outside a `renderElement` context, or if the current cell is a * link rather than an element. * @group Hooks * @example Basic usage * ```tsx * import { useMeasureElement } from '@joint/react'; * import { useRef } from 'react'; * * // The element grows to fit its text label. * function LabelElement() { * const textRef = useRef(null); * const { width, height } = useMeasureElement(textRef); * * return ( * <> * * Hello world * * ); * } * ``` * @example Use the returned size * ```tsx * import { useMeasureElement } from '@joint/react'; * import { useRef } from 'react'; * * const iconURL = 'https://example.com/icon.svg'; * * // Size follows the HTML content; use the returned size to place an icon inside. * function Card() { * const contentRef = useRef(null); * const { width, height } = useMeasureElement(contentRef); * const iconSize = 16; * * return ( * <> * * * *
Card content
*
* * ); * } * ``` * @example Adjust size with a transform * ```tsx * import { useMeasureElement, type TransformElementLayout } from '@joint/react'; * import { useRef, useCallback } from 'react'; * * function ListElement() { * const divRef = useRef(null); * const padding = 10; * const headerHeight = 50; * * const transform: TransformElementLayout = useCallback( * ({ width: measuredWidth, height: measuredHeight }) => { * return { * width: padding + measuredWidth + padding, * height: headerHeight + measuredHeight + padding, * }; * }, * [] * ); * * const { width, height } = useMeasureElement(divRef, { transform }); * * return ( * <> * * *
Content
*
* * ); * } * ``` */ declare function useMeasureElement(nodeRef: RefObject, options?: MeasureElementOptions): Required; /** * Subscribes all handlers to a paper, delegating runtime wiring to the * `addPaperEventListeners` runtime. * @param paperStore - Paper store to subscribe on. * @param handlers - Event handlers map. * @returns Cleanup callback that stops all listeners. * @internal */ declare function subscribeToPaperEvents(paperStore: PaperStore, handlers: PaperEventMap): () => void; /** * Subscribes to `dia.Paper` events, pointer clicks, hovers, drags, link * connections, zoom/pan, and more, so you can respond to user interaction on the * canvas. When no paper argument is given, the hook targets the paper from the * surrounding {@link Paper} context (or the single default paper). Mount it * inside a ``. Unlike {@link useOnGraphEvents}, it tolerates a * paper that has not mounted yet — it does not throw while waiting and * subscribes once a paper becomes available. * * Two key forms can be mixed in the same handlers map: * * **CamelCase form**: `on` keys deliver a single params * object with named properties. * ```tsx * useOnPaperEvents({ * onElementPointerClick: ({ id, model, paper, graph, event, x, y }) => {}, * onBlankPointerClick: ({ paper, graph, event, x, y }) => {}, * }); * ``` * * **Raw form**: native JointJS event names with positional arguments. Use * for events without an `on*` counterpart (`'render:done'`, * `'cell:highlight'`, …). * ```tsx * useOnPaperEvents(paperId, { * 'element:pointerclick': (view, evt, x, y) => {}, * 'render:done': (stats) => {}, * }); * ``` * * Handlers are **always-latest**: the subscription is established once and * each event reads the current handler, so inline maps and closures need no * `useCallback`. Re-subscription happens only when the paper or the set of * event names changes. * * The `on*` params object omits the React-store `record`, to read the * record shape, call `useCell(id, selector)` from your own * component (the handler closure has access to the `id` it emits). * * See {@link PaperEventMap} for every accepted key, and {@link PaperEventHandler} * for typing an individual handler. * @title On the current paper * @param handlers - Map of paper events to callbacks (camelCase `on*` and/or raw). * @group Hooks * @example * ```tsx * import { GraphProvider, Paper, useOnPaperEvents } from '@joint/react'; * * function SelectionLogger() { * useOnPaperEvents({ * onElementPointerClick: ({ id }) => console.log('clicked element', id), * onBlankPointerClick: ({ x, y }) => console.log('clicked blank at', x, y), * }); * return null; * } * * * } /> * * * ``` */ declare function useOnPaperEvents(handlers: PaperEventMap): void; /** * Subscribes to paper events on a specific paper you name, by registered id, * `dia.Paper` instance, or React ref. Use this to target one paper when several * are mounted (e.g. a main canvas plus a minimap). Same camelCase/raw handler * forms and always-latest semantics as the context form. * @title On a specific paper * @param paperTarget - The paper to listen on, see {@link PaperTarget}. * @param handlers - Map of paper events to callbacks (camelCase `on*` and/or raw). * @group Hooks * @example * ```tsx * import { useOnPaperEvents } from '@joint/react'; * * // Target a paper by its registered id. * function MinimapLogger() { * useOnPaperEvents('minimap', { * onBlankPointerClick: ({ x, y }) => console.log('minimap click', x, y), * }); * return null; * } * ``` */ declare function useOnPaperEvents(paperTarget: PaperTarget, handlers: PaperEventMap): void; interface CellDragStateBase { /** True when cell is being dragged. */ readonly isDragging: boolean; /** Reserved drop-validity flag; currently always `true` (not yet computed). */ readonly canDrop: boolean; /** True when cell is a preview. */ readonly isPreview: boolean; /** Bounding box of the dragged cell, in paper coordinates. */ readonly dropArea?: g.Rect; /** Pointer event for this drag frame. */ readonly event?: dia.Event; /** The paper the cell is being dragged on. */ readonly paper?: dia.Paper; /** Convenience alias for `paper.model`. */ readonly graph?: dia.Graph; /** ID of the cell being dragged. */ readonly cellId?: dia.Cell.ID; } interface CellDragStateDragging extends Required { isDragging: true; } interface CellDragStateIdle extends CellDragStateBase { isDragging: false; } /** * Drag state for the current cell, returned by {@link useCellDrag}. While a * drag is in progress (`isDragging` is `true`), the active fields (`event`, * `dropArea`, `paper`, `graph`, `cellId`) carry the live drag; when `false` * they are `undefined`. `isPreview` and `canDrop` are always present. * @group Types */ type CellDragState = CellDragStateDragging | CellDragStateIdle; /** * Tracks the live drag state of the current cell while the user drags it * across the paper. Read `isDragging` to dim the element, or `canDrop` / * `dropArea` to render a drop indicator. Use it inside a `renderElement` * callback. * * Only the cell being dragged re-renders; every other cell receives a shared, * frozen idle reference, so large diagrams stay cheap to render. * @group Hooks * @returns the {@link CellDragState} scoped to the current cell * @example * ```tsx * import { Paper, useCellDrag } from '@joint/react'; * * function MyElement({ label }: { label: string }) { * const { isDragging } = useCellDrag(); * return ( *
* {label} *
* ); * } * * } />; * ``` */ declare function useCellDrag(): CellDragState; export { DEFAULT_PAPER_ID as D, GraphStore as G, LinkModel as S, usePaperStore as a$, linkMarkerOneOrMany as aA, linkRoutingOrthogonal as aB, linkRoutingSmooth as aC, linkRoutingStraight as aD, linkStyle as aE, linkStyleLine as aF, linkStyleWrapper as aG, resolveLinkMarker as aH, useCellDrag as aI, useMeasureElement as aJ, useOnPaperEvents as aK, usePaper as aL, PaperStore as aM, PaperView as aP, AUTO_SIZE_OPTION as aR, DEFAULT_CELL_NAMESPACE as aT, PORTAL_SELECTOR as aW, createAtom as aZ, subscribeToPaperEvents as a_, elementAttributes as af, elementPort as ag, elementPorts as ah, linkAttributes as ai, linkLabel as aj, linkLabels as ak, linkMarkerArrow as al, linkMarkerArrowDouble as am, linkMarkerArrowOpen as an, linkMarkerArrowQuill as ao, linkMarkerArrowSunken as ap, linkMarkerCircle as aq, linkMarkerCross as ar, linkMarkerDiamond as as, linkMarkerFork as at, linkMarkerForkClose as au, linkMarkerLine as av, linkMarkerMany as aw, linkMarkerManyOptional as ax, linkMarkerOne as ay, linkMarkerOneOptional as az, useResolvePaperId as b0, ELEMENT_MODEL_TYPE as i, LINK_MODEL_TYPE as j, ElementModel as y }; export type { PaperEventMap as $, AutoSizeOrigin as A, ElementPortShape as B, CellInput as C, ElementJSONInit as E, ElementPosition as F, ElementSize as H, IncrementalCellsChange as I, LinkLabel as J, LinkMarker as K, LinkJSONInit as L, LinkMarkerName as M, LinkMarkerOptions as N, OnIncrementalCellsChange as O, PaperProps as P, LinkMarkerRecord as Q, LinkMode as R, LinkRecord as T, LinkRoutingOrthogonalOptions as U, LinkRoutingSmoothOptions as V, LinkRoutingStraightOptions as W, LinkStyle as X, MeasureElementOptions as Y, PaperApi as Z, PaperEventHandler as _, CellId as a, PaperOptions as a0, PaperTransform as a1, PortalHostCell as a2, PortalSelector as a3, PortalSelectorParams as a4, RenderElement as a5, RenderLink as a6, TransformElementLayout as a7, TransformElementLayoutParams as a8, ValidateConnection as a9, Feature as aN, GraphStoreInternalSnapshot as aO, Atom as aQ, AddPaperOptions as aS, GraphStoreOptions as aU, OmitWithoutIndexSignature as aV, PaperStoreOptions as aX, PaperStoreState as aY, ValidateConnectionParams as aa, ValidateEmbedding as ab, ValidateEmbeddingParams as ac, ValidateUnembedding as ad, ValidateUnembeddingParams as ae, CellRef as b, ArrayUpdate as c, AnyCellRecord as d, CellRecord as e, Computed as f, PaperTarget as g, ElementRecord as h, LinkLayout as k, CanConnectOptions as l, CellDragState as m, CellInteractivity as n, CellInteractivityParams as o, CellVisibility as p, CellVisibilityParams as q, ConnectionEnd as r, ConnectionStrategy as s, ConnectionStrategyOptions as t, ConnectionStrategyParams as u, ConnectionStrategyPin as v, DefaultLink as w, DefaultLinkParams as x, ElementPort as z };