NodeEditor API 仕様書

canvas-flow のコアクラス NodeEditor(フレームワーク非依存)のリファレンスです。Lit 版 <canvas-flow-editor>el.editor でこのインスタンスを公開しており、主要メソッドを委譲しています。

トップ ▶ 動くデモを開く GitHub npm

概要

NodeEditor<canvas> 要素を受け取り、Canvas 2D でノード・コネクタの描画と、ポインタ/ホイール/キーボード/タッチ操作、選択・編集コマンド、Undo/Redo、JSON 入出力を一括して引き受けます。データは内部の Grapheditor.graph)が保持し、描画・ヒットテストは空間インデックスにより表示範囲内の要素だけを処理するため、10,000 ノード規模でも軽量に動作します。

編集系メソッドと Undo
本書で Undo 可 と付いた操作は 1 回の undo() で戻ります。複数の操作を 1 回にまとめたいときは editor.graph.batch(() => { … }) で囲みます。
readOnly
options.readOnly が true のとき、編集系メソッドは何もせず null / false / 空配列を返します(クリックイベント等は発火します)。

導入

まず触ってみる
同じフォルダの demo.html は、jsDelivr の CDN から <canvas-flow-editor> を読み込むだけの 1 ファイルのデモです。ビルド不要でブラウザから開けます(CDN に届かない環境では同梱の ../src/index.js に自動でフォールバックします)。
import { NodeEditor } from '@hidemikimura/canvas-flow/core';   // または '@hidemikimura/canvas-flow'

const canvas   = document.querySelector('canvas.main');
const minimap  = document.querySelector('canvas.minimap');   // 省略可
const editor   = new NodeEditor(canvas, { minimapCanvas: minimap, theme: { lodZoom: 0.3 } });

editor.resize(800, 600);            // 表示サイズ(CSS px)。ResizeObserver 等で呼び直す
editor.resizeMinimap(200, 140);

editor.graph.addNode({ id: 'a', title: 'Source', x: 40,  y: 40,  output: true,
                       items: [{ id: 'v', label: 'value', value: '10', output: true }] });
editor.graph.addNode({ id: 'b', title: 'Sink',   x: 360, y: 120, input: true,
                       items: [{ id: 'in', label: 'in', input: true }] });
editor.graph.addEdge({ source: 'a', sourcePort: 'item:v:out', target: 'b', targetPort: 'item:in:in' });

editor.on('selection:change', ({ nodes, edges }) => console.log(nodes, edges));
editor.on('item:edit', ({ node, item, screenRect }) => { /* 入力欄を screenRect に重ねる */ });

インライン編集(ダブルクリック)はコアではイベント発火のみです。Lit 版は内蔵の入力欄を表示します。

用語と座標系

用語意味
ノード (Node)タイトル付きの矩形。項目(NodeItem)を縦に並べる。高さは項目数から自動計算される
項目 (NodeItem)ノード内の 1 行。ラベルと値を持ち、左右に入力/出力ポートを持てる
ポート (Port)接続の端子。ノードのヘッダ左右と各項目の左右にある。ポートキーで識別
コネクタ / エッジ (Edge)出力ポートから入力ポートへの接続線。常に「出力 → 入力」の向き
ワールド座標ノードの x, y が置かれている座標系。ズーム・パンの影響を受けない
スクリーン座標canvas 左上を原点とする CSS ピクセル。screen = world × zoom + (tx, ty)
クライアント座標マウスイベントの clientX / clientYclientToWorld() で変換

TypeScript

type同梱の型定義

手書きの型定義を同梱しているため、追加のインストールや設定なしで型が付きます。エントリごとに types/core.d.ts / types/lit.d.ts / types/index.d.ts が対応します。

import { NodeEditor, type Node, type EdgeType } from '@hidemikimura/canvas-flow/core';
import '@hidemikimura/canvas-flow/lit';
内容
NodeEditorEventsイベント名 → detail の対応。editor.on() は名前から detail の型が決まり、未知の名前はエラーになる
HitTestResulthitTest() の戻り値。type で判別できるユニオン
EdgeGeometryBezierEdgeGeometry(制御点あり)と PolylineEdgeGeometry のユニオン。g.type === 'bezier' で絞り込む
Node / NodeItem / Edge / PortSpec / PortKeyデータモデル。NodeInput / EdgeInput は id を省略できる追加用
Theme / ThemePatchThemePatch は深い部分指定。キー名の打ち間違いはエラーになる
CanvasFlowEditorEventMapLit 版の CustomEvent 名 → detail。HTMLElementTagNameMap も拡張済みで querySelector('canvas-flow-editor') に型が付く
/core の型は lit に依存しないので、コアだけ使う場合は lit を入れずに型チェックが通ります。

コンストラクタ / オプション

constructornew NodeEditor(canvas, options?)

オプション既定説明
themeobjectテーマの部分上書き(深いマージ)
graphGraph新規作成既存の Graph を使う
nodeTypes{ [type]: { style } }{}ノード種別ごとの既定スタイル(node.type で参照)
minimapCanvasHTMLCanvasElement指定するとミニマップを描画・操作する
wheelMode'zoom' | 'pan''zoom'ホイールの既定動作。zoom: 通常ズーム / Shift でパン。pan: 通常パン / Ctrl・Cmd でズーム
dragMode'pan' | 'select''pan'空白を左ドラッグしたときの動作(Shift で反転)
readOnlybooleanfalse編集操作を無効化
historyLimitnumber200Undo 履歴の上限
minNodeWidthnumber100リサイズ時の最小幅
rules{ maxInputs?, maxOutputs?, onFull? }無制限 / 'reject'接続ルール。ポート側で上限を指定しないときの既定上限と、満杯ポートへ接続したときの挙動('reject' 拒否 / 'replace' 最古のコネクタを外して付け替え)
edgeDeleteIconbooleantrueホバー/選択中のコネクタ中央に削除アイコンを表示
keyboardScopeElementcanvasこの要素内を最後にポインタ操作していればキーボードショートカットを受け付ける(Lit 版はホスト要素)
wheelGestureGapnumber (ms)250この間隔以内のホイールイベントは同じスクロール操作とみなし、ズーム/パンの判定を維持する(慣性スクロール対策)
moveSnapnumber (px)0(無効)ノードの移動単位。ドラッグ・矢印キー・ノード追加・JSON 追加の位置がこの単位に揃う
focusMode'off' | 'connected' | 'neighbors''off'選択ノードと同じ流れにある要素を強調し、他を薄く描く
focusDepthnumber1'neighbors' のときに何段まで辿るか
focusDirection'lineage' | 'downstream' | 'upstream' | 'both''lineage'辿る向き。既定は起点の祖先と子孫だけ(起点を通る道筋)
theme.edge.type'bezier' | 'straight' | 'step''bezier'コネクタの描画方法の既定(theme オプション経由。後から setEdgeType() で変更可)

