f1ow 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/components/Canvas/ConnectionPoints.d.ts +2 -0
- package/dist/components/Canvas/TextHtmlOverlay.d.ts +12 -0
- package/dist/components/shapes/TextLabel.d.ts +1 -1
- package/dist/components/shapes/TextShape.d.ts +1 -0
- package/dist/f1ow-collaboration.js +2603 -0
- package/dist/f1ow.js +9787 -12922
- package/dist/f1ow.umd.cjs +122 -15860
- package/dist/hooks/useFlowAnimation.d.ts +8 -0
- package/dist/lib/FlowCanvasProps.d.ts +31 -1
- package/dist/lib/collaboration.d.ts +10 -0
- package/dist/lib/index.d.ts +7 -10
- package/dist/store/CanvasStoreContext.d.ts +14 -0
- package/dist/store/useCanvasStore.d.ts +32 -0
- package/dist/syncBridge-BBEfPdNb.js +254 -0
- package/dist/types/index.d.ts +97 -1
- package/dist/utils/connection.d.ts +30 -2
- package/dist/utils/editable.d.ts +2 -0
- package/dist/utils/elbow.d.ts +41 -8
- package/dist/utils/labelMetrics.d.ts +21 -0
- package/dist/utils/markdown.d.ts +14 -0
- package/dist/utils/markdownEditing.d.ts +1 -0
- package/dist/utils/textBinding.d.ts +18 -0
- package/dist/utils/textStyleTargets.d.ts +6 -0
- package/dist/yjsProvider-CXmqUJXr.js +72 -0
- package/package.json +121 -107
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { default as Konva } from 'konva';
|
|
2
|
+
/**
|
|
3
|
+
* Animate dashOffset on a Konva node to create a "flowing" dash effect.
|
|
4
|
+
* @param nodeRef - React ref to a Konva Line or Shape node
|
|
5
|
+
* @param enabled - whether animation is active
|
|
6
|
+
* @param speed - pixels per second (default 40)
|
|
7
|
+
*/
|
|
8
|
+
export declare function useFlowAnimation(nodeRef: React.RefObject<Konva.Line | Konva.Shape | null>, enabled: boolean, speed?: number): void;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { CanvasElement, ElementStyle, ToolType } from '../types';
|
|
1
|
+
import { CanvasElement, ElementStyle, ToolType, ConnectionConfig } from '../types';
|
|
2
2
|
import { ContextMenuItem } from '../components/ContextMenu/ContextMenu';
|
|
3
3
|
import { CollaborationConfig } from '../collaboration/types';
|
|
4
4
|
import { CustomElementConfig } from '../utils/elementRegistry';
|
|
5
5
|
import { RenderAnnotationFn } from '../components/Canvas/AnnotationsOverlay';
|
|
6
|
+
import { CanvasStore } from '../store/useCanvasStore';
|
|
6
7
|
export type { ContextMenuItem };
|
|
7
8
|
/** Context passed to custom context menu renderers */
|
|
8
9
|
export interface ContextMenuContext {
|
|
@@ -122,6 +123,19 @@ export interface FlowCanvasProps {
|
|
|
122
123
|
* Pass a `CollaborationConfig` to connect, or `undefined`/`null` to disable.
|
|
123
124
|
*/
|
|
124
125
|
collaboration?: CollaborationConfig | null;
|
|
126
|
+
/**
|
|
127
|
+
* Optional canvas store instance produced by `createCanvasStore()`.
|
|
128
|
+
* When supplied, this `<FlowCanvas>` and its descendant React
|
|
129
|
+
* subscribers (Toolbar, StylePanel, overlays) read state from this
|
|
130
|
+
* isolated store instead of the module-level singleton, allowing
|
|
131
|
+
* multiple canvases to coexist on the same page without cross-talk.
|
|
132
|
+
*
|
|
133
|
+
* Note: tools, keyboard shortcuts, and the collaboration sync bridge
|
|
134
|
+
* still read from the singleton via `getState()`. Until that wiring
|
|
135
|
+
* is migrated, those subsystems target the singleton even when this
|
|
136
|
+
* prop is supplied.
|
|
137
|
+
*/
|
|
138
|
+
store?: CanvasStore;
|
|
125
139
|
/**
|
|
126
140
|
* Register custom element types for this canvas instance.
|
|
127
141
|
*
|
|
@@ -143,6 +157,22 @@ export interface FlowCanvasProps {
|
|
|
143
157
|
* ```
|
|
144
158
|
*/
|
|
145
159
|
customElementTypes?: CustomElementConfig[];
|
|
160
|
+
/**
|
|
161
|
+
* Configure the connection/binding system behavior.
|
|
162
|
+
* Controls snap thresholds, port visibility, default line styles, and more.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* <FlowCanvas
|
|
167
|
+
* connectionConfig={{
|
|
168
|
+
* enablePorts: true,
|
|
169
|
+
* snapThreshold: 20,
|
|
170
|
+
* defaultLineType: 'elbow',
|
|
171
|
+
* }}
|
|
172
|
+
* />
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
connectionConfig?: ConnectionConfig;
|
|
146
176
|
/**
|
|
147
177
|
* Configure Web Workers for background processing (elbow routing, SVG export).
|
|
148
178
|
*
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { CollaborationUser, AwarenessState, CollaborationConfig, ConnectionStatus, CollaborationEvent, } from '../collaboration/types';
|
|
2
|
+
export { createCollaborationProvider, destroyCollaborationProvider, getYDoc, getYProvider, getYElements, isCollaborationActive, onStatusChange, updateAwareness, getRemoteAwareness, } from '../collaboration/yjsProvider';
|
|
3
|
+
export { startSync, stopSync } from '../collaboration/syncBridge';
|
|
4
|
+
export { elementToYMap, yMapToElement, SYNC_FIELDS, STYLE_FIELDS } from '../collaboration/syncBridgeCodec';
|
|
5
|
+
export { CollaborationManager } from '../collaboration/CollaborationManager';
|
|
6
|
+
export { SyncWorkerAdapter } from '../collaboration/syncWorker';
|
|
7
|
+
export type { WorkerInMessage, WorkerOutMessage, SyncWorkerCallbacks } from '../collaboration/syncWorker';
|
|
8
|
+
export { useCollaboration } from '../collaboration/useCollaboration';
|
|
9
|
+
export type { UseCollaborationReturn } from '../collaboration/useCollaboration';
|
|
10
|
+
export { default as CursorOverlay } from '../collaboration/CursorOverlay';
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -2,20 +2,23 @@ export { default as FlowCanvas } from './FlowCanvas';
|
|
|
2
2
|
export type { FlowCanvasProps, FlowCanvasRef, FlowCanvasTheme, ContextMenuItem, ContextMenuContext, } from './FlowCanvasProps';
|
|
3
3
|
export { DEFAULT_THEME } from './FlowCanvasProps';
|
|
4
4
|
export type { AnnotationContext, AnnotationScreenBounds, RenderAnnotationFn, } from '../components/Canvas/AnnotationsOverlay';
|
|
5
|
-
export type { CanvasElement, RectangleElement, EllipseElement, DiamondElement, LineElement, ArrowElement, FreeDrawElement, TextElement, ImageElement, BaseElement, ElementStyle, ElementType, ToolType, Point, ViewportState, ConnectionAnchor, BoundElement, Binding, SnapTarget, Arrowhead, LineType, TextAlign, VerticalAlign, ImageScaleMode, ImageCrop, ElementMeta, CanvasOperation, } from '../types';
|
|
6
|
-
export { useCanvasStore } from '../store/useCanvasStore';
|
|
5
|
+
export type { CanvasElement, RectangleElement, EllipseElement, DiamondElement, LineElement, ArrowElement, FreeDrawElement, TextElement, ImageElement, BaseElement, ElementStyle, ElementType, ToolType, Point, ViewportState, ConnectionAnchor, BoundElement, Binding, SnapTarget, Arrowhead, LineType, TextAlign, VerticalAlign, ImageScaleMode, ImageCrop, ElementMeta, CanvasOperation, AnchorId, Port, SnapMode, LineGradient, LineTaper, LineStyleExtension, ConnectionConfig, } from '../types';
|
|
6
|
+
export { useCanvasStore, createCanvasStore } from '../store/useCanvasStore';
|
|
7
|
+
export type { CanvasStore } from '../store/useCanvasStore';
|
|
8
|
+
export { CanvasStoreProvider, useCanvasStoreInstance } from '../store/CanvasStoreContext';
|
|
9
|
+
export type { CanvasStoreProviderProps } from '../store/CanvasStoreContext';
|
|
7
10
|
export { DEFAULT_STYLE, STROKE_COLORS, FILL_COLORS, STROKE_WIDTHS, TOOLS, ARROWHEAD_TYPES, LINE_TYPES, ROUGHNESS_CONFIGS } from '../constants';
|
|
8
11
|
export { generateId } from '../utils/id';
|
|
9
12
|
export { distance, normalizeRect, rotatePoint, isPointInRect, getDiamondPoints, getStrokeDash } from '../utils/geometry';
|
|
10
13
|
export { exportToDataURL, downloadPNG, exportToJSON, downloadJSON, exportToSVG, downloadSVG } from '../utils/export';
|
|
11
14
|
export { drawArrowhead, arrowheadSize, flatToPoints } from '../utils/arrowheads';
|
|
12
15
|
export { computeCurveControlPoint, quadBezierAt, quadBezierTangent, curveArrowPrev, CURVE_RATIO } from '../utils/curve';
|
|
16
|
+
export { anchorToFixedPoint, fixedPointToAnchor, resolvePort, resolveBindingPoint, createBindingFromSnap, isBindingStale, findNearestSnapTarget, recomputeBoundPoints, computeFixedPoint, getEdgePointFromFixedPoint, isConnectable, getConnectionPoints, getEdgePoint, getAnchorPosition, findConnectorsForElement, addBoundElement, removeBoundElement, syncBoundElements, } from '../utils/connection';
|
|
13
17
|
export { LABEL_PADDING_H, LABEL_PADDING_V, LABEL_CORNER, LABEL_LINE_HEIGHT, LABEL_MIN_WIDTH, measureLabelText, computePillSize } from '../utils/labelMetrics';
|
|
14
18
|
export { elementRegistry, registerCustomElement } from '../utils/elementRegistry';
|
|
15
19
|
export type { CustomElementConfig, ValidationResult } from '../utils/elementRegistry';
|
|
16
20
|
export { computeElbowPoints, computeElbowRoute, simplifyElbowPath, clearElbowRouteCache, directionFromFixedPoint, directionFromPoints, directionFromShapeToPoint, directionFromEdgePoint, getElbowPreferredDirection, } from '../utils/elbow';
|
|
17
21
|
export type { Direction } from '../utils/elbow';
|
|
18
|
-
export { getConnectionPoints, getEdgePoint, getEdgePointFromFixedPoint, computeFixedPoint, getAnchorPosition, findNearestSnapTarget, isConnectable, recomputeBoundPoints, findConnectorsForElement, addBoundElement, removeBoundElement, syncBoundElements, } from '../utils/connection';
|
|
19
22
|
export { fileToDataURL, loadImage, computeImageElementDimensions, createImageElement, getImageFilesFromDataTransfer, extractImageDataFromClipboard, clipboardHasImage, resolveImageSource, openImageFilePicker, } from '../utils/image';
|
|
20
23
|
export { getVisibleBounds, getElementAABB, aabbOverlaps, cullToViewport, buildElementMap, cloneElementsForHistory, rafThrottle, batchElementUpdates, } from '../utils/performance';
|
|
21
24
|
export type { AABB } from '../utils/performance';
|
|
@@ -41,19 +44,13 @@ export { generateKeyBetween, generateNKeysBetween, isValidFractionalIndex, compa
|
|
|
41
44
|
export { OperationLog, opAdd, opDelete, opMove, opResize, opStyle, opRotate, opReorder, opUpdatePoints, opSetText, opBatch, applyOperation, detectOperations, } from '../utils/crdtPrep';
|
|
42
45
|
export type { OperationEntry } from '../utils/crdtPrep';
|
|
43
46
|
export type { CollaborationUser, AwarenessState, CollaborationConfig, ConnectionStatus, CollaborationEvent, } from '../collaboration/types';
|
|
44
|
-
export { createCollaborationProvider, destroyCollaborationProvider, getYDoc, getYProvider, getYElements, isCollaborationActive, onStatusChange, updateAwareness, getRemoteAwareness, } from '../collaboration/yjsProvider';
|
|
45
|
-
export { startSync, stopSync } from '../collaboration/syncBridge';
|
|
46
|
-
export { elementToYMap, yMapToElement, SYNC_FIELDS, STYLE_FIELDS } from '../collaboration/syncBridgeCodec';
|
|
47
|
-
export { CollaborationManager } from '../collaboration/CollaborationManager';
|
|
48
|
-
export { SyncWorkerAdapter } from '../collaboration/syncWorker';
|
|
49
|
-
export type { WorkerInMessage, WorkerOutMessage, SyncWorkerCallbacks } from '../collaboration/syncWorker';
|
|
50
47
|
export { useCollaboration } from '../collaboration/useCollaboration';
|
|
51
48
|
export type { UseCollaborationReturn } from '../collaboration/useCollaboration';
|
|
52
49
|
export { default as CursorOverlay } from '../collaboration/CursorOverlay';
|
|
53
50
|
export { TileCache, tileKey } from '../rendering/tileCache';
|
|
54
51
|
export type { TileCoord } from '../rendering/tileCache';
|
|
55
52
|
export { TileRenderer, TILE_SIZE, discreteZoom, worldTileSize, getVisibleTiles, tileBounds, getElementTiles, } from '../rendering/tileRenderer';
|
|
56
|
-
export type { TileDrawFn, TileRendererOptions } from '../rendering/tileRenderer';
|
|
53
|
+
export type { TileDrawFn, TileRendererOptions, TileSpatialQuery } from '../rendering/tileRenderer';
|
|
57
54
|
export { useTileRenderer } from '../rendering/useTileRenderer';
|
|
58
55
|
export type { UseTileRendererOptions, UseTileRendererReturn } from '../rendering/useTileRenderer';
|
|
59
56
|
export { TextureAtlas } from '../webgl/textureAtlas';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { CanvasStore } from './useCanvasStore';
|
|
3
|
+
export interface CanvasStoreProviderProps {
|
|
4
|
+
/** Store instance produced by `createCanvasStore()`. */
|
|
5
|
+
store: CanvasStore;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function CanvasStoreProvider({ store, children }: CanvasStoreProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the canvas store for the current React subtree. Falls back to
|
|
11
|
+
* the singleton `useCanvasStore` when no provider is mounted, so existing
|
|
12
|
+
* single-instance apps work without changes.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useCanvasStoreInstance(): CanvasStore;
|
|
@@ -15,11 +15,17 @@ interface ElementDiff {
|
|
|
15
15
|
*/
|
|
16
16
|
interface HistoryEntry {
|
|
17
17
|
diffs: ElementDiff[];
|
|
18
|
+
/** Element order before this entry, used to restore z-order changes. */
|
|
19
|
+
beforeOrder?: string[];
|
|
20
|
+
/** Element order after this entry, used to redo z-order changes. */
|
|
21
|
+
afterOrder?: string[];
|
|
18
22
|
/** Optional named mark/checkpoint for grouping */
|
|
19
23
|
mark?: string;
|
|
20
24
|
/** Timestamp for squash heuristics */
|
|
21
25
|
timestamp: number;
|
|
22
26
|
}
|
|
27
|
+
type AlignMode = 'left' | 'centerH' | 'right' | 'top' | 'centerV' | 'bottom';
|
|
28
|
+
type FlipAxis = 'horizontal' | 'vertical';
|
|
23
29
|
interface CanvasState {
|
|
24
30
|
elements: CanvasElement[];
|
|
25
31
|
selectedIds: string[];
|
|
@@ -35,6 +41,8 @@ interface CanvasState {
|
|
|
35
41
|
historyIndex: number;
|
|
36
42
|
/** Baseline snapshot for computing diffs against current state */
|
|
37
43
|
_historyBaseline: Map<string, CanvasElement>;
|
|
44
|
+
/** Baseline element order for z-order history. */
|
|
45
|
+
_historyOrderBaseline: string[];
|
|
38
46
|
/** Whether history recording is temporarily paused */
|
|
39
47
|
_historyPaused: boolean;
|
|
40
48
|
showGrid: boolean;
|
|
@@ -55,6 +63,9 @@ interface CanvasState {
|
|
|
55
63
|
sendToBack: (ids: string[]) => void;
|
|
56
64
|
bringForward: (ids: string[]) => void;
|
|
57
65
|
sendBackward: (ids: string[]) => void;
|
|
66
|
+
alignElements: (ids: string[], mode: AlignMode) => void;
|
|
67
|
+
rotateElements: (ids: string[], deltaDegrees: number) => void;
|
|
68
|
+
flipElements: (ids: string[], axis: FlipAxis) => void;
|
|
58
69
|
toggleLockElements: (ids: string[]) => void;
|
|
59
70
|
groupElements: (ids: string[]) => void;
|
|
60
71
|
ungroupElements: (ids: string[]) => void;
|
|
@@ -123,5 +134,26 @@ interface CanvasState {
|
|
|
123
134
|
canRedo: () => boolean;
|
|
124
135
|
toggleGrid: () => void;
|
|
125
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Factory: create a fresh canvas store instance.
|
|
139
|
+
*
|
|
140
|
+
* Each call returns an independent Zustand store with its own elements,
|
|
141
|
+
* selection, viewport, and history. Use this with `CanvasStoreProvider`
|
|
142
|
+
* to render multiple `<FlowCanvas>` instances side-by-side without state
|
|
143
|
+
* cross-talk on the React subscriber side.
|
|
144
|
+
*
|
|
145
|
+
* Note: the module-level `useCanvasStore` singleton remains exported for
|
|
146
|
+
* backward compatibility and is still used by tools, hooks, and the
|
|
147
|
+
* collaboration sync bridge that read state via `getState()`. Full
|
|
148
|
+
* multi-instance isolation across those subsystems is a follow-up phase.
|
|
149
|
+
*/
|
|
150
|
+
export type CanvasStore = ReturnType<typeof createCanvasStore>;
|
|
151
|
+
export declare function createCanvasStore(): import('zustand').UseBoundStore<import('zustand').StoreApi<CanvasState>>;
|
|
152
|
+
/**
|
|
153
|
+
* Default singleton store. Used by tools, hooks, and the collaboration sync
|
|
154
|
+
* bridge that read state via `getState()`. Most apps render a single
|
|
155
|
+
* `<FlowCanvas>` per page, in which case this singleton is what the
|
|
156
|
+
* `CanvasStoreProvider` exposes.
|
|
157
|
+
*/
|
|
126
158
|
export declare const useCanvasStore: import('zustand').UseBoundStore<import('zustand').StoreApi<CanvasState>>;
|
|
127
159
|
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import * as A from "yjs";
|
|
2
|
+
import { useCanvasStore as w } from "./f1ow.js";
|
|
3
|
+
import { getYElements as _, getYDoc as W } from "./yjsProvider-CXmqUJXr.js";
|
|
4
|
+
const R = [
|
|
5
|
+
"id",
|
|
6
|
+
"type",
|
|
7
|
+
"x",
|
|
8
|
+
"y",
|
|
9
|
+
"width",
|
|
10
|
+
"height",
|
|
11
|
+
"rotation",
|
|
12
|
+
"isLocked",
|
|
13
|
+
"isVisible",
|
|
14
|
+
"sortOrder",
|
|
15
|
+
"version"
|
|
16
|
+
], T = [
|
|
17
|
+
"strokeColor",
|
|
18
|
+
"fillColor",
|
|
19
|
+
"strokeWidth",
|
|
20
|
+
"opacity",
|
|
21
|
+
"strokeStyle",
|
|
22
|
+
"roughness",
|
|
23
|
+
"fontSize",
|
|
24
|
+
"fontFamily"
|
|
25
|
+
];
|
|
26
|
+
function B(t, e) {
|
|
27
|
+
const s = t;
|
|
28
|
+
for (const i of R) {
|
|
29
|
+
const r = s[i];
|
|
30
|
+
r !== void 0 && e.set(i, r);
|
|
31
|
+
}
|
|
32
|
+
if (t.style)
|
|
33
|
+
for (const i of T)
|
|
34
|
+
e.set(`style.${i}`, t.style[i]);
|
|
35
|
+
switch (t.boundElements ? e.set("boundElements", JSON.stringify(t.boundElements)) : e.set("boundElements", null), t.ports && e.set("ports", JSON.stringify(t.ports)), "lineStyle" in t && t.lineStyle && e.set("lineStyle", JSON.stringify(t.lineStyle)), t.ports && e.set("ports", JSON.stringify(t.ports)), "lineStyle" in t && t.lineStyle && e.set("lineStyle", JSON.stringify(t.lineStyle)), t.groupIds && e.set("groupIds", JSON.stringify(t.groupIds)), t.type) {
|
|
36
|
+
case "rectangle":
|
|
37
|
+
e.set("cornerRadius", t.cornerRadius);
|
|
38
|
+
break;
|
|
39
|
+
case "line":
|
|
40
|
+
case "arrow":
|
|
41
|
+
e.set("points", JSON.stringify(t.points)), e.set("lineType", t.lineType), t.curvature !== void 0 && e.set("curvature", t.curvature), e.set("startBinding", t.startBinding ? JSON.stringify(t.startBinding) : null), e.set("endBinding", t.endBinding ? JSON.stringify(t.endBinding) : null), t.type === "arrow" && (e.set("startArrowhead", t.startArrowhead), e.set("endArrowhead", t.endArrowhead));
|
|
42
|
+
break;
|
|
43
|
+
case "freedraw":
|
|
44
|
+
e.set("points", JSON.stringify(t.points));
|
|
45
|
+
break;
|
|
46
|
+
case "text":
|
|
47
|
+
e.set("text", t.text), e.set("containerId", t.containerId), e.set("textAlign", t.textAlign), e.set("verticalAlign", t.verticalAlign);
|
|
48
|
+
break;
|
|
49
|
+
case "image":
|
|
50
|
+
e.set("src", t.src), e.set("naturalWidth", t.naturalWidth), e.set("naturalHeight", t.naturalHeight), e.set("scaleMode", t.scaleMode), e.set("crop", t.crop ? JSON.stringify(t.crop) : null), e.set("cornerRadius", t.cornerRadius), e.set("alt", t.alt);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function E(t) {
|
|
55
|
+
const e = t.get("type"), s = t.get("id");
|
|
56
|
+
if (!e || !s) return null;
|
|
57
|
+
const i = {};
|
|
58
|
+
for (const n of T) {
|
|
59
|
+
const h = t.get(`style.${n}`);
|
|
60
|
+
h !== void 0 && (i[n] = h);
|
|
61
|
+
}
|
|
62
|
+
const r = {
|
|
63
|
+
id: s,
|
|
64
|
+
type: e,
|
|
65
|
+
x: t.get("x") ?? 0,
|
|
66
|
+
y: t.get("y") ?? 0,
|
|
67
|
+
width: t.get("width") ?? 100,
|
|
68
|
+
height: t.get("height") ?? 100,
|
|
69
|
+
rotation: t.get("rotation") ?? 0,
|
|
70
|
+
isLocked: t.get("isLocked") ?? !1,
|
|
71
|
+
isVisible: t.get("isVisible") ?? !0,
|
|
72
|
+
version: t.get("version") ?? 0,
|
|
73
|
+
style: i,
|
|
74
|
+
boundElements: u(t.get("boundElements")) ?? null,
|
|
75
|
+
groupIds: u(t.get("groupIds")) ?? void 0,
|
|
76
|
+
sortOrder: t.get("sortOrder") ?? void 0,
|
|
77
|
+
ports: u(t.get("ports")) ?? void 0
|
|
78
|
+
};
|
|
79
|
+
switch (e) {
|
|
80
|
+
case "rectangle":
|
|
81
|
+
r.cornerRadius = t.get("cornerRadius") ?? 0;
|
|
82
|
+
break;
|
|
83
|
+
case "line":
|
|
84
|
+
case "arrow":
|
|
85
|
+
r.points = u(t.get("points")) ?? [0, 0, 100, 0], r.lineType = t.get("lineType") ?? "sharp", r.curvature = t.get("curvature") ?? void 0, r.startBinding = u(t.get("startBinding")), r.endBinding = u(t.get("endBinding")), e === "arrow" && (r.startArrowhead = t.get("startArrowhead") ?? null, r.endArrowhead = t.get("endArrowhead") ?? "arrow");
|
|
86
|
+
{
|
|
87
|
+
const n = u(t.get("lineStyle"));
|
|
88
|
+
n && (r.lineStyle = n);
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case "freedraw":
|
|
92
|
+
r.points = u(t.get("points")) ?? [];
|
|
93
|
+
break;
|
|
94
|
+
case "text":
|
|
95
|
+
r.text = t.get("text") ?? "", r.containerId = t.get("containerId") ?? null, r.textAlign = t.get("textAlign") ?? "center", r.verticalAlign = t.get("verticalAlign") ?? "middle";
|
|
96
|
+
break;
|
|
97
|
+
case "image":
|
|
98
|
+
r.src = t.get("src") ?? "", r.naturalWidth = t.get("naturalWidth") ?? 0, r.naturalHeight = t.get("naturalHeight") ?? 0, r.scaleMode = t.get("scaleMode") ?? "fit", r.crop = u(t.get("crop")) ?? null, r.cornerRadius = t.get("cornerRadius") ?? 0, r.alt = t.get("alt") ?? "";
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
return r;
|
|
102
|
+
}
|
|
103
|
+
function u(t) {
|
|
104
|
+
if (t == null) return null;
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(t);
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
let m = !1, S = !1, x = null, J = null, O = null, a = [];
|
|
112
|
+
function $(t = 50) {
|
|
113
|
+
const e = W(), s = _();
|
|
114
|
+
if (!e || !s)
|
|
115
|
+
return;
|
|
116
|
+
if (H(), s.size > 0) {
|
|
117
|
+
m = !0;
|
|
118
|
+
const o = C(s);
|
|
119
|
+
w.getState().setElements(o), a = o, m = !1;
|
|
120
|
+
} else {
|
|
121
|
+
const o = w.getState().elements;
|
|
122
|
+
o.length > 0 && (S = !0, e.transact(() => {
|
|
123
|
+
for (const l of o) {
|
|
124
|
+
const c = new A.Map();
|
|
125
|
+
B(l, c), s.set(l.id, c);
|
|
126
|
+
}
|
|
127
|
+
}, "local-init"), S = !1), a = o;
|
|
128
|
+
}
|
|
129
|
+
const i = (o, l) => {
|
|
130
|
+
if (l.origin === "local-sync" || l.origin === "local-init" || S) return;
|
|
131
|
+
m = !0;
|
|
132
|
+
const c = w.getState();
|
|
133
|
+
let d = [...a], v = !1;
|
|
134
|
+
for (const [f, g] of o.keys)
|
|
135
|
+
if (g.action === "add" || g.action === "update") {
|
|
136
|
+
const b = s.get(f);
|
|
137
|
+
if (b) {
|
|
138
|
+
const k = E(b);
|
|
139
|
+
if (k) {
|
|
140
|
+
const N = d.findIndex((I) => I.id === f);
|
|
141
|
+
N >= 0 ? d[N] = k : d.push(k), v = !0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else g.action === "delete" && (d = d.filter((b) => b.id !== f), v = !0);
|
|
145
|
+
v && (d.sort((f, g) => f.sortOrder && g.sortOrder ? f.sortOrder < g.sortOrder ? -1 : f.sortOrder > g.sortOrder ? 1 : 0 : 0), c.setElements(d), a = d), m = !1;
|
|
146
|
+
};
|
|
147
|
+
let r = null;
|
|
148
|
+
const n = /* @__PURE__ */ new Set(), h = (o) => {
|
|
149
|
+
if (!S) {
|
|
150
|
+
for (const l of o) {
|
|
151
|
+
let c = l.target;
|
|
152
|
+
for (; c && !(c instanceof A.Map && c.parent === s); )
|
|
153
|
+
c = c.parent;
|
|
154
|
+
if (c instanceof A.Map) {
|
|
155
|
+
const d = c.get("id");
|
|
156
|
+
d && n.add(d);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
r && clearTimeout(r), r = setTimeout(() => {
|
|
160
|
+
if (n.size === 0 || S) return;
|
|
161
|
+
m = !0;
|
|
162
|
+
let l = [...a], c = !1;
|
|
163
|
+
for (const d of n) {
|
|
164
|
+
const v = s.get(d);
|
|
165
|
+
if (!v) continue;
|
|
166
|
+
const f = E(v);
|
|
167
|
+
if (!f) continue;
|
|
168
|
+
const g = l.findIndex((b) => b.id === d);
|
|
169
|
+
g >= 0 && (l[g] = f, c = !0);
|
|
170
|
+
}
|
|
171
|
+
n.clear(), c && (w.getState().setElements(l), a = l), m = !1;
|
|
172
|
+
}, 16);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
s.observe(i), s.observeDeep(h), J = () => {
|
|
176
|
+
s.unobserve(i), s.unobserveDeep(h), r && clearTimeout(r), n.clear();
|
|
177
|
+
}, x = w.subscribe(
|
|
178
|
+
(o) => {
|
|
179
|
+
m || o.elements !== a && (O && clearTimeout(O), O = setTimeout(() => {
|
|
180
|
+
L(o.elements, s, e);
|
|
181
|
+
}, t));
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
function H() {
|
|
186
|
+
x && (x(), x = null), J && (J(), J = null), O && (clearTimeout(O), O = null), a = [];
|
|
187
|
+
}
|
|
188
|
+
function L(t, e, s) {
|
|
189
|
+
S = !0, a = t;
|
|
190
|
+
const i = /* @__PURE__ */ new Map();
|
|
191
|
+
for (const r of t)
|
|
192
|
+
i.set(r.id, r);
|
|
193
|
+
s.transact(() => {
|
|
194
|
+
for (const [r] of e.entries())
|
|
195
|
+
i.has(r) || e.delete(r);
|
|
196
|
+
for (const r of t) {
|
|
197
|
+
let n = e.get(r.id);
|
|
198
|
+
n ? Y(r, n) : (n = new A.Map(), B(r, n), e.set(r.id, n));
|
|
199
|
+
}
|
|
200
|
+
}, "local-sync"), S = !1;
|
|
201
|
+
}
|
|
202
|
+
function Y(t, e) {
|
|
203
|
+
const s = t;
|
|
204
|
+
for (const r of R) {
|
|
205
|
+
const n = s[r];
|
|
206
|
+
n !== e.get(r) && e.set(r, n);
|
|
207
|
+
}
|
|
208
|
+
if (t.style)
|
|
209
|
+
for (const r of T) {
|
|
210
|
+
const n = t.style[r];
|
|
211
|
+
n !== e.get(`style.${r}`) && e.set(`style.${r}`, n);
|
|
212
|
+
}
|
|
213
|
+
const i = t.boundElements ? JSON.stringify(t.boundElements) : null;
|
|
214
|
+
switch (i !== e.get("boundElements") && e.set("boundElements", i), t.type) {
|
|
215
|
+
case "rectangle":
|
|
216
|
+
t.cornerRadius !== e.get("cornerRadius") && e.set("cornerRadius", t.cornerRadius);
|
|
217
|
+
break;
|
|
218
|
+
case "line":
|
|
219
|
+
case "arrow": {
|
|
220
|
+
const n = JSON.stringify(t.points);
|
|
221
|
+
n !== e.get("points") && e.set("points", n), t.lineType !== e.get("lineType") && e.set("lineType", t.lineType), t.curvature !== e.get("curvature") && e.set("curvature", t.curvature);
|
|
222
|
+
const h = t.startBinding ? JSON.stringify(t.startBinding) : null;
|
|
223
|
+
h !== e.get("startBinding") && e.set("startBinding", h);
|
|
224
|
+
const o = t.endBinding ? JSON.stringify(t.endBinding) : null;
|
|
225
|
+
o !== e.get("endBinding") && e.set("endBinding", o), t.type === "arrow" && (t.startArrowhead !== e.get("startArrowhead") && e.set("startArrowhead", t.startArrowhead), t.endArrowhead !== e.get("endArrowhead") && e.set("endArrowhead", t.endArrowhead));
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "freedraw": {
|
|
229
|
+
const n = JSON.stringify(t.points);
|
|
230
|
+
n !== e.get("points") && e.set("points", n);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case "text":
|
|
234
|
+
t.text !== e.get("text") && e.set("text", t.text), t.containerId !== e.get("containerId") && e.set("containerId", t.containerId), t.textAlign !== e.get("textAlign") && e.set("textAlign", t.textAlign), t.verticalAlign !== e.get("verticalAlign") && e.set("verticalAlign", t.verticalAlign);
|
|
235
|
+
break;
|
|
236
|
+
case "image":
|
|
237
|
+
t.src !== e.get("src") && e.set("src", t.src), t.naturalWidth !== e.get("naturalWidth") && e.set("naturalWidth", t.naturalWidth), t.naturalHeight !== e.get("naturalHeight") && e.set("naturalHeight", t.naturalHeight), t.scaleMode !== e.get("scaleMode") && e.set("scaleMode", t.scaleMode);
|
|
238
|
+
const r = t.crop ? JSON.stringify(t.crop) : null;
|
|
239
|
+
r !== e.get("crop") && e.set("crop", r), t.cornerRadius !== e.get("cornerRadius") && e.set("cornerRadius", t.cornerRadius), t.alt !== e.get("alt") && e.set("alt", t.alt);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function C(t) {
|
|
244
|
+
const e = [];
|
|
245
|
+
for (const [, s] of t.entries()) {
|
|
246
|
+
const i = E(s);
|
|
247
|
+
i && e.push(i);
|
|
248
|
+
}
|
|
249
|
+
return e.sort((s, i) => s.sortOrder && i.sortOrder ? s.sortOrder < i.sortOrder ? -1 : s.sortOrder > i.sortOrder ? 1 : 0 : 0), e;
|
|
250
|
+
}
|
|
251
|
+
export {
|
|
252
|
+
$ as startSync,
|
|
253
|
+
H as stopSync
|
|
254
|
+
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,6 +2,27 @@ export type ToolType = 'select' | 'hand' | 'rectangle' | 'ellipse' | 'diamond' |
|
|
|
2
2
|
export type ElementType = 'rectangle' | 'ellipse' | 'diamond' | 'line' | 'arrow' | 'freedraw' | 'text' | 'image';
|
|
3
3
|
export type Arrowhead = 'arrow' | 'triangle' | 'triangle_outline' | 'circle' | 'circle_outline' | 'diamond' | 'diamond_outline' | 'bar' | 'crowfoot_one' | 'crowfoot_many' | 'crowfoot_one_or_many';
|
|
4
4
|
export type LineType = 'sharp' | 'curved' | 'elbow';
|
|
5
|
+
/** Gradient definition for connector strokes */
|
|
6
|
+
export interface LineGradient {
|
|
7
|
+
/** Color stops: [offset (0-1), cssColor] */
|
|
8
|
+
stops: [number, string][];
|
|
9
|
+
/** Gradient follows the path direction */
|
|
10
|
+
type: 'along-path';
|
|
11
|
+
}
|
|
12
|
+
/** Tapered stroke: width varies from start to end */
|
|
13
|
+
export interface LineTaper {
|
|
14
|
+
startWidth: number;
|
|
15
|
+
endWidth: number;
|
|
16
|
+
}
|
|
17
|
+
/** Extended line style options for connectors */
|
|
18
|
+
export interface LineStyleExtension {
|
|
19
|
+
/** Gradient stroke along the connector path */
|
|
20
|
+
gradient?: LineGradient;
|
|
21
|
+
/** Tapered stroke width */
|
|
22
|
+
taper?: LineTaper;
|
|
23
|
+
/** Animated flow direction indicator (dash animation) */
|
|
24
|
+
flowAnimation?: boolean;
|
|
25
|
+
}
|
|
5
26
|
export type FreehandStyle = 'standard' | 'pen' | 'brush' | 'pencil';
|
|
6
27
|
export interface ElementStyle {
|
|
7
28
|
strokeColor: string;
|
|
@@ -27,6 +48,17 @@ export interface BaseElement {
|
|
|
27
48
|
isVisible: boolean;
|
|
28
49
|
/** Bidirectional refs: arrows/text bound to this element */
|
|
29
50
|
boundElements: BoundElement[] | null;
|
|
51
|
+
/**
|
|
52
|
+
* Custom ports defined on this element instance.
|
|
53
|
+
* Used for architecture diagrams where shapes have named I/O points.
|
|
54
|
+
*/
|
|
55
|
+
ports?: Port[];
|
|
56
|
+
/**
|
|
57
|
+
* Monotonic version counter — bumped on every geometry/port mutation.
|
|
58
|
+
* Used by binding system to detect stale worker results and cache invalidation.
|
|
59
|
+
* Auto-managed by the store; consumers should not set this directly.
|
|
60
|
+
*/
|
|
61
|
+
version: number;
|
|
30
62
|
/** Group hierarchy: element belongs to these groups (innermost first, outermost last) */
|
|
31
63
|
groupIds?: string[];
|
|
32
64
|
/**
|
|
@@ -123,6 +155,8 @@ export interface LineElement extends BaseElement {
|
|
|
123
155
|
curvature?: number;
|
|
124
156
|
startBinding: Binding | null;
|
|
125
157
|
endBinding: Binding | null;
|
|
158
|
+
/** Extended line style (gradient, taper, animation) */
|
|
159
|
+
lineStyle?: LineStyleExtension;
|
|
126
160
|
}
|
|
127
161
|
export interface ArrowElement extends BaseElement {
|
|
128
162
|
type: 'arrow';
|
|
@@ -141,6 +175,8 @@ export interface ArrowElement extends BaseElement {
|
|
|
141
175
|
curvature?: number;
|
|
142
176
|
startBinding: Binding | null;
|
|
143
177
|
endBinding: Binding | null;
|
|
178
|
+
/** Extended line style (gradient, taper, animation) */
|
|
179
|
+
lineStyle?: LineStyleExtension;
|
|
144
180
|
}
|
|
145
181
|
export interface FreeDrawElement extends BaseElement {
|
|
146
182
|
type: 'freedraw';
|
|
@@ -208,8 +244,23 @@ export interface SelectionBox {
|
|
|
208
244
|
width: number;
|
|
209
245
|
height: number;
|
|
210
246
|
}
|
|
247
|
+
/** Named anchor positions on a shape's bounding box (compass notation) */
|
|
248
|
+
export type AnchorId = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw' | 'center' | 'auto';
|
|
249
|
+
/** Custom port on an element — user-defined attachment point */
|
|
250
|
+
export interface Port {
|
|
251
|
+
/** Unique port identifier within the element */
|
|
252
|
+
id: string;
|
|
253
|
+
/** Normalized [0-1, 0-1] position on the element's bounding box */
|
|
254
|
+
ratio: [number, number];
|
|
255
|
+
/** Optional display label for the port */
|
|
256
|
+
label?: string;
|
|
257
|
+
/** Optional preferred edge (used for elbow routing direction) */
|
|
258
|
+
edge?: AnchorId;
|
|
259
|
+
}
|
|
260
|
+
/** How the binding target was determined */
|
|
261
|
+
export type SnapMode = 'anchor' | 'port' | 'edge' | 'center';
|
|
211
262
|
/**
|
|
212
|
-
* @deprecated Use
|
|
263
|
+
* @deprecated Use AnchorId instead. Kept for backward-compat exports.
|
|
213
264
|
*/
|
|
214
265
|
export type ConnectionAnchor = 'top' | 'bottom' | 'left' | 'right' | 'center';
|
|
215
266
|
/** Bidirectional reference stored on shapes to track bound connectors/text */
|
|
@@ -221,13 +272,30 @@ export type BoundElement = {
|
|
|
221
272
|
export interface Binding {
|
|
222
273
|
/** The element this end is connected to */
|
|
223
274
|
elementId: string;
|
|
275
|
+
/**
|
|
276
|
+
* Named anchor position. When set, takes precedence over fixedPoint.
|
|
277
|
+
* Resolves to a fixedPoint at bind time, but re-resolves after resize
|
|
278
|
+
* to maintain semantic meaning (e.g. "always top-center").
|
|
279
|
+
* 'auto' = let the system choose the nearest anchor.
|
|
280
|
+
*/
|
|
281
|
+
anchor?: AnchorId;
|
|
282
|
+
/** Custom port ID. When set, takes precedence over anchor and fixedPoint. */
|
|
283
|
+
portId?: string;
|
|
224
284
|
/**
|
|
225
285
|
* Continuous attachment ratio [0-1, 0-1] on target's bounding box.
|
|
226
286
|
* e.g. [0.5, 0] = top center, [1, 0.5] = right center, [0.3, 0.7] = arbitrary.
|
|
287
|
+
* Always populated — serves as resolved cache when anchor/port is set.
|
|
227
288
|
*/
|
|
228
289
|
fixedPoint: [number, number];
|
|
229
290
|
/** Gap between the edge of the shape and the arrow tip (px) */
|
|
230
291
|
gap: number;
|
|
292
|
+
/** How the binding target was determined */
|
|
293
|
+
snapMode: SnapMode;
|
|
294
|
+
/**
|
|
295
|
+
* Version of the target element at bind time.
|
|
296
|
+
* Used to detect stale bindings (e.g. worker results arriving after element changed).
|
|
297
|
+
*/
|
|
298
|
+
elementVersion: number;
|
|
231
299
|
/**
|
|
232
300
|
* Whether to bind to the exact fixedPoint position, or to the shape center.
|
|
233
301
|
* When false (default), the arrow connects from/to the shape's center,
|
|
@@ -250,6 +318,12 @@ export interface SnapTarget {
|
|
|
250
318
|
* - `false`: cursor is in the center zone → attaches to center for auto-routing
|
|
251
319
|
*/
|
|
252
320
|
isPrecise: boolean;
|
|
321
|
+
/** Named anchor if snapped to a cardinal position */
|
|
322
|
+
anchor?: AnchorId;
|
|
323
|
+
/** Custom port ID if snapped to a port */
|
|
324
|
+
portId?: string;
|
|
325
|
+
/** How the snap target was determined */
|
|
326
|
+
snapMode: SnapMode;
|
|
253
327
|
}
|
|
254
328
|
/** State for editing individual points of a line/arrow element */
|
|
255
329
|
export interface LinearEditState {
|
|
@@ -266,3 +340,25 @@ export interface LinearEditState {
|
|
|
266
340
|
/** Whether a point is currently being dragged */
|
|
267
341
|
isDraggingPoint: boolean;
|
|
268
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Consumer-facing configuration for the connection/binding system.
|
|
345
|
+
* Pass via `FlowCanvasProps.connectionConfig`.
|
|
346
|
+
*/
|
|
347
|
+
export interface ConnectionConfig {
|
|
348
|
+
/** Distance outside shape perimeter to activate edge snap (px). Default: 24 */
|
|
349
|
+
snapThreshold?: number;
|
|
350
|
+
/** Hysteresis margin to prevent edge/center mode flickering (px). Default: 6 */
|
|
351
|
+
hysteresisMargin?: number;
|
|
352
|
+
/** Clearance around shapes for elbow routing (px). Default: adaptive */
|
|
353
|
+
elbowMargin?: number;
|
|
354
|
+
/** Hit radius for port dots (px, before zoom compensation). Default: 8 */
|
|
355
|
+
portHitRadius?: number;
|
|
356
|
+
/** Minimum stub length for elbow route endpoints (px). Default: 36 */
|
|
357
|
+
stubLength?: number;
|
|
358
|
+
/** Whether to render custom ports on shapes. Default: true */
|
|
359
|
+
enablePorts?: boolean;
|
|
360
|
+
/** Default line style extension applied to new connectors */
|
|
361
|
+
defaultLineStyle?: LineStyleExtension;
|
|
362
|
+
/** Default line type for new arrows/lines. Default: 'sharp' */
|
|
363
|
+
defaultLineType?: LineType;
|
|
364
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CanvasElement, Point, ConnectionAnchor, Binding, SnapTarget, ArrowElement, LineElement, TextElement, BoundElement } from '../types';
|
|
1
|
+
import { CanvasElement, Point, ConnectionAnchor, Binding, SnapTarget, ArrowElement, LineElement, TextElement, BoundElement, AnchorId, SnapMode } from '../types';
|
|
2
2
|
/** Whether an element can be a connection target */
|
|
3
3
|
export declare function isConnectable(el: CanvasElement): boolean;
|
|
4
4
|
/** Get the 5 named anchor positions for a bounding-box shape */
|
|
@@ -7,6 +7,29 @@ export declare function getConnectionPoints(el: CanvasElement): Record<Connectio
|
|
|
7
7
|
* @deprecated Use fixedPoint-based getEdgePointFromFixedPoint() instead.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getAnchorPosition(el: CanvasElement, anchor: ConnectionAnchor): Point;
|
|
10
|
+
/** Map a named AnchorId to its fixedPoint ratio on the bounding box */
|
|
11
|
+
export declare function anchorToFixedPoint(anchor: AnchorId): [number, number];
|
|
12
|
+
/** Find the nearest cardinal AnchorId for a given fixedPoint */
|
|
13
|
+
export declare function fixedPointToAnchor(fp: [number, number]): AnchorId;
|
|
14
|
+
/** Resolve a port's ratio to a fixedPoint on an element */
|
|
15
|
+
export declare function resolvePort(el: CanvasElement, portId: string): [number, number] | null;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the effective fixedPoint for a binding, respecting priority:
|
|
18
|
+
* portId > anchor > fixedPoint.
|
|
19
|
+
*
|
|
20
|
+
* Returns the resolved fixedPoint and the effective snapMode.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveBindingPoint(binding: Binding, element: CanvasElement): {
|
|
23
|
+
fixedPoint: [number, number];
|
|
24
|
+
snapMode: SnapMode;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Create a Binding from a SnapTarget result.
|
|
28
|
+
* Convenience helper for LinearTool and LinearElementHandles.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createBindingFromSnap(snap: SnapTarget, gap: number, elementVersion: number): Binding;
|
|
31
|
+
/** Check whether a binding's elementVersion matches the current element */
|
|
32
|
+
export declare function isBindingStale(binding: Binding, element: CanvasElement): boolean;
|
|
10
33
|
/**
|
|
11
34
|
* Compute the appropriate gap between a connector and a shape edge,
|
|
12
35
|
* based on both the connector's stroke width and a base offset.
|
|
@@ -21,8 +44,13 @@ export declare function computeFixedPoint(el: CanvasElement, worldPt: Point): [n
|
|
|
21
44
|
/**
|
|
22
45
|
* Convert a fixedPoint ratio back to a world-space target point,
|
|
23
46
|
* then compute the edge intersection from center to that target.
|
|
47
|
+
*
|
|
48
|
+
* @param toward Optional direction hint for center fixedPoint [0.5, 0.5].
|
|
49
|
+
* When the fixedPoint is exactly center, there's no meaningful
|
|
50
|
+
* direction — `toward` provides the other endpoint so we can
|
|
51
|
+
* compute a stable edge exit. Without it, returns the element center.
|
|
24
52
|
*/
|
|
25
|
-
export declare function getEdgePointFromFixedPoint(el: CanvasElement, fixedPoint: [number, number], gap?: number): Point;
|
|
53
|
+
export declare function getEdgePointFromFixedPoint(el: CanvasElement, fixedPoint: [number, number], gap?: number, toward?: Point): Point;
|
|
26
54
|
/**
|
|
27
55
|
* Given a shape and an external world-space point, compute the point on
|
|
28
56
|
* the shape's perimeter closest to `toward`.
|