js-draw 1.0.1 → 1.1.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/LICENSE +21 -0
- package/dist/Editor.css +1 -0
- package/dist/bundle.js +1 -1
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +9 -13
- package/dist/cjs/toolbar/AbstractToolbar.js +14 -19
- package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
- package/dist/cjs/toolbar/widgets/SaveActionWidget.js +26 -0
- package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +9 -13
- package/dist/mjs/toolbar/AbstractToolbar.mjs +14 -19
- package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
- package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +21 -0
- package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
- package/dist/mjs/version.mjs +1 -1
- package/docs/img/readme-images/js-draw.jpg +0 -0
- package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
- package/package.json +5 -4
- package/src/toolbar/EdgeToolbar.scss +1 -0
- package/dist-test/test_imports/package-lock.json +0 -13
- package/dist-test/test_imports/package.json +0 -12
- package/dist-test/test_imports/test-imports.js +0 -11
- package/dist-test/test_imports/test-require.cjs +0 -14
- package/src/Editor.loadFrom.test.ts +0 -24
- package/src/Editor.test.ts +0 -107
- package/src/Editor.toSVG.test.ts +0 -294
- package/src/Editor.ts +0 -1443
- package/src/EditorImage.test.ts +0 -117
- package/src/EditorImage.ts +0 -609
- package/src/EventDispatcher.test.ts +0 -123
- package/src/EventDispatcher.ts +0 -72
- package/src/Pointer.ts +0 -183
- package/src/SVGLoader.test.ts +0 -114
- package/src/SVGLoader.ts +0 -672
- package/src/UndoRedoHistory.test.ts +0 -34
- package/src/UndoRedoHistory.ts +0 -102
- package/src/Viewport.ts +0 -322
- package/src/bundle/bundled.ts +0 -7
- package/src/commands/Command.ts +0 -45
- package/src/commands/Duplicate.ts +0 -75
- package/src/commands/Erase.ts +0 -95
- package/src/commands/SerializableCommand.ts +0 -49
- package/src/commands/UnresolvedCommand.ts +0 -37
- package/src/commands/invertCommand.ts +0 -58
- package/src/commands/lib.ts +0 -16
- package/src/commands/localization.ts +0 -47
- package/src/commands/uniteCommands.test.ts +0 -23
- package/src/commands/uniteCommands.ts +0 -140
- package/src/components/AbstractComponent.transformBy.test.ts +0 -23
- package/src/components/AbstractComponent.ts +0 -383
- package/src/components/BackgroundComponent.test.ts +0 -44
- package/src/components/BackgroundComponent.ts +0 -348
- package/src/components/ImageComponent.ts +0 -176
- package/src/components/RestylableComponent.ts +0 -161
- package/src/components/SVGGlobalAttributesObject.ts +0 -79
- package/src/components/Stroke.test.ts +0 -137
- package/src/components/Stroke.ts +0 -294
- package/src/components/TextComponent.test.ts +0 -202
- package/src/components/TextComponent.ts +0 -429
- package/src/components/UnknownSVGObject.test.ts +0 -10
- package/src/components/UnknownSVGObject.ts +0 -60
- package/src/components/builders/ArrowBuilder.ts +0 -106
- package/src/components/builders/CircleBuilder.ts +0 -100
- package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
- package/src/components/builders/FreehandLineBuilder.ts +0 -210
- package/src/components/builders/LineBuilder.ts +0 -77
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
- package/src/components/builders/RectangleBuilder.ts +0 -73
- package/src/components/builders/types.ts +0 -15
- package/src/components/lib.ts +0 -31
- package/src/components/localization.ts +0 -24
- package/src/components/util/StrokeSmoother.ts +0 -302
- package/src/components/util/describeComponentList.ts +0 -18
- package/src/dialogs/makeAboutDialog.ts +0 -82
- package/src/inputEvents.ts +0 -143
- package/src/lib.ts +0 -91
- package/src/localization.ts +0 -34
- package/src/localizations/de.ts +0 -146
- package/src/localizations/en.ts +0 -8
- package/src/localizations/es.ts +0 -74
- package/src/localizations/getLocalizationTable.test.ts +0 -27
- package/src/localizations/getLocalizationTable.ts +0 -74
- package/src/rendering/Display.ts +0 -247
- package/src/rendering/RenderablePathSpec.ts +0 -88
- package/src/rendering/RenderingStyle.test.ts +0 -68
- package/src/rendering/RenderingStyle.ts +0 -55
- package/src/rendering/TextRenderingStyle.ts +0 -55
- package/src/rendering/caching/CacheRecord.test.ts +0 -48
- package/src/rendering/caching/CacheRecord.ts +0 -76
- package/src/rendering/caching/CacheRecordManager.ts +0 -71
- package/src/rendering/caching/RenderingCache.test.ts +0 -43
- package/src/rendering/caching/RenderingCache.ts +0 -66
- package/src/rendering/caching/RenderingCacheNode.ts +0 -404
- package/src/rendering/caching/testUtils.ts +0 -35
- package/src/rendering/caching/types.ts +0 -34
- package/src/rendering/lib.ts +0 -8
- package/src/rendering/localization.ts +0 -20
- package/src/rendering/renderers/AbstractRenderer.ts +0 -232
- package/src/rendering/renderers/CanvasRenderer.ts +0 -312
- package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
- package/src/rendering/renderers/DummyRenderer.ts +0 -142
- package/src/rendering/renderers/SVGRenderer.ts +0 -434
- package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
- package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
- package/src/shortcuts/KeyBinding.test.ts +0 -61
- package/src/shortcuts/KeyBinding.ts +0 -257
- package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
- package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
- package/src/shortcuts/lib.ts +0 -3
- package/src/testing/createEditor.ts +0 -11
- package/src/testing/getUniquePointerId.ts +0 -18
- package/src/testing/lib.ts +0 -3
- package/src/testing/sendPenEvent.ts +0 -36
- package/src/testing/sendTouchEvent.ts +0 -71
- package/src/toolbar/AbstractToolbar.ts +0 -542
- package/src/toolbar/DropdownToolbar.ts +0 -220
- package/src/toolbar/EdgeToolbar.test.ts +0 -54
- package/src/toolbar/EdgeToolbar.ts +0 -543
- package/src/toolbar/IconProvider.ts +0 -861
- package/src/toolbar/constants.ts +0 -1
- package/src/toolbar/lib.ts +0 -6
- package/src/toolbar/localization.ts +0 -136
- package/src/toolbar/types.ts +0 -13
- package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
- package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
- package/src/toolbar/widgets/BaseWidget.ts +0 -495
- package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
- package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
- package/src/toolbar/widgets/HandToolWidget.ts +0 -239
- package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
- package/src/toolbar/widgets/OverflowWidget.ts +0 -92
- package/src/toolbar/widgets/PenToolWidget.ts +0 -369
- package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
- package/src/toolbar/widgets/TextToolWidget.ts +0 -149
- package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
- package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
- package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
- package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
- package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
- package/src/toolbar/widgets/keybindings.ts +0 -19
- package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
- package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
- package/src/toolbar/widgets/layout/types.ts +0 -74
- package/src/toolbar/widgets/lib.ts +0 -13
- package/src/tools/BaseTool.ts +0 -169
- package/src/tools/Eraser.test.ts +0 -103
- package/src/tools/Eraser.ts +0 -173
- package/src/tools/FindTool.test.ts +0 -67
- package/src/tools/FindTool.ts +0 -153
- package/src/tools/InputFilter/FunctionMapper.ts +0 -17
- package/src/tools/InputFilter/InputMapper.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
- package/src/tools/InputFilter/InputPipeline.ts +0 -34
- package/src/tools/InputFilter/InputStabilizer.ts +0 -254
- package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
- package/src/tools/PanZoom.test.ts +0 -339
- package/src/tools/PanZoom.ts +0 -525
- package/src/tools/PasteHandler.ts +0 -94
- package/src/tools/Pen.test.ts +0 -260
- package/src/tools/Pen.ts +0 -284
- package/src/tools/PipetteTool.ts +0 -84
- package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
- package/src/tools/SelectionTool/Selection.ts +0 -647
- package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
- package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
- package/src/tools/SelectionTool/SelectionTool.ts +0 -510
- package/src/tools/SelectionTool/TransformMode.ts +0 -112
- package/src/tools/SelectionTool/types.ts +0 -11
- package/src/tools/SoundUITool.ts +0 -221
- package/src/tools/TextTool.ts +0 -339
- package/src/tools/ToolController.ts +0 -224
- package/src/tools/ToolEnabledGroup.ts +0 -14
- package/src/tools/ToolSwitcherShortcut.ts +0 -39
- package/src/tools/ToolbarShortcutHandler.ts +0 -39
- package/src/tools/UndoRedoShortcut.test.ts +0 -62
- package/src/tools/UndoRedoShortcut.ts +0 -24
- package/src/tools/keybindings.ts +0 -85
- package/src/tools/lib.ts +0 -22
- package/src/tools/localization.ts +0 -76
- package/src/types.ts +0 -151
- package/src/util/ReactiveValue.test.ts +0 -168
- package/src/util/ReactiveValue.ts +0 -241
- package/src/util/assertions.ts +0 -55
- package/src/util/fileToBase64.ts +0 -18
- package/src/util/guessKeyCodeFromKey.ts +0 -36
- package/src/util/listPrefixMatch.ts +0 -19
- package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
- package/src/util/untilNextAnimationFrame.ts +0 -9
- package/src/util/waitForAll.ts +0 -18
- package/src/util/waitForTimeout.ts +0 -9
- package/src/version.test.ts +0 -12
- package/src/version.ts +0 -3
- package/tools/allLocales.js +0 -4
- package/tools/copyREADME.ts +0 -62
@@ -1,195 +0,0 @@
|
|
1
|
-
import { Color4 } from '@js-draw/math';
|
2
|
-
import { isRestylableComponent } from '../../components/RestylableComponent';
|
3
|
-
import Editor from '../../Editor';
|
4
|
-
import uniteCommands from '../../commands/uniteCommands';
|
5
|
-
import SelectionTool from '../../tools/SelectionTool/SelectionTool';
|
6
|
-
import { EditorEventType } from '../../types';
|
7
|
-
import { KeyPressEvent } from '../../inputEvents';
|
8
|
-
import { ToolbarLocalization } from '../localization';
|
9
|
-
import makeColorInput from './components/makeColorInput';
|
10
|
-
import ActionButtonWidget from './ActionButtonWidget';
|
11
|
-
import BaseToolWidget from './BaseToolWidget';
|
12
|
-
import { resizeImageToSelectionKeyboardShortcut } from './keybindings';
|
13
|
-
import makeSeparator from './components/makeSeparator';
|
14
|
-
import { toolbarCSSPrefix } from '../constants';
|
15
|
-
|
16
|
-
const makeFormatMenu = (editor: Editor, selectionTool: SelectionTool, localizationTable: ToolbarLocalization) => {
|
17
|
-
const container = document.createElement('div');
|
18
|
-
container.classList.add(
|
19
|
-
'selection-format-menu', `${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}indentedList`
|
20
|
-
);
|
21
|
-
|
22
|
-
const colorRow = document.createElement('div');
|
23
|
-
const colorLabel = document.createElement('label');
|
24
|
-
const {
|
25
|
-
input: colorInput, container: colorInputContainer, setValue: setColorInputValue
|
26
|
-
} = makeColorInput(editor, color => {
|
27
|
-
const selection = selectionTool.getSelection();
|
28
|
-
|
29
|
-
if (selection) {
|
30
|
-
const updateStyleCommands = [];
|
31
|
-
|
32
|
-
for (const elem of selection.getSelectedObjects()) {
|
33
|
-
if (isRestylableComponent(elem)) {
|
34
|
-
updateStyleCommands.push(elem.updateStyle({ color }));
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
const unitedCommand = uniteCommands(updateStyleCommands);
|
39
|
-
editor.dispatch(unitedCommand);
|
40
|
-
}
|
41
|
-
});
|
42
|
-
|
43
|
-
colorLabel.innerText = localizationTable.colorLabel;
|
44
|
-
|
45
|
-
const update = () => {
|
46
|
-
const selection = selectionTool.getSelection();
|
47
|
-
if (selection && selection.getSelectedItemCount() > 0) {
|
48
|
-
colorInput.disabled = false;
|
49
|
-
container.classList.remove('disabled');
|
50
|
-
|
51
|
-
const colors = [];
|
52
|
-
for (const elem of selection.getSelectedObjects()) {
|
53
|
-
if (isRestylableComponent(elem)) {
|
54
|
-
const color = elem.getStyle().color;
|
55
|
-
if (color) {
|
56
|
-
colors.push(color);
|
57
|
-
}
|
58
|
-
}
|
59
|
-
}
|
60
|
-
setColorInputValue(Color4.average(colors));
|
61
|
-
} else {
|
62
|
-
colorInput.disabled = true;
|
63
|
-
container.classList.add('disabled');
|
64
|
-
setColorInputValue(Color4.transparent);
|
65
|
-
}
|
66
|
-
};
|
67
|
-
|
68
|
-
colorRow.replaceChildren(colorLabel, colorInputContainer);
|
69
|
-
container.replaceChildren(colorRow);
|
70
|
-
|
71
|
-
return {
|
72
|
-
addTo: (parent: HTMLElement) => {
|
73
|
-
parent.appendChild(container);
|
74
|
-
},
|
75
|
-
update,
|
76
|
-
};
|
77
|
-
};
|
78
|
-
|
79
|
-
export default class SelectionToolWidget extends BaseToolWidget {
|
80
|
-
private updateFormatMenu: ()=>void = () => {};
|
81
|
-
|
82
|
-
public constructor(
|
83
|
-
editor: Editor, private tool: SelectionTool, localization?: ToolbarLocalization
|
84
|
-
) {
|
85
|
-
super(editor, tool, 'selection-tool-widget', localization);
|
86
|
-
|
87
|
-
const resizeButton = new ActionButtonWidget(
|
88
|
-
editor, 'resize-btn',
|
89
|
-
() => editor.icons.makeResizeImageToSelectionIcon(),
|
90
|
-
this.localizationTable.resizeImageToSelection,
|
91
|
-
() => {
|
92
|
-
this.resizeImageToSelection();
|
93
|
-
},
|
94
|
-
localization,
|
95
|
-
);
|
96
|
-
const deleteButton = new ActionButtonWidget(
|
97
|
-
editor, 'delete-btn',
|
98
|
-
() => editor.icons.makeDeleteSelectionIcon(),
|
99
|
-
this.localizationTable.deleteSelection,
|
100
|
-
() => {
|
101
|
-
const selection = this.tool.getSelection();
|
102
|
-
this.editor.dispatch(selection!.deleteSelectedObjects());
|
103
|
-
this.tool.clearSelection();
|
104
|
-
},
|
105
|
-
localization,
|
106
|
-
);
|
107
|
-
const duplicateButton = new ActionButtonWidget(
|
108
|
-
editor, 'duplicate-btn',
|
109
|
-
() => editor.icons.makeDuplicateSelectionIcon(),
|
110
|
-
this.localizationTable.duplicateSelection,
|
111
|
-
async () => {
|
112
|
-
const selection = this.tool.getSelection();
|
113
|
-
this.editor.dispatch(await selection!.duplicateSelectedObjects());
|
114
|
-
this.setDropdownVisible(false);
|
115
|
-
},
|
116
|
-
localization,
|
117
|
-
);
|
118
|
-
|
119
|
-
this.addSubWidget(resizeButton);
|
120
|
-
this.addSubWidget(deleteButton);
|
121
|
-
this.addSubWidget(duplicateButton);
|
122
|
-
|
123
|
-
const updateDisabled = (disabled: boolean) => {
|
124
|
-
resizeButton.setDisabled(disabled);
|
125
|
-
deleteButton.setDisabled(disabled);
|
126
|
-
duplicateButton.setDisabled(disabled);
|
127
|
-
};
|
128
|
-
updateDisabled(true);
|
129
|
-
|
130
|
-
// Enable/disable actions based on whether items are selected
|
131
|
-
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
132
|
-
if (toolEvt.kind !== EditorEventType.ToolUpdated) {
|
133
|
-
throw new Error('Invalid event type!');
|
134
|
-
}
|
135
|
-
|
136
|
-
if (toolEvt.tool === this.tool) {
|
137
|
-
const selection = this.tool.getSelection();
|
138
|
-
const hasSelection = selection && selection.getSelectedItemCount() > 0;
|
139
|
-
|
140
|
-
updateDisabled(!hasSelection);
|
141
|
-
this.updateFormatMenu();
|
142
|
-
}
|
143
|
-
});
|
144
|
-
}
|
145
|
-
|
146
|
-
private resizeImageToSelection() {
|
147
|
-
const selection = this.tool.getSelection();
|
148
|
-
if (selection) {
|
149
|
-
this.editor.dispatch(this.editor.setImportExportRect(selection.region));
|
150
|
-
}
|
151
|
-
}
|
152
|
-
|
153
|
-
protected override onKeyPress(event: KeyPressEvent): boolean {
|
154
|
-
const shortcuts = this.editor.shortcuts;
|
155
|
-
|
156
|
-
// Resize image to selection:
|
157
|
-
// Other keys are handled directly by the selection tool.
|
158
|
-
if (shortcuts.matchesShortcut(resizeImageToSelectionKeyboardShortcut, event)) {
|
159
|
-
this.resizeImageToSelection();
|
160
|
-
return true;
|
161
|
-
}
|
162
|
-
|
163
|
-
// If we didn't handle the event, allow the superclass to handle it.
|
164
|
-
if (super.onKeyPress(event)) {
|
165
|
-
return true;
|
166
|
-
}
|
167
|
-
return false;
|
168
|
-
}
|
169
|
-
|
170
|
-
protected getTitle(): string {
|
171
|
-
return this.localizationTable.select;
|
172
|
-
}
|
173
|
-
|
174
|
-
protected createIcon(): Element {
|
175
|
-
return this.editor.icons.makeSelectionIcon();
|
176
|
-
}
|
177
|
-
|
178
|
-
protected override fillDropdown(dropdown: HTMLElement): boolean {
|
179
|
-
super.fillDropdown(dropdown);
|
180
|
-
|
181
|
-
const controlsContainer = document.createElement('div');
|
182
|
-
controlsContainer.classList.add(`${toolbarCSSPrefix}nonbutton-controls-main-list`);
|
183
|
-
dropdown.appendChild(controlsContainer);
|
184
|
-
|
185
|
-
makeSeparator(this.localizationTable.reformatSelection).addTo(controlsContainer);
|
186
|
-
|
187
|
-
const formatMenu = makeFormatMenu(this.editor, this.tool, this.localizationTable);
|
188
|
-
formatMenu.addTo(controlsContainer);
|
189
|
-
this.updateFormatMenu = () => formatMenu.update();
|
190
|
-
|
191
|
-
formatMenu.update();
|
192
|
-
|
193
|
-
return true;
|
194
|
-
}
|
195
|
-
}
|
@@ -1,149 +0,0 @@
|
|
1
|
-
import { Color4 } from '@js-draw/math';
|
2
|
-
import Editor from '../../Editor';
|
3
|
-
import TextTool from '../../tools/TextTool';
|
4
|
-
import { EditorEventType } from '../../types';
|
5
|
-
import { toolbarCSSPrefix } from '../constants';
|
6
|
-
import { ToolbarLocalization } from '../localization';
|
7
|
-
import makeColorInput from './components/makeColorInput';
|
8
|
-
import BaseToolWidget from './BaseToolWidget';
|
9
|
-
import { SavedToolbuttonState } from './BaseWidget';
|
10
|
-
|
11
|
-
export default class TextToolWidget extends BaseToolWidget {
|
12
|
-
private updateDropdownInputs: (()=>void)|null = null;
|
13
|
-
public constructor(editor: Editor, private tool: TextTool, localization?: ToolbarLocalization) {
|
14
|
-
super(editor, tool, 'text-tool-widget', localization);
|
15
|
-
|
16
|
-
editor.notifier.on(EditorEventType.ToolUpdated, evt => {
|
17
|
-
if (evt.kind === EditorEventType.ToolUpdated && evt.tool === tool) {
|
18
|
-
this.updateIcon();
|
19
|
-
this.updateDropdownInputs?.();
|
20
|
-
}
|
21
|
-
});
|
22
|
-
}
|
23
|
-
|
24
|
-
protected getTitle(): string {
|
25
|
-
return this.targetTool.description;
|
26
|
-
}
|
27
|
-
|
28
|
-
protected createIcon(): Element {
|
29
|
-
const textStyle = this.tool.getTextStyle();
|
30
|
-
return this.editor.icons.makeTextIcon(textStyle);
|
31
|
-
}
|
32
|
-
|
33
|
-
private static idCounter: number = 0;
|
34
|
-
protected override fillDropdown(dropdown: HTMLElement): boolean {
|
35
|
-
const container = document.createElement('div');
|
36
|
-
container.classList.add(
|
37
|
-
`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`
|
38
|
-
);
|
39
|
-
const fontRow = document.createElement('div');
|
40
|
-
const colorRow = document.createElement('div');
|
41
|
-
const sizeRow = document.createElement('div');
|
42
|
-
|
43
|
-
const fontInput = document.createElement('select');
|
44
|
-
const fontLabel = document.createElement('label');
|
45
|
-
|
46
|
-
const sizeInput = document.createElement('input');
|
47
|
-
const sizeLabel = document.createElement('label');
|
48
|
-
|
49
|
-
const {
|
50
|
-
input: colorInput, container: colorInputContainer, setValue: setColorInputValue
|
51
|
-
} = makeColorInput(this.editor, color => {
|
52
|
-
this.tool.setColor(color);
|
53
|
-
});
|
54
|
-
const colorLabel = document.createElement('label');
|
55
|
-
|
56
|
-
const fontsInInput = new Set();
|
57
|
-
const addFontToInput = (fontName: string) => {
|
58
|
-
const option = document.createElement('option');
|
59
|
-
option.value = fontName;
|
60
|
-
option.textContent = fontName;
|
61
|
-
fontInput.appendChild(option);
|
62
|
-
fontsInInput.add(fontName);
|
63
|
-
};
|
64
|
-
|
65
|
-
sizeInput.setAttribute('type', 'number');
|
66
|
-
sizeInput.min = '1';
|
67
|
-
sizeInput.max = '128';
|
68
|
-
|
69
|
-
fontLabel.innerText = this.localizationTable.fontLabel;
|
70
|
-
colorLabel.innerText = this.localizationTable.colorLabel;
|
71
|
-
sizeLabel.innerText = this.localizationTable.textSize;
|
72
|
-
|
73
|
-
colorInput.id = `${toolbarCSSPrefix}-text-color-input-${TextToolWidget.idCounter++}`;
|
74
|
-
colorLabel.setAttribute('for', colorInput.id);
|
75
|
-
|
76
|
-
sizeInput.id = `${toolbarCSSPrefix}-text-size-input-${TextToolWidget.idCounter++}`;
|
77
|
-
sizeLabel.setAttribute('for', sizeInput.id);
|
78
|
-
|
79
|
-
addFontToInput('monospace');
|
80
|
-
addFontToInput('serif');
|
81
|
-
addFontToInput('sans-serif');
|
82
|
-
fontInput.id = `${toolbarCSSPrefix}-text-font-input-${TextToolWidget.idCounter++}`;
|
83
|
-
fontLabel.setAttribute('for', fontInput.id);
|
84
|
-
|
85
|
-
fontInput.onchange = () => {
|
86
|
-
this.tool.setFontFamily(fontInput.value);
|
87
|
-
};
|
88
|
-
|
89
|
-
sizeInput.onchange = () => {
|
90
|
-
const size = parseInt(sizeInput.value);
|
91
|
-
if (!isNaN(size) && size > 0) {
|
92
|
-
this.tool.setFontSize(size);
|
93
|
-
}
|
94
|
-
};
|
95
|
-
|
96
|
-
colorRow.appendChild(colorLabel);
|
97
|
-
colorRow.appendChild(colorInputContainer);
|
98
|
-
|
99
|
-
fontRow.appendChild(fontLabel);
|
100
|
-
fontRow.appendChild(fontInput);
|
101
|
-
|
102
|
-
sizeRow.appendChild(sizeLabel);
|
103
|
-
sizeRow.appendChild(sizeInput);
|
104
|
-
|
105
|
-
this.updateDropdownInputs = () => {
|
106
|
-
const style = this.tool.getTextStyle();
|
107
|
-
setColorInputValue(style.renderingStyle.fill);
|
108
|
-
|
109
|
-
if (!fontsInInput.has(style.fontFamily)) {
|
110
|
-
addFontToInput(style.fontFamily);
|
111
|
-
}
|
112
|
-
fontInput.value = style.fontFamily;
|
113
|
-
sizeInput.value = `${style.size}`;
|
114
|
-
};
|
115
|
-
this.updateDropdownInputs();
|
116
|
-
|
117
|
-
container.replaceChildren(colorRow, sizeRow, fontRow);
|
118
|
-
dropdown.appendChild(container);
|
119
|
-
return true;
|
120
|
-
}
|
121
|
-
|
122
|
-
public override serializeState(): SavedToolbuttonState {
|
123
|
-
const textStyle = this.tool.getTextStyle();
|
124
|
-
|
125
|
-
return {
|
126
|
-
...super.serializeState(),
|
127
|
-
|
128
|
-
fontFamily: textStyle.fontFamily,
|
129
|
-
textSize: textStyle.size,
|
130
|
-
color: textStyle.renderingStyle.fill.toHexString(),
|
131
|
-
};
|
132
|
-
}
|
133
|
-
|
134
|
-
public override deserializeFrom(state: SavedToolbuttonState) {
|
135
|
-
if (state.fontFamily && typeof(state.fontFamily) === 'string') {
|
136
|
-
this.tool.setFontFamily(state.fontFamily);
|
137
|
-
}
|
138
|
-
|
139
|
-
if (state.color && typeof(state.color) === 'string') {
|
140
|
-
this.tool.setColor(Color4.fromHex(state.color));
|
141
|
-
}
|
142
|
-
|
143
|
-
if (state.textSize && typeof(state.textSize) === 'number') {
|
144
|
-
this.tool.setFontSize(state.textSize);
|
145
|
-
}
|
146
|
-
|
147
|
-
super.deserializeFrom(state);
|
148
|
-
}
|
149
|
-
}
|
@@ -1,184 +0,0 @@
|
|
1
|
-
import { Color4 } from '@js-draw/math';
|
2
|
-
import Editor from '../../../Editor';
|
3
|
-
import PipetteTool from '../../../tools/PipetteTool';
|
4
|
-
import { EditorEventType } from '../../../types';
|
5
|
-
|
6
|
-
type OnColorChangeListener = (color: Color4)=>void;
|
7
|
-
|
8
|
-
// Returns [ color input, input container, callback to change the color value ].
|
9
|
-
export const makeColorInput = (
|
10
|
-
editor: Editor, onColorChange: OnColorChangeListener
|
11
|
-
) => {
|
12
|
-
|
13
|
-
const colorInputContainer = document.createElement('span');
|
14
|
-
const colorInput = document.createElement('input');
|
15
|
-
|
16
|
-
colorInput.type = 'button';
|
17
|
-
colorInput.classList.add('coloris_input');
|
18
|
-
colorInputContainer.classList.add('color-input-container');
|
19
|
-
|
20
|
-
colorInputContainer.appendChild(colorInput);
|
21
|
-
const pipetteController = addPipetteTool(editor, colorInputContainer, (color: Color4) => {
|
22
|
-
colorInput.value = color.toHexString();
|
23
|
-
onInputEnd();
|
24
|
-
|
25
|
-
// Update the color preview, if it exists (may be managed by Coloris).
|
26
|
-
const parentElem = colorInput.parentElement;
|
27
|
-
if (parentElem && parentElem.classList.contains('clr-field')) {
|
28
|
-
parentElem.style.color = colorInput.value;
|
29
|
-
}
|
30
|
-
});
|
31
|
-
|
32
|
-
let currentColor: Color4|undefined;
|
33
|
-
const handleColorInput = () => {
|
34
|
-
currentColor = Color4.fromHex(colorInput.value);
|
35
|
-
};
|
36
|
-
|
37
|
-
// Only change the pen color when we finish sending input (this limits the number of
|
38
|
-
// editor events triggered and accessibility announcements).
|
39
|
-
const onInputEnd = () => {
|
40
|
-
handleColorInput();
|
41
|
-
|
42
|
-
if (currentColor) {
|
43
|
-
editor.announceForAccessibility(
|
44
|
-
editor.localization.colorChangedAnnouncement(currentColor.toHexString())
|
45
|
-
);
|
46
|
-
onColorChange(currentColor);
|
47
|
-
editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
|
48
|
-
kind: EditorEventType.ColorPickerColorSelected,
|
49
|
-
color: currentColor,
|
50
|
-
});
|
51
|
-
}
|
52
|
-
};
|
53
|
-
|
54
|
-
colorInput.oninput = handleColorInput;
|
55
|
-
let isOpen = false;
|
56
|
-
colorInput.addEventListener('open', () => {
|
57
|
-
isOpen = true;
|
58
|
-
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
59
|
-
kind: EditorEventType.ColorPickerToggled,
|
60
|
-
open: true,
|
61
|
-
});
|
62
|
-
pipetteController.cancel();
|
63
|
-
colorInputContainer.classList.add('picker-open');
|
64
|
-
|
65
|
-
// Focus the Coloris color picker, if it exists.
|
66
|
-
// Don't focus the text input within the color picker, however,
|
67
|
-
// as this displays a keyboard on mobile devices.
|
68
|
-
const colorPickerElem: HTMLElement|null = document.querySelector('#clr-picker #clr-hue-slider');
|
69
|
-
colorPickerElem?.focus();
|
70
|
-
});
|
71
|
-
|
72
|
-
const onClose = () => {
|
73
|
-
isOpen = false;
|
74
|
-
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
75
|
-
kind: EditorEventType.ColorPickerToggled,
|
76
|
-
open: false,
|
77
|
-
});
|
78
|
-
onInputEnd();
|
79
|
-
|
80
|
-
// Restore focus to the input that opened the color picker
|
81
|
-
colorInput.focus();
|
82
|
-
|
83
|
-
colorInputContainer.classList.remove('picker-open');
|
84
|
-
};
|
85
|
-
colorInput.addEventListener('close', () => {
|
86
|
-
onClose();
|
87
|
-
});
|
88
|
-
|
89
|
-
const setColorInputValue = (color: Color4|string) => {
|
90
|
-
if (typeof color === 'object') {
|
91
|
-
color = color.toHexString();
|
92
|
-
}
|
93
|
-
|
94
|
-
colorInput.value = color;
|
95
|
-
|
96
|
-
// Fire all color event listeners. See
|
97
|
-
// https://github.com/mdbassit/Coloris#manually-updating-the-thumbnail
|
98
|
-
colorInput.dispatchEvent(new Event('input', { bubbles: true }));
|
99
|
-
};
|
100
|
-
|
101
|
-
return {
|
102
|
-
input: colorInput,
|
103
|
-
container: colorInputContainer,
|
104
|
-
setValue: setColorInputValue,
|
105
|
-
closePicker: () => {
|
106
|
-
if (isOpen) {
|
107
|
-
onInputEnd();
|
108
|
-
}
|
109
|
-
},
|
110
|
-
};
|
111
|
-
};
|
112
|
-
|
113
|
-
const addPipetteTool = (editor: Editor, container: HTMLElement, onColorChange: OnColorChangeListener) => {
|
114
|
-
const pipetteButton = document.createElement('button');
|
115
|
-
pipetteButton.classList.add('pipetteButton');
|
116
|
-
pipetteButton.title = editor.localization.pickColorFromScreen;
|
117
|
-
pipetteButton.setAttribute('alt', pipetteButton.title);
|
118
|
-
|
119
|
-
const pickColorLabel = document.createElement('span');
|
120
|
-
pickColorLabel.classList.add('pickColorInstructions');
|
121
|
-
pickColorLabel.innerText = editor.localization.clickToPickColorAnnouncement;
|
122
|
-
|
123
|
-
const updatePipetteButtonContent = (color?: Color4) => {
|
124
|
-
pipetteButton.replaceChildren(
|
125
|
-
editor.icons.makePipetteIcon(color), pickColorLabel
|
126
|
-
);
|
127
|
-
};
|
128
|
-
updatePipetteButtonContent();
|
129
|
-
|
130
|
-
const pipetteTool: PipetteTool|undefined = editor.toolController.getMatchingTools(PipetteTool)[0];
|
131
|
-
|
132
|
-
const endColorSelectMode = () => {
|
133
|
-
pipetteTool?.clearColorListener();
|
134
|
-
updatePipetteButtonContent();
|
135
|
-
pipetteButton.classList.remove('active');
|
136
|
-
};
|
137
|
-
|
138
|
-
const pipetteColorSelect = (color: Color4|null) => {
|
139
|
-
endColorSelectMode();
|
140
|
-
|
141
|
-
if (color) {
|
142
|
-
onColorChange(color);
|
143
|
-
}
|
144
|
-
};
|
145
|
-
|
146
|
-
const pipetteColorPreview = (color: Color4|null) => {
|
147
|
-
if (color) {
|
148
|
-
updatePipetteButtonContent(color);
|
149
|
-
} else {
|
150
|
-
updatePipetteButtonContent();
|
151
|
-
}
|
152
|
-
};
|
153
|
-
|
154
|
-
pipetteButton.onclick = () => {
|
155
|
-
// If already picking, cancel it.
|
156
|
-
if (pipetteButton.classList.contains('active')) {
|
157
|
-
endColorSelectMode();
|
158
|
-
editor.announceForAccessibility(editor.localization.colorSelectionCanceledAnnouncement);
|
159
|
-
return;
|
160
|
-
}
|
161
|
-
|
162
|
-
pipetteTool?.setColorListener(
|
163
|
-
pipetteColorPreview,
|
164
|
-
pipetteColorSelect,
|
165
|
-
);
|
166
|
-
|
167
|
-
if (pipetteTool) {
|
168
|
-
pipetteButton.classList.add('active');
|
169
|
-
|
170
|
-
editor.announceForAccessibility(editor.localization.clickToPickColorAnnouncement);
|
171
|
-
}
|
172
|
-
};
|
173
|
-
|
174
|
-
container.appendChild(pipetteButton);
|
175
|
-
|
176
|
-
return {
|
177
|
-
// Cancel a pipette color selection if one is in progress.
|
178
|
-
cancel: () => {
|
179
|
-
endColorSelectMode();
|
180
|
-
},
|
181
|
-
};
|
182
|
-
};
|
183
|
-
|
184
|
-
export default makeColorInput;
|
@@ -1,128 +0,0 @@
|
|
1
|
-
import ReactiveValue, { MutableReactiveValue } from '../../../util/ReactiveValue';
|
2
|
-
import { ToolbarContext } from '../../types';
|
3
|
-
|
4
|
-
let idCounter = 0;
|
5
|
-
|
6
|
-
/**
|
7
|
-
* Creates a stylized file input.
|
8
|
-
*/
|
9
|
-
const makeFileInput = (labelText: string, context: ToolbarContext, accepts: string = '*') => {
|
10
|
-
const container = document.createElement('div');
|
11
|
-
const label = document.createElement('label');
|
12
|
-
const input = document.createElement('input');
|
13
|
-
|
14
|
-
const descriptionBox = document.createElement('div');
|
15
|
-
descriptionBox.classList.add('toolbar--file-input-description');
|
16
|
-
const descriptionText = document.createElement('span');
|
17
|
-
|
18
|
-
container.classList.add('toolbar--file-input-container');
|
19
|
-
|
20
|
-
label.appendChild(document.createTextNode(labelText));
|
21
|
-
input.accept = accepts;
|
22
|
-
input.type = 'file';
|
23
|
-
|
24
|
-
// Associate the label with the input
|
25
|
-
const inputId = `js-draw-file-input-${idCounter ++}`;
|
26
|
-
input.setAttribute('id', inputId);
|
27
|
-
label.htmlFor = inputId;
|
28
|
-
|
29
|
-
const icon = context.icons.makeUploadFileIcon();
|
30
|
-
icon.classList.add('icon');
|
31
|
-
|
32
|
-
descriptionBox.replaceChildren(icon, descriptionText);
|
33
|
-
label.appendChild(descriptionBox);
|
34
|
-
container.replaceChildren(label, input);
|
35
|
-
|
36
|
-
const selectedFiles: MutableReactiveValue<File[]> = ReactiveValue.fromInitialValue([]);
|
37
|
-
|
38
|
-
// Support droping files
|
39
|
-
label.addEventListener('dragover', event => {
|
40
|
-
event.preventDefault();
|
41
|
-
label.classList.add('drag-target');
|
42
|
-
});
|
43
|
-
label.addEventListener('dragenter', event => {
|
44
|
-
event.preventDefault();
|
45
|
-
label.classList.add('drag-target');
|
46
|
-
});
|
47
|
-
label.addEventListener('dragleave', event => {
|
48
|
-
event.preventDefault();
|
49
|
-
|
50
|
-
// Ensure the event wasn't targeting a child.
|
51
|
-
// See https://stackoverflow.com/a/54271161 and
|
52
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget
|
53
|
-
const enteringElement = event.relatedTarget as HTMLElement;
|
54
|
-
if (!enteringElement || !label.contains(enteringElement)) {
|
55
|
-
label.classList.remove('drag-target');
|
56
|
-
}
|
57
|
-
});
|
58
|
-
|
59
|
-
// See https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#process_the_drop
|
60
|
-
label.addEventListener('drop', event => {
|
61
|
-
event.preventDefault();
|
62
|
-
label.classList.remove('drag-target');
|
63
|
-
|
64
|
-
const fileList: File[] = [];
|
65
|
-
|
66
|
-
if (event.dataTransfer) {
|
67
|
-
fileList.push(...event.dataTransfer.files);
|
68
|
-
}
|
69
|
-
|
70
|
-
selectedFiles.set(fileList);
|
71
|
-
});
|
72
|
-
input.addEventListener('change', () => {
|
73
|
-
const fileList = input.files ?? [];
|
74
|
-
selectedFiles.set([ ...fileList ]);
|
75
|
-
});
|
76
|
-
|
77
|
-
selectedFiles.onUpdate(files => {
|
78
|
-
if (files.length === 0 && input.files && input.files.length > 0) {
|
79
|
-
input.value = '';
|
80
|
-
}
|
81
|
-
});
|
82
|
-
|
83
|
-
// Update the status text and hide/show the icon.
|
84
|
-
selectedFiles.onUpdateAndNow(files => {
|
85
|
-
if (files.length > 0) {
|
86
|
-
descriptionText.innerText = files.map(file => file.name).join('\n');
|
87
|
-
|
88
|
-
// Only show the icon when there are files
|
89
|
-
icon.style.display = 'none';
|
90
|
-
} else {
|
91
|
-
// Show the icon
|
92
|
-
icon.style.display = '';
|
93
|
-
|
94
|
-
const text = context.localization.dragAndDropHereOrBrowse;
|
95
|
-
|
96
|
-
// Split into regions surrounded by {{curly braces}} and regions that are
|
97
|
-
// not.
|
98
|
-
// When given a regular expression, `.split` outputs an array. For example,
|
99
|
-
// "a test __of__ split".split(/__(.*)__/)
|
100
|
-
// results in
|
101
|
-
// ['a test ', 'of', ' split'].
|
102
|
-
const segments = text.split(/[{]{2}(.*)[}]{2}/g);
|
103
|
-
descriptionText.replaceChildren();
|
104
|
-
|
105
|
-
for (let i = 0; i < segments.length; i++) {
|
106
|
-
// Inside a {{pair of curly braces}}?
|
107
|
-
if (i % 2 === 1) {
|
108
|
-
const boldedText = document.createElement('b');
|
109
|
-
boldedText.innerText = segments[i];
|
110
|
-
descriptionText.appendChild(boldedText);
|
111
|
-
} else {
|
112
|
-
descriptionText.appendChild(document.createTextNode(segments[i]));
|
113
|
-
}
|
114
|
-
}
|
115
|
-
}
|
116
|
-
});
|
117
|
-
|
118
|
-
return {
|
119
|
-
container,
|
120
|
-
input,
|
121
|
-
selectedFiles,
|
122
|
-
addTo: (parent: HTMLElement) => {
|
123
|
-
parent.appendChild(container);
|
124
|
-
},
|
125
|
-
};
|
126
|
-
};
|
127
|
-
|
128
|
-
export default makeFileInput;
|