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
@@ -1,195 +0,0 @@
1
- import { Color4 } from '@js-draw/math';
2
- import { isRestylableComponent } from '../../components/RestylableComponent';
3
- import Editor from '../../Editor';
4
- import uniteCommands from '../../commands/uniteCommands';
5
- import SelectionTool from '../../tools/SelectionTool/SelectionTool';
6
- import { EditorEventType } from '../../types';
7
- import { KeyPressEvent } from '../../inputEvents';
8
- import { ToolbarLocalization } from '../localization';
9
- import makeColorInput from './components/makeColorInput';
10
- import ActionButtonWidget from './ActionButtonWidget';
11
- import BaseToolWidget from './BaseToolWidget';
12
- import { resizeImageToSelectionKeyboardShortcut } from './keybindings';
13
- import makeSeparator from './components/makeSeparator';
14
- import { toolbarCSSPrefix } from '../constants';
15
-
16
- const makeFormatMenu = (editor: Editor, selectionTool: SelectionTool, localizationTable: ToolbarLocalization) => {
17
- const container = document.createElement('div');
18
- container.classList.add(
19
- 'selection-format-menu', `${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}indentedList`
20
- );
21
-
22
- const colorRow = document.createElement('div');
23
- const colorLabel = document.createElement('label');
24
- const {
25
- input: colorInput, container: colorInputContainer, setValue: setColorInputValue
26
- } = makeColorInput(editor, color => {
27
- const selection = selectionTool.getSelection();
28
-
29
- if (selection) {
30
- const updateStyleCommands = [];
31
-
32
- for (const elem of selection.getSelectedObjects()) {
33
- if (isRestylableComponent(elem)) {
34
- updateStyleCommands.push(elem.updateStyle({ color }));
35
- }
36
- }
37
-
38
- const unitedCommand = uniteCommands(updateStyleCommands);
39
- editor.dispatch(unitedCommand);
40
- }
41
- });
42
-
43
- colorLabel.innerText = localizationTable.colorLabel;
44
-
45
- const update = () => {
46
- const selection = selectionTool.getSelection();
47
- if (selection && selection.getSelectedItemCount() > 0) {
48
- colorInput.disabled = false;
49
- container.classList.remove('disabled');
50
-
51
- const colors = [];
52
- for (const elem of selection.getSelectedObjects()) {
53
- if (isRestylableComponent(elem)) {
54
- const color = elem.getStyle().color;
55
- if (color) {
56
- colors.push(color);
57
- }
58
- }
59
- }
60
- setColorInputValue(Color4.average(colors));
61
- } else {
62
- colorInput.disabled = true;
63
- container.classList.add('disabled');
64
- setColorInputValue(Color4.transparent);
65
- }
66
- };
67
-
68
- colorRow.replaceChildren(colorLabel, colorInputContainer);
69
- container.replaceChildren(colorRow);
70
-
71
- return {
72
- addTo: (parent: HTMLElement) => {
73
- parent.appendChild(container);
74
- },
75
- update,
76
- };
77
- };
78
-
79
- export default class SelectionToolWidget extends BaseToolWidget {
80
- private updateFormatMenu: ()=>void = () => {};
81
-
82
- public constructor(
83
- editor: Editor, private tool: SelectionTool, localization?: ToolbarLocalization
84
- ) {
85
- super(editor, tool, 'selection-tool-widget', localization);
86
-
87
- const resizeButton = new ActionButtonWidget(
88
- editor, 'resize-btn',
89
- () => editor.icons.makeResizeImageToSelectionIcon(),
90
- this.localizationTable.resizeImageToSelection,
91
- () => {
92
- this.resizeImageToSelection();
93
- },
94
- localization,
95
- );
96
- const deleteButton = new ActionButtonWidget(
97
- editor, 'delete-btn',
98
- () => editor.icons.makeDeleteSelectionIcon(),
99
- this.localizationTable.deleteSelection,
100
- () => {
101
- const selection = this.tool.getSelection();
102
- this.editor.dispatch(selection!.deleteSelectedObjects());
103
- this.tool.clearSelection();
104
- },
105
- localization,
106
- );
107
- const duplicateButton = new ActionButtonWidget(
108
- editor, 'duplicate-btn',
109
- () => editor.icons.makeDuplicateSelectionIcon(),
110
- this.localizationTable.duplicateSelection,
111
- async () => {
112
- const selection = this.tool.getSelection();
113
- this.editor.dispatch(await selection!.duplicateSelectedObjects());
114
- this.setDropdownVisible(false);
115
- },
116
- localization,
117
- );
118
-
119
- this.addSubWidget(resizeButton);
120
- this.addSubWidget(deleteButton);
121
- this.addSubWidget(duplicateButton);
122
-
123
- const updateDisabled = (disabled: boolean) => {
124
- resizeButton.setDisabled(disabled);
125
- deleteButton.setDisabled(disabled);
126
- duplicateButton.setDisabled(disabled);
127
- };
128
- updateDisabled(true);
129
-
130
- // Enable/disable actions based on whether items are selected
131
- this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
132
- if (toolEvt.kind !== EditorEventType.ToolUpdated) {
133
- throw new Error('Invalid event type!');
134
- }
135
-
136
- if (toolEvt.tool === this.tool) {
137
- const selection = this.tool.getSelection();
138
- const hasSelection = selection && selection.getSelectedItemCount() > 0;
139
-
140
- updateDisabled(!hasSelection);
141
- this.updateFormatMenu();
142
- }
143
- });
144
- }
145
-
146
- private resizeImageToSelection() {
147
- const selection = this.tool.getSelection();
148
- if (selection) {
149
- this.editor.dispatch(this.editor.setImportExportRect(selection.region));
150
- }
151
- }
152
-
153
- protected override onKeyPress(event: KeyPressEvent): boolean {
154
- const shortcuts = this.editor.shortcuts;
155
-
156
- // Resize image to selection:
157
- // Other keys are handled directly by the selection tool.
158
- if (shortcuts.matchesShortcut(resizeImageToSelectionKeyboardShortcut, event)) {
159
- this.resizeImageToSelection();
160
- return true;
161
- }
162
-
163
- // If we didn't handle the event, allow the superclass to handle it.
164
- if (super.onKeyPress(event)) {
165
- return true;
166
- }
167
- return false;
168
- }
169
-
170
- protected getTitle(): string {
171
- return this.localizationTable.select;
172
- }
173
-
174
- protected createIcon(): Element {
175
- return this.editor.icons.makeSelectionIcon();
176
- }
177
-
178
- protected override fillDropdown(dropdown: HTMLElement): boolean {
179
- super.fillDropdown(dropdown);
180
-
181
- const controlsContainer = document.createElement('div');
182
- controlsContainer.classList.add(`${toolbarCSSPrefix}nonbutton-controls-main-list`);
183
- dropdown.appendChild(controlsContainer);
184
-
185
- makeSeparator(this.localizationTable.reformatSelection).addTo(controlsContainer);
186
-
187
- const formatMenu = makeFormatMenu(this.editor, this.tool, this.localizationTable);
188
- formatMenu.addTo(controlsContainer);
189
- this.updateFormatMenu = () => formatMenu.update();
190
-
191
- formatMenu.update();
192
-
193
- return true;
194
- }
195
- }
@@ -1,149 +0,0 @@
1
- import { Color4 } from '@js-draw/math';
2
- import Editor from '../../Editor';
3
- import TextTool from '../../tools/TextTool';
4
- import { EditorEventType } from '../../types';
5
- import { toolbarCSSPrefix } from '../constants';
6
- import { ToolbarLocalization } from '../localization';
7
- import makeColorInput from './components/makeColorInput';
8
- import BaseToolWidget from './BaseToolWidget';
9
- import { SavedToolbuttonState } from './BaseWidget';
10
-
11
- export default class TextToolWidget extends BaseToolWidget {
12
- private updateDropdownInputs: (()=>void)|null = null;
13
- public constructor(editor: Editor, private tool: TextTool, localization?: ToolbarLocalization) {
14
- super(editor, tool, 'text-tool-widget', localization);
15
-
16
- editor.notifier.on(EditorEventType.ToolUpdated, evt => {
17
- if (evt.kind === EditorEventType.ToolUpdated && evt.tool === tool) {
18
- this.updateIcon();
19
- this.updateDropdownInputs?.();
20
- }
21
- });
22
- }
23
-
24
- protected getTitle(): string {
25
- return this.targetTool.description;
26
- }
27
-
28
- protected createIcon(): Element {
29
- const textStyle = this.tool.getTextStyle();
30
- return this.editor.icons.makeTextIcon(textStyle);
31
- }
32
-
33
- private static idCounter: number = 0;
34
- protected override fillDropdown(dropdown: HTMLElement): boolean {
35
- const container = document.createElement('div');
36
- container.classList.add(
37
- `${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`
38
- );
39
- const fontRow = document.createElement('div');
40
- const colorRow = document.createElement('div');
41
- const sizeRow = document.createElement('div');
42
-
43
- const fontInput = document.createElement('select');
44
- const fontLabel = document.createElement('label');
45
-
46
- const sizeInput = document.createElement('input');
47
- const sizeLabel = document.createElement('label');
48
-
49
- const {
50
- input: colorInput, container: colorInputContainer, setValue: setColorInputValue
51
- } = makeColorInput(this.editor, color => {
52
- this.tool.setColor(color);
53
- });
54
- const colorLabel = document.createElement('label');
55
-
56
- const fontsInInput = new Set();
57
- const addFontToInput = (fontName: string) => {
58
- const option = document.createElement('option');
59
- option.value = fontName;
60
- option.textContent = fontName;
61
- fontInput.appendChild(option);
62
- fontsInInput.add(fontName);
63
- };
64
-
65
- sizeInput.setAttribute('type', 'number');
66
- sizeInput.min = '1';
67
- sizeInput.max = '128';
68
-
69
- fontLabel.innerText = this.localizationTable.fontLabel;
70
- colorLabel.innerText = this.localizationTable.colorLabel;
71
- sizeLabel.innerText = this.localizationTable.textSize;
72
-
73
- colorInput.id = `${toolbarCSSPrefix}-text-color-input-${TextToolWidget.idCounter++}`;
74
- colorLabel.setAttribute('for', colorInput.id);
75
-
76
- sizeInput.id = `${toolbarCSSPrefix}-text-size-input-${TextToolWidget.idCounter++}`;
77
- sizeLabel.setAttribute('for', sizeInput.id);
78
-
79
- addFontToInput('monospace');
80
- addFontToInput('serif');
81
- addFontToInput('sans-serif');
82
- fontInput.id = `${toolbarCSSPrefix}-text-font-input-${TextToolWidget.idCounter++}`;
83
- fontLabel.setAttribute('for', fontInput.id);
84
-
85
- fontInput.onchange = () => {
86
- this.tool.setFontFamily(fontInput.value);
87
- };
88
-
89
- sizeInput.onchange = () => {
90
- const size = parseInt(sizeInput.value);
91
- if (!isNaN(size) && size > 0) {
92
- this.tool.setFontSize(size);
93
- }
94
- };
95
-
96
- colorRow.appendChild(colorLabel);
97
- colorRow.appendChild(colorInputContainer);
98
-
99
- fontRow.appendChild(fontLabel);
100
- fontRow.appendChild(fontInput);
101
-
102
- sizeRow.appendChild(sizeLabel);
103
- sizeRow.appendChild(sizeInput);
104
-
105
- this.updateDropdownInputs = () => {
106
- const style = this.tool.getTextStyle();
107
- setColorInputValue(style.renderingStyle.fill);
108
-
109
- if (!fontsInInput.has(style.fontFamily)) {
110
- addFontToInput(style.fontFamily);
111
- }
112
- fontInput.value = style.fontFamily;
113
- sizeInput.value = `${style.size}`;
114
- };
115
- this.updateDropdownInputs();
116
-
117
- container.replaceChildren(colorRow, sizeRow, fontRow);
118
- dropdown.appendChild(container);
119
- return true;
120
- }
121
-
122
- public override serializeState(): SavedToolbuttonState {
123
- const textStyle = this.tool.getTextStyle();
124
-
125
- return {
126
- ...super.serializeState(),
127
-
128
- fontFamily: textStyle.fontFamily,
129
- textSize: textStyle.size,
130
- color: textStyle.renderingStyle.fill.toHexString(),
131
- };
132
- }
133
-
134
- public override deserializeFrom(state: SavedToolbuttonState) {
135
- if (state.fontFamily && typeof(state.fontFamily) === 'string') {
136
- this.tool.setFontFamily(state.fontFamily);
137
- }
138
-
139
- if (state.color && typeof(state.color) === 'string') {
140
- this.tool.setColor(Color4.fromHex(state.color));
141
- }
142
-
143
- if (state.textSize && typeof(state.textSize) === 'number') {
144
- this.tool.setFontSize(state.textSize);
145
- }
146
-
147
- super.deserializeFrom(state);
148
- }
149
- }
@@ -1,184 +0,0 @@
1
- import { Color4 } from '@js-draw/math';
2
- import Editor from '../../../Editor';
3
- import PipetteTool from '../../../tools/PipetteTool';
4
- import { EditorEventType } from '../../../types';
5
-
6
- type OnColorChangeListener = (color: Color4)=>void;
7
-
8
- // Returns [ color input, input container, callback to change the color value ].
9
- export const makeColorInput = (
10
- editor: Editor, onColorChange: OnColorChangeListener
11
- ) => {
12
-
13
- const colorInputContainer = document.createElement('span');
14
- const colorInput = document.createElement('input');
15
-
16
- colorInput.type = 'button';
17
- colorInput.classList.add('coloris_input');
18
- colorInputContainer.classList.add('color-input-container');
19
-
20
- colorInputContainer.appendChild(colorInput);
21
- const pipetteController = addPipetteTool(editor, colorInputContainer, (color: Color4) => {
22
- colorInput.value = color.toHexString();
23
- onInputEnd();
24
-
25
- // Update the color preview, if it exists (may be managed by Coloris).
26
- const parentElem = colorInput.parentElement;
27
- if (parentElem && parentElem.classList.contains('clr-field')) {
28
- parentElem.style.color = colorInput.value;
29
- }
30
- });
31
-
32
- let currentColor: Color4|undefined;
33
- const handleColorInput = () => {
34
- currentColor = Color4.fromHex(colorInput.value);
35
- };
36
-
37
- // Only change the pen color when we finish sending input (this limits the number of
38
- // editor events triggered and accessibility announcements).
39
- const onInputEnd = () => {
40
- handleColorInput();
41
-
42
- if (currentColor) {
43
- editor.announceForAccessibility(
44
- editor.localization.colorChangedAnnouncement(currentColor.toHexString())
45
- );
46
- onColorChange(currentColor);
47
- editor.notifier.dispatch(EditorEventType.ColorPickerColorSelected, {
48
- kind: EditorEventType.ColorPickerColorSelected,
49
- color: currentColor,
50
- });
51
- }
52
- };
53
-
54
- colorInput.oninput = handleColorInput;
55
- let isOpen = false;
56
- colorInput.addEventListener('open', () => {
57
- isOpen = true;
58
- editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
59
- kind: EditorEventType.ColorPickerToggled,
60
- open: true,
61
- });
62
- pipetteController.cancel();
63
- colorInputContainer.classList.add('picker-open');
64
-
65
- // Focus the Coloris color picker, if it exists.
66
- // Don't focus the text input within the color picker, however,
67
- // as this displays a keyboard on mobile devices.
68
- const colorPickerElem: HTMLElement|null = document.querySelector('#clr-picker #clr-hue-slider');
69
- colorPickerElem?.focus();
70
- });
71
-
72
- const onClose = () => {
73
- isOpen = false;
74
- editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
75
- kind: EditorEventType.ColorPickerToggled,
76
- open: false,
77
- });
78
- onInputEnd();
79
-
80
- // Restore focus to the input that opened the color picker
81
- colorInput.focus();
82
-
83
- colorInputContainer.classList.remove('picker-open');
84
- };
85
- colorInput.addEventListener('close', () => {
86
- onClose();
87
- });
88
-
89
- const setColorInputValue = (color: Color4|string) => {
90
- if (typeof color === 'object') {
91
- color = color.toHexString();
92
- }
93
-
94
- colorInput.value = color;
95
-
96
- // Fire all color event listeners. See
97
- // https://github.com/mdbassit/Coloris#manually-updating-the-thumbnail
98
- colorInput.dispatchEvent(new Event('input', { bubbles: true }));
99
- };
100
-
101
- return {
102
- input: colorInput,
103
- container: colorInputContainer,
104
- setValue: setColorInputValue,
105
- closePicker: () => {
106
- if (isOpen) {
107
- onInputEnd();
108
- }
109
- },
110
- };
111
- };
112
-
113
- const addPipetteTool = (editor: Editor, container: HTMLElement, onColorChange: OnColorChangeListener) => {
114
- const pipetteButton = document.createElement('button');
115
- pipetteButton.classList.add('pipetteButton');
116
- pipetteButton.title = editor.localization.pickColorFromScreen;
117
- pipetteButton.setAttribute('alt', pipetteButton.title);
118
-
119
- const pickColorLabel = document.createElement('span');
120
- pickColorLabel.classList.add('pickColorInstructions');
121
- pickColorLabel.innerText = editor.localization.clickToPickColorAnnouncement;
122
-
123
- const updatePipetteButtonContent = (color?: Color4) => {
124
- pipetteButton.replaceChildren(
125
- editor.icons.makePipetteIcon(color), pickColorLabel
126
- );
127
- };
128
- updatePipetteButtonContent();
129
-
130
- const pipetteTool: PipetteTool|undefined = editor.toolController.getMatchingTools(PipetteTool)[0];
131
-
132
- const endColorSelectMode = () => {
133
- pipetteTool?.clearColorListener();
134
- updatePipetteButtonContent();
135
- pipetteButton.classList.remove('active');
136
- };
137
-
138
- const pipetteColorSelect = (color: Color4|null) => {
139
- endColorSelectMode();
140
-
141
- if (color) {
142
- onColorChange(color);
143
- }
144
- };
145
-
146
- const pipetteColorPreview = (color: Color4|null) => {
147
- if (color) {
148
- updatePipetteButtonContent(color);
149
- } else {
150
- updatePipetteButtonContent();
151
- }
152
- };
153
-
154
- pipetteButton.onclick = () => {
155
- // If already picking, cancel it.
156
- if (pipetteButton.classList.contains('active')) {
157
- endColorSelectMode();
158
- editor.announceForAccessibility(editor.localization.colorSelectionCanceledAnnouncement);
159
- return;
160
- }
161
-
162
- pipetteTool?.setColorListener(
163
- pipetteColorPreview,
164
- pipetteColorSelect,
165
- );
166
-
167
- if (pipetteTool) {
168
- pipetteButton.classList.add('active');
169
-
170
- editor.announceForAccessibility(editor.localization.clickToPickColorAnnouncement);
171
- }
172
- };
173
-
174
- container.appendChild(pipetteButton);
175
-
176
- return {
177
- // Cancel a pipette color selection if one is in progress.
178
- cancel: () => {
179
- endColorSelectMode();
180
- },
181
- };
182
- };
183
-
184
- export default makeColorInput;
@@ -1,128 +0,0 @@
1
- import ReactiveValue, { MutableReactiveValue } from '../../../util/ReactiveValue';
2
- import { ToolbarContext } from '../../types';
3
-
4
- let idCounter = 0;
5
-
6
- /**
7
- * Creates a stylized file input.
8
- */
9
- const makeFileInput = (labelText: string, context: ToolbarContext, accepts: string = '*') => {
10
- const container = document.createElement('div');
11
- const label = document.createElement('label');
12
- const input = document.createElement('input');
13
-
14
- const descriptionBox = document.createElement('div');
15
- descriptionBox.classList.add('toolbar--file-input-description');
16
- const descriptionText = document.createElement('span');
17
-
18
- container.classList.add('toolbar--file-input-container');
19
-
20
- label.appendChild(document.createTextNode(labelText));
21
- input.accept = accepts;
22
- input.type = 'file';
23
-
24
- // Associate the label with the input
25
- const inputId = `js-draw-file-input-${idCounter ++}`;
26
- input.setAttribute('id', inputId);
27
- label.htmlFor = inputId;
28
-
29
- const icon = context.icons.makeUploadFileIcon();
30
- icon.classList.add('icon');
31
-
32
- descriptionBox.replaceChildren(icon, descriptionText);
33
- label.appendChild(descriptionBox);
34
- container.replaceChildren(label, input);
35
-
36
- const selectedFiles: MutableReactiveValue<File[]> = ReactiveValue.fromInitialValue([]);
37
-
38
- // Support droping files
39
- label.addEventListener('dragover', event => {
40
- event.preventDefault();
41
- label.classList.add('drag-target');
42
- });
43
- label.addEventListener('dragenter', event => {
44
- event.preventDefault();
45
- label.classList.add('drag-target');
46
- });
47
- label.addEventListener('dragleave', event => {
48
- event.preventDefault();
49
-
50
- // Ensure the event wasn't targeting a child.
51
- // See https://stackoverflow.com/a/54271161 and
52
- // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget
53
- const enteringElement = event.relatedTarget as HTMLElement;
54
- if (!enteringElement || !label.contains(enteringElement)) {
55
- label.classList.remove('drag-target');
56
- }
57
- });
58
-
59
- // See https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#process_the_drop
60
- label.addEventListener('drop', event => {
61
- event.preventDefault();
62
- label.classList.remove('drag-target');
63
-
64
- const fileList: File[] = [];
65
-
66
- if (event.dataTransfer) {
67
- fileList.push(...event.dataTransfer.files);
68
- }
69
-
70
- selectedFiles.set(fileList);
71
- });
72
- input.addEventListener('change', () => {
73
- const fileList = input.files ?? [];
74
- selectedFiles.set([ ...fileList ]);
75
- });
76
-
77
- selectedFiles.onUpdate(files => {
78
- if (files.length === 0 && input.files && input.files.length > 0) {
79
- input.value = '';
80
- }
81
- });
82
-
83
- // Update the status text and hide/show the icon.
84
- selectedFiles.onUpdateAndNow(files => {
85
- if (files.length > 0) {
86
- descriptionText.innerText = files.map(file => file.name).join('\n');
87
-
88
- // Only show the icon when there are files
89
- icon.style.display = 'none';
90
- } else {
91
- // Show the icon
92
- icon.style.display = '';
93
-
94
- const text = context.localization.dragAndDropHereOrBrowse;
95
-
96
- // Split into regions surrounded by {{curly braces}} and regions that are
97
- // not.
98
- // When given a regular expression, `.split` outputs an array. For example,
99
- // "a test __of__ split".split(/__(.*)__/)
100
- // results in
101
- // ['a test ', 'of', ' split'].
102
- const segments = text.split(/[{]{2}(.*)[}]{2}/g);
103
- descriptionText.replaceChildren();
104
-
105
- for (let i = 0; i < segments.length; i++) {
106
- // Inside a {{pair of curly braces}}?
107
- if (i % 2 === 1) {
108
- const boldedText = document.createElement('b');
109
- boldedText.innerText = segments[i];
110
- descriptionText.appendChild(boldedText);
111
- } else {
112
- descriptionText.appendChild(document.createTextNode(segments[i]));
113
- }
114
- }
115
- }
116
- });
117
-
118
- return {
119
- container,
120
- input,
121
- selectedFiles,
122
- addTo: (parent: HTMLElement) => {
123
- parent.appendChild(container);
124
- },
125
- };
126
- };
127
-
128
- export default makeFileInput;