zudo-panel-designer docs
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

アンドゥ/リドゥ履歴

スナップショットベースの履歴リデューサー、ジェスチャーのバッチ化、50 エントリの上限(history.ts より)。

スナップショットベースのリデューサー

アンドゥ/リドゥは、差分/パッチのログではなく、ドキュメントの完全な状態スナップショットに対する、素の・React に依存しないリデューサーです。

interface HistoryState<T> {
  past: T[];
  present: T;
  future: T[];
}

createHistory(initial) は、空の past/futurepresent = initial で開始します。このリデューサーは React 依存のない素の関数であるため、@zpd/core は単体でテストしたり使ったりできます。アプリのウェーブでは、これを自身が使う任意の状態コンテナに配線します。

commitreplace

Function効果
commit(state, next)現在の present を新しいアンドゥエントリとして past に積み、next を新しい present に設定し、リドゥのブランチを破棄するfuture がクリアされる)。
replace(state, next)present をその場で更新する——新しいアンドゥエントリは作られない

replace は、連続的で進行中の更新——例えばドラッグ中のすべての pointermove——を、直近の commit/beginGesture が開いたエントリにまとめるために存在します。動きの 1 ピクセルごとにアンドゥのステップを作るのを避けるためです。

ジェスチャーのバッチ化

function beginGesture<T>(state: HistoryState<T>): HistoryState<T> {
  // Same as committing the current present onto itself
  return commit(state, state.present);
}

beginGesture は、present をまだ変えずに新しいアンドゥエントリを開きます。呼び出しのパターンは次のとおりです。ドラッグ/リサイズ/ノード編集の操作が始まったときに beginGesture を一度呼び、そのジェスチャーの残りの中間更新すべてで replace を呼びます。その結果、1 つのジェスチャー全体——それがどれだけ多くのポインタイベントを生み出そうと——がちょうど 1 つのアンドゥエントリに畳み込まれます。

50 エントリの上限

const MAX_HISTORY = 50;

commit(したがって、commit 経由で実装されている beginGesture も)は、積むたびに past を直近 50 エントリに切り詰めます(past: [...state.past, state.present].slice(-MAX_HISTORY))。履歴の深さには上限があり、長い編集セッションでもアンドゥが無制限に増えることはありません。

アンドゥ、リドゥ、can-* ガード

Function効果
undo(state)past の最後のエントリを present に移し、それまでの presentfuture の先頭に積む。past が空なら no-op。
redo(state)future の最初のエントリを present に移し、それまでの presentpast に積む。future が空なら no-op。
canUndo(state)past.length > 0 のとき true
canRedo(state)future.length > 0 のとき true

commit は常に future をクリアするため、リドゥはアンドゥの直後にのみ利用できます。アンドゥ後に新しい編集を行うと、放棄されたリドゥのブランチは破棄されます——これは標準的なアンドゥ/リドゥのセマンティクスです。