js-draw 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +30 -30
  2. package/dist/Editor.css +70 -4
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +73 -40
  6. package/dist/cjs/Editor.js +90 -24
  7. package/dist/cjs/EditorImage.d.ts +58 -6
  8. package/dist/cjs/EditorImage.js +336 -60
  9. package/dist/cjs/SVGLoader.d.ts +10 -4
  10. package/dist/cjs/SVGLoader.js +30 -10
  11. package/dist/cjs/UndoRedoHistory.d.ts +2 -2
  12. package/dist/cjs/UndoRedoHistory.js +4 -2
  13. package/dist/cjs/Viewport.d.ts +2 -1
  14. package/dist/cjs/Viewport.js +12 -3
  15. package/dist/cjs/commands/Command.d.ts +1 -0
  16. package/dist/cjs/commands/Command.js +1 -0
  17. package/dist/cjs/commands/Erase.js +1 -1
  18. package/dist/cjs/commands/SerializableCommand.d.ts +1 -1
  19. package/dist/cjs/commands/SerializableCommand.js +16 -2
  20. package/dist/cjs/commands/localization.d.ts +2 -0
  21. package/dist/cjs/commands/localization.js +2 -0
  22. package/dist/cjs/components/AbstractComponent.d.ts +38 -0
  23. package/dist/cjs/components/AbstractComponent.js +31 -0
  24. package/dist/cjs/components/BackgroundComponent.d.ts +10 -1
  25. package/dist/cjs/components/BackgroundComponent.js +60 -6
  26. package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +2 -1
  27. package/dist/cjs/components/SVGGlobalAttributesObject.js +30 -1
  28. package/dist/cjs/components/Stroke.d.ts +1 -0
  29. package/dist/cjs/components/Stroke.js +44 -0
  30. package/dist/cjs/components/UnknownSVGObject.d.ts +2 -1
  31. package/dist/cjs/components/UnknownSVGObject.js +30 -1
  32. package/dist/cjs/components/lib.d.ts +2 -2
  33. package/dist/cjs/components/lib.js +15 -2
  34. package/dist/cjs/lib.d.ts +2 -45
  35. package/dist/cjs/lib.js +2 -45
  36. package/dist/cjs/rendering/RenderablePathSpec.d.ts +1 -0
  37. package/dist/cjs/rendering/RenderablePathSpec.js +1 -0
  38. package/dist/cjs/rendering/RenderingStyle.d.ts +1 -0
  39. package/dist/cjs/rendering/lib.d.ts +1 -0
  40. package/dist/cjs/rendering/lib.js +5 -1
  41. package/dist/cjs/rendering/renderers/AbstractRenderer.js +1 -1
  42. package/dist/cjs/shortcuts/KeyboardShortcutManager.d.ts +2 -2
  43. package/dist/cjs/shortcuts/KeyboardShortcutManager.js +2 -2
  44. package/dist/cjs/toolbar/localization.d.ts +1 -0
  45. package/dist/cjs/toolbar/localization.js +1 -0
  46. package/dist/cjs/toolbar/widgets/BaseWidget.js +5 -0
  47. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +54 -25
  48. package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +8 -0
  49. package/dist/cjs/tools/PanZoom.js +13 -8
  50. package/dist/cjs/tools/ScrollbarTool.d.ts +18 -0
  51. package/dist/cjs/tools/ScrollbarTool.js +85 -0
  52. package/dist/cjs/tools/SelectionTool/SelectionTool.selecting.test.d.ts +1 -0
  53. package/dist/cjs/tools/ToolController.js +2 -0
  54. package/dist/cjs/types.d.ts +3 -1
  55. package/dist/cjs/util/adjustEditorThemeForContrast.js +1 -0
  56. package/dist/cjs/util/assertions.d.ts +4 -0
  57. package/dist/cjs/util/assertions.js +12 -1
  58. package/dist/cjs/version.js +1 -1
  59. package/dist/mjs/Editor.d.ts +73 -40
  60. package/dist/mjs/Editor.mjs +90 -24
  61. package/dist/mjs/EditorImage.d.ts +58 -6
  62. package/dist/mjs/EditorImage.mjs +313 -61
  63. package/dist/mjs/SVGLoader.d.ts +10 -4
  64. package/dist/mjs/SVGLoader.mjs +29 -9
  65. package/dist/mjs/UndoRedoHistory.d.ts +2 -2
  66. package/dist/mjs/UndoRedoHistory.mjs +4 -2
  67. package/dist/mjs/Viewport.d.ts +2 -1
  68. package/dist/mjs/Viewport.mjs +12 -3
  69. package/dist/mjs/commands/Command.d.ts +1 -0
  70. package/dist/mjs/commands/Command.mjs +1 -0
  71. package/dist/mjs/commands/Erase.mjs +1 -1
  72. package/dist/mjs/commands/SerializableCommand.d.ts +1 -1
  73. package/dist/mjs/commands/SerializableCommand.mjs +16 -2
  74. package/dist/mjs/commands/localization.d.ts +2 -0
  75. package/dist/mjs/commands/localization.mjs +2 -0
  76. package/dist/mjs/components/AbstractComponent.d.ts +38 -0
  77. package/dist/mjs/components/AbstractComponent.mjs +30 -0
  78. package/dist/mjs/components/BackgroundComponent.d.ts +10 -1
  79. package/dist/mjs/components/BackgroundComponent.mjs +37 -6
  80. package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +2 -1
  81. package/dist/mjs/components/SVGGlobalAttributesObject.mjs +7 -1
  82. package/dist/mjs/components/Stroke.d.ts +1 -0
  83. package/dist/mjs/components/Stroke.mjs +44 -0
  84. package/dist/mjs/components/UnknownSVGObject.d.ts +2 -1
  85. package/dist/mjs/components/UnknownSVGObject.mjs +7 -1
  86. package/dist/mjs/components/lib.d.ts +2 -2
  87. package/dist/mjs/components/lib.mjs +2 -2
  88. package/dist/mjs/lib.d.ts +2 -45
  89. package/dist/mjs/lib.mjs +2 -45
  90. package/dist/mjs/rendering/RenderablePathSpec.d.ts +1 -0
  91. package/dist/mjs/rendering/RenderablePathSpec.mjs +1 -0
  92. package/dist/mjs/rendering/RenderingStyle.d.ts +1 -0
  93. package/dist/mjs/rendering/lib.d.ts +1 -0
  94. package/dist/mjs/rendering/lib.mjs +1 -0
  95. package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +1 -1
  96. package/dist/mjs/shortcuts/KeyboardShortcutManager.d.ts +2 -2
  97. package/dist/mjs/shortcuts/KeyboardShortcutManager.mjs +2 -2
  98. package/dist/mjs/toolbar/localization.d.ts +1 -0
  99. package/dist/mjs/toolbar/localization.mjs +1 -0
  100. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +5 -0
  101. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +54 -25
  102. package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +8 -0
  103. package/dist/mjs/tools/PanZoom.mjs +13 -8
  104. package/dist/mjs/tools/ScrollbarTool.d.ts +18 -0
  105. package/dist/mjs/tools/ScrollbarTool.mjs +79 -0
  106. package/dist/mjs/tools/SelectionTool/SelectionTool.selecting.test.d.ts +1 -0
  107. package/dist/mjs/tools/ToolController.mjs +2 -0
  108. package/dist/mjs/types.d.ts +3 -1
  109. package/dist/mjs/util/adjustEditorThemeForContrast.mjs +1 -0
  110. package/dist/mjs/util/assertions.d.ts +4 -0
  111. package/dist/mjs/util/assertions.mjs +10 -0
  112. package/dist/mjs/version.mjs +1 -1
  113. package/package.json +3 -4
  114. package/src/Editor.scss +8 -0
  115. package/src/dialogs/dialogs.scss +2 -1
  116. package/src/toolbar/AbstractToolbar.scss +3 -0
  117. package/src/toolbar/EdgeToolbar.scss +4 -1
  118. package/src/toolbar/widgets/DocumentPropertiesWidget.scss +12 -0
  119. package/src/toolbar/widgets/components/makeGridSelector.scss +6 -1
  120. package/src/tools/ScrollbarTool.scss +57 -0
  121. package/src/tools/{SoundUITool.css → SoundUITool.scss} +4 -0
  122. package/src/tools/tools.scss +2 -1
