WeaveNodesSnappingPlugin
Allow to snap nodes to guides and to themselves
Overview
The WeaveNodesSnappingPlugin class that enables smart snapping behavior when users move or resize nodes. It helps align nodes with each other, grids, or guides automatically, improving layout precision and visual consistency across the canvas.
Snapping provides users with subtle, intuitive assistance when organizing elements, making building clean and aligned interfaces much easier—especially in collaborative environments.
Guides can be:
- STATIC: are guides that can be pre-defined, but the user cannot move, for example the center of a frame or its edges.
- CUSTOM: are guides that users can create, edit and delete.
The class extends the WeavePlugin class
Name
This plugin name property value is nodesSnapping.
Import
import { WeaveNodesSnappingPlugin } from "@inditextech/weave-sdk";Instantiation
new WeaveNodesSnappingPlugin(params?: WeaveNodesSnappingPluginParams);TypeScript types
const GUIDE_STATE = {
DEFAULT: "default",
SELECTED: "selected",
} as const;
const GUIDE_KIND = {
STATIC: "static",
CUSTOM: "custom",
EQUAL_DISTANCE: "equal-distance",
CENTERED_HORIZONTAL: "centered-horizontal",
CENTERED_VERTICAL: "centered-vertical",
} as const;
const GUIDE_ORIENTATION = {
["HORIZONTAL"]: "H",
["VERTICAL"]: "V",
} as const;
const GUIDE_DISTANCE_ORIGIN = {
FROM: "from",
TO: "to",
} as const;
const MOVE_ORIENTATION = {
["UP"]: "up",
["DOWN"]: "down",
["LEFT"]: "left",
["RIGHT"]: "right",
} as const;
const DEFAULT_SNAPPING_MANAGER_CONFIG: WeaveNodesSnappingPluginConfig = {
snap: {
tolerance: 5,
},
persistence: {
enabled: false,
},
movement: {
delta: 0.5,
shiftDelta: 10,
},
style: {
[GUIDE_KIND.CUSTOM]: {
default: {
stroke: "#FF3B30",
strokeWidth: 0.5,
dash: [],
opacity: 1,
},
selected: {
stroke: "#0D99FF",
strokeWidth: 1,
dash: [],
opacity: 1,
},
},
[GUIDE_KIND.STATIC]: {
default: {
stroke: "#FF3B30",
strokeWidth: 0.5,
dash: [6, 6],
opacity: 1,
},
selected: {
stroke: "#0D99FF",
strokeWidth: 1,
dash: [6, 6],
opacity: 1,
},
},
},
targetDistanceStyle: {
target: {
stroke: "#FF3B30",
strokeWidth: 1,
dash: [],
opacity: 1,
},
distance: {
opacity: 1,
line: {
stroke: "#FF3B30",
strokeWidth: 1,
dash: [],
opacity: 1,
},
text: {
fill: "#ffffff",
fontSize: 10,
fontFamily: "monospace",
opacity: 1,
},
background: {
fill: "#FF3B30",
cornerRadius: 4,
stroke: "#FF3B30",
strokeWidth: 0,
opacity: 1,
},
},
},
};
type MoveOrientationKeys = keyof typeof MOVE_ORIENTATION;
type MoveOrientation = (typeof MOVE_ORIENTATION)[MoveOrientationKeys];
type GuideKindKeys = keyof typeof GUIDE_KIND;
type GuideKind = (typeof GUIDE_KIND)[GuideKindKeys];
type DistanceOriginKeys = keyof typeof GUIDE_DISTANCE_ORIGIN;
type DistanceOrigin = (typeof GUIDE_DISTANCE_ORIGIN)[DistanceOriginKeys];
type GuideOrientationKeys = keyof typeof GUIDE_ORIENTATION;
type GuideOrientation = (typeof GUIDE_ORIENTATION)[GuideOrientationKeys];
type Guide = {
orientation: GuideOrientation;
value: number;
renderValue?: number;
kind: GuideKind;
guideId: string;
containerId: string;
persist?: boolean;
} & (
| {
kind: "static" | "custom";
}
| {
kind: "equal-distance";
distanceCombinationIndex: number;
distanceOrigin: DistanceOrigin;
distance: number;
}
| {
kind: "centered-horizontal" | "centered-vertical";
distance: number;
center: {
from: BoundingBoxWithId;
center: BoundingBoxWithId;
to: BoundingBoxWithId;
};
}
);
type SnapPoint = {
orientation: GuideOrientation;
guideId: string;
value: number;
offset: number;
kind: GuideKind;
};
type SnapMatch = {
orientation: GuideOrientation;
guideId: string;
containerId: string;
guide: number;
offset: number;
diff: number;
} & (
| {
kind: "static" | "custom";
}
| {
kind: "equal-distance";
distanceCombinationIndex: number;
distanceOrigin: DistanceOrigin;
distance: number;
}
| {
kind: "centered-horizontal" | "centered-vertical";
distance: number;
center: {
from: BoundingBoxWithId;
center: BoundingBoxWithId;
to: BoundingBoxWithId;
};
}
);
type SnapResult = {
vertical?: SnapMatch;
horizontal?: SnapMatch;
};
type SnapOptions = {
tolerance: number;
};
type VisibleWorldRect = {
x: number;
y: number;
width: number;
height: number;
};
type WeaveNodesSnappingPluginParams = {
config?: DeepPartial<WeaveNodesSnappingPluginConfig>;
};
type WeaveNodesSnappingPluginConfig = {
snap: SnapOptions;
persistence: SnappingManagerPersistenceConfig;
movement: SnappingManagerMovementConfig;
style: SnappingManagerStyle;
targetDistanceStyle: GuideDistanceToTargetInfoStyle;
getStaticGuides?: (params: {
instance: Weave;
containerId: string;
}) => Guide[];
};
type SnappingManagerKindStyle = {
default: {
stroke: string;
strokeWidth: number;
opacity: number;
dash?: number[];
};
selected: {
stroke: string;
strokeWidth: number;
opacity: number;
dash?: number[];
};
};
type GuideKindOnlyCustomOrStatic = Exclude<
GuideKind,
"centered-horizontal" | "centered-vertical" | "equal-distance"
>;
type SnappingManagerStyle = Record<
GuideKindOnlyCustomOrStatic,
SnappingManagerKindStyle
>;
type SnappingManagerPersistenceConfig = {
enabled: boolean;
};
type SnappingManagerMovementConfig = {
delta: number;
shiftDelta: number;
};
type WeaveGetStaticGuidesFunction = (params: {
instance: Weave;
containerId: string;
}) => Guide[];
type WeaveNodesSnappingCustomGuidesConfig = {
persistence: SnappingManagerPersistenceConfig;
movement: SnappingManagerMovementConfig;
style: SnappingManagerStyle;
targetDistanceStyle: GuideDistanceToTargetInfoStyle;
getStaticGuides?: WeaveGetStaticGuidesFunction;
};
type GuideDistanceToTargetInfoParams = {
config: GuideDistanceToTargetInfoConfig;
};
type GuideDistanceToTargetInfoConfig = {
style: GuideDistanceToTargetInfoStyle;
};
type GuideDistanceToTargetInfoStyle = {
target: {
stroke: string;
strokeWidth: number;
opacity: number;
dash?: number[];
};
distance: {
opacity: number;
line: {
stroke: string;
strokeWidth: number;
opacity: number;
dash?: number[];
};
text: {
fill: string;
fontSize: number;
fontFamily: string;
opacity: number;
};
background: {
fill: string;
cornerRadius: number;
stroke: string;
strokeWidth: number;
opacity: number;
};
};
};
type DistanceInfoH = {
from: { id: string; box: BoundingBox };
to: { id: string; box: BoundingBox };
midY: number;
distance: number;
};
type DistanceInfoV = {
from: { id: string; box: BoundingBox };
to: { id: string; box: BoundingBox };
midX: number;
distance: number;
};
type BoundingBoxWithId = {
id: string;
box: BoundingBox;
};
type HIntersection = {
combination: BoundingBoxWithId[];
targetIndex: number;
distances: DistanceInfoH[];
targetDistanceIndexes: number[];
};
type VIntersection = {
combination: BoundingBoxWithId[];
targetIndex: number;
distances: DistanceInfoV[];
targetDistanceIndexes: number[];
};Parameters
For WeaveNodesSnappingPluginParams:
| Prop | Type | Default |
|---|---|---|
config? | WeaveNodesSnappingPluginConfig | - |
For WeaveNodesSnappingPluginConfig:
| Prop | Type | Default |
|---|---|---|
getStaticGuides? | WeaveGetStaticGuidesFunction | undefined |
targetDistanceStyle? | GuideDistanceToTargetInfoStyle | DEFAULT_SNAPPING_MANAGER_CONFIG.targetDistanceStyle |
style? | SnappingManagerStyle | DEFAULT_SNAPPING_MANAGER_CONFIG.style |
movement.shiftDelta? | number | 10 |
movement.delta? | number | 0.5 |
persistence.enabled? | SnappingManagerPersistenceConfig | true |
snap.tolerance? | number | 5 |
