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,61 +0,0 @@
|
|
1
|
-
import KeyBinding from './KeyBinding';
|
2
|
-
|
3
|
-
describe('KeyBinding', () => {
|
4
|
-
it('toString should produce keybinds that can be interpreted by fromString', () => {
|
5
|
-
const testStrings = [
|
6
|
-
'c',
|
7
|
-
'Shift+c',
|
8
|
-
'Ctrl+Shift+a',
|
9
|
-
'Ctrl+c',
|
10
|
-
'control',
|
11
|
-
'shift',
|
12
|
-
'meta',
|
13
|
-
'Ctrl+Alt+delete',
|
14
|
-
'leftarrow',
|
15
|
-
'CtrlOrMeta+z',
|
16
|
-
'á',
|
17
|
-
'-',
|
18
|
-
'+',
|
19
|
-
];
|
20
|
-
|
21
|
-
for (const str of testStrings) {
|
22
|
-
expect(KeyBinding.fromString(str).toString()).toBe(str);
|
23
|
-
}
|
24
|
-
});
|
25
|
-
|
26
|
-
it('ctrlOrMeta should match events with either ctrl or meta (or both) modifiers', () => {
|
27
|
-
const ctrlOrMetaA = KeyBinding.fromString('CtrlOrMeta-a');
|
28
|
-
const ctrlA = KeyBinding.fromString('ctrl-a');
|
29
|
-
const metaA = KeyBinding.fromString('meta-a');
|
30
|
-
const ctrlOrMetaShiftB = KeyBinding.fromString('CtrlOrMeta-Shift-b');
|
31
|
-
const shiftB = KeyBinding.fromString('Shift-b');
|
32
|
-
|
33
|
-
expect(ctrlOrMetaA.matchesEvent(ctrlA)).toBe(true);
|
34
|
-
expect(ctrlOrMetaA.matchesEvent(metaA)).toBe(true);
|
35
|
-
expect(ctrlOrMetaA.matchesEvent(ctrlOrMetaShiftB)).toBe(false);
|
36
|
-
expect(
|
37
|
-
ctrlOrMetaShiftB.matchesEvent({
|
38
|
-
key: 'b', shiftKey: true, metaKey: true
|
39
|
-
}),
|
40
|
-
).toBe(true);
|
41
|
-
expect(ctrlOrMetaShiftB.matchesEvent(shiftB)).toBe(false);
|
42
|
-
});
|
43
|
-
|
44
|
-
it('ctrl-1 should match ctrl + the number 1', () => {
|
45
|
-
const ctrl1 = KeyBinding.fromString('ctrl-1');
|
46
|
-
|
47
|
-
expect(ctrl1.matchesEvent({ key: '1', shiftKey: false, ctrlKey: true })).toBe(true);
|
48
|
-
expect(ctrl1.matchesEvent({ key: '1', shiftKey: true, ctrlKey: true })).toBe(true);
|
49
|
-
expect(ctrl1.matchesEvent({ key: '1', shiftKey: true, ctrlKey: false })).toBe(false);
|
50
|
-
});
|
51
|
-
|
52
|
-
it('ctrl-KeyA should match ctrl + event with code KeyA', () => {
|
53
|
-
const ctrlA = KeyBinding.fromString('ctrl-KeyA');
|
54
|
-
|
55
|
-
expect(ctrlA.matchesEvent({ code: 'KeyA', shiftKey: false, ctrlKey: true })).toBe(true);
|
56
|
-
expect(ctrlA.matchesEvent({ key: 'a', code: 'KeyA', shiftKey: false, ctrlKey: true })).toBe(true);
|
57
|
-
expect(ctrlA.matchesEvent({ key: 'a', code: 'KeyA', shiftKey: false, ctrlKey: false })).toBe(false);
|
58
|
-
expect(ctrlA.matchesEvent({ code: 'KeyB', shiftKey: false, ctrlKey: true })).toBe(false);
|
59
|
-
expect(ctrlA.matchesEvent({ code: 'KeyA', shiftKey: true, ctrlKey: true })).toBe(false);
|
60
|
-
});
|
61
|
-
});
|
@@ -1,257 +0,0 @@
|
|
1
|
-
|
2
|
-
export interface KeyCombination {
|
3
|
-
/** A key (e.g. `a`, `b`, `control`). */
|
4
|
-
readonly key: string;
|
5
|
-
|
6
|
-
/**
|
7
|
-
* The layout-independent name of the key being pressed. For example,
|
8
|
-
* KeyA for the `a` key.
|
9
|
-
*/
|
10
|
-
readonly code?: string;
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Whether the shift key must be pressed to trigger the shortcut.
|
14
|
-
*/
|
15
|
-
readonly shiftKey: boolean|undefined;
|
16
|
-
|
17
|
-
/** Whether the control key must be pressed to trigger the shortcut */
|
18
|
-
readonly ctrlKey: boolean;
|
19
|
-
|
20
|
-
/** Whether the alt key must be pressed to trigger the shortcut */
|
21
|
-
readonly altKey: boolean;
|
22
|
-
|
23
|
-
/** Whether the meta key must be pressed to trigger the shortcut */
|
24
|
-
readonly metaKey: boolean;
|
25
|
-
|
26
|
-
/** True if this shortcut/key combination applies for either control or meta. */
|
27
|
-
readonly controlOrMeta: boolean;
|
28
|
-
}
|
29
|
-
|
30
|
-
const isUppercaseLetter = (text: string) => {
|
31
|
-
return text.toUpperCase() === text
|
32
|
-
&& text.toLowerCase() !== text
|
33
|
-
&& text.length === 1;
|
34
|
-
};
|
35
|
-
|
36
|
-
const isLowercaseLetter = (text: string) => {
|
37
|
-
return text.toLowerCase() === text
|
38
|
-
&& text.toUpperCase() !== text
|
39
|
-
&& text.length === 1;
|
40
|
-
};
|
41
|
-
|
42
|
-
/** Represents a key combination that can trigger a keyboard shortcut. */
|
43
|
-
export default class KeyBinding implements KeyCombination {
|
44
|
-
/** @inheritdoc */
|
45
|
-
public readonly key: string;
|
46
|
-
|
47
|
-
/**
|
48
|
-
* If undefined, the state of the shift key is ignored.
|
49
|
-
*/
|
50
|
-
public readonly shiftKey: boolean|undefined;
|
51
|
-
|
52
|
-
/** @inheritdoc */
|
53
|
-
public readonly ctrlKey: boolean;
|
54
|
-
|
55
|
-
/** @inheritdoc */
|
56
|
-
public readonly altKey: boolean;
|
57
|
-
|
58
|
-
/** @inheritdoc */
|
59
|
-
public readonly metaKey: boolean;
|
60
|
-
|
61
|
-
/** @inheritdoc */
|
62
|
-
public readonly controlOrMeta: boolean;
|
63
|
-
|
64
|
-
public constructor(trigger: KeyCombination) {
|
65
|
-
this.key = trigger.key;
|
66
|
-
this.shiftKey = trigger.shiftKey;
|
67
|
-
this.ctrlKey = trigger.ctrlKey;
|
68
|
-
this.altKey = trigger.altKey;
|
69
|
-
this.metaKey = trigger.metaKey;
|
70
|
-
this.controlOrMeta = trigger.controlOrMeta;
|
71
|
-
}
|
72
|
-
|
73
|
-
/** Returns true if and only if `keyEvent` should trigger this shortcut. */
|
74
|
-
public matchesEvent(keyEvent: Partial<KeyCombination>) {
|
75
|
-
const lowercaseKey = keyEvent.key?.toLowerCase();
|
76
|
-
|
77
|
-
// Determine whether the input is an upper case letter or not.
|
78
|
-
const isUpperCaseKey = isUppercaseLetter(keyEvent.key ?? '');
|
79
|
-
const isLowercaseKey = isLowercaseLetter(keyEvent.key ?? '');
|
80
|
-
|
81
|
-
const ctrlKey = (keyEvent.ctrlKey ?? false) || lowercaseKey === 'control';
|
82
|
-
const altKey = (keyEvent.altKey ?? false) || lowercaseKey === 'alt';
|
83
|
-
const metaKey = (keyEvent.metaKey ?? false) || lowercaseKey === 'meta';
|
84
|
-
const shiftKey =
|
85
|
-
(keyEvent.shiftKey ?? isUpperCaseKey) || lowercaseKey === 'shift';
|
86
|
-
const keyEventHasCtrlOrMeta =
|
87
|
-
keyEvent.controlOrMeta || keyEvent.ctrlKey || keyEvent.metaKey || false;
|
88
|
-
|
89
|
-
// If we're not working with key codes,
|
90
|
-
if (this.key !== keyEvent.code) {
|
91
|
-
// Different keys entirely? They don't match.
|
92
|
-
if (this.key.toLowerCase() !== lowercaseKey) {
|
93
|
-
return false;
|
94
|
-
}
|
95
|
-
|
96
|
-
// If a case where the ASCII case of the given key might matter,
|
97
|
-
// compare.
|
98
|
-
if ((isUpperCaseKey || isLowercaseKey) && this.key !== keyEvent.key) {
|
99
|
-
// this.shiftKey may be interpreted as allowing this shortcut to be uppercased.
|
100
|
-
// If so, try making this.key uppercase and matching the shortcut.
|
101
|
-
const uppercaseKeyMatches = this.shiftKey === true && this.key.toUpperCase() === keyEvent.key;
|
102
|
-
if (!uppercaseKeyMatches) {
|
103
|
-
return false;
|
104
|
-
}
|
105
|
-
}
|
106
|
-
}
|
107
|
-
|
108
|
-
const shortcutControlOrMeta = this.controlOrMeta;
|
109
|
-
// Match ctrl/meta if the shortcut doesn't have controlOrMeta specified
|
110
|
-
// (controlOrMeta should match either).
|
111
|
-
const ctrlAndMetaMatches =
|
112
|
-
ctrlKey === this.ctrlKey
|
113
|
-
&& metaKey === this.metaKey
|
114
|
-
&& !shortcutControlOrMeta;
|
115
|
-
|
116
|
-
const matches =
|
117
|
-
(ctrlAndMetaMatches || (shortcutControlOrMeta && keyEventHasCtrlOrMeta))
|
118
|
-
&& altKey === this.altKey
|
119
|
-
&& (shiftKey === this.shiftKey || this.shiftKey === undefined);
|
120
|
-
return matches;
|
121
|
-
}
|
122
|
-
|
123
|
-
/**
|
124
|
-
* Returns a string representation of this shortcut in the same format accepted by
|
125
|
-
* {@link fromString}.
|
126
|
-
*/
|
127
|
-
public toString() {
|
128
|
-
const result = [];
|
129
|
-
|
130
|
-
if (this.ctrlKey && this.key !== 'control') {
|
131
|
-
result.push('Ctrl');
|
132
|
-
}
|
133
|
-
if (this.controlOrMeta) {
|
134
|
-
result.push('CtrlOrMeta');
|
135
|
-
}
|
136
|
-
if (this.altKey && this.key !== 'alt') {
|
137
|
-
result.push('Alt');
|
138
|
-
}
|
139
|
-
if (this.metaKey && this.key !== 'meta') {
|
140
|
-
result.push('Meta');
|
141
|
-
}
|
142
|
-
if (this.shiftKey && this.key !== 'shift') {
|
143
|
-
result.push('Shift');
|
144
|
-
}
|
145
|
-
result.push(this.key);
|
146
|
-
|
147
|
-
return result.join('+');
|
148
|
-
}
|
149
|
-
|
150
|
-
/**
|
151
|
-
* Accepts a string in the form `modifier1+modifier2+...+key` (e.g. `Ctrl+Shift+a`)
|
152
|
-
* and returns the corresponding `KeyboardShortcut`.
|
153
|
-
*/
|
154
|
-
public static fromString(shortcutStr: string): KeyBinding {
|
155
|
-
const getDefaultModifiers = (key: string) => {
|
156
|
-
// Unless a letter, as long as the given key matches, it shouldn't matter whether
|
157
|
-
// the shift key is pressed.
|
158
|
-
let shiftKey: boolean|undefined = undefined;
|
159
|
-
|
160
|
-
if (isUppercaseLetter(key)) {
|
161
|
-
shiftKey = true;
|
162
|
-
}
|
163
|
-
else if (isLowercaseLetter(key)) {
|
164
|
-
shiftKey = false;
|
165
|
-
}
|
166
|
-
// If not just a single character (e.g. a key code like KeyA), shift must
|
167
|
-
// be specified manually.
|
168
|
-
else if (key.length > 1) {
|
169
|
-
shiftKey = false;
|
170
|
-
}
|
171
|
-
|
172
|
-
const lowercaseKey = key.toLowerCase();
|
173
|
-
|
174
|
-
// shiftKey should always be true if the key is 'shift'
|
175
|
-
if (lowercaseKey === 'shift') {
|
176
|
-
shiftKey = true;
|
177
|
-
}
|
178
|
-
|
179
|
-
return {
|
180
|
-
shiftKey,
|
181
|
-
ctrlKey: lowercaseKey === 'control' || lowercaseKey === 'ctrl',
|
182
|
-
altKey: lowercaseKey === 'alt',
|
183
|
-
metaKey: lowercaseKey === 'meta',
|
184
|
-
controlOrMeta: lowercaseKey === 'control or meta' || lowercaseKey === 'ctrlormeta',
|
185
|
-
};
|
186
|
-
};
|
187
|
-
|
188
|
-
const hasNoModifiers = shortcutStr.search(/[-+]/) === -1 || shortcutStr.length === 1;
|
189
|
-
if (hasNoModifiers) {
|
190
|
-
const modifiers = getDefaultModifiers(shortcutStr);
|
191
|
-
|
192
|
-
return new KeyBinding({
|
193
|
-
key: shortcutStr,
|
194
|
-
...modifiers,
|
195
|
-
});
|
196
|
-
}
|
197
|
-
|
198
|
-
const keyModifiersExp = /^(.*[-+])?(.+)$/g;
|
199
|
-
const match = keyModifiersExp.exec(shortcutStr);
|
200
|
-
|
201
|
-
if (!match) {
|
202
|
-
throw new Error(`Invalid shortcut expression, ${shortcutStr}!`);
|
203
|
-
}
|
204
|
-
|
205
|
-
const key = match[2];
|
206
|
-
const defaultModifiers = getDefaultModifiers(key);
|
207
|
-
const modifierStrings = (match[1] ?? '').split(/[-+]/);
|
208
|
-
|
209
|
-
let shiftKey = defaultModifiers.shiftKey;
|
210
|
-
let ctrlKey = defaultModifiers.ctrlKey;
|
211
|
-
let altKey = defaultModifiers.altKey;
|
212
|
-
let metaKey = defaultModifiers.metaKey;
|
213
|
-
let controlOrMeta = defaultModifiers.controlOrMeta;
|
214
|
-
|
215
|
-
for (const modifier of modifierStrings) {
|
216
|
-
if (modifier === '') {
|
217
|
-
continue;
|
218
|
-
}
|
219
|
-
|
220
|
-
switch (modifier.toLowerCase()) {
|
221
|
-
case 'shift':
|
222
|
-
shiftKey = true;
|
223
|
-
break;
|
224
|
-
case 'anyshift':
|
225
|
-
shiftKey = undefined;
|
226
|
-
break;
|
227
|
-
case 'ctrl':
|
228
|
-
case 'control':
|
229
|
-
ctrlKey = true;
|
230
|
-
break;
|
231
|
-
case 'meta':
|
232
|
-
metaKey = true;
|
233
|
-
break;
|
234
|
-
case 'ctrlormeta':
|
235
|
-
case 'ctrl or meta':
|
236
|
-
case 'controlormeta':
|
237
|
-
controlOrMeta = true;
|
238
|
-
break;
|
239
|
-
case 'alt':
|
240
|
-
altKey = true;
|
241
|
-
break;
|
242
|
-
default:
|
243
|
-
throw new Error(`Unknown modifier: "${modifier}" in shortcut ${shortcutStr}.`);
|
244
|
-
}
|
245
|
-
}
|
246
|
-
|
247
|
-
const shortcut = new KeyBinding({
|
248
|
-
key,
|
249
|
-
shiftKey,
|
250
|
-
ctrlKey,
|
251
|
-
altKey,
|
252
|
-
metaKey,
|
253
|
-
controlOrMeta,
|
254
|
-
});
|
255
|
-
return shortcut;
|
256
|
-
}
|
257
|
-
}
|
@@ -1,95 +0,0 @@
|
|
1
|
-
import { undoKeyboardShortcutId } from '../tools/keybindings';
|
2
|
-
import KeyBinding from './KeyBinding';
|
3
|
-
import KeyboardShortcutManager from './KeyboardShortcutManager';
|
4
|
-
|
5
|
-
describe('KeyboardShortcutManager', () => {
|
6
|
-
it('should contain default shortcuts for undo', () => {
|
7
|
-
const defaultUndoShortcutKeybinds =
|
8
|
-
KeyboardShortcutManager.getShortcutDefaultKeybindings(undoKeyboardShortcutId);
|
9
|
-
expect(defaultUndoShortcutKeybinds.length).toBeGreaterThanOrEqual(1);
|
10
|
-
expect(defaultUndoShortcutKeybinds.some(shortcut => {
|
11
|
-
return shortcut.key === 'KeyZ' && shortcut.controlOrMeta;
|
12
|
-
})).toBe(true);
|
13
|
-
});
|
14
|
-
|
15
|
-
it('should be possible to override keyboard shortcuts', () => {
|
16
|
-
const testNewId = 'someIdThatDoesNowExist';
|
17
|
-
KeyboardShortcutManager.registerDefaultKeyboardShortcut(
|
18
|
-
testNewId,
|
19
|
-
[ KeyBinding.fromString('ctrl-1'), KeyBinding.fromString('-') ],
|
20
|
-
'Some description'
|
21
|
-
);
|
22
|
-
|
23
|
-
const shortcutManager = new KeyboardShortcutManager({
|
24
|
-
someIdThatDoesNotExist: [
|
25
|
-
KeyBinding.fromString('ctrl-shift-a'),
|
26
|
-
KeyBinding.fromString('ctrl-a'),
|
27
|
-
],
|
28
|
-
});
|
29
|
-
|
30
|
-
// Should work before overriding
|
31
|
-
expect(
|
32
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-1'))
|
33
|
-
).toBe(true);
|
34
|
-
expect(
|
35
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('-'))
|
36
|
-
).toBe(true);
|
37
|
-
expect(
|
38
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-b'))
|
39
|
-
).toBe(false);
|
40
|
-
expect(
|
41
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-0'))
|
42
|
-
).toBe(false);
|
43
|
-
expect(
|
44
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('1'))
|
45
|
-
).toBe(false);
|
46
|
-
|
47
|
-
shortcutManager.overrideShortcut(testNewId, [
|
48
|
-
KeyBinding.fromString('ctrl-b'),
|
49
|
-
]);
|
50
|
-
|
51
|
-
|
52
|
-
// Should work after overriding
|
53
|
-
expect(
|
54
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-1'))
|
55
|
-
).toBe(false);
|
56
|
-
expect(
|
57
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('-'))
|
58
|
-
).toBe(false);
|
59
|
-
expect(
|
60
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-b'))
|
61
|
-
).toBe(true);
|
62
|
-
expect(
|
63
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('ctrl-0'))
|
64
|
-
).toBe(false);
|
65
|
-
expect(
|
66
|
-
shortcutManager.matchesShortcut(testNewId, KeyBinding.fromString('1'))
|
67
|
-
).toBe(false);
|
68
|
-
});
|
69
|
-
|
70
|
-
it('should provide localized descriptions of keyboard shortcuts', () => {
|
71
|
-
const testNewId = 'someIdThatDoesNowExist--testingLocalization';
|
72
|
-
const defaultDescription = 'Some description 1';
|
73
|
-
KeyboardShortcutManager.registerDefaultKeyboardShortcut(
|
74
|
-
testNewId,
|
75
|
-
[ KeyBinding.fromString('ctrl-1'), KeyBinding.fromString('-') ],
|
76
|
-
defaultDescription
|
77
|
-
);
|
78
|
-
|
79
|
-
expect(KeyboardShortcutManager.getShortcutDescription(testNewId)).toBe(defaultDescription);
|
80
|
-
expect(
|
81
|
-
KeyboardShortcutManager.getShortcutDescription(testNewId, ['fakeLocale'])
|
82
|
-
).toBe(defaultDescription);
|
83
|
-
|
84
|
-
const spanishDescription = 'Alguna descripción';
|
85
|
-
KeyboardShortcutManager.provideShortcutDescription(
|
86
|
-
testNewId,
|
87
|
-
'es',
|
88
|
-
spanishDescription
|
89
|
-
);
|
90
|
-
|
91
|
-
expect(
|
92
|
-
KeyboardShortcutManager.getShortcutDescription(testNewId, ['es', 'en'])
|
93
|
-
).toBe(spanishDescription);
|
94
|
-
});
|
95
|
-
});
|
@@ -1,163 +0,0 @@
|
|
1
|
-
import { matchingLocalizationTable } from '../localizations/getLocalizationTable';
|
2
|
-
import KeyBinding, { KeyCombination } from './KeyBinding';
|
3
|
-
|
4
|
-
type ShortcutDictionary = Record<string, KeyBinding[]>;
|
5
|
-
|
6
|
-
// Maps from shortcut IDs to a description of each.
|
7
|
-
type ShortcutDescriptionDictionary = Record<string, string>;
|
8
|
-
|
9
|
-
/**
|
10
|
-
* Allows adding/changing keyboard shortcuts. This class provides static methods for registering
|
11
|
-
* default shortcuts. An instance of this class must be used to access or change keyboard shortcuts.
|
12
|
-
*/
|
13
|
-
export default class KeyboardShortcutManager {
|
14
|
-
private static shortcuts: ShortcutDictionary = Object.create(null);
|
15
|
-
private static shortcutDefaultDescriptions: ShortcutDescriptionDictionary
|
16
|
-
= Object.create(null);
|
17
|
-
private static shortcutLocalizedDescriptions: Record<string, ShortcutDescriptionDictionary>
|
18
|
-
= Object.create(null);
|
19
|
-
|
20
|
-
private shortcutOverrides: ShortcutDictionary = Object.create(null);
|
21
|
-
|
22
|
-
/**
|
23
|
-
* Creates a new `ShortcutManager` with an initial set of shortcut overrides.
|
24
|
-
*
|
25
|
-
* @internal
|
26
|
-
*/
|
27
|
-
public constructor(initialOverrides: ShortcutDictionary) {
|
28
|
-
for (const id in initialOverrides) {
|
29
|
-
this.overrideShortcut(id, initialOverrides[id]);
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
/**
|
34
|
-
* Override an existing shortcut with a custom set of triggers.
|
35
|
-
* @internal
|
36
|
-
*/
|
37
|
-
public overrideShortcut(shortcutId: string, overrideWith: KeyBinding[]) {
|
38
|
-
this.shortcutOverrides[shortcutId] = [ ...overrideWith ];
|
39
|
-
}
|
40
|
-
|
41
|
-
/** Returns true if `keyEvent` matches the shortcut with `shortcutId`. @internal */
|
42
|
-
public matchesShortcut(shortcutId: string, keyEvent: Partial<KeyCombination>) {
|
43
|
-
// Get all shortcucts associated with `shortcutId`.
|
44
|
-
let shortcutList = this.shortcutOverrides[shortcutId];
|
45
|
-
|
46
|
-
if (!shortcutList) {
|
47
|
-
if (shortcutId in KeyboardShortcutManager.shortcuts) {
|
48
|
-
shortcutList = KeyboardShortcutManager.shortcuts[shortcutId];
|
49
|
-
} else {
|
50
|
-
throw new Error(`No shortcut with ID ${shortcutId} exists!`);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
// return true if keyEvent matches *any* shortcuts in shortcutList
|
55
|
-
for (const shortcut of shortcutList) {
|
56
|
-
if (shortcut.matchesEvent(keyEvent)) {
|
57
|
-
return true;
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
return false;
|
62
|
-
}
|
63
|
-
|
64
|
-
/**
|
65
|
-
* Registers a default keyboard shortcut that can be overridden by individual instances
|
66
|
-
* of `ShortcutManager`. Note that `id` should be a globally unique identifier.
|
67
|
-
*
|
68
|
-
* Only the first call to this method for a given `id` has an effect.
|
69
|
-
*
|
70
|
-
* @example
|
71
|
-
* ```ts
|
72
|
-
* const shortcutId = 'io.github.personalizedrefrigerator.js-draw.select-all';
|
73
|
-
*
|
74
|
-
* // Associate two shortcuts with the same ID
|
75
|
-
* const shortcut1 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+a');
|
76
|
-
* const shortcut2 = KeyboardShortcutManager.keyboardShortcutFromString('ctrlOrMeta+shift+a');
|
77
|
-
* KeyboardShortcutManager.registerDefaultKeyboardShortcut(
|
78
|
-
* shortcutId,
|
79
|
-
* [ shortcut1, shortcut2 ],
|
80
|
-
* "Select All",
|
81
|
-
* );
|
82
|
-
*
|
83
|
-
* // Provide a localized description
|
84
|
-
* KeyboardShortcutManager.provideShortcutDescription(
|
85
|
-
* shotcutId,
|
86
|
-
* 'es',
|
87
|
-
* 'Seleccionar todo',
|
88
|
-
* );
|
89
|
-
* ```
|
90
|
-
*
|
91
|
-
* @internal
|
92
|
-
*/
|
93
|
-
public static registerDefaultKeyboardShortcut(
|
94
|
-
id: string,
|
95
|
-
shortcuts: (KeyBinding|string)[],
|
96
|
-
defaultDescription: string,
|
97
|
-
): boolean {
|
98
|
-
if (id in KeyboardShortcutManager.shortcuts) {
|
99
|
-
return false;
|
100
|
-
}
|
101
|
-
|
102
|
-
// Convert the strings to shortcut maps.
|
103
|
-
const shortcutsAsShortcuts = shortcuts.map(shortcut => {
|
104
|
-
if (typeof (shortcut) === 'string') {
|
105
|
-
return KeyBinding.fromString(shortcut);
|
106
|
-
}
|
107
|
-
return shortcut;
|
108
|
-
});
|
109
|
-
|
110
|
-
KeyboardShortcutManager.shortcuts[id] = [ ...shortcutsAsShortcuts ];
|
111
|
-
KeyboardShortcutManager.shortcutDefaultDescriptions[id] = defaultDescription;
|
112
|
-
return true;
|
113
|
-
}
|
114
|
-
|
115
|
-
/** Provides a localized description of a keyboard shortcut. @internal */
|
116
|
-
public static provideShortcutDescription(id: string, locale: string, description: string) {
|
117
|
-
if (!(locale in KeyboardShortcutManager.shortcutLocalizedDescriptions)) {
|
118
|
-
KeyboardShortcutManager.shortcutLocalizedDescriptions[locale] = Object.create(null);
|
119
|
-
}
|
120
|
-
|
121
|
-
KeyboardShortcutManager.shortcutLocalizedDescriptions[locale][id] = description;
|
122
|
-
}
|
123
|
-
|
124
|
-
/**
|
125
|
-
* Gets all registered keyboard shortcut IDs.
|
126
|
-
*
|
127
|
-
* @see {@link getShortcutDescription}
|
128
|
-
*/
|
129
|
-
public static getAllShortcutIds() {
|
130
|
-
const ids = [];
|
131
|
-
for (const id in this.shortcuts) {
|
132
|
-
ids.push(id);
|
133
|
-
}
|
134
|
-
return ids;
|
135
|
-
}
|
136
|
-
|
137
|
-
/**
|
138
|
-
* Get the default keybindings associated with a keyboard shortcut.
|
139
|
-
*
|
140
|
-
* Any keybinding in the resultant list, by default, can trigger the function associated
|
141
|
-
* with the shortcut.
|
142
|
-
*/
|
143
|
-
public static getShortcutDefaultKeybindings(shortcutId: string): KeyBinding[] {
|
144
|
-
if (!(shortcutId in KeyboardShortcutManager.shortcuts)) {
|
145
|
-
throw new Error(`No shortcut with ID ${shortcutId} exists!`);
|
146
|
-
}
|
147
|
-
|
148
|
-
return KeyboardShortcutManager.shortcuts[shortcutId];
|
149
|
-
}
|
150
|
-
|
151
|
-
/**
|
152
|
-
* Get a description of a keyboard shortcut.
|
153
|
-
*
|
154
|
-
* `localeList`, if given, attempts to
|
155
|
-
*/
|
156
|
-
public static getShortcutDescription(id: string, localeList?: readonly string[]): string|null {
|
157
|
-
const localizationTable = matchingLocalizationTable(
|
158
|
-
localeList ?? [], this.shortcutLocalizedDescriptions, this.shortcutDefaultDescriptions
|
159
|
-
);
|
160
|
-
|
161
|
-
return localizationTable[id] ?? this.shortcutDefaultDescriptions[id] ?? null;
|
162
|
-
}
|
163
|
-
}
|
package/src/shortcuts/lib.ts
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
import { RenderingMode } from '../rendering/Display';
|
2
|
-
import Editor from '../Editor';
|
3
|
-
|
4
|
-
/** Creates an editor. Should only be used in test files. */
|
5
|
-
export default () => {
|
6
|
-
if (jest === undefined) {
|
7
|
-
throw new Error('Files in the testing/ folder should only be used in tests!');
|
8
|
-
}
|
9
|
-
|
10
|
-
return new Editor(document.body, { renderingMode: RenderingMode.DummyRenderer });
|
11
|
-
};
|
@@ -1,18 +0,0 @@
|
|
1
|
-
import Pointer from '../Pointer';
|
2
|
-
|
3
|
-
/** Returns the smallest ID not used by the pointers in the given list. */
|
4
|
-
const getUniquePointerId = (pointers: Pointer[]) => {
|
5
|
-
let ptrId = 0;
|
6
|
-
|
7
|
-
const pointerIds = pointers.map(ptr => ptr.id);
|
8
|
-
pointerIds.sort();
|
9
|
-
for (const pointerId of pointerIds) {
|
10
|
-
if (ptrId === pointerId) {
|
11
|
-
ptrId = pointerId + 1;
|
12
|
-
}
|
13
|
-
}
|
14
|
-
|
15
|
-
return ptrId;
|
16
|
-
};
|
17
|
-
|
18
|
-
export default getUniquePointerId;
|
package/src/testing/lib.ts
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
import Editor from '../Editor';
|
2
|
-
import { Point2 } from '@js-draw/math';
|
3
|
-
import Pointer from '../Pointer';
|
4
|
-
import { InputEvtType } from '../inputEvents';
|
5
|
-
import getUniquePointerId from './getUniquePointerId';
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Dispatch a pen event to the currently selected tool.
|
9
|
-
* Intended for unit tests.
|
10
|
-
*
|
11
|
-
* @see {@link sendTouchEvent}
|
12
|
-
*/
|
13
|
-
const sendPenEvent = (
|
14
|
-
editor: Editor,
|
15
|
-
eventType: InputEvtType.PointerDownEvt|InputEvtType.PointerMoveEvt|InputEvtType.PointerUpEvt,
|
16
|
-
point: Point2,
|
17
|
-
|
18
|
-
allPointers?: Pointer[]
|
19
|
-
) => {
|
20
|
-
const id = getUniquePointerId(allPointers ?? []);
|
21
|
-
|
22
|
-
const mainPointer = Pointer.ofCanvasPoint(
|
23
|
-
point, eventType !== InputEvtType.PointerUpEvt, editor.viewport, id
|
24
|
-
);
|
25
|
-
|
26
|
-
editor.toolController.dispatchInputEvent({
|
27
|
-
kind: eventType,
|
28
|
-
allPointers: allPointers ?? [
|
29
|
-
mainPointer,
|
30
|
-
],
|
31
|
-
current: mainPointer,
|
32
|
-
});
|
33
|
-
|
34
|
-
return mainPointer;
|
35
|
-
};
|
36
|
-
export default sendPenEvent;
|