js-draw 1.0.0 → 1.0.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.
- package/README.md +20 -6
- package/dist/bundle.js +1 -1
- package/dist/cjs/Editor.js +1 -1
- package/dist/cjs/Editor.loadFrom.test.d.ts +1 -0
- package/dist/cjs/Editor.test.d.ts +1 -0
- package/dist/cjs/Editor.toSVG.test.d.ts +1 -0
- package/dist/cjs/EditorImage.test.d.ts +1 -0
- package/dist/cjs/EventDispatcher.test.d.ts +1 -0
- package/dist/cjs/SVGLoader.test.d.ts +1 -0
- package/dist/cjs/UndoRedoHistory.test.d.ts +1 -0
- package/dist/cjs/commands/uniteCommands.test.d.ts +1 -0
- package/dist/cjs/components/AbstractComponent.transformBy.test.d.ts +1 -0
- package/dist/cjs/components/BackgroundComponent.test.d.ts +1 -0
- package/dist/cjs/components/Stroke.test.d.ts +1 -0
- package/dist/cjs/components/TextComponent.test.d.ts +1 -0
- package/dist/cjs/components/UnknownSVGObject.test.d.ts +1 -0
- package/dist/cjs/components/builders/FreehandLineBuilder.test.d.ts +1 -0
- package/dist/cjs/localizations/getLocalizationTable.test.d.ts +1 -0
- package/dist/cjs/rendering/RenderingStyle.test.d.ts +1 -0
- package/dist/cjs/rendering/caching/CacheRecord.test.d.ts +1 -0
- package/dist/cjs/rendering/caching/RenderingCache.test.d.ts +1 -0
- package/dist/cjs/rendering/renderers/DummyRenderer.test.d.ts +1 -0
- package/dist/cjs/rendering/renderers/TextOnlyRenderer.test.d.ts +1 -0
- package/dist/cjs/shortcuts/KeyBinding.test.d.ts +1 -0
- package/dist/cjs/shortcuts/KeyboardShortcutManager.test.d.ts +1 -0
- package/dist/cjs/toolbar/EdgeToolbar.test.d.ts +1 -0
- package/dist/cjs/tools/Eraser.test.d.ts +1 -0
- package/dist/cjs/tools/FindTool.test.d.ts +1 -0
- package/dist/cjs/tools/InputFilter/InputPipeline.test.d.ts +1 -0
- package/dist/cjs/tools/PanZoom.test.d.ts +1 -0
- package/dist/cjs/tools/Pen.test.d.ts +1 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.test.d.ts +1 -0
- package/dist/cjs/tools/UndoRedoShortcut.test.d.ts +1 -0
- package/dist/cjs/util/ReactiveValue.test.d.ts +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.test.d.ts +1 -0
- package/dist/mjs/Editor.loadFrom.test.d.ts +1 -0
- package/dist/mjs/Editor.mjs +1 -1
- package/dist/mjs/Editor.test.d.ts +1 -0
- package/dist/mjs/Editor.toSVG.test.d.ts +1 -0
- package/dist/mjs/EditorImage.test.d.ts +1 -0
- package/dist/mjs/EventDispatcher.test.d.ts +1 -0
- package/dist/mjs/SVGLoader.test.d.ts +1 -0
- package/dist/mjs/UndoRedoHistory.test.d.ts +1 -0
- package/dist/mjs/commands/uniteCommands.test.d.ts +1 -0
- package/dist/mjs/components/AbstractComponent.transformBy.test.d.ts +1 -0
- package/dist/mjs/components/BackgroundComponent.test.d.ts +1 -0
- package/dist/mjs/components/Stroke.test.d.ts +1 -0
- package/dist/mjs/components/TextComponent.test.d.ts +1 -0
- package/dist/mjs/components/UnknownSVGObject.test.d.ts +1 -0
- package/dist/mjs/components/builders/FreehandLineBuilder.test.d.ts +1 -0
- package/dist/mjs/localizations/getLocalizationTable.test.d.ts +1 -0
- package/dist/mjs/rendering/RenderingStyle.test.d.ts +1 -0
- package/dist/mjs/rendering/caching/CacheRecord.test.d.ts +1 -0
- package/dist/mjs/rendering/caching/RenderingCache.test.d.ts +1 -0
- package/dist/mjs/rendering/renderers/DummyRenderer.test.d.ts +1 -0
- package/dist/mjs/rendering/renderers/TextOnlyRenderer.test.d.ts +1 -0
- package/dist/mjs/shortcuts/KeyBinding.test.d.ts +1 -0
- package/dist/mjs/shortcuts/KeyboardShortcutManager.test.d.ts +1 -0
- package/dist/mjs/toolbar/EdgeToolbar.test.d.ts +1 -0
- package/dist/mjs/tools/Eraser.test.d.ts +1 -0
- package/dist/mjs/tools/FindTool.test.d.ts +1 -0
- package/dist/mjs/tools/InputFilter/InputPipeline.test.d.ts +1 -0
- package/dist/mjs/tools/PanZoom.test.d.ts +1 -0
- package/dist/mjs/tools/Pen.test.d.ts +1 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.test.d.ts +1 -0
- package/dist/mjs/tools/UndoRedoShortcut.test.d.ts +1 -0
- package/dist/mjs/util/ReactiveValue.test.d.ts +1 -0
- package/dist/mjs/version.mjs +1 -1
- package/dist/mjs/version.test.d.ts +1 -0
- package/dist-test/test_imports/package-lock.json +13 -0
- package/dist-test/test_imports/package.json +12 -0
- package/dist-test/test_imports/test-imports.js +11 -0
- package/dist-test/test_imports/test-require.cjs +14 -0
- package/package.json +2 -2
- package/src/Editor.loadFrom.test.ts +24 -0
- package/src/Editor.test.ts +107 -0
- package/src/Editor.toSVG.test.ts +294 -0
- package/src/Editor.ts +1443 -0
- package/src/EditorImage.test.ts +117 -0
- package/src/EditorImage.ts +609 -0
- package/src/EventDispatcher.test.ts +123 -0
- package/src/EventDispatcher.ts +72 -0
- package/src/Pointer.ts +183 -0
- package/src/SVGLoader.test.ts +114 -0
- package/src/SVGLoader.ts +672 -0
- package/src/UndoRedoHistory.test.ts +34 -0
- package/src/UndoRedoHistory.ts +102 -0
- package/src/Viewport.ts +322 -0
- package/src/bundle/bundled.ts +7 -0
- package/src/commands/Command.ts +45 -0
- package/src/commands/Duplicate.ts +75 -0
- package/src/commands/Erase.ts +95 -0
- package/src/commands/SerializableCommand.ts +49 -0
- package/src/commands/UnresolvedCommand.ts +37 -0
- package/src/commands/invertCommand.ts +58 -0
- package/src/commands/lib.ts +16 -0
- package/src/commands/localization.ts +47 -0
- package/src/commands/uniteCommands.test.ts +23 -0
- package/src/commands/uniteCommands.ts +140 -0
- package/src/components/AbstractComponent.transformBy.test.ts +23 -0
- package/src/components/AbstractComponent.ts +383 -0
- package/src/components/BackgroundComponent.test.ts +44 -0
- package/src/components/BackgroundComponent.ts +348 -0
- package/src/components/ImageComponent.ts +176 -0
- package/src/components/RestylableComponent.ts +161 -0
- package/src/components/SVGGlobalAttributesObject.ts +79 -0
- package/src/components/Stroke.test.ts +137 -0
- package/src/components/Stroke.ts +294 -0
- package/src/components/TextComponent.test.ts +202 -0
- package/src/components/TextComponent.ts +429 -0
- package/src/components/UnknownSVGObject.test.ts +10 -0
- package/src/components/UnknownSVGObject.ts +60 -0
- package/src/components/builders/ArrowBuilder.ts +106 -0
- package/src/components/builders/CircleBuilder.ts +100 -0
- package/src/components/builders/FreehandLineBuilder.test.ts +24 -0
- package/src/components/builders/FreehandLineBuilder.ts +210 -0
- package/src/components/builders/LineBuilder.ts +77 -0
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +453 -0
- package/src/components/builders/RectangleBuilder.ts +73 -0
- package/src/components/builders/types.ts +15 -0
- package/src/components/lib.ts +31 -0
- package/src/components/localization.ts +24 -0
- package/src/components/util/StrokeSmoother.ts +302 -0
- package/src/components/util/describeComponentList.ts +18 -0
- package/src/dialogs/makeAboutDialog.ts +82 -0
- package/src/inputEvents.ts +143 -0
- package/src/lib.ts +91 -0
- package/src/localization.ts +34 -0
- package/src/localizations/de.ts +146 -0
- package/src/localizations/en.ts +8 -0
- package/src/localizations/es.ts +74 -0
- package/src/localizations/getLocalizationTable.test.ts +27 -0
- package/src/localizations/getLocalizationTable.ts +74 -0
- package/src/rendering/Display.ts +247 -0
- package/src/rendering/RenderablePathSpec.ts +88 -0
- package/src/rendering/RenderingStyle.test.ts +68 -0
- package/src/rendering/RenderingStyle.ts +55 -0
- package/src/rendering/TextRenderingStyle.ts +55 -0
- package/src/rendering/caching/CacheRecord.test.ts +48 -0
- package/src/rendering/caching/CacheRecord.ts +76 -0
- package/src/rendering/caching/CacheRecordManager.ts +71 -0
- package/src/rendering/caching/RenderingCache.test.ts +43 -0
- package/src/rendering/caching/RenderingCache.ts +66 -0
- package/src/rendering/caching/RenderingCacheNode.ts +404 -0
- package/src/rendering/caching/testUtils.ts +35 -0
- package/src/rendering/caching/types.ts +34 -0
- package/src/rendering/lib.ts +8 -0
- package/src/rendering/localization.ts +20 -0
- package/src/rendering/renderers/AbstractRenderer.ts +232 -0
- package/src/rendering/renderers/CanvasRenderer.ts +312 -0
- package/src/rendering/renderers/DummyRenderer.test.ts +41 -0
- package/src/rendering/renderers/DummyRenderer.ts +142 -0
- package/src/rendering/renderers/SVGRenderer.ts +434 -0
- package/src/rendering/renderers/TextOnlyRenderer.test.ts +34 -0
- package/src/rendering/renderers/TextOnlyRenderer.ts +68 -0
- package/src/shortcuts/KeyBinding.test.ts +61 -0
- package/src/shortcuts/KeyBinding.ts +257 -0
- package/src/shortcuts/KeyboardShortcutManager.test.ts +95 -0
- package/src/shortcuts/KeyboardShortcutManager.ts +163 -0
- package/src/shortcuts/lib.ts +3 -0
- package/src/testing/createEditor.ts +11 -0
- package/src/testing/getUniquePointerId.ts +18 -0
- package/src/testing/lib.ts +3 -0
- package/src/testing/sendPenEvent.ts +36 -0
- package/src/testing/sendTouchEvent.ts +71 -0
- package/src/toolbar/AbstractToolbar.ts +542 -0
- package/src/toolbar/DropdownToolbar.ts +220 -0
- package/src/toolbar/EdgeToolbar.test.ts +54 -0
- package/src/toolbar/EdgeToolbar.ts +543 -0
- package/src/toolbar/IconProvider.ts +861 -0
- package/src/toolbar/constants.ts +1 -0
- package/src/toolbar/lib.ts +6 -0
- package/src/toolbar/localization.ts +136 -0
- package/src/toolbar/types.ts +13 -0
- package/src/toolbar/widgets/ActionButtonWidget.ts +39 -0
- package/src/toolbar/widgets/BaseToolWidget.ts +81 -0
- package/src/toolbar/widgets/BaseWidget.ts +495 -0
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +250 -0
- package/src/toolbar/widgets/EraserToolWidget.ts +84 -0
- package/src/toolbar/widgets/HandToolWidget.ts +239 -0
- package/src/toolbar/widgets/InsertImageWidget.ts +248 -0
- package/src/toolbar/widgets/OverflowWidget.ts +92 -0
- package/src/toolbar/widgets/PenToolWidget.ts +369 -0
- package/src/toolbar/widgets/SelectionToolWidget.ts +195 -0
- package/src/toolbar/widgets/TextToolWidget.ts +149 -0
- package/src/toolbar/widgets/components/makeColorInput.ts +184 -0
- package/src/toolbar/widgets/components/makeFileInput.ts +128 -0
- package/src/toolbar/widgets/components/makeGridSelector.ts +179 -0
- package/src/toolbar/widgets/components/makeSeparator.ts +17 -0
- package/src/toolbar/widgets/components/makeThicknessSlider.ts +62 -0
- package/src/toolbar/widgets/keybindings.ts +19 -0
- package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +262 -0
- package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +71 -0
- package/src/toolbar/widgets/layout/types.ts +74 -0
- package/src/toolbar/widgets/lib.ts +13 -0
- package/src/tools/BaseTool.ts +169 -0
- package/src/tools/Eraser.test.ts +103 -0
- package/src/tools/Eraser.ts +173 -0
- package/src/tools/FindTool.test.ts +67 -0
- package/src/tools/FindTool.ts +153 -0
- package/src/tools/InputFilter/FunctionMapper.ts +17 -0
- package/src/tools/InputFilter/InputMapper.ts +41 -0
- package/src/tools/InputFilter/InputPipeline.test.ts +41 -0
- package/src/tools/InputFilter/InputPipeline.ts +34 -0
- package/src/tools/InputFilter/InputStabilizer.ts +254 -0
- package/src/tools/InputFilter/StrokeKeyboardControl.ts +104 -0
- package/src/tools/PanZoom.test.ts +339 -0
- package/src/tools/PanZoom.ts +525 -0
- package/src/tools/PasteHandler.ts +94 -0
- package/src/tools/Pen.test.ts +260 -0
- package/src/tools/Pen.ts +284 -0
- package/src/tools/PipetteTool.ts +84 -0
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +29 -0
- package/src/tools/SelectionTool/Selection.ts +647 -0
- package/src/tools/SelectionTool/SelectionHandle.ts +142 -0
- package/src/tools/SelectionTool/SelectionTool.test.ts +370 -0
- package/src/tools/SelectionTool/SelectionTool.ts +510 -0
- package/src/tools/SelectionTool/TransformMode.ts +112 -0
- package/src/tools/SelectionTool/types.ts +11 -0
- package/src/tools/SoundUITool.ts +221 -0
- package/src/tools/TextTool.ts +339 -0
- package/src/tools/ToolController.ts +224 -0
- package/src/tools/ToolEnabledGroup.ts +14 -0
- package/src/tools/ToolSwitcherShortcut.ts +39 -0
- package/src/tools/ToolbarShortcutHandler.ts +39 -0
- package/src/tools/UndoRedoShortcut.test.ts +62 -0
- package/src/tools/UndoRedoShortcut.ts +24 -0
- package/src/tools/keybindings.ts +85 -0
- package/src/tools/lib.ts +22 -0
- package/src/tools/localization.ts +76 -0
- package/src/types.ts +151 -0
- package/src/util/ReactiveValue.test.ts +168 -0
- package/src/util/ReactiveValue.ts +241 -0
- package/src/util/assertions.ts +55 -0
- package/src/util/fileToBase64.ts +18 -0
- package/src/util/guessKeyCodeFromKey.ts +36 -0
- package/src/util/listPrefixMatch.ts +19 -0
- package/src/util/stopPropagationOfScrollingWheelEvents.ts +20 -0
- package/src/util/untilNextAnimationFrame.ts +9 -0
- package/src/util/waitForAll.ts +18 -0
- package/src/util/waitForTimeout.ts +9 -0
- package/src/version.test.ts +12 -0
- package/src/version.ts +3 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
import Erase from '../../commands/Erase';
|
2
|
+
import SerializableCommand from '../../commands/SerializableCommand';
|
3
|
+
import uniteCommands from '../../commands/uniteCommands';
|
4
|
+
import BackgroundComponent, { BackgroundType } from '../../components/BackgroundComponent';
|
5
|
+
import Editor from '../../Editor';
|
6
|
+
import { EditorImageEventType } from '../../EditorImage';
|
7
|
+
import { Rect2, Color4 } from '@js-draw/math';
|
8
|
+
import { EditorEventType } from '../../types';
|
9
|
+
import { toolbarCSSPrefix } from '../constants';
|
10
|
+
import { ToolbarLocalization } from '../localization';
|
11
|
+
import makeColorInput from './components/makeColorInput';
|
12
|
+
import BaseWidget from './BaseWidget';
|
13
|
+
|
14
|
+
export default class DocumentPropertiesWidget extends BaseWidget {
|
15
|
+
private updateDropdownContent: ()=>void = () => {};
|
16
|
+
|
17
|
+
public constructor(editor: Editor, localizationTable?: ToolbarLocalization) {
|
18
|
+
super(editor, 'document-properties-widget', localizationTable);
|
19
|
+
|
20
|
+
// Make it possible to open the dropdown, even if this widget isn't selected.
|
21
|
+
this.container.classList.add('dropdownShowable');
|
22
|
+
|
23
|
+
this.editor.notifier.on(EditorEventType.UndoRedoStackUpdated, () => {
|
24
|
+
this.queueDropdownUpdate();
|
25
|
+
});
|
26
|
+
|
27
|
+
this.editor.image.notifier.on(EditorImageEventType.ExportViewportChanged, () => {
|
28
|
+
this.queueDropdownUpdate();
|
29
|
+
});
|
30
|
+
}
|
31
|
+
|
32
|
+
protected getTitle(): string {
|
33
|
+
return this.localizationTable.documentProperties;
|
34
|
+
}
|
35
|
+
|
36
|
+
protected createIcon(): Element {
|
37
|
+
return this.editor.icons.makeConfigureDocumentIcon();
|
38
|
+
}
|
39
|
+
|
40
|
+
protected handleClick() {
|
41
|
+
this.setDropdownVisible(!this.isDropdownVisible());
|
42
|
+
this.queueDropdownUpdate();
|
43
|
+
}
|
44
|
+
|
45
|
+
private dropdownUpdateQueued: boolean = false;
|
46
|
+
private queueDropdownUpdate() {
|
47
|
+
if (!this.dropdownUpdateQueued) {
|
48
|
+
requestAnimationFrame(() => this.updateDropdown());
|
49
|
+
this.dropdownUpdateQueued = true;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
private updateDropdown() {
|
54
|
+
this.dropdownUpdateQueued = false;
|
55
|
+
|
56
|
+
if (this.isDropdownVisible()) {
|
57
|
+
this.updateDropdownContent();
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
private setBackgroundColor(color: Color4) {
|
62
|
+
this.editor.dispatch(this.editor.setBackgroundColor(color));
|
63
|
+
}
|
64
|
+
|
65
|
+
private getBackgroundColor() {
|
66
|
+
return this.editor.estimateBackgroundColor();
|
67
|
+
}
|
68
|
+
|
69
|
+
private removeBackgroundComponents(): SerializableCommand {
|
70
|
+
const previousBackgrounds = [];
|
71
|
+
for (const component of this.editor.image.getBackgroundComponents()) {
|
72
|
+
if (component instanceof BackgroundComponent) {
|
73
|
+
previousBackgrounds.push(component);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
return new Erase(previousBackgrounds);
|
78
|
+
}
|
79
|
+
|
80
|
+
/** Replace existing background components with a background of the given type. */
|
81
|
+
private setBackgroundType(backgroundType: BackgroundType): SerializableCommand {
|
82
|
+
const prevBackgroundColor = this.editor.estimateBackgroundColor();
|
83
|
+
const newBackground = new BackgroundComponent(backgroundType, prevBackgroundColor);
|
84
|
+
const addBackgroundCommand = this.editor.image.addElement(newBackground);
|
85
|
+
|
86
|
+
return uniteCommands([ this.removeBackgroundComponents(), addBackgroundCommand ]);
|
87
|
+
}
|
88
|
+
|
89
|
+
/** Returns the type of the topmost background component */
|
90
|
+
private getBackgroundType(): BackgroundType {
|
91
|
+
const backgroundComponents = this.editor.image.getBackgroundComponents();
|
92
|
+
for (let i = backgroundComponents.length - 1; i >= 0; i--) {
|
93
|
+
const component = backgroundComponents[i];
|
94
|
+
if (component instanceof BackgroundComponent) {
|
95
|
+
return component.getBackgroundType();
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
return BackgroundType.None;
|
100
|
+
}
|
101
|
+
|
102
|
+
private updateImportExportRectSize(size: { width?: number, height?: number }) {
|
103
|
+
const filterDimension = (dim: number|undefined) => {
|
104
|
+
if (dim !== undefined && (!isFinite(dim) || dim <= 0)) {
|
105
|
+
dim = 100;
|
106
|
+
}
|
107
|
+
|
108
|
+
return dim;
|
109
|
+
};
|
110
|
+
|
111
|
+
const width = filterDimension(size.width);
|
112
|
+
const height = filterDimension(size.height);
|
113
|
+
|
114
|
+
const currentRect = this.editor.getImportExportRect();
|
115
|
+
const newRect = new Rect2(
|
116
|
+
currentRect.x, currentRect.y,
|
117
|
+
width ?? currentRect.w, height ?? currentRect.h
|
118
|
+
);
|
119
|
+
|
120
|
+
this.editor.dispatch(this.editor.image.setImportExportRect(newRect));
|
121
|
+
this.editor.queueRerender();
|
122
|
+
}
|
123
|
+
|
124
|
+
private static idCounter = 0;
|
125
|
+
|
126
|
+
protected override fillDropdown(dropdown: HTMLElement): boolean {
|
127
|
+
const container = document.createElement('div');
|
128
|
+
container.classList.add(
|
129
|
+
`${toolbarCSSPrefix}spacedList`,
|
130
|
+
`${toolbarCSSPrefix}nonbutton-controls-main-list`,
|
131
|
+
`${toolbarCSSPrefix}document-properties-widget`
|
132
|
+
);
|
133
|
+
|
134
|
+
// Background color input
|
135
|
+
const backgroundColorRow = document.createElement('div');
|
136
|
+
const backgroundColorLabel = document.createElement('label');
|
137
|
+
|
138
|
+
backgroundColorLabel.innerText = this.localizationTable.backgroundColor;
|
139
|
+
|
140
|
+
const {
|
141
|
+
input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue
|
142
|
+
} = makeColorInput(this.editor, color => {
|
143
|
+
if (!color.eq(this.getBackgroundColor())) {
|
144
|
+
this.setBackgroundColor(color);
|
145
|
+
}
|
146
|
+
});
|
147
|
+
|
148
|
+
colorInput.id = `${toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
|
149
|
+
backgroundColorLabel.htmlFor = colorInput.id;
|
150
|
+
|
151
|
+
backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
|
152
|
+
|
153
|
+
// Background style selector
|
154
|
+
const useGridRow = document.createElement('div');
|
155
|
+
const useGridLabel = document.createElement('label');
|
156
|
+
const useGridCheckbox = document.createElement('input');
|
157
|
+
|
158
|
+
useGridCheckbox.id = `${toolbarCSSPrefix}docPropertiesUseGridCheckbox-${DocumentPropertiesWidget.idCounter++}`;
|
159
|
+
useGridLabel.htmlFor = useGridCheckbox.id;
|
160
|
+
|
161
|
+
useGridCheckbox.type = 'checkbox';
|
162
|
+
useGridLabel.innerText = this.localizationTable.useGridOption;
|
163
|
+
|
164
|
+
useGridCheckbox.oninput = () => {
|
165
|
+
const prevBackgroundType = this.getBackgroundType();
|
166
|
+
const wasGrid = prevBackgroundType === BackgroundType.Grid;
|
167
|
+
|
168
|
+
if (wasGrid === useGridCheckbox.checked) {
|
169
|
+
// Already the requested background type.
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
let newBackgroundType = BackgroundType.SolidColor;
|
174
|
+
if (useGridCheckbox.checked) {
|
175
|
+
newBackgroundType = BackgroundType.Grid;
|
176
|
+
}
|
177
|
+
|
178
|
+
this.editor.dispatch(this.setBackgroundType(newBackgroundType));
|
179
|
+
};
|
180
|
+
|
181
|
+
useGridRow.replaceChildren(useGridLabel, useGridCheckbox);
|
182
|
+
|
183
|
+
// Adds a width/height input
|
184
|
+
const addDimensionRow = (labelContent: string, onChange: (value: number)=>void) => {
|
185
|
+
const row = document.createElement('div');
|
186
|
+
const label = document.createElement('label');
|
187
|
+
const input = document.createElement('input');
|
188
|
+
|
189
|
+
label.innerText = labelContent;
|
190
|
+
input.type = 'number';
|
191
|
+
input.min = '0';
|
192
|
+
input.id = `${toolbarCSSPrefix}docPropertiesDimensionRow-${DocumentPropertiesWidget.idCounter++}`;
|
193
|
+
label.htmlFor = input.id;
|
194
|
+
|
195
|
+
input.style.flexGrow = '2';
|
196
|
+
input.style.width = '25px';
|
197
|
+
|
198
|
+
row.style.display = 'flex';
|
199
|
+
|
200
|
+
input.oninput = () => {
|
201
|
+
onChange(parseFloat(input.value));
|
202
|
+
};
|
203
|
+
|
204
|
+
row.replaceChildren(label, input);
|
205
|
+
|
206
|
+
return {
|
207
|
+
setValue: (value: number) => {
|
208
|
+
input.value = value.toString();
|
209
|
+
},
|
210
|
+
element: row,
|
211
|
+
};
|
212
|
+
};
|
213
|
+
|
214
|
+
const imageWidthRow = addDimensionRow(this.localizationTable.imageWidthOption, (value: number) => {
|
215
|
+
this.updateImportExportRectSize({ width: value });
|
216
|
+
});
|
217
|
+
const imageHeightRow = addDimensionRow(this.localizationTable.imageHeightOption, (value: number) => {
|
218
|
+
this.updateImportExportRectSize({ height: value });
|
219
|
+
});
|
220
|
+
|
221
|
+
// The "About..." button
|
222
|
+
const aboutButton = document.createElement('button');
|
223
|
+
aboutButton.classList.add('about-button');
|
224
|
+
aboutButton.innerText = this.localizationTable.about;
|
225
|
+
|
226
|
+
aboutButton.onclick = () => {
|
227
|
+
this.editor.showAboutDialog();
|
228
|
+
};
|
229
|
+
|
230
|
+
|
231
|
+
this.updateDropdownContent = () => {
|
232
|
+
setBgColorInputValue(this.getBackgroundColor());
|
233
|
+
|
234
|
+
const importExportRect = this.editor.getImportExportRect();
|
235
|
+
imageWidthRow.setValue(importExportRect.width);
|
236
|
+
imageHeightRow.setValue(importExportRect.height);
|
237
|
+
|
238
|
+
useGridCheckbox.checked = this.getBackgroundType() === BackgroundType.Grid;
|
239
|
+
};
|
240
|
+
this.updateDropdownContent();
|
241
|
+
|
242
|
+
|
243
|
+
container.replaceChildren(
|
244
|
+
backgroundColorRow, useGridRow, imageWidthRow.element, imageHeightRow.element, aboutButton
|
245
|
+
);
|
246
|
+
dropdown.replaceChildren(container);
|
247
|
+
|
248
|
+
return true;
|
249
|
+
}
|
250
|
+
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import Eraser from '../../tools/Eraser';
|
3
|
+
import { EditorEventType } from '../../types';
|
4
|
+
import { toolbarCSSPrefix } from '../constants';
|
5
|
+
import { ToolbarLocalization } from '../localization';
|
6
|
+
import BaseToolWidget from './BaseToolWidget';
|
7
|
+
import { SavedToolbuttonState } from './BaseWidget';
|
8
|
+
import makeThicknessSlider from './components/makeThicknessSlider';
|
9
|
+
|
10
|
+
export default class EraserToolWidget extends BaseToolWidget {
|
11
|
+
private updateInputs: ()=>void = () => {};
|
12
|
+
|
13
|
+
public constructor(
|
14
|
+
editor: Editor,
|
15
|
+
private tool: Eraser,
|
16
|
+
localizationTable?: ToolbarLocalization
|
17
|
+
) {
|
18
|
+
super(editor, tool, 'eraser-tool-widget', localizationTable);
|
19
|
+
|
20
|
+
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
21
|
+
if (toolEvt.kind === EditorEventType.ToolUpdated && toolEvt.tool === this.tool) {
|
22
|
+
this.updateInputs();
|
23
|
+
this.updateIcon();
|
24
|
+
}
|
25
|
+
});
|
26
|
+
}
|
27
|
+
|
28
|
+
protected getTitle(): string {
|
29
|
+
return this.localizationTable.eraser;
|
30
|
+
}
|
31
|
+
|
32
|
+
protected createIcon(): Element {
|
33
|
+
return this.editor.icons.makeEraserIcon(this.tool.getThickness());
|
34
|
+
}
|
35
|
+
|
36
|
+
protected override fillDropdown(dropdown: HTMLElement): boolean {
|
37
|
+
const container = document.createElement('div');
|
38
|
+
|
39
|
+
container.classList.add(`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`);
|
40
|
+
|
41
|
+
const thicknessSlider = makeThicknessSlider(this.editor, thickness => {
|
42
|
+
this.tool.setThickness(thickness);
|
43
|
+
});
|
44
|
+
thicknessSlider.setBounds(10, 55);
|
45
|
+
|
46
|
+
this.updateInputs = () => {
|
47
|
+
thicknessSlider.setValue(this.tool.getThickness());
|
48
|
+
};
|
49
|
+
|
50
|
+
this.updateInputs();
|
51
|
+
|
52
|
+
const spacer = document.createElement('div');
|
53
|
+
spacer.style.height = '5px';
|
54
|
+
|
55
|
+
container.replaceChildren(thicknessSlider.container, spacer);
|
56
|
+
|
57
|
+
dropdown.replaceChildren(container);
|
58
|
+
return true;
|
59
|
+
}
|
60
|
+
|
61
|
+
public override serializeState(): SavedToolbuttonState {
|
62
|
+
return {
|
63
|
+
...super.serializeState(),
|
64
|
+
|
65
|
+
thickness: this.tool.getThickness(),
|
66
|
+
};
|
67
|
+
}
|
68
|
+
|
69
|
+
public override deserializeFrom(state: SavedToolbuttonState) {
|
70
|
+
super.deserializeFrom(state);
|
71
|
+
|
72
|
+
if (state.thickness) {
|
73
|
+
const parsedThickness = parseFloat(state.thickness);
|
74
|
+
|
75
|
+
if (typeof parsedThickness !== 'number' || !isFinite(parsedThickness)) {
|
76
|
+
throw new Error(
|
77
|
+
`Deserializing property ${parsedThickness} is not a number or is not finite.`
|
78
|
+
);
|
79
|
+
}
|
80
|
+
|
81
|
+
this.tool.setThickness(parsedThickness);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
@@ -0,0 +1,239 @@
|
|
1
|
+
import Editor from '../../Editor';
|
2
|
+
import { Mat33 } from '@js-draw/math';
|
3
|
+
import PanZoom, { PanZoomMode } from '../../tools/PanZoom';
|
4
|
+
import ToolController from '../../tools/ToolController';
|
5
|
+
import { EditorEventType } from '../../types';
|
6
|
+
import Viewport from '../../Viewport';
|
7
|
+
import { toolbarCSSPrefix } from '../constants';
|
8
|
+
import { ToolbarLocalization } from '../localization';
|
9
|
+
import BaseToolWidget from './BaseToolWidget';
|
10
|
+
import BaseWidget, { SavedToolbuttonState } from './BaseWidget';
|
11
|
+
import makeSeparator from './components/makeSeparator';
|
12
|
+
|
13
|
+
const makeZoomControl = (localizationTable: ToolbarLocalization, editor: Editor) => {
|
14
|
+
const zoomLevelRow = document.createElement('div');
|
15
|
+
|
16
|
+
const increaseButton = document.createElement('button');
|
17
|
+
const decreaseButton = document.createElement('button');
|
18
|
+
const resetViewButton = document.createElement('button');
|
19
|
+
const zoomLevelDisplay = document.createElement('span');
|
20
|
+
increaseButton.innerText = '+';
|
21
|
+
decreaseButton.innerText = '-';
|
22
|
+
resetViewButton.innerText = localizationTable.resetView;
|
23
|
+
zoomLevelRow.replaceChildren(zoomLevelDisplay, increaseButton, decreaseButton, resetViewButton);
|
24
|
+
|
25
|
+
zoomLevelRow.classList.add(`${toolbarCSSPrefix}zoomLevelEditor`);
|
26
|
+
zoomLevelDisplay.classList.add('zoomDisplay');
|
27
|
+
|
28
|
+
let lastZoom: number|undefined;
|
29
|
+
const updateZoomDisplay = () => {
|
30
|
+
let zoomLevel = editor.viewport.getScaleFactor() * 100;
|
31
|
+
|
32
|
+
if (zoomLevel > 0.1) {
|
33
|
+
zoomLevel = Math.round(zoomLevel * 10) / 10;
|
34
|
+
} else {
|
35
|
+
zoomLevel = Math.round(zoomLevel * 1000) / 1000;
|
36
|
+
}
|
37
|
+
|
38
|
+
if (zoomLevel !== lastZoom) {
|
39
|
+
zoomLevelDisplay.innerText = localizationTable.zoomLevel(zoomLevel);
|
40
|
+
lastZoom = zoomLevel;
|
41
|
+
}
|
42
|
+
};
|
43
|
+
updateZoomDisplay();
|
44
|
+
|
45
|
+
editor.notifier.on(EditorEventType.ViewportChanged, (event) => {
|
46
|
+
if (event.kind === EditorEventType.ViewportChanged) {
|
47
|
+
updateZoomDisplay();
|
48
|
+
|
49
|
+
// Can't reset if already reset.
|
50
|
+
resetViewButton.disabled = event.newTransform.eq(Mat33.identity);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
|
54
|
+
const zoomBy = (factor: number) => {
|
55
|
+
const screenCenter = editor.viewport.visibleRect.center;
|
56
|
+
const transformUpdate = Mat33.scaling2D(factor, screenCenter);
|
57
|
+
editor.dispatch(Viewport.transformBy(transformUpdate), false);
|
58
|
+
};
|
59
|
+
|
60
|
+
increaseButton.onclick = () => {
|
61
|
+
zoomBy(5.0/4);
|
62
|
+
};
|
63
|
+
|
64
|
+
decreaseButton.onclick = () => {
|
65
|
+
zoomBy(4.0/5);
|
66
|
+
};
|
67
|
+
|
68
|
+
resetViewButton.onclick = () => {
|
69
|
+
const addToHistory = false;
|
70
|
+
editor.dispatch(Viewport.transformBy(
|
71
|
+
editor.viewport.canvasToScreenTransform.inverse()
|
72
|
+
), addToHistory);
|
73
|
+
};
|
74
|
+
|
75
|
+
return zoomLevelRow;
|
76
|
+
};
|
77
|
+
|
78
|
+
class HandModeWidget extends BaseWidget {
|
79
|
+
public constructor(
|
80
|
+
editor: Editor,
|
81
|
+
|
82
|
+
protected tool: PanZoom, protected flag: PanZoomMode, protected makeIcon: ()=> Element,
|
83
|
+
private title: string,
|
84
|
+
|
85
|
+
localizationTable?: ToolbarLocalization,
|
86
|
+
) {
|
87
|
+
super(editor, `pan-mode-${flag}`, localizationTable);
|
88
|
+
|
89
|
+
editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
90
|
+
if (toolEvt.kind === EditorEventType.ToolUpdated && toolEvt.tool === tool) {
|
91
|
+
const allEnabled = !!(tool.getMode() & PanZoomMode.SinglePointerGestures);
|
92
|
+
this.setSelected(!!(tool.getMode() & flag) || allEnabled);
|
93
|
+
|
94
|
+
// Unless this widget toggles all single pointer gestures, toggling while
|
95
|
+
// single pointer gestures are enabled should have no effect
|
96
|
+
this.setDisabled(allEnabled && flag !== PanZoomMode.SinglePointerGestures);
|
97
|
+
}
|
98
|
+
});
|
99
|
+
this.setSelected(false);
|
100
|
+
}
|
101
|
+
|
102
|
+
private setModeFlag(enabled: boolean) {
|
103
|
+
this.tool.setModeEnabled(this.flag, enabled);
|
104
|
+
}
|
105
|
+
|
106
|
+
protected handleClick() {
|
107
|
+
this.setModeFlag(!this.isSelected());
|
108
|
+
}
|
109
|
+
|
110
|
+
protected getTitle(): string {
|
111
|
+
return this.title;
|
112
|
+
}
|
113
|
+
|
114
|
+
protected createIcon(): Element {
|
115
|
+
return this.makeIcon();
|
116
|
+
}
|
117
|
+
|
118
|
+
protected override fillDropdown(_dropdown: HTMLElement): boolean {
|
119
|
+
return false;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
export default class HandToolWidget extends BaseToolWidget {
|
124
|
+
private allowTogglingBaseTool: boolean;
|
125
|
+
|
126
|
+
public constructor(
|
127
|
+
editor: Editor,
|
128
|
+
|
129
|
+
// Pan zoom tool that overrides all other tools (enabling this tool for a device
|
130
|
+
// causes that device to pan/zoom instead of interact with the primary tools)
|
131
|
+
protected overridePanZoomTool: PanZoom,
|
132
|
+
|
133
|
+
localizationTable: ToolbarLocalization,
|
134
|
+
) {
|
135
|
+
const primaryHandTool = HandToolWidget.getPrimaryHandTool(editor.toolController);
|
136
|
+
const tool = primaryHandTool ?? overridePanZoomTool;
|
137
|
+
super(editor, tool, 'hand-tool-widget', localizationTable);
|
138
|
+
|
139
|
+
// Only allow toggling a hand tool if we're using the primary hand tool and not the override
|
140
|
+
// hand tool for this button.
|
141
|
+
this.allowTogglingBaseTool = primaryHandTool !== null;
|
142
|
+
|
143
|
+
// Allow showing/hiding the dropdown, even if `overridePanZoomTool` isn't enabled.
|
144
|
+
if (!this.allowTogglingBaseTool) {
|
145
|
+
this.container.classList.add('dropdownShowable');
|
146
|
+
}
|
147
|
+
|
148
|
+
// Controls for the overriding hand tool.
|
149
|
+
const touchPanningWidget = new HandModeWidget(
|
150
|
+
editor,
|
151
|
+
|
152
|
+
overridePanZoomTool, PanZoomMode.OneFingerTouchGestures,
|
153
|
+
() => this.editor.icons.makeTouchPanningIcon(),
|
154
|
+
|
155
|
+
localizationTable.touchPanning,
|
156
|
+
|
157
|
+
localizationTable,
|
158
|
+
);
|
159
|
+
|
160
|
+
const rotationLockWidget = new HandModeWidget(
|
161
|
+
editor,
|
162
|
+
|
163
|
+
overridePanZoomTool, PanZoomMode.RotationLocked,
|
164
|
+
() => this.editor.icons.makeRotationLockIcon(),
|
165
|
+
|
166
|
+
localizationTable.lockRotation,
|
167
|
+
localizationTable,
|
168
|
+
);
|
169
|
+
|
170
|
+
this.addSubWidget(touchPanningWidget);
|
171
|
+
this.addSubWidget(rotationLockWidget);
|
172
|
+
}
|
173
|
+
|
174
|
+
private static getPrimaryHandTool(toolController: ToolController): PanZoom|null {
|
175
|
+
const primaryPanZoomToolList = toolController.getPrimaryTools().filter(tool => tool instanceof PanZoom);
|
176
|
+
const primaryPanZoomTool = primaryPanZoomToolList[0];
|
177
|
+
return primaryPanZoomTool as PanZoom|null;
|
178
|
+
}
|
179
|
+
|
180
|
+
protected getTitle(): string {
|
181
|
+
return this.localizationTable.handTool;
|
182
|
+
}
|
183
|
+
|
184
|
+
protected createIcon(): Element {
|
185
|
+
return this.editor.icons.makeHandToolIcon();
|
186
|
+
}
|
187
|
+
|
188
|
+
protected override handleClick(): void {
|
189
|
+
if (this.allowTogglingBaseTool) {
|
190
|
+
super.handleClick();
|
191
|
+
} else {
|
192
|
+
this.setDropdownVisible(!this.isDropdownVisible());
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
protected override fillDropdown(dropdown: HTMLElement): boolean {
|
197
|
+
super.fillDropdown(dropdown);
|
198
|
+
|
199
|
+
// The container for all actions that come after the toolbar buttons.
|
200
|
+
const nonbuttonActionContainer = document.createElement('div');
|
201
|
+
nonbuttonActionContainer.classList.add(`${toolbarCSSPrefix}nonbutton-controls-main-list`);
|
202
|
+
|
203
|
+
makeSeparator().addTo(nonbuttonActionContainer);
|
204
|
+
nonbuttonActionContainer.appendChild(
|
205
|
+
makeZoomControl(this.localizationTable, this.editor)
|
206
|
+
);
|
207
|
+
dropdown.appendChild(nonbuttonActionContainer);
|
208
|
+
|
209
|
+
return true;
|
210
|
+
}
|
211
|
+
|
212
|
+
public override setSelected(selected: boolean): void {
|
213
|
+
if (this.allowTogglingBaseTool) {
|
214
|
+
super.setSelected(selected);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
public override serializeState(): SavedToolbuttonState {
|
219
|
+
const toolMode = this.overridePanZoomTool.getMode();
|
220
|
+
|
221
|
+
return {
|
222
|
+
...super.serializeState(),
|
223
|
+
touchPanning: toolMode & PanZoomMode.OneFingerTouchGestures,
|
224
|
+
rotationLocked: toolMode & PanZoomMode.RotationLocked,
|
225
|
+
};
|
226
|
+
}
|
227
|
+
|
228
|
+
public override deserializeFrom(state: SavedToolbuttonState): void {
|
229
|
+
if (state.touchPanning !== undefined) {
|
230
|
+
this.overridePanZoomTool.setModeEnabled(PanZoomMode.OneFingerTouchGestures, state.touchPanning);
|
231
|
+
}
|
232
|
+
|
233
|
+
if (state.rotationLocked !== undefined) {
|
234
|
+
this.overridePanZoomTool.setModeEnabled(PanZoomMode.RotationLocked, state.rotationLocked);
|
235
|
+
}
|
236
|
+
|
237
|
+
super.deserializeFrom(state);
|
238
|
+
}
|
239
|
+
}
|