js-draw 1.17.0 → 1.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. package/README.md +70 -10
  2. package/dist/Editor.css +35 -3
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +38 -21
  6. package/dist/cjs/Editor.js +11 -2
  7. package/dist/cjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  8. package/dist/cjs/{SVGLoader.js → SVGLoader/index.js} +12 -29
  9. package/dist/cjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
  10. package/dist/cjs/SVGLoader/utils/determineFontSize.js +27 -0
  11. package/dist/cjs/Viewport.d.ts +33 -1
  12. package/dist/cjs/components/AbstractComponent.d.ts +17 -5
  13. package/dist/cjs/components/AbstractComponent.js +15 -15
  14. package/dist/cjs/components/Stroke.d.ts +4 -1
  15. package/dist/cjs/components/Stroke.js +158 -2
  16. package/dist/cjs/components/TextComponent.js +3 -1
  17. package/dist/cjs/components/builders/PolylineBuilder.d.ts +1 -1
  18. package/dist/cjs/components/builders/PolylineBuilder.js +9 -2
  19. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  20. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +44 -51
  21. package/dist/cjs/image/EditorImage.js +1 -1
  22. package/dist/cjs/localizations/de.js +1 -1
  23. package/dist/cjs/localizations/es.js +1 -1
  24. package/dist/cjs/rendering/caching/RenderingCacheNode.js +20 -15
  25. package/dist/cjs/testing/createEditor.d.ts +2 -2
  26. package/dist/cjs/testing/createEditor.js +2 -2
  27. package/dist/cjs/testing/findNodeWithText.d.ts +3 -0
  28. package/dist/cjs/testing/findNodeWithText.js +16 -0
  29. package/dist/cjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  30. package/dist/cjs/testing/firstElementAncestorOfNode.js +13 -0
  31. package/dist/cjs/testing/sendKeyPressRelease.d.ts +3 -0
  32. package/dist/cjs/testing/sendKeyPressRelease.js +8 -0
  33. package/dist/cjs/testing/sendPenEvent.d.ts +2 -2
  34. package/dist/cjs/testing/sendPenEvent.js +26 -3
  35. package/dist/cjs/toolbar/IconProvider.d.ts +3 -1
  36. package/dist/cjs/toolbar/IconProvider.js +15 -3
  37. package/dist/cjs/toolbar/localization.d.ts +8 -1
  38. package/dist/cjs/toolbar/localization.js +9 -2
  39. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  40. package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -0
  41. package/dist/cjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  42. package/dist/cjs/toolbar/widgets/EraserToolWidget.js +45 -5
  43. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +22 -0
  44. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.js +58 -0
  45. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  46. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.js +21 -0
  47. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  48. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.js +281 -0
  49. package/dist/cjs/toolbar/widgets/PenToolWidget.js +10 -3
  50. package/dist/cjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  51. package/dist/cjs/toolbar/widgets/TextToolWidget.js +5 -3
  52. package/dist/cjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  53. package/dist/cjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  54. package/dist/cjs/toolbar/widgets/components/makeFileInput.js +102 -45
  55. package/dist/cjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  56. package/dist/cjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  57. package/dist/cjs/toolbar/widgets/components/makeSnappedList.js +103 -0
  58. package/dist/cjs/toolbar/widgets/keybindings.js +1 -1
  59. package/dist/cjs/tools/Eraser.d.ts +31 -6
  60. package/dist/cjs/tools/Eraser.js +161 -21
  61. package/dist/cjs/tools/PasteHandler.js +0 -1
  62. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -2
  63. package/dist/cjs/tools/SelectionTool/Selection.js +20 -20
  64. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  65. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +6 -0
  66. package/dist/cjs/tools/SelectionTool/SelectionTool.js +1 -1
  67. package/dist/cjs/tools/SelectionTool/types.d.ts +19 -0
  68. package/dist/cjs/tools/TextTool.js +2 -1
  69. package/dist/cjs/tools/TextTool.test.d.ts +1 -0
  70. package/dist/cjs/tools/ToolController.d.ts +2 -0
  71. package/dist/cjs/tools/ToolController.js +10 -1
  72. package/dist/cjs/tools/lib.d.ts +1 -4
  73. package/dist/cjs/tools/lib.js +2 -4
  74. package/dist/cjs/util/ReactiveValue.d.ts +2 -0
  75. package/dist/cjs/util/ReactiveValue.js +11 -0
  76. package/dist/cjs/util/bytesToSizeString.d.ts +8 -0
  77. package/dist/cjs/util/bytesToSizeString.js +26 -0
  78. package/dist/cjs/util/bytesToSizeString.test.d.ts +1 -0
  79. package/dist/cjs/util/stopPropagationOfScrollingWheelEvents.js +10 -6
  80. package/dist/cjs/util/waitForAll.d.ts +2 -0
  81. package/dist/cjs/util/waitForAll.js +2 -0
  82. package/dist/cjs/util/waitForImageLoaded.js +3 -0
  83. package/dist/cjs/util/waitForTimeout.d.ts +1 -0
  84. package/dist/cjs/util/waitForTimeout.js +1 -1
  85. package/dist/cjs/version.js +1 -1
  86. package/dist/mjs/Editor.d.ts +38 -21
  87. package/dist/mjs/Editor.mjs +11 -2
  88. package/dist/mjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  89. package/dist/mjs/{SVGLoader.mjs → SVGLoader/index.mjs} +12 -29
  90. package/dist/mjs/SVGLoader/index.test.d.ts +1 -0
  91. package/dist/mjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
  92. package/dist/mjs/SVGLoader/utils/determineFontSize.mjs +25 -0
  93. package/dist/mjs/Viewport.d.ts +33 -1
  94. package/dist/mjs/components/AbstractComponent.d.ts +17 -5
  95. package/dist/mjs/components/AbstractComponent.mjs +15 -15
  96. package/dist/mjs/components/Stroke.d.ts +4 -1
  97. package/dist/mjs/components/Stroke.mjs +159 -3
  98. package/dist/mjs/components/TextComponent.mjs +3 -1
  99. package/dist/mjs/components/builders/PolylineBuilder.d.ts +1 -1
  100. package/dist/mjs/components/builders/PolylineBuilder.mjs +10 -3
  101. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  102. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +45 -52
  103. package/dist/mjs/image/EditorImage.mjs +1 -1
  104. package/dist/mjs/localizations/de.mjs +1 -1
  105. package/dist/mjs/localizations/es.mjs +1 -1
  106. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +20 -15
  107. package/dist/mjs/testing/createEditor.d.ts +2 -2
  108. package/dist/mjs/testing/createEditor.mjs +2 -2
  109. package/dist/mjs/testing/findNodeWithText.d.ts +3 -0
  110. package/dist/mjs/testing/findNodeWithText.mjs +14 -0
  111. package/dist/mjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  112. package/dist/mjs/testing/firstElementAncestorOfNode.mjs +11 -0
  113. package/dist/mjs/testing/sendKeyPressRelease.d.ts +3 -0
  114. package/dist/mjs/testing/sendKeyPressRelease.mjs +6 -0
  115. package/dist/mjs/testing/sendPenEvent.d.ts +2 -2
  116. package/dist/mjs/testing/sendPenEvent.mjs +3 -3
  117. package/dist/mjs/toolbar/IconProvider.d.ts +3 -1
  118. package/dist/mjs/toolbar/IconProvider.mjs +15 -3
  119. package/dist/mjs/toolbar/localization.d.ts +8 -1
  120. package/dist/mjs/toolbar/localization.mjs +9 -2
  121. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  122. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -0
  123. package/dist/mjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  124. package/dist/mjs/toolbar/widgets/EraserToolWidget.mjs +47 -6
  125. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +22 -0
  126. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.mjs +54 -0
  127. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  128. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.mjs +16 -0
  129. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  130. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.mjs +276 -0
  131. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.test.d.ts +1 -0
  132. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +10 -3
  133. package/dist/mjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  134. package/dist/mjs/toolbar/widgets/TextToolWidget.mjs +5 -3
  135. package/dist/mjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  136. package/dist/mjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  137. package/dist/mjs/toolbar/widgets/components/makeFileInput.mjs +102 -45
  138. package/dist/mjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  139. package/dist/mjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  140. package/dist/mjs/toolbar/widgets/components/makeSnappedList.mjs +98 -0
  141. package/dist/mjs/toolbar/widgets/keybindings.mjs +1 -1
  142. package/dist/mjs/tools/Eraser.d.ts +31 -6
  143. package/dist/mjs/tools/Eraser.mjs +161 -22
  144. package/dist/mjs/tools/PasteHandler.mjs +0 -1
  145. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -2
  146. package/dist/mjs/tools/SelectionTool/Selection.mjs +20 -20
  147. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  148. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +6 -0
  149. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +1 -1
  150. package/dist/mjs/tools/SelectionTool/types.d.ts +19 -0
  151. package/dist/mjs/tools/TextTool.mjs +2 -1
  152. package/dist/mjs/tools/TextTool.test.d.ts +1 -0
  153. package/dist/mjs/tools/ToolController.d.ts +2 -0
  154. package/dist/mjs/tools/ToolController.mjs +10 -1
  155. package/dist/mjs/tools/lib.d.ts +1 -4
  156. package/dist/mjs/tools/lib.mjs +1 -4
  157. package/dist/mjs/util/ReactiveValue.d.ts +2 -0
  158. package/dist/mjs/util/ReactiveValue.mjs +11 -0
  159. package/dist/mjs/util/bytesToSizeString.d.ts +8 -0
  160. package/dist/mjs/util/bytesToSizeString.mjs +24 -0
  161. package/dist/mjs/util/bytesToSizeString.test.d.ts +1 -0
  162. package/dist/mjs/util/stopPropagationOfScrollingWheelEvents.mjs +10 -6
  163. package/dist/mjs/util/waitForAll.d.ts +2 -0
  164. package/dist/mjs/util/waitForAll.mjs +2 -0
  165. package/dist/mjs/util/waitForImageLoaded.mjs +3 -0
  166. package/dist/mjs/util/waitForTimeout.d.ts +1 -0
  167. package/dist/mjs/util/waitForTimeout.mjs +1 -1
  168. package/dist/mjs/version.mjs +1 -1
  169. package/package.json +4 -4
  170. package/src/toolbar/toolbar.scss +1 -7
  171. package/src/toolbar/widgets/{InsertImageWidget.scss → InsertImageWidget/index.scss} +3 -2
  172. package/src/toolbar/widgets/components/components.scss +2 -1
  173. package/src/toolbar/widgets/components/makeFileInput.scss +14 -1
  174. package/src/toolbar/widgets/components/makeSnappedList.scss +28 -0
  175. package/src/toolbar/widgets/widgets.scss +7 -0
  176. package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  177. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +0 -269
  178. package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  179. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +0 -264
  180. /package/dist/cjs/{SVGLoader.test.d.ts → SVGLoader/index.test.d.ts} +0 -0
  181. /package/dist/{mjs/SVGLoader.test.d.ts → cjs/toolbar/widgets/InsertImageWidget/index.test.d.ts} +0 -0
