js-draw 1.22.0 → 1.23.1

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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/Editor.d.ts +1 -3
  4. package/dist/cjs/Editor.js +2 -4
  5. package/dist/cjs/SVGLoader/SVGLoader.js +2 -0
  6. package/dist/cjs/Viewport.d.ts +1 -1
  7. package/dist/cjs/Viewport.js +1 -1
  8. package/dist/cjs/components/AbstractComponent.d.ts +1 -1
  9. package/dist/cjs/components/AbstractComponent.js +1 -1
  10. package/dist/cjs/components/builders/ArrowBuilder.d.ts +1 -1
  11. package/dist/cjs/components/builders/ArrowBuilder.js +1 -1
  12. package/dist/cjs/image/EditorImage.d.ts +30 -7
  13. package/dist/cjs/image/EditorImage.js +30 -7
  14. package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +2 -25
  15. package/dist/cjs/rendering/renderers/CanvasRenderer.js +2 -25
  16. package/dist/cjs/rendering/renderers/SVGRenderer.js +2 -2
  17. package/dist/cjs/toolbar/AbstractToolbar.d.ts +19 -0
  18. package/dist/cjs/toolbar/AbstractToolbar.js +19 -0
  19. package/dist/cjs/toolbar/IconProvider.d.ts +5 -1
  20. package/dist/cjs/toolbar/IconProvider.js +112 -146
  21. package/dist/cjs/toolbar/localization.js +2 -2
  22. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +1 -1
  23. package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -1
  24. package/dist/cjs/tools/PanZoom.js +1 -1
  25. package/dist/cjs/tools/Pen.d.ts +13 -0
  26. package/dist/cjs/tools/Pen.js +13 -0
  27. package/dist/cjs/tools/lib.d.ts +1 -0
  28. package/dist/cjs/tools/lib.js +3 -1
  29. package/dist/cjs/util/cloneElementWithStyles.js +1 -1
  30. package/dist/cjs/util/createElement.d.ts +62 -0
  31. package/dist/cjs/util/createElement.js +53 -0
  32. package/dist/cjs/version.js +1 -1
  33. package/dist/mjs/Editor.d.ts +1 -3
  34. package/dist/mjs/Editor.mjs +2 -4
  35. package/dist/mjs/SVGLoader/SVGLoader.mjs +2 -0
  36. package/dist/mjs/Viewport.d.ts +1 -1
  37. package/dist/mjs/Viewport.mjs +1 -1
  38. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  39. package/dist/mjs/components/AbstractComponent.mjs +1 -1
  40. package/dist/mjs/components/builders/ArrowBuilder.d.ts +1 -1
  41. package/dist/mjs/components/builders/ArrowBuilder.mjs +1 -1
  42. package/dist/mjs/image/EditorImage.d.ts +30 -7
  43. package/dist/mjs/image/EditorImage.mjs +30 -7
  44. package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +2 -25
  45. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +2 -25
  46. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +2 -2
  47. package/dist/mjs/toolbar/AbstractToolbar.d.ts +19 -0
  48. package/dist/mjs/toolbar/AbstractToolbar.mjs +19 -0
  49. package/dist/mjs/toolbar/IconProvider.d.ts +5 -1
  50. package/dist/mjs/toolbar/IconProvider.mjs +112 -146
  51. package/dist/mjs/toolbar/localization.mjs +2 -2
  52. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +1 -1
  53. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -1
  54. package/dist/mjs/tools/PanZoom.mjs +1 -1
  55. package/dist/mjs/tools/Pen.d.ts +13 -0
  56. package/dist/mjs/tools/Pen.mjs +13 -0
  57. package/dist/mjs/tools/lib.d.ts +1 -0
  58. package/dist/mjs/tools/lib.mjs +1 -0
  59. package/dist/mjs/util/cloneElementWithStyles.mjs +1 -1
  60. package/dist/mjs/util/createElement.d.ts +62 -0
  61. package/dist/mjs/util/createElement.mjs +47 -0
  62. package/dist/mjs/version.mjs +1 -1
  63. package/package.json +4 -4
