js-draw 1.18.0 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. package/README.md +51 -0
  2. package/dist/Editor.css +78 -5
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +20 -1
  6. package/dist/cjs/Editor.js +6 -0
  7. package/dist/cjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  8. package/dist/cjs/{SVGLoader.js → SVGLoader/index.js} +15 -30
  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/TextComponent.js +3 -1
  13. package/dist/cjs/image/EditorImage.d.ts +2 -1
  14. package/dist/cjs/image/EditorImage.js +101 -5
  15. package/dist/cjs/rendering/caching/RenderingCacheNode.js +20 -15
  16. package/dist/cjs/rendering/renderers/CanvasRenderer.js +4 -4
  17. package/dist/cjs/testing/findNodeWithText.d.ts +3 -0
  18. package/dist/cjs/testing/findNodeWithText.js +16 -0
  19. package/dist/cjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  20. package/dist/cjs/testing/firstElementAncestorOfNode.js +13 -0
  21. package/dist/cjs/testing/sendKeyPressRelease.d.ts +3 -0
  22. package/dist/cjs/testing/sendKeyPressRelease.js +8 -0
  23. package/dist/cjs/testing/sendPenEvent.d.ts +2 -2
  24. package/dist/cjs/testing/sendPenEvent.js +26 -3
  25. package/dist/cjs/toolbar/localization.d.ts +3 -0
  26. package/dist/cjs/toolbar/localization.js +3 -0
  27. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  28. package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -0
  29. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +23 -0
  30. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.js +65 -0
  31. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  32. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.js +21 -0
  33. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  34. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.js +289 -0
  35. package/dist/cjs/toolbar/widgets/TextToolWidget.js +5 -3
  36. package/dist/cjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  37. package/dist/cjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  38. package/dist/cjs/toolbar/widgets/components/makeFileInput.js +113 -45
  39. package/dist/cjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  40. package/dist/cjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  41. package/dist/cjs/toolbar/widgets/components/makeSnappedList.js +168 -0
  42. package/dist/cjs/tools/Eraser.d.ts +7 -2
  43. package/dist/cjs/tools/Eraser.js +76 -6
  44. package/dist/cjs/tools/PanZoom.d.ts +54 -0
  45. package/dist/cjs/tools/PanZoom.js +54 -2
  46. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -2
  47. package/dist/cjs/tools/SelectionTool/Selection.js +20 -20
  48. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  49. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +6 -0
  50. package/dist/cjs/tools/SelectionTool/SelectionTool.js +1 -1
  51. package/dist/cjs/tools/SelectionTool/types.d.ts +19 -0
  52. package/dist/cjs/tools/TextTool.js +2 -1
  53. package/dist/cjs/tools/TextTool.test.d.ts +1 -0
  54. package/dist/cjs/tools/ToolController.d.ts +2 -0
  55. package/dist/cjs/tools/ToolController.js +10 -1
  56. package/dist/cjs/util/ReactiveValue.d.ts +6 -0
  57. package/dist/cjs/util/ReactiveValue.js +16 -0
  58. package/dist/cjs/util/bytesToSizeString.d.ts +8 -0
  59. package/dist/cjs/util/bytesToSizeString.js +26 -0
  60. package/dist/cjs/util/bytesToSizeString.test.d.ts +1 -0
  61. package/dist/cjs/util/stopPropagationOfScrollingWheelEvents.js +10 -6
  62. package/dist/cjs/util/waitForAll.d.ts +2 -0
  63. package/dist/cjs/util/waitForAll.js +2 -0
  64. package/dist/cjs/util/waitForImageLoaded.js +3 -0
  65. package/dist/cjs/util/waitForTimeout.d.ts +1 -0
  66. package/dist/cjs/util/waitForTimeout.js +1 -1
  67. package/dist/cjs/version.js +1 -1
  68. package/dist/mjs/Editor.d.ts +20 -1
  69. package/dist/mjs/Editor.mjs +6 -0
  70. package/dist/mjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  71. package/dist/mjs/{SVGLoader.mjs → SVGLoader/index.mjs} +15 -30
  72. package/dist/mjs/SVGLoader/index.test.d.ts +1 -0
  73. package/dist/mjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
  74. package/dist/mjs/SVGLoader/utils/determineFontSize.mjs +25 -0
  75. package/dist/mjs/Viewport.d.ts +33 -1
  76. package/dist/mjs/components/TextComponent.mjs +3 -1
  77. package/dist/mjs/image/EditorImage.d.ts +2 -1
  78. package/dist/mjs/image/EditorImage.mjs +101 -5
  79. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +20 -15
  80. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +4 -4
  81. package/dist/mjs/testing/findNodeWithText.d.ts +3 -0
  82. package/dist/mjs/testing/findNodeWithText.mjs +14 -0
  83. package/dist/mjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  84. package/dist/mjs/testing/firstElementAncestorOfNode.mjs +11 -0
  85. package/dist/mjs/testing/sendKeyPressRelease.d.ts +3 -0
  86. package/dist/mjs/testing/sendKeyPressRelease.mjs +6 -0
  87. package/dist/mjs/testing/sendPenEvent.d.ts +2 -2
  88. package/dist/mjs/testing/sendPenEvent.mjs +3 -3
  89. package/dist/mjs/toolbar/localization.d.ts +3 -0
  90. package/dist/mjs/toolbar/localization.mjs +3 -0
  91. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  92. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -0
  93. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +23 -0
  94. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.mjs +61 -0
  95. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  96. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.mjs +16 -0
  97. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  98. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.mjs +284 -0
  99. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.test.d.ts +1 -0
  100. package/dist/mjs/toolbar/widgets/TextToolWidget.mjs +5 -3
  101. package/dist/mjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  102. package/dist/mjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  103. package/dist/mjs/toolbar/widgets/components/makeFileInput.mjs +113 -45
  104. package/dist/mjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  105. package/dist/mjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  106. package/dist/mjs/toolbar/widgets/components/makeSnappedList.mjs +163 -0
  107. package/dist/mjs/tools/Eraser.d.ts +7 -2
  108. package/dist/mjs/tools/Eraser.mjs +76 -6
  109. package/dist/mjs/tools/PanZoom.d.ts +54 -0
  110. package/dist/mjs/tools/PanZoom.mjs +54 -2
  111. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -2
  112. package/dist/mjs/tools/SelectionTool/Selection.mjs +20 -20
  113. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  114. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +6 -0
  115. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +1 -1
  116. package/dist/mjs/tools/SelectionTool/types.d.ts +19 -0
  117. package/dist/mjs/tools/TextTool.mjs +2 -1
  118. package/dist/mjs/tools/TextTool.test.d.ts +1 -0
  119. package/dist/mjs/tools/ToolController.d.ts +2 -0
  120. package/dist/mjs/tools/ToolController.mjs +10 -1
  121. package/dist/mjs/util/ReactiveValue.d.ts +6 -0
  122. package/dist/mjs/util/ReactiveValue.mjs +16 -0
  123. package/dist/mjs/util/bytesToSizeString.d.ts +8 -0
  124. package/dist/mjs/util/bytesToSizeString.mjs +24 -0
  125. package/dist/mjs/util/bytesToSizeString.test.d.ts +1 -0
  126. package/dist/mjs/util/stopPropagationOfScrollingWheelEvents.mjs +10 -6
  127. package/dist/mjs/util/waitForAll.d.ts +2 -0
  128. package/dist/mjs/util/waitForAll.mjs +2 -0
  129. package/dist/mjs/util/waitForImageLoaded.mjs +3 -0
  130. package/dist/mjs/util/waitForTimeout.d.ts +1 -0
  131. package/dist/mjs/util/waitForTimeout.mjs +1 -1
  132. package/dist/mjs/version.mjs +1 -1
  133. package/package.json +4 -4
  134. package/src/toolbar/EdgeToolbar.scss +8 -3
  135. package/src/toolbar/toolbar.scss +1 -7
  136. package/src/toolbar/widgets/{InsertImageWidget.scss → InsertImageWidget/index.scss} +3 -2
  137. package/src/toolbar/widgets/components/components.scss +2 -1
  138. package/src/toolbar/widgets/components/makeFileInput.scss +14 -1
  139. package/src/toolbar/widgets/components/makeSnappedList.scss +74 -0
  140. package/src/toolbar/widgets/widgets.scss +7 -0
  141. package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  142. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +0 -269
  143. package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  144. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +0 -264
  145. /package/dist/cjs/{SVGLoader.test.d.ts → SVGLoader/index.test.d.ts} +0 -0
  146. /package/dist/{mjs/SVGLoader.test.d.ts → cjs/toolbar/widgets/InsertImageWidget/index.test.d.ts} +0 -0