オプションは editor.options に保持され、wheelMode / dragMode / readOnly / edgeDeleteIcon は実行中に書き換えても次の操作から反映されます。

プロパティ

プロパティ説明
graphGraphノード・コネクタのモデル。低レベルな操作はここから行う
viewportViewporttx, ty, zoom, width, heighttoWorld() / toScreen() / visibleRect() / snapshot() / restore()
rendererRenderer描画器。renderer.stats に直近フレームの { nodes, edges, ms }
historyHistoryUndo 履歴。begin(label) / end() でトランザクションを明示できる
minimapMinimap | nullminimapCanvas を渡したときのみ
interactionInteraction入力処理。dragThreshold(既定 3px)、pointerInside
selection{ nodes: Set<string>, edges: Set<string> }選択中の ID。直接変更せず 選択メソッドを使う
selectedNodesNode[](getter)選択中ノードの配列
hover{ node, edge, port, edgeDelete }ホバー中の要素 ID(読み取り用)
themeobject現在のテーマ(マージ済み)。変更は setTheme()
nodeTypesobjectノード種別の定義
optionsobjectコンストラクタのオプション(既定値でマージ済み)
canvasHTMLCanvasElement描画先
canUndo / canRedoboolean(getter)履歴の有無
nodeRenderer / edgeRenderer / overlayRendererfunction(setter)独自描画関数・追加描画。見た目を参照

見た目

methodsetTheme(patch)

テーマを部分的に上書きします。node.headerHeight / itemHeight / padding / width / edge.curvature / edge.type / edge.stepOffset はレイアウトにも反映され、空間インデックスが再構築されます。

editor.setTheme({ background: '#0f172a', node: { fill: '#1e293b', titleColor: '#f1f5f9' }, lodZoom: 0.25 });

methodsetRules(rules)

接続ルール { maxInputs, maxOutputs, onFull } を変更します(editor.graph.rules に反映)。

methodsetEdgeType(type, edgeIds?) / getter/setteredgeType / methodsetSelectedEdgeType(type)

コネクタの描画方法を変えます。type'bezier'(三次ベジェ曲線・既定)、'straight'(直線)、'step'(90 度にだけ曲がる階段状の折れ線)。edgeIds を省略すると全体の既定(theme.edge.type / graph.layout.edgeType)を変え、edge.type を持たないすべてのコネクタが切り替わります。edgeIds を渡すとそのコネクタの edge.type を更新します(Undo 可、JSON にも保存)。setSelectedEdgeType は選択中のコネクタだけ(選択が無ければ全体の既定)。いずれも 'edge-type:change' を発火します。

type
'bezier'制御点を横に max(40, |dx| × theme.edge.curvature) 出した三次ベジェ曲線
'straight'2 点を結ぶ直線
'step'前方(右側)の相手へは横中央で 1 回だけ縦に折れる。後方の相手へは theme.edge.stepOffset(既定 24px)だけ右へ突き出し → 上下の中間の高さで左へ戻り → 相手の左から入る
editor.setEdgeType('step');                 // 全体
editor.setEdgeType('straight', ['e1']);     // e1 だけ
editor.graph.addEdge({ source: 'a', sourcePort: 'out', target: 'b', targetPort: 'in', type: 'step' });

ドラッグ中の仮コネクタ・ヒットテスト・削除アイコンの位置(形状の中点)・簡易表示(LOD)も同じ形に従います。'line''straight''orthogonal' / 'smoothstep''step' の別名は normalizeEdgeType() が丸めます。

methodsetFocusMode(mode, options?) / getter/setterfocusMode

選択ノードと同じ流れにある要素だけをはっきり描き、それ以外を薄くします。選択が空のときは自動的に解除されます。 対象が変わると 'focus:change'{ mode, direction, nodes, edges })が発火します。

mode強調する範囲
'off'(既定)無効。全部を通常どおり描く
'connected'進める先をすべて辿る
'neighbors'focusDepth 段まで(既定 1 = 次の 1 段だけ)
direction辿る向き
'lineage'(既定)起点の祖先(上流)と子孫(下流)だけ=起点を通る道筋
'downstream'出力ポートから進める先だけ
'upstream'入力ポートへ入ってくる側だけ
'both'向きを問わず繋がっているもの全部
'lineage' の考え方
「その要素を通る道筋」だけを強調します。起点まで遡れるノード(祖先)と起点から進めるノード(子孫)が対象で、祖先から分かれた別の枝や、子孫へ合流してくる別の枝は入りません
A → B → C / A → E / D → E / E → F というグラフなら、B を選ぶと A・B・C(E・F・D は薄い)、E を選ぶと A・D・E・F(B・C は薄い)、A を選ぶと A・B・C・E・F(D は薄い)。
goto でも同じで、A → B → C に加えて A が goto で D を指していても、C を選んだときに D は強調されません。前後の枝まで見たいときは 'both' を使ってください。
editor.setFocusMode('connected');                              // 同じ流れ全部(既定)
editor.setFocusMode('neighbors', { depth: 2 });                // 2 段まで
editor.setFocusMode('connected', { direction: 'downstream' }); // 進める先だけ
editor.focusMode = 'off';

methodfocusSet() → { nodes: Set, edges: Set } | null

現在の強調対象。無効なとき・選択が空のときは null(= 全部を通常描画)。選択とグラフの変更でキャッシュが破棄されます。

methodselectFocused(options?) → { nodes, edges } | null

いま強調表示されている要素focusSet() の中身)をそのまま選択します。選んだ流れをまとめて移動・複製・削除したいときに使います。ツールバーの「強調を選択」ボタンと Ctrl/Cmd + Shift + A からも呼べます。

オプション意味
additivetrue でいまの選択に加える(既定 false = 置き換え)
depth / direction強調表示の設定を使わず、この条件で辿り直して選択する
editor.selectFocused();                            // 強調されている要素で選択を置き換える
editor.selectFocused({ additive: true });          // いまの選択に加える
editor.selectFocused({ direction: 'downstream' }); // 向きを指定して辿り直す

強調表示が無効(focusMode: 'off')のときは focusDirection / focusDepth の設定どおりに繋がりを辿って選択します。選択ノードが無ければ何もせず null。実行すると 'focus:select'{ mode, nodes, edges })が発火します。選択操作なので readOnly でも使えます。

選択したあとは、その範囲がそのまま強調対象として固定されます(次に選択・グラフを変えるまで)。固定しないと「選択が増えた分だけ強調範囲も広がる」ため、押すたびに範囲が育ってしまいます。

methodUndo 可selectConnected(options?) → { nodes, edges } | null

繋がっている要素を選択に加えます(まとめて移動・削除したいとき)。探索だけしたい場合は graph.connectedTo(startIds, { depth, direction, includeStart }) を直接呼べます。

薄さと強調の見た目は theme.focusdimOpacity 既定 0.12 / dimOpacityLod 既定 0.25 / edgeStroke / edgeWidth)で変えられます。簡易表示(LOD)中は線が細く消えやすいため別の値を持たせています。

