js-draw 1.4.0 → 1.5.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.
- package/README.md +1 -1
- package/dist/Editor.css +11 -10
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/components/BackgroundComponent.js +12 -6
- package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +2 -2
- package/dist/cjs/components/SVGGlobalAttributesObject.js +10 -14
- package/dist/cjs/image/export/adjustExportedSVGSize.d.ts +6 -0
- package/dist/cjs/image/export/{setExportedSVGSize.js → adjustExportedSVGSize.js} +4 -7
- package/dist/cjs/image/export/editorImageToSVG.d.ts +1 -1
- package/dist/cjs/image/export/editorImageToSVG.js +22 -8
- package/dist/cjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
- package/dist/cjs/rendering/renderers/AbstractRenderer.js +8 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +28 -1
- package/dist/cjs/rendering/renderers/SVGRenderer.js +58 -7
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +9 -3
- package/dist/cjs/toolbar/AbstractToolbar.js +11 -4
- package/dist/cjs/toolbar/AbstractToolbar.test.d.ts +1 -0
- package/dist/cjs/toolbar/EdgeToolbar.js +2 -2
- package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +2 -1
- package/dist/cjs/toolbar/widgets/SaveActionWidget.js +6 -2
- package/dist/cjs/tools/SelectionTool/Selection.js +4 -2
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/components/BackgroundComponent.mjs +12 -6
- package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +2 -2
- package/dist/mjs/components/SVGGlobalAttributesObject.mjs +10 -14
- package/dist/mjs/image/export/adjustExportedSVGSize.d.ts +6 -0
- package/dist/mjs/image/export/{setExportedSVGSize.mjs → adjustExportedSVGSize.mjs} +4 -7
- package/dist/mjs/image/export/editorImageToSVG.d.ts +1 -1
- package/dist/mjs/image/export/editorImageToSVG.mjs +23 -9
- package/dist/mjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
- package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +8 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +28 -1
- package/dist/mjs/rendering/renderers/SVGRenderer.mjs +58 -7
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +9 -3
- package/dist/mjs/toolbar/AbstractToolbar.mjs +11 -4
- package/dist/mjs/toolbar/AbstractToolbar.test.d.ts +1 -0
- package/dist/mjs/toolbar/EdgeToolbar.mjs +2 -2
- package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +2 -1
- package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +6 -2
- package/dist/mjs/tools/SelectionTool/Selection.mjs +4 -2
- package/dist/mjs/version.mjs +1 -1
- package/package.json +2 -2
- package/src/toolbar/EdgeToolbar.scss +7 -4
- package/src/tools/SelectionTool/SelectionTool.scss +2 -2
- package/dist/cjs/image/export/setExportedSVGSize.d.ts +0 -6
- package/dist/mjs/image/export/setExportedSVGSize.d.ts +0 -6
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -235,11 +235,11 @@ class EdgeToolbar extends AbstractToolbar_1.default {
|
|
235
235
|
widget.removeCSSClassFromContainer('label-right');
|
236
236
|
if (tags.includes(BaseWidget_1.ToolbarWidgetTag.Save)) {
|
237
237
|
widget.addCSSClassToContainer('label-inline');
|
238
|
-
widget.addCSSClassToContainer('label-
|
238
|
+
widget.addCSSClassToContainer('label-left');
|
239
239
|
}
|
240
240
|
if (tags.includes(BaseWidget_1.ToolbarWidgetTag.Exit)) {
|
241
241
|
widget.addCSSClassToContainer('label-inline');
|
242
|
-
widget.addCSSClassToContainer('label-
|
242
|
+
widget.addCSSClassToContainer('label-right');
|
243
243
|
}
|
244
244
|
}
|
245
245
|
addWidgetInternal(widget) {
|
@@ -2,8 +2,9 @@ import { KeyPressEvent } from '../../inputEvents';
|
|
2
2
|
import Editor from '../../Editor';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import ActionButtonWidget from './ActionButtonWidget';
|
5
|
+
import { ActionButtonIcon } from '../types';
|
5
6
|
declare class SaveActionWidget extends ActionButtonWidget {
|
6
|
-
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void);
|
7
|
+
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>);
|
7
8
|
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
8
9
|
protected onKeyPress(event: KeyPressEvent): boolean;
|
9
10
|
mustBeInToplevelMenu(): boolean;
|
@@ -7,8 +7,12 @@ const ActionButtonWidget_1 = __importDefault(require("./ActionButtonWidget"));
|
|
7
7
|
const BaseWidget_1 = require("./BaseWidget");
|
8
8
|
const keybindings_1 = require("./keybindings");
|
9
9
|
class SaveActionWidget extends ActionButtonWidget_1.default {
|
10
|
-
constructor(editor, localization, saveCallback) {
|
11
|
-
super(editor, 'save-button',
|
10
|
+
constructor(editor, localization, saveCallback, labelOverride = {}) {
|
11
|
+
super(editor, 'save-button',
|
12
|
+
// Creates an icon
|
13
|
+
() => {
|
14
|
+
return labelOverride.icon ?? editor.icons.makeSaveIcon();
|
15
|
+
}, labelOverride.label ?? localization.save, saveCallback);
|
12
16
|
this.setTags([BaseWidget_1.ToolbarWidgetTag.Save]);
|
13
17
|
}
|
14
18
|
shouldAutoDisableInReadOnlyEditor() {
|
@@ -157,8 +157,10 @@ class Selection {
|
|
157
157
|
// Reset for the next drag
|
158
158
|
this.originalRegion = this.originalRegion.transformedBoundingBox(this.transform);
|
159
159
|
this.transform = math_1.Mat33.identity;
|
160
|
-
// Make the commands undo-able
|
161
|
-
|
160
|
+
// Make the commands undo-able, but only if the transform is non-empty.
|
161
|
+
if (!fullTransform.eq(math_1.Mat33.identity)) {
|
162
|
+
await this.editor.dispatch(new Selection.ApplyTransformationCommand(this, selectedElems, fullTransform));
|
163
|
+
}
|
162
164
|
// Clear renderings of any in-progress transformations
|
163
165
|
const wetInkRenderer = this.editor.display.getWetInkRenderer();
|
164
166
|
wetInkRenderer.clear();
|
package/dist/cjs/version.js
CHANGED
@@ -190,6 +190,14 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
190
190
|
if (this.backgroundType === BackgroundType.None) {
|
191
191
|
return;
|
192
192
|
}
|
193
|
+
// If visibleRect is null, components should render everything.
|
194
|
+
// In that case, a full render is being done.
|
195
|
+
const mustRender = !visibleRect;
|
196
|
+
// If this.fillsScreen, the visibleRect needs to be known.
|
197
|
+
// Use the screen rect.
|
198
|
+
if (this.fillsScreen) {
|
199
|
+
visibleRect ??= canvas.getVisibleRect();
|
200
|
+
}
|
193
201
|
const clip = this.backgroundType === BackgroundType.Grid;
|
194
202
|
const contentBBox = this.getFullBoundingBox(visibleRect);
|
195
203
|
canvas.startObject(contentBBox, clip);
|
@@ -197,13 +205,11 @@ export default class BackgroundComponent extends AbstractComponent {
|
|
197
205
|
// If the rectangle for this region contains the visible rect,
|
198
206
|
// we can fill the entire visible rectangle (which may be more efficient than
|
199
207
|
// filling the entire region for this.)
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
canvas.fillRect(intersection, this.mainColor);
|
204
|
-
}
|
208
|
+
const intersection = visibleRect?.intersection(contentBBox);
|
209
|
+
if (intersection) {
|
210
|
+
canvas.fillRect(intersection, this.mainColor);
|
205
211
|
}
|
206
|
-
else {
|
212
|
+
else if (mustRender) {
|
207
213
|
canvas.fillRect(contentBBox, this.mainColor);
|
208
214
|
}
|
209
215
|
}
|
@@ -4,8 +4,8 @@ import AbstractComponent, { ComponentSizingMode } from './AbstractComponent';
|
|
4
4
|
import { ImageComponentLocalization } from './localization';
|
5
5
|
type GlobalAttrsList = Array<[string, string | null]>;
|
6
6
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
7
|
-
private readonly attrs;
|
8
7
|
protected contentBBox: Rect2;
|
8
|
+
private readonly attrs;
|
9
9
|
constructor(attrs: GlobalAttrsList);
|
10
10
|
render(canvas: AbstractRenderer, _visibleRect?: Rect2): void;
|
11
11
|
intersects(_lineSegment: LineSegment2): boolean;
|
@@ -15,6 +15,6 @@ export default class SVGGlobalAttributesObject extends AbstractComponent {
|
|
15
15
|
protected createClone(): SVGGlobalAttributesObject;
|
16
16
|
description(localization: ImageComponentLocalization): string;
|
17
17
|
protected serializeToJSON(): string | null;
|
18
|
-
static deserializeFromString(
|
18
|
+
static deserializeFromString(_data: string): AbstractComponent;
|
19
19
|
}
|
20
20
|
export {};
|
@@ -10,10 +10,16 @@ import AbstractComponent, { ComponentSizingMode } from './AbstractComponent.mj
|
|
10
10
|
const componentKind = 'svg-global-attributes';
|
11
11
|
// Stores global SVG attributes (e.g. namespace identifiers.)
|
12
12
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
13
|
+
// Does not modify `attrs`
|
13
14
|
constructor(attrs) {
|
14
15
|
super(componentKind);
|
15
|
-
this.attrs = attrs;
|
16
16
|
this.contentBBox = Rect2.empty;
|
17
|
+
// Already stored/managed in `editor.image`.
|
18
|
+
const attrsManagedByRenderer = ['viewBox', 'width', 'height'];
|
19
|
+
// Only store attributes that aren't managed by other parts of the app.
|
20
|
+
this.attrs = attrs.filter(([attr, _value]) => {
|
21
|
+
return !attrsManagedByRenderer.includes(attr);
|
22
|
+
});
|
17
23
|
}
|
18
24
|
render(canvas, _visibleRect) {
|
19
25
|
if (!(canvas instanceof SVGRenderer)) {
|
@@ -47,19 +53,9 @@ export default class SVGGlobalAttributesObject extends AbstractComponent {
|
|
47
53
|
serializeToJSON() {
|
48
54
|
return JSON.stringify(this.attrs);
|
49
55
|
}
|
50
|
-
static deserializeFromString(
|
51
|
-
|
52
|
-
|
53
|
-
const numericAndSpaceContentExp = /^[ \t\n0-9.-eE]+$/;
|
54
|
-
// Don't deserialize all attributes, just those that should be safe.
|
55
|
-
for (const [key, val] of json) {
|
56
|
-
if (key === 'viewBox' || key === 'width' || key === 'height') {
|
57
|
-
if (val && numericAndSpaceContentExp.exec(val)) {
|
58
|
-
attrs.push([key, val]);
|
59
|
-
}
|
60
|
-
}
|
61
|
-
}
|
62
|
-
return new SVGGlobalAttributesObject(attrs);
|
56
|
+
static deserializeFromString(_data) {
|
57
|
+
// To be safe, don't deserialize any attributes
|
58
|
+
return new SVGGlobalAttributesObject([]);
|
63
59
|
}
|
64
60
|
}
|
65
61
|
AbstractComponent.registerComponent(componentKind, SVGGlobalAttributesObject.deserializeFromString);
|
@@ -1,12 +1,9 @@
|
|
1
1
|
import { toRoundedString } from '@js-draw/math';
|
2
2
|
// @internal
|
3
|
-
const
|
4
|
-
// Just show the main region
|
5
|
-
const rect = viewport.visibleRect;
|
6
|
-
svg.setAttribute('viewBox', [rect.x, rect.y, rect.w, rect.h].map(part => toRoundedString(part)).join(' '));
|
3
|
+
const adjustExportedSVGSize = (svg, exportRect, options) => {
|
7
4
|
// Adjust the width/height as necessary
|
8
|
-
let width =
|
9
|
-
let height =
|
5
|
+
let width = exportRect.w;
|
6
|
+
let height = exportRect.h;
|
10
7
|
if (options?.minDimension && width < options.minDimension) {
|
11
8
|
const newWidth = options.minDimension;
|
12
9
|
height *= newWidth / (width || 1);
|
@@ -20,4 +17,4 @@ const setExportedSVGSize = (svg, viewport, options) => {
|
|
20
17
|
svg.setAttribute('width', toRoundedString(width));
|
21
18
|
svg.setAttribute('height', toRoundedString(height));
|
22
19
|
};
|
23
|
-
export default
|
20
|
+
export default adjustExportedSVGSize;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import EditorImage, { PreRenderComponentCallback } from '../EditorImage';
|
2
|
-
import { SVGSizingOptions } from './
|
2
|
+
import { SVGSizingOptions } from './adjustExportedSVGSize';
|
3
3
|
export interface SVGExportOptions extends SVGSizingOptions {
|
4
4
|
sanitize?: boolean;
|
5
5
|
minDimension?: number;
|
@@ -1,25 +1,39 @@
|
|
1
|
-
import {
|
1
|
+
import { Rect2 } from '@js-draw/math';
|
2
2
|
import SVGRenderer from '../../rendering/renderers/SVGRenderer.mjs';
|
3
3
|
import { svgLoaderAutoresizeClassName } from '../../SVGLoader.mjs';
|
4
|
-
import
|
4
|
+
import adjustExportedSVGSize from './adjustExportedSVGSize.mjs';
|
5
5
|
const toSVGInternal = (image, renderFunction, options) => {
|
6
6
|
const importExportViewport = image.getImportExportViewport().getTemporaryClone();
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
// If the rectangle has zero width or height, its size can't be increased
|
8
|
+
// -- set its size to the minimum.
|
9
|
+
if (options?.minDimension) {
|
10
|
+
const originalRect = importExportViewport.visibleRect;
|
11
|
+
let rect = originalRect;
|
12
|
+
if (rect.w <= 0) {
|
13
|
+
rect = new Rect2(rect.x, rect.y, options.minDimension, rect.h);
|
14
|
+
}
|
15
|
+
if (rect.h <= 0) {
|
16
|
+
rect = new Rect2(rect.x, rect.y, rect.w, options.minDimension);
|
17
|
+
}
|
18
|
+
if (!rect.eq(originalRect)) {
|
19
|
+
importExportViewport.updateScreenSize(rect.size);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
const { element: result, renderer } = SVGRenderer.fromViewport(importExportViewport, {
|
23
|
+
sanitize: options.sanitize ?? false,
|
24
|
+
useViewBoxForPositioning: true,
|
25
|
+
});
|
12
26
|
// Use a callback rather than async/await to allow this function to create
|
13
27
|
// both sync and async render functions
|
14
28
|
renderFunction(renderer, () => {
|
15
|
-
importExportViewport.resetTransform(origTransform);
|
16
29
|
if (image.getAutoresizeEnabled()) {
|
17
30
|
result.classList.add(svgLoaderAutoresizeClassName);
|
18
31
|
}
|
19
32
|
else {
|
20
33
|
result.classList.remove(svgLoaderAutoresizeClassName);
|
21
34
|
}
|
22
|
-
|
35
|
+
const exportRect = importExportViewport.visibleRect;
|
36
|
+
adjustExportedSVGSize(result, exportRect, options);
|
23
37
|
return result;
|
24
38
|
});
|
25
39
|
return result;
|
@@ -158,4 +158,12 @@ export default class AbstractRenderer {
|
|
158
158
|
getSizeOfCanvasPixelOnScreen() {
|
159
159
|
return this.getCanvasToScreenTransform().transformVec3(Vec2.unitX).length();
|
160
160
|
}
|
161
|
+
// Returns the region in canvas space that is visible within the viewport this
|
162
|
+
// canvas is rendering to.
|
163
|
+
//
|
164
|
+
// Note that in some cases this might not be the same as the `visibleRect` given
|
165
|
+
// to components in their `render` method.
|
166
|
+
getVisibleRect() {
|
167
|
+
return this.viewport.visibleRect;
|
168
|
+
}
|
161
169
|
}
|
@@ -6,6 +6,15 @@ import TextRenderingStyle from '../TextRenderingStyle';
|
|
6
6
|
import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
|
7
7
|
import RenderablePathSpec from '../RenderablePathSpec';
|
8
8
|
export declare const renderedStylesheetId = "js-draw-style-sheet";
|
9
|
+
type FromViewportOptions = {
|
10
|
+
sanitize?: boolean;
|
11
|
+
/**
|
12
|
+
* Rather than having the top left of the `viewBox` set to (0, 0),
|
13
|
+
* if `useViewBoxForPositioning` is `true`, the `viewBox`'s top left
|
14
|
+
* is based on the top left of the rendering viewport's `visibleRect`.
|
15
|
+
*/
|
16
|
+
useViewBoxForPositioning?: boolean;
|
17
|
+
};
|
9
18
|
/**
|
10
19
|
* Renders onto an `SVGElement`.
|
11
20
|
*
|
@@ -50,8 +59,26 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
50
59
|
drawPoints(...points: Point2[]): void;
|
51
60
|
drawSVGElem(elem: SVGElement): void;
|
52
61
|
isTooSmallToRender(_rect: Rect2): boolean;
|
53
|
-
|
62
|
+
private visibleRectOverride;
|
63
|
+
/**
|
64
|
+
* Overrides the visible region returned by `getVisibleRect`.
|
65
|
+
*
|
66
|
+
* This is useful when the `viewport`'s transform has been modified,
|
67
|
+
* for example, to compensate for storing part of the image's
|
68
|
+
* transformation in an SVG property.
|
69
|
+
*/
|
70
|
+
private overrideVisibleRect;
|
71
|
+
getVisibleRect(): Rect2;
|
72
|
+
/**
|
73
|
+
* Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
|
74
|
+
* and other metadata attributes set for the given `Viewport`.
|
75
|
+
*
|
76
|
+
* If `options` is a `boolean`, it is interpreted as whether to sanitize (not add unknown
|
77
|
+
* SVG entities to) the output.
|
78
|
+
*/
|
79
|
+
static fromViewport(viewport: Viewport, options?: FromViewportOptions | boolean): {
|
54
80
|
element: SVGSVGElement;
|
55
81
|
renderer: SVGRenderer;
|
56
82
|
};
|
57
83
|
}
|
84
|
+
export {};
|
@@ -36,6 +36,7 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
36
36
|
this.textContainer = null;
|
37
37
|
this.textContainerTransform = null;
|
38
38
|
this.textParentStyle = defaultTextStyle;
|
39
|
+
this.visibleRectOverride = null;
|
39
40
|
this.clear();
|
40
41
|
this.addStyleSheet();
|
41
42
|
}
|
@@ -338,20 +339,70 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
338
339
|
isTooSmallToRender(_rect) {
|
339
340
|
return false;
|
340
341
|
}
|
341
|
-
|
342
|
-
|
342
|
+
/**
|
343
|
+
* Overrides the visible region returned by `getVisibleRect`.
|
344
|
+
*
|
345
|
+
* This is useful when the `viewport`'s transform has been modified,
|
346
|
+
* for example, to compensate for storing part of the image's
|
347
|
+
* transformation in an SVG property.
|
348
|
+
*/
|
349
|
+
overrideVisibleRect(newRect) {
|
350
|
+
this.visibleRectOverride = newRect;
|
351
|
+
}
|
352
|
+
getVisibleRect() {
|
353
|
+
return this.visibleRectOverride ?? super.getVisibleRect();
|
354
|
+
}
|
355
|
+
/**
|
356
|
+
* Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
|
357
|
+
* and other metadata attributes set for the given `Viewport`.
|
358
|
+
*
|
359
|
+
* If `options` is a `boolean`, it is interpreted as whether to sanitize (not add unknown
|
360
|
+
* SVG entities to) the output.
|
361
|
+
*/
|
362
|
+
static fromViewport(viewport, options = true) {
|
363
|
+
let sanitize;
|
364
|
+
let useViewBoxForPositioning;
|
365
|
+
if (typeof options === 'boolean') {
|
366
|
+
sanitize = options;
|
367
|
+
useViewBoxForPositioning = false;
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
sanitize = options.sanitize ?? true;
|
371
|
+
useViewBoxForPositioning = options.useViewBoxForPositioning ?? false;
|
372
|
+
}
|
343
373
|
const svgNameSpace = 'http://www.w3.org/2000/svg';
|
344
374
|
const result = document.createElementNS(svgNameSpace, 'svg');
|
345
|
-
const
|
375
|
+
const screenRectSize = viewport.getScreenRectSize();
|
376
|
+
const visibleRect = viewport.visibleRect;
|
377
|
+
let viewBoxComponents;
|
378
|
+
if (useViewBoxForPositioning) {
|
379
|
+
const exportRect = viewport.visibleRect;
|
380
|
+
viewBoxComponents = [
|
381
|
+
exportRect.x, exportRect.y, exportRect.w, exportRect.h,
|
382
|
+
];
|
383
|
+
// Replace the viewport with a copy that has a modified transform.
|
384
|
+
// (Avoids modifying the original viewport).
|
385
|
+
viewport = viewport.getTemporaryClone();
|
386
|
+
// TODO: This currently discards any rotation information.
|
387
|
+
// Render with (0,0) at (0,0) -- the translation is handled by the viewBox.
|
388
|
+
viewport.resetTransform(Mat33.identity);
|
389
|
+
}
|
390
|
+
else {
|
391
|
+
viewBoxComponents = [0, 0, screenRectSize.x, screenRectSize.y];
|
392
|
+
}
|
346
393
|
// rect.x -> size of rect in x direction, rect.y -> size of rect in y direction.
|
347
|
-
result.setAttribute('viewBox',
|
348
|
-
result.setAttribute('width', toRoundedString(
|
349
|
-
result.setAttribute('height', toRoundedString(
|
394
|
+
result.setAttribute('viewBox', viewBoxComponents.map(part => toRoundedString(part)).join(' '));
|
395
|
+
result.setAttribute('width', toRoundedString(screenRectSize.x));
|
396
|
+
result.setAttribute('height', toRoundedString(screenRectSize.y));
|
350
397
|
// Ensure the image can be identified as an SVG if downloaded.
|
351
398
|
// See https://jwatt.org/svg/authoring/
|
352
399
|
result.setAttribute('version', '1.1');
|
353
400
|
result.setAttribute('baseProfile', 'full');
|
354
401
|
result.setAttribute('xmlns', svgNameSpace);
|
355
|
-
|
402
|
+
const renderer = new SVGRenderer(result, viewport, sanitize);
|
403
|
+
if (!visibleRect.eq(viewport.visibleRect)) {
|
404
|
+
renderer.overrideVisibleRect(visibleRect);
|
405
|
+
}
|
406
|
+
return { element: result, renderer };
|
356
407
|
}
|
357
408
|
}
|
@@ -124,24 +124,30 @@ export default abstract class AbstractToolbar {
|
|
124
124
|
* toolbar.addDefaults();
|
125
125
|
* toolbar.addSaveButton(() => alert('save clicked!'));
|
126
126
|
* ```
|
127
|
+
*
|
128
|
+
* `labelOverride` can optionally be used to change the `label` or `icon` of the button.
|
127
129
|
*/
|
128
|
-
addSaveButton(saveCallback: () => void): BaseWidget;
|
130
|
+
addSaveButton(saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>): BaseWidget;
|
129
131
|
/**
|
130
132
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
131
133
|
*
|
132
|
-
* **Note**: This is equivalent to
|
134
|
+
* **Note**: This is roughly equivalent to
|
133
135
|
* ```ts
|
134
136
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
135
137
|
* label: this.editor.localization.exit,
|
136
138
|
* icon: this.editor.icons.makeCloseIcon(),
|
139
|
+
*
|
140
|
+
* // labelOverride can be used to override label or icon.
|
141
|
+
* ...labelOverride,
|
137
142
|
* }, () => {
|
138
143
|
* exitCallback();
|
139
144
|
* });
|
140
145
|
* ```
|
146
|
+
* with some additional configuration.
|
141
147
|
*
|
142
148
|
* @final
|
143
149
|
*/
|
144
|
-
addExitButton(exitCallback: () => void): BaseWidget;
|
150
|
+
addExitButton(exitCallback: () => void, labelOverride?: Partial<ActionButtonIcon>): BaseWidget;
|
145
151
|
/**
|
146
152
|
* Adds undo and redo buttons that trigger the editor's built-in undo and redo
|
147
153
|
* functionality.
|
@@ -287,9 +287,11 @@ class AbstractToolbar {
|
|
287
287
|
* toolbar.addDefaults();
|
288
288
|
* toolbar.addSaveButton(() => alert('save clicked!'));
|
289
289
|
* ```
|
290
|
+
*
|
291
|
+
* `labelOverride` can optionally be used to change the `label` or `icon` of the button.
|
290
292
|
*/
|
291
|
-
addSaveButton(saveCallback) {
|
292
|
-
const widget = new SaveActionWidget(this.editor, this.localizationTable, saveCallback);
|
293
|
+
addSaveButton(saveCallback, labelOverride = {}) {
|
294
|
+
const widget = new SaveActionWidget(this.editor, this.localizationTable, saveCallback, labelOverride);
|
293
295
|
widget.setTags([ToolbarWidgetTag.Save]);
|
294
296
|
this.addWidget(widget);
|
295
297
|
return widget;
|
@@ -297,22 +299,27 @@ class AbstractToolbar {
|
|
297
299
|
/**
|
298
300
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
299
301
|
*
|
300
|
-
* **Note**: This is equivalent to
|
302
|
+
* **Note**: This is roughly equivalent to
|
301
303
|
* ```ts
|
302
304
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
303
305
|
* label: this.editor.localization.exit,
|
304
306
|
* icon: this.editor.icons.makeCloseIcon(),
|
307
|
+
*
|
308
|
+
* // labelOverride can be used to override label or icon.
|
309
|
+
* ...labelOverride,
|
305
310
|
* }, () => {
|
306
311
|
* exitCallback();
|
307
312
|
* });
|
308
313
|
* ```
|
314
|
+
* with some additional configuration.
|
309
315
|
*
|
310
316
|
* @final
|
311
317
|
*/
|
312
|
-
addExitButton(exitCallback) {
|
318
|
+
addExitButton(exitCallback, labelOverride = {}) {
|
313
319
|
return this.addTaggedActionButton([ToolbarWidgetTag.Exit], {
|
314
320
|
label: this.editor.localization.exit,
|
315
321
|
icon: this.editor.icons.makeCloseIcon(),
|
322
|
+
...labelOverride,
|
316
323
|
}, () => {
|
317
324
|
exitCallback();
|
318
325
|
}, {
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -228,11 +228,11 @@ export default class EdgeToolbar extends AbstractToolbar {
|
|
228
228
|
widget.removeCSSClassFromContainer('label-right');
|
229
229
|
if (tags.includes(ToolbarWidgetTag.Save)) {
|
230
230
|
widget.addCSSClassToContainer('label-inline');
|
231
|
-
widget.addCSSClassToContainer('label-
|
231
|
+
widget.addCSSClassToContainer('label-left');
|
232
232
|
}
|
233
233
|
if (tags.includes(ToolbarWidgetTag.Exit)) {
|
234
234
|
widget.addCSSClassToContainer('label-inline');
|
235
|
-
widget.addCSSClassToContainer('label-
|
235
|
+
widget.addCSSClassToContainer('label-right');
|
236
236
|
}
|
237
237
|
}
|
238
238
|
addWidgetInternal(widget) {
|
@@ -2,8 +2,9 @@ import { KeyPressEvent } from '../../inputEvents';
|
|
2
2
|
import Editor from '../../Editor';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import ActionButtonWidget from './ActionButtonWidget';
|
5
|
+
import { ActionButtonIcon } from '../types';
|
5
6
|
declare class SaveActionWidget extends ActionButtonWidget {
|
6
|
-
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void);
|
7
|
+
constructor(editor: Editor, localization: ToolbarLocalization, saveCallback: () => void, labelOverride?: Partial<ActionButtonIcon>);
|
7
8
|
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
8
9
|
protected onKeyPress(event: KeyPressEvent): boolean;
|
9
10
|
mustBeInToplevelMenu(): boolean;
|
@@ -2,8 +2,12 @@ import ActionButtonWidget from './ActionButtonWidget.mjs';
|
|
2
2
|
import { ToolbarWidgetTag } from './BaseWidget.mjs';
|
3
3
|
import { saveKeyboardShortcut } from './keybindings.mjs';
|
4
4
|
class SaveActionWidget extends ActionButtonWidget {
|
5
|
-
constructor(editor, localization, saveCallback) {
|
6
|
-
super(editor, 'save-button',
|
5
|
+
constructor(editor, localization, saveCallback, labelOverride = {}) {
|
6
|
+
super(editor, 'save-button',
|
7
|
+
// Creates an icon
|
8
|
+
() => {
|
9
|
+
return labelOverride.icon ?? editor.icons.makeSaveIcon();
|
10
|
+
}, labelOverride.label ?? localization.save, saveCallback);
|
7
11
|
this.setTags([ToolbarWidgetTag.Save]);
|
8
12
|
}
|
9
13
|
shouldAutoDisableInReadOnlyEditor() {
|
@@ -129,8 +129,10 @@ class Selection {
|
|
129
129
|
// Reset for the next drag
|
130
130
|
this.originalRegion = this.originalRegion.transformedBoundingBox(this.transform);
|
131
131
|
this.transform = Mat33.identity;
|
132
|
-
// Make the commands undo-able
|
133
|
-
|
132
|
+
// Make the commands undo-able, but only if the transform is non-empty.
|
133
|
+
if (!fullTransform.eq(Mat33.identity)) {
|
134
|
+
await this.editor.dispatch(new Selection.ApplyTransformationCommand(this, selectedElems, fullTransform));
|
135
|
+
}
|
134
136
|
// Clear renderings of any in-progress transformations
|
135
137
|
const wetInkRenderer = this.editor.display.getWetInkRenderer();
|
136
138
|
wetInkRenderer.clear();
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.5.0",
|
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",
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "c2252181b1ab0eb7047ad66590bca2eabd61681d"
|
90
90
|
}
|
@@ -60,7 +60,7 @@
|
|
60
60
|
@media (hover: hover) {
|
61
61
|
// Only show an animation when opening the label due to a hover --
|
62
62
|
// show the label immediately otherwise.
|
63
|
-
&:hover > #{$label-selector} {
|
63
|
+
&:hover:not(:focus-visible) > #{$label-selector} {
|
64
64
|
opacity: $label-visible-opacity;
|
65
65
|
animation: 1s $hover-active-animation;
|
66
66
|
}
|
@@ -69,7 +69,7 @@
|
|
69
69
|
// When the user is pressing/long-pressing the button
|
70
70
|
&:active > #{$label-selector} {
|
71
71
|
opacity: $label-visible-opacity;
|
72
|
-
animation:
|
72
|
+
animation: 1s $hover-active-animation;
|
73
73
|
}
|
74
74
|
|
75
75
|
$keyboard-hide-animation: 1.5s ease rehide-label;
|
@@ -265,7 +265,7 @@
|
|
265
265
|
--button-flex-direction: row-reverse;
|
266
266
|
|
267
267
|
> .toolbar-button > .toolbar-icon {
|
268
|
-
margin-left:
|
268
|
+
margin-left: 7px;
|
269
269
|
margin-right: 0;
|
270
270
|
}
|
271
271
|
|
@@ -284,8 +284,11 @@
|
|
284
284
|
|
285
285
|
> .toolbar-icon {
|
286
286
|
height: 100%;
|
287
|
-
margin-right:
|
287
|
+
margin-right: 7px;
|
288
288
|
margin-left: 0;
|
289
|
+
|
290
|
+
// Make smaller than the other icons
|
291
|
+
width: 22px;
|
289
292
|
}
|
290
293
|
}
|
291
294
|
}
|