js-draw 1.18.0 → 1.20.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +51 -0
- package/dist/Editor.css +78 -5
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +20 -1
- package/dist/cjs/Editor.js +6 -0
- package/dist/cjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
- package/dist/cjs/{SVGLoader.js → SVGLoader/index.js} +15 -30
- package/dist/cjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
- package/dist/cjs/SVGLoader/utils/determineFontSize.js +27 -0
- package/dist/cjs/Viewport.d.ts +33 -1
- package/dist/cjs/components/TextComponent.js +3 -1
- package/dist/cjs/image/EditorImage.d.ts +2 -1
- package/dist/cjs/image/EditorImage.js +101 -5
- package/dist/cjs/rendering/caching/RenderingCacheNode.js +20 -15
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +4 -4
- package/dist/cjs/testing/findNodeWithText.d.ts +3 -0
- package/dist/cjs/testing/findNodeWithText.js +16 -0
- package/dist/cjs/testing/firstElementAncestorOfNode.d.ts +3 -0
- package/dist/cjs/testing/firstElementAncestorOfNode.js +13 -0
- package/dist/cjs/testing/sendKeyPressRelease.d.ts +3 -0
- package/dist/cjs/testing/sendKeyPressRelease.js +8 -0
- package/dist/cjs/testing/sendPenEvent.d.ts +2 -2
- package/dist/cjs/testing/sendPenEvent.js +26 -3
- package/dist/cjs/toolbar/localization.d.ts +3 -0
- package/dist/cjs/toolbar/localization.js +3 -0
- package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +23 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/ImageWrapper.js +65 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/fileToImages.js +21 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget/index.js +289 -0
- package/dist/cjs/toolbar/widgets/TextToolWidget.js +5 -3
- package/dist/cjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
- package/dist/cjs/toolbar/widgets/components/makeFileInput.js +113 -45
- package/dist/cjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
- package/dist/cjs/toolbar/widgets/components/makeSnappedList.js +168 -0
- package/dist/cjs/tools/Eraser.d.ts +7 -2
- package/dist/cjs/tools/Eraser.js +76 -6
- package/dist/cjs/tools/PanZoom.d.ts +54 -0
- package/dist/cjs/tools/PanZoom.js +54 -2
- package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -2
- package/dist/cjs/tools/SelectionTool/Selection.js +20 -20
- package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
- package/dist/cjs/tools/SelectionTool/SelectionHandle.js +6 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +1 -1
- package/dist/cjs/tools/SelectionTool/types.d.ts +19 -0
- package/dist/cjs/tools/TextTool.js +2 -1
- package/dist/cjs/tools/TextTool.test.d.ts +1 -0
- package/dist/cjs/tools/ToolController.d.ts +2 -0
- package/dist/cjs/tools/ToolController.js +10 -1
- package/dist/cjs/util/ReactiveValue.d.ts +6 -0
- package/dist/cjs/util/ReactiveValue.js +16 -0
- package/dist/cjs/util/bytesToSizeString.d.ts +8 -0
- package/dist/cjs/util/bytesToSizeString.js +26 -0
- package/dist/cjs/util/bytesToSizeString.test.d.ts +1 -0
- package/dist/cjs/util/stopPropagationOfScrollingWheelEvents.js +10 -6
- package/dist/cjs/util/waitForAll.d.ts +2 -0
- package/dist/cjs/util/waitForAll.js +2 -0
- package/dist/cjs/util/waitForImageLoaded.js +3 -0
- package/dist/cjs/util/waitForTimeout.d.ts +1 -0
- package/dist/cjs/util/waitForTimeout.js +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +20 -1
- package/dist/mjs/Editor.mjs +6 -0
- package/dist/mjs/{SVGLoader.d.ts → SVGLoader/index.d.ts} +1 -1
- package/dist/mjs/{SVGLoader.mjs → SVGLoader/index.mjs} +15 -30
- package/dist/mjs/SVGLoader/index.test.d.ts +1 -0
- package/dist/mjs/SVGLoader/utils/determineFontSize.d.ts +3 -0
- package/dist/mjs/SVGLoader/utils/determineFontSize.mjs +25 -0
- package/dist/mjs/Viewport.d.ts +33 -1
- package/dist/mjs/components/TextComponent.mjs +3 -1
- package/dist/mjs/image/EditorImage.d.ts +2 -1
- package/dist/mjs/image/EditorImage.mjs +101 -5
- package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +20 -15
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +4 -4
- package/dist/mjs/testing/findNodeWithText.d.ts +3 -0
- package/dist/mjs/testing/findNodeWithText.mjs +14 -0
- package/dist/mjs/testing/firstElementAncestorOfNode.d.ts +3 -0
- package/dist/mjs/testing/firstElementAncestorOfNode.mjs +11 -0
- package/dist/mjs/testing/sendKeyPressRelease.d.ts +3 -0
- package/dist/mjs/testing/sendKeyPressRelease.mjs +6 -0
- package/dist/mjs/testing/sendPenEvent.d.ts +2 -2
- package/dist/mjs/testing/sendPenEvent.mjs +3 -3
- package/dist/mjs/toolbar/localization.d.ts +3 -0
- package/dist/mjs/toolbar/localization.mjs +3 -0
- package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.d.ts +23 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/ImageWrapper.mjs +61 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.d.ts +3 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/fileToImages.mjs +16 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/index.d.ts +37 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/index.mjs +284 -0
- package/dist/mjs/toolbar/widgets/InsertImageWidget/index.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/TextToolWidget.mjs +5 -3
- package/dist/mjs/toolbar/widgets/TextToolWidget.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/components/makeFileInput.d.ts +12 -2
- package/dist/mjs/toolbar/widgets/components/makeFileInput.mjs +113 -45
- package/dist/mjs/toolbar/widgets/components/makeFileInput.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/components/makeSnappedList.d.ts +15 -0
- package/dist/mjs/toolbar/widgets/components/makeSnappedList.mjs +163 -0
- package/dist/mjs/tools/Eraser.d.ts +7 -2
- package/dist/mjs/tools/Eraser.mjs +76 -6
- package/dist/mjs/tools/PanZoom.d.ts +54 -0
- package/dist/mjs/tools/PanZoom.mjs +54 -2
- package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -2
- package/dist/mjs/tools/SelectionTool/Selection.mjs +20 -20
- package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +8 -2
- package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +6 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +1 -1
- package/dist/mjs/tools/SelectionTool/types.d.ts +19 -0
- package/dist/mjs/tools/TextTool.mjs +2 -1
- package/dist/mjs/tools/TextTool.test.d.ts +1 -0
- package/dist/mjs/tools/ToolController.d.ts +2 -0
- package/dist/mjs/tools/ToolController.mjs +10 -1
- package/dist/mjs/util/ReactiveValue.d.ts +6 -0
- package/dist/mjs/util/ReactiveValue.mjs +16 -0
- package/dist/mjs/util/bytesToSizeString.d.ts +8 -0
- package/dist/mjs/util/bytesToSizeString.mjs +24 -0
- package/dist/mjs/util/bytesToSizeString.test.d.ts +1 -0
- package/dist/mjs/util/stopPropagationOfScrollingWheelEvents.mjs +10 -6
- package/dist/mjs/util/waitForAll.d.ts +2 -0
- package/dist/mjs/util/waitForAll.mjs +2 -0
- package/dist/mjs/util/waitForImageLoaded.mjs +3 -0
- package/dist/mjs/util/waitForTimeout.d.ts +1 -0
- package/dist/mjs/util/waitForTimeout.mjs +1 -1
- package/dist/mjs/version.mjs +1 -1
- package/package.json +4 -4
- package/src/toolbar/EdgeToolbar.scss +8 -3
- package/src/toolbar/toolbar.scss +1 -7
- package/src/toolbar/widgets/{InsertImageWidget.scss → InsertImageWidget/index.scss} +3 -2
- package/src/toolbar/widgets/components/components.scss +2 -1
- package/src/toolbar/widgets/components/makeFileInput.scss +14 -1
- package/src/toolbar/widgets/components/makeSnappedList.scss +74 -0
- package/src/toolbar/widgets/widgets.scss +7 -0
- package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
- package/dist/cjs/toolbar/widgets/InsertImageWidget.js +0 -269
- package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +0 -22
- package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +0 -264
- /package/dist/cjs/{SVGLoader.test.d.ts → SVGLoader/index.test.d.ts} +0 -0
- /package/dist/{mjs/SVGLoader.test.d.ts → cjs/toolbar/widgets/InsertImageWidget/index.test.d.ts} +0 -0
@@ -344,7 +344,7 @@ class EditorImage {
|
|
344
344
|
*
|
345
345
|
* @internal
|
346
346
|
*/
|
347
|
-
setDebugMode(newDebugMode) {
|
347
|
+
static setDebugMode(newDebugMode) {
|
348
348
|
debugMode = newDebugMode;
|
349
349
|
}
|
350
350
|
}
|
@@ -617,8 +617,8 @@ class ImageNode {
|
|
617
617
|
const nodeForChildren = new ImageNode(this);
|
618
618
|
nodeForChildren.children = this.children;
|
619
619
|
this.children = [nodeForNewLeaf, nodeForChildren];
|
620
|
-
nodeForChildren.recomputeBBox(true);
|
621
620
|
nodeForChildren.updateParents();
|
621
|
+
nodeForChildren.recomputeBBox(true);
|
622
622
|
}
|
623
623
|
return nodeForNewLeaf.addLeaf(leaf);
|
624
624
|
}
|
@@ -635,6 +635,9 @@ class ImageNode {
|
|
635
635
|
const newNode = ImageNode.createLeafNode(this, leaf);
|
636
636
|
this.children.push(newNode);
|
637
637
|
newNode.recomputeBBox(true);
|
638
|
+
if (this.children.length >= this.targetChildCount) {
|
639
|
+
this.rebalance();
|
640
|
+
}
|
638
641
|
return newNode;
|
639
642
|
}
|
640
643
|
// Creates a new leaf node with the given content.
|
@@ -667,6 +670,7 @@ class ImageNode {
|
|
667
670
|
this.parent?.recomputeBBox(true);
|
668
671
|
}
|
669
672
|
}
|
673
|
+
this.checkRep();
|
670
674
|
}
|
671
675
|
// Grows this' bounding box to also include `other`.
|
672
676
|
// Always bubbles up.
|
@@ -690,10 +694,12 @@ class ImageNode {
|
|
690
694
|
// Remove this' parent, if this' parent isn't the root.
|
691
695
|
const oldParent = this.parent;
|
692
696
|
if (oldParent.parent !== null) {
|
693
|
-
|
694
|
-
|
695
|
-
this.parent.children.push(this);
|
697
|
+
const newParent = oldParent.parent;
|
698
|
+
newParent.children = newParent.children.filter(c => c !== oldParent);
|
696
699
|
oldParent.parent = null;
|
700
|
+
oldParent.children = [];
|
701
|
+
this.parent = newParent;
|
702
|
+
newParent.children.push(this);
|
697
703
|
this.parent.recomputeBBox(false);
|
698
704
|
}
|
699
705
|
else if (this.content === null) {
|
@@ -703,10 +709,63 @@ class ImageNode {
|
|
703
709
|
this.parent = null;
|
704
710
|
}
|
705
711
|
}
|
712
|
+
// Create virtual containers for children. Handles the case where there
|
713
|
+
// are many small, often non-overlapping children that we still want to be grouped.
|
714
|
+
if (this.children.length > this.targetChildCount * 10) {
|
715
|
+
const grid = this.getBBox().divideIntoGrid(4, 4);
|
716
|
+
const indexToCount = [];
|
717
|
+
while (indexToCount.length < grid.length) {
|
718
|
+
indexToCount.push(0);
|
719
|
+
}
|
720
|
+
for (const child of this.children) {
|
721
|
+
for (let i = 0; i < grid.length; i++) {
|
722
|
+
if (grid[i].containsRect(child.getBBox())) {
|
723
|
+
indexToCount[i]++;
|
724
|
+
}
|
725
|
+
}
|
726
|
+
}
|
727
|
+
let indexWithGreatest = 0;
|
728
|
+
let greatestCount = indexToCount[0];
|
729
|
+
for (let i = 1; i < indexToCount.length; i++) {
|
730
|
+
if (indexToCount[i] > greatestCount) {
|
731
|
+
indexWithGreatest = i;
|
732
|
+
greatestCount = indexToCount[i];
|
733
|
+
}
|
734
|
+
}
|
735
|
+
const targetGridSquare = grid[indexWithGreatest];
|
736
|
+
// Avoid clustering if just a few children would be grouped.
|
737
|
+
// Unnecessary clustering can lead to unnecessarily nested nodes.
|
738
|
+
if (greatestCount > 4) {
|
739
|
+
const newChildren = [];
|
740
|
+
const childNodeChildren = [];
|
741
|
+
for (const child of this.children) {
|
742
|
+
if (targetGridSquare.containsRect(child.getBBox())) {
|
743
|
+
childNodeChildren.push(child);
|
744
|
+
}
|
745
|
+
else {
|
746
|
+
newChildren.push(child);
|
747
|
+
}
|
748
|
+
}
|
749
|
+
if (childNodeChildren.length < this.children.length) {
|
750
|
+
this.children = newChildren;
|
751
|
+
const child = new ImageNode(this);
|
752
|
+
this.children.push(child);
|
753
|
+
child.children = childNodeChildren;
|
754
|
+
child.updateParents(false);
|
755
|
+
child.recomputeBBox(false);
|
756
|
+
child.rebalance();
|
757
|
+
}
|
758
|
+
}
|
759
|
+
}
|
760
|
+
// Empty?
|
761
|
+
if (this.parent && this.children.length === 0 && this.content === null) {
|
762
|
+
this.remove();
|
763
|
+
}
|
706
764
|
}
|
707
765
|
// Removes the parent-to-child link.
|
708
766
|
// Called internally by `.remove`
|
709
767
|
removeChild(child) {
|
768
|
+
this.checkRep();
|
710
769
|
const oldChildCount = this.children.length;
|
711
770
|
this.children = this.children.filter(node => {
|
712
771
|
return node !== child;
|
@@ -716,6 +775,8 @@ class ImageNode {
|
|
716
775
|
child.rebalance();
|
717
776
|
});
|
718
777
|
this.recomputeBBox(true);
|
778
|
+
this.rebalance();
|
779
|
+
this.checkRep();
|
719
780
|
}
|
720
781
|
// Remove this node and all of its children
|
721
782
|
remove() {
|
@@ -730,6 +791,7 @@ class ImageNode {
|
|
730
791
|
this.parent = null;
|
731
792
|
this.content = null;
|
732
793
|
this.children = [];
|
794
|
+
this.checkRep();
|
733
795
|
}
|
734
796
|
// Creates a (potentially incomplete) async rendering of this image.
|
735
797
|
// Returns false if stopped early
|
@@ -796,11 +858,45 @@ class ImageNode {
|
|
796
858
|
const lineWidth = isLeaf ? 1 * pixelSize : 2 * pixelSize;
|
797
859
|
renderer.drawRect(bbox.intersection(visibleRect), lineWidth, { fill });
|
798
860
|
renderer.endObject();
|
861
|
+
if (bbox.maxDimension > visibleRect.maxDimension / 3) {
|
862
|
+
const textStyle = {
|
863
|
+
fontFamily: 'monospace',
|
864
|
+
size: bbox.minDimension / 20,
|
865
|
+
renderingStyle: { fill: math_1.Color4.red },
|
866
|
+
};
|
867
|
+
renderer.drawText(`Depth: ${depth}`, math_1.Mat33.translation(bbox.bottomLeft), textStyle);
|
868
|
+
}
|
799
869
|
// Render debug information for children
|
800
870
|
for (const child of this.children) {
|
801
871
|
child.renderDebugBoundingBoxes(renderer, visibleRect, depth + 1);
|
802
872
|
}
|
803
873
|
}
|
874
|
+
checkRep(depth = 0) {
|
875
|
+
// Slow -- disabld by default
|
876
|
+
if (debugMode) {
|
877
|
+
if (this.parent && !this.parent.children.includes(this)) {
|
878
|
+
throw new Error(`Parent does not have this node as a child. (depth: ${depth})`);
|
879
|
+
}
|
880
|
+
let expectedBBox = null;
|
881
|
+
const seenChildren = new Set();
|
882
|
+
for (const child of this.children) {
|
883
|
+
expectedBBox ??= child.getBBox();
|
884
|
+
expectedBBox = expectedBBox.union(child.getBBox());
|
885
|
+
if (child.parent !== this) {
|
886
|
+
throw new Error(`Child with bbox ${child.getBBox()} and ${child.children.length} has wrong parent (was ${child.parent}).`);
|
887
|
+
}
|
888
|
+
// Children should only be present once
|
889
|
+
if (seenChildren.has(child)) {
|
890
|
+
throw new Error(`Child ${child} is present twice or more in its parent's child list`);
|
891
|
+
}
|
892
|
+
seenChildren.add(child);
|
893
|
+
}
|
894
|
+
const tolerance = this.bbox.minDimension / 100;
|
895
|
+
if (expectedBBox && !this.bbox.eq(expectedBBox, tolerance)) {
|
896
|
+
throw new Error(`Wrong bounding box ${expectedBBox} \\neq ${this.bbox} (depth: ${depth})`);
|
897
|
+
}
|
898
|
+
}
|
899
|
+
}
|
804
900
|
}
|
805
901
|
exports.ImageNode = ImageNode;
|
806
902
|
ImageNode.idCounter = 0;
|
@@ -141,24 +141,29 @@ class RenderingCacheNode {
|
|
141
141
|
|| items.length === 0) {
|
142
142
|
return;
|
143
143
|
}
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
const
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
144
|
+
// Divide [items] until nodes are smaller than this, or are leaves.
|
145
|
+
const divideUntilSmallerThanThis = (itemsToDivide) => {
|
146
|
+
const newItems = [];
|
147
|
+
for (const item of itemsToDivide) {
|
148
|
+
const bbox = item.getBBox();
|
149
|
+
if (!bbox.intersects(this.region)) {
|
150
|
+
continue;
|
151
|
+
}
|
152
|
+
if (bbox.maxDimension >= this.region.maxDimension) {
|
153
|
+
newItems.push(...item.getChildrenOrSelfIntersectingRegion(this.region));
|
154
|
+
}
|
155
|
+
else {
|
156
|
+
newItems.push(item);
|
157
|
+
}
|
156
158
|
}
|
157
|
-
|
158
|
-
|
159
|
+
return newItems;
|
160
|
+
};
|
161
|
+
items = divideUntilSmallerThanThis(items);
|
159
162
|
// Can we cache at all?
|
160
163
|
if (!this.cacheState.props.isOfCorrectType(screenRenderer)) {
|
161
|
-
|
164
|
+
for (const item of items) {
|
165
|
+
item.render(screenRenderer, viewport.visibleRect);
|
166
|
+
}
|
162
167
|
return;
|
163
168
|
}
|
164
169
|
if (this.cacheState.debugMode) {
|
@@ -69,12 +69,12 @@ class CanvasRenderer extends AbstractRenderer_1.default {
|
|
69
69
|
setDraftMode(draftMode) {
|
70
70
|
if (draftMode) {
|
71
71
|
this.minSquareCurveApproxDist = 9;
|
72
|
-
this.minRenderSizeBothDimens =
|
73
|
-
this.minRenderSizeAnyDimen = 0.
|
72
|
+
this.minRenderSizeBothDimens = 1;
|
73
|
+
this.minRenderSizeAnyDimen = 0.1;
|
74
74
|
}
|
75
75
|
else {
|
76
76
|
this.minSquareCurveApproxDist = 0.5;
|
77
|
-
this.minRenderSizeBothDimens = 0.
|
77
|
+
this.minRenderSizeBothDimens = 0.1;
|
78
78
|
this.minRenderSizeAnyDimen = 1e-6;
|
79
79
|
}
|
80
80
|
}
|
@@ -243,7 +243,7 @@ class CanvasRenderer extends AbstractRenderer_1.default {
|
|
243
243
|
// @internal
|
244
244
|
isTooSmallToRender(rect) {
|
245
245
|
// Should we ignore all objects within this object's bbox?
|
246
|
-
const diagonal = rect.size.times(this.
|
246
|
+
const diagonal = rect.size.times(this.getSizeOfCanvasPixelOnScreen());
|
247
247
|
const bothDimenMinSize = this.minRenderSizeBothDimens;
|
248
248
|
const bothTooSmall = Math.abs(diagonal.x) < bothDimenMinSize && Math.abs(diagonal.y) < bothDimenMinSize;
|
249
249
|
const anyDimenMinSize = this.minRenderSizeAnyDimen;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
/** Returns the first node or element with `textContent` matching `expectedText`. */
|
4
|
+
const findNodeWithText = (expectedText, parent) => {
|
5
|
+
if (parent.textContent === expectedText) {
|
6
|
+
return parent;
|
7
|
+
}
|
8
|
+
for (const child of parent.childNodes) {
|
9
|
+
const results = findNodeWithText(expectedText, child);
|
10
|
+
if (results) {
|
11
|
+
return results;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
return null;
|
15
|
+
};
|
16
|
+
exports.default = findNodeWithText;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
/** Returns the first ancestor of the given node that is an HTMLElement */
|
4
|
+
const firstElementAncestorOfNode = (node) => {
|
5
|
+
if (node instanceof HTMLElement) {
|
6
|
+
return node;
|
7
|
+
}
|
8
|
+
else if (node?.parentNode) {
|
9
|
+
return firstElementAncestorOfNode(node.parentNode);
|
10
|
+
}
|
11
|
+
return null;
|
12
|
+
};
|
13
|
+
exports.default = firstElementAncestorOfNode;
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const inputEvents_1 = require("../inputEvents");
|
4
|
+
const sendKeyPressRelease = (editor, key) => {
|
5
|
+
editor.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyPressEvent, key);
|
6
|
+
editor.sendKeyboardEvent(inputEvents_1.InputEvtType.KeyUpEvent, key);
|
7
|
+
};
|
8
|
+
exports.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,9 +1,32 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
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
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
19
|
+
if (mod && mod.__esModule) return mod;
|
20
|
+
var result = {};
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
22
|
+
__setModuleDefault(result, mod);
|
23
|
+
return result;
|
24
|
+
};
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
27
|
};
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
const Pointer_1 =
|
29
|
+
const Pointer_1 = __importStar(require("../Pointer"));
|
7
30
|
const inputEvents_1 = require("../inputEvents");
|
8
31
|
const getUniquePointerId_1 = __importDefault(require("./getUniquePointerId"));
|
9
32
|
/**
|
@@ -12,9 +35,9 @@ const getUniquePointerId_1 = __importDefault(require("./getUniquePointerId"));
|
|
12
35
|
*
|
13
36
|
* @see {@link sendTouchEvent}
|
14
37
|
*/
|
15
|
-
const sendPenEvent = (editor, eventType, point, allPointers) => {
|
38
|
+
const sendPenEvent = (editor, eventType, point, allPointers, deviceType = Pointer_1.PointerDevice.Pen) => {
|
16
39
|
const id = (0, getUniquePointerId_1.default)(allPointers ?? []);
|
17
|
-
const mainPointer = Pointer_1.default.ofCanvasPoint(point, eventType !== inputEvents_1.InputEvtType.PointerUpEvt, editor.viewport, id);
|
40
|
+
const mainPointer = Pointer_1.default.ofCanvasPoint(point, eventType !== inputEvents_1.InputEvtType.PointerUpEvt, editor.viewport, id, deviceType);
|
18
41
|
editor.toolController.dispatchInputEvent({
|
19
42
|
kind: eventType,
|
20
43
|
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,8 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
|
|
56
57
|
strokeAutocorrect: string;
|
57
58
|
errorImageHasZeroSize: string;
|
58
59
|
describeTheImage: string;
|
60
|
+
fileInput__loading: string;
|
61
|
+
fileInput__andNMoreFiles: (count: number) => string;
|
59
62
|
penDropdown__baseHelpText: string;
|
60
63
|
penDropdown__colorHelpText: string;
|
61
64
|
penDropdown__thicknessHelpText: string;
|
@@ -17,6 +17,7 @@ exports.defaultToolbarLocalization = {
|
|
17
17
|
chooseFile: 'Choose file',
|
18
18
|
dragAndDropHereOrBrowse: 'Drag and drop here\nor\n{{browse}}',
|
19
19
|
submit: 'Submit',
|
20
|
+
addAll: 'Add all',
|
20
21
|
cancel: 'Cancel',
|
21
22
|
resetView: 'Reset view',
|
22
23
|
thicknessLabel: 'Thickness',
|
@@ -60,6 +61,8 @@ exports.defaultToolbarLocalization = {
|
|
60
61
|
paste: 'Paste',
|
61
62
|
errorImageHasZeroSize: 'Error: Image has zero size',
|
62
63
|
describeTheImage: 'Image description',
|
64
|
+
fileInput__loading: 'Loading...',
|
65
|
+
fileInput__andNMoreFiles: (n) => `(...${n} more)`,
|
63
66
|
// Help text
|
64
67
|
penDropdown__baseHelpText: 'This tool draws shapes or freehand lines.',
|
65
68
|
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;
|
@@ -0,0 +1,23 @@
|
|
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
|
+
isLarge(): boolean;
|
15
|
+
getBase64Url(): string;
|
16
|
+
getAltText(): string;
|
17
|
+
setAltText(text: string): void;
|
18
|
+
static fromSrcAndPreview(initialBase64Src: string, preview: HTMLImageElement, onUrlUpdate: () => void): ImageWrapper;
|
19
|
+
static fromRenderable(renderable: RenderableImage, onUrlUpdate: () => void): {
|
20
|
+
wrapper: ImageWrapper;
|
21
|
+
preview: HTMLImageElement;
|
22
|
+
};
|
23
|
+
}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ImageWrapper = void 0;
|
4
|
+
/** Handles filtering and other operations on an image. */
|
5
|
+
class ImageWrapper {
|
6
|
+
constructor(imageBase64Url, preview, onUrlUpdate) {
|
7
|
+
this.imageBase64Url = imageBase64Url;
|
8
|
+
this.preview = preview;
|
9
|
+
this.onUrlUpdate = onUrlUpdate;
|
10
|
+
this.originalSrc = imageBase64Url;
|
11
|
+
preview.src = imageBase64Url;
|
12
|
+
}
|
13
|
+
updateImageData(base64DataUrl) {
|
14
|
+
this.preview.src = base64DataUrl;
|
15
|
+
this.imageBase64Url = base64DataUrl;
|
16
|
+
this.onUrlUpdate();
|
17
|
+
}
|
18
|
+
decreaseSize(resizeFactor = 3 / 4) {
|
19
|
+
const canvas = document.createElement('canvas');
|
20
|
+
canvas.width = this.preview.naturalWidth * resizeFactor;
|
21
|
+
canvas.height = this.preview.naturalHeight * resizeFactor;
|
22
|
+
const ctx = canvas.getContext('2d');
|
23
|
+
ctx?.drawImage(this.preview, 0, 0, canvas.width, canvas.height);
|
24
|
+
// JPEG can be much smaller than PNG for the same image size. Prefer it if
|
25
|
+
// the image is already a JPEG.
|
26
|
+
const format = this.originalSrc?.startsWith('data:image/jpeg;') ? 'image/jpeg' : 'image/png';
|
27
|
+
this.updateImageData(canvas.toDataURL(format));
|
28
|
+
}
|
29
|
+
reset() {
|
30
|
+
this.updateImageData(this.originalSrc);
|
31
|
+
}
|
32
|
+
isChanged() {
|
33
|
+
return this.imageBase64Url !== this.originalSrc;
|
34
|
+
}
|
35
|
+
// Returns true if the current image is large enough to display a "decrease size"
|
36
|
+
// option.
|
37
|
+
isLarge() {
|
38
|
+
const largeImageThreshold = 0.12 * 1024 * 1024; // 0.12 MiB
|
39
|
+
return this.getBase64Url().length > largeImageThreshold;
|
40
|
+
}
|
41
|
+
getBase64Url() {
|
42
|
+
return this.imageBase64Url;
|
43
|
+
}
|
44
|
+
getAltText() {
|
45
|
+
return this.altText;
|
46
|
+
}
|
47
|
+
setAltText(text) {
|
48
|
+
this.altText = text;
|
49
|
+
this.preview.alt = text;
|
50
|
+
}
|
51
|
+
static fromSrcAndPreview(initialBase64Src, preview, onUrlUpdate) {
|
52
|
+
return new ImageWrapper(initialBase64Src, preview, onUrlUpdate);
|
53
|
+
}
|
54
|
+
static fromRenderable(renderable, onUrlUpdate) {
|
55
|
+
const preview = new Image();
|
56
|
+
preview.src = renderable.base64Url;
|
57
|
+
const result = new ImageWrapper(renderable.base64Url, preview, onUrlUpdate);
|
58
|
+
const altText = renderable.label ?? renderable.image.getAttribute('alt');
|
59
|
+
if (altText) {
|
60
|
+
result.setAltText(altText);
|
61
|
+
}
|
62
|
+
return { wrapper: result, preview };
|
63
|
+
}
|
64
|
+
}
|
65
|
+
exports.ImageWrapper = ImageWrapper;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
const fileToBase64Url_1 = __importDefault(require("../../../util/fileToBase64Url"));
|
7
|
+
const math_1 = require("@js-draw/math");
|
8
|
+
const fileToImages = async (imageFile) => {
|
9
|
+
const result = [];
|
10
|
+
const imageElement = new Image();
|
11
|
+
const base64Url = await (0, fileToBase64Url_1.default)(imageFile);
|
12
|
+
if (base64Url) {
|
13
|
+
result.push({
|
14
|
+
image: imageElement,
|
15
|
+
base64Url: base64Url,
|
16
|
+
transform: math_1.Mat33.identity,
|
17
|
+
});
|
18
|
+
}
|
19
|
+
return result;
|
20
|
+
};
|
21
|
+
exports.default = fileToImages;
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import Editor from '../../../Editor';
|
2
|
+
import { ToolbarLocalization } from '../../localization';
|
3
|
+
import BaseWidget from '../BaseWidget';
|
4
|
+
/**
|
5
|
+
* Provides a widget that allows inserting or modifying raster images.
|
6
|
+
*
|
7
|
+
* It's possible to customize the file picker used by this widget through {@link EditorSettings.image}.
|
8
|
+
*
|
9
|
+
* @example
|
10
|
+
* ```ts,runnable
|
11
|
+
* import { Editor, makeEdgeToolbar, InsertImageWidget } from 'js-draw';
|
12
|
+
*
|
13
|
+
* const editor = new Editor(document.body);
|
14
|
+
* const toolbar = makeEdgeToolbar(editor);
|
15
|
+
*
|
16
|
+
* toolbar.addWidget(new InsertImageWidget(editor));
|
17
|
+
* ```
|
18
|
+
*/
|
19
|
+
export default class InsertImageWidget extends BaseWidget {
|
20
|
+
private images;
|
21
|
+
private imagesPreview;
|
22
|
+
private selectedFiles;
|
23
|
+
private imageAltTextInput;
|
24
|
+
private statusView;
|
25
|
+
private submitButton;
|
26
|
+
constructor(editor: Editor, localization?: ToolbarLocalization);
|
27
|
+
protected getTitle(): string;
|
28
|
+
protected createIcon(): Element | null;
|
29
|
+
protected setDropdownVisible(visible: boolean): void;
|
30
|
+
protected handleClick(): void;
|
31
|
+
private static nextInputId;
|
32
|
+
protected fillDropdown(dropdown: HTMLElement): boolean;
|
33
|
+
private onImageDataUpdate;
|
34
|
+
private hideDialog;
|
35
|
+
private updateImageSizeDisplay;
|
36
|
+
private updateInputs;
|
37
|
+
}
|