diff --git a/src/commands/upsert-terrain-command.ts b/src/commands/upsert-terrain-command.ts new file mode 100644 index 00000000..29b05c39 --- /dev/null +++ b/src/commands/upsert-terrain-command.ts @@ -0,0 +1,92 @@ +import { createOpaqueId } from "../core/ids"; +import { cloneEditorSelection, type EditorSelection } from "../core/selection"; +import type { ToolMode } from "../core/tool-mode"; +import { + cloneTerrain, + getTerrainKindLabel, + type Terrain +} from "../document/terrains"; + +import type { EditorCommand } from "./command"; + +interface UpsertTerrainCommandOptions { + terrain: Terrain; + label?: string; +} + +function setSingleTerrainSelection(terrainId: string): EditorSelection { + return { + kind: "terrains", + ids: [terrainId] + }; +} + +function createDefaultTerrainCommandLabel(isNewTerrain: boolean): string { + const action = isNewTerrain ? "Create" : "Update"; + return `${action} ${getTerrainKindLabel().toLowerCase()}`; +} + +export function createUpsertTerrainCommand( + options: UpsertTerrainCommandOptions +): EditorCommand { + const nextTerrain = cloneTerrain(options.terrain); + let previousTerrain: Terrain | null = null; + let previousSelection: EditorSelection | null = null; + let previousToolMode: ToolMode | null = null; + + return { + id: createOpaqueId("command"), + label: options.label ?? createDefaultTerrainCommandLabel(true), + execute(context) { + const currentDocument = context.getDocument(); + const currentTerrain = currentDocument.terrains[nextTerrain.id]; + + if (previousSelection === null) { + previousSelection = cloneEditorSelection(context.getSelection()); + } + + if (previousToolMode === null) { + previousToolMode = context.getToolMode(); + } + + if (previousTerrain === null && currentTerrain !== undefined) { + previousTerrain = cloneTerrain(currentTerrain); + } + + context.setDocument({ + ...currentDocument, + terrains: { + ...currentDocument.terrains, + [nextTerrain.id]: cloneTerrain(nextTerrain) + } + }); + context.setSelection(setSingleTerrainSelection(nextTerrain.id)); + context.setToolMode("select"); + }, + undo(context) { + const currentDocument = context.getDocument(); + const nextTerrains = { + ...currentDocument.terrains + }; + + if (previousTerrain === null) { + delete nextTerrains[nextTerrain.id]; + } else { + nextTerrains[nextTerrain.id] = cloneTerrain(previousTerrain); + } + + context.setDocument({ + ...currentDocument, + terrains: nextTerrains + }); + + if (previousSelection !== null) { + context.setSelection(previousSelection); + } + + if (previousToolMode !== null) { + context.setToolMode(previousToolMode); + } + } + }; +}