js-draw 1.22.0 → 1.23.1

Sign up to get free protection for your applications and to get access to all the features.
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
  }