feat(text): create text boxes by dragging

This commit is contained in:
Danila Poyarkov 2026-07-20 21:22:30 +03:00
parent 6a0009b25c
commit f3fbc94abf
4 changed files with 44 additions and 10 deletions

View file

@ -3,7 +3,7 @@ import type { Ref } from 'vue'
import type { Editor } from '@open-pencil/core/editor' import type { Editor } from '@open-pencil/core/editor'
import { startPenInput } from '#vue/canvas/pen-input/use' import { startPenInput } from '#vue/canvas/pen-input/use'
import { startShapeDraw, startTextTool } from '#vue/shared/input/draw' import { startShapeDraw, startTextDraw } from '#vue/shared/input/draw'
import { startPanDrag } from '#vue/shared/input/pan' import { startPanDrag } from '#vue/shared/input/pan'
import { handleSelectDown } from '#vue/shared/input/select' import { handleSelectDown } from '#vue/shared/input/select'
import type { HitTestFns } from '#vue/shared/input/select' import type { HitTestFns } from '#vue/shared/input/select'
@ -67,7 +67,7 @@ export function handleToolMouseDown({
} }
if (tool === 'TEXT') { if (tool === 'TEXT') {
startTextTool(cx, cy, editor) startTextDraw(cx, cy, editor, setDrag)
return return
} }

View file

@ -4,13 +4,17 @@ import type { Editor } from '@open-pencil/core/editor'
import { TOOL_TO_NODE } from '#vue/shared/input/types' import { TOOL_TO_NODE } from '#vue/shared/input/types'
import type { DragDraw, DragState } from '#vue/shared/input/types' import type { DragDraw, DragState } from '#vue/shared/input/types'
export function startTextTool(cx: number, cy: number, editor: Editor) { export function startTextDraw(
const nodeId = editor.createShape('TEXT', cx, cy, DEFAULT_TEXT_WIDTH, DEFAULT_TEXT_HEIGHT) cx: number,
cy: number,
editor: Editor,
setDrag: (d: DragState) => void
) {
editor.undo.beginBatch('Create text')
const nodeId = editor.createShape('TEXT', cx, cy, 0, 0)
editor.graph.updateNode(nodeId, { text: '' }) editor.graph.updateNode(nodeId, { text: '' })
editor.select([nodeId]) editor.select([nodeId])
editor.startTextEditing(nodeId) setDrag({ type: 'draw', startX: cx, startY: cy, nodeId })
editor.setTool('SELECT')
editor.requestRender()
} }
export function startShapeDraw( export function startShapeDraw(
@ -54,7 +58,14 @@ export function handleDrawMove(
export function handleDrawUp(d: DragDraw, editor: Editor) { export function handleDrawUp(d: DragDraw, editor: Editor) {
const node = editor.graph.getNode(d.nodeId) const node = editor.graph.getNode(d.nodeId)
if (node && node.width < 2 && node.height < 2) { if (node?.type === 'TEXT') {
const isPointText = node.width < 2 && node.height < 2
editor.updateNode(d.nodeId, {
width: isPointText ? DEFAULT_TEXT_WIDTH : node.width,
height: isPointText ? DEFAULT_TEXT_HEIGHT : node.height,
textAutoResize: isPointText ? 'WIDTH_AND_HEIGHT' : 'NONE'
})
} else if (node && node.width < 2 && node.height < 2) {
editor.updateNode(d.nodeId, { width: 100, height: 100 }) editor.updateNode(d.nodeId, { width: 100, height: 100 })
} }
if (node?.type === 'SECTION') { if (node?.type === 'SECTION') {
@ -63,4 +74,5 @@ export function handleDrawUp(d: DragDraw, editor: Editor) {
editor.commitResize(d.nodeId, { x: d.startX, y: d.startY, width: 0, height: 0 }) editor.commitResize(d.nodeId, { x: d.startX, y: d.startY, width: 0, height: 0 })
editor.undo.commitBatch() editor.undo.commitBatch()
editor.setTool('SELECT') editor.setTool('SELECT')
if (node?.type === 'TEXT') editor.startTextEditing(node.id)
} }

View file

@ -39,9 +39,10 @@ test('clicking with text tool creates a text node', async () => {
expect(textNode).toBeTruthy() expect(textNode).toBeTruthy()
}) })
test('text node is selected after creation', async () => { test('point text is selected and auto-sizes after creation', async () => {
const node = await getSelectedNode(editor.page) const node = await getSelectedNode(editor.page)
expect(node?.type).toBe('TEXT') expect(node?.type).toBe('TEXT')
expect(node?.textAutoResize).toBe('WIDTH_AND_HEIGHT')
}) })
test('typography section appears for text node', async () => { test('typography section appears for text node', async () => {
@ -63,6 +64,26 @@ test('text node has default font properties', async () => {
expect(node?.fontFamily).toBeTruthy() expect(node?.fontFamily).toBeTruthy()
}) })
test('dragging with the text tool creates a fixed-width text box', async () => {
await editor.page.keyboard.press('Escape')
await editor.page.keyboard.press('t')
await editor.canvas.drag(300, 120, 520, 240)
await editor.canvas.waitForRender()
const node = expectDefined(await getSelectedNode(editor.page), 'selected text box')
expect(node.type).toBe('TEXT')
expect(node.width).toBeCloseTo(220)
expect(node.height).toBeCloseTo(120)
expect(node.textAutoResize).toBe('NONE')
const editingTextId = await editor.page.evaluate(() => {
const store = window.openPencil?.getStore?.()
if (!store) throw new Error('OpenPencil store not initialized')
return store.state.editingTextId
})
expect(editingTextId).toBe(node.id)
})
test('creating text via store works', async () => { test('creating text via store works', async () => {
await editor.page.keyboard.press('Escape') await editor.page.keyboard.press('Escape')
await editor.canvas.waitForRender() await editor.canvas.waitForRender()

View file

@ -60,7 +60,8 @@ export function getSelectedNode(page: Page) {
fontSize: n.fontSize, fontSize: n.fontSize,
fontFamily: n.fontFamily, fontFamily: n.fontFamily,
fontWeight: n.fontWeight, fontWeight: n.fontWeight,
italic: n.italic italic: n.italic,
textAutoResize: n.textAutoResize
} }
}) })
} }