js-draw 1.18.0 → 1.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. package/dist/Editor.css +35 -3
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/Editor.d.ts +20 -1
  5. package/dist/cjs/Editor.js +6 -0
  6. package/dist/cjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  7. package/dist/cjs/{SVGLoader.js → SVGLoader/index.js} +12 -29
  8. package/dist/cjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
  9. package/dist/cjs/SVGLoader/utils/determineFontSize.js +27 -0
  10. package/dist/cjs/Viewport.d.ts +33 -1
  11. package/dist/cjs/components/TextComponent.js +3 -1
  12. package/dist/cjs/rendering/caching/RenderingCacheNode.js +20 -15
  13. package/dist/cjs/testing/findNodeWithText.d.ts +3 -0
  14. package/dist/cjs/testing/findNodeWithText.js +16 -0
  15. package/dist/cjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  16. package/dist/cjs/testing/firstElementAncestorOfNode.js +13 -0
  17. package/dist/cjs/testing/sendKeyPressRelease.d.ts +3 -0
  18. package/dist/cjs/testing/sendKeyPressRelease.js +8 -0
  19. package/dist/cjs/testing/sendPenEvent.d.ts +2 -2
  20. package/dist/cjs/testing/sendPenEvent.js +26 -3
  21. package/dist/cjs/toolbar/localization.d.ts +2 -0
  22. package/dist/cjs/toolbar/localization.js +2 -0
  23. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  24. package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -0
  25. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +22 -0
  26. package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.js +58 -0
  27. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  28. package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.js +21 -0
  29. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  30. package/dist/cjs/toolbar/widgets/InsertImageWidget/index.js +281 -0
  31. package/dist/cjs/toolbar/widgets/TextToolWidget.js +5 -3
  32. package/dist/cjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  33. package/dist/cjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  34. package/dist/cjs/toolbar/widgets/components/makeFileInput.js +102 -45
  35. package/dist/cjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  36. package/dist/cjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  37. package/dist/cjs/toolbar/widgets/components/makeSnappedList.js +103 -0
  38. package/dist/cjs/tools/Eraser.d.ts +7 -2
  39. package/dist/cjs/tools/Eraser.js +54 -1
  40. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -2
  41. package/dist/cjs/tools/SelectionTool/Selection.js +20 -20
  42. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  43. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +6 -0
  44. package/dist/cjs/tools/SelectionTool/SelectionTool.js +1 -1
  45. package/dist/cjs/tools/SelectionTool/types.d.ts +19 -0
  46. package/dist/cjs/tools/TextTool.js +2 -1
  47. package/dist/cjs/tools/TextTool.test.d.ts +1 -0
  48. package/dist/cjs/tools/ToolController.d.ts +2 -0
  49. package/dist/cjs/tools/ToolController.js +10 -1
  50. package/dist/cjs/util/ReactiveValue.d.ts +2 -0
  51. package/dist/cjs/util/ReactiveValue.js +11 -0
  52. package/dist/cjs/util/bytesToSizeString.d.ts +8 -0
  53. package/dist/cjs/util/bytesToSizeString.js +26 -0
  54. package/dist/cjs/util/bytesToSizeString.test.d.ts +1 -0
  55. package/dist/cjs/util/stopPropagationOfScrollingWheelEvents.js +10 -6
  56. package/dist/cjs/util/waitForAll.d.ts +2 -0
  57. package/dist/cjs/util/waitForAll.js +2 -0
  58. package/dist/cjs/util/waitForImageLoaded.js +3 -0
  59. package/dist/cjs/util/waitForTimeout.d.ts +1 -0
  60. package/dist/cjs/util/waitForTimeout.js +1 -1
  61. package/dist/cjs/version.js +1 -1
  62. package/dist/mjs/Editor.d.ts +20 -1
  63. package/dist/mjs/Editor.mjs +6 -0
  64. package/dist/mjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
  65. package/dist/mjs/{SVGLoader.mjs → SVGLoader/index.mjs} +12 -29
  66. package/dist/mjs/SVGLoader/index.test.d.ts +1 -0
  67. package/dist/mjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
  68. package/dist/mjs/SVGLoader/utils/determineFontSize.mjs +25 -0
  69. package/dist/mjs/Viewport.d.ts +33 -1
  70. package/dist/mjs/components/TextComponent.mjs +3 -1
  71. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +20 -15
  72. package/dist/mjs/testing/findNodeWithText.d.ts +3 -0
  73. package/dist/mjs/testing/findNodeWithText.mjs +14 -0
  74. package/dist/mjs/testing/firstElementAncestorOfNode.d.ts +3 -0
  75. package/dist/mjs/testing/firstElementAncestorOfNode.mjs +11 -0
  76. package/dist/mjs/testing/sendKeyPressRelease.d.ts +3 -0
  77. package/dist/mjs/testing/sendKeyPressRelease.mjs +6 -0
  78. package/dist/mjs/testing/sendPenEvent.d.ts +2 -2
  79. package/dist/mjs/testing/sendPenEvent.mjs +3 -3
  80. package/dist/mjs/toolbar/localization.d.ts +2 -0
  81. package/dist/mjs/toolbar/localization.mjs +2 -0
  82. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +1 -0
  83. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -0
  84. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +22 -0
  85. package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.mjs +54 -0
  86. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
  87. package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.mjs +16 -0
  88. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
  89. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.mjs +276 -0
  90. package/dist/mjs/toolbar/widgets/InsertImageWidget/index.test.d.ts +1 -0
  91. package/dist/mjs/toolbar/widgets/TextToolWidget.mjs +5 -3
  92. package/dist/mjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
  93. package/dist/mjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
  94. package/dist/mjs/toolbar/widgets/components/makeFileInput.mjs +102 -45
  95. package/dist/mjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
  96. package/dist/mjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
  97. package/dist/mjs/toolbar/widgets/components/makeSnappedList.mjs +98 -0
  98. package/dist/mjs/tools/Eraser.d.ts +7 -2
  99. package/dist/mjs/tools/Eraser.mjs +54 -1
  100. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -2
  101. package/dist/mjs/tools/SelectionTool/Selection.mjs +20 -20
  102. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
  103. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +6 -0
  104. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +1 -1
  105. package/dist/mjs/tools/SelectionTool/types.d.ts +19 -0
  106. package/dist/mjs/tools/TextTool.mjs +2 -1
  107. package/dist/mjs/tools/TextTool.test.d.ts +1 -0
  108. package/dist/mjs/tools/ToolController.d.ts +2 -0
  109. package/dist/mjs/tools/ToolController.mjs +10 -1
  110. package/dist/mjs/util/ReactiveValue.d.ts +2 -0
  111. package/dist/mjs/util/ReactiveValue.mjs +11 -0
  112. package/dist/mjs/util/bytesToSizeString.d.ts +8 -0
  113. package/dist/mjs/util/bytesToSizeString.mjs +24 -0
  114. package/dist/mjs/util/bytesToSizeString.test.d.ts +1 -0
  115. package/dist/mjs/util/stopPropagationOfScrollingWheelEvents.mjs +10 -6
  116. package/dist/mjs/util/waitForAll.d.ts +2 -0
  117. package/dist/mjs/util/waitForAll.mjs +2 -0
  118. package/dist/mjs/util/waitForImageLoaded.mjs +3 -0
  119. package/dist/mjs/util/waitForTimeout.d.ts +1 -0
  120. package/dist/mjs/util/waitForTimeout.mjs +1 -1
  121. package/dist/mjs/version.mjs +1 -1
  122. package/package.json +4 -4
  123. package/src/toolbar/toolbar.scss +1 -7
  124. package/src/toolbar/widgets/{InsertImageWidget.scss → InsertImageWidget/index.scss} +3 -2
  125. package/src/toolbar/widgets/components/components.scss +2 -1
  126. package/src/toolbar/widgets/components/makeFileInput.scss +14 -1
  127. package/src/toolbar/widgets/components/makeSnappedList.scss +28 -0
  128. package/src/toolbar/widgets/widgets.scss +7 -0
  129. package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  130. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +0 -269
  131. package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
  132. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +0 -264
  133. /package/dist/cjs/{SVGLoader.test.d.ts → SVGLoader/index.test.d.ts} +0 -0
  134. /package/dist/{mjs/SVGLoader.test.d.ts → cjs/toolbar/widgets/InsertImageWidget/index.test.d.ts} +0 -0