@@ -0,0 +1,6 @@
1
+ import { InputEvtType } from '../inputEvents.mjs';
2
+ const sendKeyPressRelease = (editor, key) => {
3
+ editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, key);
4
+ editor.sendKeyboardEvent(InputEvtType.KeyUpEvent, key);
5
+ };
6
+ export default sendKeyPressRelease;
@@ -1,6 +1,6 @@
1
1
  import Editor from '../Editor';
2
2
  import { Point2 } from '@js-draw/math';
3
- import Pointer from '../Pointer';
3
+ import Pointer, { PointerDevice } from '../Pointer';
4
4
  import { InputEvtType } from '../inputEvents';
5
5
  /**
6
6
  * Dispatch a pen event to the currently selected tool.
@@ -8,5 +8,5 @@ import { InputEvtType } from '../inputEvents';
8
8
  *
9
9
  * @see {@link sendTouchEvent}
10
10
  */
11
- declare const sendPenEvent: (editor: Editor, eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[]) => Pointer;
11
+ declare const sendPenEvent: (editor: Editor, eventType: InputEvtType.PointerDownEvt | InputEvtType.PointerMoveEvt | InputEvtType.PointerUpEvt, point: Point2, allPointers?: Pointer[], deviceType?: PointerDevice) => Pointer;
12
12
  export default sendPenEvent;
