fix excalidraw paste (#3822)

it broke for a few things, so this fixes it and adds some tests

### Change Type


- [x] `sdk` — Changes the tldraw SDK
- [x] `bugfix` — Bug fix
This commit is contained in:
alex 2024-05-23 21:32:59 +01:00 committed by GitHub
parent 58104c1a4c
commit 6c9ead0309
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 3102 additions and 57 deletions

View file

@ -0,0 +1,107 @@
{
"type": "excalidraw/clipboard",
"elements": [
{
"type": "rectangle",
"version": 1626,
"versionNonce": 64820303,
"index": "a0",
"isDeleted": false,
"id": "PZcAULwVLLyQ4Z9av5DmV",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -2101.46484375,
"y": 275.8671875,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 149.70703125,
"height": 116.80078125,
"seed": 429665350,
"groupIds": [],
"frameId": null,
"roundness": { "type": 3 },
"boundElements": [{ "id": "kaL7WbxkiKT-RKqorwtRm", "type": "arrow" }],
"updated": 1716494684633,
"link": null,
"locked": false
},
{
"id": "3g3MQdG7t5-VkoMU4W4wh",
"type": "rectangle",
"x": -1852.40234375,
"y": 240.8046875,
"width": 156.33984375,
"height": 129.7109375,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a1",
"roundness": { "type": 3 },
"seed": 115875375,
"version": 201,
"versionNonce": 331141167,
"isDeleted": false,
"boundElements": [{ "id": "kaL7WbxkiKT-RKqorwtRm", "type": "arrow" }],
"updated": 1716494669928,
"link": null,
"locked": false
},
{
"id": "kaL7WbxkiKT-RKqorwtRm",
"type": "arrow",
"x": -1942.37890625,
"y": 304.1567904612487,
"width": 77.18359375,
"height": 13.573000528631098,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "a2",
"roundness": { "type": 2 },
"seed": 1254418753,
"version": 3490,
"versionNonce": 1305578639,
"isDeleted": false,
"boundElements": null,
"updated": 1716494684634,
"link": null,
"locked": false,
"points": [
[0, 0],
[77.18359375, 13.573000528631098]
],
"lastCommittedPoint": null,
"startBinding": {
"elementId": "PZcAULwVLLyQ4Z9av5DmV",
"focus": -0.6277363915690484,
"gap": 9.37890625
},
"endBinding": {
"elementId": "3g3MQdG7t5-VkoMU4W4wh",
"focus": -0.3570621758346426,
"gap": 12.79296875
},
"startArrowhead": null,
"endArrowhead": "arrow"
}
],
"files": {}
}

View file

@ -0,0 +1,43 @@
import { readdirSync } from 'fs'
import { readFile } from 'fs/promises'
import { join } from 'path'
import { TestEditor } from '../../../../test/TestEditor'
import { pasteExcalidrawContent } from './pasteExcalidrawContent'
jest.mock('nanoid', () => {
let nextNanoId = 0
const nanoid = () => {
nextNanoId++
return `${nextNanoId}`
}
return {
nanoid,
default: nanoid,
__reset: () => {
nextNanoId = 0
},
}
})
beforeEach(() => {
// eslint-disable-next-line
require('nanoid').__reset()
})
describe('pasteExcalidrawContent test fixtures', () => {
const files = readdirSync(join(__dirname, 'excalidraw-test-fixtures')).filter((fileName) =>
fileName.endsWith('.json')
)
test.each(files)('%s', async (fileName) => {
const filePath = join(__dirname, 'excalidraw-test-fixtures', fileName)
const fileContent = JSON.parse(await readFile(filePath, 'utf-8'))
const editor = new TestEditor()
pasteExcalidrawContent(editor, fileContent)
expect(editor.store.serialize()).toMatchSnapshot()
})
})

View file

@ -18,11 +18,11 @@ import {
VecLike,
ZERO_INDEX_KEY,
compact,
createBindingId,
createShapeId,
getIndexAbove,
getIndices,
isShapeId,
uniqueId,
} from '@tldraw/editor'
/**
@ -38,7 +38,6 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
const tldrawContent: TLContent = {
shapes: [],
// todo(alex) #write these properly
bindings: [],
rootShapeIds: [],
assets: [],
@ -165,8 +164,10 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
break
}
case 'line': {
const start = element.points[0]
const end = element.points[element.points.length - 1]
const points = element.points.slice()
if (points.length < 2) {
break
}
const indices = getIndices(element.points.length)
tldrawContent.shapes.push({
@ -177,39 +178,17 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
size: strokeWidthsToSizes[element.strokeWidth],
color: colorsToColors[element.strokeColor] ?? 'black',
spline: element.roundness ? 'cubic' : 'line',
handles: {
start: {
id: 'start',
type: 'vertex',
index: indices[0],
x: start[0],
y: start[1],
},
end: {
id: 'end',
type: 'vertex',
index: indices[indices.length - 1],
x: end[0],
y: end[1],
},
points: {
...Object.fromEntries(
element.points.slice(1, -1).map(([x, y]: number[], i: number) => {
const id = uniqueId()
return [
id,
{
id,
type: 'vertex',
index: indices[i + 1],
x,
y,
},
]
element.points.map(([x, y]: number[], i: number) => {
const index = indices[i]
return [index, { id: index, index, x, y }]
})
),
},
},
})
break
}
case 'arrow': {
@ -241,36 +220,45 @@ export async function pasteExcalidrawContent(editor: Editor, clipboard: any, poi
dash: getDash(element),
size: strokeWidthsToSizes[element.strokeWidth] ?? 'm',
color: colorsToColors[element.strokeColor] ?? 'black',
start: startTargetId
? {
type: 'binding',
boundShapeId: startTargetId,
normalizedAnchor: { x: 0.5, y: 0.5 },
isPrecise: false,
isExact: false,
}
: {
type: 'point',
x: start[0],
y: start[1],
},
end: endTargetId
? {
type: 'binding',
boundShapeId: endTargetId,
normalizedAnchor: { x: 0.5, y: 0.5 },
isPrecise: false,
isExact: false,
}
: {
type: 'point',
x: end[0],
y: end[1],
},
start: { x: start[0], y: start[1] },
end: { x: end[0], y: end[1] },
arrowheadEnd: arrowheadsToArrowheadTypes[element.endArrowhead] ?? 'none',
arrowheadStart: arrowheadsToArrowheadTypes[element.startArrowhead] ?? 'none',
},
})
if (startTargetId) {
tldrawContent.bindings!.push({
id: createBindingId(),
typeName: 'binding',
type: 'arrow',
fromId: id,
toId: startTargetId,
props: {
terminal: 'start',
normalizedAnchor: { x: 0.5, y: 0.5 },
isPrecise: false,
isExact: false,
},
meta: {},
})
}
if (endTargetId) {
tldrawContent.bindings!.push({
id: createBindingId(),
typeName: 'binding',
type: 'arrow',
fromId: id,
toId: endTargetId,
props: {
terminal: 'end',
normalizedAnchor: { x: 0.5, y: 0.5 },
isPrecise: false,
isExact: false,
},
meta: {},
})
}
break
}
case 'text': {