@@ -127,6 +127,50 @@ class Stroke extends AbstractComponent_1.default {
127
127
  }
128
128
  return false;
129
129
  }
130
+ intersectsRect(rect) {
131
+ // AbstractComponent::intersectsRect can be inexact for strokes with non-zero
132
+ // stroke radius (has many false negatives). As such, additional checks are
133
+ // done here, before passing to the superclass.
134
+ if (!rect.intersects(this.getBBox())) {
135
+ return false;
136
+ }
137
+ // The following check only checks for the positive case:
138
+ // Sample a set of points that are known to be within each part of this
139
+ // stroke. For example, the points marked with an "x" below:
140
+ // ___________________
141
+ // / \
142
+ // | x x |
143
+ // \_____________ |
144
+ // | x |
145
+ // \_____/
146
+ //
147
+ // Because we don't want the following case to result in selection,
148
+ // __________________
149
+ // /.___. \
150
+ // || x | x | <- /* The
151
+ // |·---· | .___.
152
+ // \____________ | | |
153
+ // | x | ·---·
154
+ // \_____/ denotes the input rectangle */
155
+ //
156
+ // we need to ensure that the rectangle intersects each point **and** the
157
+ // edge of the rectangle.
158
+ for (const part of this.parts) {
159
+ // As such, we need to shrink the input rectangle to verify that the original,
160
+ // unshrunken rectangle would have intersected the edge of the stroke if it
161
+ // intersects a point within the stroke.
162
+ const interiorRect = rect.grownBy(-(part.style.stroke?.width ?? 0));
163
+ if (interiorRect.area === 0) {
164
+ continue;
165
+ }
166
+ for (const point of part.path.startEndPoints()) {
167
+ if (interiorRect.containsPoint(point)) {
168
+ return true;
169
+ }
170
+ }
171
+ }
172
+ return super.intersectsRect(rect);
173
+ }
130
174
  render(canvas, visibleRect) {
131
175
  canvas.startObject(this.getBBox());
132
176
  for (const part of this.parts) {
@@ -1,6 +1,6 @@
1
1
  import { LineSegment2, Mat33, Rect2 } from '@js-draw/math';
2
2
  import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
3
- import AbstractComponent from './AbstractComponent';
3
+ import AbstractComponent, { ComponentSizingMode } from './AbstractComponent';
4
4
  import { ImageComponentLocalization } from './localization';
5
5
  export default class UnknownSVGObject extends AbstractComponent {
6
6
  private svgObject;
@@ -10,6 +10,7 @@ export default class UnknownSVGObject extends AbstractComponent {
10
10
  intersects(lineSegment: LineSegment2): boolean;
11
11
  protected applyTransformation(_affineTransfm: Mat33): void;
12
12
  isSelectable(): boolean;
13
+ getSizingMode(): ComponentSizingMode;
13
14
  protected createClone(): AbstractComponent;
14
15
  description(localization: ImageComponentLocalization): string;
15
16
  protected serializeToJSON(): string | null;
@@ -4,13 +4,36 @@
4
4
  // @internal
5
5
  // @packageDocumentation
6
6
  //
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || function (mod) {
24
+ if (mod && mod.__esModule) return mod;
25
+ var result = {};
26
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
+ __setModuleDefault(result, mod);
28
+ return result;
29
+ };
7
30
  var __importDefault = (this && this.__importDefault) || function (mod) {
8
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
9
32
  };
10
33
  Object.defineProperty(exports, "__esModule", { value: true });
11
34
  const math_1 = require("@js-draw/math");
12
35
  const SVGRenderer_1 = __importDefault(require("../rendering/renderers/SVGRenderer"));
13
- const AbstractComponent_1 = __importDefault(require("./AbstractComponent"));
36
+ const AbstractComponent_1 = __importStar(require("./AbstractComponent"));
14
37
  const componentId = 'unknown-svg-object';
15
38
  class UnknownSVGObject extends AbstractComponent_1.default {
16
39
  constructor(svgObject) {
@@ -35,6 +58,12 @@ class UnknownSVGObject extends AbstractComponent_1.default {
35
58
  isSelectable() {
36
59
  return false;
37
60
  }
61
+ getSizingMode() {
62
+ // This component can be shown anywhere (it won't be
63
+ // visible to the user, it just needs to be saved with
64
+ // the image).
65
+ return AbstractComponent_1.ComponentSizingMode.Anywhere;
66
+ }
38
67
  createClone() {
39
68
  return new UnknownSVGObject(this.svgObject.cloneNode(true));
40
69
  }
@@ -10,7 +10,7 @@ import TextComponent from './TextComponent';
10
10
  import ImageComponent from './ImageComponent';
11
11
  import RestyleableComponent from './RestylableComponent';
12
12
  import { createRestyleComponentCommand, isRestylableComponent, ComponentStyle as RestyleableComponentStyle } from './RestylableComponent';
13
- import BackgroundComponent from './BackgroundComponent';
13
+ import BackgroundComponent, { BackgroundType } from './BackgroundComponent';
14
14
  export { Stroke, RestyleableComponent, createRestyleComponentCommand, isRestylableComponent, RestyleableComponentStyle, TextComponent,
15
15
  /** @deprecated use {@link TextComponent} */
16
- TextComponent as Text, Stroke as StrokeComponent, BackgroundComponent, ImageComponent, };
16
+ TextComponent as Text, Stroke as StrokeComponent, BackgroundComponent, BackgroundType as BackgroundComponentBackgroundType, ImageComponent, };
@@ -10,14 +10,26 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
13
18
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
19
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
20
  };
21
+ var __importStar = (this && this.__importStar) || function (mod) {
22
+ if (mod && mod.__esModule) return mod;
23
+ var result = {};
24
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
25
+ __setModuleDefault(result, mod);
26
+ return result;
27
+ };
16
28
  var __importDefault = (this && this.__importDefault) || function (mod) {
17
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
30
  };
19
31
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.ImageComponent = exports.BackgroundComponent = exports.StrokeComponent = exports.Text = exports.TextComponent = exports.isRestylableComponent = exports.createRestyleComponentCommand = exports.Stroke = exports.AbstractComponent = exports.StrokeSmoother = exports.makeOutlinedCircleBuilder = exports.makePressureSensitiveFreehandLineBuilder = exports.makeFreehandLineBuilder = void 0;
32
+ exports.ImageComponent = exports.BackgroundComponentBackgroundType = exports.BackgroundComponent = exports.StrokeComponent = exports.Text = exports.TextComponent = exports.isRestylableComponent = exports.createRestyleComponentCommand = exports.Stroke = exports.AbstractComponent = exports.StrokeSmoother = exports.makeOutlinedCircleBuilder = exports.makePressureSensitiveFreehandLineBuilder = exports.makeFreehandLineBuilder = void 0;
21
33
  __exportStar(require("./builders/types"), exports);
22
34
  var FreehandLineBuilder_1 = require("./builders/FreehandLineBuilder");
23
35
  Object.defineProperty(exports, "makeFreehandLineBuilder", { enumerable: true, get: function () { return FreehandLineBuilder_1.makeFreehandLineBuilder; } });
@@ -41,5 +53,6 @@ exports.ImageComponent = ImageComponent_1.default;
41
53
  const RestylableComponent_1 = require("./RestylableComponent");
42
54
  Object.defineProperty(exports, "createRestyleComponentCommand", { enumerable: true, get: function () { return RestylableComponent_1.createRestyleComponentCommand; } });
43
55
  Object.defineProperty(exports, "isRestylableComponent", { enumerable: true, get: function () { return RestylableComponent_1.isRestylableComponent; } });
44
- const BackgroundComponent_1 = __importDefault(require("./BackgroundComponent"));
56
+ const BackgroundComponent_1 = __importStar(require("./BackgroundComponent"));
45
57
  exports.BackgroundComponent = BackgroundComponent_1.default;
58
+ Object.defineProperty(exports, "BackgroundComponentBackgroundType", { enumerable: true, get: function () { return BackgroundComponent_1.BackgroundType; } });
package/dist/cjs/lib.d.ts CHANGED
@@ -2,52 +2,9 @@
2
2
  * The main entrypoint for the NPM package. Everything exported by this file
3
3
  * is available through the [`js-draw` package](https://www.npmjs.com/package/js-draw).
4
4
  *
5
- * @example
6
- * ```ts,runnable
7
- * import { Editor, Vec3, Mat33, ToolbarWidgetTag } from 'js-draw';
5
+ * ## Example
8
6
  *
9
- * // Use the Material Icon pack.
10
- * import { MaterialIconProvider } from '@js-draw/material-icons';
11
- *
12
- * // Apply js-draw CSS
13
- * import 'js-draw/styles';
14
- * // If your bundler doesn't support the above, try
15
- * // import 'js-draw/bundledStyles';
16
- *
17
- * (async () => {
18
- * const editor = new Editor(document.body, {
19
- * iconProvider: new MaterialIconProvider(),
20
- * });
21
- * const toolbar = editor.addToolbar();
22
- *
23
- * // Increases the minimum height of the editor
24
- * editor.getRootElement().style.height = '600px';
25
- *
26
- * // Loads from SVG data
27
- * await editor.loadFromSVG(`
28
- * <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
29
- * <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
30
- * <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background"></path>
31
- * <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;">Testing...</text>
32
- * </svg>
33
- * `);
34
- *
35
- * // Adding tags to a toolbar button allows different styles to be applied.
36
- * // Also see addActionButton.
37
- * const buttonLabels = [ ToolbarWidgetTag.Save ];
38
- *
39
- * toolbar.addSaveButton(() => {
40
- * const saveData = editor.toSVG().outerHTML;
41
- *
42
- * // Do something with saveData
43
- * });
44
- *
45
- * toolbar.addExitButton(() => {
46
- * // Save/confirm exiting here?
47
- * editor.remove();
48
- * });
49
- * })();
50
- * ```
7
+ * [[include:doc-pages/inline-examples/main-js-draw-example.md]]
51
8
  *
52
9
  * @see
53
10
  * - {@link Editor}
package/dist/cjs/lib.js CHANGED
@@ -3,52 +3,9 @@
3
3
  * The main entrypoint for the NPM package. Everything exported by this file
4
4
  * is available through the [`js-draw` package](https://www.npmjs.com/package/js-draw).
5
5
  *
6
- * @example
7
- * ```ts,runnable
8
- * import { Editor, Vec3, Mat33, ToolbarWidgetTag } from 'js-draw';
6
+ * ## Example
9
7
  *
10
- * // Use the Material Icon pack.
11
- * import { MaterialIconProvider } from '@js-draw/material-icons';
12
- *
13
- * // Apply js-draw CSS
14
- * import 'js-draw/styles';
15
- * // If your bundler doesn't support the above, try
16
- * // import 'js-draw/bundledStyles';
17
- *
18
- * (async () => {
19
- * const editor = new Editor(document.body, {
20
- * iconProvider: new MaterialIconProvider(),
21
- * });
22
- * const toolbar = editor.addToolbar();
23
- *
24
- * // Increases the minimum height of the editor
25
- * editor.getRootElement().style.height = '600px';
26
- *
27
- * // Loads from SVG data
28
- * await editor.loadFromSVG(`
29
- * <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
30
- * <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
31
- * <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background"></path>
32
- * <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;">Testing...</text>
33
- * </svg>
34
- * `);
35
- *
36
- * // Adding tags to a toolbar button allows different styles to be applied.
37
- * // Also see addActionButton.
38
- * const buttonLabels = [ ToolbarWidgetTag.Save ];
39
- *
40
- * toolbar.addSaveButton(() => {
41
- * const saveData = editor.toSVG().outerHTML;
42
- *
43
- * // Do something with saveData
44
- * });
45
- *
46
- * toolbar.addExitButton(() => {
47
- * // Save/confirm exiting here?
48
- * editor.remove();
49
- * });
50
- * })();
51
- * ```
8
+ * [[include:doc-pages/inline-examples/main-js-draw-example.md]]
52
9
  *
53
10
  * @see
54
11
  * - {@link Editor}
@@ -6,6 +6,7 @@ interface RenderablePathSpec {
6
6
  style: RenderingStyle;
7
7
  path?: Path;
8
8
  }
9
+ /** Converts a renderable path (a path with a `startPoint`, `commands`, and `style`). */
9
10
  export declare const pathFromRenderable: (renderable: RenderablePathSpec) => Path;
10
11
  export declare const pathToRenderable: (path: Path, style: RenderingStyle) => RenderablePathSpec;
11
12
  /**
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.visualEquivalent = exports.pathToRenderable = exports.pathFromRenderable = void 0;
4
4
  const math_1 = require("@js-draw/math");
5
+ /** Converts a renderable path (a path with a `startPoint`, `commands`, and `style`). */
5
6
  const pathFromRenderable = (renderable) => {
6
7
  if (renderable.path) {
7
8
  return renderable.path;
@@ -3,6 +3,7 @@ interface RenderingStyle {
3
3
  readonly fill: Color4;
4
4
  readonly stroke?: {
5
5
  readonly color: Color4;
6
+ /** Note: The stroke `width` is twice the stroke radius. */
6
7
  readonly width: number;
7
8
  };
8
9
  }
@@ -5,3 +5,4 @@ export { default as CanvasRenderer } from './renderers/CanvasRenderer';
5
5
  export { default as Display, RenderingMode } from './Display';
6
6
  export { default as TextRenderingStyle } from './TextRenderingStyle';
7
7
  export { default as RenderingStyle } from './RenderingStyle';
8
+ export { pathToRenderable, pathFromRenderable, visualEquivalent as pathVisualEquivalent, default as RenderablePathSpec, } from './RenderablePathSpec';
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RenderingMode = exports.Display = exports.CanvasRenderer = exports.SVGRenderer = exports.DummyRenderer = exports.AbstractRenderer = void 0;
6
+ exports.pathVisualEquivalent = exports.pathFromRenderable = exports.pathToRenderable = exports.RenderingMode = exports.Display = exports.CanvasRenderer = exports.SVGRenderer = exports.DummyRenderer = exports.AbstractRenderer = void 0;
7
7
  var AbstractRenderer_1 = require("./renderers/AbstractRenderer");
8
8
  Object.defineProperty(exports, "AbstractRenderer", { enumerable: true, get: function () { return __importDefault(AbstractRenderer_1).default; } });
9
9
  var DummyRenderer_1 = require("./renderers/DummyRenderer");
@@ -15,3 +15,7 @@ Object.defineProperty(exports, "CanvasRenderer", { enumerable: true, get: functi
15
15
  var Display_1 = require("./Display");
16
16
  Object.defineProperty(exports, "Display", { enumerable: true, get: function () { return __importDefault(Display_1).default; } });
17
17
  Object.defineProperty(exports, "RenderingMode", { enumerable: true, get: function () { return Display_1.RenderingMode; } });
18
+ var RenderablePathSpec_1 = require("./RenderablePathSpec");
19
+ Object.defineProperty(exports, "pathToRenderable", { enumerable: true, get: function () { return RenderablePathSpec_1.pathToRenderable; } });
20
+ Object.defineProperty(exports, "pathFromRenderable", { enumerable: true, get: function () { return RenderablePathSpec_1.pathFromRenderable; } });
21
+ Object.defineProperty(exports, "pathVisualEquivalent", { enumerable: true, get: function () { return RenderablePathSpec_1.visualEquivalent; } });
@@ -65,7 +65,7 @@ class AbstractRenderer {
65
65
  drawPath(path) {
66
66
  // If we're being called outside of an object,
67
67
  // we can't delay rendering
68
- if (this.objectLevel === 0) {
68
+ if (this.objectLevel === 0 || this.currentPaths === null) {
69
69
  this.currentPaths = [path];
70
70
  this.flushPath();
71
71
  this.currentPaths = null;
@@ -33,8 +33,8 @@ export default class KeyboardShortcutManager {
33
33
  * const shortcutId = 'io.github.personalizedrefrigerator.js-draw.select-all';
34
34
  *
35
35
  * // Associate two shortcuts with the same ID
36
- * const shortcut1 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+a');
37
- * const shortcut2 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+shift+a');
36
+ * const shortcut1 = KeyBinding.fromString('ctrlOrMeta+a');
37
+ * const shortcut2 = KeyBinding.fromString('ctrlOrMeta+shift+a');
38
38
  * KeyboardShortcutManager.registerDefaultKeyboardShortcut(
39
39
  * shortcutId,
40
40
  * [ shortcut1, shortcut2 ],
@@ -59,8 +59,8 @@ class KeyboardShortcutManager {
59
59
  * const shortcutId = 'io.github.personalizedrefrigerator.js-draw.select-all';
60
60
  *
61
61
  * // Associate two shortcuts with the same ID
62
- * const shortcut1 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+a');
63
- * const shortcut2 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+shift+a');
62
+ * const shortcut1 = KeyBinding.fromString('ctrlOrMeta+a');
63
+ * const shortcut2 = KeyBinding.fromString('ctrlOrMeta+shift+a');
64
64
  * KeyboardShortcutManager.registerDefaultKeyboardShortcut(
65
65
  * shortcutId,
66
66
  * [ shortcut1, shortcut2 ],
@@ -44,6 +44,7 @@ export interface ToolbarLocalization {
44
44
  imageWidthOption: string;
45
45
  imageHeightOption: string;
46
46
  useGridOption: string;
47
+ enableAutoresizeOption: string;
47
48
  toggleOverflow: string;
48
49
  about: string;
49
50
  inputStabilization: string;
@@ -37,6 +37,7 @@ exports.defaultToolbarLocalization = {
37
37
  imageWidthOption: 'Width',
38
38
  imageHeightOption: 'Height',
39
39
  useGridOption: 'Grid',
40
+ enableAutoresizeOption: 'Auto-resize',
40
41
  toggleOverflow: 'More',
41
42
  about: 'About',
42
43
  inputStabilization: 'Input stabilization',
@@ -56,6 +56,11 @@ class BaseWidget {
56
56
  this.label = document.createElement('label');
57
57
  this.button.setAttribute('role', 'button');
58
58
  this.button.tabIndex = 0;
59
+ // Disable the context menu. This allows long-press gestures to trigger the button's
60
+ // tooltip instead.
61
+ this.button.oncontextmenu = event => {
62
+ event.preventDefault();
63
+ };
59
64
  const toolbarShortcutHandlers = this.editor.toolController.getMatchingTools(ToolbarShortcutHandler_1.default);
60
65
  // If the onKeyPress function has been extended and the editor is configured to send keypress events to
61
66
  // toolbar widgets,
@@ -122,39 +122,49 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
122
122
  const container = document.createElement('div');
123
123
  container.classList.add(`${constants_1.toolbarCSSPrefix}spacedList`, `${constants_1.toolbarCSSPrefix}nonbutton-controls-main-list`, `${constants_1.toolbarCSSPrefix}document-properties-widget`);
124
124
  // Background color input
125
- const backgroundColorRow = document.createElement('div');
126
- const backgroundColorLabel = document.createElement('label');
127
- backgroundColorLabel.innerText = this.localizationTable.backgroundColor;
128
- const { input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue } = (0, makeColorInput_1.default)(this.editor, color => {
129
- if (!color.eq(this.getBackgroundColor())) {
130
- this.setBackgroundColor(color);
131
- }
132
- });
133
- colorInput.id = `${constants_1.toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
134
- backgroundColorLabel.htmlFor = colorInput.id;
135
- backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
125
+ const makeBackgroundColorInput = () => {
126
+ const backgroundColorRow = document.createElement('div');
127
+ const backgroundColorLabel = document.createElement('label');
128
+ backgroundColorLabel.innerText = this.localizationTable.backgroundColor;
129
+ const { input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue } = (0, makeColorInput_1.default)(this.editor, color => {
130
+ if (!color.eq(this.getBackgroundColor())) {
131
+ this.setBackgroundColor(color);
132
+ }
133
+ });
134
+ colorInput.id = `${constants_1.toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
135
+ backgroundColorLabel.htmlFor = colorInput.id;
136
+ backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
137
+ return { setBgColorInputValue, backgroundColorRow };
138
+ };
139
+ const { backgroundColorRow, setBgColorInputValue } = makeBackgroundColorInput();
140
+ const makeCheckboxRow = (labelText, onChange) => {
141
+ const rowContainer = document.createElement('div');
142
+ const labelElement = document.createElement('label');
143
+ const checkboxElement = document.createElement('input');
144
+ checkboxElement.id = `${constants_1.toolbarCSSPrefix}docPropertiesCheckbox-${DocumentPropertiesWidget.idCounter++}`;
145
+ labelElement.htmlFor = checkboxElement.id;
146
+ checkboxElement.type = 'checkbox';
147
+ labelElement.innerText = labelText;
148
+ checkboxElement.oninput = () => {
149
+ onChange(checkboxElement.checked);
150
+ };
151
+ rowContainer.replaceChildren(labelElement, checkboxElement);
152
+ return { container: rowContainer, checkbox: checkboxElement };
153
+ };
136
154
  // Background style selector
137
- const useGridRow = document.createElement('div');
138
- const useGridLabel = document.createElement('label');
139
- const useGridCheckbox = document.createElement('input');
140
- useGridCheckbox.id = `${constants_1.toolbarCSSPrefix}docPropertiesUseGridCheckbox-${DocumentPropertiesWidget.idCounter++}`;
141
- useGridLabel.htmlFor = useGridCheckbox.id;
142
- useGridCheckbox.type = 'checkbox';
143
- useGridLabel.innerText = this.localizationTable.useGridOption;
144
- useGridCheckbox.oninput = () => {
155
+ const { container: useGridRow, checkbox: useGridCheckbox } = makeCheckboxRow(this.localizationTable.useGridOption, (checked) => {
145
156
  const prevBackgroundType = this.getBackgroundType();
146
157
  const wasGrid = prevBackgroundType === BackgroundComponent_1.BackgroundType.Grid;
147
- if (wasGrid === useGridCheckbox.checked) {
158
+ if (wasGrid === checked) {
148
159
  // Already the requested background type.
149
160
  return;
150
161
  }
151
162
  let newBackgroundType = BackgroundComponent_1.BackgroundType.SolidColor;
152
- if (useGridCheckbox.checked) {
163
+ if (checked) {
153
164
  newBackgroundType = BackgroundComponent_1.BackgroundType.Grid;
154
165
  }
155
166
  this.editor.dispatch(this.setBackgroundType(newBackgroundType));
156
- };
157
- useGridRow.replaceChildren(useGridLabel, useGridCheckbox);
167
+ });
158
168
  // Adds a width/height input
159
169
  const addDimensionRow = (labelContent, onChange) => {
160
170
  const row = document.createElement('div');
@@ -167,15 +177,25 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
167
177
  label.htmlFor = input.id;
168
178
  input.style.flexGrow = '2';
169
179
  input.style.width = '25px';
170
- row.style.display = 'flex';
171
180
  input.oninput = () => {
172
181
  onChange(parseFloat(input.value));
173
182
  };
183
+ row.classList.add('js-draw-size-input-row');
174
184
  row.replaceChildren(label, input);
175
185
  return {
176
186
  setValue: (value) => {
177
187
  input.value = value.toString();
178
188
  },
189
+ setIsAutomaticSize: (automatic) => {
190
+ input.disabled = automatic;
191
+ const automaticSizeClass = 'size-input-row--automatic-size';
192
+ if (automatic) {
193
+ row.classList.add(automaticSizeClass);
194
+ }
195
+ else {
196
+ row.classList.remove(automaticSizeClass);
197
+ }
198
+ },
179
199
  element: row,
180
200
  };
181
201
  };
@@ -185,6 +205,11 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
185
205
  const imageHeightRow = addDimensionRow(this.localizationTable.imageHeightOption, (value) => {
186
206
  this.updateImportExportRectSize({ height: value });
187
207
  });
208
+ // The autoresize checkbox
209
+ const { container: auroresizeRow, checkbox: autoresizeCheckbox } = makeCheckboxRow(this.localizationTable.enableAutoresizeOption, (checked) => {
210
+ const image = this.editor.image;
211
+ this.editor.dispatch(image.setAutoresizeEnabled(checked));
212
+ });
188
213
  // The "About..." button
189
214
  const aboutButton = document.createElement('button');
190
215
  aboutButton.classList.add('about-button');
@@ -194,13 +219,17 @@ class DocumentPropertiesWidget extends BaseWidget_1.default {
194
219
  };
195
220
  this.updateDropdownContent = () => {
196
221
  setBgColorInputValue(this.getBackgroundColor());
222
+ const autoresize = this.editor.image.getAutoresizeEnabled();
197
223
  const importExportRect = this.editor.getImportExportRect();
198
224
  imageWidthRow.setValue(importExportRect.width);
199
225
  imageHeightRow.setValue(importExportRect.height);
226
+ autoresizeCheckbox.checked = autoresize;
227
+ imageWidthRow.setIsAutomaticSize(autoresize);
228
+ imageHeightRow.setIsAutomaticSize(autoresize);
200
229
  useGridCheckbox.checked = this.getBackgroundType() === BackgroundComponent_1.BackgroundType.Grid;
201
230
  };
202
231
  this.updateDropdownContent();
203
- container.replaceChildren(backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element, aboutButton);
232
+ container.replaceChildren(backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element, auroresizeRow, aboutButton);
204
233
  dropdown.replaceChildren(container);
205
234
  return true;
206
235
  }
@@ -73,6 +73,14 @@ labelText, defaultId, choices) => {
73
73
  }
74
74
  updateButtonCSS();
75
75
  };
76
+ button.onfocus = () => {
77
+ if (buttonContainer.querySelector(':focus-visible')) {
78
+ buttonContainer.classList.add('focus-visible');
79
+ }
80
+ };
81
+ button.onblur = () => {
82
+ buttonContainer.classList.remove('focus-visible');
83
+ };
76
84
  buttonContainer.replaceChildren(button, labelContainer);
77
85
  menuContainer.appendChild(buttonContainer);
78
86
  // Set whether the current button is checked
@@ -212,24 +212,26 @@ class PanZoom extends BaseTool_1.default {
212
212
  this.lastScreenCenter = screenCenter;
213
213
  this.lastDist = dist;
214
214
  this.transform = Viewport_1.Viewport.transformBy(this.transform.transform.rightMul(transformUpdate));
215
+ return transformUpdate;
215
216
  }
216
217
  handleOneFingerMove(pointer) {
217
218
  const delta = this.getCenterDelta(pointer.screenPos);
218
- this.transform = Viewport_1.Viewport.transformBy(this.transform.transform.rightMul(math_1.Mat33.translation(delta)));
219
+ const transformUpdate = math_1.Mat33.translation(delta);
220
+ this.transform = Viewport_1.Viewport.transformBy(this.transform.transform.rightMul(transformUpdate));
219
221
  this.updateVelocity(pointer.screenPos);
220
222
  this.lastScreenCenter = pointer.screenPos;
223
+ return transformUpdate;
221
224
  }
222
225
  onPointerMove({ allPointers }) {
223
226
  this.transform ??= Viewport_1.Viewport.transformBy(math_1.Mat33.identity);
224
- const lastTransform = this.transform;
227
+ let transformUpdate = math_1.Mat33.identity;
225
228
  if (allPointers.length === 2) {
226
- this.handleTwoFingerMove(allPointers);
229
+ transformUpdate = this.handleTwoFingerMove(allPointers);
227
230
  }
228
231
  else if (allPointers.length === 1) {
229
- this.handleOneFingerMove(allPointers[0]);
232
+ transformUpdate = this.handleOneFingerMove(allPointers[0]);
230
233
  }
231
- lastTransform.unapply(this.editor);
232
- this.transform.apply(this.editor);
234
+ Viewport_1.Viewport.transformBy(transformUpdate).apply(this.editor);
233
235
  this.lastTimestamp = performance.now();
234
236
  }
235
237
  onPointerUp(event) {
@@ -309,8 +311,11 @@ class PanZoom extends BaseTool_1.default {
309
311
  const toCanvas = this.editor.viewport.screenToCanvasTransform;
310
312
  // Transform without including translation
311
313
  const translation = toCanvas.transformVec3(math_1.Vec3.of(-delta.x, -delta.y, 0));
312
- const pinchZoomScaleFactor = 1.03;
313
- const transformUpdate = math_1.Mat33.scaling2D(Math.max(0.25, Math.min(Math.pow(pinchZoomScaleFactor, -delta.z), 4)), canvasPos).rightMul(math_1.Mat33.translation(translation));
314
+ let pinchAmount = delta.z;
315
+ // Clamp the magnitude of pinchAmount
316
+ pinchAmount = Math.atan(pinchAmount / 2) * 2;
317
+ const pinchZoomScaleFactor = 1.04;
318
+ const transformUpdate = math_1.Mat33.scaling2D(Math.max(0.4, Math.min(Math.pow(pinchZoomScaleFactor, -pinchAmount), 4)), canvasPos).rightMul(math_1.Mat33.translation(translation));
314
319
  this.updateTransform(transformUpdate, true);
315
320
  return true;
316
321
  }
@@ -0,0 +1,18 @@
1
+ import Editor from '../Editor';
2
+ import BaseTool from './BaseTool';
3
+ /**
4
+ * This tool, when enabled, renders scrollbars reflecting the current position
5
+ * of the view relative to the import/export area of the image.
6
+ *
7
+ * **Note**: These scrollbars are currently not draggable. This may change in
8
+ * a future release.
9
+ */
10
+ export default class ScrollbarTool extends BaseTool {
11
+ private editor;
12
+ private scrollbarOverlay;
13
+ private verticalScrollbar;
14
+ private horizontalScrollbar;
15
+ constructor(editor: Editor);
16
+ private fadeOutTimeout;
17
+ private updateScrollbars;
18
+ }