js-draw 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Editor.css +1 -0
  3. package/dist/bundle.js +1 -1
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/toolbar/AbstractToolbar.d.ts +9 -13
  6. package/dist/cjs/toolbar/AbstractToolbar.js +14 -19
  7. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
  8. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +26 -0
  9. package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
  10. package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
  11. package/dist/cjs/version.js +1 -1
  12. package/dist/mjs/toolbar/AbstractToolbar.d.ts +9 -13
  13. package/dist/mjs/toolbar/AbstractToolbar.mjs +14 -19
  14. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
  15. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +21 -0
  16. package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
  17. package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
  18. package/dist/mjs/version.mjs +1 -1
  19. package/docs/img/readme-images/js-draw.jpg +0 -0
  20. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  21. package/package.json +5 -4
  22. package/src/toolbar/EdgeToolbar.scss +1 -0
  23. package/dist-test/test_imports/package-lock.json +0 -13
  24. package/dist-test/test_imports/package.json +0 -12
  25. package/dist-test/test_imports/test-imports.js +0 -11
  26. package/dist-test/test_imports/test-require.cjs +0 -14
  27. package/src/Editor.loadFrom.test.ts +0 -24
  28. package/src/Editor.test.ts +0 -107
  29. package/src/Editor.toSVG.test.ts +0 -294
  30. package/src/Editor.ts +0 -1443
  31. package/src/EditorImage.test.ts +0 -117
  32. package/src/EditorImage.ts +0 -609
  33. package/src/EventDispatcher.test.ts +0 -123
  34. package/src/EventDispatcher.ts +0 -72
  35. package/src/Pointer.ts +0 -183
  36. package/src/SVGLoader.test.ts +0 -114
  37. package/src/SVGLoader.ts +0 -672
  38. package/src/UndoRedoHistory.test.ts +0 -34
  39. package/src/UndoRedoHistory.ts +0 -102
  40. package/src/Viewport.ts +0 -322
  41. package/src/bundle/bundled.ts +0 -7
  42. package/src/commands/Command.ts +0 -45
  43. package/src/commands/Duplicate.ts +0 -75
  44. package/src/commands/Erase.ts +0 -95
  45. package/src/commands/SerializableCommand.ts +0 -49
  46. package/src/commands/UnresolvedCommand.ts +0 -37
  47. package/src/commands/invertCommand.ts +0 -58
  48. package/src/commands/lib.ts +0 -16
  49. package/src/commands/localization.ts +0 -47
  50. package/src/commands/uniteCommands.test.ts +0 -23
  51. package/src/commands/uniteCommands.ts +0 -140
  52. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  53. package/src/components/AbstractComponent.ts +0 -383
  54. package/src/components/BackgroundComponent.test.ts +0 -44
  55. package/src/components/BackgroundComponent.ts +0 -348
  56. package/src/components/ImageComponent.ts +0 -176
  57. package/src/components/RestylableComponent.ts +0 -161
  58. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  59. package/src/components/Stroke.test.ts +0 -137
  60. package/src/components/Stroke.ts +0 -294
  61. package/src/components/TextComponent.test.ts +0 -202
  62. package/src/components/TextComponent.ts +0 -429
  63. package/src/components/UnknownSVGObject.test.ts +0 -10
  64. package/src/components/UnknownSVGObject.ts +0 -60
  65. package/src/components/builders/ArrowBuilder.ts +0 -106
  66. package/src/components/builders/CircleBuilder.ts +0 -100
  67. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  68. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  69. package/src/components/builders/LineBuilder.ts +0 -77
  70. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  71. package/src/components/builders/RectangleBuilder.ts +0 -73
  72. package/src/components/builders/types.ts +0 -15
  73. package/src/components/lib.ts +0 -31
  74. package/src/components/localization.ts +0 -24
  75. package/src/components/util/StrokeSmoother.ts +0 -302
  76. package/src/components/util/describeComponentList.ts +0 -18
  77. package/src/dialogs/makeAboutDialog.ts +0 -82
  78. package/src/inputEvents.ts +0 -143
  79. package/src/lib.ts +0 -91
  80. package/src/localization.ts +0 -34
  81. package/src/localizations/de.ts +0 -146
  82. package/src/localizations/en.ts +0 -8
  83. package/src/localizations/es.ts +0 -74
  84. package/src/localizations/getLocalizationTable.test.ts +0 -27
  85. package/src/localizations/getLocalizationTable.ts +0 -74
  86. package/src/rendering/Display.ts +0 -247
  87. package/src/rendering/RenderablePathSpec.ts +0 -88
  88. package/src/rendering/RenderingStyle.test.ts +0 -68
  89. package/src/rendering/RenderingStyle.ts +0 -55
  90. package/src/rendering/TextRenderingStyle.ts +0 -55
  91. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  92. package/src/rendering/caching/CacheRecord.ts +0 -76
  93. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  94. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  95. package/src/rendering/caching/RenderingCache.ts +0 -66
  96. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  97. package/src/rendering/caching/testUtils.ts +0 -35
  98. package/src/rendering/caching/types.ts +0 -34
  99. package/src/rendering/lib.ts +0 -8
  100. package/src/rendering/localization.ts +0 -20
  101. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  102. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  103. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  104. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  105. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  106. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  107. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  108. package/src/shortcuts/KeyBinding.test.ts +0 -61
  109. package/src/shortcuts/KeyBinding.ts +0 -257
  110. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  111. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  112. package/src/shortcuts/lib.ts +0 -3
  113. package/src/testing/createEditor.ts +0 -11
  114. package/src/testing/getUniquePointerId.ts +0 -18
  115. package/src/testing/lib.ts +0 -3
  116. package/src/testing/sendPenEvent.ts +0 -36
  117. package/src/testing/sendTouchEvent.ts +0 -71
  118. package/src/toolbar/AbstractToolbar.ts +0 -542
  119. package/src/toolbar/DropdownToolbar.ts +0 -220
  120. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  121. package/src/toolbar/EdgeToolbar.ts +0 -543
  122. package/src/toolbar/IconProvider.ts +0 -861
  123. package/src/toolbar/constants.ts +0 -1
  124. package/src/toolbar/lib.ts +0 -6
  125. package/src/toolbar/localization.ts +0 -136
  126. package/src/toolbar/types.ts +0 -13
  127. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  128. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  129. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  130. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  131. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  132. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  133. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  134. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  135. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  136. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  137. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  138. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  139. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  140. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  141. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  142. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  143. package/src/toolbar/widgets/keybindings.ts +0 -19
  144. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  145. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  146. package/src/toolbar/widgets/layout/types.ts +0 -74
  147. package/src/toolbar/widgets/lib.ts +0 -13
  148. package/src/tools/BaseTool.ts +0 -169
  149. package/src/tools/Eraser.test.ts +0 -103
  150. package/src/tools/Eraser.ts +0 -173
  151. package/src/tools/FindTool.test.ts +0 -67
  152. package/src/tools/FindTool.ts +0 -153
  153. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  154. package/src/tools/InputFilter/InputMapper.ts +0 -41
  155. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  156. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  157. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  158. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  159. package/src/tools/PanZoom.test.ts +0 -339
  160. package/src/tools/PanZoom.ts +0 -525
  161. package/src/tools/PasteHandler.ts +0 -94
  162. package/src/tools/Pen.test.ts +0 -260
  163. package/src/tools/Pen.ts +0 -284
  164. package/src/tools/PipetteTool.ts +0 -84
  165. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  166. package/src/tools/SelectionTool/Selection.ts +0 -647
  167. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  168. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  169. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  170. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  171. package/src/tools/SelectionTool/types.ts +0 -11
  172. package/src/tools/SoundUITool.ts +0 -221
  173. package/src/tools/TextTool.ts +0 -339
  174. package/src/tools/ToolController.ts +0 -224
  175. package/src/tools/ToolEnabledGroup.ts +0 -14
  176. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  177. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  178. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  179. package/src/tools/UndoRedoShortcut.ts +0 -24
  180. package/src/tools/keybindings.ts +0 -85
  181. package/src/tools/lib.ts +0 -22
  182. package/src/tools/localization.ts +0 -76
  183. package/src/types.ts +0 -151
  184. package/src/util/ReactiveValue.test.ts +0 -168
  185. package/src/util/ReactiveValue.ts +0 -241
  186. package/src/util/assertions.ts +0 -55
  187. package/src/util/fileToBase64.ts +0 -18
  188. package/src/util/guessKeyCodeFromKey.ts +0 -36
  189. package/src/util/listPrefixMatch.ts +0 -19
  190. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  191. package/src/util/untilNextAnimationFrame.ts +0 -9
  192. package/src/util/waitForAll.ts +0 -18
  193. package/src/util/waitForTimeout.ts +0 -9
  194. package/src/version.test.ts +0 -12
  195. package/src/version.ts +0 -3
  196. package/tools/allLocales.js +0 -4
  197. package/tools/copyREADME.ts +0 -62
