Merge pull request #31 from tldraw/testing-30-transform-command
Adds testMode, tests for transform
This commit is contained in:
commit
b2904d6a1e
40 changed files with 1796 additions and 231 deletions
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
|
@ -12,3 +12,5 @@ jobs:
|
||||||
uses: mattallty/jest-github-action@v1.0.3
|
uses: mattallty/jest-github-action@v1.0.3
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
test-command: 'yarn test'
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"childIndex": 3,
|
"childIndex": 3,
|
||||||
"point": [171.47, 288.63],
|
"point": [100, 100],
|
||||||
"size": [176.22, 192.26],
|
"size": [100, 100],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"style": {
|
"style": {
|
||||||
|
@ -32,8 +32,8 @@
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"childIndex": 4,
|
"childIndex": 4,
|
||||||
"point": [511.7, 404.19],
|
"point": [500, 400],
|
||||||
"size": [181.08999999999992, 150.40999999999997],
|
"size": [200, 200],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"style": {
|
"style": {
|
||||||
|
|
|
@ -64,14 +64,14 @@ for (let i = 0; i < count; i++) {
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"point": Array [
|
"point": Array [
|
||||||
511.7,
|
500,
|
||||||
404.19,
|
400,
|
||||||
],
|
],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"size": Array [
|
"size": Array [
|
||||||
181.08999999999992,
|
200,
|
||||||
150.40999999999997,
|
200,
|
||||||
],
|
],
|
||||||
"style": Object {
|
"style": Object {
|
||||||
"color": "Black",
|
"color": "Black",
|
||||||
|
@ -330,14 +330,14 @@ for (let i = 0; i < count; i++) {
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"point": Array [
|
"point": Array [
|
||||||
171.47,
|
100,
|
||||||
288.63,
|
100,
|
||||||
],
|
],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"size": Array [
|
"size": Array [
|
||||||
176.22,
|
100,
|
||||||
192.26,
|
100,
|
||||||
],
|
],
|
||||||
"style": Object {
|
"style": Object {
|
||||||
"color": "Black",
|
"color": "Black",
|
||||||
|
@ -468,14 +468,14 @@ for (let i = 0; i < count; i++) {
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"point": Array [
|
"point": Array [
|
||||||
511.7,
|
500,
|
||||||
404.19,
|
400,
|
||||||
],
|
],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"size": Array [
|
"size": Array [
|
||||||
181.08999999999992,
|
200,
|
||||||
150.40999999999997,
|
200,
|
||||||
],
|
],
|
||||||
"style": Object {
|
"style": Object {
|
||||||
"color": "Black",
|
"color": "Black",
|
||||||
|
@ -734,14 +734,14 @@ for (let i = 0; i < count; i++) {
|
||||||
"name": "Rectangle",
|
"name": "Rectangle",
|
||||||
"parentId": "page1",
|
"parentId": "page1",
|
||||||
"point": Array [
|
"point": Array [
|
||||||
171.47,
|
100,
|
||||||
288.63,
|
100,
|
||||||
],
|
],
|
||||||
"radius": 2,
|
"radius": 2,
|
||||||
"rotation": 0,
|
"rotation": 0,
|
||||||
"size": Array [
|
"size": Array [
|
||||||
176.22,
|
100,
|
||||||
192.26,
|
100,
|
||||||
],
|
],
|
||||||
"style": Object {
|
"style": Object {
|
||||||
"color": "Black",
|
"color": "Black",
|
||||||
|
|
852
__tests__/commands/__snapshots__/transform.test.ts.snap
Normal file
852
__tests__/commands/__snapshots__/transform.test.ts.snap
Normal file
|
@ -0,0 +1,852 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 1`] = `
|
||||||
|
Object {
|
||||||
|
"height": 593.73,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": -12.476,
|
||||||
|
"minY": 6.27,
|
||||||
|
"width": 712.476,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 2`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
-12.476,
|
||||||
|
6.27,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
118.75,
|
||||||
|
118.75,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
462.51,
|
||||||
|
362.51,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
237.49,
|
||||||
|
237.49,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 3`] = `
|
||||||
|
Object {
|
||||||
|
"height": 531.6320000000001,
|
||||||
|
"maxX": 737.9599999999999,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 68.368,
|
||||||
|
"width": 637.9599999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 4`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
68.368,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.33,
|
||||||
|
106.33,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
525.31,
|
||||||
|
387.35,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
212.65,
|
||||||
|
212.65,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 5`] = `
|
||||||
|
Object {
|
||||||
|
"height": 533.62,
|
||||||
|
"maxX": 740.3499999999999,
|
||||||
|
"maxY": 633.62,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 640.3499999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 6`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.72,
|
||||||
|
106.72,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
526.9,
|
||||||
|
420.17,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
213.45,
|
||||||
|
213.45,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 7`] = `
|
||||||
|
Object {
|
||||||
|
"height": 490.52,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 590.52,
|
||||||
|
"minX": 111.38,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 588.62,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms corners 8`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
111.38,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
98.104,
|
||||||
|
98.104,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
503.79,
|
||||||
|
394.31,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
196.21,
|
||||||
|
196.21,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 1`] = `
|
||||||
|
Object {
|
||||||
|
"height": 593.73,
|
||||||
|
"maxX": 756.24,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 43.762,
|
||||||
|
"minY": 6.27,
|
||||||
|
"width": 712.4780000000001,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 2`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
43.762,
|
||||||
|
6.27,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
118.75,
|
||||||
|
118.75,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
518.75,
|
||||||
|
362.51,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
237.49,
|
||||||
|
237.49,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 3`] = `
|
||||||
|
Object {
|
||||||
|
"height": 531.6260000000001,
|
||||||
|
"maxX": 737.9599999999999,
|
||||||
|
"maxY": 615.8100000000001,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 84.184,
|
||||||
|
"width": 637.9599999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 4`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
84.184,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.33,
|
||||||
|
106.33,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
525.31,
|
||||||
|
403.16,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
212.65,
|
||||||
|
212.65,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 5`] = `
|
||||||
|
Object {
|
||||||
|
"height": 411.35,
|
||||||
|
"maxX": 646.81,
|
||||||
|
"maxY": 511.35,
|
||||||
|
"minX": 153.19,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 493.61999999999995,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 6`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
153.19,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
82.269,
|
||||||
|
82.269,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
482.27,
|
||||||
|
346.81,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
164.54,
|
||||||
|
164.54,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 7`] = `
|
||||||
|
Object {
|
||||||
|
"height": 490.52,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 595.26,
|
||||||
|
"minX": 111.38,
|
||||||
|
"minY": 104.74,
|
||||||
|
"width": 588.62,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests shift-transforms edges 8`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
111.38,
|
||||||
|
104.74,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
98.104,
|
||||||
|
98.104,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
503.79,
|
||||||
|
399.05,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
196.21,
|
||||||
|
196.21,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 1`] = `
|
||||||
|
Object {
|
||||||
|
"height": 593.73,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 27.892,
|
||||||
|
"minY": 6.27,
|
||||||
|
"width": 672.108,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 2`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
27.892,
|
||||||
|
6.27,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
112.02,
|
||||||
|
118.75,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
475.96,
|
||||||
|
362.51,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
224.04,
|
||||||
|
237.49,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 3`] = `
|
||||||
|
Object {
|
||||||
|
"height": 490.17,
|
||||||
|
"maxX": 737.9599999999999,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 109.83,
|
||||||
|
"width": 637.9599999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 4`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
109.83,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.33,
|
||||||
|
98.034,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
525.31,
|
||||||
|
403.93,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
212.65,
|
||||||
|
196.07,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 5`] = `
|
||||||
|
Object {
|
||||||
|
"height": 411.35,
|
||||||
|
"maxX": 740.3499999999999,
|
||||||
|
"maxY": 511.35,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 640.3499999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 6`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.72,
|
||||||
|
82.269,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
526.9,
|
||||||
|
346.81,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
213.45,
|
||||||
|
164.54,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 7`] = `
|
||||||
|
Object {
|
||||||
|
"height": 437.4,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 537.4,
|
||||||
|
"minX": 111.38,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 588.62,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms corners 8`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
111.38,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
98.104,
|
||||||
|
87.479,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
503.79,
|
||||||
|
362.44,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
196.21,
|
||||||
|
174.96,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 1`] = `
|
||||||
|
Object {
|
||||||
|
"height": 593.73,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 6.27,
|
||||||
|
"width": 600,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 2`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
6.27,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
100,
|
||||||
|
118.75,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
500,
|
||||||
|
362.51,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
200,
|
||||||
|
237.49,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 3`] = `
|
||||||
|
Object {
|
||||||
|
"height": 500,
|
||||||
|
"maxX": 737.9599999999999,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 637.9599999999999,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 4`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
106.33,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
525.31,
|
||||||
|
400,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
212.65,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 5`] = `
|
||||||
|
Object {
|
||||||
|
"height": 411.35,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 511.35,
|
||||||
|
"minX": 100,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 600,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 6`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
100,
|
||||||
|
82.269,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
500,
|
||||||
|
346.81,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
200,
|
||||||
|
164.54,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 7`] = `
|
||||||
|
Object {
|
||||||
|
"height": 500,
|
||||||
|
"maxX": 700,
|
||||||
|
"maxY": 600,
|
||||||
|
"minX": 111.38,
|
||||||
|
"minY": 100,
|
||||||
|
"width": 588.62,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command snapshot tests transforms edges 8`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
111.38,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
98.104,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
503.79,
|
||||||
|
400,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
196.21,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the bottom edge 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
100,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
500,
|
||||||
|
460,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
200,
|
||||||
|
240,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the bottom-left corner 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
200,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
83.333,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
533.33,
|
||||||
|
460,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
166.67,
|
||||||
|
240,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the bottom-right corner 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
116.67,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
566.67,
|
||||||
|
460,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
233.33,
|
||||||
|
240,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the left edge 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
200,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
83.333,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
533.33,
|
||||||
|
400,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
166.67,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the right edge 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
116.67,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
566.67,
|
||||||
|
400,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
233.33,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the top edge 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
100,
|
||||||
|
80,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
500,
|
||||||
|
440,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
200,
|
||||||
|
160,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the top-left corner 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
200,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
83.333,
|
||||||
|
80,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
533.33,
|
||||||
|
440,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
166.67,
|
||||||
|
160,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command transforms from the top-right corner 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
116.67,
|
||||||
|
80,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
566.67,
|
||||||
|
440,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
233.33,
|
||||||
|
160,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command when transforming from the bottom-right corner does command 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
116.67,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
566.67,
|
||||||
|
460,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
233.33,
|
||||||
|
240,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command when transforming from the bottom-right corner re-does command 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
116.67,
|
||||||
|
120,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
566.67,
|
||||||
|
460,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
233.33,
|
||||||
|
240,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform command when transforming from the bottom-right corner un-does command 1`] = `
|
||||||
|
Object {
|
||||||
|
"rect1": Object {
|
||||||
|
"point": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"rect2": Object {
|
||||||
|
"point": Array [
|
||||||
|
500,
|
||||||
|
400,
|
||||||
|
],
|
||||||
|
"size": Array [
|
||||||
|
200,
|
||||||
|
200,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
40
__tests__/commands/align.test.ts
Normal file
40
__tests__/commands/align.test.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('align command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when multiple items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
21
__tests__/commands/change-page.ts
Normal file
21
__tests__/commands/change-page.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('change page command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
57
__tests__/commands/delete-page.ts
Normal file
57
__tests__/commands/delete-page.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('delete page command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when last page is selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when first page is selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when project only has one page', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
40
__tests__/commands/delete-selected.ts
Normal file
40
__tests__/commands/delete-selected.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('delete-selected command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when multiple items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,7 +1,7 @@
|
||||||
import { ShapeType } from 'types'
|
import { ShapeType } from 'types'
|
||||||
import TestState, { rectangleId, arrowId } from './test-utils'
|
import TestState, { rectangleId, arrowId } from '../test-utils'
|
||||||
|
|
||||||
describe('deleting single shapes', () => {
|
describe('delete command', () => {
|
||||||
const tt = new TestState()
|
const tt = new TestState()
|
||||||
|
|
||||||
describe('deleting single shapes', () => {
|
describe('deleting single shapes', () => {
|
37
__tests__/commands/distribute.ts
Normal file
37
__tests__/commands/distribute.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('distribute command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does not change anything', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when two items are selected', () => {
|
||||||
|
it('does not change anything', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when three or more items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
21
__tests__/commands/draw.ts
Normal file
21
__tests__/commands/draw.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('draw command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
40
__tests__/commands/duplicate.ts
Normal file
40
__tests__/commands/duplicate.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('duplicate command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when multiple items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
21
__tests__/commands/edit.ts
Normal file
21
__tests__/commands/edit.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('edit command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
21
__tests__/commands/generate.ts
Normal file
21
__tests__/commands/generate.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('generate command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
40
__tests__/commands/group.ts
Normal file
40
__tests__/commands/group.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('group command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does not change anything', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when multiple items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('groups shapes with different parents', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not group a parent group shape and its child', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
40
__tests__/commands/move-to-page.ts
Normal file
40
__tests__/commands/move-to-page.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('move-to-page command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
describe('when one item is selected', () => {
|
||||||
|
it('does not change anything', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when multiple items are selected', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reparents children of groups to page', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('correctly preserves moved groups', () => {
|
||||||
|
// TODO
|
||||||
|
null
|
||||||
|
})
|
||||||
|
})
|
347
__tests__/commands/transform.test.ts
Normal file
347
__tests__/commands/transform.test.ts
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
import { Corner, Edge, RectangleShape, ShapeType } from 'types'
|
||||||
|
import { rng } from 'utils'
|
||||||
|
import TestState from '../test-utils'
|
||||||
|
|
||||||
|
describe('transform command', () => {
|
||||||
|
const tt = new TestState()
|
||||||
|
tt.resetDocumentState()
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [100, 100],
|
||||||
|
size: [100, 100],
|
||||||
|
childIndex: 1,
|
||||||
|
},
|
||||||
|
'rect1'
|
||||||
|
)
|
||||||
|
.createShape(
|
||||||
|
{
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
point: [500, 400],
|
||||||
|
size: [200, 200],
|
||||||
|
childIndex: 2,
|
||||||
|
},
|
||||||
|
'rect2'
|
||||||
|
)
|
||||||
|
.clickShape('rect1')
|
||||||
|
.clickShape('rect2', { shiftKey: true })
|
||||||
|
.save()
|
||||||
|
|
||||||
|
function getSnapInfo() {
|
||||||
|
return {
|
||||||
|
rect1: {
|
||||||
|
point: tt.getShape<RectangleShape>('rect1').point,
|
||||||
|
size: tt.getShape<RectangleShape>('rect1').size,
|
||||||
|
},
|
||||||
|
rect2: {
|
||||||
|
point: tt.getShape<RectangleShape>('rect2').point,
|
||||||
|
size: tt.getShape<RectangleShape>('rect2').size,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('sets up initial bounds', () => {
|
||||||
|
expect(tt.selectedIds).toEqual(['rect1', 'rect2'])
|
||||||
|
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 600,
|
||||||
|
width: 600,
|
||||||
|
height: 500,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when transforming from the bottom-right corner', () => {
|
||||||
|
it('does command', () => {
|
||||||
|
// Restore the saved data state, with the initial selection
|
||||||
|
tt.restore()
|
||||||
|
|
||||||
|
// Move the bounds handle
|
||||||
|
tt.startClickingBoundsHandle(Corner.BottomRight)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 800,
|
||||||
|
maxY: 700,
|
||||||
|
width: 700,
|
||||||
|
height: 600,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('un-does command', () => {
|
||||||
|
// Repeat the same actions, but add an undo at the end
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.BottomRight)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
.undo()
|
||||||
|
|
||||||
|
// Expect the bounds to be the initial bounds
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 600,
|
||||||
|
width: 600,
|
||||||
|
height: 500,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('re-does command', () => {
|
||||||
|
// Repeat the same actions but add an undo and a redo at the end
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.BottomRight)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
.undo()
|
||||||
|
.redo()
|
||||||
|
|
||||||
|
// Expect the bounds to be the transformed bounds
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 800,
|
||||||
|
maxY: 700,
|
||||||
|
width: 700,
|
||||||
|
height: 600,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// From here on, let's assume that the undo and redos work as expected,
|
||||||
|
// so let's only test the command's execution.
|
||||||
|
|
||||||
|
it('transforms from the top edge', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Edge.Top)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 200,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 600,
|
||||||
|
width: 600,
|
||||||
|
height: 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the right edge', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Edge.Right)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 800,
|
||||||
|
maxY: 600,
|
||||||
|
width: 700,
|
||||||
|
height: 500,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the bottom edge', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Edge.Bottom)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 700,
|
||||||
|
width: 600,
|
||||||
|
height: 600,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the left edge', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Edge.Left)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 200,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 600,
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the top-left corner', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.TopLeft)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 200,
|
||||||
|
minY: 200,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 600,
|
||||||
|
width: 500,
|
||||||
|
height: 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the top-right corner', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.TopRight)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 200,
|
||||||
|
maxX: 800,
|
||||||
|
maxY: 600,
|
||||||
|
width: 700,
|
||||||
|
height: 400,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the bottom-right corner', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.BottomRight)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 100,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 800,
|
||||||
|
maxY: 700,
|
||||||
|
width: 700,
|
||||||
|
height: 600,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms from the bottom-left corner', () => {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(Corner.BottomLeft)
|
||||||
|
.movePointerBy([100, 100])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchObject({
|
||||||
|
minX: 200,
|
||||||
|
minY: 100,
|
||||||
|
maxX: 700,
|
||||||
|
maxY: 700,
|
||||||
|
width: 500,
|
||||||
|
height: 600,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('snapshot tests', () => {
|
||||||
|
it('transforms corners', () => {
|
||||||
|
const getRandom = rng('transform-tests-random-number-generator')
|
||||||
|
|
||||||
|
for (const corner of Object.values(Corner)) {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(corner)
|
||||||
|
.movePointerBy([getRandom() * 200, getRandom() * 200])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchSnapshot()
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('transforms edges', () => {
|
||||||
|
const getRandom = rng('transform-tests-random-number-generator')
|
||||||
|
|
||||||
|
for (const edge of Object.values(Edge)) {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(edge)
|
||||||
|
.movePointerBy([getRandom() * 200, getRandom() * 200])
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchSnapshot()
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shift-transforms corners', () => {
|
||||||
|
const getRandom = rng('transform-tests-random-number-generator')
|
||||||
|
|
||||||
|
for (const corner of Object.values(Corner)) {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(corner)
|
||||||
|
.movePointerBy([getRandom() * 200, getRandom() * 200], {
|
||||||
|
shiftKey: true,
|
||||||
|
})
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchSnapshot()
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shift-transforms edges', () => {
|
||||||
|
const getRandom = rng('transform-tests-random-number-generator')
|
||||||
|
|
||||||
|
for (const edge of Object.values(Edge)) {
|
||||||
|
tt.restore()
|
||||||
|
.startClickingBoundsHandle(edge)
|
||||||
|
.movePointerBy([getRandom() * 200, getRandom() * 200], {
|
||||||
|
shiftKey: true,
|
||||||
|
})
|
||||||
|
.stopClickingBounds()
|
||||||
|
|
||||||
|
// Ensure the bounds have been transformed
|
||||||
|
expect(tt.state.values.selectedBounds).toMatchSnapshot()
|
||||||
|
|
||||||
|
expect(getSnapInfo()).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,11 +1,9 @@
|
||||||
import state from 'state'
|
import TestState from '../test-utils'
|
||||||
import * as json from './__mocks__/document.json'
|
|
||||||
|
|
||||||
state.reset()
|
describe('translate command', () => {
|
||||||
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
const tt = new TestState()
|
||||||
state.send('CLEARED_PAGE')
|
tt.resetDocumentState()
|
||||||
|
|
||||||
describe('translates shapes', () => {
|
|
||||||
it('translates a single selected shape', () => {
|
it('translates a single selected shape', () => {
|
||||||
// TODO
|
// TODO
|
||||||
null
|
null
|
|
@ -1,6 +1,6 @@
|
||||||
import TestState from './test-utils'
|
import TestState from './test-utils'
|
||||||
|
|
||||||
describe('locked shapes', () => {
|
describe('lock command', () => {
|
||||||
const tt = new TestState()
|
const tt = new TestState()
|
||||||
tt.resetDocumentState()
|
tt.resetDocumentState()
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import _state from 'state'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
import { createShape, getShapeUtils } from 'state/shape-utils'
|
import { createShape, getShapeUtils } from 'state/shape-utils'
|
||||||
import { Data, Shape, ShapeType, ShapeUtility } from 'types'
|
import { Corner, Data, Edge, Shape, ShapeType, ShapeUtility } from 'types'
|
||||||
import { deepCompareArrays, setToArray, uniqueId, vec } from 'utils'
|
import { deepClone, deepCompareArrays, uniqueId, vec } from 'utils'
|
||||||
import * as mockDocument from './__mocks__/document.json'
|
import * as mockDocument from './__mocks__/document.json'
|
||||||
|
|
||||||
type State = typeof _state
|
type State = typeof _state
|
||||||
|
@ -22,9 +22,12 @@ interface PointerOptions {
|
||||||
|
|
||||||
class TestState {
|
class TestState {
|
||||||
state: State
|
state: State
|
||||||
|
snapshot: Data
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.state = _state
|
this.state = _state
|
||||||
|
this.state.send('TOGGLED_TEST_MODE')
|
||||||
|
this.snapshot = deepClone(this.state.data)
|
||||||
this.reset()
|
this.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +60,7 @@ class TestState {
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
resetDocumentState(): TestState {
|
resetDocumentState(): TestState {
|
||||||
this.state.send('RESET_DOCUMENT_STATE')
|
this.state.send('RESET_DOCUMENT_STATE').send('TOGGLED_TEST_MODE')
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +125,13 @@ class TestState {
|
||||||
idsAreSelected(ids: string[], strict = true): boolean {
|
idsAreSelected(ids: string[], strict = true): boolean {
|
||||||
const selectedIds = tld.getSelectedIds(this.data)
|
const selectedIds = tld.getSelectedIds(this.data)
|
||||||
return (
|
return (
|
||||||
(strict ? selectedIds.size === ids.length : true) &&
|
(strict ? selectedIds.length === ids.length : true) &&
|
||||||
ids.every((id) => selectedIds.has(id))
|
ids.every((id) => selectedIds.includes(id))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
get selectedIds(): string[] {
|
get selectedIds(): string[] {
|
||||||
return setToArray(tld.getSelectedIds(this.data))
|
return tld.getSelectedIds(this.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -340,6 +343,63 @@ class TestState {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start clicking bounds.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* tt.startClickingBounds()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
startClickingBounds(options: PointerOptions = {}): TestState {
|
||||||
|
this.state.send(
|
||||||
|
'POINTED_BOUNDS',
|
||||||
|
inputs.pointerDown(TestState.point(options), 'bounds')
|
||||||
|
)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop clicking the bounding box.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* tt.stopClickingBounds()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
stopClickingBounds(options: PointerOptions = {}): TestState {
|
||||||
|
this.state.send(
|
||||||
|
'STOPPED_POINTING',
|
||||||
|
inputs.pointerUp(TestState.point(options), 'bounds')
|
||||||
|
)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start clicking a bounds handle.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* tt.startClickingBoundsHandle(Edge.Top)
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
startClickingBoundsHandle(
|
||||||
|
handle: Corner | Edge | 'center',
|
||||||
|
options: PointerOptions = {}
|
||||||
|
): TestState {
|
||||||
|
this.state.send(
|
||||||
|
'POINTED_BOUNDS_HANDLE',
|
||||||
|
inputs.pointerDown(TestState.point(options), handle)
|
||||||
|
)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the pointer to a new point, or to several points in order.
|
* Move the pointer to a new point, or to several points in order.
|
||||||
*
|
*
|
||||||
|
@ -572,6 +632,35 @@ class TestState {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a snapshot of the state's current data.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* tt.save()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
save(): TestState {
|
||||||
|
this.snapshot = deepClone(this.data)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the state's saved data.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
*```ts
|
||||||
|
* tt.save()
|
||||||
|
* tt.restore()
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
restore(): TestState {
|
||||||
|
this.state.forceData(this.snapshot)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the state's current data.
|
* Get the state's current data.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
import state from 'state'
|
|
||||||
import * as json from './__mocks__/document.json'
|
|
||||||
|
|
||||||
state.reset()
|
|
||||||
state.send('MOUNTED').send('LOADED_FROM_FILE', { json: JSON.stringify(json) })
|
|
||||||
state.send('CLEARED_PAGE')
|
|
||||||
|
|
||||||
describe('transforms shapes', () => {
|
|
||||||
it('transforms from the top edge', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the right edge', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom edge', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the left edge', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the top-left corner', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the top-right corner', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom-right corner', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom-left corner', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('transforms shapes while aspect-ratio locked', () => {
|
|
||||||
// Fixed
|
|
||||||
|
|
||||||
it('transforms from the top edge while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the right edge while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom edge while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
it('transforms from the left edge while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the top-left corner while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the top-right corner while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom-right corner while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
|
|
||||||
it('transforms from the bottom-left corner while aspect-ratio locked', () => {
|
|
||||||
// TODO
|
|
||||||
null
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1840,14 +1840,6 @@ type RequiredKeys<T> = {
|
||||||
return Array.from(new Set(items).values())
|
return Array.from(new Set(items).values())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a set to an array.
|
|
||||||
* @param set
|
|
||||||
*/
|
|
||||||
static setToArray<T>(set: Set<T>): T[] {
|
|
||||||
return Array.from(set.values())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the outer of between a circle and a point.
|
* Get the outer of between a circle and a point.
|
||||||
* @param C The circle's center.
|
* @param C The circle's center.
|
||||||
|
|
|
@ -86,11 +86,11 @@ function ShapesFunctions() {
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasSelection = useSelector((s) => {
|
const hasSelection = useSelector((s) => {
|
||||||
return tld.getSelectedIds(s.data).size > 0
|
return tld.getSelectedIds(s.data).length > 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasMultipleSelection = useSelector((s) => {
|
const hasMultipleSelection = useSelector((s) => {
|
||||||
return tld.getSelectedIds(s.data).size > 1
|
return tld.getSelectedIds(s.data).length > 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,13 +7,13 @@ import {
|
||||||
SizeStyle,
|
SizeStyle,
|
||||||
} from 'types'
|
} from 'types'
|
||||||
import { createShape, getShapeUtils } from 'state/shape-utils'
|
import { createShape, getShapeUtils } from 'state/shape-utils'
|
||||||
import { setToArray, uniqueId } from 'utils'
|
import { uniqueId } from 'utils'
|
||||||
import Vec from 'utils/vec'
|
import Vec from 'utils/vec'
|
||||||
|
|
||||||
export const codeShapes = new Set<CodeShape<Shape>>([])
|
export const codeShapes = new Set<CodeShape<Shape>>([])
|
||||||
|
|
||||||
function getOrderedShapes() {
|
function getOrderedShapes() {
|
||||||
return setToArray(codeShapes).sort(
|
return Array.from(codeShapes.values()).sort(
|
||||||
(a, b) => a.shape.childIndex - b.shape.childIndex
|
(a, b) => a.shape.childIndex - b.shape.childIndex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { setToArray } from 'utils'
|
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
/* ------------------ Command Class ----------------- */
|
/* ------------------ Command Class ----------------- */
|
||||||
|
@ -85,7 +84,7 @@ export class BaseCommand<T extends any> {
|
||||||
export default class Command extends BaseCommand<Data> {
|
export default class Command extends BaseCommand<Data> {
|
||||||
saveSelectionState = (data: Data): ((next: Data) => void) => {
|
saveSelectionState = (data: Data): ((next: Data) => void) => {
|
||||||
const { currentPageId } = data
|
const { currentPageId } = data
|
||||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
const selectedIds = [...tld.getSelectedIds(data)]
|
||||||
return (next: Data) => {
|
return (next: Data) => {
|
||||||
next.currentPageId = currentPageId
|
next.currentPageId = currentPageId
|
||||||
next.hoveredId = undefined
|
next.hoveredId = undefined
|
||||||
|
|
|
@ -54,7 +54,7 @@ function getSnapshot(data: Data) {
|
||||||
|
|
||||||
const pageState: PageState = {
|
const pageState: PageState = {
|
||||||
id,
|
id,
|
||||||
selectedIds: new Set([]),
|
selectedIds: [],
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
|
|
|
@ -100,7 +100,7 @@ export default function groupCommand(data: Data): void {
|
||||||
getShapeUtils(oldParent).setProperty(
|
getShapeUtils(oldParent).setProperty(
|
||||||
oldParent,
|
oldParent,
|
||||||
'children',
|
'children',
|
||||||
oldParent.children.filter((id) => !oldSelectedIds.has(id))
|
oldParent.children.filter((id) => !oldSelectedIds.includes(id))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data } from 'types'
|
import { Data } from 'types'
|
||||||
import { setToArray, uniqueArray } from 'utils'
|
import { uniqueArray } from 'utils'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import storage from 'state/storage'
|
import storage from 'state/storage'
|
||||||
|
@ -9,7 +9,7 @@ import storage from 'state/storage'
|
||||||
export default function moveToPageCommand(data: Data, newPageId: string): void {
|
export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
const { currentPageId: oldPageId } = data
|
const { currentPageId: oldPageId } = data
|
||||||
const oldPage = tld.getPage(data)
|
const oldPage = tld.getPage(data)
|
||||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
const selectedIds = [...tld.getSelectedIds(data)]
|
||||||
|
|
||||||
const idsToMove = uniqueArray(
|
const idsToMove = uniqueArray(
|
||||||
...selectedIds.flatMap((id) => tld.getDocumentBranch(data, id))
|
...selectedIds.flatMap((id) => tld.getDocumentBranch(data, id))
|
||||||
|
@ -59,7 +59,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Clear the current page state's selected ids
|
// Clear the current page state's selected ids
|
||||||
tld.getPageState(data).selectedIds.clear()
|
tld.setSelectedIds(data, [])
|
||||||
|
|
||||||
// Save the "from" page
|
// Save the "from" page
|
||||||
storage.savePage(data, data.document.id, fromPageId)
|
storage.savePage(data, data.document.id, fromPageId)
|
||||||
|
@ -83,7 +83,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select the selected ids on the new page
|
// Select the selected ids on the new page
|
||||||
tld.getPageState(data).selectedIds = new Set(selectedIds)
|
tld.setSelectedIds(data, [...selectedIds])
|
||||||
|
|
||||||
// Move to the new page
|
// Move to the new page
|
||||||
data.currentPageId = toPageId
|
data.currentPageId = toPageId
|
||||||
|
@ -113,7 +113,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
delete fromPage.shapes[shape.id]
|
delete fromPage.shapes[shape.id]
|
||||||
})
|
})
|
||||||
|
|
||||||
tld.getPageState(data).selectedIds.clear()
|
tld.setSelectedIds(data, [])
|
||||||
|
|
||||||
storage.savePage(data, data.document.id, fromPageId)
|
storage.savePage(data, data.document.id, fromPageId)
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ export default function moveToPageCommand(data: Data, newPageId: string): void {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
tld.getPageState(data).selectedIds = new Set(selectedIds)
|
tld.setSelectedIds(data, [...selectedIds])
|
||||||
|
|
||||||
data.currentPageId = toPageId
|
data.currentPageId = toPageId
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, MoveType, Shape } from 'types'
|
import { Data, MoveType, Shape } from 'types'
|
||||||
import { setToArray } from 'utils'
|
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function moveCommand(data: Data, type: MoveType): void {
|
export default function moveCommand(data: Data, type: MoveType): void {
|
||||||
const page = tld.getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
const selectedIds = [...tld.getSelectedIds(data)]
|
||||||
|
|
||||||
const initialIndices = Object.fromEntries(
|
const initialIndices = Object.fromEntries(
|
||||||
selectedIds.map((id) => [id, page.shapes[id].childIndex])
|
selectedIds.map((id) => [id, page.shapes[id].childIndex])
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Command from './command'
|
import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, Shape } from 'types'
|
import { Data, Shape } from 'types'
|
||||||
import { getCommonBounds, setToArray } from 'utils'
|
import { getCommonBounds } from 'utils'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import { uniqueId } from 'utils/utils'
|
import { uniqueId } from 'utils/utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
|
@ -26,7 +26,7 @@ export default function pasteCommand(data: Data, initialShapes: Shape[]): void {
|
||||||
initialShapes.map((shape) => [shape.id, uniqueId()])
|
initialShapes.map((shape) => [shape.id, uniqueId()])
|
||||||
)
|
)
|
||||||
|
|
||||||
const oldSelectedIds = setToArray(tld.getSelectedIds(data))
|
const oldSelectedIds = [...tld.getSelectedIds(data)]
|
||||||
|
|
||||||
history.execute(
|
history.execute(
|
||||||
data,
|
data,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Command from './command'
|
||||||
import history from '../history'
|
import history from '../history'
|
||||||
import { Data, ShapeStyles } from 'types'
|
import { Data, ShapeStyles } from 'types'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import { deepClone, setToArray } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
|
|
||||||
export default function styleCommand(
|
export default function styleCommand(
|
||||||
|
@ -11,7 +11,7 @@ export default function styleCommand(
|
||||||
): void {
|
): void {
|
||||||
const page = tld.getPage(data)
|
const page = tld.getPage(data)
|
||||||
|
|
||||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
const selectedIds = [...tld.getSelectedIds(data)]
|
||||||
|
|
||||||
const shapesToStyle = selectedIds
|
const shapesToStyle = selectedIds
|
||||||
.flatMap((id) => tld.getDocumentBranch(data, id))
|
.flatMap((id) => tld.getDocumentBranch(data, id))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { DrawShape, PointerInfo } from 'types'
|
import { DrawShape, PointerInfo } from 'types'
|
||||||
import { deepClone, setToArray } from 'utils'
|
import { deepClone } from 'utils'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import { freeze } from 'immer'
|
import { freeze } from 'immer'
|
||||||
import session from './session'
|
import session from './session'
|
||||||
|
@ -56,7 +56,7 @@ export function fastDrawUpdate(info: PointerInfo): void {
|
||||||
info.shiftKey
|
info.shiftKey
|
||||||
)
|
)
|
||||||
|
|
||||||
const selectedId = setToArray(tld.getSelectedIds(data))[0]
|
const selectedId = [...tld.getSelectedIds(data)][0]
|
||||||
|
|
||||||
const { shapes } = data.document.pages[data.currentPageId]
|
const { shapes } = data.document.pages[data.currentPageId]
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { Data } from 'types'
|
||||||
import clipboard from './clipboard'
|
import clipboard from './clipboard'
|
||||||
import state from './state'
|
import state from './state'
|
||||||
import { isDraft, current } from 'immer'
|
import { isDraft, current } from 'immer'
|
||||||
import { setToArray } from 'utils'
|
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
import inputs from './inputs'
|
import inputs from './inputs'
|
||||||
|
|
||||||
|
@ -59,9 +58,9 @@ class Logger {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.snapshotStart.pageStates[data.currentPageId].selectedIds = setToArray(
|
this.snapshotStart.pageStates[data.currentPageId].selectedIds = [
|
||||||
tld.getSelectedIds(data)
|
...tld.getSelectedIds(data),
|
||||||
)
|
]
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -84,9 +83,9 @@ class Logger {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.snapshotEnd.pageStates[data.currentPageId].selectedIds = setToArray(
|
this.snapshotEnd.pageStates[data.currentPageId].selectedIds = [
|
||||||
tld.getSelectedIds(data)
|
...tld.getSelectedIds(data),
|
||||||
)
|
]
|
||||||
|
|
||||||
// if (window.confirm('Stopped logging. Copy to clipboard?')) {
|
// if (window.confirm('Stopped logging. Copy to clipboard?')) {
|
||||||
// this.copyToJson()
|
// this.copyToJson()
|
||||||
|
@ -138,9 +137,9 @@ class Logger {
|
||||||
this.isSimulating = true
|
this.isSimulating = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data.pageStates[data.currentPageId].selectedIds = new Set(
|
data.pageStates[data.currentPageId].selectedIds = [
|
||||||
start.pageStates[start.currentPageId].selectedIds
|
...start.pageStates[start.currentPageId].selectedIds,
|
||||||
)
|
]
|
||||||
|
|
||||||
state.send('RESET_DOCUMENT_STATE').forceData(start)
|
state.send('RESET_DOCUMENT_STATE').forceData(start)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Bounds, Data, ShapeType } from 'types'
|
import { Bounds, Data, ShapeType } from 'types'
|
||||||
import BaseSession from './base-session'
|
import BaseSession from './base-session'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import { deepClone, getBoundsFromPoints, setToArray } from 'utils'
|
import { deepClone, getBoundsFromPoints } from 'utils'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import tld from 'utils/tld'
|
import tld from 'utils/tld'
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@ export default class BrushSession extends BaseSession {
|
||||||
|
|
||||||
const hits = new Set<string>([])
|
const hits = new Set<string>([])
|
||||||
|
|
||||||
const selectedIds = new Set(snapshot.selectedIds)
|
const selectedIds = [...snapshot.selectedIds]
|
||||||
|
|
||||||
for (const id in snapshot.shapeHitTests) {
|
for (const id in snapshot.shapeHitTests) {
|
||||||
if (selectedIds.has(id)) continue
|
if (selectedIds.includes(id)) continue
|
||||||
|
|
||||||
const { test, selectId } = snapshot.shapeHitTests[id]
|
const { test, selectId } = snapshot.shapeHitTests[id]
|
||||||
if (!hits.has(selectId)) {
|
if (!hits.has(selectId)) {
|
||||||
|
@ -35,11 +35,11 @@ export default class BrushSession extends BaseSession {
|
||||||
hits.add(selectId)
|
hits.add(selectId)
|
||||||
|
|
||||||
// When brushing a shape, select its top group parent.
|
// When brushing a shape, select its top group parent.
|
||||||
if (!selectedIds.has(selectId)) {
|
if (!selectedIds.includes(selectId)) {
|
||||||
selectedIds.add(selectId)
|
selectedIds.push(selectId)
|
||||||
}
|
}
|
||||||
} else if (selectedIds.has(selectId)) {
|
} else if (selectedIds.includes(selectId)) {
|
||||||
selectedIds.delete(selectId)
|
selectedIds.splice(selectedIds.indexOf(selectId), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,12 +73,15 @@ export function getBrushSnapshot(data: Data) {
|
||||||
.getShapes(cData)
|
.getShapes(cData)
|
||||||
.filter((shape) => shape.type !== ShapeType.Group && !shape.isHidden)
|
.filter((shape) => shape.type !== ShapeType.Group && !shape.isHidden)
|
||||||
.filter(
|
.filter(
|
||||||
(shape) => !(selectedIds.has(shape.id) || selectedIds.has(shape.parentId))
|
(shape) =>
|
||||||
|
!(
|
||||||
|
selectedIds.includes(shape.id) || selectedIds.includes(shape.parentId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.map(deepClone)
|
.map(deepClone)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedIds: setToArray(selectedIds),
|
selectedIds: [...selectedIds],
|
||||||
shapeHitTests: Object.fromEntries(
|
shapeHitTests: Object.fromEntries(
|
||||||
shapesToTest.map((shape) => {
|
shapesToTest.map((shape) => {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -133,22 +133,21 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
|
|
||||||
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
transform(shape, bounds, { initialShape, transformOrigin, scaleX, scaleY }) {
|
||||||
if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
|
if (shape.rotation === 0 && !shape.isAspectRatioLocked) {
|
||||||
shape.size = [bounds.width, bounds.height]
|
shape.size = vec.round([bounds.width, bounds.height])
|
||||||
shape.point = [bounds.minX, bounds.minY]
|
shape.point = vec.round([bounds.minX, bounds.minY])
|
||||||
} else {
|
} else {
|
||||||
shape.size = vec.mul(
|
shape.size = vec.round(
|
||||||
initialShape.size,
|
vec.mul(initialShape.size, Math.min(Math.abs(scaleX), Math.abs(scaleY)))
|
||||||
Math.min(Math.abs(scaleX), Math.abs(scaleY))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
shape.point = [
|
shape.point = vec.round([
|
||||||
bounds.minX +
|
bounds.minX +
|
||||||
(bounds.width - shape.size[0]) *
|
(bounds.width - shape.size[0]) *
|
||||||
(scaleX < 0 ? 1 - transformOrigin[0] : transformOrigin[0]),
|
(scaleX < 0 ? 1 - transformOrigin[0] : transformOrigin[0]),
|
||||||
bounds.minY +
|
bounds.minY +
|
||||||
(bounds.height - shape.size[1]) *
|
(bounds.height - shape.size[1]) *
|
||||||
(scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
|
(scaleY < 0 ? 1 - transformOrigin[1] : transformOrigin[1]),
|
||||||
]
|
])
|
||||||
|
|
||||||
shape.rotation =
|
shape.rotation =
|
||||||
(scaleX < 0 && scaleY >= 0) || (scaleY < 0 && scaleX >= 0)
|
(scaleX < 0 && scaleY >= 0) || (scaleY < 0 && scaleX >= 0)
|
||||||
|
@ -160,8 +159,8 @@ const rectangle = registerShapeUtils<RectangleShape>({
|
||||||
},
|
},
|
||||||
|
|
||||||
transformSingle(shape, bounds) {
|
transformSingle(shape, bounds) {
|
||||||
shape.size = [bounds.width, bounds.height]
|
shape.size = vec.round([bounds.width, bounds.height])
|
||||||
shape.point = [bounds.minX, bounds.minY]
|
shape.point = vec.round([bounds.minX, bounds.minY])
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,7 +13,6 @@ import {
|
||||||
getCommonBounds,
|
getCommonBounds,
|
||||||
rotateBounds,
|
rotateBounds,
|
||||||
getBoundsCenter,
|
getBoundsCenter,
|
||||||
setToArray,
|
|
||||||
deepClone,
|
deepClone,
|
||||||
pointInBounds,
|
pointInBounds,
|
||||||
uniqueId,
|
uniqueId,
|
||||||
|
@ -43,6 +42,7 @@ const initialData: Data = {
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
settings: {
|
settings: {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
isTestMode: false,
|
||||||
isDarkMode: false,
|
isDarkMode: false,
|
||||||
isCodeOpen: false,
|
isCodeOpen: false,
|
||||||
isDebugMode: false,
|
isDebugMode: false,
|
||||||
|
@ -133,7 +133,7 @@ for (let i = 0; i < count; i++) {
|
||||||
pageStates: {
|
pageStates: {
|
||||||
page1: {
|
page1: {
|
||||||
id: 'page1',
|
id: 'page1',
|
||||||
selectedIds: new Set([]),
|
selectedIds: [],
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
|
@ -147,6 +147,7 @@ const state = createState({
|
||||||
on: {
|
on: {
|
||||||
TOGGLED_DEBUG_PANEL: 'toggleDebugPanel',
|
TOGGLED_DEBUG_PANEL: 'toggleDebugPanel',
|
||||||
TOGGLED_DEBUG_MODE: 'toggleDebugMode',
|
TOGGLED_DEBUG_MODE: 'toggleDebugMode',
|
||||||
|
TOGGLED_TEST_MODE: 'toggleTestMode',
|
||||||
TOGGLED_LOGGER: 'toggleLogger',
|
TOGGLED_LOGGER: 'toggleLogger',
|
||||||
COPIED_DEBUG_LOG: 'copyDebugLog',
|
COPIED_DEBUG_LOG: 'copyDebugLog',
|
||||||
LOADED_FROM_SNAPSHOT: {
|
LOADED_FROM_SNAPSHOT: {
|
||||||
|
@ -588,7 +589,10 @@ const state = createState({
|
||||||
onEnter: 'startTransformSession',
|
onEnter: 'startTransformSession',
|
||||||
onExit: 'completeSession',
|
onExit: 'completeSession',
|
||||||
on: {
|
on: {
|
||||||
// MOVED_POINTER: 'updateTransformSession', using hacks.fastTransform
|
MOVED_POINTER: {
|
||||||
|
ifAny: ['isSimulating', 'isTestMode'],
|
||||||
|
do: 'updateTransformSession',
|
||||||
|
},
|
||||||
PANNED_CAMERA: 'updateTransformSession',
|
PANNED_CAMERA: 'updateTransformSession',
|
||||||
PRESSED_SHIFT_KEY: 'keyUpdateTransformSession',
|
PRESSED_SHIFT_KEY: 'keyUpdateTransformSession',
|
||||||
RELEASED_SHIFT_KEY: 'keyUpdateTransformSession',
|
RELEASED_SHIFT_KEY: 'keyUpdateTransformSession',
|
||||||
|
@ -638,7 +642,7 @@ const state = createState({
|
||||||
'startBrushSession',
|
'startBrushSession',
|
||||||
],
|
],
|
||||||
on: {
|
on: {
|
||||||
// MOVED_POINTER: 'updateBrushSession', using hacks.fastBrushSelect
|
MOVED_POINTER: { if: 'isTestMode', do: 'updateBrushSession' },
|
||||||
PANNED_CAMERA: 'updateBrushSession',
|
PANNED_CAMERA: 'updateBrushSession',
|
||||||
STOPPED_POINTING: { to: 'selecting' },
|
STOPPED_POINTING: { to: 'selecting' },
|
||||||
STARTED_PINCHING: { to: 'pinching' },
|
STARTED_PINCHING: { to: 'pinching' },
|
||||||
|
@ -694,7 +698,7 @@ const state = createState({
|
||||||
},
|
},
|
||||||
pinching: {
|
pinching: {
|
||||||
on: {
|
on: {
|
||||||
// PINCHED: { do: 'pinchCamera' }, using hacks.fastPinchCamera
|
PINCHED: { if: 'isTestMode', do: 'pinchCamera' },
|
||||||
},
|
},
|
||||||
initial: 'selectPinching',
|
initial: 'selectPinching',
|
||||||
onExit: { secretlyDo: 'updateZoomCSS' },
|
onExit: { secretlyDo: 'updateZoomCSS' },
|
||||||
|
@ -760,7 +764,7 @@ const state = createState({
|
||||||
RELEASED_SHIFT: 'keyUpdateDrawSession',
|
RELEASED_SHIFT: 'keyUpdateDrawSession',
|
||||||
PANNED_CAMERA: 'updateDrawSession',
|
PANNED_CAMERA: 'updateDrawSession',
|
||||||
MOVED_POINTER: {
|
MOVED_POINTER: {
|
||||||
if: 'isSimulating',
|
ifAny: ['isSimulating', 'isTestMode'],
|
||||||
do: 'updateDrawSession',
|
do: 'updateDrawSession',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1115,6 +1119,9 @@ const state = createState({
|
||||||
isSimulating() {
|
isSimulating() {
|
||||||
return logger.isSimulating
|
return logger.isSimulating
|
||||||
},
|
},
|
||||||
|
isTestMode(data) {
|
||||||
|
return data.settings.isTestMode
|
||||||
|
},
|
||||||
isEditingShape(data, payload: { id: string }) {
|
isEditingShape(data, payload: { id: string }) {
|
||||||
return payload.id === data.editingId
|
return payload.id === data.editingId
|
||||||
},
|
},
|
||||||
|
@ -1131,7 +1138,7 @@ const state = createState({
|
||||||
return tld.getShape(data, payload.target)?.type === ShapeType.Text
|
return tld.getShape(data, payload.target)?.type === ShapeType.Text
|
||||||
},
|
},
|
||||||
isPointingBounds(data, payload: PointerInfo) {
|
isPointingBounds(data, payload: PointerInfo) {
|
||||||
return tld.getSelectedIds(data).size > 0 && payload.target === 'bounds'
|
return tld.getSelectedIds(data).length > 0 && payload.target === 'bounds'
|
||||||
},
|
},
|
||||||
isPointingShape(data, payload: PointerInfo) {
|
isPointingShape(data, payload: PointerInfo) {
|
||||||
return (
|
return (
|
||||||
|
@ -1156,7 +1163,7 @@ const state = createState({
|
||||||
return payload.target !== undefined
|
return payload.target !== undefined
|
||||||
},
|
},
|
||||||
isPointedShapeSelected(data) {
|
isPointedShapeSelected(data) {
|
||||||
return tld.getSelectedIds(data).has(data.pointedId)
|
return tld.getSelectedIds(data).includes(data.pointedId)
|
||||||
},
|
},
|
||||||
isPressingShiftKey(data, payload: PointerInfo) {
|
isPressingShiftKey(data, payload: PointerInfo) {
|
||||||
return payload.shiftKey
|
return payload.shiftKey
|
||||||
|
@ -1192,13 +1199,13 @@ const state = createState({
|
||||||
return payload.target === 'rotate'
|
return payload.target === 'rotate'
|
||||||
},
|
},
|
||||||
hasSelection(data) {
|
hasSelection(data) {
|
||||||
return tld.getSelectedIds(data).size > 0
|
return tld.getSelectedIds(data).length > 0
|
||||||
},
|
},
|
||||||
hasSingleSelection(data) {
|
hasSingleSelection(data) {
|
||||||
return tld.getSelectedIds(data).size === 1
|
return tld.getSelectedIds(data).length === 1
|
||||||
},
|
},
|
||||||
hasMultipleSelection(data) {
|
hasMultipleSelection(data) {
|
||||||
return tld.getSelectedIds(data).size > 1
|
return tld.getSelectedIds(data).length > 1
|
||||||
},
|
},
|
||||||
hasCurrentParentShape(data) {
|
hasCurrentParentShape(data) {
|
||||||
return data.currentParentId !== data.currentPageId
|
return data.currentParentId !== data.currentPageId
|
||||||
|
@ -1230,6 +1237,9 @@ const state = createState({
|
||||||
toggleDebugMode(data) {
|
toggleDebugMode(data) {
|
||||||
data.settings.isDebugMode = !data.settings.isDebugMode
|
data.settings.isDebugMode = !data.settings.isDebugMode
|
||||||
},
|
},
|
||||||
|
toggleTestMode(data) {
|
||||||
|
data.settings.isTestMode = !data.settings.isTestMode
|
||||||
|
},
|
||||||
toggleDebugPanel(data) {
|
toggleDebugPanel(data) {
|
||||||
data.settings.isDebugOpen = !data.settings.isDebugOpen
|
data.settings.isDebugOpen = !data.settings.isDebugOpen
|
||||||
},
|
},
|
||||||
|
@ -1304,7 +1314,7 @@ const state = createState({
|
||||||
data.pageStates = {
|
data.pageStates = {
|
||||||
[newPageId]: {
|
[newPageId]: {
|
||||||
id: newPageId,
|
id: newPageId,
|
||||||
selectedIds: new Set(),
|
selectedIds: [],
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
|
@ -1464,7 +1474,7 @@ const state = createState({
|
||||||
|
|
||||||
// Handles
|
// Handles
|
||||||
doublePointHandle(data, payload: PointerInfo) {
|
doublePointHandle(data, payload: PointerInfo) {
|
||||||
const id = setToArray(tld.getSelectedIds(data))[0]
|
const id = tld.getSelectedIds(data)[0]
|
||||||
commands.doublePointHandle(data, id, payload)
|
commands.doublePointHandle(data, id, payload)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1511,7 +1521,7 @@ const state = createState({
|
||||||
) {
|
) {
|
||||||
const point = tld.screenToWorld(inputs.pointer.origin, data)
|
const point = tld.screenToWorld(inputs.pointer.origin, data)
|
||||||
session.begin(
|
session.begin(
|
||||||
tld.getSelectedIds(data).size === 1
|
tld.getSelectedIds(data).length === 1
|
||||||
? new Sessions.TransformSingleSession(data, payload.target, point)
|
? new Sessions.TransformSingleSession(data, payload.target, point)
|
||||||
: new Sessions.TransformSession(data, payload.target, point)
|
: new Sessions.TransformSession(data, payload.target, point)
|
||||||
)
|
)
|
||||||
|
@ -1618,7 +1628,7 @@ const state = createState({
|
||||||
inputs.clear()
|
inputs.clear()
|
||||||
},
|
},
|
||||||
deselectAll(data) {
|
deselectAll(data) {
|
||||||
tld.getSelectedIds(data).clear()
|
tld.setSelectedIds(data, [])
|
||||||
},
|
},
|
||||||
selectAll(data) {
|
selectAll(data) {
|
||||||
tld.setSelectedIds(
|
tld.setSelectedIds(
|
||||||
|
@ -1673,10 +1683,10 @@ const state = createState({
|
||||||
pullPointedIdFromSelectedIds(data) {
|
pullPointedIdFromSelectedIds(data) {
|
||||||
const { pointedId } = data
|
const { pointedId } = data
|
||||||
const selectedIds = tld.getSelectedIds(data)
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
selectedIds.delete(pointedId)
|
selectedIds.splice(selectedIds.indexOf(pointedId), 1)
|
||||||
},
|
},
|
||||||
pushPointedIdToSelectedIds(data) {
|
pushPointedIdToSelectedIds(data) {
|
||||||
tld.getSelectedIds(data).add(data.pointedId)
|
tld.getSelectedIds(data).push(data.pointedId)
|
||||||
},
|
},
|
||||||
moveSelection(data, payload: { type: MoveType }) {
|
moveSelection(data, payload: { type: MoveType }) {
|
||||||
commands.move(data, payload.type)
|
commands.move(data, payload.type)
|
||||||
|
@ -1732,7 +1742,7 @@ const state = createState({
|
||||||
data.editingId = selectedShape.id
|
data.editingId = selectedShape.id
|
||||||
}
|
}
|
||||||
|
|
||||||
tld.getPageState(data).selectedIds = new Set([selectedShape.id])
|
tld.getPageState(data).selectedIds = [selectedShape.id]
|
||||||
},
|
},
|
||||||
clearEditingId(data) {
|
clearEditingId(data) {
|
||||||
data.editingId = null
|
data.editingId = null
|
||||||
|
@ -2094,7 +2104,7 @@ const state = createState({
|
||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
selectedIds(data) {
|
selectedIds(data) {
|
||||||
return setToArray(tld.getSelectedIds(data))
|
return tld.getSelectedIds(data)
|
||||||
},
|
},
|
||||||
selectedBounds(data) {
|
selectedBounds(data) {
|
||||||
return getSelectionBounds(data)
|
return getSelectionBounds(data)
|
||||||
|
@ -2107,7 +2117,7 @@ const state = createState({
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
},
|
},
|
||||||
selectedStyle(data) {
|
selectedStyle(data) {
|
||||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
const selectedIds = tld.getSelectedIds(data)
|
||||||
const { currentStyle } = data
|
const { currentStyle } = data
|
||||||
|
|
||||||
if (selectedIds.length === 0) {
|
if (selectedIds.length === 0) {
|
||||||
|
@ -2195,9 +2205,9 @@ function getSelectionBounds(data: Data) {
|
||||||
|
|
||||||
const shapes = tld.getSelectedShapes(data)
|
const shapes = tld.getSelectedShapes(data)
|
||||||
|
|
||||||
if (selectedIds.size === 0) return null
|
if (selectedIds.length === 0) return null
|
||||||
|
|
||||||
if (selectedIds.size === 1) {
|
if (selectedIds.length === 1) {
|
||||||
if (!shapes[0]) {
|
if (!shapes[0]) {
|
||||||
console.warn('Could not find that shape! Clearing selected IDs.')
|
console.warn('Could not find that shape! Clearing selected IDs.')
|
||||||
tld.setSelectedIds(data, [])
|
tld.setSelectedIds(data, [])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Data, PageState, TLDocument } from 'types'
|
import { Data, PageState, TLDocument } from 'types'
|
||||||
import { decompress, compress, setToArray } from 'utils'
|
import { decompress, compress } from 'utils'
|
||||||
import state from './state'
|
import state from './state'
|
||||||
import { uniqueId } from 'utils/utils'
|
import { uniqueId } from 'utils/utils'
|
||||||
import * as idb from 'idb-keyval'
|
import * as idb from 'idb-keyval'
|
||||||
|
@ -132,14 +132,11 @@ class Storage {
|
||||||
if (savedPageState !== null) {
|
if (savedPageState !== null) {
|
||||||
// If we've found a page state in local storage, set it into state.
|
// If we've found a page state in local storage, set it into state.
|
||||||
data.pageStates[pageId] = JSON.parse(decompress(savedPageState))
|
data.pageStates[pageId] = JSON.parse(decompress(savedPageState))
|
||||||
data.pageStates[pageId].selectedIds = new Set(
|
|
||||||
data.pageStates[pageId].selectedIds
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// Or else create a new one.
|
// Or else create a new one.
|
||||||
data.pageStates[pageId] = {
|
data.pageStates[pageId] = {
|
||||||
id: pageId,
|
id: pageId,
|
||||||
selectedIds: new Set([]),
|
selectedIds: [],
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
|
@ -161,13 +158,13 @@ class Storage {
|
||||||
throw new Error('Page state id not in document')
|
throw new Error('Page state id not in document')
|
||||||
}
|
}
|
||||||
|
|
||||||
pageState.selectedIds = new Set([])
|
pageState.selectedIds = []
|
||||||
data.pageStates[pageState.id] = pageState
|
data.pageStates[pageState.id] = pageState
|
||||||
data.currentPageId = pageState.id
|
data.currentPageId = pageState.id
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
data.pageStates[data.currentPageId] = {
|
data.pageStates[data.currentPageId] = {
|
||||||
id: data.currentPageId,
|
id: data.currentPageId,
|
||||||
selectedIds: new Set([]),
|
selectedIds: [],
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
|
@ -249,7 +246,7 @@ class Storage {
|
||||||
storageId(fileId, 'pageState', pageId),
|
storageId(fileId, 'pageState', pageId),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
...currentPageState,
|
...currentPageState,
|
||||||
selectedIds: setToArray(currentPageState.selectedIds),
|
selectedIds: [...currentPageState.selectedIds],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -286,7 +283,6 @@ class Storage {
|
||||||
// If we have a page, move it into state
|
// If we have a page, move it into state
|
||||||
const restored: PageState = JSON.parse(savedPageState)
|
const restored: PageState = JSON.parse(savedPageState)
|
||||||
data.pageStates[pageId] = restored
|
data.pageStates[pageId] = restored
|
||||||
data.pageStates[pageId].selectedIds = new Set(restored.selectedIds)
|
|
||||||
} else {
|
} else {
|
||||||
data.pageStates[pageId] = {
|
data.pageStates[pageId] = {
|
||||||
id: pageId,
|
id: pageId,
|
||||||
|
@ -294,7 +290,7 @@ class Storage {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
},
|
},
|
||||||
selectedIds: new Set([]),
|
selectedIds: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
types.ts
3
types.ts
|
@ -8,6 +8,7 @@ export interface Data {
|
||||||
fontSize: number
|
fontSize: number
|
||||||
isDarkMode: boolean
|
isDarkMode: boolean
|
||||||
isCodeOpen: boolean
|
isCodeOpen: boolean
|
||||||
|
isTestMode: boolean
|
||||||
isDebugOpen: boolean
|
isDebugOpen: boolean
|
||||||
isDebugMode: boolean
|
isDebugMode: boolean
|
||||||
isStyleOpen: boolean
|
isStyleOpen: boolean
|
||||||
|
@ -66,7 +67,7 @@ export interface Page {
|
||||||
|
|
||||||
export interface PageState {
|
export interface PageState {
|
||||||
id: string
|
id: string
|
||||||
selectedIds: Set<string>
|
selectedIds: string[]
|
||||||
camera: {
|
camera: {
|
||||||
point: number[]
|
point: number[]
|
||||||
zoom: number
|
zoom: number
|
||||||
|
|
12
utils/tld.ts
12
utils/tld.ts
|
@ -1,4 +1,4 @@
|
||||||
import { clamp, deepClone, getCommonBounds, setToArray } from 'utils'
|
import { clamp, deepClone, getCommonBounds } from 'utils'
|
||||||
import { getShapeUtils } from 'state/shape-utils'
|
import { getShapeUtils } from 'state/shape-utils'
|
||||||
import vec from './vec'
|
import vec from './vec'
|
||||||
import {
|
import {
|
||||||
|
@ -98,7 +98,7 @@ export default class StateUtils {
|
||||||
*/
|
*/
|
||||||
static getSelectedShapes(data: Data): Shape[] {
|
static getSelectedShapes(data: Data): Shape[] {
|
||||||
const page = this.getPage(data)
|
const page = this.getPage(data)
|
||||||
const ids = setToArray(this.getSelectedIds(data))
|
const ids = this.getSelectedIds(data)
|
||||||
return ids.map((id) => page.shapes[id])
|
return ids.map((id) => page.shapes[id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,12 +306,12 @@ export default class StateUtils {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSelectedIds(data: Data): Set<string> {
|
static getSelectedIds(data: Data): string[] {
|
||||||
return data.pageStates[data.currentPageId].selectedIds
|
return data.pageStates[data.currentPageId].selectedIds
|
||||||
}
|
}
|
||||||
|
|
||||||
static setSelectedIds(data: Data, ids: string[]): Set<string> {
|
static setSelectedIds(data: Data, ids: string[]): string[] {
|
||||||
data.pageStates[data.currentPageId].selectedIds = new Set(ids)
|
data.pageStates[data.currentPageId].selectedIds = [...ids]
|
||||||
return data.pageStates[data.currentPageId].selectedIds
|
return data.pageStates[data.currentPageId].selectedIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ export default class StateUtils {
|
||||||
>(data: Data, fn?: F): (Shape | K)[] {
|
>(data: Data, fn?: F): (Shape | K)[] {
|
||||||
const page = this.getPage(data)
|
const page = this.getPage(data)
|
||||||
|
|
||||||
const copies = setToArray(this.getSelectedIds(data))
|
const copies = this.getSelectedIds(data)
|
||||||
.flatMap((id) =>
|
.flatMap((id) =>
|
||||||
this.getDocumentBranch(data, id).map((id) => page.shapes[id])
|
this.getDocumentBranch(data, id).map((id) => page.shapes[id])
|
||||||
)
|
)
|
||||||
|
|
|
@ -1572,14 +1572,6 @@ export function uniqueArray<T extends string | number>(...items: T[]): T[] {
|
||||||
return Array.from(new Set(items).values())
|
return Array.from(new Set(items).values())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a set to an array.
|
|
||||||
* @param set
|
|
||||||
*/
|
|
||||||
export function setToArray<T>(set: Set<T>): T[] {
|
|
||||||
return Array.from(set.values())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
/* Browser and DOM */
|
/* Browser and DOM */
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
|
|
Loading…
Reference in a new issue