@@ -64,6 +64,11 @@ class SelectionHandle {
64
64
  addTo(container) {
65
65
  container.appendChild(this.element);
66
66
  }
67
+ /**
68
+ * Removes this element from its container. Should only be called
69
+ * after {@link addTo}.
70
+ */
71
+ remove() { this.element.remove(); }
67
72
  /**
68
73
  * Returns this handle's bounding box relative to the top left of the
69
74
  * selection box.
@@ -112,6 +117,7 @@ class SelectionHandle {
112
117
  handleDragStart(pointer) {
113
118
  this.onDragStart(pointer.canvasPos);
114
119
  this.dragLastPos = pointer.canvasPos;
120
+ return true;
115
121
  }
116
122
  handleDragUpdate(pointer) {
117
123
  if (!this.dragLastPos) {
@@ -404,7 +404,7 @@ class SelectionTool extends BaseTool_1.default {
404
404
  resolve(blob);
405
405
  }
406
406
  else {
407
- reject('Failed to convert canvas to blob.');
407
+ reject(new Error('Failed to convert canvas to blob.'));
408
408
  }
409
409
  }, 'image/png');
410
410
  }));
@@ -1,3 +1,5 @@
1
+ import type { Rect2, Point2 } from '@js-draw/math';
2
+ import Pointer from '../../Pointer';
1
3
  export declare enum ResizeMode {
2
4
  Both = 0,
3
5
  HorizontalOnly = 1,
@@ -7,3 +9,20 @@ export declare enum TransformMode {
7
9
  Snap = 0,
8
10
  NoSnap = 1
9
11
  }
12
+ /**
13
+ * Represents a child of the selection that should move with the selection
14
+ * and handle events.
15
+ *
16
+ * Although selection children should be `HTMLElement`s, the selection may be
17
+ * hidden behind an invisible element. As such, these elements should handle
18
+ * drag start/update/end events.
19
+ */
20
+ export interface SelectionBoxChild {
21
+ updatePosition(selectionScreenBBox: Rect2): void;
22
+ containsPoint(point: Point2): boolean;
23
+ addTo(container: HTMLElement): void;
24
+ remove(): void;
25
+ handleDragStart(pointer: Pointer): boolean;
26
+ handleDragUpdate(pointer: Pointer): void;
27
+ handleDragEnd(): void;
28
+ }
@@ -23,9 +23,10 @@ class TextTool extends BaseTool_1.default {
23
23
  this.textMeasuringCtx = null;
24
24
  this.textScale = math_1.Vec2.of(1, 1);
25
25
  this.removeExistingCommand = null;
26
+ const editorFonts = editor.getCurrentSettings().text?.fonts ?? [];
26
27
  this.textStyleValue = ReactiveValue_1.ReactiveValue.fromInitialValue({
27
28
  size: 32,
28
- fontFamily: 'sans-serif',
29
+ fontFamily: editorFonts.length > 0 ? editorFonts[0] : 'sans-serif',
29
30
  renderingStyle: {
30
31
  fill: math_1.Color4.purple,
31
32
  },
@@ -0,0 +1 @@
1
+ export {};
@@ -55,6 +55,8 @@ export default class ToolController implements InputEventListener {
55
55
  insertToolsAfter(insertAfter: BaseTool, toolsToInsert: BaseTool[]): void;
56
56
  /** @see {@link insertToolsAfter} */
57
57
  insertToolsBefore(insertBefore: BaseTool, toolsToInsert: BaseTool[]): void;
58
+ /** @internal */
59
+ changeActiveToolTo(tool: BaseTool): void;
58
60
  private onEventInternal;
59
61
  /** Alias for {@link dispatchInputEvent}. */
60
62
  onEvent(event: InputEvt): boolean;
@@ -62,6 +62,7 @@ class ToolController {
62
62
  const secondaryPenTool = new Pen_1.default(editor, localization.penTool(2), { color: math_1.Color4.clay, thickness: 4 });
63
63
  // Stabilize the secondary pen tool.
64
64
  secondaryPenTool.setInputMapper(new InputStabilizer_1.default(editor.viewport));
65
+ const eraser = new Eraser_1.default(editor, localization.eraserTool);
65
66
  const primaryTools = [
66
67
  // Three pens
67
68
  primaryPenTool,
@@ -72,7 +73,7 @@ class ToolController {
72
73
  thickness: 40,
73
74
  factory: PressureSensitiveFreehandLineBuilder_1.makePressureSensitiveFreehandLineBuilder
74
75
  }),
75
- new Eraser_1.default(editor, localization.eraserTool),
76
+ eraser,
76
77
  new SelectionTool_1.default(editor, localization.selectionTool),
77
78
  new TextTool_1.default(editor, localization.textTool, localization),
78
79
  new PanZoom_1.default(editor, PanZoom_1.PanZoomMode.SinglePointerGestures, localization.anyDevicePanning),
@@ -90,6 +91,7 @@ class ToolController {
90
91
  new UndoRedoShortcut_1.default(editor),
91
92
  new ToolbarShortcutHandler_1.default(editor),
92
93
  new ToolSwitcherShortcut_1.default(editor),
94
+ eraser.makeEraserSwitcherTool(),
93
95
  new FindTool_1.default(editor),
94
96
  new PasteHandler_1.default(editor),
95
97
  new SelectAllShortcutHandler_1.default(editor),
@@ -208,6 +210,13 @@ class ToolController {
208
210
  insertToolsBefore(insertBefore, toolsToInsert) {
209
211
  this.insertTools(insertBefore, toolsToInsert, 'before');
210
212
  }
213
+ /** @internal */
214
+ changeActiveToolTo(tool) {
215
+ if (!tool.isEnabled()) {
216
+ tool.setEnabled(true);
217
+ }
218
+ this.activeTool = tool;
219
+ }
211
220
  // @internal use `dispatchEvent` rather than calling `onEvent` directly.
212
221
  onEventInternal(event) {
213
222
  const isEditorReadOnly = this.isEditorReadOnly.get();
@@ -2,6 +2,9 @@ type ListenerResult = {
2
2
  remove(): void;
3
3
  };
4
4
  type UpdateCallback<T> = (value: T) => void;
5
+ type ReactiveValuesOf<T extends unknown[]> = {
6
+ [key in keyof T]: ReactiveValue<T[key]>;
7
+ };
5
8
  /**
6
9
  * A `ReactiveValue` is a value that
7
10
  * - updates periodically,
@@ -33,6 +36,8 @@ export declare abstract class ReactiveValue<T> {
33
36
  * @see {@link onUpdate}.
34
37
  */
35
38
  abstract onUpdateAndNow(callback: UpdateCallback<T>): ListenerResult;
39
+ /** Returns a promise that resolves when this value is next changed. */
40
+ waitForNextUpdate(): Promise<T>;
36
41
  /** Creates a `ReactiveValue` with an initial value, `initialValue`. */
37
42
  static fromInitialValue<T>(initialValue: T): MutableReactiveValue<T>;
38
43
  /** Returns a `ReactiveValue` that is **known** will never change. */
@@ -54,6 +59,7 @@ export declare abstract class ReactiveValue<T> {
54
59
  * Returns a reactive value derived from a single `source`.
55
60
  */
56
61
  static map<A, B>(source: ReactiveValue<A>, map: (a: A) => B, inverseMap: (b: B) => A): MutableReactiveValue<B>;
62
+ static union<Values extends [...unknown[]]>(values: ReactiveValuesOf<Values>): ReactiveValue<Values>;
57
63
  }
58
64
  export declare abstract class MutableReactiveValue<T> extends ReactiveValue<T> {
59
65
  /**
@@ -38,6 +38,15 @@ const noOpSetUpdateListener = () => {
38
38
  * Avoid extending this class from an external library, as that may not be stable.
39
39
  */
40
40
  class ReactiveValue {
41
+ /** Returns a promise that resolves when this value is next changed. */
42
+ waitForNextUpdate() {
43
+ return new Promise(resolve => {
44
+ const listener = this.onUpdate(value => {
45
+ listener.remove();
46
+ resolve(value);
47
+ });
48
+ });
49
+ }
41
50
  /** Creates a `ReactiveValue` with an initial value, `initialValue`. */
42
51
  static fromInitialValue(initialValue) {
43
52
  return new ReactiveValueImpl(initialValue);
@@ -51,6 +60,8 @@ class ReactiveValue {
51
60
  callback(value);
52
61
  return noOpUpdateListenerResult;
53
62
  },
63
+ // Never resolves -- immutable.
64
+ waitForNextUpdate: () => new Promise(() => { }),
54
65
  };
55
66
  }
56
67
  /**
@@ -95,6 +106,11 @@ class ReactiveValue {
95
106
  }
96
107
  return result;
97
108
  }
109
+ static union(values) {
110
+ return ReactiveValue.fromCallback(() => {
111
+ return values.map(value => value.get());
112
+ }, values);
113
+ }
98
114
  }
99
115
  exports.ReactiveValue = ReactiveValue;
100
116
  class MutableReactiveValue extends ReactiveValue {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns a size in bytes, KiB, or MiB with units suffix.
3
+ */
4
+ declare const bytesToSizeString: (sizeBytes: number) => {
5
+ size: number;
6
+ units: string;
7
+ };
8
+ export default bytesToSizeString;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Returns a size in bytes, KiB, or MiB with units suffix.
5
+ */
6
+ const bytesToSizeString = (sizeBytes) => {
7
+ const sizeInKiB = sizeBytes / 1024;
8
+ const sizeInMiB = sizeInKiB / 1024;
9
+ const sizeInGiB = sizeInMiB / 1024;
10
+ let units = 'B';
11
+ let size = sizeBytes;
12
+ if (sizeInGiB >= 1) {
13
+ size = sizeInGiB;
14
+ units = 'GiB';
15
+ }
16
+ else if (sizeInMiB >= 1) {
17
+ size = sizeInMiB;
18
+ units = 'MiB';
19
+ }
20
+ else if (sizeInKiB >= 1) {
21
+ size = sizeInKiB;
22
+ units = 'KiB';
23
+ }
24
+ return { size, units };
25
+ };
26
+ exports.default = bytesToSizeString;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,15 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const stopPropagationOfScrollingWheelEvents = (scrollingContainer) => {
4
+ const scrollsAxis = (delta, clientSize, scrollOffset, scrollSize) => {
5
+ const hasScroll = clientSize !== scrollSize && delta !== 0;
6
+ const eventScrollsPastStart = scrollOffset + delta <= 0;
7
+ const scrollEnd = scrollOffset + clientSize;
8
+ const eventScrollsPastEnd = scrollEnd + delta > scrollSize;
9
+ return hasScroll && !eventScrollsPastStart && !eventScrollsPastEnd;
10
+ };
4
11
  scrollingContainer.onwheel = (event) => {
5
- const hasScroll = scrollingContainer.clientWidth !== scrollingContainer.scrollWidth
6
- && event.deltaX !== 0;
7
- const eventScrollsPastLeft = scrollingContainer.scrollLeft + event.deltaX <= 0;
8
- const scrollRight = scrollingContainer.scrollLeft + scrollingContainer.clientWidth;
9
- const eventScrollsPastRight = scrollRight + event.deltaX > scrollingContainer.scrollWidth;
12
+ const scrollsX = scrollsAxis(event.deltaX, scrollingContainer.clientWidth, scrollingContainer.scrollLeft, scrollingContainer.scrollWidth);
13
+ const scrollsY = scrollsAxis(event.deltaY, scrollingContainer.clientHeight, scrollingContainer.scrollTop, scrollingContainer.scrollHeight);
10
14
  // Stop the editor from receiving the event if it will scroll the pen type selector
11
15
  // instead.
12
- if (hasScroll && !eventScrollsPastLeft && !eventScrollsPastRight) {
16
+ if (scrollsX || scrollsY) {
13
17
  event.stopPropagation();
14
18
  }
15
19
  };
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Resolves when all given promises have resolved. If no promises are given,
3
3
  * does not return a Promise.
4
+ *
5
+ * If all elements of `results` are known to be `Promise`s, use `Promise.all`.
4
6
  */
5
7
  declare const waitForAll: (results: (Promise<void> | void)[]) => Promise<void> | void;
6
8
  export default waitForAll;
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
4
  * Resolves when all given promises have resolved. If no promises are given,
5
5
  * does not return a Promise.
6
+ *
7
+ * If all elements of `results` are known to be `Promise`s, use `Promise.all`.
6
8
  */
7
9
  const waitForAll = (results) => {
8
10
  // If any are Promises...
@@ -4,7 +4,10 @@ const waitForImageLoad = async (image) => {
4
4
  if (!image.complete) {
5
5
  await new Promise((resolve, reject) => {
6
6
  image.onload = event => resolve(event);
7
+ // TODO(v2): Return a `new Error(event.message)`
8
+ // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- Forwarding an error-like object.
7
9
  image.onerror = event => reject(event);
10
+ // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- Forwarding an error-like object.
8
11
  image.onabort = event => reject(event);
9
12
  });
10
13
  }
@@ -1,2 +1,3 @@
1
+ /** Returns a promise that resolves after `timeout` milliseconds. */
1
2
  declare const waitForTimeout: (timeout: number) => Promise<void>;
2
3
  export default waitForTimeout;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- // Returns a promise that resolves after `timeout` milliseconds.
3
+ /** Returns a promise that resolves after `timeout` milliseconds. */
4
4
  const waitForTimeout = (timeout) => {
5
5
  return new Promise(resolve => {
6
6
  setTimeout(() => resolve(), timeout);
@@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  * @internal
7
7
  */
8
8
  exports.default = {
9
- number: '1.18.0',
9
+ number: '1.20.0',
10
10
  };
@@ -19,6 +19,7 @@ import RenderablePathSpec from './rendering/RenderablePathSpec';
19
19
  import { AboutDialogEntry } from './dialogs/makeAboutDialog';
20
20
  import ReactiveValue, { MutableReactiveValue } from './util/ReactiveValue';
21
21
  import { PenTypeRecord } from './toolbar/widgets/PenToolWidget';
22
+ import { ShowCustomFilePickerCallback } from './toolbar/widgets/components/makeFileInput';
22
23
  /**
23
24
  * Provides settings to an instance of an editor. See the Editor {@link Editor.constructor}.
24
25
  *
@@ -76,7 +77,7 @@ export interface EditorSettings {
76
77
  version?: string;
77
78
  } | null;
78
79
  /**
79
- * Configures the default pen tools.
80
+ * Configures the default {@link PenTool} tools.
80
81
  *
81
82
  * **Example**:
82
83
  * [[include:doc-pages/inline-examples/editor-settings-polyline-pen.md]]
@@ -102,6 +103,24 @@ export interface EditorSettings {
102
103
  */
103
104
  filterPenTypes?: (penType: PenTypeRecord) => boolean;
104
105
  } | null;
106
+ /** Configures the default {@link TextTool} control and text tool. */
107
+ text: {
108
+ /** Fonts to show in the text UI. */
109
+ fonts?: string[];
110
+ } | null;
111
+ /** Configures the default {@link InsertImageWidget} control. */
112
+ image: {
113
+ /**
114
+ * A custom callback to show an image picker. If given, this should return
115
+ * a list of `File`s representing the images selected by the picker.
116
+ *
117
+ * If not given, the default file picker shown by a [file input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)
118
+ * is shown.
119
+ *
120
+ * @beta -- API may change between minor releases.
121
+ */
122
+ showImagePicker?: ShowCustomFilePickerCallback;
123
+ } | null;
105
124
  }
106
125
  /**
107
126
  * The main entrypoint for the full editor.
@@ -113,6 +113,12 @@ export class Editor {
113
113
  additionalPenTypes: settings.pens?.additionalPenTypes ?? [],
114
114
  filterPenTypes: settings.pens?.filterPenTypes ?? (() => true)
115
115
  },
116
+ text: {
117
+ fonts: settings.text?.fonts ?? ['sans-serif', 'serif', 'monospace'],
118
+ },
119
+ image: {
120
+ showImagePicker: settings.image?.showImagePicker ?? undefined,
121
+ },
116
122
  };
117
123
  // Validate settings
118
124
  if (this.settings.minZoom > this.settings.maxZoom) {
@@ -1,5 +1,5 @@
1
1
  import { Rect2 } from '@js-draw/math';
2
- import { ComponentAddedListener, ImageLoader, OnDetermineExportRectListener, OnProgressListener } from './types';
2
+ import { ComponentAddedListener, ImageLoader, OnDetermineExportRectListener, OnProgressListener } from '../types';
3
3
  export declare const defaultSVGViewRect: Rect2;
4
4
  export declare const svgAttributesDataKey = "svgAttrs";
5
5
  export declare const svgStyleAttributesDataKey = "svgStyleAttrs";
@@ -1,12 +1,13 @@
1
1
  import { Color4, Mat33, Path, Rect2, Vec2 } from '@js-draw/math';
2
- import BackgroundComponent, { BackgroundType, backgroundTypeToClassNameMap, imageBackgroundCSSClassName, imageBackgroundGridSizeCSSPrefix, imageBackgroundNonAutomaticSecondaryColorCSSClassName } from './components/BackgroundComponent.mjs';
3
- import ImageComponent from './components/ImageComponent.mjs';
4
- import Stroke from './components/Stroke.mjs';
5
- import SVGGlobalAttributesObject from './components/SVGGlobalAttributesObject.mjs';
6
- import TextComponent, { TextTransformMode } from './components/TextComponent.mjs';
7
- import UnknownSVGObject from './components/UnknownSVGObject.mjs';
8
- import { pathToRenderable } from './rendering/RenderablePathSpec.mjs';
9
- import { renderedStylesheetId } from './rendering/renderers/SVGRenderer.mjs';
2
+ import BackgroundComponent, { BackgroundType, backgroundTypeToClassNameMap, imageBackgroundCSSClassName, imageBackgroundGridSizeCSSPrefix, imageBackgroundNonAutomaticSecondaryColorCSSClassName } from '../components/BackgroundComponent.mjs';
3
+ import ImageComponent from '../components/ImageComponent.mjs';
4
+ import Stroke from '../components/Stroke.mjs';
5
+ import SVGGlobalAttributesObject from '../components/SVGGlobalAttributesObject.mjs';
6
+ import TextComponent, { TextTransformMode } from '../components/TextComponent.mjs';
7
+ import UnknownSVGObject from '../components/UnknownSVGObject.mjs';
8
+ import { pathToRenderable } from '../rendering/RenderablePathSpec.mjs';
9
+ import { renderedStylesheetId } from '../rendering/renderers/SVGRenderer.mjs';
10
+ import determineFontSize from './utils/determineFontSize.mjs';
10
11
  // Size of a loaded image if no size is specified.
11
12
  export const defaultSVGViewRect = new Rect2(0, 0, 500, 500);
12
13
  // Key to retrieve unrecognised attributes from an AbstractComponent
@@ -46,7 +47,9 @@ export default class SVGLoader {
46
47
  let fill = Color4.transparent;
47
48
  let stroke;
48
49
  // If possible, use computedStyles (allows property inheritance).
49
- const fillAttribute = node.getAttribute('fill') ?? computedStyles?.fill ?? node.style?.fill;
50
+ // Chromium, however, sets .fill to a falsy, but not undefined value in some cases where
51
+ // styles are available. As such, use || instead of ??.
52
+ const fillAttribute = node.getAttribute('fill') ?? (computedStyles?.fill || node.style?.fill);
50
53
  if (fillAttribute) {
51
54
  try {
52
55
  fill = Color4.fromString(fillAttribute);
@@ -296,31 +299,13 @@ export default class SVGLoader {
296
299
  }
297
300
  // Compute styles.
298
301
  const computedStyles = this.getComputedStyle(elem);
299
- const fontSizeExp = /^([-0-9.e]+)px/i;
300
- // In some environments, computedStyles.fontSize can be increased by the system.
301
- // Thus, to prevent text from growing on load/save, prefer .style.fontSize.
302
- let fontSizeMatch = fontSizeExp.exec(elem.style?.fontSize ?? '');
303
- if (!fontSizeMatch && elem.tagName.toLowerCase() === 'tspan' && elem.parentElement) {
304
- // Try to inherit the font size of the parent text element.
305
- fontSizeMatch = fontSizeExp.exec(elem.parentElement.style?.fontSize ?? '');
306
- }
307
- // If we still couldn't find a font size, try to use computedStyles (which can be
308
- // wrong).
309
- if (!fontSizeMatch && computedStyles) {
310
- fontSizeMatch = fontSizeExp.exec(computedStyles.fontSize);
311
- }
312
- const supportedStyleAttrs = [
302
+ const supportedStyleAttrs = new Set([
313
303
  'fontFamily',
314
304
  'transform',
315
305
  ...supportedStrokeFillStyleAttrs,
316
- ];
317
- let fontSize = 12;
318
- if (fontSizeMatch) {
319
- supportedStyleAttrs.push('fontSize');
320
- fontSize = parseFloat(fontSizeMatch[1]);
321
- }
306
+ ]);
322
307
  const style = {
323
- size: fontSize,
308
+ size: determineFontSize(elem, computedStyles, supportedStyleAttrs),
324
309
  fontFamily: computedStyles?.fontFamily || elem.style?.fontFamily || 'sans-serif',
325
310
  fontWeight: computedStyles?.fontWeight || elem.style?.fontWeight || undefined,
326
311
  fontStyle: computedStyles?.fontStyle || elem.style?.fontStyle || undefined,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ /** Computes the font size of a text element, based on style information. */
2
+ declare const determineFontSize: (elem: SVGTextElement | SVGTSpanElement, computedStyles: CSSStyleDeclaration | undefined, supportedStyleAttrs: Set<string>) => number;
3
+ export default determineFontSize;
@@ -0,0 +1,25 @@
1
+ /** Computes the font size of a text element, based on style information. */
2
+ const determineFontSize = (elem, computedStyles,
3
+ // output: Written to to update supported style attributes
4
+ supportedStyleAttrs) => {
5
+ const fontSizeExp = /^([-0-9.e]+)px/i;
6
+ // In some environments, computedStyles.fontSize can be increased by the system.
7
+ // Thus, to prevent text from growing on load/save, prefer .style.fontSize.
8
+ let fontSizeMatch = fontSizeExp.exec(elem.style?.fontSize ?? '');
9
+ if (!fontSizeMatch && elem.tagName.toLowerCase() === 'tspan' && elem.parentElement) {
10
+ // Try to inherit the font size of the parent text element.
11
+ fontSizeMatch = fontSizeExp.exec(elem.parentElement.style?.fontSize ?? '');
12
+ }
13
+ // If we still couldn't find a font size, try to use computedStyles (which can be
14
+ // wrong).
15
+ if (!fontSizeMatch && computedStyles) {
16
+ fontSizeMatch = fontSizeExp.exec(computedStyles.fontSize);
17
+ }
18
+ let fontSize = 12;
19
+ if (fontSizeMatch) {
20
+ supportedStyleAttrs.add('fontSize');
21
+ fontSize = parseFloat(fontSizeMatch[1]);
22
+ }
23
+ return fontSize;
24
+ };
25
+ export default determineFontSize;
@@ -55,7 +55,39 @@ export declare class Viewport {
55
55
  *
56
56
  * @see {@link getGridSize} and {@link getScaleFactorToNearestPowerOf}.
57
57
  */
58
- snapToGrid(canvasPos: Point2): Vec3;
58
+ snapToGrid(canvasPos: Point2): {
59
+ readonly x: number;
60
+ readonly y: number;
61
+ readonly z: number;
62
+ readonly xy: {
63
+ x: number;
64
+ y: number;
65
+ };
66
+ at(idx: number): number;
67
+ length(): number;
68
+ magnitude(): number;
69
+ magnitudeSquared(): number;
70
+ squareDistanceTo(p: Vec3): number;
71
+ distanceTo(p: Vec3): number;
72
+ maximumEntryMagnitude(): number;
73
+ angle(): number;
74
+ normalized(): Vec3;
75
+ normalizedOrZero(): Vec3;
76
+ times(c: number): Vec3;
77
+ plus(v: Vec3): Vec3;
78
+ minus(v: Vec3): Vec3;
79
+ dot(other: Vec3): number;
80
+ cross(other: Vec3): Vec3;
81
+ scale(other: number | Vec3): Vec3;
82
+ orthog(): Vec3;
83
+ extend(distance: number, direction: Vec3): Vec3;
84
+ lerp(target: Vec3, fractionTo: number): Vec3;
85
+ zip(other: Vec3, zip: (componentInThis: number, componentInOther: number) => number): Vec3;
86
+ map(fn: (component: number, index: number) => number): Vec3;
87
+ asArray(): [number, number, number];
88
+ eq(other: Vec3, fuzz?: number | undefined): boolean;
89
+ toString(): string;
90
+ };
59
91
  /** Returns the size of one screen pixel in canvas units. */
60
92
  getSizeOfPixelOnCanvas(): number;
61
93
  /**
@@ -83,7 +83,9 @@ class TextComponent extends AbstractComponent {
83
83
  }
84
84
  static applyTextStyles(ctx, style) {
85
85
  // Quote the font family if necessary.
86
- const fontFamily = style.fontFamily.match(/\s/) ? style.fontFamily.replace(/["]/g, '\\"') : style.fontFamily;
86
+ const hasSpaces = style.fontFamily.match(/\s/);
87
+ const isQuoted = style.fontFamily.match(/^".*"$/);
88
+ const fontFamily = hasSpaces && !isQuoted ? `"${style.fontFamily.replace(/["]/g, '\\"')}"` : style.fontFamily;
87
89
  ctx.font = [
88
90
  style.fontStyle ?? '',
89
91
  style.fontWeight ?? '',
@@ -166,7 +166,7 @@ export default class EditorImage {
166
166
  *
167
167
  * @internal
168
168
  */
169
- setDebugMode(newDebugMode: boolean): void;
169
+ static setDebugMode(newDebugMode: boolean): void;
170
170
  private static SetImportExportRectCommand;
171
171
  }
172
172
  /**
@@ -214,6 +214,7 @@ export declare class ImageNode {
214
214
  renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>;
215
215
  render(renderer: AbstractRenderer, visibleRect?: Rect2): void;
216
216
  renderDebugBoundingBoxes(renderer: AbstractRenderer, visibleRect: Rect2, depth?: number): void;
217
+ private checkRep;
217
218
  }
218
219
  /** An `ImageNode` that can properly handle fullscreen/data components. @internal */
219
220
  export declare class RootImageNode extends ImageNode {