methodregisterNodeType(type, { style })

ノード種別を登録します。スタイルの優先順位は node.style > nodeTypes[node.type].style > theme.node です。

setternodeRenderer = (ctx, node, rect, api) => boolean | void
setteredgeRenderer = (ctx, edge, geometry, api) => boolean | void

独自の描画関数を設定します(null で既定に戻す)。true を返すと既定描画を省略、それ以外は既定描画にフォールバックします。api には selected, hover, style, lod, zoom, theme, graph, roundRect(), fitText() が入ります。geometry{ type, x1, y1, x2, y2, points }points は折れ線の頂点列。type === 'bezier' のときは制御点 c1x, c1y, c2x, c2y も入る)です。

独自描画関数が設定されている間は、縮小時の一括簡易描画(LOD)は使われず、各要素に対して関数が呼ばれます(api.lod で判断できます)。

setter/getteroverlayRenderer = (ctx, api) => void

ノードとコネクタを描いたあとに 1 回だけ呼ばれる追加描画です(null で解除)。ノードの上に重ねたいもの(分析用のバーやバッジ、注釈)はここで描きます。nodeRenderer と違い既定描画を置き換えないので、縮小時の一括簡易描画もそのまま使われます。

api{ visible, lod, zoom, nodes, edges, theme, graph, roundRect(), fitText() }nodes / edges は表示範囲内の要素(子ノードも含む)です。ワールド座標系のまま呼ばれるので、画面上で一定の大きさにしたいものは zoom で割ります。

editor.overlayRenderer = (ctx, api) => {
  if (api.lod) return;
  for (const node of api.nodes) {
    const r = api.graph.nodeRect(node);
    ctx.fillStyle = '#3b82f6';
    api.roundRect(r.x + 8, r.y + r.h - 6, (r.w - 16) * 0.42, 4, 2);
    ctx.fill();
  }
};
計測値をノードに重ねる例として、シナリオ分析ビューの試作が demo/analytics.html にあります(項目行の選択率バー・ヘッダの離脱率バー・ヒートマップ・コネクタの太さ・ホバー詳細)。

サイズ・描画

methodresize(width, height, dpr?) / resizeMinimap(width, height, dpr?)

表示サイズ(CSS px)を設定します。dpr 省略時は devicePixelRatio。コンテナのサイズが変わるたびに呼んでください。

methodrequestRender() / render()

requestRender() は次の requestAnimationFrame で 1 回だけ描画を予約します(複数回呼んでも 1 フレームにまとまる)。render() は即時描画し、'render' イベントで統計を通知します。モデルやビューの変更は内部で自動的に requestRender() されるため、通常は呼ぶ必要はありません。

座標・ヒットテスト

methodclientToWorld(clientX, clientY) → {x, y}

マウスイベントのクライアント座標をワールド座標に変換します。

methodworldRectToScreen(rect) → {x, y, w, h}

ワールド矩形を canvas 基準のスクリーン矩形に変換します(インライン編集 UI の配置などに)。

methodviewCenter() → {x, y}

表示中の画面中央のワールド座標。

methodgetPointer() → { world, screen, inside } | null

最後に観測したポインタ位置。inside はポインタが canvas 上にあるか。一度も観測していなければ null

methodhitTest(wx, wy) → Hit

ワールド座標にある要素を返します。判定の優先順位は 削除アイコン > リサイズグリップ > ポート > ノード(ヘッダ/項目/本体)> コネクタ です。

type追加フィールド意味
'edge-delete'edge表示中のコネクタ削除アイコン
'resize'nodeノード右下のリサイズグリップ
'port'node, port: { key, dir, itemId, x, y, visible }表示中のポート
'item'node, itemノード内の項目行
'node'node, header: booleanノードのヘッダまたは本体
'edge'edgeコネクタ(線から約 6px 以内)
'none'空白

methodedgeAt(wx, wy) → Edge | null

点に最も近いコネクタ(しきい値内)。

methodedgeDeleteIconAt(wx, wy) → Edge | null / deleteIconEdges() → Edge[]

削除アイコンの当たり判定と、現在アイコンを表示しているコネクタ一覧(ホバー中 + 選択中。readOnly / LOD 時は空)。

選択

methodselect({ nodes?, edges? }, { additive? })

ID 配列を選択します。additive: true で既存の選択に追加。'selection:change' を発火します。

methodtoggleSelect({ nodes?, edges? })

指定 ID の選択状態を反転します。

methodclearSelection() / selectAll()

methodselectInRect(rect, { additive? })

ワールド矩形に完全に含まれるノードと、その間のコネクタを選択します(範囲選択と同じ挙動)。

editor.select({ nodes: ['a', 'b'] });
editor.select({ edges: ['e1'] }, { additive: true });
console.log(editor.selectedNodes.map(n => n.title));

編集コマンド

methodUndo 可deleteSelection()

選択中のノードとコネクタを削除します。ノードに繋がるコネクタも一緒に削除され、1 回の Undo で戻ります。

methodUndo 可deleteSelectedEdges({ scope = 'between' }) → string[]

選択範囲内のコネクタだけを削除し、ノードは残します。戻り値は削除したコネクタ ID。'edges:delete' を発火します。Shift + Delete、Lit 版ツールバーの「コネクタ削除」に対応。

scope対象
'between'(既定)選択中のコネクタ + 両端とも選択中ノードに繋がるコネクタ(範囲選択の内側)
'attached'選択中のコネクタ + 選択中ノードに繋がるすべてのコネクタ(範囲外へ出るものも含む)
'selected'選択中のコネクタのみ

methodselectedEdgeIds({ scope = 'between' }) → string[]

deleteSelectedEdges と同じ規則で対象になるコネクタ ID を返します(削除はしない)。

methodUndo 可removeNode(id) / removeEdge(id)

methodUndo 可duplicateSelection(offset = {x: 40, y: 40}) → { nodes, edges, idMap } | null

選択中のノードと、その内部で閉じているコネクタを複製し、複製側を選択状態にします。

methodUndo 可duplicateNode(id, offset?) → Node | null

methodcopySelection() → boolean

選択内容を内部クリップボードに保存します。キーボードの Ctrl/Cmd+C はこれに加えてシステムのクリップボードへ JSON を書き込みます。

methodUndo 可paste(at?) → { nodes, edges } | null

内部クリップボードの内容を貼り付けます。at(ワールド座標)を渡すと貼り付け内容の左上をそこに置き、省略時は元の位置から 40px ずらします。

methodUndo 可moveSelection(dx, dy)

選択中ノードをワールド座標で相対移動します(移動単位は適用しない)。

methodUndo 可nudgeSelection(stepsX, stepsY, { pixels = 1 })

選択中ノードを移動単位で動かします(矢印キーの実装)。moveSnap 未設定なら pixels × steps。設定時は先頭の選択ノードが目盛りに乗っていなければ進む方向の次の目盛りが 1 歩目になり、動かす軸だけ揃えます。

