web based infinite canvas
at main 64 lines 1.7 kB view raw
1import type { Action } from "../actions"; 2import { createId, ShapeRecord } from "../model"; 3import type { EditorState, ToolId } from "../reactivity"; 4import { getCurrentPage } from "../reactivity"; 5import type { Tool } from "./base"; 6 7/** 8 * Text tool - creates text shapes on click 9 * 10 * Features: 11 * - Click to create a text shape at the pointer position 12 * - Text is created with default content "Text" 13 * - Shape is immediately selected after creation 14 */ 15export class TextTool implements Tool { 16 readonly id: ToolId = "text"; 17 18 onEnter(state: EditorState): EditorState { 19 return state; 20 } 21 22 onExit(state: EditorState): EditorState { 23 return state; 24 } 25 26 onAction(state: EditorState, action: Action): EditorState { 27 switch (action.type) { 28 case "pointer-down": { 29 return this.handlePointerDown(state, action); 30 } 31 default: { 32 return state; 33 } 34 } 35 } 36 37 private handlePointerDown(state: EditorState, action: Action): EditorState { 38 if (action.type !== "pointer-down") return state; 39 40 const currentPage = getCurrentPage(state); 41 if (!currentPage) return state; 42 43 const shapeId = createId("shape"); 44 45 const shape = ShapeRecord.createText(currentPage.id, action.world.x, action.world.y, { 46 text: "Text", 47 fontSize: 16, 48 fontFamily: "sans-serif", 49 color: "#1f2933", 50 }, shapeId); 51 52 const newPage = { ...currentPage, shapeIds: [...currentPage.shapeIds, shapeId] }; 53 54 return { 55 ...state, 56 doc: { 57 ...state.doc, 58 shapes: { ...state.doc.shapes, [shapeId]: shape }, 59 pages: { ...state.doc.pages, [currentPage.id]: newPage }, 60 }, 61 ui: { ...state.ui, selectionIds: [shapeId] }, 62 }; 63 } 64}