4.7 KiB
Migrating to @blinksgg/canvas v1.0
This guide covers breaking changes and migration steps from v0.x to v1.0.
Requirements
- React 19.0+ — React 18 is not supported (React Compiler, context-as-JSX)
- Jotai 2.6+ — Required for atom families and store API
- Node 18+ — For ESM support
Breaking Changes Summary
From v0.14+ (Gesture System v2)
onAction callback type changed: GestureEvent → InputEvent
The Canvas component's onAction prop now receives InputEvent (union of pointer + keyboard events) instead of GestureEvent (pointer only).
// Before (v0.x)
onAction={(event: GestureEvent, resolution) => {
const { subject, worldPosition } = event;
}}
// After (v1.0)
onAction={(event: InputEvent, resolution) => {
if (event.kind === 'key') return; // skip keyboard events
const { subject, worldPosition } = event; // narrowed to pointer
}}
Old gesture config replaced: The v1 gesture config shape (event-to-action mapping objects) was replaced by the v2 specificity-based binding system.
// Before (v0.x) — removed
<Canvas gestureConfig={{ onNodeDoubleTap: 'fit-to-view' }} />
// After (v1.0) — use binding contexts
import { gesturesV2 } from '@blinksgg/canvas';
// Bindings are configured via contexts, not flat config
From v0.23+ (Removed Exports)
These were deprecated in v0.16 and removed in v0.23:
| Removed | Replacement |
|---|---|
findPortAtPosition() |
Use hitTestPort() from @blinksgg/canvas/utils |
resolveGestureIntent() |
Use gesturesV2.resolve() |
isBackgroundGesture() |
Check event.subject.kind === 'background' |
useSupabaseClient() |
Use CanvasDbProvider with storage adapter |
From v0.6+ (React 19)
Context.Providersyntax →<Context>(React 19 style)- Manual
React.memo()/useCallback()/useMemo()removed — React Compiler handles memoization - Ref cleanup functions used instead of
useEffectreturn for pointer capture
Import Path Changes
All import paths are stable and unchanged since v0.14:
import { Canvas, useCanvasSelection, graphAtom } from '@blinksgg/canvas';
import { commandRegistry } from '@blinksgg/canvas/commands';
import { graphAtom, selectedNodeIdsAtom } from '@blinksgg/canvas/core';
import { useNodeDrag, useFitToBounds } from '@blinksgg/canvas/hooks';
import { gesturesV2 } from '@blinksgg/canvas'; // or from '@blinksgg/canvas/gestures'
import { CanvasStorageAdapter } from '@blinksgg/canvas/db';
Storage Adapter Migration
If you were using the old Supabase provider props:
// Before (v0.x) — Supabase-specific props
<Canvas supabaseUrl="..." supabaseKey="..." />
// After (v1.0) — adapter pattern
import { CanvasDbProvider, SupabaseStorageAdapter } from '@blinksgg/canvas/db';
const adapter = new SupabaseStorageAdapter(supabaseClient);
<CanvasDbProvider adapter={adapter}>
<Canvas ... />
</CanvasDbProvider>
Or use callback props for simple persistence (no adapter needed):
<Canvas
onNodePersist={async (nodeId, graphId, uiProps) => {
await saveToYourBackend(nodeId, graphId, uiProps);
}}
/>
Peer Dependencies
Required:
react^19.0.0react-dom^19.0.0jotai^2.6.0
Optional (install only if using the feature):
d3-force^3.0.0 — foruseForceLayouthook@tanstack/react-query^5.17.0 — for query-based atomsjotai-tanstack-query— foratomsWithQueryintegration@blocknote/core,@blocknote/react,@blocknote/shadcn^0.45.0 — forNoteNode
Gesture System v1 → v2 Mapping
| v1 Gesture Config | v2 Equivalent |
|---|---|
onNodeDoubleTap: 'fit-to-view' |
Default binding: double-tap node → fit-to-view |
onBackgroundDoubleTap: 'create-node' |
Default binding: double-tap background → create-node |
onNodeRightClick: 'context-menu' |
Default binding: right-click node → context-menu |
onNodeLongPress: 'toggle-lock' |
Default binding: long-press node → toggle-lock |
All default bindings are pre-configured. Override via gestureConfig prop or addGestureRuleAtom.
Quick Start (v1.0)
import { Provider as JotaiProvider } from 'jotai';
import { Canvas, CanvasStyleProvider, graphAtom } from '@blinksgg/canvas';
function App() {
return (
<JotaiProvider>
<CanvasStyleProvider isDark={true}>
<Canvas
renderNode={({ node, isSelected }) => (
<div>{node.label}</div>
)}
onAction={(event, resolution) => {
if (event.kind === 'key') return;
console.log(resolution.actionId, event.subject);
}}
/>
</CanvasStyleProvider>
</JotaiProvider>
);
}