js-draw 1.0.1 → 1.0.2
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/bundle.js +1 -1
- package/dist/cjs/version.js +1 -1
- 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/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
package/src/types.ts
DELETED
@@ -1,151 +0,0 @@
|
|
1
|
-
// Types related to the image editor
|
2
|
-
|
3
|
-
import type EventDispatcher from './EventDispatcher';
|
4
|
-
import type { Mat33, Point2, Vec2, Rect2, Color4 } from '@js-draw/math';
|
5
|
-
import type BaseTool from './tools/BaseTool';
|
6
|
-
import type AbstractComponent from './components/AbstractComponent';
|
7
|
-
import type Command from './commands/Command';
|
8
|
-
import type { WidgetContentLayoutManager } from './toolbar/widgets/layout/types';
|
9
|
-
|
10
|
-
export type EditorNotifier = EventDispatcher<EditorEventType, EditorEventDataType>;
|
11
|
-
|
12
|
-
export enum EditorEventType {
|
13
|
-
ToolEnabled,
|
14
|
-
ToolDisabled,
|
15
|
-
ToolUpdated,
|
16
|
-
|
17
|
-
UndoRedoStackUpdated,
|
18
|
-
CommandDone,
|
19
|
-
CommandUndone,
|
20
|
-
ObjectAdded,
|
21
|
-
|
22
|
-
ViewportChanged,
|
23
|
-
DisplayResized,
|
24
|
-
|
25
|
-
SelectionUpdated,
|
26
|
-
|
27
|
-
/** @internal */
|
28
|
-
ColorPickerToggled,
|
29
|
-
|
30
|
-
/** @internal */
|
31
|
-
ColorPickerColorSelected,
|
32
|
-
|
33
|
-
/** @deprecated @internal */
|
34
|
-
ToolbarDropdownShown,
|
35
|
-
}
|
36
|
-
|
37
|
-
// Types of `EditorUndoStackUpdated` events.
|
38
|
-
export enum UndoEventType {
|
39
|
-
CommandDone,
|
40
|
-
CommandUndone,
|
41
|
-
CommandRedone,
|
42
|
-
}
|
43
|
-
|
44
|
-
type EditorToolEventType = EditorEventType.ToolEnabled
|
45
|
-
| EditorEventType.ToolDisabled
|
46
|
-
| EditorEventType.ToolUpdated;
|
47
|
-
|
48
|
-
export interface EditorToolEvent {
|
49
|
-
readonly kind: EditorToolEventType;
|
50
|
-
readonly tool: BaseTool;
|
51
|
-
}
|
52
|
-
|
53
|
-
export interface EditorObjectEvent {
|
54
|
-
readonly kind: EditorEventType.ObjectAdded;
|
55
|
-
readonly object: AbstractComponent;
|
56
|
-
}
|
57
|
-
|
58
|
-
export interface EditorViewportChangedEvent {
|
59
|
-
readonly kind: EditorEventType.ViewportChanged;
|
60
|
-
|
61
|
-
// Canvas -> screen transform
|
62
|
-
readonly newTransform: Mat33;
|
63
|
-
readonly oldTransform: Mat33;
|
64
|
-
}
|
65
|
-
|
66
|
-
export interface DisplayResizedEvent {
|
67
|
-
readonly kind: EditorEventType.DisplayResized;
|
68
|
-
readonly newSize: Vec2;
|
69
|
-
}
|
70
|
-
|
71
|
-
export interface EditorUndoStackUpdated {
|
72
|
-
readonly kind: EditorEventType.UndoRedoStackUpdated;
|
73
|
-
|
74
|
-
readonly undoStackSize: number;
|
75
|
-
readonly redoStackSize: number;
|
76
|
-
|
77
|
-
readonly command?: Command;
|
78
|
-
readonly stackUpdateType: UndoEventType;
|
79
|
-
}
|
80
|
-
|
81
|
-
export interface CommandDoneEvent {
|
82
|
-
readonly kind: EditorEventType.CommandDone;
|
83
|
-
readonly command: Command;
|
84
|
-
}
|
85
|
-
|
86
|
-
export interface CommandUndoneEvent {
|
87
|
-
readonly kind: EditorEventType.CommandUndone;
|
88
|
-
readonly command: Command;
|
89
|
-
}
|
90
|
-
|
91
|
-
export interface SelectionUpdated {
|
92
|
-
readonly kind: EditorEventType.SelectionUpdated;
|
93
|
-
readonly selectedComponents: AbstractComponent[];
|
94
|
-
readonly tool: BaseTool;
|
95
|
-
}
|
96
|
-
|
97
|
-
export interface ColorPickerToggled {
|
98
|
-
readonly kind: EditorEventType.ColorPickerToggled;
|
99
|
-
readonly open: boolean;
|
100
|
-
}
|
101
|
-
|
102
|
-
export interface ColorPickerColorSelected {
|
103
|
-
readonly kind: EditorEventType.ColorPickerColorSelected;
|
104
|
-
readonly color: Color4;
|
105
|
-
}
|
106
|
-
|
107
|
-
export interface ToolbarDropdownShownEvent {
|
108
|
-
readonly kind: EditorEventType.ToolbarDropdownShown;
|
109
|
-
|
110
|
-
// True iff the source dropdown is toplevel.
|
111
|
-
readonly fromToplevelDropdown: boolean;
|
112
|
-
readonly layoutManager: WidgetContentLayoutManager;
|
113
|
-
}
|
114
|
-
|
115
|
-
export type EditorEventDataType = EditorToolEvent | EditorObjectEvent
|
116
|
-
| EditorViewportChangedEvent | DisplayResizedEvent
|
117
|
-
| EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent
|
118
|
-
| SelectionUpdated
|
119
|
-
| ColorPickerToggled | ColorPickerColorSelected
|
120
|
-
| ToolbarDropdownShownEvent;
|
121
|
-
|
122
|
-
|
123
|
-
// Returns a Promise to indicate that the event source should pause until the Promise resolves.
|
124
|
-
// Returns null to continue loading without pause.
|
125
|
-
// [totalToProcess] can be an estimate and may change if a better estimate becomes available.
|
126
|
-
export type OnProgressListener =
|
127
|
-
(amountProcessed: number, totalToProcess: number)=> Promise<void>|null|void;
|
128
|
-
|
129
|
-
export type ComponentAddedListener = (component: AbstractComponent)=> Promise<void>|void;
|
130
|
-
|
131
|
-
// Called when a new estimate for the import/export rect has been generated. This can be called multiple times.
|
132
|
-
// Only the last call to this listener must be accurate.
|
133
|
-
// The import/export rect is also returned by [start].
|
134
|
-
export type OnDetermineExportRectListener = (exportRect: Rect2)=> void;
|
135
|
-
|
136
|
-
export interface ImageLoader {
|
137
|
-
start(
|
138
|
-
onAddComponent: ComponentAddedListener,
|
139
|
-
onProgressListener: OnProgressListener,
|
140
|
-
onDetermineExportRect?: OnDetermineExportRectListener,
|
141
|
-
): Promise<void>;
|
142
|
-
}
|
143
|
-
|
144
|
-
export interface StrokeDataPoint {
|
145
|
-
pos: Point2;
|
146
|
-
width: number;
|
147
|
-
|
148
|
-
/** Time in milliseconds (e.g. as returned by `performance.now()`). */
|
149
|
-
time: number;
|
150
|
-
color: Color4;
|
151
|
-
}
|
@@ -1,168 +0,0 @@
|
|
1
|
-
import { ReactiveValue, MutableReactiveValue } from './ReactiveValue';
|
2
|
-
|
3
|
-
describe('ReactiveValue', () => {
|
4
|
-
it('should fire update listeners on update', () => {
|
5
|
-
const value = ReactiveValue.fromInitialValue(3);
|
6
|
-
const listener = jest.fn();
|
7
|
-
const { remove: removeListener } = value.onUpdateAndNow(listener);
|
8
|
-
|
9
|
-
expect(listener).toHaveBeenCalledWith(3);
|
10
|
-
expect(listener).toHaveBeenCalledTimes(1);
|
11
|
-
|
12
|
-
value.set(2);
|
13
|
-
|
14
|
-
expect(listener).toHaveBeenLastCalledWith(2);
|
15
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
16
|
-
|
17
|
-
removeListener();
|
18
|
-
|
19
|
-
value.set(4);
|
20
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
21
|
-
|
22
|
-
// Should be able to call remove multiple times without error.
|
23
|
-
removeListener();
|
24
|
-
value.set(6);
|
25
|
-
});
|
26
|
-
|
27
|
-
it('values from callbacks should derive values from the callback', () => {
|
28
|
-
const sourceValue1 = ReactiveValue.fromInitialValue('test');
|
29
|
-
const sourceValue2 = ReactiveValue.fromInitialValue('test2');
|
30
|
-
const sourceValue3 = ReactiveValue.fromImmutable(3);
|
31
|
-
|
32
|
-
// Create a value that is computed from the three above values.
|
33
|
-
const derivedValue1 = ReactiveValue.fromCallback(() => {
|
34
|
-
return [
|
35
|
-
sourceValue1.get(),
|
36
|
-
sourceValue2.get(),
|
37
|
-
sourceValue3.get(),
|
38
|
-
].join(',');
|
39
|
-
}, [ sourceValue1, sourceValue2, sourceValue3 ]);
|
40
|
-
|
41
|
-
const derivedValue1Listener = jest.fn();
|
42
|
-
derivedValue1.onUpdate(derivedValue1Listener);
|
43
|
-
|
44
|
-
expect(derivedValue1.get()).toBe('test,test2,3');
|
45
|
-
expect(derivedValue1Listener).toHaveBeenCalledTimes(0);
|
46
|
-
|
47
|
-
// Create a value that is computed just from derivedValue1
|
48
|
-
const derivedValue2 = ReactiveValue.fromCallback(() => {
|
49
|
-
return derivedValue1.get() + '!';
|
50
|
-
}, [ derivedValue1 ]);
|
51
|
-
|
52
|
-
const derivedValue2Listener = jest.fn();
|
53
|
-
derivedValue2.onUpdate(derivedValue2Listener);
|
54
|
-
|
55
|
-
expect(derivedValue2.get()).toBe('test,test2,3!');
|
56
|
-
expect(derivedValue1Listener).toHaveBeenCalledTimes(0);
|
57
|
-
expect(derivedValue2Listener).toHaveBeenCalledTimes(0);
|
58
|
-
|
59
|
-
// Changing the source values should update the derived values
|
60
|
-
sourceValue1.set('...');
|
61
|
-
|
62
|
-
// The change should trigger the listeners
|
63
|
-
expect(derivedValue1Listener).toHaveBeenCalledTimes(1);
|
64
|
-
expect(derivedValue1Listener).toHaveBeenCalledWith(derivedValue1.get());
|
65
|
-
expect(derivedValue2Listener).toHaveBeenCalledTimes(1);
|
66
|
-
expect(derivedValue2Listener).toHaveBeenCalledWith(derivedValue2.get());
|
67
|
-
|
68
|
-
// The values should be updated to the expected values.
|
69
|
-
expect(derivedValue1.get()).toBe('...,test2,3');
|
70
|
-
expect(derivedValue2.get()).toBe('...,test2,3!');
|
71
|
-
|
72
|
-
// Something similar should happen when other values are changed
|
73
|
-
sourceValue1.set('1');
|
74
|
-
sourceValue2.set('2');
|
75
|
-
|
76
|
-
expect(derivedValue1.get()).toBe('1,2,3');
|
77
|
-
expect(derivedValue2.get()).toBe('1,2,3!');
|
78
|
-
});
|
79
|
-
|
80
|
-
it('should be able to create values from properties', () => {
|
81
|
-
const sourceValue = ReactiveValue.fromInitialValue({
|
82
|
-
a: 1,
|
83
|
-
b: 2,
|
84
|
-
c: { d: 3 },
|
85
|
-
});
|
86
|
-
|
87
|
-
const destValue1 = MutableReactiveValue.fromProperty(sourceValue, 'c');
|
88
|
-
const destValue2 = MutableReactiveValue.fromProperty(sourceValue, 'b');
|
89
|
-
expect(destValue1.get()).toBe(sourceValue.get().c);
|
90
|
-
expect(destValue2.get()).toBe(2);
|
91
|
-
|
92
|
-
// Updating the source value should update the destination values.
|
93
|
-
sourceValue.set({
|
94
|
-
...sourceValue.get(),
|
95
|
-
c: { d: 4 },
|
96
|
-
});
|
97
|
-
|
98
|
-
expect(destValue1.get()).toBe(sourceValue.get().c);
|
99
|
-
expect(destValue1.get().d).toBe(4);
|
100
|
-
expect(destValue2.get()).toBe(2);
|
101
|
-
|
102
|
-
sourceValue.set({
|
103
|
-
a: 3,
|
104
|
-
b: 4,
|
105
|
-
c: { d: 5 },
|
106
|
-
});
|
107
|
-
expect(destValue1.get().d).toBe(5);
|
108
|
-
expect(destValue2.get()).toBe(4);
|
109
|
-
|
110
|
-
// Updating the destination values should update the source values
|
111
|
-
destValue1.set({ d: 8 });
|
112
|
-
expect(sourceValue.get().c.d).toBe(8);
|
113
|
-
|
114
|
-
destValue2.set(5);
|
115
|
-
expect(sourceValue.get().b).toBe(5);
|
116
|
-
});
|
117
|
-
|
118
|
-
it('mutable map should be bidirectional', () => {
|
119
|
-
const sourceValue = ReactiveValue.fromInitialValue(5);
|
120
|
-
const mappedValue = MutableReactiveValue.map(
|
121
|
-
sourceValue, a => a ** 2, b => Math.sqrt(b),
|
122
|
-
);
|
123
|
-
|
124
|
-
expect(mappedValue.get()).toBeCloseTo(25);
|
125
|
-
|
126
|
-
// Changing the destination should change the source
|
127
|
-
mappedValue.set(26);
|
128
|
-
expect(sourceValue.get()).toBeCloseTo(Math.sqrt(26));
|
129
|
-
|
130
|
-
// Changing the source should change the destination
|
131
|
-
sourceValue.set(10);
|
132
|
-
expect(mappedValue.get()).toBeCloseTo(100);
|
133
|
-
});
|
134
|
-
|
135
|
-
it('single-directional map should apply the given mapping function', () => {
|
136
|
-
const sourceValue = ReactiveValue.fromInitialValue(1);
|
137
|
-
const midValue = ReactiveValue.map(sourceValue, a => a * 2);
|
138
|
-
const destValue = ReactiveValue.map(midValue, _ => 0);
|
139
|
-
|
140
|
-
const sourceUpdateFn = jest.fn();
|
141
|
-
const midUpdateFn = jest.fn();
|
142
|
-
const destUpdateFn = jest.fn();
|
143
|
-
|
144
|
-
sourceValue.onUpdate(sourceUpdateFn);
|
145
|
-
midValue.onUpdate(midUpdateFn);
|
146
|
-
destValue.onUpdate(destUpdateFn);
|
147
|
-
|
148
|
-
// Initial value checking
|
149
|
-
expect(destValue.get()).toBe(0);
|
150
|
-
expect(sourceUpdateFn).toHaveBeenCalledTimes(0);
|
151
|
-
expect(midUpdateFn).toHaveBeenCalledTimes(0);
|
152
|
-
expect(destUpdateFn).toHaveBeenCalledTimes(0);
|
153
|
-
|
154
|
-
// Setting to the same value should trigger no listeners
|
155
|
-
sourceValue.set(1);
|
156
|
-
expect(sourceUpdateFn).toHaveBeenCalledTimes(0);
|
157
|
-
expect(midUpdateFn).toHaveBeenCalledTimes(0);
|
158
|
-
expect(destUpdateFn).toHaveBeenCalledTimes(0);
|
159
|
-
|
160
|
-
// Changing the initial value should only fire listeners that
|
161
|
-
// result in a different value
|
162
|
-
sourceValue.set(2);
|
163
|
-
expect(sourceUpdateFn).toHaveBeenCalledTimes(1);
|
164
|
-
expect(midUpdateFn).toHaveBeenCalledTimes(1);
|
165
|
-
expect(destUpdateFn).toHaveBeenCalledTimes(0);
|
166
|
-
expect(midValue.get()).toBe(4);
|
167
|
-
});
|
168
|
-
});
|
@@ -1,241 +0,0 @@
|
|
1
|
-
|
2
|
-
type ListenerResult = { remove(): void; };
|
3
|
-
type UpdateCallback<T> = (value: T)=>void;
|
4
|
-
|
5
|
-
const noOpUpdateListenerResult = {
|
6
|
-
remove() { }
|
7
|
-
};
|
8
|
-
|
9
|
-
/**
|
10
|
-
* An update listener that does nothing. Useful for reactive values
|
11
|
-
* that will never change.
|
12
|
-
*/
|
13
|
-
const noOpSetUpdateListener = () => {
|
14
|
-
return noOpUpdateListenerResult;
|
15
|
-
};
|
16
|
-
|
17
|
-
/**
|
18
|
-
* A `ReactiveValue` is a value that
|
19
|
-
* - updates periodically,
|
20
|
-
* - can fire listeners when it updates,
|
21
|
-
* - and can be chanined together with other `ReactiveValue`s.
|
22
|
-
*
|
23
|
-
* A `ReactiveValue` is a read-only view. See {@link MutableReactiveValue} for a
|
24
|
-
* read-write view.
|
25
|
-
*
|
26
|
-
* Static methods in the `ReactiveValue` and `MutableReactiveValue` classes are
|
27
|
-
* constructors (e.g. `fromImmutable`).
|
28
|
-
*/
|
29
|
-
export abstract class ReactiveValue<T> {
|
30
|
-
/**
|
31
|
-
* Returns a reference to the current value of this `ReactiveValue`.
|
32
|
-
*
|
33
|
-
* The result of this **should not be modified** (use `setValue` instead).
|
34
|
-
*/
|
35
|
-
public abstract get(): T;
|
36
|
-
|
37
|
-
/**
|
38
|
-
* Registers a listener that is notified when the value of this changes.
|
39
|
-
*/
|
40
|
-
public abstract onUpdate(listener: UpdateCallback<T>): ListenerResult;
|
41
|
-
|
42
|
-
/**
|
43
|
-
* Calls `callback` immediately, then registers `callback` as an onUpdateListener.
|
44
|
-
*
|
45
|
-
* @see {@link onUpdate}.
|
46
|
-
*/
|
47
|
-
public abstract onUpdateAndNow(callback: UpdateCallback<T>): ListenerResult;
|
48
|
-
|
49
|
-
/** Creates a `ReactiveValue` with an initial value, `initialValue`. */
|
50
|
-
public static fromInitialValue<T> (
|
51
|
-
initialValue: T
|
52
|
-
): MutableReactiveValue<T>{
|
53
|
-
return new ReactiveValueImpl(initialValue);
|
54
|
-
}
|
55
|
-
|
56
|
-
/** Returns a `ReactiveValue` that is **known** will never change. */
|
57
|
-
public static fromImmutable<T> (
|
58
|
-
value: T
|
59
|
-
): ReactiveValue<T>{
|
60
|
-
return {
|
61
|
-
get: () => value,
|
62
|
-
onUpdate: noOpSetUpdateListener,
|
63
|
-
onUpdateAndNow: callback => {
|
64
|
-
callback(value);
|
65
|
-
return noOpUpdateListenerResult;
|
66
|
-
},
|
67
|
-
};
|
68
|
-
}
|
69
|
-
|
70
|
-
/**
|
71
|
-
* Creates a `ReactiveValue` whose values come from `callback`.
|
72
|
-
*
|
73
|
-
* `callback` is called whenever any of `sourceValues` are updated and initially to
|
74
|
-
* set the initial value of the result.
|
75
|
-
*/
|
76
|
-
public static fromCallback<T> (
|
77
|
-
callback: ()=>T, sourceValues: ReactiveValue<any>[]
|
78
|
-
): ReactiveValue<T>{
|
79
|
-
const result = new ReactiveValueImpl(callback());
|
80
|
-
const resultRef = window.WeakRef ? new WeakRef(result) : { deref: () => result };
|
81
|
-
|
82
|
-
for (const value of sourceValues) {
|
83
|
-
const listener = value.onUpdate(() => {
|
84
|
-
// Use resultRef to allow `result` to be garbage collected
|
85
|
-
// despite this listener.
|
86
|
-
const value = resultRef.deref();
|
87
|
-
if (value) {
|
88
|
-
value.set(callback());
|
89
|
-
} else {
|
90
|
-
listener.remove();
|
91
|
-
}
|
92
|
-
});
|
93
|
-
}
|
94
|
-
|
95
|
-
return result;
|
96
|
-
}
|
97
|
-
|
98
|
-
/**
|
99
|
-
* Returns a reactive value derived from a single `source`.
|
100
|
-
*
|
101
|
-
* If `inverseMap` is `undefined`, the result is a read-only view.
|
102
|
-
*/
|
103
|
-
public static map<A, B> (
|
104
|
-
source: ReactiveValue<A>,
|
105
|
-
map: (a: A)=>B,
|
106
|
-
inverseMap?: undefined,
|
107
|
-
): ReactiveValue<B>;
|
108
|
-
|
109
|
-
|
110
|
-
/**
|
111
|
-
* Returns a reactive value derived from a single `source`.
|
112
|
-
*/
|
113
|
-
public static map<A, B> (
|
114
|
-
source: ReactiveValue<A>,
|
115
|
-
map: (a: A)=>B,
|
116
|
-
inverseMap: (b: B)=>A,
|
117
|
-
): MutableReactiveValue<B>;
|
118
|
-
|
119
|
-
|
120
|
-
public static map<A, B> (
|
121
|
-
source: MutableReactiveValue<A>,
|
122
|
-
map: (a: A)=>B,
|
123
|
-
inverseMap: ((b: B)=>A)|undefined,
|
124
|
-
): ReactiveValue<B>|MutableReactiveValue<B>{
|
125
|
-
const result = ReactiveValue.fromInitialValue(map(source.get()));
|
126
|
-
|
127
|
-
let expectedResultValue = result.get();
|
128
|
-
source.onUpdate(newValue => {
|
129
|
-
expectedResultValue = map(newValue);
|
130
|
-
result.set(expectedResultValue);
|
131
|
-
});
|
132
|
-
|
133
|
-
if (inverseMap) {
|
134
|
-
result.onUpdate(newValue => {
|
135
|
-
// Prevent infinite loops if inverseMap is not a true
|
136
|
-
// inverse.
|
137
|
-
if (newValue !== expectedResultValue) {
|
138
|
-
source.set(inverseMap(newValue));
|
139
|
-
}
|
140
|
-
});
|
141
|
-
}
|
142
|
-
|
143
|
-
return result;
|
144
|
-
}
|
145
|
-
}
|
146
|
-
|
147
|
-
export abstract class MutableReactiveValue<T> extends ReactiveValue<T> {
|
148
|
-
/**
|
149
|
-
* Changes the value of this and fires all update listeners.
|
150
|
-
*
|
151
|
-
* @see {@link onUpdate}
|
152
|
-
*/
|
153
|
-
public abstract set(newValue: T): void;
|
154
|
-
|
155
|
-
public static fromProperty<SourceType extends object, Name extends keyof SourceType> (
|
156
|
-
sourceValue: MutableReactiveValue<SourceType>,
|
157
|
-
propertyName: Name,
|
158
|
-
): MutableReactiveValue<SourceType[Name]>{
|
159
|
-
const child = ReactiveValue.fromInitialValue(
|
160
|
-
sourceValue.get()[propertyName]
|
161
|
-
);
|
162
|
-
const childRef = new WeakRef(child);
|
163
|
-
|
164
|
-
// When the source is updated...
|
165
|
-
const sourceListener = sourceValue.onUpdate(newValue => {
|
166
|
-
const childValue = childRef.deref();
|
167
|
-
|
168
|
-
if (childValue) {
|
169
|
-
childValue.set(newValue[propertyName]);
|
170
|
-
} else {
|
171
|
-
// TODO: What if `sourceValue` would be dropped before
|
172
|
-
// the child value?
|
173
|
-
sourceListener.remove();
|
174
|
-
}
|
175
|
-
});
|
176
|
-
|
177
|
-
// When the child is updated, also apply the update to the
|
178
|
-
// parent.
|
179
|
-
child.onUpdate(newValue => {
|
180
|
-
sourceValue.set({
|
181
|
-
...sourceValue.get(),
|
182
|
-
[propertyName]: newValue,
|
183
|
-
});
|
184
|
-
});
|
185
|
-
|
186
|
-
return child;
|
187
|
-
}
|
188
|
-
}
|
189
|
-
|
190
|
-
// @internal
|
191
|
-
class ReactiveValueImpl<T> extends MutableReactiveValue<T> {
|
192
|
-
#value: T;
|
193
|
-
#onUpdateListeners: Array<(value: T)=>void>;
|
194
|
-
|
195
|
-
public constructor(initialValue: T) {
|
196
|
-
super();
|
197
|
-
|
198
|
-
this.#value = initialValue;
|
199
|
-
this.#onUpdateListeners = [];
|
200
|
-
}
|
201
|
-
|
202
|
-
public set(newValue: T) {
|
203
|
-
if (this.#value === newValue) {
|
204
|
-
return;
|
205
|
-
}
|
206
|
-
|
207
|
-
this.#value = newValue;
|
208
|
-
|
209
|
-
for (const listener of this.#onUpdateListeners) {
|
210
|
-
listener(newValue);
|
211
|
-
}
|
212
|
-
}
|
213
|
-
|
214
|
-
public get() {
|
215
|
-
return this.#value;
|
216
|
-
}
|
217
|
-
|
218
|
-
public onUpdate(listener: (value: T)=>void) {
|
219
|
-
// **Note**: If memory is a concern, listeners should avoid referencing this
|
220
|
-
// reactive value directly. Doing so allows the value to be garbage collected when
|
221
|
-
// no longer referenced.
|
222
|
-
|
223
|
-
this.#onUpdateListeners.push(listener);
|
224
|
-
|
225
|
-
return {
|
226
|
-
remove: () => {
|
227
|
-
this.#onUpdateListeners = this.#onUpdateListeners.filter(otherListener => {
|
228
|
-
return otherListener !== listener;
|
229
|
-
});
|
230
|
-
},
|
231
|
-
};
|
232
|
-
}
|
233
|
-
|
234
|
-
public onUpdateAndNow(callback: (value: T)=>void) {
|
235
|
-
callback(this.get());
|
236
|
-
return this.onUpdate(callback);
|
237
|
-
}
|
238
|
-
}
|
239
|
-
|
240
|
-
|
241
|
-
export default ReactiveValue;
|
package/src/util/assertions.ts
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
|
2
|
-
/**
|
3
|
-
* Compile-time assertion that a branch of code is unreachable.
|
4
|
-
* @internal
|
5
|
-
*/
|
6
|
-
export const assertUnreachable = (key: never): never => {
|
7
|
-
// See https://stackoverflow.com/a/39419171/17055750
|
8
|
-
throw new Error(`Should be unreachable. Key: ${key}.`);
|
9
|
-
};
|
10
|
-
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Throws an exception if the typeof given value is not a number or `value` is NaN.
|
14
|
-
*
|
15
|
-
* @example
|
16
|
-
* ```ts
|
17
|
-
* const foo: unknown = 3;
|
18
|
-
* assertIsNumber(foo);
|
19
|
-
*
|
20
|
-
* assertIsNumber('hello, world'); // throws an Error.
|
21
|
-
* ```
|
22
|
-
*
|
23
|
-
*
|
24
|
-
*/
|
25
|
-
export const assertIsNumber = (value: any, allowNaN: boolean = false): value is number => {
|
26
|
-
if (typeof value !== 'number' || (!allowNaN && isNaN(value))) {
|
27
|
-
throw new Error('Given value is not a number');
|
28
|
-
// return false;
|
29
|
-
}
|
30
|
-
|
31
|
-
return true;
|
32
|
-
};
|
33
|
-
|
34
|
-
/**
|
35
|
-
* Throws if any of `values` is not of type number.
|
36
|
-
*/
|
37
|
-
export const assertIsNumberArray = (
|
38
|
-
values: any[], allowNaN: boolean = false
|
39
|
-
): values is number[] => {
|
40
|
-
if (typeof values !== 'object') {
|
41
|
-
throw new Error('Asserting isNumberArray: Given entity is not an array');
|
42
|
-
}
|
43
|
-
|
44
|
-
if (!assertIsNumber(values['length'])) {
|
45
|
-
return false;
|
46
|
-
}
|
47
|
-
|
48
|
-
for (const value of values) {
|
49
|
-
if (!assertIsNumber(value, allowNaN)) {
|
50
|
-
return false;
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
return true;
|
55
|
-
};
|
package/src/util/fileToBase64.ts
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
|
2
|
-
type ProgressListener = (evt: ProgressEvent<FileReader>)=> void;
|
3
|
-
const fileToBase64 = (file: File, onprogress?: ProgressListener): Promise<string|null> => {
|
4
|
-
const reader = new FileReader();
|
5
|
-
|
6
|
-
return new Promise((resolve: (result: string|null)=>void, reject) => {
|
7
|
-
reader.onload = () => resolve(reader.result as string|null);
|
8
|
-
reader.onerror = reject;
|
9
|
-
reader.onabort = reject;
|
10
|
-
reader.onprogress = (evt) => {
|
11
|
-
onprogress?.(evt);
|
12
|
-
};
|
13
|
-
|
14
|
-
reader.readAsDataURL(file);
|
15
|
-
});
|
16
|
-
};
|
17
|
-
|
18
|
-
export default fileToBase64;
|
@@ -1,36 +0,0 @@
|
|
1
|
-
|
2
|
-
// See https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values for
|
3
|
-
// more
|
4
|
-
const keyToKeyCode: Record<string, string> = {
|
5
|
-
'Control': 'ControlLeft',
|
6
|
-
'=': 'Equal',
|
7
|
-
'-': 'Minus',
|
8
|
-
';': 'Semicolon',
|
9
|
-
' ': 'Space',
|
10
|
-
};
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Attempts to guess the .code value corresponding to the given key.
|
14
|
-
*
|
15
|
-
* Use this to facilitate testing.
|
16
|
-
*
|
17
|
-
* If no matching keycode is found, returns `key`.
|
18
|
-
*/
|
19
|
-
const guessKeyCodeFromKey = (key: string) => {
|
20
|
-
const upperKey = key.toUpperCase();
|
21
|
-
if ('A' <= upperKey && upperKey <= 'Z') {
|
22
|
-
return `Key${upperKey}`;
|
23
|
-
}
|
24
|
-
|
25
|
-
if ('0' <= key && key <= '9') {
|
26
|
-
return `Digit${key}`;
|
27
|
-
}
|
28
|
-
|
29
|
-
if (key in keyToKeyCode) {
|
30
|
-
return keyToKeyCode[key];
|
31
|
-
}
|
32
|
-
|
33
|
-
return key;
|
34
|
-
};
|
35
|
-
|
36
|
-
export default guessKeyCodeFromKey;
|
@@ -1,19 +0,0 @@
|
|
1
|
-
|
2
|
-
/**
|
3
|
-
* Returns true iff all elements in the shorter list equal (===) the elements
|
4
|
-
* in the longer list.
|
5
|
-
*/
|
6
|
-
const listPrefixMatch = <T> (a: T[], b: T[]) => {
|
7
|
-
const shorter = a.length < b.length ? a : b;
|
8
|
-
const longer = shorter === a ? b : a;
|
9
|
-
|
10
|
-
for (let i = 0; i < shorter.length; i++) {
|
11
|
-
if (shorter[i] !== longer[i]) {
|
12
|
-
return false;
|
13
|
-
}
|
14
|
-
}
|
15
|
-
|
16
|
-
return true;
|
17
|
-
};
|
18
|
-
|
19
|
-
export default listPrefixMatch;
|
@@ -1,20 +0,0 @@
|
|
1
|
-
|
2
|
-
const stopPropagationOfScrollingWheelEvents = (scrollingContainer: HTMLElement) => {
|
3
|
-
scrollingContainer.onwheel = (event) => {
|
4
|
-
const hasScroll = scrollingContainer.clientWidth !== scrollingContainer.scrollWidth
|
5
|
-
&& event.deltaX !== 0;
|
6
|
-
const eventScrollsPastLeft =
|
7
|
-
scrollingContainer.scrollLeft + event.deltaX <= 0;
|
8
|
-
const scrollRight = scrollingContainer.scrollLeft + scrollingContainer.clientWidth;
|
9
|
-
const eventScrollsPastRight =
|
10
|
-
scrollRight + event.deltaX > scrollingContainer.scrollWidth;
|
11
|
-
|
12
|
-
// Stop the editor from receiving the event if it will scroll the pen type selector
|
13
|
-
// instead.
|
14
|
-
if (hasScroll && !eventScrollsPastLeft && !eventScrollsPastRight) {
|
15
|
-
event.stopPropagation();
|
16
|
-
}
|
17
|
-
};
|
18
|
-
};
|
19
|
-
|
20
|
-
export default stopPropagationOfScrollingWheelEvents;
|