@@ -10,31 +10,32 @@ import Viewport from '../Viewport.mjs';
10
10
  import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder.mjs';
11
11
  import { makePolylineBuilder } from '../components/builders/PolylineBuilder.mjs';
12
12
  import { EraserMode } from '../tools/Eraser.mjs';
13
+ import { createSvgElement, createSvgElements, createSvgPaths } from '../util/createElement.mjs';
13
14
  const svgNamespace = 'http://www.w3.org/2000/svg';
14
- const iconColorFill = `
15
- style='fill: var(--icon-color);'
16
- `;
17
- const iconColorStrokeFill = `
18
- style='fill: var(--icon-color); stroke: var(--icon-color);'
19
- `;
20
15
  let checkerboardIdCounter = 0;
21
16
  const makeCheckerboardPattern = () => {
22
17
  const id = `checkerboard-${checkerboardIdCounter++}`;
23
- const patternDef = `
24
- <pattern
25
- id='${id}'
26
- viewBox='0,0,10,10'
27
- width='20%'
28
- height='20%'
29
- patternUnits='userSpaceOnUse'
30
- >
31
- <rect x='0' y='0' width='10' height='10' fill='white'/>
32
- <rect x='0' y='0' width='5' height='5' fill='gray'/>
33
- <rect x='5' y='5' width='5' height='5' fill='gray'/>
34
- </pattern>
35
- `;
18
+ const patternElement = createSvgElement('pattern', {
19
+ id: id,
20
+ viewBox: '0,0,10,10',
21
+ width: '20%',
22
+ height: '20%',
23
+ patternUnits: 'userSpaceOnUse',
24
+ children: createSvgElements('rect', [
25
+ { x: 0, y: 0, width: 10, height: 10, fill: 'white' },
26
+ { x: 0, y: 0, width: 5, height: 5, fill: 'gray' },
27
+ { x: 5, y: 5, width: 5, height: 5, fill: 'gray' },
28
+ ]),
29
+ });
36
30
  const patternRef = `url(#${id})`;
37
- return { patternDef, patternRef };
31
+ return {
32
+ patternDefElement: patternElement,
33
+ // @deprecated use patternDefElement
34
+ get patternDef() {
35
+ return patternElement.innerHTML;
36
+ },
37
+ patternRef,
38
+ };
38
39
  };