@@ -1,107 +0,0 @@
1
- import { BaseTool, InputEvtType } from './lib';
2
- import createEditor from './testing/createEditor';
3
-
4
- describe('Editor', () => {
5
- it('should fire keyup events when the editor loses focus', () => {
6
- const editor = createEditor();
7
- const rootElem = editor.getRootElement();
8
-
9
- const inputArea = rootElem.querySelector('textarea')! as HTMLTextAreaElement;
10
-
11
- // Set the only tool to a tool that reports which keys are pressed.
12
- const keyPressMock = jest.fn(() => true);
13
- const keyReleaseMock = jest.fn();
14
- editor.toolController.setTools([
15
- new (class extends BaseTool {
16
- public constructor() {
17
- super(editor.notifier, 'test');
18
- }
19
-
20
- public override onKeyPress = keyPressMock;
21
- public override onKeyUp = keyReleaseMock;
22
- })()
23
- ]);
24
-
25
- inputArea.focus();
26
-
27
- // Sends a keyboard event to the editor
28
- const dispatchKeyEvent = (kind: 'keydown'|'keyup', code: string, key: string) => {
29
- const event = new KeyboardEvent(kind, {
30
- bubbles: true,
31
- key,
32
- code,
33
- shiftKey: false,
34
- ctrlKey: false,
35
- metaKey: false,
36
- });
37
- inputArea.dispatchEvent(event);
38
- };
39
-
40
- // Press the A key
41
- dispatchKeyEvent('keydown', 'KeyA', 'a');
42
-
43
- const keyAEvent = {
44
- kind: InputEvtType.KeyPressEvent,
45
- key: 'a',
46
- code: 'KeyA',
47
- ctrlKey: false,
48
- altKey: false,
49
- shiftKey: false,
50
- };
51
- expect(keyPressMock).toHaveBeenLastCalledWith(keyAEvent);
52
- expect(keyPressMock).toHaveBeenCalledTimes(1);
53
-
54
- // Press it again
55
- dispatchKeyEvent('keydown', 'KeyA', 'a');
56
-
57
- expect(keyPressMock).toHaveBeenLastCalledWith(keyAEvent);
58
- expect(keyPressMock).toHaveBeenCalledTimes(2);
59
-
60
- // Pressing a different key should send a different keydownb event to the toolbar
61
- dispatchKeyEvent('keydown', 'KeyB', 'b');
62
-
63
- expect(keyPressMock).not.toHaveBeenLastCalledWith(keyAEvent);
64
- expect(keyPressMock).toHaveBeenCalledTimes(3);
65
-
66
- // Press yet another key (and multiple times) -- if this key is still down when the
67
- // editor is blured, a keyup event should only be fired once.
68
- dispatchKeyEvent('keydown', 'KeyF', 'f');
69
- expect(keyPressMock).toHaveBeenCalledTimes(4);
70
-
71
- dispatchKeyEvent('keydown', 'KeyF', 'f');
72
- expect(keyPressMock).toHaveBeenCalledTimes(5);
73
-
74
- dispatchKeyEvent('keyup', 'KeyA', 'a');
75
- expect(keyPressMock).toHaveBeenCalledTimes(5);
76
- expect(keyReleaseMock).toHaveBeenCalledTimes(1);
77
- expect(keyReleaseMock).toHaveBeenLastCalledWith({
78
- ...keyAEvent,
79
- kind: InputEvtType.KeyUpEvent
80
- });
81
-
82
- // Defocus the input --- this should fire a key up event for the keys still down
83
- inputArea.blur();
84
- inputArea.dispatchEvent(new Event('blur', { bubbles: true }));
85
-
86
- const focusable = document.createElement('button');
87
- document.body.appendChild(focusable);
88
- focusable.focus();
89
-
90
- expect(keyReleaseMock).toHaveBeenCalledTimes(3);
91
-
92
- // Events for both keys that were still down should have been fired:
93
- const secondToLastCall = keyReleaseMock.mock.calls[keyReleaseMock.mock.calls.length - 2];
94
- const lastCall = keyReleaseMock.mock.lastCall;
95
-
96
- expect(secondToLastCall).toMatchObject([{
97
- kind: InputEvtType.KeyUpEvent,
98
- key: 'b',
99
- code: 'KeyB',
100
- }]);
101
- expect(lastCall).toMatchObject([{
102
- kind: InputEvtType.KeyUpEvent,
103
- key: 'f',
104
- code: 'KeyF',
105
- }]);
106
- });
107
- });
@@ -1,294 +0,0 @@
1
- import { Color4, Mat33, Rect2, TextComponent, EditorImage, Vec2, StrokeComponent, SelectionTool, sendPenEvent, InputEvtType } from './lib';
2
- import TextRenderingStyle from './rendering/TextRenderingStyle';
3
- import SVGLoader from './SVGLoader';
4
- import createEditor from './testing/createEditor';
5
-
6
- describe('Editor.toSVG', () => {
7
- it('should correctly nest text objects', async () => {
8
- const editor = createEditor();
9
- const textStyle: TextRenderingStyle = {
10
- fontFamily: 'sans', size: 12, renderingStyle: { fill: Color4.black }
11
- };
12
- const text = new TextComponent([
13
- 'Testing...',
14
- new TextComponent([ 'Test 2' ], Mat33.translation(Vec2.of(0, 100)), textStyle),
15
- ], Mat33.identity, textStyle);
16
- editor.dispatch(EditorImage.addElement(text));
17
-
18
- const matches = editor.image.getElementsIntersectingRegion(new Rect2(4, -100, 100, 100));
19
- expect(matches).toHaveLength(1);
20
- expect(text).not.toBeNull();
21
-
22
- const asSVG = editor.toSVG();
23
- const allTSpans = [ ...asSVG.querySelectorAll('tspan') ];
24
- expect(allTSpans).toHaveLength(1);
25
- expect(allTSpans[0].getAttribute('x')).toBe('0');
26
- expect(allTSpans[0].getAttribute('y')).toBe('100');
27
- expect(allTSpans[0].style.transform).toBe('');
28
- });
29
-
30
- it('should preserve empty tspans', async () => {
31
- const editor = createEditor();
32
- await editor.loadFrom(SVGLoader.fromString(`
33
- <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
34
- <style id="js-draw-style-sheet">
35
- path {
36
- stroke-linecap:round;
37
- stroke-linejoin:round;
38
- }
39
- </style>
40
- <text style="transform: matrix(1, 0, 0, 1, 12, 35); font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Testing...<tspan x="3" y="40" style="font-family: sans-serif; font-size: 33px; fill: rgb(128, 51, 128);"></tspan><tspan x="3" y="70">Test 2. ☺</tspan></text>
41
- </svg>
42
- `, true));
43
-
44
- const textNodesInImage = editor.image.getAllElements().filter(elem => elem instanceof TextComponent);
45
- expect(
46
- textNodesInImage
47
- ).toHaveLength(1);
48
-
49
- const asSVG = editor.toSVG();
50
- const textObject = asSVG.querySelector('text');
51
-
52
- if (!textObject) {
53
- throw new Error('No text object found');
54
- }
55
-
56
- const childTextNodes = textObject.querySelectorAll('tspan');
57
- expect(childTextNodes).toHaveLength(2);
58
- });
59
-
60
- it('should preserve text child size/placement while not saving additional properties', async () => {
61
- const secondLineText = 'This is a test of a thing that has been known to break. Will this test catch the issue?';
62
- const thirdLineText = 'This is a test of saving/loading multi-line text...';
63
-
64
- const editor = createEditor();
65
- await editor.loadFrom(SVGLoader.fromString(`
66
- <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
67
- <style id="js-draw-style-sheet">
68
- path {
69
- stroke-linecap:round;
70
- stroke-linejoin:round;
71
- }
72
- </style>
73
- <text style="transform: matrix(1, 0, 0, 1, 12, 35); font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Testing...<tspan x="3" y="40" style="font-family: sans-serif; font-size: 33px; fill: rgb(128, 51, 128);">${secondLineText}</tspan><tspan x="0" y="72" style="font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">${thirdLineText}</tspan><tspan x="0" y="112" style="font-family: sans-serif; font-size: 32px; fill: rgb(128, 51, 128);">Will it pass or fail?</tspan></text>
74
- </svg>
75
- `, true));
76
-
77
- expect(
78
- editor.image.getAllElements().filter(elem => elem instanceof TextComponent)
79
- ).toHaveLength(1);
80
-
81
- const asSVG = editor.toSVG();
82
- const textObject = asSVG.querySelector('text');
83
-
84
- if (!textObject) {
85
- throw new Error('No text object found');
86
- }
87
-
88
- expect(textObject.style.transform.replace(/\s+/g, '')).toBe('matrix(1,0,0,1,12,35)');
89
- expect(textObject.style.fontFamily).toBe('sans-serif');
90
- expect(textObject.style.fontSize).toBe('32px');
91
-
92
- const childTextNodes = textObject.querySelectorAll('tspan');
93
- expect(childTextNodes).toHaveLength(3);
94
- const firstChild = childTextNodes[0];
95
-
96
- expect(firstChild.textContent).toBe(secondLineText);
97
- expect(firstChild.style.transform).toBe('');
98
- expect(firstChild.style.fontSize).toBe('33px');
99
- expect(firstChild.getAttribute('x')).toBe('3');
100
- expect(firstChild.getAttribute('y')).toBe('40');
101
-
102
- // Should not save a fontSize when not necessary (same fill as parent text node)
103
- const secondChild = childTextNodes[1];
104
- expect(secondChild.style.fontSize ?? '').toBe('');
105
-
106
- // Should not save additional "style" attributes when not necessary
107
- // TODO: Uncomment before some future major version release. Currently a "fill" is set for every
108
- // tspan to work around a loading bug.
109
- //expect(secondChild.outerHTML).toBe(`<tspan x="0" y="72">${thirdLineText}</tspan>`);
110
- });
111
-
112
- it('should preserve group elements', async () => {
113
- const editor = createEditor();
114
- await editor.loadFrom(SVGLoader.fromString(`
115
- <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
116
- <style id="js-draw-style-sheet">
117
- path {
118
- stroke-linecap:round;
119
- stroke-linejoin:round;
120
- }
121
- </style>
122
- <g id='main-group'>
123
- <g id='sub-group-1'>
124
- <path d='M0,0 L10,10 0,10' fill='#f00'/>
125
- <path d='M20,0 L10,10 0,10'/>
126
- </g>
127
- <g id='empty-group-2'></g>
128
- </g>
129
- <g id='empty-group-3'></g>
130
-
131
- <!-- Groups without IDs should also be preserved -->
132
- <g><g><g id='marker-1'></g></g></g>
133
- <g class='test'><g id='marker-2'/></g>
134
-
135
- <!-- Groups with duplicate IDs should preserved (though IDs)
136
- may be changed -->
137
- <g id='empty-group-2'/>
138
- <g id='empty-group-2'><g id='empty-group-2'/></g>
139
- </svg>
140
- `));
141
-
142
- // Both paths should exist.
143
- expect(
144
- editor.image
145
- .getElementsIntersectingRegion(new Rect2(-10, -10, 100, 100))
146
- .filter(elem => elem instanceof StrokeComponent)
147
- ).toHaveLength(2);
148
-
149
- const outputSVG = editor.toSVG();
150
-
151
- // Should still have the expected number of groups
152
- expect(outputSVG.querySelectorAll('g')).toHaveLength(12);
153
-
154
- // Should preserve the empty group.
155
- expect(outputSVG.querySelectorAll('g#empty-group-2')).toHaveLength(1);
156
-
157
- // The empty group should still have the correct parent
158
- expect(outputSVG.querySelectorAll('g#main-group > g#empty-group-2')).toHaveLength(1);
159
-
160
- // Paths should still be children of sub-group-1
161
- expect(outputSVG.querySelectorAll('g#sub-group-1 > path')).toHaveLength(2);
162
-
163
- // sub-group-1 should have the correct parent
164
- expect(outputSVG.querySelectorAll('g#main-group > g#sub-group-1')).toHaveLength(1);
165
-
166
- // And these should be the only paths.
167
- expect(outputSVG.querySelectorAll('path')).toHaveLength(2);
168
-
169
- // Should also preserve groups without IDs
170
- // Selector ref: https://stackoverflow.com/a/18607777
171
- expect(outputSVG.querySelectorAll('svg > g > g > g#marker-1')).toHaveLength(1);
172
- expect(outputSVG.querySelectorAll('svg > g > g#marker-2')).toHaveLength(1);
173
-
174
- // Should preserve class names on `g` objects:
175
- expect(outputSVG.querySelectorAll('svg > g.test > g#marker-2')).toHaveLength(1);
176
-
177
- // Should preserve groups that had duplicate IDs
178
- expect(outputSVG.querySelectorAll('svg > g#empty-group-2--1')).toHaveLength(1);
179
- expect(outputSVG.querySelectorAll('svg > g#empty-group-2--2')).toHaveLength(1);
180
- expect(outputSVG.querySelectorAll('svg > g#empty-group-2--2 > g#empty-group-2--3')).toHaveLength(1);
181
- });
182
-
183
- describe('should not preserve group elements when doing so would change the z order', () => {
184
- it('in an image with few items', async () => {
185
- const editor = createEditor();
186
- await editor.loadFrom(SVGLoader.fromString(`
187
- <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
188
- <g id='main-group-1'>
189
- <path d='M0,0 L-10,10 0,10' fill='#f00'/>
190
- <path d='M20,0 L10,10 0,10'/>
191
- </g>
192
- <path d='M40,40 l10,10 0,10'/>
193
- </svg>
194
- `));
195
-
196
- // All paths should exist.
197
- expect(
198
- editor.image
199
- .getElementsIntersectingRegion(new Rect2(-10, -10, 100, 100))
200
- .filter(elem => elem instanceof StrokeComponent)
201
- ).toHaveLength(3);
202
-
203
- // Before modifying, both paths should be within the main-group-1 group
204
- expect(editor.toSVG().querySelectorAll('svg > g#main-group-1 > path')).toHaveLength(2);
205
-
206
- const selectionTool = editor.toolController.getMatchingTools(SelectionTool)[0];
207
- selectionTool.setEnabled(true);
208
-
209
- // Select the bottommost stroke
210
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(-11, 9));
211
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(-9, 10));
212
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(-9, 10));
213
-
214
- // The stroke should be selected
215
- expect(selectionTool.getSelectedObjects()).toHaveLength(1);
216
- expect(selectionTool.getSelectedObjects()[0].getBBox())
217
- .objEq(new Rect2(-10, 0, 10, 10));
218
-
219
- // Drag the selection (moves the selected item to the top)
220
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(-11, 9));
221
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0, 0));
222
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0, 0));
223
-
224
- expect(selectionTool.getSelectedObjects()[0].getBBox())
225
- .not.objEq(new Rect2(-10, 0, 10, 10));
226
- selectionTool.setEnabled(false);
227
-
228
- // One of the items should have been moved out of the main group
229
- const outputSVG = editor.toSVG();
230
- expect(outputSVG.querySelectorAll('svg > path')).toHaveLength(2);
231
- expect(outputSVG.querySelectorAll('svg > g#main-group-1 > path')).toHaveLength(1);
232
- });
233
-
234
- it('in an image with many items in nested groups', async () => {
235
- const editor = createEditor();
236
- await editor.loadFrom(SVGLoader.fromString(`
237
- <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
238
- <path d='M-100,-100 l 2,2 0,2'/>
239
- <g id='group-1'>
240
- <path d='M0,0 L-10,10 0,10' fill='#f00'/>
241
- <path d='M20,0 L10,10 0,10'/>
242
-
243
- <g id='group-2'>
244
- <path d='M100,100 l 2,2 0,2'/>
245
- </g>
246
- </g>
247
- <path d='M40,40 l10,10 0,10'/>
248
- </svg>
249
- `));
250
-
251
- // .expects that all paths have their original parent groups.
252
- const expectGroupParentsToBeOriginal = () => {
253
- expect(
254
- editor.image
255
- .getAllElements()
256
- .filter(elem => elem instanceof StrokeComponent)
257
- ).toHaveLength(5);
258
-
259
- const output = editor.toSVG();
260
- expect(
261
- output.querySelectorAll('svg > g#group-1 path')
262
- ).toHaveLength(3);
263
- expect(
264
- output.querySelectorAll('svg > g#group-1 > g > path')
265
- ).toHaveLength(1);
266
- };
267
-
268
- expectGroupParentsToBeOriginal();
269
-
270
- const nudgePathNear = async (pos: Vec2) => {
271
- const targetElems = editor.image.getElementsIntersectingRegion(Rect2.bboxOf([ pos ], 5));
272
-
273
- expect(targetElems).toHaveLength(1);
274
-
275
- // Move the path to the top
276
- const target = targetElems[0];
277
- await editor.dispatch(target.transformBy(Mat33.scaling2D(1.01)));
278
- };
279
-
280
- // Moving a path that's below all groups should not change group parentings.
281
- nudgePathNear(Vec2.of(-100, -100));
282
- expectGroupParentsToBeOriginal();
283
-
284
- // Moving a path that's within nested groups should move the path out of that group.
285
- nudgePathNear(Vec2.of(100, 100));
286
-
287
- const outputSVG = editor.toSVG();
288
- expect(outputSVG.querySelectorAll('svg > path')).toHaveLength(3);
289
- expect(outputSVG.querySelectorAll('svg > g#group-1 > path')).toHaveLength(2);
290
- expect(outputSVG.querySelectorAll('svg > g#group-1 > g')).toHaveLength(1);
291
- expect(outputSVG.querySelectorAll('svg > g#group-1 > g > *')).toHaveLength(0);
292
- });
293
- });
294
- });