methodUndo 可resizeNode(id, width) → Node | null

ノードの幅を変更します(minNodeWidth 未満は切り上げ)。高さは項目数で決まるため変更できません。

methodUndo 可setPortVisible(nodeId, portKey, visible) → boolean

1 つのポートの表示/非表示。ポートが存在しなければ false

methodUndo 可setPortsVisible(nodeId, visible, itemId?) → boolean

ノード(itemId 指定時はその項目)の全ポートの表示/非表示(showPorts を設定)。

非表示のポートは描画されずマウス操作の対象にもなりませんが、存在はしているため既存のコネクタは保たれ、addEdge() / connect() / JSON 読み込みからの接続は有効です。

methodsetMoveSnap(step) / gettermoveSnap

ノードの移動単位(ワールド px)を設定・取得します。0 で無効。設定するとドラッグ中はクリックしたノードの左上が単位に揃い、他の選択ノードは相対位置を保って追従します。addNodeAt / insertJSONsnap 既定値も true になります。

methodsnapValue(v) → number / snapPoint({x, y}) → {x, y}

値・座標を移動単位に丸めます(無効時はそのまま)。

methodUndo 可snapNodes(ids?) → string[]

ノードの左上を移動単位に揃えます。ids 省略時は選択中ノード、選択が無ければ全ノード。戻り値は動かしたノード ID。

ノード追加

methodUndo 可addNodeAt(spec, options?) → Node | null

位置を指定してノードを追加し、選択状態にします。specNode 定義(x, y は計算で上書き)。

オプション既定説明
at: {x, y}ワールド座標
client: {clientX, clientY}クライアント座標(マウスイベントをそのまま渡せる)
どちらも省略時はポインタ位置。ポインタが canvas 外か未観測なら画面中央
anchor'center'位置に合わせる基準点。'center' / 'top-left' / 'header'(ヘッダ中央)
selecttrue追加後に選択する
snapmoveSnap > 0追加位置を moveSnap(未設定なら theme.grid.size)に吸着
avoidOverlapfalse既存ノードと重なる場合は右下へ moveSnap(未設定なら 24px)ずつずらす(最大 50 回)

methodUndo 可addNodeAtPointer(spec, options?) / addNodeAtCenter(spec, options?)

それぞれ「ポインタ位置(canvas 外なら画面中央)」「画面中央」に追加する省略形です。

document.addEventListener('keydown', (e) => {
  if (e.key === 'n') editor.addNodeAtPointer({ title: '新規', input: true, output: true, items: [] }, { avoidOverlap: true });
});
editor.on('canvas:dblclick', ({ x, y }) => editor.addNodeAt({ title: '新規' }, { at: { x, y }, anchor: 'header' }));

位置をそのまま使いたい場合は editor.graph.addNode(node) を使います。

子ノード

ノードに childs を持たせると、その中に子ノードが入ります。子ノードは親の項目の下に上から順に縦に積まれ、位置(x / y)と幅は親が自動計算します。入れ子は何段でも可能です。

editor.graph.addNode({
  id: 'n0', title: '親ノード', x: 100, y: 50, input: true, output: true,
  items: [{ id: 'p0', label: 'value', input: true, output: true }],
  childs: [
    {
      id: 'n1', title: '子ノード', input: true, output: true,
      items: [{ id: 'p1', label: 'value', input: true, output: true }],
      childs: [{ id: 'n2', title: '孫ノード', input: true, output: true }],
    },
  ],
});
項目ふるまい
配置親の項目の下に縦に並ぶ。左右は theme.node.childIndent(既定 10px)だけ内側、間隔は theme.node.childGap(既定 6px)。子の x / y / width の指定は無視される
親の高さheaderHeight + 項目 + 子ブロック。孫まで含めて自動計算される
移動子だけを動かすことはできない。子をドラッグすると一番外側の親ごと動く(moveNodes も同じ)
ポート・接続子もヘッダポートと項目ポートを持ち、外のノードとも自由に接続できる。端子の丸は一番外側の親の左右の縁(portEdgeX)に並ぶ
選択・ヒットテスト深い子ノードが優先して当たる。範囲選択(selectInRect)は親だけを選ぶ
削除・複製親を削除すると子孫もまとめて消える。複製(duplicateNodes)は部分木ごとコピーされ、二重にはコピーされない
幅リサイズ子ノードは親に追従するためリサイズできない(グリップも出ない)
JSONchilds として入れ子で入出力される

methodUndo 可addChild(parentId, child, index?) → Node | null

子ノードを追加します。index を渡すとその位置に挿入します(省略時は末尾)。

methodUndo 可removeChild(id, { x?, y? }) → Node | null

子を親から外して独立したノードに戻します。x / y を渡すとその位置に置きます。子ノードでなければ null。Lit 版では DOM の予約名を避けて detachChild(id, position?) という名前です。

methodUndo 可setParent(id, parentId | null, index?) → boolean

親を付け替えます。parentIdnull を渡すと独立させます。自分の子孫を親にするような循環は false を返して拒否します。

methodchildrenOf(node) → Node[] / rootNodeOf(node) → Node | null

子ノードの配列(表示順)と、一番外側の親(自分が子でなければ自分自身)。引数はノードでも ID でも渡せます。さらに細かい問い合わせは Graph 側の isChild / parentOf / rootOf / depthOf / descendantIds / rootNodes を使います。

goto(ID 指定の遷移)

「この選択肢を選んだらこのノードへ」という遷移を、コネクタを引かずにノード ID で指定できます。ノードにも項目にも goto を書けて、コネクタと混ぜて使えます。

graph.addNode({
  id: 'menu', title: 'メニュー', x: 300, y: 0, input: true,
  items: [
    { id: 'm1', label: '料金を知りたい', goto: 'price' },                          // ID だけ
    { id: 'm2', label: '使い方を知りたい', goto: { to: 'howto', label: '使い方' } },  // ラベル付き
    { id: 'm3', label: 'その他', goto: ['faq', 'agent'] },                         // 複数
  ],
  goto: 'survey',   // ノード本体に書くと「この画面のあと進む先」
});
扱い内容
強調表示connectedTo() がコネクタと同じ向きのつながりとして辿るので、focusMode でも goto 先が強調される({ links: false } で除外)
自動整列autoLayout() も層の計算に使う({ links: false } で除外)
描画普段は描かず、そのノードを選択したとき・強調表示で辿られたときだけ点線の矢印で表示(theme.goto
JSONそのまま保存・復元。merge / insertJSON で ID が付け替わるときは遷移先も付け替わる
存在しない IDエラーにならず exists: false として辿られない

typeGotoSpec

'n1' / ['n1','n2'] / { to: 'n1', label?: string } / その配列。normalizeGoto(value)[{to, label?}] に正規化できます。

methodUndo 可setGoto(nodeOrId, value, { itemId })

設定・解除(null で解除)。itemId を渡すとその項目に設定します。内部では updateNode / updateItem なので 1 回の Undo で戻ります。

methodgotoLinks(nodeOrId?) → GotoLink[] / gotoSources(nodeOrId) → GotoLink[] / gotoTargets(nodeOrId) → Node[]

gotoLinks は出ていく遷移(引数を省略するとグラフ全体)、gotoSources は「このノードを指しているリンク」、gotoTargets は存在する遷移先ノードです。GotoLink{ key, from, itemId, to, label, exists }keygoto:<from>:<itemId|->:<index> という安定した識別子で、強調表示の links に入るのもこのキー)。

editor.setGoto('menu', 'price', { itemId: 'm1' });
editor.gotoLinks('menu');     // → [{ key, from, itemId: 'm1', to: 'price', label: null, exists: true }, ...]
editor.gotoSources('price');  // → price へ入ってくるリンク
editor.on('focus:change', ({ links }) => console.log('辿った goto', links));
点線の始点・終点は graph.gotoAnchor(link) で取れます(始点は項目の行、終点は遷移先ノードのヘッダ)。独自に描きたいときは overlayRenderer と組み合わせてください。

右クリックメニュー

右クリックすると、対象(ノード/項目/コネクタ/空白)に応じたメニューが出ます。ブラウザ既定のメニューは常に抑制されます。未選択のものを右クリックした場合はそれを選択してからメニューを出し、選択中のノードを右クリックしたときは複数選択を保ちます(まとめて複製・削除できる)。readOnly のときと context-menu="false" のときはメニューを出しません(イベントだけ発火)。

event'context:menu'(Lit: context-menu

コアが出すのはこのイベントだけで、メニューの描画は Lit 版が担当します。detail{ type, node, item, edge, port, x, y, screen, client, selection, originalEvent }typehitTest と同じ(none / node / item / port / resize / edge / edge-delete)、x / y はワールド座標、screen は canvas 基準、client はビューポート基準です。

// 自前のメニューを出す(内蔵メニューは抑止)
el.addEventListener('context-menu', (e) => {
  e.preventDefault();
  myMenu.open(e.detail.client.x, e.detail.client.y, e.detail);
});

propertycontextMenuItems

内蔵メニューの項目。配列を渡すと常に同じメニュー、関数を渡すと対象ごとに組み立てられます。ctx はイベントの detailel / editor / graph / defaultItems を足したものです。

el.contextMenuItems = (ctx) => [
  ...ctx.defaultItems.filter((i) => i.id !== 'export'),
  { type: 'separator' },
  { id: 'log', label: 'ID をログ出力', shortcut: 'Ctrl+L', run: (c) => console.log(c.node?.id) },
];
項目のフィールド意味
id項目を見分ける ID(context-menu-select の detail に入る)
label表示名
shortcut右端に薄く出す表記(動作は割り当てない)
disabled / hidden無効表示 / 一覧から除く
dangertrue で赤字(削除など)
run(ctx)実行する処理(非同期でもよい)
{ type: 'separator' }区切り線。先頭・末尾・連続したものは自動で削られる

既定の項目は defaultContextMenuItems(ctx) として公開しています(@hidemikimura/canvas-flow / /lit から import)。対象ごとの内容は次のとおりです。

対象項目
ノード・項目複製 / 削除 / つながりを外す / 子ノードを追加 / 強調されている要素を選択 / つながっている要素を選択 / コピー / このノードを中心に
子ノードこの子ノードを複製 / この子ノードを削除 / 子ノードを親から出す(ほかはノードと同じ)
コネクタこのコネクタを削除 / 曲線・直線・直角にする / 両端のノードを選択
空白ここにノードを追加 / 貼り付け / すべて選択 / 選択を解除 / 整列 / 全体表示 / 縮尺をリセット / JSON を書き出し

methodcloseContextMenu() / openContextMenuAt(x, y, detail?)

閉じる・任意の位置(要素内の px)に出す。ツールバーの「…」ボタンから同じメニューを出したいときなどに使います。

キーボードは ↑ ↓ で移動、Enter で実行、Escape で閉じる。外 のクリック・ズーム・パンでも閉じます。見た目は part="context-menu" で上書きできます。

自動整列

methodUndo 可autoLayout(options?) → string[]

階層レイアウト(Sugiyama 方式の簡略版)で並べ直します。コネクタは左から右へ流れ、層をまたぐコネクタは途中の層で場所を確保するため他のノードの上を横切りません。2 つ以上のノードを選択していればその範囲だけ、なければ全ノードが対象です。戻り値は移動対象になったノード ID。

オプション既定説明
nodeIds選択 or 全体対象を明示
fit / animatetrue / true整列後に表示を合わせる(全体なら全体表示、範囲なら中央へ)
layerGap120層と層の横の間隔
nodeGap40同じ層のノード間の縦の間隔
componentGap80連結成分の間隔
dummyHeight24通過するコネクタ 1 本が確保する高さ
iterations8順序付け・座標調整の反復回数
origin: {x, y}対象の元の左上整列後の左上位置

位置だけを計算したい場合は import { layeredLayout } from '@hidemikimura/canvas-flow'layeredLayout(graph, nodeIds, options) → Map<id, {x, y}> を使います。

Undo / Redo

methodundo() → boolean / redo() → boolean

履歴を 1 段戻す/進めます。存在しなくなった要素は選択から外れます。canUndo / canRedo で可否を確認できます。

履歴の単位: graph.batch(fn) の中、または history.begin()history.end() の間の操作は 1 項目になります。同じノード群の連続移動、同じノードの連続更新は併合されます。load() / importData({ mode: 'replace' }) で履歴は破棄されます。
editor.graph.batch(() => {
  editor.graph.addNode({ id: 'x', title: 'X' });
  editor.graph.addNode({ id: 'y', title: 'Y', x: 300 });
  editor.graph.addEdge({ source: 'x', sourcePort: 'out', target: 'y', targetPort: 'in' });
});
editor.undo();   // 3 つまとめて消える

ビュー操作

methodzoomIn(factor = 1.2) / zoomOut(factor = 1.2) / setZoom(zoom)

画面中央を基準にズームします。範囲は viewport.minZoom(0.05)〜 maxZoom(4)。

methodresetZoom() / resetView()

resetZoom() は縮尺を 1 に戻し(中心は維持)、resetView() は縮尺 1 で原点を左上に戻します。

methodfitView(padding = 40, { animate = false })

全ノードが収まるようにズーム・位置を合わせます。

methodcenterOnNode(id, { zoom?, animate = true, duration = 300 }) → boolean

ノードが画面中央に来るようにスクロールします(既定でアニメーション)。

methodcenterOn(wx, wy, { zoom?, animate = false, duration = 300 })

ビューの変更後は 'viewport:change' が発火します。直接 editor.viewport を操作した場合は requestRender() を呼んでください。

methodsearch(query) → Node[]

タイトル・ID・項目ラベル・項目値を部分一致(大文字小文字無視)で検索します。query は文字列、RegExp、または (node) => boolean

methodsearchAndFocus(query, index = 0) → Node[]

検索して index 番目の一致(循環)を選択し、中央へアニメーションで移動します。

JSON 入出力

methodexportData({ selectionOnly = false, includeViewport = true }) → object

JSON フォーマットのオブジェクトを返します。selectionOnly で選択中のノードとその間のコネクタだけを出力(このとき viewport は含まれない)。'export' イベントを発火します。

methodexportJSON({ pretty = true, ...exportDataOptions }) → string

methoddownloadJSON(filename = 'canvas-flow.json', options?)

ブラウザでファイルとしてダウンロードさせます。

methodtoJSON() → object

JSON.stringify(editor) で使われます。exportData() と同じ形式(イベントは発火しない)。

methodimportData(input, options?) → { ok: true, mode, nodes, edges, warnings } | { ok: false, errors }

JSON 文字列またはオブジェクトを検証して読み込みます。importJSON() は別名です。

オプション既定説明
mode'replace''replace': 内容を置き換え(履歴は破棄) / 'merge': 既存に追加。衝突する ID は付け替え、Undo 可
at: {x, y}merge 時の配置位置。基準は anchor で選ぶ
anchor'top-left''origin': JSON の座標を at からの相対位置として扱う / 'top-left': ノード群の左上を at に / 'center': 中心を at に
offset: {x, y}merge 時に加える位置オフセット
selecttruemerge 後に読み込んだ要素を選択
restoreViewporttruereplace 時に viewport を復元
fitViewfalse読み込み後に全体表示

検証で構文エラー・構造エラー・未対応 format/version は errors、存在しないノード/ポートを指すコネクタは除外して warnings に記録します。成功時は 'import' イベントを発火します。

methodUndo 可insertJSON(input, options?) → result

複数のノード・コネクタを含む JSON を指定位置に追加します(既存の内容は残す)。JSON 内のノード座標は追加位置からの相対位置として扱われ(既定 anchor: 'origin')、(0, 0) のノードが追加位置にそのまま置かれます。衝突する ID は付け替え、コネクタの参照も追従します。戻り値は importData と同じで、成功時に 'insert' イベントを発火します。

オプション既定説明
at: {x, y} / client: {clientX, clientY}ポインタ位置追加位置。省略時はポインタ位置(canvas 外・未観測なら画面中央)
anchor'origin''origin' / 'top-left' / 'center'
selecttrue追加した要素を選択
snapmoveSnap > 0追加位置を moveSnap(未設定なら theme.grid.size)に吸着
const template = { nodes: [{ id: 'a', title: 'A', x: 0, y: 0, output: true }, { id: 'b', title: 'B', x: 300, y: 80, input: true }],
                   edges: [{ source: 'a', sourcePort: 'out', target: 'b', targetPort: 'in' }] };
editor.insertJSON(template, { at: { x: 500, y: 200 } });   // A → (500,200), B → (800,280)

methodUndo 可insertJSONAtPointer(input, options?)

insertJSON をポインタ位置で実行します(canvas 外なら画面中央)。

methodimportFromFile(file | string, options?) → Promise

methodopenImportDialog(options?) → Promise<result | null>

ファイル選択ダイアログを開いて読み込みます。キャンセル時は null

methodload(data)

検証なしで内容を置き換えます({ nodes, edges, viewport? })。信頼できるデータ向け。

ライフサイクル

methodon(type, handler) → unsubscribe / off(type, handler)

イベント購読。戻り値の関数で解除できます。

methoddestroy()

イベントリスナ(canvas / document)とアニメーションを解除します。canvas を DOM から外す前に呼んでください。

イベント一覧

コアは editor.on(type, handler)、Lit 版は CustomEvent(名前は kebab-case、内容は event.detail)です。

event状態・モデルの変化

コアLitpayloadタイミング
selection:changeselection-change{ nodes: string[], edges: string[] }選択が変わった
viewport:changeviewport-change{ tx, ty, zoom }パン/ズームの操作が終わった(ホイールは 120ms 間引き)
graph:changegraph-changeモデルが変わった(batch 内はまとめて 1 回)
node:add / node:remove / node:changenode-add / node-remove / node-changeNodeノードの追加/削除/プロパティ変更
nodes:movenodes-move{ ids, dx, dy }移動のたび(ドラッグ中も)
nodes:move:endnodes-move-end{ ids, dx, dy, start }ドラッグ移動の終了
node:resize:endnode-resize-end{ id, from, to }リサイズの終了
edge:add / edge:remove / edge:changeedge-add / …Edgeコネクタの追加/削除/変更
history:changehistory-change{ canUndo, canRedo }履歴の状態が変わった
import / exportimport / export{ mode, nodes, edges, warnings } / { data, selectionOnly }JSON 入出力
layoutlayout{ nodes: string[] }自動整列を適用した
edge-type:changeedge-type-change{ type, edges: string[] | null }コネクタの描画方法を変えた(edges が null なら全体の既定)
focus:changefocus-change{ mode, direction, nodes: string[], edges: string[], links: string[] }強調表示の対象が変わった(links は辿った goto のキー)
focus:selectfocus-select{ mode, nodes: string[], edges: string[] }強調表示されている要素を選択した(selectFocused
context:menucontext-menu{ type, node, item, edge, port, x, y, screen, client, selection, originalEvent }右クリック。Lit 側の context-menupreventDefault() で内蔵メニューを抑止できる
context-menu-select{ id, item, target }右クリックメニューの項目を実行した(Lit のみ)
edges:deleteedges-delete{ ids: string[], scope }選択範囲内のコネクタだけを削除した
insertinsert{ at, anchor, nodes, edges }insertJSON で JSON を指定位置に追加した
renderrender{ nodes, edges, ms }描画後の統計

eventユーザー操作

コアLitpayloadタイミング
node:clicknode-click{ node, item, header, port, …共通 }ノードのどこかをクリック(ドラッグせずに離した)
item:clickitem-click{ node, item, …共通 }項目をクリック(続けて node:click も発火)
edge:clickedge-click{ edge, …共通 }コネクタをクリック
canvas:clickcanvas-click{ …共通 }空白をクリック
node:editnode-edit{ node, rect, screenRect }ヘッダをダブルクリック(Lit 版は preventDefault() で内蔵編集を抑止)
item:edititem-edit{ node, item, rect, screenRect }項目をダブルクリック
canvas:dblclickcanvas-dblclick{ x, y }空白をダブルクリック
edge:dblclickedge-dblclick{ edge, at }コネクタをダブルクリック
connect:cancelconnect-cancel{ from: { node, port }, at }コネクタのドラッグを空白で離した
connect:rejectedconnect-rejected{ node, port, reason }満杯のポートからコネクタを引こうとした
edge:delete-iconedge-delete-icon{ edge }削除アイコンのクリックでコネクタを削除した

クリック系の共通 payload: x, y(ワールド), screen: {x, y}, button, shiftKey, ctrlKey, metaKey, altKey, pointerType, originalEvent。選択の更新はクリックイベントより前に行われ、readOnly でも発火します。

Node / NodeItem

typeNode

フィールド説明
idstring一意な ID(省略時は自動生成)
typestring?ノード種別。nodeTypes のスタイルを適用
titlestringヘッダに表示(既定 'Node'
x, ynumber左上のワールド座標
widthnumber?幅。省略時は theme.node.width(200)
input / outputPortSpecヘッダ左/右のポート
itemsNodeItem[]項目。高さは headerHeight + items.length × itemHeight + padding
childsNode[]?子ノード。項目の下に縦に並ぶ。位置と幅は親が自動計算(子の x / y / width は無視される)。何段でも入れ子にできる
parentstring?子ノードにだけ入る親の ID(読み取り専用。変更は setParent()
gotoGotoSpec?コネクタを使わない ID 指定の遷移先。'n1' / ['n1','n2'] / {to:'n1', label:'戻る'}
showPortsboolean?false で全ポートの丸を非表示
styleobject?theme.node.* の上書き
dataany?任意データ(そのまま JSON に保存)

typeNodeItem

フィールド説明
idstringノード内で一意(省略時は自動生成)
labelstring左側に表示
valuestring?右側に表示。あればダブルクリックで値を、無ければラベルを編集
input / outputPortSpec行の左/右のポート
showPortsboolean?false でこの項目のポートを非表示
gotoGotoSpec?この項目が選ばれたときの遷移先(ID 指定)

PortSpec / ポートキー

typePortSpec = false | true | number | { max?: number, visible?: boolean }

意味
false / 省略ポート無し(接続もできない)
trueポート有り。上限は rules.maxInputs / maxOutputs(既定 無制限)
数値 n最大 n
{ max, visible }max 省略で既定上限。visible: false で丸を描かない(接続は有効)

output の上限は「そのポートから開始できるコネクタ数」、input は「そのポートに終了できるコネクタ数」です。上限に達したポートは theme.port.fullFill(橙)で描かれます。

typeポートキー

キー場所
'in' / 'out'ノードヘッダの左/右
'item:<itemId>:in' / 'item:<itemId>:out'項目の左/右

portKey(itemId, dir) / parsePortKey(key)canvas-flow から export)で生成・分解できます。

Edge

typeEdge

フィールド説明
idstring一意な ID(省略時は自動生成)
source / sourcePortstring出力側のノード ID とポートキー(out 系)
target / targetPortstring入力側のノード ID とポートキー(in 系)
styleobject?theme.edge.* の上書き
type'bezier' | 'straight' | 'step'?描画方法。省略時は全体の既定(theme.edge.type
dataany?任意データ

向きが逆(in → out)で渡された場合、addEdge() が正規化します。同一ノード間・同じポート組の重複・存在しないポート・上限超過は追加されず null を返します。

JSON フォーマット

{
  "format": "canvas-flow",
  "version": 1,
  "nodes":  [ { "id": "a", "title": "A", "x": 0, "y": 0, "output": true, "items": [],
                "childs": [ { "id": "a1", "title": "子", "input": true, "items": [] } ] } ],
  "edges":  [ { "id": "e1", "source": "a", "sourcePort": "out", "target": "b", "targetPort": "in" } ],
  "viewport": { "tx": 0, "ty": 0, "zoom": 1 }
}

format / version / viewport は省略可能で、プレーンな { nodes, edges } も読み込めます。未対応の formatversion > 1 はエラーになります。

子ノードは nodes の中に childs として入れ子で書き出されます(トップレベルの nodes に並ぶのは親を持たないノードだけ)。子ノードの座標と幅は親が決めるため出力されず、読み込み時に指定されていても無視されます。edges は入れ子にならず、子ノードの ID をそのまま参照します。

Graph(editor.graph)

モデル操作の中心です。変更系はすべて Undo 可load / clear を除く)。

メソッド説明
addNode(node, { parent?, index? }) → Nodeノードを追加(ID 重複は例外)。node.childs があれば子ノードもまとめて追加
getNode(id) / getEdge(id)取得
updateNode(id, patch)プロパティを部分更新(title, x, y, width, items, style, data…)
updateItem(nodeId, itemId, patch) / addItem(nodeId, item, index?) / removeItem(nodeId, itemId)項目の更新・追加・削除(削除時はその項目のコネクタも削除)
moveNodes(ids, dx, dy)複数ノードを相対移動(子ノードの ID を渡すと一番外側の親ごと動く)
removeNode(id) / removeNodes(ids)接続コネクタと子孫ノードごと削除
childrenOf(node) / isChild(node) / parentOf(node) / rootOf(node) / depthOf(node) / descendantIds(node, { includeSelf? }) / rootNodes()親子関係の問い合わせ(引数はノードでも ID でも可)
addChild(parentId, child, index?) / removeChild(id, { x?, y? }) / setParent(id, parentId | null, index?)子ノードの追加・切り離し・付け替え(循環は setParentfalse を返して拒否)
relayoutChildren(node, { reindex? }) / nodeWidthOfChild(parent) / portEdgeX(node)子ノードの再配置(通常は自動)・子の幅・ポートを描く左右の X
addEdge(edge) → Edge | null検証して追加(常に拒否モード)
connect(source, sourcePort, target, targetPort, { replace? }) → Edge | nullrules.onFull に従って接続(満杯なら付け替え)
canConnect(...) → boolean / connectError(...) → string | null接続可否。理由: same-node / invalid-port / missing-port / duplicate / source-full / target-full
updateEdge(id, patch) / removeEdge(id) / removeEdges(ids)コネクタの更新・削除
edgesOf(nodeId) → Edge[]ノードに接続しているコネクタ
connectedTo(startIds, { depth, direction, includeStart, links }) → { nodes: Set, edges: Set, links: Set }起点から辿れるノード・コネクタ・goto(強調表示・まとめ選択用)。direction'lineage' | 'downstream' | 'upstream' | 'both'(この API の既定は 'both')。links: false で goto を辿らない
gotoLinks(node?) / gotoSources(node) / gotoTargets(node) / setGoto(node, value, { itemId }) / gotoAnchor(link)goto の一覧・逆引き・設定・点線の始点終点
portSpec(nodeId, key) / portEdges(nodeId, key) / portCapacity(nodeId, key) → { count, max, full }ポートの定義・接続・残量
portVisible(nodeId, key) / setPortVisible(...) / setPortsVisible(...)ポートの表示状態
nodeRect(node) / nodeWidth(node) / nodeHeight(node)矩形・寸法(高さはヘッダ+項目+子ノード)
nodePorts(node, { visibleOnly? }) / portPosition(nodeId, key) / itemRect(node, itemId)ポート位置・項目矩形
edgeGeometry(edge) / edgePoint(edge, t = 0.5) / edgeRect(edge) / edgeType(edge)形状({ type, x1, y1, x2, y2, points, c1x… })・形状上の点(折れ線は道のり比)・外接矩形・描画方法
nodesInRect(rect) / edgesInRect(rect) / nodesFullyInRect(rect)空間インデックスによる範囲問い合わせ
bounds() → rect | null全ノードを囲む矩形
duplicateNodes(ids, offset) → { nodes, edges, idMap }複製
batch(fn)複数変更を 1 回の change と 1 つの Undo 項目にまとめる
toJSON() / load(data) / clear()直列化(子は childs に入れ子)・置き換え・全消去
rules(プロパティ){ maxInputs, maxOutputs, onFull }

Graph 自身も on(type, handler) を持ち、change, node:add などに加えて Undo 用の opload, clear を発火します。

テーマ(defaultTheme)

{
  background: '#f6f7f9',
  grid: { color: '#e0e3e8', size: 32, majorEvery: 4, majorColor: '#cfd4db' },
  font: '13px system-ui, …', titleFont: 'bold 13px system-ui, …',
  node: { fill, stroke, strokeWidth: 1, radius: 8, headerFill, headerHeight: 30, titleColor, textColor,
          itemHeight: 26, itemSeparator, padding: 8, width: 200,
          childIndent: 10,  // 子ノードの左右インデント
          childGap: 6,      // 子ノードどうしの縦の間隔
          selectedStroke, selectedStrokeWidth: 2, hoverStroke, shadow },
  port: { radius: 5, fill, stroke, strokeWidth: 1.5, hoverFill, connectedFill, fullFill: '#f59e0b' },
  edge: { stroke, strokeWidth: 2, selectedStroke, selectedStrokeWidth: 3, hoverStroke, pendingStroke,
          type: 'bezier',   // 'bezier' | 'straight' | 'step'
          curvature: 0.5,   // bezier の曲がり具合
          stepOffset: 24,   // step の水平突き出し量
          deleteIcon: { radius: 9, fill, stroke, color, hoverFill, hoverColor } },
  goto: { stroke: '#94a3b8', strokeWidth: 1.5, dash: [6, 4], arrow: 9,   // ID 指定の遷移の点線
          labelColor, labelFont, labelBg },
  focus: { dimOpacity: 0.12, dimOpacityLod: 0.25, edgeStroke: null, edgeWidth: null },
  selectionBox: { fill, stroke },
  minimap: { background, border, node, selectedNode, viewportFill, viewportStroke },
  lodZoom: 0.3      // この拡大率以下で文字・ポート・影を省いた簡易描画
}

setTheme(patch) で部分上書きします。deleteIcon.radius は画面ピクセル単位(ズームに影響されない)。node.shadow は拡大率 50% 未満では省略されます。

<canvas-flow-editor>(Lit 版)

コアを包む Web Component。el.editorNodeEditor に触れます。ツールバー(検索・ズーム・Undo/Redo・複製・削除・コネクタ削除・整列・読み込み・書き出し)、インライン編集、ミニマップ、.json ドロップ、ResizeObserver による自動リサイズを内蔵します。

属性型 / 既定説明
data(プロパティ)object設定すると load()
theme(プロパティ)objectテーマの部分上書き
node-typesobject / {}ノード種別
minimap / minimap-width / minimap-heighttrue / 200 / 140ミニマップ
toolbartrueツールバー表示
wheel-mode / drag-mode / read-onlyzoom / pan / falseコアのオプションに対応
import-mode / export-filenamereplace / canvas-flow.jsonツールバー・ドロップでの読み込み方法、書き出しファイル名
max-inputs / max-outputs / on-full無制限 / reject接続ルール
edge-delete-icon"false" で無効削除アイコン
move-snap0ノードの移動単位(px)
edge-typebezierコネクタの描画方法(bezier / straight / step
focus-modeoff同じ流れの強調表示(off / connected / neighbors)。向きは setFocusMode(mode, {direction})
context-menu有効"false" で内蔵の右クリックメニューを無効(read-only では出ない)。項目は contextMenuItems で差し替え

委譲メソッド: addNode, addNodeAt, addNodeAtPointer, addNodeAtCenter, getPointer, addEdge, removeNode, removeEdge, updateNode, updateItem, duplicateSelection, deleteSelection, deleteSelectedEdges, autoLayout, setPortVisible, setPortsVisible, setMoveSnap, snapNodes, addChild, detachChild, setParent, childrenOf, rootNodeOf, setEdgeType, setSelectedEdgeType, setFocusMode, selectFocused, selectConnected, setGoto, gotoLinks, gotoSources, closeContextMenu, openContextMenuAt, undo, redo, canUndo, canRedo, zoomIn, zoomOut, resetZoom, fitView, search, focusNode, exportData, exportJSON, downloadJSON, importJSON, insertJSON, insertJSONAtPointer, importFromFile, openImportDialog, toJSON, load。追加イベント: ready{ editor }), import-error{ errors })。スロット: 既定(canvas 上に重ねる要素)と toolbar(ツールバー末尾)。

操作・ショートカット

操作動作
ノードをクリック / ドラッグ選択(Shift・Ctrl・Cmd で追加・トグル)/ 選択中ノードをまとめて移動
ポートをドラッグコネクタ作成(出力 → 入力。上限超過先には繋げない)
右下グリップをドラッグ幅のリサイズ
コネクタをクリック / 中央の × をクリック選択 / 削除
空白を左ドラッグパン(dragMode='select' なら範囲選択。Shift で反転)
中ボタン / Space + ドラッグパン
ホイールズーム。Shift + ホイールでパン(wheelMode='pan' なら逆)。慣性スクロール中は最初の動作を維持
2 本指(タッチ)ピンチでズーム、同時にパン
ダブルクリックヘッダ / 項目: 編集イベント。空白: canvas:dblclick
Delete / Backspace選択を削除
Shift + Delete / Backspace選択範囲内のコネクタだけを削除(ノードは残す)
Ctrl/Cmd + A / C / V / D全選択 / コピー / 貼り付け(カーソル位置)/ 複製
Ctrl/Cmd + Shift + A強調表示されている要素をまとめて選択(selectFocused。ツールバー「強調を選択」も同じ)
右クリック対象に応じたメニュー(↑ ↓ で移動、Enter で実行、Escape で閉じる)
Ctrl/Cmd + Z / Shift+Z / YUndo / Redo
Ctrl/Cmd + 0 / + / −縮尺リセット / 拡大 / 縮小
矢印キー(Shift で 10 倍)選択ノードを微移動(1px。moveSnap 設定時はその単位)
Escape操作キャンセル / 選択解除

キーボードは document で受け、keyboardScope 内を最後にポインタ操作していて入力欄にフォーカスが無いときだけ処理します。

canvas-flow v0.5.0 · この仕様書は docs/api.html として同梱されています。デモは docs/demo.html