39
40
  const makeRedoIcon = (mirror) => {
40
41
  const icon = document.createElementNS(svgNamespace, 'svg');
@@ -50,11 +51,14 @@ const makeRedoIcon = (mirror) => {
50
51
  transform-origin: center;
51
52
  }
52
53
  </style>
53
- <path
54
- d='M20,20 A15,15 0 0 1 70,80 L80,90 L60,70 L65,90 L87,90 L65,80'
55
- class='toolbar-svg-undo-redo-icon'
56
- style='${mirror ? 'transform: scale(-1, 1);' : ''}'/>
57
54
  `;
55
+ const path = document.createElementNS(svgNamespace, 'path');
56
+ path.setAttribute('d', 'M20,20 A15,15 0 0 1 70,80 L80,90 L60,70 L65,90 L87,90 L65,80');
57
+ path.classList.add('toolbar-svg-undo-redo-icon');
58
+ if (mirror) {
59
+ path.style.transform = 'scale(-1, 1)';
60
+ }
61
+ icon.appendChild(path);
58
62
  icon.setAttribute('viewBox', '0 0 100 100');
59
63
  return icon;
60
64
  };
@@ -102,65 +106,62 @@ class IconProvider {
102
106
  return makeRedoIcon(false);
103
107
  }
104
108
  makeDropdownIcon() {
105
- const icon = document.createElementNS(svgNamespace, 'svg');
106
- icon.innerHTML = `
107
- <g>
108
- <path
109
- d='M5,10 L50,90 L95,10 Z'
110
- ${iconColorFill}
111
- />
112
- </g>
113
- `;
109
+ const icon = this.makeIconFromPath('M5,10 L50,90 L95,10 Z');
114
110
  icon.setAttribute('viewBox', '-10 -10 110 110');
115
111
  return icon;
116
112
  }
117
113
  makeEraserIcon(eraserSize, mode) {
118
- const icon = document.createElementNS(svgNamespace, 'svg');
119
114
  eraserSize ??= 10;
120
115
  const scaledSize = eraserSize / 4;
121
116
  const eraserColor = '#ff70af';
122
117
  // Draw an eraser-like shape. Created with Inkscape
123
- icon.innerHTML = `
124
- <defs>
125
- <linearGradient id="dash-pattern">
126
- <stop offset="80%" stop-color="${eraserColor}"/>
127
- <stop offset="85%" stop-color="white"/>
128
- <stop offset="90%" stop-color="${eraserColor}"/>
129
- </linearGradient>
130
- </defs>
131
- <g>
132
- <path
133
- style="fill:${mode === EraserMode.PartialStroke ? 'url(#dash-pattern)' : eraserColor}"
134
- stroke="black"
135
- transform="rotate(41.35)"
136
- d="M 52.5 27
137
- C 50 28.9 48.9 31.7 48.9 34.8
138
- L 48.9 39.8
139
- C 48.9 45.3 53.4 49.8 58.9 49.8
140
- L 103.9 49.8
141
- C 105.8 49.8 107.6 49.2 109.1 48.3
142
- L 110.2 ${scaledSize + 49.5} L 159.7 ${scaledSize + 5}
143
- L 157.7 ${-scaledSize + 5.2} L 112.4 ${49.5 - scaledSize}
144
- C 113.4 43.5 113.9 41.7 113.9 39.8
145
- L 113.9 34.8
146
- C 113.9 29.3 109.4 24.8 103.9 24.8
147
- L 58.9 24.8
148
- C 56.5 24.8 54.3 25.7 52.5 27
149
- z "
150
- id="path438" />
151
-
152
- <rect
153
- stroke="#cc8077"
154
- ${iconColorFill}
155
- id="rect218"
156
- width="65"
157
- height="75"
158
- x="48.9"
159
- y="-38.7"
160
- transform="rotate(41.35)" />
161
- </g>
162
- `;
163
- icon.setAttribute('viewBox', '0 0 120 120');
118
+ const icon = createSvgElement('svg', {
119
+ viewBox: '0 0 120 120',
120
+ children: [
121
+ createSvgElement('defs', {
122
+ children: [
123
+ createSvgElement('linearGradient', {
124
+ id: 'dash-pattern',
125
+ children: createSvgElements('stop', [
126
+ { offset: '80%', 'stop-color': eraserColor },
127
+ { offset: '85%', 'stop-color': 'white' },
128
+ { offset: '90%', 'stop-color': eraserColor },
129
+ ]),
130
+ }),
131
+ ],
132
+ }),
133
+ createSvgElement('path', {
134
+ fill: mode === EraserMode.PartialStroke ? 'url(#dash-pattern)' : eraserColor,
135
+ stroke: 'black',
136
+ transform: 'rotate(41.35)',
137
+ d: `
138
+ M 52.5 27
139
+ C 50 28.9 48.9 31.7 48.9 34.8
140
+ L 48.9 39.8
141
+ C 48.9 45.3 53.4 49.8 58.9 49.8
142
+ L 103.9 49.8
143
+ C 105.8 49.8 107.6 49.2 109.1 48.3
144
+ L 110.2 ${scaledSize + 49.5} L 159.7 ${scaledSize + 5}
145
+ L 157.7 ${-scaledSize + 5.2} L 112.4 ${49.5 - scaledSize}
146
+ C 113.4 43.5 113.9 41.7 113.9 39.8
147
+ L 113.9 34.8
148
+ C 113.9 29.3 109.4 24.8 103.9 24.8
149
+ L 58.9 24.8
150
+ C 56.5 24.8 54.3 25.7 52.5 27
151
+ z
152
+ `,
153
+ }),
154
+ createSvgElement('rect', {
155
+ stroke: '#cc8077',
156
+ fill: 'var(--icon-color)',
157
+ width: 65,
158
+ height: 75,
159
+ x: 48.9,
160
+ y: -38.7,
161
+ transform: 'rotate(41.35)',
162
+ }),
163
+ ],
164
+ });
164
165
  return icon;
165
166
  }
166
167
  makeSelectionIcon() {
@@ -168,8 +169,8 @@ class IconProvider {
168
169
  // Draw a cursor-like shape
169
170
  icon.innerHTML = `
170
171
  <g>
171
- <rect x=10 y=10 width=70 height=70 fill='pink' stroke='black'/>
172
- <rect x=75 y=75 width=10 height=10 fill='white' stroke='black'/>
172
+ <rect x="10" y="10" width="70" height="70" fill="pink" stroke="black"/>
173
+ <rect x="75" y="75" width="10" height="10" fill="white" stroke="black"/>
173
174
  </g>
174
175
  `;
175
176
  icon.setAttribute('viewBox', '0 0 100 100');
@@ -432,8 +433,6 @@ class IconProvider {
432
433
  const strokeSize = Math.round(Math.sqrt(penStyle.thickness) * 4);
433
434
  const color = penStyle.color;
434
435
  const rounded = this.isRoundedTipPen(penStyle);
435
- const icon = document.createElementNS(svgNamespace, 'svg');
436
- icon.setAttribute('viewBox', '0 0 100 100');
437
436
  const tipThickness = strokeSize / 2;
438
437
  const inkTipPath = `
439
438
  M ${15 - tipThickness},${80 - tipThickness}
@@ -466,71 +465,35 @@ class IconProvider {
466
465
  const pencilTipColor = Color4.fromHex('#f4d7d7');
467
466
  const tipColor = pencilTipColor.mix(color, tipThickness / 40 - 0.1).toHexString();
468
467
  const checkerboardPattern = makeCheckerboardPattern();
469
- const ink = `
470
- <path
471
- fill="${checkerboardPattern.patternRef}"
472
- d="${inkTipPath}"
473
- />
474
- <path
475
- fill="${checkerboardPattern.patternRef}"
476
- d="${inkTrailPath}"
477
- />
478
- <path
479
- fill="${color}"
480
- d="${inkTipPath}"
481
- />
482
- <path
483
- fill="${color}"
484
- d="${inkTrailPath}"
485
- />
486
- `;
487
- const penTip = `
488
- <path
489
- fill="${checkerboardPattern.patternRef}"
490
- d="${penTipPath}"
491
- />
492
- <path
493
- fill="${tipColor}"
494
- stroke="${color}"
495
- d="${penTipPath}"
496
- />
497
- `;
498
- const grip = `
499
- <path
500
- ${iconColorStrokeFill}
501
- d="${gripMainPath}"
502
- />
503
-
504
- <!-- shadows -->
505
- <path
506
- fill="rgba(150, 150, 150, 0.3)"
507
- d="${gripShadow1Path}"
508
- />
509
- <path
510
- fill="rgba(100, 100, 100, 0.2)"
511
- d="${gripShadow2Path}"
512
- />
513
-
514
- <!-- color bubble -->
515
- <path
516
- fill="${checkerboardPattern.patternRef}"
517
- d="${colorBubblePath}"
518
- />
519
- <path
520
- fill="${color}"
521
- d="${colorBubblePath}"
522
- />
523
- `;
524
- icon.innerHTML = `
525
- <defs>
526
- ${checkerboardPattern.patternDef}
527
- </defs>
528
- <g>
529
- ${ink}
530
- ${penTip}
531
- ${grip}
532
- </g>
533
- `;
468
+ const colorString = color.toHexString();
469
+ const ink = createSvgPaths({
470
+ fill: checkerboardPattern.patternRef,
471
+ d: inkTipPath,
472
+ }, {
473
+ fill: checkerboardPattern.patternRef,
474
+ d: inkTrailPath,
475
+ }, {
476
+ fill: colorString,
477
+ d: inkTipPath,
478
+ }, {
479
+ fill: colorString,
480
+ d: inkTrailPath,
481
+ });
482
+ const penTip = createSvgPaths({ fill: checkerboardPattern.patternRef, d: penTipPath }, { fill: tipColor, stroke: colorString, d: penTipPath });
483
+ const grip = createSvgPaths({ fill: 'var(--icon-color)', stroke: 'var(--icon-color)', d: gripMainPath },
484
+ // Shadows
485
+ { fill: 'rgba(150, 150, 150, 0.3)', d: gripShadow1Path }, { fill: 'rgba(100, 100, 100, 0.2)', d: gripShadow2Path },
486
+ // Color bubble
487
+ { fill: checkerboardPattern.patternRef, d: colorBubblePath }, { fill: colorString, d: colorBubblePath });
488
+ const icon = document.createElementNS(svgNamespace, 'svg');
489
+ icon.setAttribute('viewBox', '0 0 100 100');
490
+ const iconMainContent = createSvgElement('g', {
491
+ children: [ink, penTip, grip].flat(),
492
+ });
493
+ const defs = createSvgElement('defs', {
494
+ children: [checkerboardPattern.patternDefElement],
495
+ });
496
+ icon.replaceChildren(defs, iconMainContent);
534
497
  return icon;
535
498
  }
536
499
  makeIconFromFactory(penStyle) {
@@ -562,7 +525,7 @@ class IconProvider {
562
525
  if (includeTransparencyGrid) {
563
526
  const checkerboardPattern = makeCheckerboardPattern();
564
527
  const defs = document.createElementNS(svgNamespace, 'defs');
565
- defs.innerHTML = checkerboardPattern.patternDef;
528
+ defs.appendChild(checkerboardPattern.patternDefElement);
566
529
  icon.appendChild(defs);
567
530
  const background = document.createElementNS(svgNamespace, 'g');
568
531
  icon.appendChild(background);
@@ -636,7 +599,7 @@ class IconProvider {
636
599
  if (color) {
637
600
  const checkerboardPattern = makeCheckerboardPattern();
638
601
  const defs = document.createElementNS(svgNamespace, 'defs');
639
- defs.innerHTML = checkerboardPattern.patternDef;
602
+ defs.appendChild(checkerboardPattern.patternDefElement);
640
603
  icon.appendChild(defs);
641
604
  const fluidBackground = document.createElementNS(svgNamespace, 'path');
642
605
  const fluid = document.createElementNS(svgNamespace, 'path');
@@ -841,6 +804,9 @@ class IconProvider {
841
804
  * @returns An object with both the definition of a checkerboard pattern and the syntax to
842
805
  * reference that pattern. The defs provided by this function should be wrapped within a
843
806
  * `<defs></defs>` element.
807
+ *
808
+ * **Note**: This function's return value includes both `patternDefElement` (which returns
809
+ * an Element) and a (deprecated) `patternDef` string. Avoid using the `patternDef` result.
844
810
  */
845
811
  makeCheckerboardPattern() {
846
812
  return makeCheckerboardPattern();
@@ -45,7 +45,7 @@ export const defaultToolbarLocalization = {
45
45
  about: 'About',
46
46
  inputStabilization: 'Stabilization',
47
47
  strokeAutocorrect: 'Autocorrect',
48
- touchPanning: 'Touchscreen panning',
48
+ touchPanning: 'Scroll with touch',
49
49
  roundedTipPen: 'Round',
50
50
  roundedTipPen2: 'Polyline',
51
51
  flatTipPen: 'Flat',
@@ -72,7 +72,7 @@ export const defaultToolbarLocalization = {
72
72
  handDropdown__zoomOutHelpText: 'Zooms out.',
73
73
  handDropdown__resetViewHelpText: 'Resets the zoom level to 100% and resets scroll.',
74
74
  handDropdown__zoomDisplayHelpText: 'Shows the current zoom level. 100% shows the image at its actual size.',
75
- handDropdown__touchPanningHelpText: 'When enabled, touch gestures move the image rather than select or draw.',
75
+ handDropdown__touchPanningHelpText: 'When enabled, touchscreen gestures move the image rather than select or draw.',
76
76
  handDropdown__lockRotationHelpText: 'When enabled, prevents touch gestures from rotating the screen.',
77
77
  eraserDropdown__baseHelpText: 'This tool removes strokes, images, and text under the cursor.',
78
78
  eraserDropdown__thicknessHelpText: 'Changes the size of the eraser.',
@@ -98,7 +98,7 @@ export default abstract class BaseWidget {
98
98
  addTo(parent: HTMLElement): HTMLElement;
99
99
  /**
100
100
  * Remove this. This allows the widget to be added to a toolbar again
101
- * in the future using {@link addTo}.
101
+ * in the future using `addTo`.
102
102
  */
103
103
  remove(): void;
104
104
  focus(): void;
@@ -300,7 +300,7 @@ class BaseWidget {
300
300
  }
301
301
  /**
302
302
  * Remove this. This allows the widget to be added to a toolbar again
303
- * in the future using {@link addTo}.
303
+ * in the future using `addTo`.
304
304
  */
305
305
  remove() {
306
306
  this.container.remove();
@@ -427,7 +427,7 @@ export default class PanZoom extends BaseTool {
427
427
  translation = translation.times(-1);
428
428
  rotation = rotation * -1;
429
429
  scale = 1 / scale;
430
- // Work around an issue that seems to be related to rotation matricies losing precision on inversion.
430
+ // Work around an issue that seems to be related to rotation matrices losing precision on inversion.
431
431
  // TODO: Figure out why and implement a better solution.
432
432
  if (rotation !== 0) {
433
433
  rotation += 0.0001;
@@ -11,6 +11,12 @@ export interface PenStyle {
11
11
  readonly thickness: number;
12
12
  readonly factory: ComponentBuilderFactory;
13
13
  }
14
+ /**
15
+ * A tool that allows drawing shapes and freehand lines.
16
+ *
17
+ * To change the type of shape drawn by the pen (e.g. to switch to the rectangle
18
+ * pen type), see {@link setStrokeFactory}.
19
+ */
14
20
  export default class Pen extends BaseTool {
15
21
  private editor;
16
22
  protected builder: ComponentBuilder | null;
@@ -42,6 +48,13 @@ export default class Pen extends BaseTool {
42
48
  private noteUpdated;
43
49
  setColor(color: Color4): void;
44
50
  setThickness(thickness: number): void;
51
+ /**
52
+ * Changes the type of stroke created by the pen. The given `factory` can be one of the built-in
53
+ * stroke factories (e.g. {@link makeFreehandLineBuilder}) or a custom stroke factory.
54
+ *
55
+ * Example:
56
+ * [[include:doc-pages/inline-examples/changing-pen-types.md]]
57
+ */
45
58
  setStrokeFactory(factory: ComponentBuilderFactory): void;
46
59
  setHasStabilization(hasStabilization: boolean): void;
47
60
  setStrokeAutocorrectEnabled(enabled: boolean): void;
@@ -9,6 +9,12 @@ import { decreaseSizeKeyboardShortcutId, increaseSizeKeyboardShortcutId } from
9
9
  import InputStabilizer from './InputFilter/InputStabilizer.mjs';
10
10
  import { ReactiveValue } from '../util/ReactiveValue.mjs';
11
11
  import StationaryPenDetector, { defaultStationaryDetectionConfig, } from './util/StationaryPenDetector.mjs';
12
+ /**
13
+ * A tool that allows drawing shapes and freehand lines.
14
+ *
15
+ * To change the type of shape drawn by the pen (e.g. to switch to the rectangle
16
+ * pen type), see {@link setStrokeFactory}.
17
+ */
12
18
  export default class Pen extends BaseTool {
13
19
  constructor(editor, description, style) {
14
20
  super(editor.notifier, description);
@@ -244,6 +250,13 @@ export default class Pen extends BaseTool {
244
250
  });
245
251
  }
246
252
  }
253
+ /**
254
+ * Changes the type of stroke created by the pen. The given `factory` can be one of the built-in
255
+ * stroke factories (e.g. {@link makeFreehandLineBuilder}) or a custom stroke factory.
256
+ *
257
+ * Example:
258
+ * [[include:doc-pages/inline-examples/changing-pen-types.md]]
259
+ */
247
260
  setStrokeFactory(factory) {
248
261
  if (factory !== this.style.factory) {
249
262
  this.styleValue.set({
@@ -1,3 +1,4 @@
1
+ export { default as InputMapper } from './InputFilter/InputMapper';
1
2
  export { default as BaseTool } from './BaseTool';
2
3
  export { default as ToolController } from './ToolController';
3
4
  export { default as ToolEnabledGroup } from './ToolEnabledGroup';
@@ -1,3 +1,4 @@
1
+ export { default as InputMapper } from './InputFilter/InputMapper.mjs';
1
2
  export { default as BaseTool } from './BaseTool.mjs';
2
3
  export { default as ToolController } from './ToolController.mjs';
3
4
  export { default as ToolEnabledGroup } from './ToolEnabledGroup.mjs';
@@ -10,7 +10,7 @@ const cloneElementWithStyles = (element) => {
10
10
  for (let index = 0; index < originalComputedStyle.length; index++) {
11
11
  const propertyName = originalComputedStyle.item(index);
12
12
  const propertyValue = originalComputedStyle.getPropertyValue(propertyName);
13
- clonedElement.style.setProperty(propertyName, propertyValue);
13
+ clonedElement.style?.setProperty(propertyName, propertyValue);
14
14
  }
15
15
  for (let i = 0; i < originalElement.children.length; i++) {
16
16
  const originalChild = originalElement.children.item(i);
@@ -0,0 +1,62 @@
1
+ type ElementTagNames = keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap;
2
+ /**
3
+ * Maps from known elment tag names to options that can be set with .setAttribute.
4
+ * New elements/properties should be added as necessary.
5
+ */
6
+ interface ElementToPropertiesMap {
7
+ path: {
8
+ d: string;
9
+ fill: string;
10
+ stroke: string;
11
+ transform: string;
12
+ };
13
+ rect: {
14
+ stroke: string;
15
+ fill: string;
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ transform: string;
21
+ };
22
+ pattern: {
23
+ viewBox: string;
24
+ width: string;
25
+ height: string;
26
+ patternUnits: 'userSpaceOnUse';
27
+ };
28
+ stop: {
29
+ offset: string;
30
+ 'stop-color': string;
31
+ };
32
+ svg: {
33
+ viewBox: `${number} ${number} ${number} ${number}`;
34
+ };
35
+ }
36
+ type EmptyObject = Record<never, never>;
37
+ type ElementProperties<Tag extends ElementTagNames> = Tag extends keyof ElementToPropertiesMap ? Partial<ElementToPropertiesMap[Tag]> : EmptyObject;
38
+ /** Contains options for creating an element with tag = `Tag`. */
39
+ type ElementConfig<Tag extends ElementTagNames> = ElementProperties<Tag> & {
40
+ id?: string;
41
+ children?: (HTMLElement | SVGElement)[];
42
+ };
43
+ /**
44
+ * Maps from element tag names (e.g. `Tag='button'`) to the corresponding element type
45
+ * (e.g. `HTMLButtonElement`).
46
+ */
47
+ type ElementTagToType<Tag extends ElementTagNames> = Tag extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[Tag] : Tag extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[Tag] : never;
48
+ export declare enum ElementNamespace {
49
+ Html = "html",
50
+ Svg = "svg"
51
+ }
52
+ /**
53
+ * Shorthand for creating an element with `document.createElement`, then assigning properties.
54
+ *
55
+ * Non-HTML elements (e.g. `svg` elements) should use the `elementType` parameter to select
56
+ * the element namespace.
57
+ */
58
+ declare const createElement: <Tag extends ElementTagNames>(tag: Tag, props: ElementConfig<Tag>, elementType?: ElementNamespace) => ElementTagToType<Tag>;
59
+ export declare const createSvgElement: <Tag extends keyof SVGElementTagNameMap>(tag: Tag, props: ElementConfig<Tag>) => ElementTagToType<Tag>;
60
+ export declare const createSvgElements: <Tag extends keyof SVGElementTagNameMap>(tag: Tag, elements: ElementConfig<Tag>[]) => ElementTagToType<Tag>[];
61
+ export declare const createSvgPaths: (...paths: ElementConfig<"path">[]) => SVGPathElement[];
62
+ export default createElement;
@@ -0,0 +1,47 @@
1
+ export var ElementNamespace;
2
+ (function (ElementNamespace) {
3
+ ElementNamespace["Html"] = "html";
4
+ ElementNamespace["Svg"] = "svg";
5
+ })(ElementNamespace || (ElementNamespace = {}));
6
+ /**
7
+ * Shorthand for creating an element with `document.createElement`, then assigning properties.
8
+ *
9
+ * Non-HTML elements (e.g. `svg` elements) should use the `elementType` parameter to select
10
+ * the element namespace.
11
+ */
12
+ const createElement = (tag, props, elementType = ElementNamespace.Html) => {
13
+ let elem;
14
+ if (elementType === ElementNamespace.Html) {
15
+ elem = document.createElement(tag);
16
+ }
17
+ else if (elementType === ElementNamespace.Svg) {
18
+ elem = document.createElementNS('http://www.w3.org/2000/svg', tag);
19
+ }
20
+ else {
21
+ throw new Error(`Unknown element type ${elementType}`);
22
+ }
23
+ for (const [key, value] of Object.entries(props)) {
24
+ if (key === 'children')
25
+ continue;
26
+ if (typeof value !== 'string' && typeof value !== 'number') {
27
+ throw new Error(`Unsupported value type ${typeof value}`);
28
+ }
29
+ elem.setAttribute(key, value.toString());
30
+ }
31
+ if (props.children) {
32
+ for (const item of props.children) {
33
+ elem.appendChild(item);
34
+ }
35
+ }
36
+ return elem;
37
+ };
38
+ export const createSvgElement = (tag, props) => {
39
+ return createElement(tag, props, ElementNamespace.Svg);
40
+ };
41
+ export const createSvgElements = (tag, elements) => {
42
+ return elements.map((props) => createSvgElement(tag, props));
43
+ };
44
+ export const createSvgPaths = (...paths) => {
45
+ return createSvgElements('path', paths);
46
+ };
47
+ export default createElement;
@@ -4,5 +4,5 @@
4
4
  * @internal
5
5
  */
6
6
  export default {
7
- number: '1.22.0',
7
+ number: '1.23.1',
8
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.22.0",
3
+ "version": "1.23.1",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -64,11 +64,11 @@
64
64
  "postpack": "ts-node tools/copyREADME.ts revert"
65
65
  },
66
66
  "dependencies": {
67
- "@js-draw/math": "^1.22.0",
67
+ "@js-draw/math": "^1.23.1",
68
68
  "@melloware/coloris": "0.22.0"
69
69
  },
70
70
  "devDependencies": {
71
- "@js-draw/build-tool": "^1.22.0",
71
+ "@js-draw/build-tool": "^1.23.1",
72
72
  "@types/jest": "29.5.5",
73
73
  "@types/jsdom": "21.1.3"
74
74
  },
@@ -86,5 +86,5 @@
86
86
  "freehand",
87
87
  "svg"
88
88
  ],
89
- "gitHead": "c922cf6e44d078133100e01383ba1bacdebe01bd"
89
+ "gitHead": "e0bb3336d5f3a94533c823906778d39a4880f4cf"
90
90
  }