mirror of
https://github.com/idrawjs/idraw
synced 2026-05-24 10:08:34 +00:00
test: update unit test for v1.x
This commit is contained in:
parent
3ce0043d07
commit
49ed490020
38 changed files with 2157 additions and 3540 deletions
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -21,8 +21,8 @@ jobs:
|
|||
- run: npm run test
|
||||
- run: npm run build
|
||||
- run: npm run version:reset-for-release
|
||||
# - run: npm publish --provenance --access public -w ./packages/types --tag next
|
||||
- run: npm publish --provenance --access public -w ./packages/types
|
||||
- run: npm publish --provenance --access public -w ./packages/types --tag next
|
||||
# - run: npm publish --provenance --access public -w ./packages/types
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- run: npm publish --provenance --access public -w ./packages/util
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,171 +0,0 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, findElementFromListByPosition } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
}),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateElement', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
|
||||
// modify 1: do
|
||||
const newElement1 = idraw.createElement('rect', {
|
||||
x: 22,
|
||||
y: 33,
|
||||
h: 300,
|
||||
w: 400,
|
||||
name: 'new element 001',
|
||||
detail: {
|
||||
background: '#666666'
|
||||
}
|
||||
});
|
||||
const position = [1, 2];
|
||||
idraw.addElement(newElement1, {
|
||||
position
|
||||
});
|
||||
const record1 = {
|
||||
type: 'addElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: newElement1.uuid,
|
||||
position: [...position],
|
||||
element: deepClone(newElement1)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(newElement1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const newElement2 = idraw.createElement('text', {
|
||||
x: 22,
|
||||
y: 33,
|
||||
h: 300,
|
||||
w: 400,
|
||||
name: 'new element 002',
|
||||
detail: {
|
||||
text: 'Hello Element'
|
||||
}
|
||||
});
|
||||
idraw.addElement(newElement2, { position });
|
||||
const record2 = {
|
||||
type: 'addElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: newElement2.uuid,
|
||||
position: [...position],
|
||||
element: deepClone(newElement2)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(newElement2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: record2.content.uuid,
|
||||
position: deepClone(record2.content.position),
|
||||
element: deepClone(record2.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(newElement1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: record1.content.uuid,
|
||||
position: deepClone(record1.content.position),
|
||||
element: deepClone(record1.content.element)
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: record4.content.uuid,
|
||||
position: record4.content.position,
|
||||
element: deepClone(record4.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(newElement1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: record3.content.uuid,
|
||||
position: record3.content.position,
|
||||
element: deepClone(record3.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(newElement2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
161
packages/idraw/__tests__/history-addMaterial.test.ts
Normal file
161
packages/idraw/__tests__/history-addMaterial.test.ts
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import { iDraw, useHistory, deepClone, createMaterial, findMaterialFromListByPosition } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
|
||||
// modify 1: do
|
||||
const newMaterial1 = idraw.createMaterial('rect', {
|
||||
x: 22,
|
||||
y: 33,
|
||||
height: 300,
|
||||
width: 400,
|
||||
name: 'new material 001',
|
||||
fill: '#666666',
|
||||
});
|
||||
const position = [1, 2];
|
||||
idraw.addMaterial(newMaterial1, {
|
||||
position,
|
||||
});
|
||||
const record1 = {
|
||||
type: 'addMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: newMaterial1.id,
|
||||
position: [...position],
|
||||
material: deepClone(newMaterial1),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(newMaterial1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const newMaterial2 = idraw.createMaterial('text', {
|
||||
x: 22,
|
||||
y: 33,
|
||||
height: 300,
|
||||
width: 400,
|
||||
name: 'new material 002',
|
||||
text: 'Hello Material',
|
||||
});
|
||||
idraw.addMaterial(newMaterial2, { position });
|
||||
const record2 = {
|
||||
type: 'addMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: newMaterial2.id,
|
||||
position: [...position],
|
||||
material: deepClone(newMaterial2),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(newMaterial2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: record2.content.id,
|
||||
position: deepClone(record2.content.position),
|
||||
material: deepClone(record2.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(newMaterial1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: record1.content.id,
|
||||
position: deepClone(record1.content.position),
|
||||
material: deepClone(record1.content.material),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: record4.content.id,
|
||||
position: record4.content.position,
|
||||
material: deepClone(record4.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(newMaterial1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: record3.content.id,
|
||||
position: record3.content.position,
|
||||
material: deepClone(record3.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(newMaterial2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, findElementFromListByPosition } from 'idraw';
|
||||
import type { Element } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-000',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
}),
|
||||
createElement('group', {
|
||||
uuid: 'test-001',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('image', { uuid: 'test-001-000', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('circle', { uuid: 'test-001-001' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-001-002',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-001-003', detail: { src: 'https://example.com/002.png' } }),
|
||||
createElement('rect', { uuid: 'test-001-004' }),
|
||||
createElement('circle', { uuid: 'test-001-005' })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateElement', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
|
||||
const position = [1, 2];
|
||||
const nextPosition = [1, 3];
|
||||
|
||||
// modify 1: do
|
||||
const deletedElem1 = deepClone(findElementFromListByPosition(position, data.elements) as Element);
|
||||
const expectedElem1 = deepClone(findElementFromListByPosition(nextPosition, data.elements) as Element);
|
||||
idraw.deleteElement(deletedElem1?.uuid);
|
||||
const record1 = {
|
||||
type: 'deleteElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: deletedElem1.uuid,
|
||||
position: [...position],
|
||||
element: deepClone(deletedElem1)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(expectedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const deletedElem2 = deepClone(findElementFromListByPosition(position, data.elements) as Element);
|
||||
const expectedElem2 = deepClone(findElementFromListByPosition(nextPosition, data.elements) as Element);
|
||||
idraw.deleteElement(deletedElem2?.uuid);
|
||||
const record2 = {
|
||||
type: 'deleteElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: deletedElem2.uuid,
|
||||
position: [...position],
|
||||
element: deepClone(deletedElem2)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(expectedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: record2.content.uuid,
|
||||
position: deepClone(record2.content.position),
|
||||
element: deepClone(record2.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(deletedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addElement',
|
||||
uuid: record1.content.uuid,
|
||||
position: deepClone(record1.content.position),
|
||||
element: deepClone(record1.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(deletedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: record4.content.uuid,
|
||||
position: record4.content.position,
|
||||
element: deepClone(record4.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(expectedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteElement',
|
||||
uuid: record3.content.uuid,
|
||||
position: record3.content.position,
|
||||
element: deepClone(record3.content.element)
|
||||
}
|
||||
};
|
||||
expect(findElementFromListByPosition(position, idraw.getData()?.elements || [])).toStrictEqual(expectedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
152
packages/idraw/__tests__/history-deleteMaterial.test.ts
Normal file
152
packages/idraw/__tests__/history-deleteMaterial.test.ts
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import { iDraw, useHistory, deepClone, createMaterial, findMaterialFromListByPosition } from 'idraw';
|
||||
import type { Material } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-000',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createMaterial('group', {
|
||||
id: 'test-001',
|
||||
children: [
|
||||
createMaterial('image', { id: 'test-001-000', src: 'https://example.com/001.png' }),
|
||||
createMaterial('circle', { id: 'test-001-001' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-001-002',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-001-003', src: 'https://example.com/002.png' }),
|
||||
createMaterial('rect', { id: 'test-001-004' }),
|
||||
createMaterial('circle', { id: 'test-001-005' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
|
||||
const position = [1, 2];
|
||||
const nextPosition = [1, 3];
|
||||
|
||||
// modify 1: do
|
||||
const deletedElem1 = deepClone(findMaterialFromListByPosition(position, data.materials) as Material);
|
||||
const expectedElem1 = deepClone(findMaterialFromListByPosition(nextPosition, data.materials) as Material);
|
||||
idraw.deleteMaterial(deletedElem1?.id);
|
||||
const record1 = {
|
||||
type: 'deleteMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: deletedElem1.id,
|
||||
position: [...position],
|
||||
material: deepClone(deletedElem1),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(expectedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const deletedElem2 = deepClone(findMaterialFromListByPosition(position, data.materials) as Material);
|
||||
const expectedElem2 = deepClone(findMaterialFromListByPosition(nextPosition, data.materials) as Material);
|
||||
idraw.deleteMaterial(deletedElem2?.id);
|
||||
const record2 = {
|
||||
type: 'deleteMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: deletedElem2.id,
|
||||
position: [...position],
|
||||
material: deepClone(deletedElem2),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(expectedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: record2.content.id,
|
||||
position: deepClone(record2.content.position),
|
||||
material: deepClone(record2.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(deletedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'addMaterial',
|
||||
id: record1.content.id,
|
||||
position: deepClone(record1.content.position),
|
||||
material: deepClone(record1.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(deletedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: record4.content.id,
|
||||
position: record4.content.position,
|
||||
material: deepClone(record4.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(expectedElem1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'deleteMaterial',
|
||||
id: record3.content.id,
|
||||
position: record3.content.position,
|
||||
material: deepClone(record3.content.material),
|
||||
},
|
||||
};
|
||||
expect(findMaterialFromListByPosition(position, idraw.getData()?.materials || [])).toStrictEqual(expectedElem2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,44 +1,36 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, set, get, toFlattenGlobal } from 'idraw';
|
||||
import { iDraw, useHistory, deepClone, createMaterial, set, get, toFlattenGlobal } from 'idraw';
|
||||
import type { Data, DataGlobal, RecursivePartial } from 'idraw';
|
||||
|
||||
const createData = () =>
|
||||
({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createElement('circle', { uuid: 'test-002' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-003',
|
||||
detail: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
createMaterial('circle', { id: 'test-002' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-003',
|
||||
text: 'Hello World',
|
||||
}),
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-006' }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-006' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
} as Data);
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
|
|
@ -52,7 +44,7 @@ describe('idraw: useHistory ', () => {
|
|||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
|
|
@ -61,14 +53,14 @@ describe('idraw: useHistory ', () => {
|
|||
|
||||
// modify 1: do
|
||||
const modifiedInfo1 = {
|
||||
background: '#123456'
|
||||
fill: '#123456',
|
||||
};
|
||||
idraw.modifyGlobal({
|
||||
...deepClone(modifiedInfo1)
|
||||
...deepClone(modifiedInfo1),
|
||||
});
|
||||
const expectedData1 = createData();
|
||||
const flattenModifiedInfo1 = toFlattenGlobal(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, any> | null = null;
|
||||
const beforeInfo1: Record<string, unknown> | null = null;
|
||||
const afterInfo1 = { ...flattenModifiedInfo1 };
|
||||
|
||||
Object.keys(flattenModifiedInfo1).forEach((k) => {
|
||||
|
|
@ -81,8 +73,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1
|
||||
}
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -90,14 +82,14 @@ describe('idraw: useHistory ', () => {
|
|||
|
||||
// modify 2: do
|
||||
const modifiedInfo2 = {
|
||||
background: '#AAAAAA'
|
||||
fill: '#AAAAAA',
|
||||
} as unknown as RecursivePartial<DataGlobal>;
|
||||
|
||||
idraw.modifyGlobal({ ...modifiedInfo2 });
|
||||
|
||||
const expectedData2 = deepClone(expectedData1);
|
||||
const flattenModifiedInfo2 = toFlattenGlobal(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, any> = {};
|
||||
const beforeInfo2: Record<string, unknown> = {};
|
||||
const afterInfo2 = { ...flattenModifiedInfo2 };
|
||||
|
||||
Object.keys(flattenModifiedInfo2).forEach((key) => {
|
||||
|
|
@ -110,8 +102,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2
|
||||
}
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
|
|
@ -125,8 +117,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before)
|
||||
}
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -140,8 +132,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before)
|
||||
}
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
|
|
@ -155,8 +147,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before)
|
||||
}
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
|
|
@ -170,8 +162,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyGlobal',
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before)
|
||||
}
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
|
|
|
|||
|
|
@ -1,44 +1,36 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, set, get, toFlattenLayout } from 'idraw';
|
||||
import { iDraw, useHistory, deepClone, createMaterial, set, get, toFlattenLayout } from 'idraw';
|
||||
import type { Data, DataLayout, RecursivePartial } from 'idraw';
|
||||
|
||||
const createData = () =>
|
||||
({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createElement('circle', { uuid: 'test-002' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-003',
|
||||
detail: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
createMaterial('circle', { id: 'test-002' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-003',
|
||||
text: 'Hello World',
|
||||
}),
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-006' }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-006' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
} as Data);
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
|
|
@ -52,7 +44,7 @@ describe('idraw: useHistory ', () => {
|
|||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
|
|
@ -63,19 +55,17 @@ describe('idraw: useHistory ', () => {
|
|||
const modifiedInfo1 = {
|
||||
x: 1,
|
||||
y: 2,
|
||||
w: 100,
|
||||
h: 200,
|
||||
detail: {
|
||||
background: '#123456',
|
||||
borderRadius: 3
|
||||
}
|
||||
width: 100,
|
||||
height: 200,
|
||||
fill: '#123456',
|
||||
cornerRadius: 3,
|
||||
};
|
||||
idraw.modifyLayout({
|
||||
...deepClone(modifiedInfo1)
|
||||
...deepClone(modifiedInfo1),
|
||||
});
|
||||
const expectedData1 = createData();
|
||||
const flattenModifiedInfo1 = toFlattenLayout(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, any> | null = null;
|
||||
const beforeInfo1: Record<string, unknown> | null = null;
|
||||
const afterInfo1 = { ...flattenModifiedInfo1 };
|
||||
|
||||
Object.keys(flattenModifiedInfo1).forEach((k) => {
|
||||
|
|
@ -88,8 +78,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1
|
||||
}
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -99,22 +89,20 @@ describe('idraw: useHistory ', () => {
|
|||
const modifiedInfo2 = {
|
||||
x: modifiedInfo1.x + 3,
|
||||
y: modifiedInfo1.y + 4,
|
||||
detail: {
|
||||
borderRadius: [2, 4, 6, 8]
|
||||
}
|
||||
cornerRadius: [2, 4, 6, 8],
|
||||
} as unknown as RecursivePartial<DataLayout>;
|
||||
|
||||
idraw.modifyLayout({ ...modifiedInfo2 });
|
||||
|
||||
const expectedData2 = deepClone(expectedData1);
|
||||
const flattenModifiedInfo2 = toFlattenLayout(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, any> = {};
|
||||
const beforeInfo2: Record<string, unknown> = {};
|
||||
const afterInfo2 = { ...flattenModifiedInfo2 };
|
||||
|
||||
Object.keys(flattenModifiedInfo2).forEach((key) => {
|
||||
let beforeVal = get(expectedData1.layout, key);
|
||||
let beforeKey = key;
|
||||
if (beforeVal === undefined && /(borderRadius|borderWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
if (beforeVal === undefined && /(cornerRadius|strokeWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
beforeKey = beforeKey.replace(/\[[0-9]{1,}\]$/, '');
|
||||
beforeVal = get(expectedData1.layout, beforeKey);
|
||||
}
|
||||
|
|
@ -127,8 +115,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2
|
||||
}
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
|
|
@ -142,8 +130,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before)
|
||||
}
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -157,8 +145,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before)
|
||||
}
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
|
|
@ -172,8 +160,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before)
|
||||
}
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
|
|
@ -187,8 +175,8 @@ describe('idraw: useHistory ', () => {
|
|||
content: {
|
||||
method: 'modifyLayout',
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before)
|
||||
}
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
|
|
|
|||
|
|
@ -1,43 +1,35 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, set, get, toFlattenElement } from 'idraw';
|
||||
import type { RecursivePartial, Element } from 'idraw';
|
||||
import { iDraw, useHistory, deepClone, createMaterial, set, get, toFlattenMaterial } from 'idraw';
|
||||
import type { RecursivePartial, Material } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createElement('circle', { uuid: 'test-002' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-003',
|
||||
detail: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
createMaterial('circle', { id: 'test-002' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-003',
|
||||
text: 'Hello World',
|
||||
}),
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-006' }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-006' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
|
|
@ -45,50 +37,48 @@ describe('idraw: useHistory ', () => {
|
|||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('modifyElement', () => {
|
||||
test('modifyMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
const targetElement = deepClone(data.elements[0]);
|
||||
const targetMaterial = deepClone(data.materials[0]);
|
||||
|
||||
// modify 1: do
|
||||
const modifiedInfo1 = {
|
||||
x: targetElement.x + 1,
|
||||
y: targetElement.y + 2,
|
||||
detail: {
|
||||
background: '#123456',
|
||||
borderRadius: 3
|
||||
}
|
||||
x: targetMaterial.x + 1,
|
||||
y: targetMaterial.y + 2,
|
||||
fill: '#123456',
|
||||
cornerRadius: 3,
|
||||
};
|
||||
idraw.modifyElement({
|
||||
uuid: targetElement.uuid,
|
||||
...deepClone(modifiedInfo1)
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo1),
|
||||
});
|
||||
const expectedData1 = createData();
|
||||
const flattenModifiedInfo1 = toFlattenElement(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, any> = {};
|
||||
const flattenModifiedInfo1 = toFlattenMaterial(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, unknown> = {};
|
||||
const afterInfo1 = { ...flattenModifiedInfo1 };
|
||||
Object.keys(flattenModifiedInfo1).forEach((key) => {
|
||||
beforeInfo1[key] = get(expectedData1.elements[0], key);
|
||||
set(expectedData1.elements[0], key, flattenModifiedInfo1[key]);
|
||||
beforeInfo1[key] = get(expectedData1.materials[0], key);
|
||||
set(expectedData1.materials[0], key, flattenModifiedInfo1[key]);
|
||||
});
|
||||
const record1 = {
|
||||
type: 'modifyElement',
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1
|
||||
}
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -98,40 +88,38 @@ describe('idraw: useHistory ', () => {
|
|||
const modifiedInfo2 = {
|
||||
x: modifiedInfo1.x + 3,
|
||||
y: modifiedInfo1.y + 4,
|
||||
detail: {
|
||||
borderRadius: [2, 4, 6, 8]
|
||||
}
|
||||
} as unknown as RecursivePartial<Omit<Element, 'uuid'>>;
|
||||
cornerRadius: [2, 4, 6, 8],
|
||||
} as unknown as RecursivePartial<Omit<Material, 'id'>>;
|
||||
|
||||
idraw.modifyElement({
|
||||
uuid: targetElement.uuid,
|
||||
...deepClone(modifiedInfo2)
|
||||
} as RecursivePartial<Omit<Element, 'uuid'>> & Pick<Element, 'uuid'>);
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo2),
|
||||
} as RecursivePartial<Omit<Material, 'id'>> & Pick<Material, 'id'>);
|
||||
|
||||
const expectedData2 = deepClone(expectedData1);
|
||||
const flattenModifiedInfo2 = toFlattenElement(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, any> = {};
|
||||
const flattenModifiedInfo2 = toFlattenMaterial(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, unknown> = {};
|
||||
const afterInfo2 = { ...flattenModifiedInfo2 };
|
||||
|
||||
Object.keys(flattenModifiedInfo2).forEach((key) => {
|
||||
let beforeVal = get(expectedData1.elements[0], key);
|
||||
let beforeVal = get(expectedData1.materials[0], key);
|
||||
let beforeKey = key;
|
||||
if (beforeVal === undefined && /(borderRadius|borderWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
if (beforeVal === undefined && /(cornerRadius|strokeWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
beforeKey = beforeKey.replace(/\[[0-9]{1,}\]$/, '');
|
||||
beforeVal = get(expectedData1.elements[0], beforeKey);
|
||||
beforeVal = get(expectedData1.materials[0], beforeKey);
|
||||
}
|
||||
beforeInfo2[beforeKey] = beforeVal;
|
||||
set(expectedData2.elements[0], key, flattenModifiedInfo2[key]);
|
||||
set(expectedData2.materials[0], key, flattenModifiedInfo2[key]);
|
||||
});
|
||||
const record2 = {
|
||||
type: 'modifyElement',
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2
|
||||
}
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
|
|
@ -143,11 +131,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before)
|
||||
}
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -159,11 +147,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before)
|
||||
}
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
|
|
@ -175,11 +163,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before)
|
||||
}
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
|
|
@ -191,11 +179,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before)
|
||||
}
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
222
packages/idraw/__tests__/history-modifyMaterials.test.ts
Normal file
222
packages/idraw/__tests__/history-modifyMaterials.test.ts
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import { iDraw, useHistory, deepClone, createMaterial, set, get, toFlattenMaterial } from 'idraw';
|
||||
import type { RecursivePartial, Material } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
materials: [
|
||||
createMaterial('group', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 2000,
|
||||
height: 2000,
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-002', x: 20, y: 20, width: 20, height: 20 }),
|
||||
createMaterial('circle', { id: 'test-003', x: 40, y: 40, width: 40, height: 40 }),
|
||||
createMaterial('text', {
|
||||
id: 'test-004',
|
||||
x: 60,
|
||||
y: 60,
|
||||
width: 60,
|
||||
height: 60,
|
||||
fontSize: 16,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-005',
|
||||
x: 80,
|
||||
y: 80,
|
||||
width: 80,
|
||||
height: 80,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
createMaterial('group', {
|
||||
id: 'test-101',
|
||||
x: 500,
|
||||
y: 500,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-102', x: 20, y: 20, width: 20, height: 20 }),
|
||||
createMaterial('circle', { id: 'test-103', x: 40, y: 40, width: 40, height: 40 }),
|
||||
createMaterial('text', {
|
||||
id: 'test-104',
|
||||
x: 60,
|
||||
y: 60,
|
||||
width: 60,
|
||||
height: 60,
|
||||
fontSize: 16,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-105',
|
||||
x: 80,
|
||||
y: 80,
|
||||
width: 80,
|
||||
height: 80,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('modifyMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
const targetMaterial = deepClone(data.materials[0]);
|
||||
|
||||
// modify 1: do
|
||||
const modifiedInfo1 = {
|
||||
x: targetMaterial.x + 1,
|
||||
y: targetMaterial.y + 2,
|
||||
fill: '#123456',
|
||||
cornerRadius: 3,
|
||||
};
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo1),
|
||||
});
|
||||
const expectedData1 = createData();
|
||||
const flattenModifiedInfo1 = toFlattenMaterial(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, unknown> = {};
|
||||
const afterInfo1 = { ...flattenModifiedInfo1 };
|
||||
Object.keys(flattenModifiedInfo1).forEach((key) => {
|
||||
beforeInfo1[key] = get(expectedData1.materials[0], key);
|
||||
set(expectedData1.materials[0], key, flattenModifiedInfo1[key]);
|
||||
});
|
||||
const record1 = {
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const modifiedInfo2 = {
|
||||
x: modifiedInfo1.x + 3,
|
||||
y: modifiedInfo1.y + 4,
|
||||
cornerRadius: [2, 4, 6, 8],
|
||||
} as unknown as RecursivePartial<Omit<Material, 'id'>>;
|
||||
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo2),
|
||||
} as RecursivePartial<Omit<Material, 'id'>> & Pick<Material, 'id'>);
|
||||
|
||||
const expectedData2 = deepClone(expectedData1);
|
||||
const flattenModifiedInfo2 = toFlattenMaterial(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, unknown> = {};
|
||||
const afterInfo2 = { ...flattenModifiedInfo2 };
|
||||
|
||||
Object.keys(flattenModifiedInfo2).forEach((key) => {
|
||||
let beforeVal = get(expectedData1.materials[0], key);
|
||||
let beforeKey = key;
|
||||
if (beforeVal === undefined && /(cornerRadius|strokeWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
beforeKey = beforeKey.replace(/\[[0-9]{1,}\]$/, '');
|
||||
beforeVal = get(expectedData1.materials[0], beforeKey);
|
||||
}
|
||||
beforeInfo2[beforeKey] = beforeVal;
|
||||
set(expectedData2.materials[0], key, flattenModifiedInfo2[key]);
|
||||
});
|
||||
const record2 = {
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,42 +1,39 @@
|
|||
import { iDraw, useHistory, findElementFromListByPosition, calcResultMovePosition } from 'idraw';
|
||||
import type { Elements } from 'idraw';
|
||||
import { iDraw, useHistory, findMaterialFromListByPosition, calcResultMovePosition } from 'idraw';
|
||||
import type { StrictMaterial } from 'idraw';
|
||||
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 1
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
||||
|
||||
function generateElements(list: any[]): Elements {
|
||||
const elements: Elements = list.map((item) => {
|
||||
function generateMaterials(list: any[]): StrictMaterial[] {
|
||||
const materials: StrictMaterial[] = list.map((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
const groupIds = item[0].split('-');
|
||||
groupIds.pop();
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: groupIds.join('-'),
|
||||
id: groupIds.join('-'),
|
||||
type: 'group',
|
||||
detail: {
|
||||
children: generateElements(item)
|
||||
}
|
||||
children: generateMaterials(item),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: item,
|
||||
id: item,
|
||||
type: 'rect',
|
||||
detail: {}
|
||||
};
|
||||
}
|
||||
}) as Elements;
|
||||
return elements;
|
||||
}) as StrictMaterial[];
|
||||
return materials;
|
||||
}
|
||||
|
||||
const createData = (list: any[]) => ({
|
||||
elements: generateElements(list)
|
||||
materials: generateMaterials(list),
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
|
|
@ -44,14 +41,14 @@ describe('idraw: useHistory ', () => {
|
|||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('moveElement', () => {
|
||||
test('moveMaterial', () => {
|
||||
const getList1 = () => ['0', '1', '2', ['3-0', '3-1', ['3-2-0', '3-2-1', '3-2-2', '3-2-3'], '3-3'], '4', '5'];
|
||||
const data = createData(getList1());
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
|
|
@ -62,69 +59,69 @@ describe('idraw: useHistory ', () => {
|
|||
const from1 = [3, 2, 1];
|
||||
const to1 = [2];
|
||||
// result from: [ 4, 2, 1 ], to: [ 2 ]
|
||||
const uuid1 = findElementFromListByPosition(from1, data.elements)?.uuid as string;
|
||||
idraw.moveElement(uuid1, to1);
|
||||
const id1 = findMaterialFromListByPosition(from1, data.materials)?.id as string;
|
||||
idraw.moveMaterial(id1, to1);
|
||||
|
||||
const record1 = {
|
||||
type: 'moveElement',
|
||||
type: 'moveMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: uuid1,
|
||||
method: 'moveMaterial',
|
||||
id: id1,
|
||||
from: [...from1],
|
||||
to: [...to1]
|
||||
}
|
||||
to: [...to1],
|
||||
},
|
||||
};
|
||||
|
||||
// ['0', '1', '3-2-1', '2', ['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'], '4', '5'];
|
||||
const expectedElements1 = generateElements([
|
||||
const expectedMaterials1 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'3-2-1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
// const expectedElements1 = moveElementPosition(generateElements(getList1()), {
|
||||
// const expectedMaterials1 = moveMaterialPosition(generateMaterials(getList1()), {
|
||||
// from: [...from1],
|
||||
// to: [...to1]
|
||||
// }).elements;
|
||||
// }).materials;
|
||||
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements1);
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const from2 = [2];
|
||||
const to2 = [4];
|
||||
const uuid2 = findElementFromListByPosition(from2, data.elements)?.uuid as string;
|
||||
// console.log('uuid2 ----- ', uuid2, findElementFromListByPosition(to2, data.elements)?.uuid);
|
||||
idraw.moveElement(uuid1, to2);
|
||||
const id2 = findMaterialFromListByPosition(from2, data.materials)?.id as string;
|
||||
// console.log('id2 ----- ', id2, findMaterialFromListByPosition(to2, data.materials)?.id);
|
||||
idraw.moveMaterial(id1, to2);
|
||||
const record2 = {
|
||||
type: 'moveElement',
|
||||
type: 'moveMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: uuid2,
|
||||
method: 'moveMaterial',
|
||||
id: id2,
|
||||
from: [...from2],
|
||||
to: [...to2]
|
||||
}
|
||||
to: [...to2],
|
||||
},
|
||||
};
|
||||
const expectedElements2 = generateElements([
|
||||
const expectedMaterials2 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3-2-1',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
// const expectedElements2 = moveElementPosition(expectedElements1, {
|
||||
// const expectedMaterials2 = moveMaterialPosition(expectedMaterials1, {
|
||||
// from: [...from2],
|
||||
// to: [...to2]
|
||||
// }).elements;
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements2);
|
||||
// }).materials;
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
|
|
@ -132,32 +129,32 @@ describe('idraw: useHistory ', () => {
|
|||
undo();
|
||||
const moveResult2 = calcResultMovePosition({
|
||||
from: [...from2],
|
||||
to: [...to2]
|
||||
to: [...to2],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record2.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record2.content.id,
|
||||
from: [...moveResult2.to],
|
||||
to: [...moveResult2.from]
|
||||
}
|
||||
to: [...moveResult2.from],
|
||||
},
|
||||
};
|
||||
const expectedElements3 = generateElements([
|
||||
const expectedMaterials3 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'3-2-1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
// const expectedElements3 = moveElementPosition(expectedElements1, {
|
||||
// const expectedMaterials3 = moveMaterialPosition(expectedMaterials1, {
|
||||
// from: [...moveResult2.to],
|
||||
// to: [...moveResult2.from]
|
||||
// }).elements;
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements3);
|
||||
// }).materials;
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials3);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
|
|
@ -165,31 +162,31 @@ describe('idraw: useHistory ', () => {
|
|||
undo();
|
||||
const moveResult3 = calcResultMovePosition({
|
||||
from: [...from1],
|
||||
to: [...to1]
|
||||
to: [...to1],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record1.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record1.content.id,
|
||||
from: [...moveResult3.to],
|
||||
to: [...moveResult3.from]
|
||||
}
|
||||
to: [...moveResult3.from],
|
||||
},
|
||||
};
|
||||
const expectedElements4 = generateElements([
|
||||
const expectedMaterials4 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-1', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
// const expectedElements4 = moveElementPosition(expectedElements3, {
|
||||
// const expectedMaterials4 = moveMaterialPosition(expectedMaterials3, {
|
||||
// from: [...moveResult3.to],
|
||||
// to: [...moveResult3.from]
|
||||
// }).elements;
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements4);
|
||||
// }).materials;
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials4);
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
|
|
@ -197,32 +194,32 @@ describe('idraw: useHistory ', () => {
|
|||
redo();
|
||||
const moveResult4 = calcResultMovePosition({
|
||||
from: [...record4.content.from],
|
||||
to: [...record4.content.to]
|
||||
to: [...record4.content.to],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record4.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record4.content.id,
|
||||
from: [...moveResult4.to],
|
||||
to: [...moveResult4.from]
|
||||
}
|
||||
to: [...moveResult4.from],
|
||||
},
|
||||
};
|
||||
const expectedElements5 = generateElements([
|
||||
const expectedMaterials5 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'3-2-1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
// const expectedElements5 = moveElementPosition(expectedElements3, {
|
||||
// const expectedMaterials5 = moveMaterialPosition(expectedMaterials3, {
|
||||
// from: [...moveResult4.from],
|
||||
// to: [...moveResult4.to]
|
||||
// }).elements;
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements5);
|
||||
// }).materials;
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials5);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
|
|
@ -230,28 +227,28 @@ describe('idraw: useHistory ', () => {
|
|||
redo();
|
||||
const moveResult5 = calcResultMovePosition({
|
||||
from: [...record3.content.from],
|
||||
to: [...record3.content.to]
|
||||
to: [...record3.content.to],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record4.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record4.content.id,
|
||||
from: [...moveResult5.to],
|
||||
to: [...moveResult5.from]
|
||||
}
|
||||
to: [...moveResult5.from],
|
||||
},
|
||||
};
|
||||
const expectedElements6 = generateElements([
|
||||
const expectedMaterials6 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3-2-1',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements6);
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials6);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
|
|
@ -259,28 +256,28 @@ describe('idraw: useHistory ', () => {
|
|||
undo();
|
||||
const moveResult6 = calcResultMovePosition({
|
||||
from: [...record6.content.from],
|
||||
to: [...record6.content.to]
|
||||
to: [...record6.content.to],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record7 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record6.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record6.content.id,
|
||||
from: [...moveResult6.to],
|
||||
to: [...moveResult6.from]
|
||||
}
|
||||
to: [...moveResult6.from],
|
||||
},
|
||||
};
|
||||
const expectedElements7 = generateElements([
|
||||
const expectedMaterials7 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'3-2-1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements7);
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials7);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record7]);
|
||||
|
||||
|
|
@ -288,27 +285,27 @@ describe('idraw: useHistory ', () => {
|
|||
undo();
|
||||
const moveResult7 = calcResultMovePosition({
|
||||
from: [...record5.content.from],
|
||||
to: [...record5.content.to]
|
||||
to: [...record5.content.to],
|
||||
}) as { from: number[]; to: number[] };
|
||||
const record8 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'moveElement',
|
||||
uuid: record5.content.uuid,
|
||||
method: 'moveMaterial',
|
||||
id: record5.content.id,
|
||||
from: [...moveResult7.to],
|
||||
to: [...moveResult7.from]
|
||||
}
|
||||
to: [...moveResult7.from],
|
||||
},
|
||||
};
|
||||
const expectedElements8 = generateElements([
|
||||
const expectedMaterials8 = generateMaterials([
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
['3-0', '3-1', ['3-2-0', '3-2-1', '3-2-2', '3-2-3'], '3-3'],
|
||||
'4',
|
||||
'5'
|
||||
'5',
|
||||
]);
|
||||
expect(idraw.getData()?.elements).toStrictEqual(expectedElements8);
|
||||
expect(idraw.getData()?.materials).toStrictEqual(expectedMaterials8);
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record7, record8]);
|
||||
});
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, toFlattenElement, mergeElement } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
}),
|
||||
createElement('circle', { uuid: 'test-002' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-003',
|
||||
detail: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-006' }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateElement', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
const targetElement = deepClone(data.elements[0]);
|
||||
|
||||
// modify 1: do
|
||||
const updatedElement1 = deepClone(targetElement);
|
||||
updatedElement1.x += 1;
|
||||
updatedElement1.y += 2;
|
||||
updatedElement1.detail.background = '#123456';
|
||||
updatedElement1.detail.borderRadius = 3;
|
||||
idraw.updateElement(updatedElement1);
|
||||
|
||||
const beforeInfo1: Record<string, any> = toFlattenElement(targetElement);
|
||||
const afterInfo1: Record<string, any> = toFlattenElement(updatedElement1);
|
||||
|
||||
const expectedData1 = createData();
|
||||
mergeElement(expectedData1.elements[0], updatedElement1);
|
||||
const record1 = {
|
||||
type: 'updateElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const updatedElement2 = deepClone(updatedElement1);
|
||||
updatedElement2.x += 3;
|
||||
updatedElement2.y += 4;
|
||||
updatedElement2.detail.borderRadius = [2, 4, 6, 8];
|
||||
idraw.updateElement(updatedElement2);
|
||||
const beforeInfo2: Record<string, any> = toFlattenElement(updatedElement1);
|
||||
const afterInfo2: Record<string, any> = toFlattenElement(updatedElement2);
|
||||
|
||||
const expectedData2 = createData();
|
||||
mergeElement(expectedData2.elements[0], updatedElement2);
|
||||
const record2 = {
|
||||
type: 'updateElement',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before)
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before)
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before)
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateElement',
|
||||
uuid: targetElement.uuid,
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before)
|
||||
}
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
169
packages/idraw/__tests__/history-updateMaterial.test.ts
Normal file
169
packages/idraw/__tests__/history-updateMaterial.test.ts
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import { iDraw, useHistory, deepClone, createMaterial, toFlattenMaterial, mergeMaterial } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createMaterial('circle', { id: 'test-002' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-003',
|
||||
text: 'Hello World',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-006' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('updateMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
const targetMaterial = deepClone(data.materials[0]);
|
||||
|
||||
// modify 1: do
|
||||
const updatedMaterial1 = deepClone(targetMaterial);
|
||||
updatedMaterial1.x += 1;
|
||||
updatedMaterial1.y += 2;
|
||||
updatedMaterial1.fill = '#123456';
|
||||
updatedMaterial1.cornerRadius = 3;
|
||||
idraw.updateMaterial(updatedMaterial1);
|
||||
|
||||
const beforeInfo1: Record<string, any> = toFlattenMaterial(targetMaterial);
|
||||
const afterInfo1: Record<string, any> = toFlattenMaterial(updatedMaterial1);
|
||||
|
||||
const expectedData1 = createData();
|
||||
mergeMaterial(expectedData1.materials[0], updatedMaterial1);
|
||||
const record1 = {
|
||||
type: 'updateMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 2: do
|
||||
const updatedMaterial2 = deepClone(updatedMaterial1);
|
||||
updatedMaterial2.x += 3;
|
||||
updatedMaterial2.y += 4;
|
||||
updatedMaterial2.cornerRadius = [2, 4, 6, 8];
|
||||
idraw.updateMaterial(updatedMaterial2);
|
||||
const beforeInfo2: Record<string, any> = toFlattenMaterial(updatedMaterial1);
|
||||
const afterInfo2: Record<string, any> = toFlattenMaterial(updatedMaterial2);
|
||||
|
||||
const expectedData2 = createData();
|
||||
mergeMaterial(expectedData2.materials[0], updatedMaterial2);
|
||||
const record2 = {
|
||||
type: 'updateMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
|
||||
// modify 3: undo
|
||||
undo();
|
||||
const record3 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 4: undo
|
||||
undo();
|
||||
const record4 = {
|
||||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3, record4]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record5 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
expect(__getUndoRecords()).toStrictEqual([record3]);
|
||||
|
||||
// modify 5: redo
|
||||
redo();
|
||||
const record6 = {
|
||||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'updateMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
expect(__getUndoRecords()).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,43 +1,35 @@
|
|||
import { iDraw, useHistory, deepClone, createElement, set, get, toFlattenElement } from 'idraw';
|
||||
import type { RecursivePartial, Element } from 'idraw';
|
||||
import { iDraw, useHistory, deepClone, createMaterial, set, get, toFlattenMaterial } from 'idraw';
|
||||
import type { RecursivePartial, Material } from 'idraw';
|
||||
|
||||
const createData = () => ({
|
||||
elements: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-001',
|
||||
materials: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-001',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
background: '#DDDDDD'
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
fill: '#DDDDDD',
|
||||
}),
|
||||
createElement('circle', { uuid: 'test-002' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-003',
|
||||
detail: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
createMaterial('circle', { id: 'test-002' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-003',
|
||||
text: 'Hello World',
|
||||
}),
|
||||
createElement('image', { uuid: 'test-004', detail: { src: 'https://example.com/001.png' } }),
|
||||
createElement('group', {
|
||||
uuid: 'test-005',
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-006' }),
|
||||
createElement('circle', { uuid: 'test-007' }),
|
||||
createElement('text', {
|
||||
uuid: 'test-008',
|
||||
detail: {
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', { uuid: 'test-009', detail: { src: 'https://example.com/002.png' } })
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
createMaterial('image', { id: 'test-004', src: 'https://example.com/001.png' }),
|
||||
createMaterial('group', {
|
||||
id: 'test-005',
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-006' }),
|
||||
createMaterial('circle', { id: 'test-007' }),
|
||||
createMaterial('text', {
|
||||
id: 'test-008',
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', { id: 'test-009', src: 'https://example.com/002.png' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('idraw: useHistory ', () => {
|
||||
|
|
@ -45,50 +37,48 @@ describe('idraw: useHistory ', () => {
|
|||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('modifyElement', () => {
|
||||
test('modifyMaterial', () => {
|
||||
const data = createData();
|
||||
const div = document.createElement('div') as HTMLDivElement;
|
||||
|
||||
const idraw = new iDraw(div, {
|
||||
height: 200,
|
||||
width: 200
|
||||
width: 200,
|
||||
});
|
||||
const { MiddlewareHistory, historyHandler } = useHistory({ core: idraw.getCore() });
|
||||
const { undo, redo, __getDoRecords, __getUndoRecords } = historyHandler;
|
||||
idraw.use(MiddlewareHistory);
|
||||
idraw.setData(data);
|
||||
const targetElement = deepClone(data.elements[0]);
|
||||
const targetMaterial = deepClone(data.materials[0]);
|
||||
|
||||
// modify 1: do
|
||||
const modifiedInfo1 = {
|
||||
x: targetElement.x + 1,
|
||||
y: targetElement.y + 2,
|
||||
detail: {
|
||||
background: '#123456',
|
||||
borderRadius: 3
|
||||
}
|
||||
x: targetMaterial.x + 1,
|
||||
y: targetMaterial.y + 2,
|
||||
fill: '#123456',
|
||||
cornerRadius: 3,
|
||||
};
|
||||
idraw.modifyElement({
|
||||
uuid: targetElement.uuid,
|
||||
...deepClone(modifiedInfo1)
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo1),
|
||||
});
|
||||
const expectedData1 = createData();
|
||||
const flattenModifiedInfo1 = toFlattenElement(modifiedInfo1);
|
||||
const flattenModifiedInfo1 = toFlattenMaterial(modifiedInfo1);
|
||||
const beforeInfo1: Record<string, any> = {};
|
||||
const afterInfo1 = { ...flattenModifiedInfo1 };
|
||||
Object.keys(flattenModifiedInfo1).forEach((key) => {
|
||||
beforeInfo1[key] = get(expectedData1.elements[0], key);
|
||||
set(expectedData1.elements[0], key, flattenModifiedInfo1[key]);
|
||||
beforeInfo1[key] = get(expectedData1.materials[0], key);
|
||||
set(expectedData1.materials[0], key, flattenModifiedInfo1[key]);
|
||||
});
|
||||
const record1 = {
|
||||
type: 'modifyElement',
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo1,
|
||||
after: afterInfo1
|
||||
}
|
||||
after: afterInfo1,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -98,40 +88,38 @@ describe('idraw: useHistory ', () => {
|
|||
const modifiedInfo2 = {
|
||||
x: modifiedInfo1.x + 3,
|
||||
y: modifiedInfo1.y + 4,
|
||||
detail: {
|
||||
borderRadius: [2, 4, 6, 8]
|
||||
}
|
||||
} as unknown as RecursivePartial<Omit<Element, 'uuid'>>;
|
||||
cornerRadius: [2, 4, 6, 8],
|
||||
} as unknown as RecursivePartial<Omit<Material, 'id'>>;
|
||||
|
||||
idraw.modifyElement({
|
||||
uuid: targetElement.uuid,
|
||||
...deepClone(modifiedInfo2)
|
||||
} as RecursivePartial<Omit<Element, 'uuid'>> & Pick<Element, 'uuid'>);
|
||||
idraw.modifyMaterial({
|
||||
id: targetMaterial.id,
|
||||
...deepClone(modifiedInfo2),
|
||||
} as RecursivePartial<Omit<Material, 'id'>> & Pick<Material, 'id'>);
|
||||
|
||||
const expectedData2 = deepClone(expectedData1);
|
||||
const flattenModifiedInfo2 = toFlattenElement(modifiedInfo2);
|
||||
const flattenModifiedInfo2 = toFlattenMaterial(modifiedInfo2);
|
||||
const beforeInfo2: Record<string, any> = {};
|
||||
const afterInfo2 = { ...flattenModifiedInfo2 };
|
||||
|
||||
Object.keys(flattenModifiedInfo2).forEach((key) => {
|
||||
let beforeVal = get(expectedData1.elements[0], key);
|
||||
let beforeVal = get(expectedData1.materials[0], key);
|
||||
let beforeKey = key;
|
||||
if (beforeVal === undefined && /(borderRadius|borderWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
if (beforeVal === undefined && /(cornerRadius|strokeWidth)\[[0-9]{1,}\]$/.test(beforeKey)) {
|
||||
beforeKey = beforeKey.replace(/\[[0-9]{1,}\]$/, '');
|
||||
beforeVal = get(expectedData1.elements[0], beforeKey);
|
||||
beforeVal = get(expectedData1.materials[0], beforeKey);
|
||||
}
|
||||
beforeInfo2[beforeKey] = beforeVal;
|
||||
set(expectedData2.elements[0], key, flattenModifiedInfo2[key]);
|
||||
set(expectedData2.materials[0], key, flattenModifiedInfo2[key]);
|
||||
});
|
||||
const record2 = {
|
||||
type: 'modifyElement',
|
||||
type: 'modifyMaterial',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: beforeInfo2,
|
||||
after: afterInfo2
|
||||
}
|
||||
after: afterInfo2,
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record1, record2]);
|
||||
|
|
@ -143,11 +131,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record2.content.after),
|
||||
after: deepClone(record2.content.before)
|
||||
}
|
||||
after: deepClone(record2.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record1]);
|
||||
|
|
@ -159,11 +147,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'undo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record1.content.after),
|
||||
after: deepClone(record1.content.before)
|
||||
}
|
||||
after: deepClone(record1.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(createData());
|
||||
expect(__getDoRecords()).toStrictEqual([]);
|
||||
|
|
@ -175,11 +163,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record4.content.after),
|
||||
after: deepClone(record4.content.before)
|
||||
}
|
||||
after: deepClone(record4.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData1);
|
||||
expect(__getDoRecords()).toStrictEqual([record5]);
|
||||
|
|
@ -191,11 +179,11 @@ describe('idraw: useHistory ', () => {
|
|||
type: 'redo',
|
||||
time: new Date().getTime(),
|
||||
content: {
|
||||
method: 'modifyElement',
|
||||
uuid: targetElement.uuid,
|
||||
method: 'modifyMaterial',
|
||||
id: targetMaterial.id,
|
||||
before: deepClone(record3.content.after),
|
||||
after: deepClone(record3.content.before)
|
||||
}
|
||||
after: deepClone(record3.content.before),
|
||||
},
|
||||
};
|
||||
expect(idraw.getData()).toStrictEqual(expectedData2);
|
||||
expect(__getDoRecords()).toStrictEqual([record5, record6]);
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import * as util from '../src';
|
||||
|
||||
const types = {
|
||||
Context2D: 'Function',
|
||||
check: 'Object',
|
||||
createUUID: 'Function',
|
||||
deepClone: 'Function',
|
||||
delay: 'Function',
|
||||
downloadImageFromCanvas: 'Function',
|
||||
is: 'Object',
|
||||
isColorStr: 'Function',
|
||||
istype: 'Object',
|
||||
loadHTML: 'AsyncFunction',
|
||||
loadImage: 'Function',
|
||||
loadSVG: 'AsyncFunction',
|
||||
throttle: 'Function',
|
||||
toColorHexNum: 'Function',
|
||||
toColorHexStr: 'Function'
|
||||
};
|
||||
|
||||
function getType(data: any): string {
|
||||
const typeStr = Object.prototype.toString.call(data) || '';
|
||||
const result = typeStr.replace(/(\[object|\])/gi, '').trim();
|
||||
return result;
|
||||
}
|
||||
|
||||
describe('@idraw/util', () => {
|
||||
// test('index', async () => {
|
||||
// const keys = Object.keys(util);
|
||||
// keys.forEach((key) => {
|
||||
// // @ts-ignore
|
||||
// const type = getType(util[key]);
|
||||
// // @ts-ignore
|
||||
// expect(type).toStrictEqual(types[key]);
|
||||
// });
|
||||
// });
|
||||
|
||||
test('index', async () => {
|
||||
const keys = Object.keys(types);
|
||||
keys.forEach((key) => {
|
||||
// @ts-ignore
|
||||
const type = getType(util[key]);
|
||||
// @ts-ignore
|
||||
expect(type).toStrictEqual(types[key]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,879 +0,0 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 1800,
|
||||
"width": 2000,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "clearRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 1800,
|
||||
"width": 2000,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 240,
|
||||
"width": 400,
|
||||
"x": 20,
|
||||
"y": 20,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 240,
|
||||
"width": 400,
|
||||
"x": 160,
|
||||
"y": 160,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 240,
|
||||
"width": 400,
|
||||
"x": 320,
|
||||
"y": 320,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 400,
|
||||
"x": 780,
|
||||
"y": 580,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.arc 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"anticlockwise": true,
|
||||
"endAngle": 6.283185307179586,
|
||||
"radius": 100,
|
||||
"startAngle": 0,
|
||||
"x": 140,
|
||||
"y": 160,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "arc",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.arcTo 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"cpx1": 100,
|
||||
"cpx2": 200,
|
||||
"cpy1": 100,
|
||||
"cpy2": 200,
|
||||
"radius": 12.566370614359172,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "arcTo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.beginPath 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.clearRect 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 1800,
|
||||
"width": 2000,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "clearRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.closePath 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "closePath",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.createPattern 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 600,
|
||||
"width": 600,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.drawImage 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"dHeight": 0,
|
||||
"dWidth": 0,
|
||||
"dx": 22,
|
||||
"dy": 24,
|
||||
"img": <img />,
|
||||
"sHeight": 0,
|
||||
"sWidth": 0,
|
||||
"sx": 0,
|
||||
"sy": 0,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "drawImage",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"dHeight": 104,
|
||||
"dWidth": 102,
|
||||
"dx": 22,
|
||||
"dy": 24,
|
||||
"img": <img />,
|
||||
"sHeight": 204,
|
||||
"sWidth": 202,
|
||||
"sx": 122,
|
||||
"sy": 124,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "drawImage",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.fill 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.fillRect 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 160,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.fillText 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"maxWidth": null,
|
||||
"text": "Hello world",
|
||||
"x": 100,
|
||||
"y": 200,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillText",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.lineTo 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "lineTo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.moveTo 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "moveTo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.rect 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"fillRule": "nonzero",
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 400,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "rect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fill",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.restore 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 20,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 200,
|
||||
"x": 300,
|
||||
"y": 150,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.rotate 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 400,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
0.8660254037844387,
|
||||
0.49999999999999994,
|
||||
-0.49999999999999994,
|
||||
0.8660254037844387,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "rect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
0.8660254037844387,
|
||||
0.49999999999999994,
|
||||
-0.49999999999999994,
|
||||
0.8660254037844387,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "stroke",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.save 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 20,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 200,
|
||||
"width": 200,
|
||||
"x": 300,
|
||||
"y": 150,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "fillRect",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.scale 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 400,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "rect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "stroke",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.stroke 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 400,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "rect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "stroke",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@idraw/board: src/lib/context Context.translate 1`] = `
|
||||
[
|
||||
{
|
||||
"props": {
|
||||
"path": [
|
||||
{
|
||||
"props": {},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
"type": "beginPath",
|
||||
},
|
||||
{
|
||||
"props": {
|
||||
"height": 400,
|
||||
"width": 200,
|
||||
"x": 20,
|
||||
"y": 40,
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
100,
|
||||
120,
|
||||
],
|
||||
"type": "rect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"transform": [
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
100,
|
||||
120,
|
||||
],
|
||||
"type": "stroke",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`@idraw/util: lib/file downloadImageFromCanvas 1`] = `<canvas />`;
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { toColorHexNum, toColorHexStr, isColorStr } from '@idraw/util';
|
||||
|
||||
describe('@idraw/util: lib/color', () => {
|
||||
const hex = '#f0f0f0';
|
||||
const num = 15790320;
|
||||
|
||||
test('toColorHexNum', async () => {
|
||||
const result = toColorHexNum(hex);
|
||||
expect(result).toStrictEqual(num);
|
||||
});
|
||||
|
||||
test('toColorHexStr', async () => {
|
||||
const result = toColorHexStr(num);
|
||||
expect(result).toStrictEqual(hex);
|
||||
});
|
||||
|
||||
test('isColorStr', async () => {
|
||||
const result = isColorStr(hex);
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { Context2D, deepClone } from '@idraw/util';
|
||||
import { getData } from './data';
|
||||
|
||||
describe('@idraw/board: src/lib/context', () => {
|
||||
const options = {
|
||||
width: 600,
|
||||
height: 400,
|
||||
contextWidth: 1000,
|
||||
contextHeight: 900,
|
||||
devicePixelRatio: 2
|
||||
};
|
||||
|
||||
test('Context', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const data = getData();
|
||||
|
||||
ctx.clearRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
data.elements.forEach((ele) => {
|
||||
ctx.fillStyle = ele.detail.color;
|
||||
ctx.fillRect(ele.x, ele.y, ele.w, ele.h);
|
||||
});
|
||||
// @ts-ignore;
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.setFillStyle', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const color = '#f0f0f0';
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
expect(ctx2d.fillStyle).toStrictEqual(color);
|
||||
});
|
||||
|
||||
test('Context.fill', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.arc', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.arc(70, 80, 50, 0, Math.PI * 2, true);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.rect', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.rect(10, 20, 100, 200);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.fillRect', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.fillRect(10, 20, 80, 100);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
// expect(calls).toStrictEqual([
|
||||
// {
|
||||
// type: 'fillRect',
|
||||
// transform: [ 1, 0, 0, 1, 0, 0 ],
|
||||
// props: { x: 0, y: 0, width: 2000, height: 1800 }
|
||||
// }
|
||||
// ]);
|
||||
});
|
||||
|
||||
test('Context.clearRect', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.clearRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.beginPath', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.beginPath();
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.closePath', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.lineTo', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.lineTo(10, 20);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.moveTo', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.moveTo(10, 20);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.arcTo', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.arcTo(50, 50, 100, 100, Math.PI * 2);
|
||||
ctx.fill();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.setLineWidth', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const lineWidth = 12;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.lineWidth = lineWidth;
|
||||
expect(ctx2d.lineWidth).toStrictEqual(lineWidth * opts.devicePixelRatio);
|
||||
});
|
||||
|
||||
test('Context.setLineDash', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const lineDash = [10, 20];
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.setLineDash(lineDash);
|
||||
// @ts-ignore
|
||||
// const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
// expect(calls).toMatchSnapshot();
|
||||
expect(ctx2d.getLineDash()).toStrictEqual(lineDash.map((n) => n * opts.devicePixelRatio));
|
||||
});
|
||||
|
||||
test('Context.setStrokeStyle', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const color = '#f0f0f0';
|
||||
ctx.strokeStyle = color;
|
||||
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
expect(ctx2d.strokeStyle).toStrictEqual(color);
|
||||
});
|
||||
|
||||
// TODO
|
||||
test('Context.isPointInPath', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
// const lineDash = [10, 20];
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const x = 50;
|
||||
const y = 50;
|
||||
const w = 50;
|
||||
const h = 50;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.closePath();
|
||||
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
expect(ctx.isPointInPath(51, 51)).toStrictEqual(ctx2d.isPointInPath(60 * opts.devicePixelRatio, 60 * opts.devicePixelRatio));
|
||||
});
|
||||
|
||||
test('Context.setStrokeStyle', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const color = '#f0f0f0';
|
||||
ctx.strokeStyle = color;
|
||||
expect(ctx2d.strokeStyle).toStrictEqual(color);
|
||||
});
|
||||
|
||||
test('Context.stroke', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.rect(10, 20, 100, 200);
|
||||
ctx.stroke();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.translate', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const x = 50;
|
||||
const y = 60;
|
||||
ctx.translate(x, y);
|
||||
ctx.rect(10, 20, 100, 200);
|
||||
ctx.stroke();
|
||||
ctx.translate(-x, -y);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.rotate', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const radian = Math.PI / 6;
|
||||
ctx.rotate(radian);
|
||||
ctx.rect(10, 20, 100, 200);
|
||||
ctx.stroke();
|
||||
ctx.rotate(-radian);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.drawImage', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const img = new Image();
|
||||
const dx = 11;
|
||||
const dy = 12;
|
||||
const dw = 51;
|
||||
const dh = 52;
|
||||
const sx = 61;
|
||||
const sy = 62;
|
||||
const sw = 101;
|
||||
const sh = 102;
|
||||
ctx.drawImage(img, dx, dy);
|
||||
ctx.drawImage(img, dx, dy, dw, dh);
|
||||
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.createPattern', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const img = new Image();
|
||||
const pattern = ctx.createPattern(img, 'repeat') as CanvasPattern;
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.fillRect(0, 0, 300, 300);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(ctx2d.fillStyle).toStrictEqual(pattern);
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.measureText', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.$setFont({ fontSize: 20 });
|
||||
const size = ctx.measureText('Hello World!');
|
||||
expect(size.width).toStrictEqual(12);
|
||||
});
|
||||
|
||||
test('Context.setTextAlign', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const textAlign = 'center';
|
||||
ctx.textAlign = textAlign;
|
||||
ctx.fillRect(0, 0, opts.contextWidth, opts.contextHeight);
|
||||
expect(ctx2d.textAlign).toStrictEqual(textAlign);
|
||||
});
|
||||
|
||||
test('Context.fillText', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.fillText('Hello world', 50, 100);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.$setFont', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const fontSize = 22;
|
||||
const fontFamily = 'Hello';
|
||||
const fontWeight = 'bold';
|
||||
ctx.$setFont({
|
||||
fontSize,
|
||||
fontFamily,
|
||||
fontWeight
|
||||
});
|
||||
// console.log('ctx2d.font =', ctx2d.font);
|
||||
expect(ctx2d.font).toStrictEqual([fontWeight, fontSize * opts.devicePixelRatio + 'px', fontFamily].join(' '));
|
||||
});
|
||||
|
||||
test('Context.textBaseline', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const textBaseline = 'bottom';
|
||||
ctx.textBaseline = textBaseline;
|
||||
expect(ctx2d.textBaseline).toStrictEqual(textBaseline);
|
||||
});
|
||||
|
||||
test('Context.globalAlpha', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const globalAlpha = 0.45;
|
||||
ctx.globalAlpha = globalAlpha;
|
||||
expect(ctx2d.globalAlpha).toStrictEqual(globalAlpha);
|
||||
});
|
||||
|
||||
test('Context.save', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#f0f0f0';
|
||||
ctx.fillRect(10, 10, 100, 100);
|
||||
ctx.restore();
|
||||
ctx.fillRect(150, 75, 100, 100);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.restore', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
ctx.save();
|
||||
ctx.fillStyle = '#f0f0f0';
|
||||
ctx.fillRect(10, 10, 100, 100);
|
||||
ctx.restore();
|
||||
ctx.fillRect(150, 75, 100, 100);
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Context.scale', async () => {
|
||||
const opts = deepClone(options);
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = opts.contextWidth;
|
||||
canvas.height = opts.contextHeight;
|
||||
const ctx2d: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const ctx = new Context2D(ctx2d, opts);
|
||||
const scaleX = 2;
|
||||
const scaleY = 3;
|
||||
ctx.scale(scaleX, scaleY);
|
||||
ctx.rect(10, 20, 100, 200);
|
||||
ctx.stroke();
|
||||
// @ts-ignore
|
||||
const calls = ctx2d.__getDrawCalls();
|
||||
// console.log('calls =', JSON.stringify(calls, null, 2));
|
||||
expect(calls).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -3,53 +3,45 @@ import type { Data } from '@idraw/types';
|
|||
import { imageBase64, html, svg } from '../_assets/base';
|
||||
|
||||
const originData: Data = {
|
||||
elements: [
|
||||
materials: [
|
||||
{
|
||||
uuid: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
id: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
src: imageBase64
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
href: imageBase64,
|
||||
},
|
||||
{
|
||||
uuid: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
id: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
type: 'group',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
svg: svg
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
html: html
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
width: 100,
|
||||
height: 100,
|
||||
children: [
|
||||
{
|
||||
id: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'foreignObject',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
content: svg,
|
||||
},
|
||||
{
|
||||
id: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'foreignObject',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
content: html,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('@idraw/util: data ', () => {
|
||||
|
|
@ -58,61 +50,59 @@ describe('@idraw/util: data ', () => {
|
|||
const compactData = filterCompactData(data);
|
||||
|
||||
const expectData: Data = {
|
||||
elements: [
|
||||
materials: [
|
||||
{
|
||||
uuid: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
id: 'b37213ce-d711-cbb3-51ac-d8081c19f127',
|
||||
type: 'image',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { src: '@assets/0a920a91-0aba-0af3-0aeb-0a730accafb' }
|
||||
width: 100,
|
||||
height: 100,
|
||||
href: '@assets/0a920a91-0aba-0af3-0aeb-0a730accafb',
|
||||
},
|
||||
{
|
||||
uuid: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
id: '063e3a80-1ede-7912-f919-975e34a9bd01',
|
||||
type: 'group',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'svg',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { svg: '@assets/0a830ab3-0a5d-0a5b-0a63-0a740a6cb34' }
|
||||
},
|
||||
{
|
||||
uuid: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'html',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
detail: { html: '@assets/0a2b0ab4-0b45-0b19-0a0d-0add0a0dab5' }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
width: 100,
|
||||
height: 100,
|
||||
children: [
|
||||
{
|
||||
id: 'b60e64e8-833e-e112-d7eb-1ab6e7d6870c',
|
||||
type: 'foreignObject',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
content: '@assets/0a830ab3-0a5d-0a5b-0a63-0a740a6cb34',
|
||||
},
|
||||
{
|
||||
id: '61f2a61e-cdd5-ae36-983f-686ba8e35973',
|
||||
type: 'foreignObject',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
content: '@assets/0a2b0ab4-0b45-0b19-0a0d-0add0a0dab5',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
assets: {
|
||||
'@assets/0a920a91-0aba-0af3-0aeb-0a730accafb': {
|
||||
type: 'image',
|
||||
value: imageBase64
|
||||
value: imageBase64,
|
||||
},
|
||||
'@assets/0a830ab3-0a5d-0a5b-0a63-0a740a6cb34': {
|
||||
type: 'svg',
|
||||
value: svg
|
||||
type: 'foreignObject',
|
||||
value: svg,
|
||||
},
|
||||
'@assets/0a2b0ab4-0b45-0b19-0a0d-0add0a0dab5': {
|
||||
type: 'html',
|
||||
value: html
|
||||
}
|
||||
}
|
||||
type: 'foreignObject',
|
||||
value: html,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(compactData).toStrictEqual(expectData);
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
export function getData() {
|
||||
const data = {
|
||||
elements: [
|
||||
{
|
||||
x: 10,
|
||||
y: 10,
|
||||
w: 200,
|
||||
h: 120,
|
||||
type: 'rect',
|
||||
detail: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 80,
|
||||
y: 80,
|
||||
w: 200,
|
||||
h: 120,
|
||||
type: 'rect',
|
||||
detail: {
|
||||
color: '#cccccc'
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 160,
|
||||
y: 160,
|
||||
w: 200,
|
||||
h: 120,
|
||||
type: 'rect',
|
||||
detail: {
|
||||
color: '#c0c0c0'
|
||||
}
|
||||
},
|
||||
{
|
||||
x: 400 - 10,
|
||||
y: 300 - 10,
|
||||
w: 200,
|
||||
h: 100,
|
||||
type: 'rect',
|
||||
detail: {
|
||||
color: '#e0e0e0'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import { createUUID, getElementPositionFromList } from '@idraw/util';
|
||||
import type { Elements } from '@idraw/types';
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 1
|
||||
};
|
||||
};
|
||||
|
||||
function generateElements(list: any[]): Elements {
|
||||
const elements: Elements = list.map((item, i) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `${i}-${createUUID()}`,
|
||||
type: 'group',
|
||||
detail: {
|
||||
children: generateElements(item)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `${i}-${createUUID()}`,
|
||||
type: 'rect',
|
||||
detail: {}
|
||||
};
|
||||
}
|
||||
}) as Elements;
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: element ', () => {
|
||||
// [4]
|
||||
test('getElementPositionFromList [4]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
const uuid = (list as any)[4].uuid;
|
||||
const position = getElementPositionFromList(uuid, list);
|
||||
expect(position).toStrictEqual([4]);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5]
|
||||
test('getElementPositionFromList [1, 2, 3, 4, 5]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
const uuid = (list as any)[1].detail.children[2].detail.children[3].detail.children[4].detail.children[5].uuid;
|
||||
const position = getElementPositionFromList(uuid, list);
|
||||
expect(position).toStrictEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4]
|
||||
test('getElementPositionFromList [1, 2, 3, 4, 5]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
const uuid = (list as any)[1].detail.children[2].detail.children[3].detail.children[4].uuid;
|
||||
const position = getElementPositionFromList(uuid, list);
|
||||
expect(position).toStrictEqual([1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { downloadImageFromCanvas } from '@idraw/util';
|
||||
|
||||
describe('@idraw/util: lib/file', () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const opts = {
|
||||
width: 600,
|
||||
height: 400
|
||||
};
|
||||
ctx.clearRect(0, 0, opts.width, opts.height);
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(0, 0, opts.width / 2, opts.height / 2);
|
||||
|
||||
test('downloadImageFromCanvas', async () => {
|
||||
downloadImageFromCanvas(canvas, {
|
||||
fileName: 'hello',
|
||||
type: 'image/png'
|
||||
});
|
||||
expect(canvas).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,92 +1,90 @@
|
|||
import type { Element } from '@idraw/types';
|
||||
import { groupElementsByPosition, ungroupElementsByPosition, insertElementToListByPosition } from '@idraw/util';
|
||||
import type { Material } from '@idraw/types';
|
||||
import { groupMaterialsByPosition, ungroupMaterialsByPosition } from '@idraw/util';
|
||||
|
||||
const createList = () => {
|
||||
const list: Element[] = [
|
||||
{ uuid: '0', type: 'rect', x: 20, y: 20, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '1', type: 'rect', x: 40, y: 40, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '2', type: 'rect', x: 60, y: 60, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '3', type: 'rect', x: 80, y: 80, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '4', type: 'rect', x: 100, y: 100, w: 50, h: 50, detail: {} }
|
||||
const list: Material[] = [
|
||||
{ id: '0', type: 'rect', x: 20, y: 20, width: 50, height: 50 },
|
||||
{ id: '1', type: 'rect', x: 40, y: 40, width: 50, height: 50 },
|
||||
{ id: '2', type: 'rect', x: 60, y: 60, width: 50, height: 50 },
|
||||
{ id: '3', type: 'rect', x: 80, y: 80, width: 50, height: 50 },
|
||||
{ id: '4', type: 'rect', x: 100, y: 100, width: 50, height: 50 },
|
||||
];
|
||||
return list;
|
||||
};
|
||||
|
||||
const createGroupedList = () => {
|
||||
const list: Element[] = [
|
||||
{ uuid: '0', type: 'rect', x: 20, y: 20, w: 50, h: 50, detail: {} },
|
||||
const list: Material[] = [
|
||||
{ id: '0', type: 'rect', x: 20, y: 20, width: 50, height: 50 },
|
||||
{
|
||||
name: 'Group',
|
||||
uuid: 'group-id',
|
||||
id: 'group-id',
|
||||
type: 'group',
|
||||
x: 40,
|
||||
y: 40,
|
||||
w: 90,
|
||||
h: 90,
|
||||
detail: {
|
||||
children: [
|
||||
{ uuid: '1', type: 'rect', x: 0, y: 0, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '2', type: 'rect', x: 20, y: 20, w: 50, h: 50, detail: {} },
|
||||
{ uuid: '3', type: 'rect', x: 40, y: 40, w: 50, h: 50, detail: {} }
|
||||
]
|
||||
}
|
||||
width: 90,
|
||||
height: 90,
|
||||
children: [
|
||||
{ id: '1', type: 'rect', x: 0, y: 0, width: 50, height: 50 },
|
||||
{ id: '2', type: 'rect', x: 20, y: 20, width: 50, height: 50 },
|
||||
{ id: '3', type: 'rect', x: 40, y: 40, width: 50, height: 50 },
|
||||
],
|
||||
},
|
||||
{ uuid: '4', type: 'rect', x: 100, y: 100, w: 50, h: 50, detail: {} }
|
||||
{ id: '4', type: 'rect', x: 100, y: 100, width: 50, height: 50 },
|
||||
];
|
||||
return list;
|
||||
};
|
||||
|
||||
describe('@idraw/util: group ', () => {
|
||||
test('groupElementsByPosition', () => {
|
||||
test('groupMaterialsByPosition', () => {
|
||||
const list = createList();
|
||||
groupElementsByPosition(list, [[1], [2], [3]]);
|
||||
list[1].uuid = 'group-id';
|
||||
groupMaterialsByPosition(list, [[1], [2], [3]]);
|
||||
list[1].id = 'group-id';
|
||||
expect(list).toStrictEqual(createGroupedList());
|
||||
});
|
||||
|
||||
test('groupElementsByPosition with disordered positions', () => {
|
||||
test('groupMaterialsByPosition with disordered positions', () => {
|
||||
const list = createList();
|
||||
groupElementsByPosition(list, [[3], [1], [2]]);
|
||||
list[1].uuid = 'group-id';
|
||||
groupMaterialsByPosition(list, [[3], [1], [2]]);
|
||||
list[1].id = 'group-id';
|
||||
expect(list).toStrictEqual(createGroupedList());
|
||||
});
|
||||
|
||||
test('groupElementsByPosition with deep position', () => {
|
||||
test('groupMaterialsByPosition with deep position', () => {
|
||||
const list = createList();
|
||||
list[0].type = 'group';
|
||||
list[0].detail = { children: createList() };
|
||||
groupElementsByPosition(list, [
|
||||
list[0].children = createList();
|
||||
groupMaterialsByPosition(list, [
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[0, 3]
|
||||
[0, 3],
|
||||
]);
|
||||
list[0].detail.children[1].uuid = 'group-id';
|
||||
list[0].children[1].id = 'group-id';
|
||||
|
||||
const expectedList = createList();
|
||||
expectedList[0].type = 'group';
|
||||
expectedList[0].detail = { children: createGroupedList() };
|
||||
expectedList[0].children = createGroupedList();
|
||||
expect(list).toStrictEqual(expectedList);
|
||||
});
|
||||
|
||||
test('groupElementsByPosition with deep position and disordered positions', () => {
|
||||
test('groupMaterialsByPosition with deep position and disordered positions', () => {
|
||||
const list = createList();
|
||||
list[0].type = 'group';
|
||||
list[0].detail = { children: createList() };
|
||||
groupElementsByPosition(list, [
|
||||
list[0].children = createList();
|
||||
groupMaterialsByPosition(list, [
|
||||
[0, 3],
|
||||
[0, 1],
|
||||
[0, 2]
|
||||
[0, 2],
|
||||
]);
|
||||
list[0].detail.children[1].uuid = 'group-id';
|
||||
list[0].children[1].id = 'group-id';
|
||||
const expectedList = createList();
|
||||
expectedList[0].type = 'group';
|
||||
expectedList[0].detail = { children: createGroupedList() };
|
||||
expectedList[0].children = createGroupedList();
|
||||
expect(list).toStrictEqual(expectedList);
|
||||
});
|
||||
|
||||
test('upgroupElementsByPosition', () => {
|
||||
test('upgroupMaterialsByPosition', () => {
|
||||
const list = createGroupedList();
|
||||
ungroupElementsByPosition(list, [1]);
|
||||
ungroupMaterialsByPosition(list, [1]);
|
||||
expect(list).toStrictEqual(createList());
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
import { moveElementPosition } from '@idraw/util';
|
||||
import type { Elements } from '@idraw/types';
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 1
|
||||
};
|
||||
};
|
||||
|
||||
function generateElements(list: any[]): Elements {
|
||||
const elements: Elements = list.map((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `group`,
|
||||
type: 'group',
|
||||
detail: {
|
||||
children: generateElements(item)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `rect-${item}`,
|
||||
type: 'rect',
|
||||
detail: {}
|
||||
};
|
||||
}
|
||||
}) as Elements;
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: handle-element ', () => {
|
||||
// [2] -> [4]
|
||||
// [0, 1, 2, 3, 2, 4, 5]
|
||||
// [0, 1, 3, 2, 4, 5]
|
||||
test('moveElementPosition, move-down [2] -> [4]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, 3, 4, 5]);
|
||||
moveElementPosition(list, {
|
||||
from: [2],
|
||||
to: [4]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, 1, 3, 2, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [4] -> [2] yes
|
||||
// [0, 1, 4, 2, 3, 4, 5]
|
||||
// [0, 1, 4, 2, 3, 5]
|
||||
test('moveElementPosition, move-up [4] -> [2]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, 3, 4, 5]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [4],
|
||||
to: [2]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, 1, 4, 2, 3, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [3, 2, 1] -> [2]
|
||||
test('moveElementPosition, move-up [3, 2, 1] -> [2]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [3, 2, 1],
|
||||
to: [2]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, 1, 1, 2, [0, 1, [0, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1] -> [1, 2, 3]
|
||||
test('moveElementPosition, move-up [1] -> [1, 2, 3]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [1],
|
||||
to: [1, 2, 3]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5] -> [1, 2, 2]
|
||||
test('moveElementPosition, move-up [1, 2, 3, 4, 5] -> [1, 2, 2]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [1, 2, 3, 4, 5],
|
||||
to: [1, 2, 2]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, [0, 1, [0, 1, 5, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1] -> [1]
|
||||
test('moveElementPosition, move-up [1] -> [1]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [1],
|
||||
to: [1]
|
||||
});
|
||||
|
||||
const expectResult = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [2, 4] -> [1, 2]
|
||||
test('moveElementPosition, move-up [1, 2] -> [2, 4]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]);
|
||||
|
||||
moveElementPosition(list, {
|
||||
from: [2, 4],
|
||||
to: [1, 2]
|
||||
});
|
||||
const expectResult = generateElements([0, [0, 1, 4, 2, 3, 4], [0, 1, 2, 3], 3, 4]);
|
||||
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
});
|
||||
141
packages/util/__tests__/lib/handle-material.test.ts
Normal file
141
packages/util/__tests__/lib/handle-material.test.ts
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import { moveMaterialPosition } from '@idraw/util';
|
||||
import type { StrictMaterial } from '@idraw/types';
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
||||
|
||||
function generateMaterials(list: any[]): StrictMaterial[] {
|
||||
const elements: StrictMaterial[] = list.map((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getElemBase(),
|
||||
id: `group`,
|
||||
type: 'group',
|
||||
children: generateMaterials(item),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
id: `rect-${item}`,
|
||||
type: 'rect',
|
||||
};
|
||||
}
|
||||
}) as StrictMaterial[];
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: handle-element ', () => {
|
||||
// [2] -> [4]
|
||||
// [0, 1, 2, 3, 2, 4, 5]
|
||||
// [0, 1, 3, 2, 4, 5]
|
||||
test('moveMaterialPosition, move-down [2] -> [4]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, 3, 4, 5]);
|
||||
moveMaterialPosition(list, {
|
||||
from: [2],
|
||||
to: [4],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([0, 1, 3, 2, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [4] -> [2] yes
|
||||
// [0, 1, 4, 2, 3, 4, 5]
|
||||
// [0, 1, 4, 2, 3, 5]
|
||||
test('moveMaterialPosition, move-up [4] -> [2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, 3, 4, 5]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [4],
|
||||
to: [2],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([0, 1, 4, 2, 3, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [3, 2, 1] -> [2]
|
||||
test('moveMaterialPosition, move-up [3, 2, 1] -> [2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [3, 2, 1],
|
||||
to: [2],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([0, 1, 1, 2, [0, 1, [0, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1] -> [1, 2, 3]
|
||||
test('moveMaterialPosition, move-up [1] -> [1, 2, 3]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [1],
|
||||
to: [1, 2, 3],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5] -> [1, 2, 2]
|
||||
test('moveMaterialPosition, move-up [1, 2, 3, 4, 5] -> [1, 2, 2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [1, 2, 3, 4, 5],
|
||||
to: [1, 2, 2],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 5, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [1] -> [1]
|
||||
test('moveMaterialPosition, move-up [1] -> [1]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [1],
|
||||
to: [1],
|
||||
});
|
||||
|
||||
const expectResult = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
|
||||
// [2, 4] -> [1, 2]
|
||||
test('moveMaterialPosition, move-up [1, 2] -> [2, 4]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]);
|
||||
|
||||
moveMaterialPosition(list, {
|
||||
from: [2, 4],
|
||||
to: [1, 2],
|
||||
});
|
||||
const expectResult = generateMaterials([0, [0, 1, 4, 2, 3, 4], [0, 1, 2, 3], 3, 4]);
|
||||
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { istype } from '@idraw/util';
|
||||
|
||||
describe('@idraw/util: lib/istype', () => {
|
||||
const _num = 123;
|
||||
const _str = 'abc';
|
||||
const _null = null;
|
||||
const _undefined = undefined;
|
||||
const _arr = [0, 1, 2, 3];
|
||||
const _json = {
|
||||
a: [0, 1, 2, 3],
|
||||
b: {
|
||||
c: 123
|
||||
}
|
||||
};
|
||||
const _func = function () {};
|
||||
const _asyncfunc = async function () {};
|
||||
const _promise = new Promise(() => {});
|
||||
|
||||
test('istype.type', async () => {
|
||||
expect(istype.type(_num)).toStrictEqual('Number');
|
||||
expect(istype.type(_str)).toStrictEqual('String');
|
||||
expect(istype.type(_undefined)).toStrictEqual('Undefined');
|
||||
expect(istype.type(_arr)).toStrictEqual('Array');
|
||||
expect(istype.type(_null)).toStrictEqual('Null');
|
||||
expect(istype.type(_json)).toStrictEqual('Object');
|
||||
expect(istype.type(_func)).toStrictEqual('Function');
|
||||
expect(istype.type(_asyncfunc)).toStrictEqual('AsyncFunction');
|
||||
expect(istype.type(_promise)).toStrictEqual('Promise');
|
||||
});
|
||||
|
||||
test('istype.array', async () => {
|
||||
expect(istype.array(_arr)).toStrictEqual(true);
|
||||
expect(istype.array(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.json', async () => {
|
||||
expect(istype.json(_json)).toStrictEqual(true);
|
||||
expect(istype.json(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.function', async () => {
|
||||
expect(istype.function(_func)).toStrictEqual(true);
|
||||
expect(istype.function(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.asyncFunction', async () => {
|
||||
expect(istype.asyncFunction(_asyncfunc)).toStrictEqual(true);
|
||||
expect(istype.asyncFunction(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.string', async () => {
|
||||
expect(istype.string(_str)).toStrictEqual(true);
|
||||
expect(istype.string(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.number', async () => {
|
||||
expect(istype.number(_num)).toStrictEqual(true);
|
||||
expect(istype.number(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.undefined', async () => {
|
||||
expect(istype.undefined(_undefined)).toStrictEqual(true);
|
||||
expect(istype.undefined(null)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.null', async () => {
|
||||
expect(istype.null(_null)).toStrictEqual(true);
|
||||
expect(istype.null(1)).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('istype.promise', async () => {
|
||||
expect(istype.promise(_promise)).toStrictEqual(true);
|
||||
expect(istype.promise(null)).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import '../../../../__tests__/polyfill/image';
|
||||
import { loadHTML, loadImage, loadSVG } from '@idraw/util';
|
||||
import { parseHTMLToDataURL, parseSVGToDataURL } from '../../src/view/parser';
|
||||
|
||||
describe('@idraw/util: lib/loader', () => {
|
||||
test('loadHTML', async () => {
|
||||
const html = `
|
||||
<style>
|
||||
.btn-box {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.btn {
|
||||
line-height: 1.5715;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 2px #00000004;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 32px;
|
||||
padding: 4px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 2px;
|
||||
color: #000000d9;
|
||||
background: #fff;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background: #1890ff;
|
||||
border-color: #1890ff;
|
||||
text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
|
||||
box-shadow: 0 2px #0000000b;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div class="btn-box" style="margin-top: 0;">
|
||||
<button class="btn" >
|
||||
<span>Button</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="btn-box">
|
||||
<button class="btn btn-primary">
|
||||
<span>Button Primary</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const opts = {
|
||||
width: 120,
|
||||
height: 80
|
||||
};
|
||||
const result = await loadHTML(html, opts);
|
||||
const expectDataURL = await parseHTMLToDataURL(html, opts);
|
||||
const expectImage = await loadImage(expectDataURL);
|
||||
expect(result.src).toStrictEqual(expectImage.src);
|
||||
});
|
||||
|
||||
test('loadSVG', async () => {
|
||||
const svg = `<svg t="1622524780663" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8365" width="200" height="200"><path d="M881 442.4H519.7v148.5h206.4c-8.9 48-35.9 88.6-76.6 115.8-34.4 23-78.3 36.6-129.9 36.6-99.9 0-184.4-67.5-214.6-158.2-7.6-23-12-47.6-12-72.9s4.4-49.9 12-72.9c30.3-90.6 114.8-158.1 214.7-158.1 56.3 0 106.8 19.4 146.6 57.4l110-110.1c-66.5-62-153.2-100-256.6-100-149.9 0-279.6 86-342.7 211.4-26 51.8-40.8 110.4-40.8 172.4S151 632.8 177 684.6C240.1 810 369.8 896 519.7 896c103.6 0 190.4-34.4 253.8-93 72.5-66.8 114.4-165.2 114.4-282.1 0-27.2-2.4-53.3-6.9-78.5z" p-id="8366" fill="#1296db"></path></svg>`;
|
||||
const result = await loadSVG(svg);
|
||||
const expectDataURL = await parseSVGToDataURL(svg);
|
||||
const expectImage = await loadImage(expectDataURL);
|
||||
expect(result.src).toStrictEqual(expectImage.src);
|
||||
});
|
||||
});
|
||||
77
packages/util/__tests__/lib/material.test.ts
Normal file
77
packages/util/__tests__/lib/material.test.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { createUUID, getMaterialPositionFromList } from '@idraw/util';
|
||||
import type { StrictMaterial } from '@idraw/types';
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
||||
|
||||
function generateMaterials(list: any[]): StrictMaterial[] {
|
||||
const elements: StrictMaterial[] = list.map((item, i) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getElemBase(),
|
||||
id: `${i}-${createUUID()}`,
|
||||
type: 'group',
|
||||
children: generateMaterials(item),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
id: `${i}-${createUUID()}`,
|
||||
type: 'rect',
|
||||
};
|
||||
}
|
||||
}) as StrictMaterial[];
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: element ', () => {
|
||||
// [4]
|
||||
test('getMaterialPositionFromList [4]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
const id = (list as any)[4].id;
|
||||
const position = getMaterialPositionFromList(id, list);
|
||||
expect(position).toStrictEqual([4]);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5]
|
||||
test('getMaterialPositionFromList [1, 2, 3, 4, 5]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
const id = (list as any)[1].children[2].children[3].children[4].children[5].id;
|
||||
const position = getMaterialPositionFromList(id, list);
|
||||
expect(position).toStrictEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4]
|
||||
test('getMaterialPositionFromList [1, 2, 3, 4, 5]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
const id = (list as any)[1].children[2].children[3].children[4].id;
|
||||
const position = getMaterialPositionFromList(id, list);
|
||||
expect(position).toStrictEqual([1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
34
packages/util/__tests__/lib/merge-material.test.ts
Normal file
34
packages/util/__tests__/lib/merge-material.test.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { Material } from '@idraw/types';
|
||||
import { mergeMaterial } from '@idraw/util';
|
||||
|
||||
const material: Material = {
|
||||
id: 'z001zeaa-5c52-590c-f8a2-d2d89d25471f',
|
||||
type: 'rect',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 200,
|
||||
height: 200,
|
||||
fill: {
|
||||
type: 'linear-gradient',
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 0, y: 200 },
|
||||
stops: [
|
||||
{ offset: 0, color: '#009688' },
|
||||
{ offset: 0.5, color: '#ffeb3b' },
|
||||
{ offset: 1, color: '#ff5722' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('@idraw/util mergeMaterial', () => {
|
||||
test('mergeMaterial', () => {
|
||||
const result = mergeMaterial(material, {
|
||||
fill: {
|
||||
stops: [
|
||||
{ offset: 0, color: '#009688' },
|
||||
{ offset: 1, color: '#ff5722' },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
import { moveElementPosition, calcRevertMovePosition } from '@idraw/util';
|
||||
import type { Elements } from '@idraw/types';
|
||||
const getElemBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 1
|
||||
};
|
||||
};
|
||||
|
||||
function generateElements(list: any[]): Elements {
|
||||
const elements: Elements = list.map((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `group`,
|
||||
type: 'group',
|
||||
detail: {
|
||||
children: generateElements(item)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getElemBase(),
|
||||
uuid: `rect-${item}`,
|
||||
type: 'rect',
|
||||
detail: {}
|
||||
};
|
||||
}
|
||||
}) as Elements;
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: handle-element ', () => {
|
||||
// [2] -> [4]
|
||||
// [0, 1, 2, 3, 2, 4, 5]
|
||||
// [0, 1, 3, 2, 4, 5]
|
||||
test('moveElementPosition, move-down [2] -> [4]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, 3, 4, 5]);
|
||||
const from = [2];
|
||||
const to = [4];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, 1, 3, 2, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result [2] [3]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [3] -> [2]
|
||||
moveElementPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to
|
||||
});
|
||||
expect(list).toStrictEqual(generateElements([0, 1, 2, 3, 4, 5]));
|
||||
});
|
||||
|
||||
// [4] -> [2] yes
|
||||
// [0, 1, 4, 2, 3, 4, 5]
|
||||
// [0, 1, 4, 2, 3, 5]
|
||||
test('moveElementPosition, move-up [4] -> [2]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, 3, 4, 5]);
|
||||
const from = [4];
|
||||
const to = [2];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, 1, 4, 2, 3, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result [5] [2]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [2] -> [5]
|
||||
moveElementPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to
|
||||
});
|
||||
expect(list).toStrictEqual(generateElements([0, 1, 2, 3, 4, 5]));
|
||||
});
|
||||
|
||||
// [3, 2, 1] -> [2]
|
||||
test('moveElementPosition, move-up [3, 2, 1] -> [2]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
const from = [3, 2, 1];
|
||||
const to = [2];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, 1, 1, 2, [0, 1, [0, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result from: [ 4, 2, 1 ], to: [ 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [2] -> [ 4, 2, 1 ]
|
||||
moveElementPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to
|
||||
});
|
||||
expect(list).toStrictEqual(generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]));
|
||||
});
|
||||
|
||||
// [1] -> [1, 2, 3]
|
||||
test('moveElementPosition, move-up [1] -> [1, 2, 3]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
const from = [1];
|
||||
const to = [1, 2, 3];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action null
|
||||
const revertInfo = calcRevertMovePosition({ from, to });
|
||||
expect(revertInfo).toBeNull();
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5] -> [1, 2, 2]
|
||||
test('moveElementPosition, move-up [1, 2, 3, 4, 5] -> [1, 2, 2]', () => {
|
||||
const list: Elements = generateElements([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]);
|
||||
const from = [1, 2, 3, 4, 5];
|
||||
const to = [1, 2, 2];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([
|
||||
0,
|
||||
[0, 1, [0, 1, 5, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result from: [ 1, 2, 4, 4, 5 ], to: [ 1, 2, 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [ 1, 2, 2 ] -> [ 1, 2, 4, 4, 5 ]
|
||||
moveElementPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to
|
||||
});
|
||||
expect(list).toStrictEqual(
|
||||
generateElements([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5])
|
||||
);
|
||||
});
|
||||
|
||||
// [1] -> [1]
|
||||
test('moveElementPosition, move-up [1] -> [1]', () => {
|
||||
const list: Elements = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
const from = [1];
|
||||
const to = [1];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action null
|
||||
const revertInfo = calcRevertMovePosition({ from, to });
|
||||
expect(revertInfo).toBeNull();
|
||||
});
|
||||
|
||||
// [2, 4] -> [1, 2]
|
||||
test('moveElementPosition, move-up [1, 2] -> [2, 4]', () => {
|
||||
const list: Elements = generateElements([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]);
|
||||
const from = [2, 4];
|
||||
const to = [1, 2];
|
||||
moveElementPosition(list, {
|
||||
from,
|
||||
to
|
||||
});
|
||||
const expectResult = generateElements([0, [0, 1, 4, 2, 3, 4], [0, 1, 2, 3], 3, 4]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
|
||||
// revert action
|
||||
// result from: [ 2, 4 ], to: [ 1, 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
|
||||
// revert [ 1, 2 ] -> [ 2, 4 ]
|
||||
moveElementPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to
|
||||
});
|
||||
expect(list).toStrictEqual(generateElements([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]));
|
||||
});
|
||||
});
|
||||
196
packages/util/__tests__/lib/move-material.test.ts
Normal file
196
packages/util/__tests__/lib/move-material.test.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import { moveMaterialPosition, calcRevertMovePosition } from '@idraw/util';
|
||||
import type { StrictMaterial } from '@idraw/types';
|
||||
const getMaterialBase = () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
};
|
||||
|
||||
function generateMaterials(list: any[]): StrictMaterial[] {
|
||||
const elements: StrictMaterial[] = list.map((item) => {
|
||||
if (Array.isArray(item)) {
|
||||
return {
|
||||
...getMaterialBase(),
|
||||
id: `group`,
|
||||
type: 'group',
|
||||
children: generateMaterials(item),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...getMaterialBase(),
|
||||
id: `rect-${item}`,
|
||||
type: 'rect',
|
||||
};
|
||||
}
|
||||
}) as StrictMaterial[];
|
||||
return elements;
|
||||
}
|
||||
|
||||
describe('@idraw/util: handle-element ', () => {
|
||||
// [2] -> [4]
|
||||
// [0, 1, 2, 3, 2, 4, 5]
|
||||
// [0, 1, 3, 2, 4, 5]
|
||||
test('moveMaterialPosition, move-down [2] -> [4]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, 3, 4, 5]);
|
||||
const from = [2];
|
||||
const to = [4];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, 1, 3, 2, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result [2] [3]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [3] -> [2]
|
||||
moveMaterialPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to,
|
||||
});
|
||||
expect(list).toStrictEqual(generateMaterials([0, 1, 2, 3, 4, 5]));
|
||||
});
|
||||
|
||||
// [4] -> [2] yes
|
||||
// [0, 1, 4, 2, 3, 4, 5]
|
||||
// [0, 1, 4, 2, 3, 5]
|
||||
test('moveMaterialPosition, move-up [4] -> [2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, 3, 4, 5]);
|
||||
const from = [4];
|
||||
const to = [2];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, 1, 4, 2, 3, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result [5] [2]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [2] -> [5]
|
||||
moveMaterialPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to,
|
||||
});
|
||||
expect(list).toStrictEqual(generateMaterials([0, 1, 2, 3, 4, 5]));
|
||||
});
|
||||
|
||||
// [3, 2, 1] -> [2]
|
||||
test('moveMaterialPosition, move-up [3, 2, 1] -> [2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
const from = [3, 2, 1];
|
||||
const to = [2];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, 1, 1, 2, [0, 1, [0, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result from: [ 4, 2, 1 ], to: [ 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [2] -> [ 4, 2, 1 ]
|
||||
moveMaterialPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to,
|
||||
});
|
||||
expect(list).toStrictEqual(generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]));
|
||||
});
|
||||
|
||||
// [1] -> [1, 2, 3]
|
||||
test('moveMaterialPosition, move-up [1] -> [1, 2, 3]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
const from = [1];
|
||||
const to = [1, 2, 3];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, [0, 1, [0, 1, 2, 3, 4, 5], 3, 4, 5], 2, 3, 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action null
|
||||
const revertInfo = calcRevertMovePosition({ from, to });
|
||||
expect(revertInfo).toBeNull();
|
||||
});
|
||||
|
||||
// [1, 2, 3, 4, 5] -> [1, 2, 2]
|
||||
test('moveMaterialPosition, move-up [1, 2, 3, 4, 5] -> [1, 2, 2]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
const from = [1, 2, 3, 4, 5];
|
||||
const to = [1, 2, 2];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([
|
||||
0,
|
||||
[0, 1, [0, 1, 5, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4], 5], 4, 5], 3, 4, 5],
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action
|
||||
// result from: [ 1, 2, 4, 4, 5 ], to: [ 1, 2, 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
// revert [ 1, 2, 2 ] -> [ 1, 2, 4, 4, 5 ]
|
||||
moveMaterialPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to,
|
||||
});
|
||||
expect(list).toStrictEqual(
|
||||
generateMaterials([0, [0, 1, [0, 1, 2, [0, 1, 2, 3, [0, 1, 2, 3, 4, 5], 5], 4, 5], 3, 4, 5], 2, 3, 4, 5])
|
||||
);
|
||||
});
|
||||
|
||||
// [1] -> [1]
|
||||
test('moveMaterialPosition, move-up [1] -> [1]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
const from = [1];
|
||||
const to = [1];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, 1, 2, [0, 1, [0, 1, 2, 3], 3], 4, 5]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
// revert action null
|
||||
const revertInfo = calcRevertMovePosition({ from, to });
|
||||
expect(revertInfo).toBeNull();
|
||||
});
|
||||
|
||||
// [2, 4] -> [1, 2]
|
||||
test('moveMaterialPosition, move-up [1, 2] -> [2, 4]', () => {
|
||||
const list: StrictMaterial[] = generateMaterials([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]);
|
||||
const from = [2, 4];
|
||||
const to = [1, 2];
|
||||
moveMaterialPosition(list, {
|
||||
from,
|
||||
to,
|
||||
});
|
||||
const expectResult = generateMaterials([0, [0, 1, 4, 2, 3, 4], [0, 1, 2, 3], 3, 4]);
|
||||
expect(list).toStrictEqual(expectResult);
|
||||
|
||||
// revert action
|
||||
// result from: [ 2, 4 ], to: [ 1, 2 ]
|
||||
const revertInfo = calcRevertMovePosition({ from, to }) as { from: number[]; to: number[] };
|
||||
|
||||
// revert [ 1, 2 ] -> [ 2, 4 ]
|
||||
moveMaterialPosition(list, {
|
||||
from: revertInfo.from,
|
||||
to: revertInfo.to,
|
||||
});
|
||||
expect(list).toStrictEqual(generateMaterials([0, [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], 3, 4]));
|
||||
});
|
||||
});
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
import { parseHTMLToDataURL, parseSVGToDataURL } from '../../src/view/parser';
|
||||
|
||||
describe('@idraw/util: lib/parser', () => {
|
||||
test('parseHTMLToDataURL', async () => {
|
||||
const result = await parseHTMLToDataURL(
|
||||
`
|
||||
<style>
|
||||
.btn {
|
||||
line-height: 1.5715;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 2px #00000004;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 32px;
|
||||
padding: 4px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 2px;
|
||||
color: #000000d9;
|
||||
background: #fff;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
</style>
|
||||
<button class="btn" >
|
||||
<span>Button</span>
|
||||
</button>
|
||||
`,
|
||||
{
|
||||
width: 120,
|
||||
height: 80
|
||||
}
|
||||
);
|
||||
expect(result).toStrictEqual(
|
||||
`data:image/svg+xml;charset=utf-8;base64,CiAgICA8c3ZnIAogICAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIAogICAgICB3aWR0aD0iMTIwIiAKICAgICAgaGVpZ2h0ID0gIjgwIj4KICAgICAgPGZvcmVpZ25PYmplY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSI+CiAgICAgICAgPGRpdiB4bWxucyA9ICJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj4KICAgICAgICAgIAogICAgPHN0eWxlPgogICAgLmJ0biB7CiAgICAgIGxpbmUtaGVpZ2h0OiAxLjU3MTU7CiAgICAgIHBvc2l0aW9uOiByZWxhdGl2ZTsKICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogICAgICBmb250LXdlaWdodDogNDAwOwogICAgICB3aGl0ZS1zcGFjZTogbm93cmFwOwogICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgIGJhY2tncm91bmQtaW1hZ2U6IG5vbmU7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHRyYW5zcGFyZW50OwogICAgICBib3gtc2hhZG93OiAwIDJweCAjMDAwMDAwMDQ7CiAgICAgIGN1cnNvcjogcG9pbnRlcjsKICAgICAgdXNlci1zZWxlY3Q6IG5vbmU7CiAgICAgIGhlaWdodDogMzJweDsKICAgICAgcGFkZGluZzogNHB4IDE1cHg7CiAgICAgIGZvbnQtc2l6ZTogMTRweDsKICAgICAgYm9yZGVyLXJhZGl1czogMnB4OwogICAgICBjb2xvcjogIzAwMDAwMGQ5OwogICAgICBiYWNrZ3JvdW5kOiAjZmZmOwogICAgICBib3JkZXItY29sb3I6ICNkOWQ5ZDk7CiAgICB9CiAgICA8L3N0eWxlPgogICAgPGJ1dHRvbiBjbGFzcz0iYnRuIiA+CiAgICAgIDxzcGFuPkJ1dHRvbjwvc3Bhbj4KICAgIDwvYnV0dG9uPgogICAgCiAgICAgICAgPC9kaXY+CiAgICAgIDwvZm9yZWlnbk9iamVjdD4KICAgIDwvc3ZnPgogICAg`
|
||||
);
|
||||
});
|
||||
|
||||
test('parseSVGToDataURL', async () => {
|
||||
const result = await parseSVGToDataURL(
|
||||
`<svg t="1622524780663" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8365" width="200" height="200"><path d="M881 442.4H519.7v148.5h206.4c-8.9 48-35.9 88.6-76.6 115.8-34.4 23-78.3 36.6-129.9 36.6-99.9 0-184.4-67.5-214.6-158.2-7.6-23-12-47.6-12-72.9s4.4-49.9 12-72.9c30.3-90.6 114.8-158.1 214.7-158.1 56.3 0 106.8 19.4 146.6 57.4l110-110.1c-66.5-62-153.2-100-256.6-100-149.9 0-279.6 86-342.7 211.4-26 51.8-40.8 110.4-40.8 172.4S151 632.8 177 684.6C240.1 810 369.8 896 519.7 896c103.6 0 190.4-34.4 253.8-93 72.5-66.8 114.4-165.2 114.4-282.1 0-27.2-2.4-53.3-6.9-78.5z" p-id="8366" fill="#1296db"></path></svg>`
|
||||
);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
`data:image/svg+xml;charset=utf-8;base64,PHN2ZyB0PSIxNjIyNTI0NzgwNjYzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjgzNjUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNODgxIDQ0Mi40SDUxOS43djE0OC41aDIwNi40Yy04LjkgNDgtMzUuOSA4OC42LTc2LjYgMTE1LjgtMzQuNCAyMy03OC4zIDM2LjYtMTI5LjkgMzYuNi05OS45IDAtMTg0LjQtNjcuNS0yMTQuNi0xNTguMi03LjYtMjMtMTItNDcuNi0xMi03Mi45czQuNC00OS45IDEyLTcyLjljMzAuMy05MC42IDExNC44LTE1OC4xIDIxNC43LTE1OC4xIDU2LjMgMCAxMDYuOCAxOS40IDE0Ni42IDU3LjRsMTEwLTExMC4xYy02Ni41LTYyLTE1My4yLTEwMC0yNTYuNi0xMDAtMTQ5LjkgMC0yNzkuNiA4Ni0zNDIuNyAyMTEuNC0yNiA1MS44LTQwLjggMTEwLjQtNDAuOCAxNzIuNFMxNTEgNjMyLjggMTc3IDY4NC42QzI0MC4xIDgxMCAzNjkuOCA4OTYgNTE5LjcgODk2YzEwMy42IDAgMTkwLjQtMzQuNCAyNTMuOC05MyA3Mi41LTY2LjggMTE0LjQtMTY1LjIgMTE0LjQtMjgyLjEgMC0yNy4yLTIuNC01My4zLTYuOS03OC41eiIgcC1pZD0iODM2NiIgZmlsbD0iIzEyOTZkYiI+PC9wYXRoPjwvc3ZnPg==`
|
||||
);
|
||||
});
|
||||
});
|
||||
189
packages/util/__tests__/lib/path-to-command.test.ts
Normal file
189
packages/util/__tests__/lib/path-to-command.test.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import {
|
||||
convertSVGPathToContext2DCommands,
|
||||
calcSVGPathBoundingBox,
|
||||
calcPathCommondsBoundingBox,
|
||||
parseSVGPath,
|
||||
} from '@idraw/util';
|
||||
|
||||
describe('convertSVGPathToContext2DCommands', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Math, 'random').mockReturnValue(0.987654321);
|
||||
});
|
||||
test('convertSVGPathToContext2DCommands', () => {
|
||||
const svgPath = `M 10,30 L 50,30 H 80 V 60 C 100,60 120,80 120,100 S 140,140 160,140 Q 180,140 190,150 T 210,170 A 30,30 0 0,1 240,200 Z m 10,10 l 10,0 h 10 v 10 c 0,0 10,10 10,10 s 10,10 10,10 q 0,0 10,10 t 10,10 a 5,5 0 0,1 10,10`;
|
||||
|
||||
const cmds = convertSVGPathToContext2DCommands(svgPath);
|
||||
expect(cmds).toStrictEqual([
|
||||
{ id: 'zk00000ytuzk0000', name: 'beginPath', params: null },
|
||||
{ id: 'zk00000ytuzk0000', name: 'moveTo', params: { x: 10, y: 30 } },
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 23.333333333333336, cp1y: 30, cp2x: 36.66666666666667, cp2y: 30, x: 50, y: 30 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 60, cp1y: 30, cp2x: 70, cp2y: 30, x: 80, y: 30 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 80, cp1y: 40, cp2x: 80, cp2y: 50, x: 80, y: 60 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 100, cp1y: 60, cp2x: 120, cp2y: 80, x: 120, y: 100 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 120, cp1y: 120, cp2x: 140, cp2y: 140, x: 160, y: 140 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: {
|
||||
cp1x: 173.33333333333334,
|
||||
cp1y: 140,
|
||||
cp2x: 183.33333333333334,
|
||||
cp2y: 143.33333333333334,
|
||||
x: 190,
|
||||
y: 150,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: {
|
||||
cp1x: 194.44444444444443,
|
||||
cp1y: 154.44444444444443,
|
||||
cp2x: 201.11111111111111,
|
||||
cp2y: 161.11111111111111,
|
||||
x: 210,
|
||||
y: 170,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'ellipse',
|
||||
params: {
|
||||
centerX: 210,
|
||||
centerY: 200,
|
||||
radiusX: 30,
|
||||
radiusY: 30,
|
||||
rotation: 0,
|
||||
startRadian: -1.5707963267948966,
|
||||
endRadian: 0,
|
||||
anticlockwise: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: {
|
||||
cp1x: 163.33333333333331,
|
||||
cp1y: 143.33333333333334,
|
||||
cp2x: 86.66666666666666,
|
||||
cp2y: 86.66666666666667,
|
||||
x: 10,
|
||||
y: 30,
|
||||
},
|
||||
},
|
||||
{ id: 'zk00000ytuzk0000', name: 'closePath', params: null },
|
||||
{ id: 'zk00000ytuzk0000', name: 'moveTo', params: { x: 20, y: 40 } },
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 23.333333333333332, cp1y: 40, cp2x: 26.666666666666668, cp2y: 40, x: 30, y: 40 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 33.333333333333336, cp1y: 40, cp2x: 36.666666666666664, cp2y: 40, x: 40, y: 40 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 40, cp1y: 43.333333333333336, cp2x: 40, cp2y: 46.666666666666664, x: 40, y: 50 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 40, cp1y: 50, cp2x: 50, cp2y: 60, x: 50, y: 60 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 50, cp1y: 60, cp2x: 60, cp2y: 70, x: 60, y: 70 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: { cp1x: 60, cp1y: 70, cp2x: 63.333333333333336, cp2y: 73.33333333333333, x: 70, y: 80 },
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'bezierCurveTo',
|
||||
params: {
|
||||
cp1x: 74.44444444444444,
|
||||
cp1y: 84.44444444444444,
|
||||
cp2x: 77.77777777777777,
|
||||
cp2y: 87.77777777777779,
|
||||
x: 80,
|
||||
y: 90,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'zk00000ytuzk0000',
|
||||
name: 'ellipse',
|
||||
params: {
|
||||
centerX: 85,
|
||||
centerY: 95,
|
||||
radiusX: 7.0710678118654755,
|
||||
radiusY: 7.0710678118654755,
|
||||
rotation: 0,
|
||||
startRadian: -2.356194490192345,
|
||||
endRadian: 0.7853981633974483,
|
||||
anticlockwise: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcSVGPathBoundingBox', () => {
|
||||
test('calcSVGPathBoundingBox', () => {
|
||||
const svgPath = `M 10,30 L 50,30 H 80 V 60 C 100,60 120,80 120,100 S 140,140 160,140 Q 180,140 190,150 T 210,170 A 30,30 0 0,1 240,200 Z m 10,10 l 10,0 h 10 v 10 c 0,0 10,10 10,10 s 10,10 10,10 q 0,0 10,10 t 10,10 a 5,5 0 0,1 10,10`;
|
||||
const boundingBox = calcSVGPathBoundingBox(svgPath);
|
||||
expect(boundingBox).toStrictEqual({
|
||||
minX: 10,
|
||||
minY: 30,
|
||||
maxX: 240,
|
||||
maxY: 200,
|
||||
width: 230,
|
||||
height: 170,
|
||||
start: { x: 10, y: 30 },
|
||||
end: { x: 240, y: 200 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcPathCommondsBoundingBox', () => {
|
||||
test('calcPathCommondsBoundingBox', () => {
|
||||
const svgPath = `M 10,30 L 50,30 H 80 V 60 C 100,60 120,80 120,100 S 140,140 160,140 Q 180,140 190,150 T 210,170 A 30,30 0 0,1 240,200 Z m 10,10 l 10,0 h 10 v 10 c 0,0 10,10 10,10 s 10,10 10,10 q 0,0 10,10 t 10,10 a 5,5 0 0,1 10,10`;
|
||||
const cmds = parseSVGPath(svgPath);
|
||||
const boundingBox = calcPathCommondsBoundingBox(cmds);
|
||||
expect(boundingBox).toStrictEqual({
|
||||
minX: 10,
|
||||
minY: 30,
|
||||
maxX: 240,
|
||||
maxY: 200,
|
||||
width: 230,
|
||||
height: 170,
|
||||
start: { x: 10, y: 30 },
|
||||
end: { x: 240, y: 200 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,292 +0,0 @@
|
|||
import { createElement, resizeEffectGroupElement } from '@idraw/util';
|
||||
import type { Element } from '@idraw/types';
|
||||
|
||||
const createGroupByRatio = (opts?: { xRatio?: number; yRatio?: number }) => {
|
||||
const { xRatio = 1, yRatio = 1 } = opts || {};
|
||||
const minRatio = Math.min(xRatio, yRatio);
|
||||
const maxRatio = Math.max(xRatio, yRatio);
|
||||
const midRatio = (minRatio + maxRatio) / 2;
|
||||
|
||||
const group: Element<'group'> = createElement('group', {
|
||||
uuid: 'test-001',
|
||||
x: 10,
|
||||
y: 10,
|
||||
w: 2000 * xRatio,
|
||||
h: 2000 * yRatio,
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-002', x: 20 * xRatio, y: 20 * yRatio, w: 20 * xRatio, h: 20 * yRatio }),
|
||||
createElement('circle', { uuid: 'test-003', x: 40 * xRatio, y: 40 * yRatio, w: 40 * xRatio, h: 40 * yRatio }),
|
||||
createElement('text', {
|
||||
uuid: 'test-004',
|
||||
x: 60 * xRatio,
|
||||
y: 60 * yRatio,
|
||||
w: 60 * xRatio,
|
||||
h: 60 * yRatio,
|
||||
detail: {
|
||||
fontSize: 16 * midRatio,
|
||||
// lineHeight: 32 * midRatio,
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', {
|
||||
uuid: 'test-005',
|
||||
x: 80 * xRatio,
|
||||
y: 80 * yRatio,
|
||||
w: 80 * xRatio,
|
||||
h: 80 * yRatio,
|
||||
detail: { src: 'https://example.com/002.png' }
|
||||
}),
|
||||
createElement('group', {
|
||||
uuid: 'test-100',
|
||||
x: 500 * xRatio,
|
||||
y: 500 * yRatio,
|
||||
w: 1000 * xRatio,
|
||||
h: 1000 * yRatio,
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-101',
|
||||
x: 20 * xRatio,
|
||||
y: 20 * yRatio,
|
||||
w: 20 * xRatio,
|
||||
h: 20 * yRatio
|
||||
}),
|
||||
createElement('circle', {
|
||||
uuid: 'test-102',
|
||||
x: 40 * xRatio,
|
||||
y: 40 * yRatio,
|
||||
w: 40 * xRatio,
|
||||
h: 40 * yRatio
|
||||
}),
|
||||
createElement('text', {
|
||||
uuid: 'test-103',
|
||||
x: 60 * xRatio,
|
||||
y: 60 * yRatio,
|
||||
w: 60 * xRatio,
|
||||
h: 60 * yRatio,
|
||||
detail: {
|
||||
fontSize: 16 * midRatio,
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', {
|
||||
uuid: 'test-104',
|
||||
x: 80 * xRatio,
|
||||
y: 80 * yRatio,
|
||||
w: 80 * xRatio,
|
||||
h: 80 * yRatio,
|
||||
detail: { src: 'https://example.com/002.png' }
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
operations: {
|
||||
resizeEffect: 'deepResize'
|
||||
}
|
||||
});
|
||||
return group;
|
||||
};
|
||||
|
||||
const createGroupByFixed = (opts: { moveX: number; moveY: number; moveW: number; moveH: number }) => {
|
||||
const { moveX, moveY, moveW, moveH } = opts || {};
|
||||
|
||||
const group: Element<'group'> = createElement('group', {
|
||||
uuid: 'test-001',
|
||||
x: 10 + moveX,
|
||||
y: 10 + moveY,
|
||||
w: 2000 + moveW,
|
||||
h: 2000 + moveH,
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', { uuid: 'test-002', x: 20 - moveX, y: 20 - moveY, w: 20, h: 20 }),
|
||||
createElement('circle', { uuid: 'test-003', x: 40 - moveX, y: 40 - moveY, w: 40, h: 40 }),
|
||||
createElement('text', {
|
||||
uuid: 'test-004',
|
||||
x: 60 - moveX,
|
||||
y: 60 - moveY,
|
||||
w: 60,
|
||||
h: 60,
|
||||
detail: {
|
||||
fontSize: 16,
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', {
|
||||
uuid: 'test-005',
|
||||
x: 80 - moveX,
|
||||
y: 80 - moveY,
|
||||
w: 80,
|
||||
h: 80,
|
||||
detail: { src: 'https://example.com/002.png' }
|
||||
}),
|
||||
createElement('group', {
|
||||
uuid: 'test-100',
|
||||
x: 500 - moveX,
|
||||
y: 500 - moveY,
|
||||
w: 1000,
|
||||
h: 1000,
|
||||
detail: {
|
||||
children: [
|
||||
createElement('rect', {
|
||||
uuid: 'test-101',
|
||||
x: 20,
|
||||
y: 20,
|
||||
w: 20,
|
||||
h: 20
|
||||
}),
|
||||
createElement('circle', {
|
||||
uuid: 'test-102',
|
||||
x: 40,
|
||||
y: 40,
|
||||
w: 40,
|
||||
h: 40
|
||||
}),
|
||||
createElement('text', {
|
||||
uuid: 'test-103',
|
||||
x: 60,
|
||||
y: 60,
|
||||
w: 60,
|
||||
h: 60,
|
||||
detail: {
|
||||
fontSize: 16,
|
||||
text: 'Text in Group'
|
||||
}
|
||||
}),
|
||||
createElement('image', {
|
||||
uuid: 'test-104',
|
||||
x: 80,
|
||||
y: 80,
|
||||
w: 80,
|
||||
h: 80,
|
||||
detail: { src: 'https://example.com/002.png' }
|
||||
})
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
operations: {
|
||||
resizeEffect: 'deepResize'
|
||||
}
|
||||
});
|
||||
return group;
|
||||
};
|
||||
|
||||
describe('resizeEffectGroupElement', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('deepSize', () => {
|
||||
const group = createGroupByRatio();
|
||||
const xRatio = 2;
|
||||
const yRatio = 3;
|
||||
|
||||
const record = resizeEffectGroupElement(
|
||||
group,
|
||||
{
|
||||
w: group.w * xRatio,
|
||||
h: group.h * yRatio
|
||||
},
|
||||
{
|
||||
resizeEffect: 'deepResize'
|
||||
}
|
||||
);
|
||||
|
||||
expect(group).toStrictEqual(
|
||||
createGroupByRatio({
|
||||
xRatio,
|
||||
yRatio
|
||||
})
|
||||
);
|
||||
|
||||
expect(record).toStrictEqual({
|
||||
type: 'resizeElements',
|
||||
time: 1735689600000,
|
||||
content: {
|
||||
method: 'modifyElements',
|
||||
before: [
|
||||
{ uuid: 'test-001', x: 10, y: 10, w: 2000, h: 2000 },
|
||||
{ x: 20, y: 20, w: 20, h: 20, uuid: 'test-002' },
|
||||
{ x: 40, y: 40, w: 40, h: 40, uuid: 'test-003' },
|
||||
{ x: 60, y: 60, w: 60, h: 60, uuid: 'test-004', 'detail.fontSize': 16 },
|
||||
{ x: 80, y: 80, w: 80, h: 80, uuid: 'test-005' },
|
||||
{ x: 500, y: 500, w: 1000, h: 1000, uuid: 'test-100' },
|
||||
{ x: 20, y: 20, w: 20, h: 20, uuid: 'test-101' },
|
||||
{ x: 40, y: 40, w: 40, h: 40, uuid: 'test-102' },
|
||||
{ x: 60, y: 60, w: 60, h: 60, uuid: 'test-103', 'detail.fontSize': 16 },
|
||||
{ x: 80, y: 80, w: 80, h: 80, uuid: 'test-104' }
|
||||
],
|
||||
after: [
|
||||
{ uuid: 'test-001', x: 10, y: 10, w: 4000, h: 6000 },
|
||||
{ x: 40, y: 60, w: 40, h: 60, uuid: 'test-002' },
|
||||
{ x: 80, y: 120, w: 80, h: 120, uuid: 'test-003' },
|
||||
{ x: 120, y: 180, w: 120, h: 180, uuid: 'test-004', 'detail.fontSize': 40 },
|
||||
{ x: 160, y: 240, w: 160, h: 240, uuid: 'test-005' },
|
||||
{ x: 1000, y: 1500, w: 2000, h: 3000, uuid: 'test-100' },
|
||||
{ x: 40, y: 60, w: 40, h: 60, uuid: 'test-101' },
|
||||
{ x: 80, y: 120, w: 80, h: 120, uuid: 'test-102' },
|
||||
{ x: 120, y: 180, w: 120, h: 180, uuid: 'test-103', 'detail.fontSize': 40 },
|
||||
{ x: 160, y: 240, w: 160, h: 240, uuid: 'test-104' }
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('fixed', () => {
|
||||
const group = createGroupByRatio();
|
||||
const moveX = 99;
|
||||
const moveY = 88;
|
||||
const moveW = 77;
|
||||
const moveH = 66;
|
||||
|
||||
const record = resizeEffectGroupElement(
|
||||
group,
|
||||
{
|
||||
x: group.x + moveX,
|
||||
y: group.y + moveY,
|
||||
w: group.w + moveW,
|
||||
h: group.h + moveH
|
||||
},
|
||||
{
|
||||
resizeEffect: 'fixed'
|
||||
}
|
||||
);
|
||||
|
||||
expect(group).toStrictEqual(
|
||||
createGroupByFixed({
|
||||
moveX,
|
||||
moveY,
|
||||
moveW,
|
||||
moveH
|
||||
})
|
||||
);
|
||||
|
||||
expect(record).toStrictEqual({
|
||||
type: 'resizeElements',
|
||||
time: 1735689600000,
|
||||
content: {
|
||||
method: 'modifyElements',
|
||||
before: [
|
||||
{ uuid: 'test-001', x: 10, y: 10, w: 2000, h: 2000 },
|
||||
{ uuid: 'test-002', x: 20, y: 20 },
|
||||
{ uuid: 'test-003', x: 40, y: 40 },
|
||||
{ uuid: 'test-004', x: 60, y: 60 },
|
||||
{ uuid: 'test-005', x: 80, y: 80 },
|
||||
{ uuid: 'test-100', x: 500, y: 500 }
|
||||
],
|
||||
after: [
|
||||
{ uuid: 'test-001', x: 109, y: 98, w: 2077, h: 2066 },
|
||||
{ uuid: 'test-002', x: -79, y: -68 },
|
||||
{ uuid: 'test-003', x: -59, y: -48 },
|
||||
{ uuid: 'test-004', x: -39, y: -28 },
|
||||
{ uuid: 'test-005', x: -19, y: -8 },
|
||||
{ uuid: 'test-100', x: 401, y: 412 }
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
289
packages/util/__tests__/lib/resize-material.test.ts
Normal file
289
packages/util/__tests__/lib/resize-material.test.ts
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
import { createMaterial, resizeEffectGroupMaterial } from '@idraw/util';
|
||||
import type { StrictMaterial } from '@idraw/types';
|
||||
|
||||
const createGroupByRatio = (opts?: { xRatio?: number; yRatio?: number }) => {
|
||||
const { xRatio = 1, yRatio = 1 } = opts || {};
|
||||
const minRatio = Math.min(xRatio, yRatio);
|
||||
const maxRatio = Math.max(xRatio, yRatio);
|
||||
const midRatio = (minRatio + maxRatio) / 2;
|
||||
|
||||
const group: StrictMaterial<'group'> = createMaterial('group', {
|
||||
id: 'test-001',
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 2000 * xRatio,
|
||||
height: 2000 * yRatio,
|
||||
children: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-002',
|
||||
x: 20 * xRatio,
|
||||
y: 20 * yRatio,
|
||||
width: 20 * xRatio,
|
||||
height: 20 * yRatio,
|
||||
}),
|
||||
createMaterial('circle', {
|
||||
id: 'test-003',
|
||||
x: 40 * xRatio,
|
||||
y: 40 * yRatio,
|
||||
width: 40 * xRatio,
|
||||
height: 40 * yRatio,
|
||||
}),
|
||||
createMaterial('text', {
|
||||
id: 'test-004',
|
||||
x: 60 * xRatio,
|
||||
y: 60 * yRatio,
|
||||
width: 60 * xRatio,
|
||||
height: 60 * yRatio,
|
||||
fontSize: 16 * midRatio,
|
||||
// lineHeight: 32 * midRatio,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-005',
|
||||
x: 80 * xRatio,
|
||||
y: 80 * yRatio,
|
||||
width: 80 * xRatio,
|
||||
height: 80 * yRatio,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
createMaterial('group', {
|
||||
id: 'test-100',
|
||||
x: 500 * xRatio,
|
||||
y: 500 * yRatio,
|
||||
width: 1000 * xRatio,
|
||||
height: 1000 * yRatio,
|
||||
children: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-101',
|
||||
x: 20 * xRatio,
|
||||
y: 20 * yRatio,
|
||||
width: 20 * xRatio,
|
||||
height: 20 * yRatio,
|
||||
}),
|
||||
createMaterial('circle', {
|
||||
id: 'test-102',
|
||||
x: 40 * xRatio,
|
||||
y: 40 * yRatio,
|
||||
width: 40 * xRatio,
|
||||
height: 40 * yRatio,
|
||||
}),
|
||||
createMaterial('text', {
|
||||
id: 'test-103',
|
||||
x: 60 * xRatio,
|
||||
y: 60 * yRatio,
|
||||
width: 60 * xRatio,
|
||||
height: 60 * yRatio,
|
||||
fontSize: 16 * midRatio,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-104',
|
||||
x: 80 * xRatio,
|
||||
y: 80 * yRatio,
|
||||
width: 80 * xRatio,
|
||||
height: 80 * yRatio,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
operations: {
|
||||
resizeEffect: 'deepResize',
|
||||
},
|
||||
});
|
||||
|
||||
return group;
|
||||
};
|
||||
|
||||
const createGroupByFixed = (opts: { moveX: number; moveY: number; moveW: number; moveH: number }) => {
|
||||
const { moveX, moveY, moveW, moveH } = opts || {};
|
||||
|
||||
const group: StrictMaterial<'group'> = createMaterial('group', {
|
||||
id: 'test-001',
|
||||
x: 10 + moveX,
|
||||
y: 10 + moveY,
|
||||
width: 2000 + moveW,
|
||||
height: 2000 + moveH,
|
||||
children: [
|
||||
createMaterial('rect', { id: 'test-002', x: 20 - moveX, y: 20 - moveY, width: 20, height: 20 }),
|
||||
createMaterial('circle', { id: 'test-003', x: 40 - moveX, y: 40 - moveY, width: 40, height: 40 }),
|
||||
createMaterial('text', {
|
||||
id: 'test-004',
|
||||
x: 60 - moveX,
|
||||
y: 60 - moveY,
|
||||
width: 60,
|
||||
height: 60,
|
||||
fontSize: 16,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-005',
|
||||
x: 80 - moveX,
|
||||
y: 80 - moveY,
|
||||
width: 80,
|
||||
height: 80,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
createMaterial('group', {
|
||||
id: 'test-100',
|
||||
x: 500 - moveX,
|
||||
y: 500 - moveY,
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
children: [
|
||||
createMaterial('rect', {
|
||||
id: 'test-101',
|
||||
x: 20,
|
||||
y: 20,
|
||||
width: 20,
|
||||
height: 20,
|
||||
}),
|
||||
createMaterial('circle', {
|
||||
id: 'test-102',
|
||||
x: 40,
|
||||
y: 40,
|
||||
width: 40,
|
||||
height: 40,
|
||||
}),
|
||||
createMaterial('text', {
|
||||
id: 'test-103',
|
||||
x: 60,
|
||||
y: 60,
|
||||
width: 60,
|
||||
height: 60,
|
||||
fontSize: 16,
|
||||
text: 'Text in Group',
|
||||
}),
|
||||
createMaterial('image', {
|
||||
id: 'test-104',
|
||||
x: 80,
|
||||
y: 80,
|
||||
width: 80,
|
||||
height: 80,
|
||||
src: 'https://example.com/002.png',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
operations: {
|
||||
resizeEffect: 'deepResize',
|
||||
},
|
||||
});
|
||||
return group;
|
||||
};
|
||||
|
||||
describe('resizeEffectGroupMaterial', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers().setSystemTime(new Date('2025-01-01'));
|
||||
});
|
||||
|
||||
test('deepSize', () => {
|
||||
const group = createGroupByRatio();
|
||||
const xRatio = 2;
|
||||
const yRatio = 3;
|
||||
|
||||
const record = resizeEffectGroupMaterial(
|
||||
group,
|
||||
{
|
||||
width: group.width * xRatio,
|
||||
height: group.height * yRatio,
|
||||
},
|
||||
{
|
||||
resizeEffect: 'deepResize',
|
||||
}
|
||||
);
|
||||
|
||||
expect(group).toStrictEqual(
|
||||
createGroupByRatio({
|
||||
xRatio,
|
||||
yRatio,
|
||||
})
|
||||
);
|
||||
|
||||
expect(record).toStrictEqual({
|
||||
type: 'resizeMaterials',
|
||||
time: 1735689600000,
|
||||
content: {
|
||||
method: 'modifyMaterials',
|
||||
before: [
|
||||
{ id: 'test-001', x: 10, y: 10, width: 2000, height: 2000 },
|
||||
{ x: 20, y: 20, width: 20, height: 20, id: 'test-002' },
|
||||
{ x: 40, y: 40, width: 40, height: 40, id: 'test-003' },
|
||||
{ x: 60, y: 60, width: 60, height: 60, id: 'test-004', fontSize: 16 },
|
||||
{ x: 80, y: 80, width: 80, height: 80, id: 'test-005' },
|
||||
{ x: 500, y: 500, width: 1000, height: 1000, id: 'test-100' },
|
||||
{ x: 20, y: 20, width: 20, height: 20, id: 'test-101' },
|
||||
{ x: 40, y: 40, width: 40, height: 40, id: 'test-102' },
|
||||
{ x: 60, y: 60, width: 60, height: 60, id: 'test-103', fontSize: 16 },
|
||||
{ x: 80, y: 80, width: 80, height: 80, id: 'test-104' },
|
||||
],
|
||||
after: [
|
||||
{ id: 'test-001', x: 10, y: 10, width: 4000, height: 6000 },
|
||||
{ x: 40, y: 60, width: 40, height: 60, id: 'test-002' },
|
||||
{ x: 80, y: 120, width: 80, height: 120, id: 'test-003' },
|
||||
{ x: 120, y: 180, width: 120, height: 180, id: 'test-004', fontSize: 40 },
|
||||
{ x: 160, y: 240, width: 160, height: 240, id: 'test-005' },
|
||||
{ x: 1000, y: 1500, width: 2000, height: 3000, id: 'test-100' },
|
||||
{ x: 40, y: 60, width: 40, height: 60, id: 'test-101' },
|
||||
{ x: 80, y: 120, width: 80, height: 120, id: 'test-102' },
|
||||
{ x: 120, y: 180, width: 120, height: 180, id: 'test-103', fontSize: 40 },
|
||||
{ x: 160, y: 240, width: 160, height: 240, id: 'test-104' },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('fixed', () => {
|
||||
const group = createGroupByRatio();
|
||||
const moveX = 99;
|
||||
const moveY = 88;
|
||||
const moveW = 77;
|
||||
const moveH = 66;
|
||||
|
||||
const record = resizeEffectGroupMaterial(
|
||||
group,
|
||||
{
|
||||
x: group.x + moveX,
|
||||
y: group.y + moveY,
|
||||
width: group.width + moveW,
|
||||
height: group.height + moveH,
|
||||
},
|
||||
{
|
||||
resizeEffect: 'fixed',
|
||||
}
|
||||
);
|
||||
|
||||
expect(group).toStrictEqual(
|
||||
createGroupByFixed({
|
||||
moveX,
|
||||
moveY,
|
||||
moveW,
|
||||
moveH,
|
||||
})
|
||||
);
|
||||
|
||||
expect(record).toStrictEqual({
|
||||
type: 'resizeMaterials',
|
||||
time: 1735689600000,
|
||||
content: {
|
||||
method: 'modifyMaterials',
|
||||
before: [
|
||||
{ id: 'test-001', x: 10, y: 10, width: 2000, height: 2000 },
|
||||
{ id: 'test-002', x: 20, y: 20 },
|
||||
{ id: 'test-003', x: 40, y: 40 },
|
||||
{ id: 'test-004', x: 60, y: 60 },
|
||||
{ id: 'test-005', x: 80, y: 80 },
|
||||
{ id: 'test-100', x: 500, y: 500 },
|
||||
],
|
||||
after: [
|
||||
{ id: 'test-001', x: 109, y: 98, width: 2077, height: 2066 },
|
||||
{ id: 'test-002', x: -79, y: -68 },
|
||||
{ id: 'test-003', x: -59, y: -48 },
|
||||
{ id: 'test-004', x: -39, y: -28 },
|
||||
{ id: 'test-005', x: -19, y: -8 },
|
||||
{ id: 'test-100', x: 401, y: 412 },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
83
packages/util/__tests__/lib/styles.test.ts
Normal file
83
packages/util/__tests__/lib/styles.test.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { parseStyles, injectStyles } from '@idraw/util';
|
||||
import type { StylesProps } from '@idraw/types';
|
||||
|
||||
// import { parseStyles, injectStyles, StylesProps } from './styleUtils';
|
||||
|
||||
describe('parseStyles', () => {
|
||||
it('should convert simple style object to CSS string', () => {
|
||||
const styles: StylesProps = {
|
||||
color: 'red',
|
||||
backgroundColor: 'blue',
|
||||
};
|
||||
const css = parseStyles(styles, '.test');
|
||||
expect(css).toBe('.test { color: red; background-color: blue; }');
|
||||
});
|
||||
|
||||
it('should handle nested pseudo-classes', () => {
|
||||
const styles: StylesProps = {
|
||||
color: 'red',
|
||||
'&:hover': {
|
||||
color: 'white',
|
||||
},
|
||||
};
|
||||
const css = parseStyles(styles, '.btn');
|
||||
expect(css).toContain('.btn { color: red; }');
|
||||
expect(css).toContain('.btn:hover { color: white; }');
|
||||
});
|
||||
|
||||
it('should handle child selectors', () => {
|
||||
const styles: StylesProps = {
|
||||
'.child': {
|
||||
fontSize: '12px',
|
||||
},
|
||||
};
|
||||
const css = parseStyles(styles, '.parent');
|
||||
expect(css).toContain('.parent .child { font-size: 12px; }');
|
||||
});
|
||||
|
||||
it('should handle media queries', () => {
|
||||
const styles: StylesProps = {
|
||||
'@media (max-width:600px)': {
|
||||
color: 'green',
|
||||
},
|
||||
};
|
||||
const css = parseStyles(styles, '.responsive');
|
||||
expect(css).toContain('@media (max-width:600px) { .responsive { color: green; } }');
|
||||
});
|
||||
});
|
||||
|
||||
describe('injectStyles', () => {
|
||||
beforeEach(() => {
|
||||
// Mock adoptedStyleSheets support
|
||||
(document as any).adoptedStyleSheets = [];
|
||||
(global as any).CSSStyleSheet = class {
|
||||
cssText = '';
|
||||
replaceSync(text: string) {
|
||||
this.cssText = text;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should inject style and return className', () => {
|
||||
const styles: StylesProps = {
|
||||
color: 'red',
|
||||
backgroundColor: 'blue',
|
||||
};
|
||||
|
||||
injectStyles({ styles, rootClassName: 'btn' });
|
||||
|
||||
// Ensure adoptedStyleSheets has one item
|
||||
expect((document as any).adoptedStyleSheets.length).toBe(1);
|
||||
|
||||
// Check CSS text content
|
||||
const sheet = (document as any).adoptedStyleSheets[0];
|
||||
expect(sheet.cssText).toContain('.btn { color: red; background-color: blue; }');
|
||||
});
|
||||
|
||||
it('should append multiple sheets without overwriting', () => {
|
||||
injectStyles({ styles: { color: 'red' }, rootClassName: 'c1' });
|
||||
injectStyles({ styles: { color: 'blue' }, rootClassName: 'c2' });
|
||||
|
||||
expect((document as any).adoptedStyleSheets.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import { delay, throttle, compose } from '@idraw/util';
|
||||
|
||||
describe('@idraw/util: lib/delay', () => {
|
||||
test('delay', async () => {
|
||||
const start = Date.now();
|
||||
const time = 1000;
|
||||
await delay(time);
|
||||
const count = Date.now() - start;
|
||||
expect(count >= time).toStrictEqual(true);
|
||||
});
|
||||
|
||||
test('throttle', async () => {
|
||||
let count = 0;
|
||||
function doThrottle() {
|
||||
return new Promise((resolve) => {
|
||||
const func = throttle(function () {
|
||||
count++;
|
||||
}, 100);
|
||||
let interval = setInterval(() => {
|
||||
if (count >= 5) {
|
||||
clearInterval(interval);
|
||||
resolve(null);
|
||||
}
|
||||
func();
|
||||
}, 5);
|
||||
});
|
||||
}
|
||||
await doThrottle();
|
||||
expect(count).toStrictEqual(5);
|
||||
});
|
||||
|
||||
test('compose', async () => {
|
||||
let middleware = [];
|
||||
let context = {
|
||||
data: []
|
||||
};
|
||||
|
||||
middleware.push(async (ctx: any, next: any) => {
|
||||
ctx.data.push(1);
|
||||
await next();
|
||||
ctx.data.push(6);
|
||||
});
|
||||
|
||||
middleware.push(async (ctx: any, next: any) => {
|
||||
ctx.data.push(2);
|
||||
await next();
|
||||
ctx.data.push(5);
|
||||
});
|
||||
|
||||
middleware.push(async (ctx: any, next: any) => {
|
||||
ctx.data.push(3);
|
||||
await next();
|
||||
ctx.data.push(4);
|
||||
});
|
||||
|
||||
const fn = compose(middleware);
|
||||
|
||||
await fn(context);
|
||||
expect(context).toStrictEqual({ data: [1, 2, 3, 4, 5, 6] });
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue