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
@@ -1,543 +0,0 @@
|
|
1
|
-
import Editor from '../Editor';
|
2
|
-
import { ToolbarLocalization } from './localization';
|
3
|
-
import BaseWidget, { ToolbarWidgetTag } from './widgets/BaseWidget';
|
4
|
-
import { toolbarCSSPrefix } from './constants';
|
5
|
-
import EdgeToolbarLayoutManager from './widgets/layout/EdgeToolbarLayoutManager';
|
6
|
-
import { MutableReactiveValue, ReactiveValue } from '../util/ReactiveValue';
|
7
|
-
import AbstractToolbar, { SpacerOptions } from './AbstractToolbar';
|
8
|
-
import stopPropagationOfScrollingWheelEvents from '../util/stopPropagationOfScrollingWheelEvents';
|
9
|
-
|
10
|
-
|
11
|
-
export const makeEdgeToolbar = (editor: Editor): AbstractToolbar => {
|
12
|
-
return new EdgeToolbar(editor, editor.getRootElement(), editor.localization);
|
13
|
-
};
|
14
|
-
|
15
|
-
|
16
|
-
export default class EdgeToolbar extends AbstractToolbar {
|
17
|
-
private toolbarContainer: HTMLElement;
|
18
|
-
|
19
|
-
// Row that contains action buttons
|
20
|
-
private toolbarActionRow: HTMLElement;
|
21
|
-
|
22
|
-
// Row that contains tools
|
23
|
-
private toolbarToolRow: HTMLElement;
|
24
|
-
|
25
|
-
private toolRowResizeObserver: ResizeObserver;
|
26
|
-
|
27
|
-
private menuContainer: HTMLElement;
|
28
|
-
private sidebarContainer: HTMLElement;
|
29
|
-
private sidebarContent: HTMLElement;
|
30
|
-
private closeButton: HTMLElement;
|
31
|
-
|
32
|
-
private layoutManager: EdgeToolbarLayoutManager;
|
33
|
-
|
34
|
-
private sidebarVisible: MutableReactiveValue<boolean>;
|
35
|
-
private sidebarY: MutableReactiveValue<number>;
|
36
|
-
private sidebarTitle: MutableReactiveValue<string>;
|
37
|
-
|
38
|
-
/** @internal */
|
39
|
-
public constructor(
|
40
|
-
editor: Editor, parent: HTMLElement,
|
41
|
-
localizationTable: ToolbarLocalization,
|
42
|
-
) {
|
43
|
-
super(editor, localizationTable);
|
44
|
-
|
45
|
-
this.toolbarContainer = document.createElement('div');
|
46
|
-
this.toolbarContainer.classList.add(`${toolbarCSSPrefix}root`);
|
47
|
-
this.toolbarContainer.classList.add(`${toolbarCSSPrefix}element`);
|
48
|
-
this.toolbarContainer.classList.add(`${toolbarCSSPrefix}edge-toolbar`);
|
49
|
-
this.toolbarContainer.setAttribute('role', 'toolbar');
|
50
|
-
|
51
|
-
this.toolbarActionRow = document.createElement('div');
|
52
|
-
this.toolbarActionRow.classList.add('toolbar-element', 'toolbar-action-row');
|
53
|
-
this.toolbarToolRow = document.createElement('div');
|
54
|
-
this.toolbarToolRow.classList.add('toolbar-element', 'toolbar-tool-row');
|
55
|
-
|
56
|
-
stopPropagationOfScrollingWheelEvents(this.toolbarToolRow);
|
57
|
-
|
58
|
-
if ('ResizeObserver' in window) {
|
59
|
-
this.toolRowResizeObserver = new ResizeObserver((_entries) => {
|
60
|
-
this.onToolbarRowResize();
|
61
|
-
});
|
62
|
-
this.toolRowResizeObserver.observe(this.toolbarToolRow);
|
63
|
-
} else {
|
64
|
-
console.warn('ResizeObserver not supported. Toolbar will not resize.');
|
65
|
-
}
|
66
|
-
|
67
|
-
this.toolbarContainer.replaceChildren(this.toolbarActionRow, this.toolbarToolRow);
|
68
|
-
parent.appendChild(this.toolbarContainer);
|
69
|
-
|
70
|
-
this.sidebarVisible = ReactiveValue.fromInitialValue(false);
|
71
|
-
this.sidebarY = ReactiveValue.fromInitialValue(0);
|
72
|
-
|
73
|
-
// Create the container elements
|
74
|
-
this.menuContainer = document.createElement('div');
|
75
|
-
this.menuContainer.classList.add(`${toolbarCSSPrefix}edgemenu-container`);
|
76
|
-
|
77
|
-
this.sidebarContainer = document.createElement('div');
|
78
|
-
this.sidebarContainer.classList.add(
|
79
|
-
`${toolbarCSSPrefix}edgemenu`,
|
80
|
-
`${toolbarCSSPrefix}element`,
|
81
|
-
);
|
82
|
-
this.sidebarContainer.classList.add(`${toolbarCSSPrefix}tool-properties`);
|
83
|
-
|
84
|
-
this.sidebarContent = document.createElement('div');
|
85
|
-
|
86
|
-
// Setup resizing/dragging
|
87
|
-
this.sidebarY.onUpdateAndNow(y => {
|
88
|
-
const belowEdgeClassName = 'dropdown-below-edge';
|
89
|
-
if (y > 0) {
|
90
|
-
this.sidebarContainer.style.translate = `0 ${y}px`;
|
91
|
-
this.sidebarContainer.style.paddingBottom = '';
|
92
|
-
this.menuContainer.classList.add(belowEdgeClassName);
|
93
|
-
} else {
|
94
|
-
this.sidebarContainer.style.translate = '';
|
95
|
-
this.sidebarContainer.style.paddingBottom = `${-y}px`;
|
96
|
-
this.menuContainer.classList.remove(belowEdgeClassName);
|
97
|
-
}
|
98
|
-
});
|
99
|
-
|
100
|
-
this.closeButton = document.createElement('button');
|
101
|
-
this.closeButton.classList.add('drag-elem');
|
102
|
-
|
103
|
-
// The close button has default focus -- forward its events to the main editor so that keyboard
|
104
|
-
// shortcuts still work.
|
105
|
-
this.editor.handleKeyEventsFrom(this.closeButton, event => {
|
106
|
-
// Don't send
|
107
|
-
return event.code !== 'Space' && event.code !== 'Enter' && event.code !== 'Tab';
|
108
|
-
});
|
109
|
-
|
110
|
-
// Close the sidebar when pressing escape
|
111
|
-
this.sidebarContainer.addEventListener('keyup', event => {
|
112
|
-
if (!event.defaultPrevented && event.code === 'Escape') {
|
113
|
-
this.sidebarVisible.set(false);
|
114
|
-
event.preventDefault();
|
115
|
-
}
|
116
|
-
});
|
117
|
-
|
118
|
-
this.initDragListeners();
|
119
|
-
|
120
|
-
// Initialize the layout manager
|
121
|
-
const setSidebarContent = (...content: HTMLElement[]) => {
|
122
|
-
this.sidebarContent.replaceChildren(...content);
|
123
|
-
this.setupColorPickers();
|
124
|
-
};
|
125
|
-
|
126
|
-
this.sidebarTitle = MutableReactiveValue.fromInitialValue('');
|
127
|
-
|
128
|
-
this.layoutManager = new EdgeToolbarLayoutManager(
|
129
|
-
setSidebarContent,
|
130
|
-
this.sidebarTitle,
|
131
|
-
this.sidebarVisible,
|
132
|
-
editor.announceForAccessibility.bind(editor),
|
133
|
-
localizationTable,
|
134
|
-
);
|
135
|
-
|
136
|
-
this.sidebarTitle.onUpdateAndNow(title => {
|
137
|
-
this.closeButton.setAttribute('aria-label', localizationTable.closeSidebar(title));
|
138
|
-
});
|
139
|
-
|
140
|
-
// Make things visible/keep hidden.
|
141
|
-
this.listenForVisibilityChanges();
|
142
|
-
|
143
|
-
this.sidebarContainer.replaceChildren(this.closeButton, this.sidebarContent);
|
144
|
-
this.menuContainer.replaceChildren(this.sidebarContainer);
|
145
|
-
parent.appendChild(this.menuContainer);
|
146
|
-
}
|
147
|
-
|
148
|
-
private listenForVisibilityChanges() {
|
149
|
-
let animationTimeout: ReturnType<typeof setTimeout>|null = null;
|
150
|
-
const animationDuration = 170;
|
151
|
-
|
152
|
-
if (!this.sidebarVisible.get()) {
|
153
|
-
this.menuContainer.style.display = 'none';
|
154
|
-
|
155
|
-
// Set the initial opacity to 0 to allow the `transition` property
|
156
|
-
// to animate it to 1.
|
157
|
-
this.menuContainer.style.opacity = '0';
|
158
|
-
}
|
159
|
-
|
160
|
-
this.sidebarVisible.onUpdate(visible => {
|
161
|
-
const animationProperties = `${animationDuration}ms ease`;
|
162
|
-
|
163
|
-
if (visible) {
|
164
|
-
this.sidebarY.set(this.snappedSidebarY());
|
165
|
-
|
166
|
-
if (animationTimeout) {
|
167
|
-
clearTimeout(animationTimeout);
|
168
|
-
animationTimeout = null;
|
169
|
-
}
|
170
|
-
|
171
|
-
this.menuContainer.style.display = '';
|
172
|
-
this.sidebarContainer.style.animation = `${animationProperties} ${toolbarCSSPrefix}-edgemenu-transition-in`;
|
173
|
-
this.menuContainer.style.animation = `${animationProperties} ${toolbarCSSPrefix}-edgemenu-container-transition-in`;
|
174
|
-
this.menuContainer.style.opacity = '1';
|
175
|
-
|
176
|
-
|
177
|
-
// Focus the close button when first shown.
|
178
|
-
this.closeButton.focus();
|
179
|
-
} else {
|
180
|
-
this.closeColorPickers();
|
181
|
-
|
182
|
-
if (animationTimeout === null) {
|
183
|
-
this.sidebarContainer.style.animation = ` ${animationProperties} ${toolbarCSSPrefix}-edgemenu-transition-out`;
|
184
|
-
this.menuContainer.style.animation = `${animationProperties} ${toolbarCSSPrefix}-edgemenu-container-transition-out`;
|
185
|
-
|
186
|
-
// Manually set the container's opacity to prevent flickering when closing
|
187
|
-
// the toolbar.
|
188
|
-
this.menuContainer.style.opacity = '0';
|
189
|
-
|
190
|
-
// Hide overflow -- don't show the part of the edge toolbar that's outside of
|
191
|
-
// the editor.
|
192
|
-
//this.menuContainer.style.overflowY = 'hidden';
|
193
|
-
|
194
|
-
this.editor.announceForAccessibility(
|
195
|
-
this.localizationTable.dropdownHidden(this.sidebarTitle.get())
|
196
|
-
);
|
197
|
-
|
198
|
-
animationTimeout = setTimeout(() => {
|
199
|
-
this.menuContainer.style.display = 'none';
|
200
|
-
this.menuContainer.style.overflowY = '';
|
201
|
-
animationTimeout = null;
|
202
|
-
}, animationDuration);
|
203
|
-
}
|
204
|
-
}
|
205
|
-
});
|
206
|
-
}
|
207
|
-
|
208
|
-
private onToolbarRowResize() {
|
209
|
-
const setExtraPadding = () => {
|
210
|
-
const visibleWidth = this.toolbarToolRow.clientWidth;
|
211
|
-
|
212
|
-
// Determine whether extra spacing needs to be added so that one button is cut
|
213
|
-
// in half. Ideally, when there is scroll, one button will be cut in half to show
|
214
|
-
// that scrolling is possible.
|
215
|
-
let currentWidth = 0;
|
216
|
-
let extraPadding = 0;
|
217
|
-
let numVisibleButtons = 0;
|
218
|
-
for (const child of this.toolbarToolRow.children) {
|
219
|
-
// Use the first child -- padding is applied around that child. Assumes
|
220
|
-
// that the button's width is its height plus some padding.
|
221
|
-
const buttonBaseSize = child.clientHeight;
|
222
|
-
currentWidth += buttonBaseSize;
|
223
|
-
numVisibleButtons ++;
|
224
|
-
|
225
|
-
if (currentWidth > visibleWidth) {
|
226
|
-
// We want extraPadding + (currentWidth - buttonWidth / 2) = visibleWidth.
|
227
|
-
// Thus, extraPadding = visibleWidth - currentWidth + buttonWidth / 2;
|
228
|
-
extraPadding = visibleWidth - currentWidth + buttonBaseSize / 2;
|
229
|
-
|
230
|
-
// Ensure that the padding is positive
|
231
|
-
if (extraPadding < 0) {
|
232
|
-
extraPadding += buttonBaseSize;
|
233
|
-
}
|
234
|
-
break;
|
235
|
-
}
|
236
|
-
}
|
237
|
-
|
238
|
-
const perButtonPadding = Math.round(extraPadding/numVisibleButtons * 10) / 10;
|
239
|
-
this.toolbarToolRow.style.setProperty('--extra-left-right-padding', `${perButtonPadding}px`);
|
240
|
-
};
|
241
|
-
|
242
|
-
const actionRowBBox = this.toolbarActionRow.getBoundingClientRect();
|
243
|
-
const toolbarRowBBox = this.toolbarToolRow.getBoundingClientRect();
|
244
|
-
const inSameRow = actionRowBBox.y === toolbarRowBBox.y;
|
245
|
-
const onDifferentRows = actionRowBBox.y + actionRowBBox.height <= toolbarRowBBox.y;
|
246
|
-
|
247
|
-
if (onDifferentRows) {
|
248
|
-
this.toolbarContainer.classList.remove('one-row');
|
249
|
-
} else {
|
250
|
-
this.toolbarContainer.classList.add('one-row');
|
251
|
-
}
|
252
|
-
|
253
|
-
if (this.toolbarToolRow.clientWidth < this.toolbarToolRow.scrollWidth) {
|
254
|
-
this.toolbarToolRow.classList.add('has-scroll');
|
255
|
-
|
256
|
-
// If both button areas are in the same row, don't change the padding --
|
257
|
-
// it could lead to an endless loop of reseize events.
|
258
|
-
if (!inSameRow) {
|
259
|
-
setExtraPadding();
|
260
|
-
}
|
261
|
-
} else {
|
262
|
-
this.toolbarToolRow.classList.remove('has-scroll', 'extra-padding');
|
263
|
-
}
|
264
|
-
}
|
265
|
-
|
266
|
-
public override addSpacer(_options?: Partial<SpacerOptions> | undefined): void {
|
267
|
-
//throw new Error('Method not implemented.');
|
268
|
-
// Unused for this toolbar.
|
269
|
-
}
|
270
|
-
|
271
|
-
public override addUndoRedoButtons(): void {
|
272
|
-
super.addUndoRedoButtons(false);
|
273
|
-
}
|
274
|
-
|
275
|
-
public override addDefaults(): void {
|
276
|
-
this.addDefaultActionButtons();
|
277
|
-
this.addDefaultToolWidgets();
|
278
|
-
}
|
279
|
-
|
280
|
-
private updateWidgetCSSClasses(widget: BaseWidget) {
|
281
|
-
const tags = widget.getTags();
|
282
|
-
widget.removeCSSClassFromContainer('label-inline');
|
283
|
-
widget.removeCSSClassFromContainer('label-left');
|
284
|
-
widget.removeCSSClassFromContainer('label-right');
|
285
|
-
|
286
|
-
if (tags.includes(ToolbarWidgetTag.Save)) {
|
287
|
-
widget.addCSSClassToContainer('label-inline');
|
288
|
-
widget.addCSSClassToContainer('label-right');
|
289
|
-
}
|
290
|
-
|
291
|
-
if (tags.includes(ToolbarWidgetTag.Exit)) {
|
292
|
-
widget.addCSSClassToContainer('label-inline');
|
293
|
-
widget.addCSSClassToContainer('label-left');
|
294
|
-
}
|
295
|
-
}
|
296
|
-
|
297
|
-
protected override addWidgetInternal(widget: BaseWidget) {
|
298
|
-
this.updateWidgetCSSClasses(widget);
|
299
|
-
|
300
|
-
widget.setLayoutManager(this.layoutManager);
|
301
|
-
if (widget.mustBeInToplevelMenu()) {
|
302
|
-
widget.addTo(this.toolbarActionRow);
|
303
|
-
} else {
|
304
|
-
widget.addTo(this.toolbarToolRow);
|
305
|
-
}
|
306
|
-
}
|
307
|
-
|
308
|
-
protected override removeWidgetInternal(widget: BaseWidget): void {
|
309
|
-
widget.remove();
|
310
|
-
}
|
311
|
-
|
312
|
-
protected override onRemove() {
|
313
|
-
this.toolbarContainer.remove();
|
314
|
-
this.menuContainer.remove();
|
315
|
-
this.toolRowResizeObserver.disconnect();
|
316
|
-
}
|
317
|
-
|
318
|
-
private initDragListeners() {
|
319
|
-
let lastX = 0;
|
320
|
-
let lastY = 0;
|
321
|
-
let startX = 0;
|
322
|
-
let startY = 0;
|
323
|
-
let pointerDown = false;
|
324
|
-
let capturedPointerId: number|null = null;
|
325
|
-
|
326
|
-
const dragElements = [ this.closeButton, this.sidebarContainer, this.sidebarContent ];
|
327
|
-
|
328
|
-
this.manageListener(this.editor.handlePointerEventsExceptClicksFrom(this.menuContainer, (eventName, event) => {
|
329
|
-
if (event.target === this.menuContainer) {
|
330
|
-
if (eventName === 'pointerdown') {
|
331
|
-
this.sidebarVisible.set(false);
|
332
|
-
}
|
333
|
-
|
334
|
-
if (eventName === 'pointerup') {
|
335
|
-
this.editor.focus();
|
336
|
-
}
|
337
|
-
|
338
|
-
return true;
|
339
|
-
}
|
340
|
-
|
341
|
-
if (!this.sidebarVisible.get()) {
|
342
|
-
return true;
|
343
|
-
}
|
344
|
-
|
345
|
-
// Don't send pointer events that don't directly target mainContainer
|
346
|
-
// to the editor
|
347
|
-
return false;
|
348
|
-
}, (_eventName, event) => {
|
349
|
-
return event.target === this.menuContainer;
|
350
|
-
}));
|
351
|
-
|
352
|
-
const isDraggableElement = (element: HTMLElement|null) => {
|
353
|
-
if (!element) {
|
354
|
-
return false;
|
355
|
-
}
|
356
|
-
|
357
|
-
if (dragElements.includes(element)) {
|
358
|
-
return true;
|
359
|
-
}
|
360
|
-
|
361
|
-
// Some inputs handle dragging themselves. Don't also interpret such gestures
|
362
|
-
// as dragging the dropdown.
|
363
|
-
const undraggableElementTypes = [ 'INPUT', 'SELECT', 'IMG' ];
|
364
|
-
|
365
|
-
let hasSuitableAncestors = false;
|
366
|
-
let ancestor = element.parentElement;
|
367
|
-
while (ancestor) {
|
368
|
-
if (undraggableElementTypes.includes(ancestor.tagName)) {
|
369
|
-
break;
|
370
|
-
}
|
371
|
-
if (ancestor === this.sidebarContainer) {
|
372
|
-
hasSuitableAncestors = true;
|
373
|
-
break;
|
374
|
-
}
|
375
|
-
ancestor = ancestor.parentElement;
|
376
|
-
}
|
377
|
-
|
378
|
-
return !undraggableElementTypes.includes(element.tagName) && hasSuitableAncestors;
|
379
|
-
};
|
380
|
-
|
381
|
-
const clickThreshold = 5;
|
382
|
-
|
383
|
-
// Returns whether the current (or if no current, **the last**) gesture is roughly a click.
|
384
|
-
// Because this can be called **after** a gesture has just ended, it should not require
|
385
|
-
// the gesture to be in progress.
|
386
|
-
const isRoughlyClick = () => {
|
387
|
-
return Math.hypot(lastX - startX, lastY - startY) < clickThreshold;
|
388
|
-
};
|
389
|
-
|
390
|
-
let startedDragging = false;
|
391
|
-
|
392
|
-
this.sidebarContainer.addEventListener('pointerdown', event => {
|
393
|
-
if (event.defaultPrevented || !isDraggableElement(event.target as HTMLElement)) {
|
394
|
-
return;
|
395
|
-
}
|
396
|
-
|
397
|
-
if (event.isPrimary) {
|
398
|
-
startedDragging = false;
|
399
|
-
|
400
|
-
lastX = event.clientX;
|
401
|
-
lastY = event.clientY;
|
402
|
-
|
403
|
-
startX = event.clientX;
|
404
|
-
startY = event.clientY;
|
405
|
-
|
406
|
-
capturedPointerId = null;
|
407
|
-
pointerDown = true;
|
408
|
-
}
|
409
|
-
}, { passive: true });
|
410
|
-
|
411
|
-
let gestureEndTimestamp = 0;
|
412
|
-
const onGestureEnd = (_event: Event) => {
|
413
|
-
// If the pointerup/pointercancel event was for a pointer not being tracked,
|
414
|
-
if (!pointerDown) {
|
415
|
-
return;
|
416
|
-
}
|
417
|
-
|
418
|
-
gestureEndTimestamp = performance.now();
|
419
|
-
|
420
|
-
if (capturedPointerId !== null) {
|
421
|
-
this.sidebarContainer.releasePointerCapture(capturedPointerId);
|
422
|
-
capturedPointerId = null;
|
423
|
-
}
|
424
|
-
|
425
|
-
this.finalizeDrag();
|
426
|
-
pointerDown = false;
|
427
|
-
startedDragging = false;
|
428
|
-
};
|
429
|
-
|
430
|
-
this.sidebarContainer.onpointermove = event => {
|
431
|
-
if (!event.isPrimary || !pointerDown) {
|
432
|
-
return undefined;
|
433
|
-
}
|
434
|
-
|
435
|
-
// Mouse event and no buttons pressed? Cancel the event.
|
436
|
-
// This can happen if the event was canceled by a focus change (e.g. by opening a
|
437
|
-
// right-click menu).
|
438
|
-
if (event.pointerType === 'mouse' && event.buttons === 0) {
|
439
|
-
onGestureEnd(event);
|
440
|
-
return undefined;
|
441
|
-
}
|
442
|
-
|
443
|
-
// Only capture after motion -- capturing early prevents click events in Chrome.
|
444
|
-
if (capturedPointerId === null && !isRoughlyClick()) {
|
445
|
-
this.sidebarContainer.setPointerCapture(event.pointerId);
|
446
|
-
capturedPointerId = event.pointerId;
|
447
|
-
}
|
448
|
-
|
449
|
-
const x = event.clientX;
|
450
|
-
const y = event.clientY;
|
451
|
-
const dx = x - lastX;
|
452
|
-
const dy = y - lastY;
|
453
|
-
|
454
|
-
if (Math.abs(y - startY) > clickThreshold || startedDragging) {
|
455
|
-
this.handleDrag(dx, dy);
|
456
|
-
|
457
|
-
lastX = x;
|
458
|
-
lastY = y;
|
459
|
-
|
460
|
-
startedDragging = true;
|
461
|
-
}
|
462
|
-
};
|
463
|
-
|
464
|
-
this.sidebarContainer.onpointerleave = event => {{
|
465
|
-
// Capture the pointer if it exits the container while dragging.
|
466
|
-
if (capturedPointerId === null && pointerDown && event.isPrimary) {
|
467
|
-
this.sidebarContainer.setPointerCapture(event.pointerId);
|
468
|
-
capturedPointerId = event.pointerId;
|
469
|
-
}
|
470
|
-
}};
|
471
|
-
|
472
|
-
this.closeButton.onclick = () => {
|
473
|
-
const wasJustDragging = performance.now() - gestureEndTimestamp < 100;
|
474
|
-
const roughlyClick = isRoughlyClick();
|
475
|
-
|
476
|
-
// Ignore the click event if it was caused by dragging the button.
|
477
|
-
if (wasJustDragging && roughlyClick || !wasJustDragging) {
|
478
|
-
this.sidebarVisible.set(false);
|
479
|
-
}
|
480
|
-
};
|
481
|
-
|
482
|
-
this.sidebarContainer.onpointerup = onGestureEnd;
|
483
|
-
this.sidebarContainer.onpointercancel = onGestureEnd;
|
484
|
-
}
|
485
|
-
|
486
|
-
/**
|
487
|
-
* Updates the position of this menu **during** a drag. After a drag ends,
|
488
|
-
* {@link finalizeDrag} should be called.
|
489
|
-
*/
|
490
|
-
private handleDrag(_deltaX: number, deltaY: number) {
|
491
|
-
this.sidebarContainer.style.transition = 'none';
|
492
|
-
this.sidebarY.set(this.sidebarY.get() + deltaY);
|
493
|
-
}
|
494
|
-
|
495
|
-
/** Returns `this.sidebarY` rounded to a valid value. */
|
496
|
-
private snappedSidebarY(sidebarY?: number) {
|
497
|
-
const y = sidebarY ?? this.sidebarY.get();
|
498
|
-
|
499
|
-
const snapYs = [ -100, 0 ];
|
500
|
-
|
501
|
-
// Allow some amount of scrolling if the sidebar is too tall to fit entirely
|
502
|
-
// in the window.
|
503
|
-
if (this.sidebarContainer.clientHeight > window.innerHeight) {
|
504
|
-
snapYs.push(100);
|
505
|
-
}
|
506
|
-
|
507
|
-
let closestSnap = snapYs[0];
|
508
|
-
for (const snapY of snapYs) {
|
509
|
-
if (Math.abs(snapY - y) < Math.abs(closestSnap - y)) {
|
510
|
-
closestSnap = snapY;
|
511
|
-
}
|
512
|
-
}
|
513
|
-
|
514
|
-
return closestSnap;
|
515
|
-
}
|
516
|
-
|
517
|
-
/**
|
518
|
-
* Moves the menu to a valid location or closes it, depending on
|
519
|
-
* the position set by the drag.
|
520
|
-
*/
|
521
|
-
private finalizeDrag() {
|
522
|
-
this.sidebarContainer.style.transition = '';
|
523
|
-
if (this.sidebarY.get() > this.sidebarContainer.clientHeight / 2) {
|
524
|
-
this.sidebarVisible.set(false);
|
525
|
-
} else {
|
526
|
-
// Snap to the closest valid Y.
|
527
|
-
this.sidebarY.set(this.snappedSidebarY());
|
528
|
-
}
|
529
|
-
}
|
530
|
-
|
531
|
-
protected override serializeInternal() {
|
532
|
-
return {
|
533
|
-
menuSizeY: this.snappedSidebarY(),
|
534
|
-
};
|
535
|
-
}
|
536
|
-
|
537
|
-
protected override deserializeInternal(json: any) {
|
538
|
-
if (typeof json === 'object' && typeof json['menuSizeY'] === 'number') {
|
539
|
-
// Load the y-position of the sidebar -- call snappedSidebarY to ensure validity.
|
540
|
-
this.sidebarY.set(this.snappedSidebarY(json['menuSizeY']));
|
541
|
-
}
|
542
|
-
}
|
543
|
-
}
|