@@ -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();
@@ -33,6 +33,8 @@ export declare abstract class ReactiveValue<T> {
33
33
  * @see {@link onUpdate}.
34
34
  */
35
35
  abstract onUpdateAndNow(callback: UpdateCallback<T>): ListenerResult;
36
+ /** Returns a promise that resolves when this value is next changed. */
37
+ waitForNextUpdate(): Promise<T>;
36
38
  /** Creates a `ReactiveValue` with an initial value, `initialValue`. */
37
39
  static fromInitialValue<T>(initialValue: T): MutableReactiveValue<T>;
38
40
  /** Returns a `ReactiveValue` that is **known** will never change. */
@@ -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
  /**
@@ -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.19.1',
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
@@ -296,31 +297,13 @@ export default class SVGLoader {
296
297
  }
297
298
  // Compute styles.
298
299
  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 = [
300
+ const supportedStyleAttrs = new Set([
313
301
  'fontFamily',
314
302
  'transform',
315
303
  ...supportedStrokeFillStyleAttrs,
316
- ];
317
- let fontSize = 12;
318
- if (fontSizeMatch) {
319
- supportedStyleAttrs.push('fontSize');
320
- fontSize = parseFloat(fontSizeMatch[1]);
321
- }
304
+ ]);
322
305
  const style = {
323
- size: fontSize,
306
+ size: determineFontSize(elem, computedStyles, supportedStyleAttrs),
324
307
  fontFamily: computedStyles?.fontFamily || elem.style?.fontFamily || 'sans-serif',
325
308
  fontWeight: computedStyles?.fontWeight || elem.style?.fontWeight || undefined,
326
309
  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 ?? '',
@@ -139,24 +139,29 @@ export default class RenderingCacheNode {
139
139
  || items.length === 0) {
140
140
  return;
141
141
  }
142
- const newItems = [];
143
- // Divide [items] until nodes are leaves or smaller than this
144
- for (const item of items) {
145
- const bbox = item.getBBox();
146
- if (!bbox.intersects(this.region)) {
147
- continue;
148
- }
149
- if (bbox.maxDimension >= this.region.maxDimension) {
150
- newItems.push(...item.getChildrenOrSelfIntersectingRegion(this.region));
151
- }
152
- else {
153
- newItems.push(item);
142
+ // Divide [items] until nodes are smaller than this, or are leaves.
143
+ const divideUntilSmallerThanThis = (itemsToDivide) => {
144
+ const newItems = [];
145
+ for (const item of itemsToDivide) {
146
+ const bbox = item.getBBox();
147
+ if (!bbox.intersects(this.region)) {
148
+ continue;
149
+ }
150
+ if (bbox.maxDimension >= this.region.maxDimension) {
151
+ newItems.push(...item.getChildrenOrSelfIntersectingRegion(this.region));
152
+ }
153
+ else {
154
+ newItems.push(item);
155
+ }
154
156
  }
155
- }
156
- items = newItems;
157
+ return newItems;
158
+ };
159
+ items = divideUntilSmallerThanThis(items);
157
160
  // Can we cache at all?
158
161
  if (!this.cacheState.props.isOfCorrectType(screenRenderer)) {
159
- items.forEach(item => item.render(screenRenderer, viewport.visibleRect));
162
+ for (const item of items) {
163
+ item.render(screenRenderer, viewport.visibleRect);
164
+ }
160
165
  return;
161
166
  }
162
167
  if (this.cacheState.debugMode) {
@@ -0,0 +1,3 @@
1
+ /** Returns the first node or element with `textContent` matching `expectedText`. */
2
+ declare const findNodeWithText: (expectedText: string, parent: Node) => Node | null;
3
+ export default findNodeWithText;
@@ -0,0 +1,14 @@
1
+ /** Returns the first node or element with `textContent` matching `expectedText`. */
2
+ const findNodeWithText = (expectedText, parent) => {
3
+ if (parent.textContent === expectedText) {
4
+ return parent;
5
+ }
6
+ for (const child of parent.childNodes) {
7
+ const results = findNodeWithText(expectedText, child);
8
+ if (results) {
9
+ return results;
10
+ }
11
+ }
12
+ return null;
13
+ };
14
+ export default findNodeWithText;
@@ -0,0 +1,3 @@
1
+ /** Returns the first ancestor of the given node that is an HTMLElement */
2
+ declare const firstElementAncestorOfNode: (node: Node | null) => HTMLElement | null;
3
+ export default firstElementAncestorOfNode;
@@ -0,0 +1,11 @@
1
+ /** Returns the first ancestor of the given node that is an HTMLElement */
2
+ const firstElementAncestorOfNode = (node) => {
3
+ if (node instanceof HTMLElement) {
4
+ return node;
5
+ }
6
+ else if (node?.parentNode) {
7
+ return firstElementAncestorOfNode(node.parentNode);
8
+ }
9
+ return null;
10
+ };
11
+ export default firstElementAncestorOfNode;
@@ -0,0 +1,3 @@
1
+ import type Editor from '../Editor';
2
+ declare const sendKeyPressRelease: (editor: Editor, key: string) => void;
3
+ export default sendKeyPressRelease;
@@ -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 ?? [
@@ -17,6 +17,7 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
17
17
  dragAndDropHereOrBrowse: string;
18
18
  cancel: string;
19
19
  submit: string;
20
+ addAll: string;
20
21
  roundedTipPen: string;
21
22
  roundedTipPen2: string;
22
23
  flatTipPen: string;
@@ -56,6 +57,7 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
56
57
  strokeAutocorrect: string;
57
58
  errorImageHasZeroSize: string;
58
59
  describeTheImage: string;
60
+ fileInput__loading: string;
59
61
  penDropdown__baseHelpText: string;
60
62
  penDropdown__colorHelpText: string;
61
63
  penDropdown__thicknessHelpText: 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',
@@ -57,6 +58,7 @@ export const defaultToolbarLocalization = {
57
58
  paste: 'Paste',
58
59
  errorImageHasZeroSize: 'Error: Image has zero size',
59
60
  describeTheImage: 'Image description',
61
+ fileInput__loading: 'Loading...',
60
62
  // Help text
61
63
  penDropdown__baseHelpText: 'This tool draws shapes or freehand lines.',
62
64
  penDropdown__colorHelpText: 'Changes the pen\'s color',
@@ -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
  }
@@ -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
+ }