feat: add Text node type to canvas
New canvas object Text with edit/create modal: text content (single or multi-line), font (Inter, JetBrains Mono, Serif, System Sans), size, color, border (style + width + color), background. Double-click on node opens edit modal. Sidebar gets "Add Text" entry below "Add Zone". Refs #124
This commit is contained in:
@@ -27,12 +27,71 @@ describe('canvasStore', () => {
|
||||
selectedNodeId: null,
|
||||
selectedNodeIds: [],
|
||||
editingGroupRectId: null,
|
||||
editingTextId: null,
|
||||
past: [],
|
||||
future: [],
|
||||
clipboard: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('setEditingTextId sets and clears editing text id', () => {
|
||||
const { setEditingTextId } = useCanvasStore.getState()
|
||||
setEditingTextId('t1')
|
||||
expect(useCanvasStore.getState().editingTextId).toBe('t1')
|
||||
setEditingTextId(null)
|
||||
expect(useCanvasStore.getState().editingTextId).toBeNull()
|
||||
})
|
||||
|
||||
it('addNode supports text type with text_content and style', () => {
|
||||
const { addNode } = useCanvasStore.getState()
|
||||
const textNode: Node<NodeData> = {
|
||||
id: 't1',
|
||||
type: 'text',
|
||||
position: { x: 50, y: 50 },
|
||||
data: {
|
||||
label: '',
|
||||
type: 'text',
|
||||
status: 'unknown',
|
||||
services: [],
|
||||
text_content: 'Hello world',
|
||||
custom_colors: {
|
||||
text_color: '#ffffff',
|
||||
text_size: 24,
|
||||
font: 'mono',
|
||||
border_style: 'dashed',
|
||||
border_width: 2,
|
||||
border: '#00d4ff',
|
||||
background: '#00000000',
|
||||
},
|
||||
},
|
||||
width: 200,
|
||||
height: 60,
|
||||
}
|
||||
addNode(textNode)
|
||||
const stored = useCanvasStore.getState().nodes.find((n) => n.id === 't1')
|
||||
expect(stored?.type).toBe('text')
|
||||
expect(stored?.data.text_content).toBe('Hello world')
|
||||
expect(stored?.data.custom_colors?.text_size).toBe(24)
|
||||
expect(stored?.data.custom_colors?.border_style).toBe('dashed')
|
||||
})
|
||||
|
||||
it('updateNode updates text_content and custom_colors on a text node', () => {
|
||||
const { addNode, updateNode } = useCanvasStore.getState()
|
||||
addNode({ ...makeNode('t1', { type: 'text', text_content: 'old' }), type: 'text' })
|
||||
updateNode('t1', { text_content: 'new', custom_colors: { text_color: '#ff0000', text_size: 32 } })
|
||||
const stored = useCanvasStore.getState().nodes.find((n) => n.id === 't1')
|
||||
expect(stored?.data.text_content).toBe('new')
|
||||
expect(stored?.data.custom_colors?.text_color).toBe('#ff0000')
|
||||
expect(stored?.data.custom_colors?.text_size).toBe(32)
|
||||
})
|
||||
|
||||
it('deleteNode removes a text node', () => {
|
||||
const { addNode, deleteNode } = useCanvasStore.getState()
|
||||
addNode({ ...makeNode('t1', { type: 'text' }), type: 'text' })
|
||||
deleteNode('t1')
|
||||
expect(useCanvasStore.getState().nodes).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('starts empty', () => {
|
||||
const { nodes, edges, hasUnsavedChanges } = useCanvasStore.getState()
|
||||
expect(nodes).toHaveLength(0)
|
||||
@@ -750,6 +809,7 @@ describe('canvasStore — custom style apply', () => {
|
||||
selectedNodeId: null,
|
||||
selectedNodeIds: [],
|
||||
editingGroupRectId: null,
|
||||
editingTextId: null,
|
||||
past: [],
|
||||
future: [],
|
||||
clipboard: [],
|
||||
|
||||
@@ -49,6 +49,8 @@ interface CanvasState {
|
||||
setNodeZIndex: (id: string, zIndex: number) => void
|
||||
editingGroupRectId: string | null
|
||||
setEditingGroupRectId: (id: string | null) => void
|
||||
editingTextId: string | null
|
||||
setEditingTextId: (id: string | null) => void
|
||||
createGroup: (nodeIds: string[], name: string) => void
|
||||
ungroup: (groupId: string) => void
|
||||
markSaved: () => void
|
||||
@@ -71,6 +73,7 @@ export const useCanvasStore = create<CanvasState>((set) => ({
|
||||
selectedNodeId: null,
|
||||
selectedNodeIds: [],
|
||||
editingGroupRectId: null,
|
||||
editingTextId: null,
|
||||
hideIp: false,
|
||||
scanEventTs: 0,
|
||||
fitViewPending: false,
|
||||
@@ -350,6 +353,8 @@ export const useCanvasStore = create<CanvasState>((set) => ({
|
||||
|
||||
setEditingGroupRectId: (id) => set({ editingGroupRectId: id }),
|
||||
|
||||
setEditingTextId: (id) => set({ editingTextId: id }),
|
||||
|
||||
createGroup: (nodeIds, name) =>
|
||||
set((state) => {
|
||||
const PADDING_H = 24
|
||||
|
||||
Reference in New Issue
Block a user