@@ -1,4 +1,4 @@
1
- import Pointer from '../Pointer.mjs';
1
+ import Pointer, { PointerDevice } from '../Pointer.mjs';
2
2
  import { InputEvtType } from '../inputEvents.mjs';
3
3
  import getUniquePointerId from './getUniquePointerId.mjs';
4
4
  /**
@@ -7,9 +7,9 @@ import getUniquePointerId from './getUniquePointerId.mjs';
7
7
  *
8
8
  * @see {@link sendTouchEvent}
9
9
  */
10
- const sendPenEvent = (editor, eventType, point, allPointers) => {
10
+ const sendPenEvent = (editor, eventType, point, allPointers, deviceType = PointerDevice.Pen) => {
11
11
  const id = getUniquePointerId(allPointers ?? []);
12
- const mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, editor.viewport, id);
12
+ const mainPointer = Pointer.ofCanvasPoint(point, eventType !== InputEvtType.PointerUpEvt, editor.viewport, id, deviceType);
13
13
  editor.toolController.dispatchInputEvent({
14
14
  kind: eventType,
15
15
  allPointers: allPointers ?? [
@@ -1,6 +1,7 @@
1
1
  import { Color4 } from '@js-draw/math';
2
2
  import TextRenderingStyle from '../rendering/TextRenderingStyle';
3
3
  import { PenStyle } from '../tools/Pen';
4
+ import { EraserMode } from '../tools/Eraser';
4
5
  export type IconElemType = HTMLImageElement | SVGElement;
5
6
  /**
6
7
  * Provides icons that can be used in the toolbar and other locations.
@@ -39,7 +40,7 @@ export default class IconProvider {
39
40
  makeUndoIcon(): IconElemType;
40
41
  makeRedoIcon(): IconElemType;
41
42
  makeDropdownIcon(): IconElemType;
42
- makeEraserIcon(eraserSize?: number): IconElemType;
43
+ makeEraserIcon(eraserSize?: number, mode?: EraserMode): IconElemType;
43
44
  makeSelectionIcon(): IconElemType;
44
45
  makeRotateIcon(): IconElemType;
45
46
  makeHandToolIcon(): IconElemType;
@@ -88,6 +89,7 @@ export default class IconProvider {
88
89
  * @returns true if the given `penStyle` is known to match a rounded tip type of pen.
89
90
  */
90
91
  protected isRoundedTipPen(penStyle: PenStyle): boolean;
92
+ protected isPolylinePen(penStyle: PenStyle): boolean;
91
93
  /** Must be overridden by icon packs that need attribution. */
92
94
  licenseInfo(): string | null;
93
95
  }
@@ -8,6 +8,8 @@ import { Vec2, Color4 } from '@js-draw/math';
8
8
  import SVGRenderer from '../rendering/renderers/SVGRenderer.mjs';
9
9
  import Viewport from '../Viewport.mjs';
10
10
  import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder.mjs';
11
+ import { makePolylineBuilder } from '../components/builders/PolylineBuilder.mjs';
12
+ import { EraserMode } from '../tools/Eraser.mjs';
11
13
  const svgNamespace = 'http://www.w3.org/2000/svg';
12
14
  const iconColorFill = `
13
15
  style='fill: var(--icon-color);'
@@ -112,16 +114,23 @@ class IconProvider {
112
114
  icon.setAttribute('viewBox', '-10 -10 110 110');
113
115
  return icon;
114
116
  }
115
- makeEraserIcon(eraserSize) {
117
+ makeEraserIcon(eraserSize, mode) {
116
118
  const icon = document.createElementNS(svgNamespace, 'svg');
117
119
  eraserSize ??= 10;
118
120
  const scaledSize = eraserSize / 4;
119
121
  const eraserColor = '#ff70af';
120
122
  // Draw an eraser-like shape. Created with Inkscape
121
123
  icon.innerHTML = `
124
+ <defs>
125
+ <linearGradient id="dash-pattern">
126
+ <stop offset="80%" stop-color="${eraserColor}"/>
127
+ <stop offset="85%" stop-color="white"/>
128
+ <stop offset="90%" stop-color="${eraserColor}"/>
129
+ </linearGradient>
130
+ </defs>
122
131
  <g>
123
132
  <path
124
- style="fill:${eraserColor}"
133
+ style="fill:${mode === EraserMode.PartialStroke ? 'url(#dash-pattern)' : eraserColor}"
125
134
  stroke="black"
126
135
  transform="rotate(41.35)"
127
136
  d="M 52.5 27
@@ -835,7 +844,10 @@ class IconProvider {
835
844
  * @returns true if the given `penStyle` is known to match a rounded tip type of pen.
836
845
  */
837
846
  isRoundedTipPen(penStyle) {
838
- return penStyle.factory === makeFreehandLineBuilder;
847
+ return penStyle.factory === makeFreehandLineBuilder || penStyle.factory === makePolylineBuilder;
848
+ }
849
+ isPolylinePen(penStyle) {
850
+ return penStyle.factory === makePolylineBuilder;
839
851
  }
840
852
  /** Must be overridden by icon packs that need attribution. */
841
853
  licenseInfo() { return null; }
@@ -17,9 +17,11 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
17
17
  dragAndDropHereOrBrowse: string;
18
18
  cancel: string;
19
19
  submit: string;
20
+ addAll: string;
20
21
  roundedTipPen: string;
22
+ roundedTipPen2: string;
21
23
  flatTipPen: string;
22
- selectPenTip: string;
24
+ selectPenType: string;
23
25
  selectShape: string;
24
26
  colorLabel: string;
25
27
  pen: string;
@@ -30,6 +32,7 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
30
32
  resizeImageToSelection: string;
31
33
  deleteSelection: string;
32
34
  duplicateSelection: string;
35
+ fullStrokeEraser: string;
33
36
  pickColorFromScreen: string;
34
37
  clickToPickColorAnnouncement: string;
35
38
  colorSelectionCanceledAnnouncement: string;
@@ -54,6 +57,7 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
54
57
  strokeAutocorrect: string;
55
58
  errorImageHasZeroSize: string;
56
59
  describeTheImage: string;
60
+ fileInput__loading: string;
57
61
  penDropdown__baseHelpText: string;
58
62
  penDropdown__colorHelpText: string;
59
63
  penDropdown__thicknessHelpText: string;
@@ -66,7 +70,10 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
66
70
  handDropdown__zoomOutHelpText: string;
67
71
  handDropdown__resetViewHelpText: string;
68
72
  handDropdown__touchPanningHelpText: string;
73
+ eraserDropdown__baseHelpText: string;
74
+ eraserDropdown__fullStrokeEraserHelpText: string;
69
75
  handDropdown__lockRotationHelpText: string;
76
+ eraserDropdown__thicknessHelpText: string;
70
77
  selectionDropdown__baseHelpText: string;
71
78
  selectionDropdown__resizeToHelpText: string;
72
79
  selectionDropdown__deleteHelpText: string;
@@ -14,6 +14,7 @@ export const defaultToolbarLocalization = {
14
14
  chooseFile: 'Choose file',
15
15
  dragAndDropHereOrBrowse: 'Drag and drop here\nor\n{{browse}}',
16
16
  submit: 'Submit',
17
+ addAll: 'Add all',
17
18
  cancel: 'Cancel',
18
19
  resetView: 'Reset view',
19
20
  thicknessLabel: 'Thickness',
@@ -27,7 +28,8 @@ export const defaultToolbarLocalization = {
27
28
  save: 'Save',
28
29
  undo: 'Undo',
29
30
  redo: 'Redo',
30
- selectPenTip: 'Pen tip',
31
+ fullStrokeEraser: 'Full stroke eraser',
32
+ selectPenType: 'Pen type',
31
33
  selectShape: 'Shape',
32
34
  pickColorFromScreen: 'Pick color from screen',
33
35
  clickToPickColorAnnouncement: 'Click on the screen to pick a color',
@@ -45,6 +47,7 @@ export const defaultToolbarLocalization = {
45
47
  strokeAutocorrect: 'Autocorrect',
46
48
  touchPanning: 'Touchscreen panning',
47
49
  roundedTipPen: 'Round',
50
+ roundedTipPen2: 'Polyline',
48
51
  flatTipPen: 'Flat',
49
52
  arrowPen: 'Arrow',
50
53
  linePen: 'Line',
@@ -55,11 +58,12 @@ export const defaultToolbarLocalization = {
55
58
  paste: 'Paste',
56
59
  errorImageHasZeroSize: 'Error: Image has zero size',
57
60
  describeTheImage: 'Image description',
61
+ fileInput__loading: 'Loading...',
58
62
  // Help text
59
63
  penDropdown__baseHelpText: 'This tool draws shapes or freehand lines.',
60
64
  penDropdown__colorHelpText: 'Changes the pen\'s color',
61
65
  penDropdown__thicknessHelpText: 'Changes the thickness of strokes drawn by the pen.',
62
- penDropdown__penTypeHelpText: 'Changes the pen style.\n\nEither a “pen tip” style or “shape” can be chosen. Choosing a “pen tip” style draws freehand lines. Choosing a “shape” draws shapes.',
66
+ penDropdown__penTypeHelpText: 'Changes the pen style.\n\nEither a “pen” style or “shape” can be chosen. Choosing a “pen” style draws freehand lines. Choosing a “shape” draws shapes.',
63
67
  penDropdown__autocorrectHelpText: 'Converts approximate freehand lines and rectangles to perfect ones.\n\nThe pen must be held stationary at the end of a stroke to trigger a correction.',
64
68
  penDropdown__stabilizationHelpText: 'Draws smoother strokes.\n\nThis also adds a short delay between the mouse/stylus and the stroke.',
65
69
  handDropdown__baseHelpText: 'This tool is responsible for scrolling, rotating, and zooming the editor.',
@@ -69,6 +73,9 @@ export const defaultToolbarLocalization = {
69
73
  handDropdown__zoomDisplayHelpText: 'Shows the current zoom level. 100% shows the image at its actual size.',
70
74
  handDropdown__touchPanningHelpText: 'When enabled, touch gestures move the image rather than select or draw.',
71
75
  handDropdown__lockRotationHelpText: 'When enabled, prevents touch gestures from rotating the screen.',
76
+ eraserDropdown__baseHelpText: 'This tool removes strokes, images, and text under the cursor.',
77
+ eraserDropdown__thicknessHelpText: 'Changes the size of the eraser.',
78
+ eraserDropdown__fullStrokeEraserHelpText: 'When in full-stroke mode, entire shapes are erased.\n\nWhen not in full-stroke mode, shapes can be partially erased.',
72
79
  selectionDropdown__baseHelpText: 'Selects content and manipulates the selection',
73
80
  selectionDropdown__resizeToHelpText: 'Crops the drawing to the size of what\'s currently selected.\n\nIf auto-resize is enabled, it will be disabled.',
74
81
  selectionDropdown__deleteHelpText: 'Erases selected items.',
@@ -126,6 +126,7 @@ export default abstract class BaseWidget {
126
126
  setHidden(hidden: boolean): void;
127
127
  /** Set whether the widget is contained within another. @internal */
128
128
  setIsToplevel(toplevel: boolean): void;
129
+ /** Returns true if the menu for this widget is open. */
129
130
  protected isDropdownVisible(): boolean;
130
131
  protected isSelected(): boolean;
131
132
  private createDropdownIcon;
@@ -399,6 +399,7 @@ class BaseWidget {
399
399
  setIsToplevel(toplevel) {
400
400
  this.toplevel = toplevel;
401
401
  }
402
+ /** Returns true if the menu for this widget is open. */
402
403
  isDropdownVisible() {
403
404
  return this.dropdown?.visible?.get() ?? false;
404
405
  }
@@ -1,15 +1,20 @@
1
1
  import Editor from '../../Editor';
2
2
  import Eraser from '../../tools/Eraser';
3
3
  import { ToolbarLocalization } from '../localization';
4
+ import HelpDisplay from '../utils/HelpDisplay';
4
5
  import BaseToolWidget from './BaseToolWidget';
5
6
  import { SavedToolbuttonState } from './BaseWidget';
6
7
  export default class EraserToolWidget extends BaseToolWidget {
7
8
  private tool;
8
9
  private updateInputs;
9
10
  constructor(editor: Editor, tool: Eraser, localizationTable?: ToolbarLocalization);
11
+ protected getHelpText(): string;
10
12
  protected getTitle(): string;
13
+ private makeIconForType;
11
14
  protected createIcon(): Element;
12
- protected fillDropdown(dropdown: HTMLElement): boolean;
15
+ private static idCounter;
16
+ private makeEraserTypeSelector;
17
+ protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
13
18
  serializeState(): SavedToolbuttonState;
14
19
  deserializeFrom(state: SavedToolbuttonState): void;
15
20
  }
@@ -1,8 +1,9 @@
1
+ import { EraserMode } from '../../tools/Eraser.mjs';
1
2
  import { EditorEventType } from '../../types.mjs';
2
3
  import { toolbarCSSPrefix } from '../constants.mjs';
3
4
  import BaseToolWidget from './BaseToolWidget.mjs';
4
5
  import makeThicknessSlider from './components/makeThicknessSlider.mjs';
5
- export default class EraserToolWidget extends BaseToolWidget {
6
+ class EraserToolWidget extends BaseToolWidget {
6
7
  constructor(editor, tool, localizationTable) {
7
8
  super(editor, tool, 'eraser-tool-widget', localizationTable);
8
9
  this.tool = tool;
@@ -14,26 +15,57 @@ export default class EraserToolWidget extends BaseToolWidget {
14
15
  }
15
16
  });
16
17
  }
18
+ getHelpText() {
19
+ return this.localizationTable.eraserDropdown__baseHelpText;
20
+ }
17
21
  getTitle() {
18
22
  return this.localizationTable.eraser;
19
23
  }
24
+ makeIconForType(mode) {
25
+ return this.editor.icons.makeEraserIcon(this.tool.getThickness(), mode);
26
+ }
20
27
  createIcon() {
21
- return this.editor.icons.makeEraserIcon(this.tool.getThickness());
28
+ return this.makeIconForType(this.tool.getModeValue().get());
29
+ }
30
+ makeEraserTypeSelector(helpDisplay) {
31
+ const container = document.createElement('div');
32
+ const labelElement = document.createElement('label');
33
+ const checkboxElement = document.createElement('input');
34
+ checkboxElement.id = `${toolbarCSSPrefix}eraserToolWidget-${EraserToolWidget.idCounter++}`;
35
+ labelElement.htmlFor = checkboxElement.id;
36
+ labelElement.innerText = this.localizationTable.fullStrokeEraser;
37
+ checkboxElement.type = 'checkbox';
38
+ checkboxElement.oninput = () => {
39
+ this.tool.getModeValue().set(checkboxElement.checked ? EraserMode.FullStroke : EraserMode.PartialStroke);
40
+ };
41
+ const updateValue = () => {
42
+ checkboxElement.checked = this.tool.getModeValue().get() === EraserMode.FullStroke;
43
+ };
44
+ container.replaceChildren(labelElement, checkboxElement);
45
+ helpDisplay?.registerTextHelpForElement(container, this.localizationTable.eraserDropdown__fullStrokeEraserHelpText);
46
+ return {
47
+ addTo: (parent) => {
48
+ parent.appendChild(container);
49
+ },
50
+ updateValue,
51
+ };
22
52
  }
23
- fillDropdown(dropdown) {
53
+ fillDropdown(dropdown, helpDisplay) {
24
54
  const container = document.createElement('div');
25
55
  container.classList.add(`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`);
26
56
  const thicknessSlider = makeThicknessSlider(this.editor, thickness => {
27
57
  this.tool.setThickness(thickness);
28
58
  });
29
59
  thicknessSlider.setBounds(10, 55);
60
+ helpDisplay?.registerTextHelpForElement(thicknessSlider.container, this.localizationTable.eraserDropdown__thicknessHelpText);
61
+ const modeSelector = this.makeEraserTypeSelector(helpDisplay);
30
62
  this.updateInputs = () => {
31
63
  thicknessSlider.setValue(this.tool.getThickness());
64
+ modeSelector.updateValue();
32
65
  };
33
66
  this.updateInputs();
34
- const spacer = document.createElement('div');
35
- spacer.style.height = '5px';
36
- container.replaceChildren(thicknessSlider.container, spacer);
67
+ container.replaceChildren(thicknessSlider.container);
68
+ modeSelector.addTo(container);
37
69
  dropdown.replaceChildren(container);
38
70
  return true;
39
71
  }
@@ -41,6 +73,7 @@ export default class EraserToolWidget extends BaseToolWidget {
41
73
  return {
42
74
  ...super.serializeState(),
43
75
  thickness: this.tool.getThickness(),
76
+ mode: this.tool.getModeValue().get(),
44
77
  };
45
78
  }
46
79
  deserializeFrom(state) {
@@ -52,5 +85,13 @@ export default class EraserToolWidget extends BaseToolWidget {
52
85
  }
53
86
  this.tool.setThickness(parsedThickness);
54
87
  }
88
+ if (state.mode) {
89
+ const mode = state.mode;
90
+ if (Object.values(EraserMode).includes(mode)) {
91
+ this.tool.getModeValue().set(mode);
92
+ }
93
+ }
55
94
  }
56
95
  }
96
+ EraserToolWidget.idCounter = 0;
97
+ export default EraserToolWidget;
@@ -0,0 +1,22 @@
1
+ import { RenderableImage } from 'js-draw/src/rendering/renderers/AbstractRenderer';
2
+ /** Handles filtering and other operations on an image. */
3
+ export declare class ImageWrapper {
4
+ private imageBase64Url;
5
+ private preview;
6
+ private onUrlUpdate;
7
+ private readonly originalSrc;
8
+ private altText;
9
+ private constructor();
10
+ private updateImageData;
11
+ decreaseSize(resizeFactor?: number): void;
12
+ reset(): void;
13
+ isChanged(): boolean;
14
+ getBase64Url(): string;
15
+ getAltText(): string;
16
+ setAltText(text: string): void;
17
+ static fromSrcAndPreview(initialBase64Src: string, preview: HTMLImageElement, onUrlUpdate: () => void): ImageWrapper;
18
+ static fromRenderable(renderable: RenderableImage, onUrlUpdate: () => void): {
19
+ wrapper: ImageWrapper;
20
+ preview: HTMLImageElement;
21
+ };
22
+ }
@@ -0,0 +1,54 @@
1
+ /** Handles filtering and other operations on an image. */
2
+ export class ImageWrapper {
3
+ constructor(imageBase64Url, preview, onUrlUpdate) {
4
+ this.imageBase64Url = imageBase64Url;
5
+ this.preview = preview;
6
+ this.onUrlUpdate = onUrlUpdate;
7
+ this.originalSrc = imageBase64Url;
8
+ preview.src = imageBase64Url;
9
+ }
10
+ updateImageData(base64DataUrl) {
11
+ this.preview.src = base64DataUrl;
12
+ this.imageBase64Url = base64DataUrl;
13
+ this.onUrlUpdate();
14
+ }
15
+ decreaseSize(resizeFactor = 3 / 4) {
16
+ const canvas = document.createElement('canvas');
17
+ canvas.width = this.preview.naturalWidth * resizeFactor;
18
+ canvas.height = this.preview.naturalHeight * resizeFactor;
19
+ const ctx = canvas.getContext('2d');
20
+ ctx?.drawImage(this.preview, 0, 0, canvas.width, canvas.height);
21
+ // JPEG can be much smaller than PNG for the same image size. Prefer it if
22
+ // the image is already a JPEG.
23
+ const format = this.originalSrc?.startsWith('data:image/jpeg;') ? 'image/jpeg' : 'image/png';
24
+ this.updateImageData(canvas.toDataURL(format));
25
+ }
26
+ reset() {
27
+ this.updateImageData(this.originalSrc);
28
+ }
29
+ isChanged() {
30
+ return this.imageBase64Url !== this.originalSrc;
31
+ }
32
+ getBase64Url() {
33
+ return this.imageBase64Url;
34
+ }
35
+ getAltText() {
36
+ return this.altText;
37
+ }
38
+ setAltText(text) {
39
+ this.altText = text;
40
+ }
41
+ static fromSrcAndPreview(initialBase64Src, preview, onUrlUpdate) {
42
+ return new ImageWrapper(initialBase64Src, preview, onUrlUpdate);
43
+ }
44
+ static fromRenderable(renderable, onUrlUpdate) {
45
+ const preview = new Image();
46
+ preview.src = renderable.base64Url;
47
+ const result = new ImageWrapper(renderable.base64Url, preview, onUrlUpdate);
48
+ const altText = renderable.label ?? renderable.image.getAttribute('alt');
49
+ if (altText) {
50
+ result.setAltText(altText);
51
+ }
52
+ return { wrapper: result, preview };
53
+ }
54
+ }
@@ -0,0 +1,3 @@
1
+ import type { RenderableImage } from '../../../rendering/renderers/AbstractRenderer';
2
+ declare const fileToImages: (imageFile: File) => Promise<RenderableImage[]>;
3
+ export default fileToImages;
@@ -0,0 +1,16 @@
1
+ import fileToBase64Url from '../../../util/fileToBase64Url.mjs';
2
+ import { Mat33 } from '@js-draw/math';
3
+ const fileToImages = async (imageFile) => {
4
+ const result = [];
5
+ const imageElement = new Image();
6
+ const base64Url = await fileToBase64Url(imageFile);
7
+ if (base64Url) {
8
+ result.push({
9
+ image: imageElement,
10
+ base64Url: base64Url,
11
+ transform: Mat33.identity,
12
+ });
13
+ }
14
+ return result;
15
+ };
16
+ export default fileToImages;
@@ -0,0 +1,37 @@
1
+ import Editor from '../../../Editor';
2
+ import { ToolbarLocalization } from '../../localization';
3
+ import BaseWidget from '../BaseWidget';
4
+ /**
5
+ * Provides a widget that allows inserting or modifying raster images.
6
+ *
7
+ * It's possible to customize the file picker used by this widget through {@link EditorSettings.image}.
8
+ *
9
+ * @example
10
+ * ```ts,runnable
11
+ * import { Editor, makeEdgeToolbar, InsertImageWidget } from 'js-draw';
12
+ *
13
+ * const editor = new Editor(document.body);
14
+ * const toolbar = makeEdgeToolbar(editor);
15
+ *
16
+ * toolbar.addWidget(new InsertImageWidget(editor));
17
+ * ```
18
+ */
19
+ export default class InsertImageWidget extends BaseWidget {
20
+ private images;
21
+ private imagesPreview;
22
+ private selectedFiles;
23
+ private imageAltTextInput;
24
+ private statusView;
25
+ private submitButton;
26
+ constructor(editor: Editor, localization?: ToolbarLocalization);
27
+ protected getTitle(): string;
28
+ protected createIcon(): Element | null;
29
+ protected setDropdownVisible(visible: boolean): void;
30
+ protected handleClick(): void;
31
+ private static nextInputId;
32
+ protected fillDropdown(dropdown: HTMLElement): boolean;
33
+ private onImageDataUpdate;
34
+ private hideDialog;
35
+ private updateImageSizeDisplay;
36
+ private updateInputs;
37
+ }