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.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Editor.css +1 -0
  3. package/dist/bundle.js +1 -1
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/toolbar/AbstractToolbar.d.ts +9 -13
  6. package/dist/cjs/toolbar/AbstractToolbar.js +14 -19
  7. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
  8. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +26 -0
  9. package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
  10. package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
  11. package/dist/cjs/version.js +1 -1
  12. package/dist/mjs/toolbar/AbstractToolbar.d.ts +9 -13
  13. package/dist/mjs/toolbar/AbstractToolbar.mjs +14 -19
  14. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +10 -0
  15. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +21 -0
  16. package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
  17. package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
  18. package/dist/mjs/version.mjs +1 -1
  19. package/docs/img/readme-images/js-draw.jpg +0 -0
  20. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  21. package/package.json +5 -4
  22. package/src/toolbar/EdgeToolbar.scss +1 -0
  23. package/dist-test/test_imports/package-lock.json +0 -13
  24. package/dist-test/test_imports/package.json +0 -12
  25. package/dist-test/test_imports/test-imports.js +0 -11
  26. package/dist-test/test_imports/test-require.cjs +0 -14
  27. package/src/Editor.loadFrom.test.ts +0 -24
  28. package/src/Editor.test.ts +0 -107
  29. package/src/Editor.toSVG.test.ts +0 -294
  30. package/src/Editor.ts +0 -1443
  31. package/src/EditorImage.test.ts +0 -117
  32. package/src/EditorImage.ts +0 -609
  33. package/src/EventDispatcher.test.ts +0 -123
  34. package/src/EventDispatcher.ts +0 -72
  35. package/src/Pointer.ts +0 -183
  36. package/src/SVGLoader.test.ts +0 -114
  37. package/src/SVGLoader.ts +0 -672
  38. package/src/UndoRedoHistory.test.ts +0 -34
  39. package/src/UndoRedoHistory.ts +0 -102
  40. package/src/Viewport.ts +0 -322
  41. package/src/bundle/bundled.ts +0 -7
  42. package/src/commands/Command.ts +0 -45
  43. package/src/commands/Duplicate.ts +0 -75
  44. package/src/commands/Erase.ts +0 -95
  45. package/src/commands/SerializableCommand.ts +0 -49
  46. package/src/commands/UnresolvedCommand.ts +0 -37
  47. package/src/commands/invertCommand.ts +0 -58
  48. package/src/commands/lib.ts +0 -16
  49. package/src/commands/localization.ts +0 -47
  50. package/src/commands/uniteCommands.test.ts +0 -23
  51. package/src/commands/uniteCommands.ts +0 -140
  52. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  53. package/src/components/AbstractComponent.ts +0 -383
  54. package/src/components/BackgroundComponent.test.ts +0 -44
  55. package/src/components/BackgroundComponent.ts +0 -348
  56. package/src/components/ImageComponent.ts +0 -176
  57. package/src/components/RestylableComponent.ts +0 -161
  58. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  59. package/src/components/Stroke.test.ts +0 -137
  60. package/src/components/Stroke.ts +0 -294
  61. package/src/components/TextComponent.test.ts +0 -202
  62. package/src/components/TextComponent.ts +0 -429
  63. package/src/components/UnknownSVGObject.test.ts +0 -10
  64. package/src/components/UnknownSVGObject.ts +0 -60
  65. package/src/components/builders/ArrowBuilder.ts +0 -106
  66. package/src/components/builders/CircleBuilder.ts +0 -100
  67. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  68. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  69. package/src/components/builders/LineBuilder.ts +0 -77
  70. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  71. package/src/components/builders/RectangleBuilder.ts +0 -73
  72. package/src/components/builders/types.ts +0 -15
  73. package/src/components/lib.ts +0 -31
  74. package/src/components/localization.ts +0 -24
  75. package/src/components/util/StrokeSmoother.ts +0 -302
  76. package/src/components/util/describeComponentList.ts +0 -18
  77. package/src/dialogs/makeAboutDialog.ts +0 -82
  78. package/src/inputEvents.ts +0 -143
  79. package/src/lib.ts +0 -91
  80. package/src/localization.ts +0 -34
  81. package/src/localizations/de.ts +0 -146
  82. package/src/localizations/en.ts +0 -8
  83. package/src/localizations/es.ts +0 -74
  84. package/src/localizations/getLocalizationTable.test.ts +0 -27
  85. package/src/localizations/getLocalizationTable.ts +0 -74
  86. package/src/rendering/Display.ts +0 -247
  87. package/src/rendering/RenderablePathSpec.ts +0 -88
  88. package/src/rendering/RenderingStyle.test.ts +0 -68
  89. package/src/rendering/RenderingStyle.ts +0 -55
  90. package/src/rendering/TextRenderingStyle.ts +0 -55
  91. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  92. package/src/rendering/caching/CacheRecord.ts +0 -76
  93. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  94. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  95. package/src/rendering/caching/RenderingCache.ts +0 -66
  96. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  97. package/src/rendering/caching/testUtils.ts +0 -35
  98. package/src/rendering/caching/types.ts +0 -34
  99. package/src/rendering/lib.ts +0 -8
  100. package/src/rendering/localization.ts +0 -20
  101. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  102. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  103. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  104. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  105. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  106. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  107. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  108. package/src/shortcuts/KeyBinding.test.ts +0 -61
  109. package/src/shortcuts/KeyBinding.ts +0 -257
  110. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  111. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  112. package/src/shortcuts/lib.ts +0 -3
  113. package/src/testing/createEditor.ts +0 -11
  114. package/src/testing/getUniquePointerId.ts +0 -18
  115. package/src/testing/lib.ts +0 -3
  116. package/src/testing/sendPenEvent.ts +0 -36
  117. package/src/testing/sendTouchEvent.ts +0 -71
  118. package/src/toolbar/AbstractToolbar.ts +0 -542
  119. package/src/toolbar/DropdownToolbar.ts +0 -220
  120. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  121. package/src/toolbar/EdgeToolbar.ts +0 -543
  122. package/src/toolbar/IconProvider.ts +0 -861
  123. package/src/toolbar/constants.ts +0 -1
  124. package/src/toolbar/lib.ts +0 -6
  125. package/src/toolbar/localization.ts +0 -136
  126. package/src/toolbar/types.ts +0 -13
  127. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  128. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  129. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  130. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  131. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  132. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  133. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  134. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  135. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  136. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  137. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  138. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  139. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  140. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  141. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  142. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  143. package/src/toolbar/widgets/keybindings.ts +0 -19
  144. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  145. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  146. package/src/toolbar/widgets/layout/types.ts +0 -74
  147. package/src/toolbar/widgets/lib.ts +0 -13
  148. package/src/tools/BaseTool.ts +0 -169
  149. package/src/tools/Eraser.test.ts +0 -103
  150. package/src/tools/Eraser.ts +0 -173
  151. package/src/tools/FindTool.test.ts +0 -67
  152. package/src/tools/FindTool.ts +0 -153
  153. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  154. package/src/tools/InputFilter/InputMapper.ts +0 -41
  155. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  156. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  157. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  158. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  159. package/src/tools/PanZoom.test.ts +0 -339
  160. package/src/tools/PanZoom.ts +0 -525
  161. package/src/tools/PasteHandler.ts +0 -94
  162. package/src/tools/Pen.test.ts +0 -260
  163. package/src/tools/Pen.ts +0 -284
  164. package/src/tools/PipetteTool.ts +0 -84
  165. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  166. package/src/tools/SelectionTool/Selection.ts +0 -647
  167. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  168. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  169. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  170. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  171. package/src/tools/SelectionTool/types.ts +0 -11
  172. package/src/tools/SoundUITool.ts +0 -221
  173. package/src/tools/TextTool.ts +0 -339
  174. package/src/tools/ToolController.ts +0 -224
  175. package/src/tools/ToolEnabledGroup.ts +0 -14
  176. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  177. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  178. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  179. package/src/tools/UndoRedoShortcut.ts +0 -24
  180. package/src/tools/keybindings.ts +0 -85
  181. package/src/tools/lib.ts +0 -22
  182. package/src/tools/localization.ts +0 -76
  183. package/src/types.ts +0 -151
  184. package/src/util/ReactiveValue.test.ts +0 -168
  185. package/src/util/ReactiveValue.ts +0 -241
  186. package/src/util/assertions.ts +0 -55
  187. package/src/util/fileToBase64.ts +0 -18
  188. package/src/util/guessKeyCodeFromKey.ts +0 -36
  189. package/src/util/listPrefixMatch.ts +0 -19
  190. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  191. package/src/util/untilNextAnimationFrame.ts +0 -9
  192. package/src/util/waitForAll.ts +0 -18
  193. package/src/util/waitForTimeout.ts +0 -9
  194. package/src/version.test.ts +0 -12
  195. package/src/version.ts +0 -3
  196. package/tools/allLocales.js +0 -4
  197. 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;
@@ -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
- };
@@ -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;