feat(commands): add selection commands

This commit is contained in:
syntaxbullet
2026-07-03 09:51:26 +02:00
parent d77bdc22a9
commit ca9e265614
5 changed files with 96 additions and 1 deletions

View File

@@ -4,6 +4,8 @@ export type { CommandId, CommandPayloads } from "./payloads";
export { createCommandDispatcher } from "./dispatcher";
export type { CommandRegistry } from "./registry";
export { createCommandRegistry } from "./registry";
export { selectionAddLayerCommand, selectionClearCommand, selectionCommands, selectionSetCommand } from "./selection";
export type { SelectionAddLayerPayload, SelectionSetPayload } from "./selection";
export {
viewportCommands,
viewportPanCommand,

View File

@@ -1,3 +1,4 @@
import type { SelectionAddLayerPayload, SelectionSetPayload } from "./selection";
import type {
ViewportPanPayload,
ViewportSetSizePayload,
@@ -6,6 +7,9 @@ import type {
} from "./viewport";
export type CommandPayloads = {
"selection.set": SelectionSetPayload;
"selection.clear": void;
"selection.addLayer": SelectionAddLayerPayload;
"viewport.pan": ViewportPanPayload;
"viewport.setZoom": ViewportSetZoomPayload;
"viewport.zoomAroundPoint": ViewportZoomAroundPointPayload;

View File

@@ -0,0 +1,25 @@
import { describe, expect, test } from "bun:test";
import { createInitialAppState } from "@editor/initial-state";
import { selectionAddLayerCommand, selectionClearCommand, selectionSetCommand } from "./selection";
describe("selection commands", () => {
test("sets selection", () => {
const next = selectionSetCommand.execute({ state: createInitialAppState("Test") }, { artboardId: "a1", layerIds: ["l1"] });
expect(next.editor.selection).toEqual({ artboardId: "a1", layerIds: ["l1"] });
});
test("adds unique layer selection", () => {
const selected = selectionSetCommand.execute({ state: createInitialAppState("Test") }, { layerIds: ["l1"] });
const next = selectionAddLayerCommand.execute({ state: selected }, { layerId: "l2" });
const same = selectionAddLayerCommand.execute({ state: next }, { layerId: "l2" });
expect(next.editor.selection.layerIds).toEqual(["l1", "l2"]);
expect(same).toBe(next);
});
test("clears selection", () => {
const selected = selectionSetCommand.execute({ state: createInitialAppState("Test") }, { artboardId: "a1", layerIds: ["l1"] });
const next = selectionClearCommand.execute({ state: selected }, undefined);
expect(next.editor.selection).toEqual({ layerIds: [] });
});
});

63
commands/selection.ts Normal file
View File

@@ -0,0 +1,63 @@
import type { ArtboardId, LayerId } from "@core/id";
import type { Command } from "./command";
export type SelectionSetPayload = {
artboardId?: ArtboardId;
layerIds: LayerId[];
};
export type SelectionAddLayerPayload = {
layerId: LayerId;
};
export const selectionSetCommand: Command<SelectionSetPayload> = {
id: "selection.set",
name: "Set selection",
execute({ state }, payload) {
return {
...state,
editor: {
...state.editor,
selection: {
artboardId: payload.artboardId,
layerIds: [...payload.layerIds],
},
},
};
},
};
export const selectionClearCommand: Command = {
id: "selection.clear",
name: "Clear selection",
execute({ state }) {
return {
...state,
editor: {
...state.editor,
selection: { layerIds: [] },
},
};
},
};
export const selectionAddLayerCommand: Command<SelectionAddLayerPayload> = {
id: "selection.addLayer",
name: "Add layer to selection",
execute({ state }, payload) {
if (state.editor.selection.layerIds.includes(payload.layerId)) return state;
return {
...state,
editor: {
...state.editor,
selection: {
...state.editor.selection,
layerIds: [...state.editor.selection.layerIds, payload.layerId],
},
},
};
},
};
export const selectionCommands = [selectionSetCommand, selectionClearCommand, selectionAddLayerCommand] satisfies Command<unknown>[];