js-draw 1.14.0 → 1.15.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/Editor.css +285 -1
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.js +5 -0
- package/dist/cjs/components/util/StrokeSmoother.js +11 -4
- package/dist/cjs/rendering/caching/CacheRecordManager.js +1 -1
- package/dist/cjs/testing/sendHtmlSwipe.d.ts +4 -0
- package/dist/cjs/testing/sendHtmlSwipe.js +14 -0
- package/dist/cjs/toolbar/EdgeToolbar.d.ts +1 -0
- package/dist/cjs/toolbar/EdgeToolbar.js +30 -110
- package/dist/cjs/toolbar/IconProvider.d.ts +1 -0
- package/dist/cjs/toolbar/IconProvider.js +27 -0
- package/dist/cjs/toolbar/localization.d.ts +28 -1
- package/dist/cjs/toolbar/localization.js +30 -1
- package/dist/cjs/toolbar/utils/HelpDisplay.d.ts +37 -0
- package/dist/cjs/toolbar/utils/HelpDisplay.js +442 -0
- package/dist/cjs/toolbar/utils/HelpDisplay.test.d.ts +1 -0
- package/dist/cjs/toolbar/utils/localization.d.ts +9 -0
- package/dist/cjs/toolbar/utils/localization.js +11 -0
- package/dist/cjs/toolbar/utils/makeDraggable.d.ts +16 -0
- package/dist/cjs/toolbar/utils/makeDraggable.js +130 -0
- package/dist/cjs/toolbar/widgets/ActionButtonWidget.d.ts +7 -0
- package/dist/cjs/toolbar/widgets/ActionButtonWidget.js +14 -2
- package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +8 -1
- package/dist/cjs/toolbar/widgets/BaseWidget.js +25 -3
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.d.ts +3 -1
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +19 -4
- package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +3 -1
- package/dist/cjs/toolbar/widgets/HandToolWidget.js +19 -7
- package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -0
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +4 -2
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +27 -8
- package/dist/cjs/toolbar/widgets/SelectionToolWidget.d.ts +3 -1
- package/dist/cjs/toolbar/widgets/SelectionToolWidget.js +19 -5
- package/dist/cjs/toolbar/widgets/components/makeColorInput.d.ts +2 -0
- package/dist/cjs/toolbar/widgets/components/makeColorInput.js +17 -7
- package/dist/cjs/toolbar/widgets/components/makeGridSelector.d.ts +6 -0
- package/dist/cjs/toolbar/widgets/components/makeGridSelector.js +3 -0
- package/dist/cjs/tools/FindTool.js +18 -5
- package/dist/cjs/util/addLongPressOrHoverCssClasses.d.ts +3 -1
- package/dist/cjs/util/addLongPressOrHoverCssClasses.js +2 -1
- package/dist/cjs/util/cloneElementWithStyles.d.ts +6 -0
- package/dist/cjs/util/cloneElementWithStyles.js +32 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.mjs +5 -0
- package/dist/mjs/components/util/StrokeSmoother.mjs +11 -4
- package/dist/mjs/rendering/caching/CacheRecordManager.mjs +1 -1
- package/dist/mjs/testing/sendHtmlSwipe.d.ts +4 -0
- package/dist/mjs/testing/sendHtmlSwipe.mjs +12 -0
- package/dist/mjs/toolbar/EdgeToolbar.d.ts +1 -0
- package/dist/mjs/toolbar/EdgeToolbar.mjs +30 -110
- package/dist/mjs/toolbar/IconProvider.d.ts +1 -0
- package/dist/mjs/toolbar/IconProvider.mjs +27 -0
- package/dist/mjs/toolbar/localization.d.ts +28 -1
- package/dist/mjs/toolbar/localization.mjs +30 -1
- package/dist/mjs/toolbar/utils/HelpDisplay.d.ts +37 -0
- package/dist/mjs/toolbar/utils/HelpDisplay.mjs +437 -0
- package/dist/mjs/toolbar/utils/HelpDisplay.test.d.ts +1 -0
- package/dist/mjs/toolbar/utils/localization.d.ts +9 -0
- package/dist/mjs/toolbar/utils/localization.mjs +8 -0
- package/dist/mjs/toolbar/utils/makeDraggable.d.ts +16 -0
- package/dist/mjs/toolbar/utils/makeDraggable.mjs +128 -0
- package/dist/mjs/toolbar/widgets/ActionButtonWidget.d.ts +7 -0
- package/dist/mjs/toolbar/widgets/ActionButtonWidget.mjs +14 -2
- package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +8 -1
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +25 -3
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.d.ts +3 -1
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +19 -4
- package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +3 -1
- package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +19 -7
- package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -0
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +4 -2
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +27 -8
- package/dist/mjs/toolbar/widgets/SelectionToolWidget.d.ts +3 -1
- package/dist/mjs/toolbar/widgets/SelectionToolWidget.mjs +19 -5
- package/dist/mjs/toolbar/widgets/components/makeColorInput.d.ts +2 -0
- package/dist/mjs/toolbar/widgets/components/makeColorInput.mjs +17 -7
- package/dist/mjs/toolbar/widgets/components/makeGridSelector.d.ts +6 -0
- package/dist/mjs/toolbar/widgets/components/makeGridSelector.mjs +3 -0
- package/dist/mjs/tools/FindTool.mjs +18 -5
- package/dist/mjs/util/addLongPressOrHoverCssClasses.d.ts +3 -1
- package/dist/mjs/util/addLongPressOrHoverCssClasses.mjs +2 -1
- package/dist/mjs/util/cloneElementWithStyles.d.ts +6 -0
- package/dist/mjs/util/cloneElementWithStyles.mjs +30 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +2 -2
- package/src/toolbar/EdgeToolbar.scss +23 -2
- package/src/toolbar/toolbar.scss +2 -0
- package/src/toolbar/utils/HelpDisplay.scss +315 -0
- package/src/toolbar/widgets/components/makeColorInput.scss +7 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
import { Vec2 } from '@js-draw/math';
|
2
|
+
const makeDraggable = (dragElement, options) => {
|
3
|
+
const dragElements = [
|
4
|
+
...options.draggableChildElements,
|
5
|
+
dragElement,
|
6
|
+
];
|
7
|
+
let lastX = 0;
|
8
|
+
let lastY = 0;
|
9
|
+
let startX = 0;
|
10
|
+
let startY = 0;
|
11
|
+
let pointerDown = false;
|
12
|
+
let capturedPointerId = null;
|
13
|
+
const isDraggableElement = (element) => {
|
14
|
+
if (!element) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
if (dragElements.includes(element)) {
|
18
|
+
return true;
|
19
|
+
}
|
20
|
+
// Some inputs handle dragging themselves. Don't also interpret such gestures
|
21
|
+
// as dragging the dropdown.
|
22
|
+
const undraggableElementTypes = ['INPUT', 'SELECT', 'IMG'];
|
23
|
+
let hasSuitableAncestors = false;
|
24
|
+
let ancestor = element.parentElement;
|
25
|
+
while (ancestor) {
|
26
|
+
if (undraggableElementTypes.includes(ancestor.tagName)) {
|
27
|
+
break;
|
28
|
+
}
|
29
|
+
if (dragElements.includes(ancestor)) {
|
30
|
+
hasSuitableAncestors = true;
|
31
|
+
break;
|
32
|
+
}
|
33
|
+
ancestor = ancestor.parentElement;
|
34
|
+
}
|
35
|
+
return !undraggableElementTypes.includes(element.tagName) && hasSuitableAncestors;
|
36
|
+
};
|
37
|
+
const removeEventListenerCallbacks = [];
|
38
|
+
const addEventListener = (listenerType, listener, options) => {
|
39
|
+
dragElement.addEventListener(listenerType, listener, options);
|
40
|
+
removeEventListenerCallbacks.push(() => {
|
41
|
+
dragElement.removeEventListener(listenerType, listener);
|
42
|
+
});
|
43
|
+
};
|
44
|
+
const clickThreshold = 5;
|
45
|
+
// Returns whether the current (or if no current, **the last**) gesture is roughly a click.
|
46
|
+
// Because this can be called **after** a gesture has just ended, it should not require
|
47
|
+
// the gesture to be in progress.
|
48
|
+
const isRoughlyClick = () => {
|
49
|
+
return Math.hypot(lastX - startX, lastY - startY) < clickThreshold;
|
50
|
+
};
|
51
|
+
let startedDragging = false;
|
52
|
+
addEventListener('pointerdown', event => {
|
53
|
+
if (event.defaultPrevented || !isDraggableElement(event.target)) {
|
54
|
+
return;
|
55
|
+
}
|
56
|
+
if (event.isPrimary) {
|
57
|
+
startedDragging = false;
|
58
|
+
lastX = event.clientX;
|
59
|
+
lastY = event.clientY;
|
60
|
+
startX = event.clientX;
|
61
|
+
startY = event.clientY;
|
62
|
+
capturedPointerId = null;
|
63
|
+
pointerDown = true;
|
64
|
+
}
|
65
|
+
}, { passive: true });
|
66
|
+
const onGestureEnd = (_event) => {
|
67
|
+
// If the pointerup/pointercancel event was for a pointer not being tracked,
|
68
|
+
if (!pointerDown) {
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
if (capturedPointerId !== null) {
|
72
|
+
dragElement.releasePointerCapture(capturedPointerId);
|
73
|
+
capturedPointerId = null;
|
74
|
+
}
|
75
|
+
options.onDragEnd({
|
76
|
+
roughlyClick: isRoughlyClick(),
|
77
|
+
endTimestamp: performance.now(),
|
78
|
+
displacement: Vec2.of(lastX - startX, lastY - startY),
|
79
|
+
});
|
80
|
+
pointerDown = false;
|
81
|
+
startedDragging = false;
|
82
|
+
};
|
83
|
+
addEventListener('pointermove', event => {
|
84
|
+
if (!event.isPrimary || !pointerDown) {
|
85
|
+
return undefined;
|
86
|
+
}
|
87
|
+
// Mouse event and no buttons pressed? Cancel the event.
|
88
|
+
// This can happen if the event was canceled by a focus change (e.g. by opening a
|
89
|
+
// right-click menu).
|
90
|
+
if (event.pointerType === 'mouse' && event.buttons === 0) {
|
91
|
+
onGestureEnd(event);
|
92
|
+
return undefined;
|
93
|
+
}
|
94
|
+
// Only capture after motion -- capturing early prevents click events in Chrome.
|
95
|
+
if (capturedPointerId === null && !isRoughlyClick()) {
|
96
|
+
dragElement.setPointerCapture(event.pointerId);
|
97
|
+
capturedPointerId = event.pointerId;
|
98
|
+
}
|
99
|
+
const x = event.clientX;
|
100
|
+
const y = event.clientY;
|
101
|
+
const dx = x - lastX;
|
102
|
+
const dy = y - lastY;
|
103
|
+
const isClick = Math.abs(x - startX) <= clickThreshold && Math.abs(y - startY) <= clickThreshold;
|
104
|
+
if (!isClick || startedDragging) {
|
105
|
+
options.onDrag(dx, dy, Vec2.of(x - startX, y - startY));
|
106
|
+
lastX = x;
|
107
|
+
lastY = y;
|
108
|
+
startedDragging = true;
|
109
|
+
}
|
110
|
+
});
|
111
|
+
addEventListener('pointerleave', event => {
|
112
|
+
// Capture the pointer if it exits the container while dragging.
|
113
|
+
if (capturedPointerId === null && pointerDown && event.isPrimary) {
|
114
|
+
dragElement.setPointerCapture(event.pointerId);
|
115
|
+
capturedPointerId = event.pointerId;
|
116
|
+
}
|
117
|
+
});
|
118
|
+
addEventListener('pointerup', onGestureEnd);
|
119
|
+
addEventListener('pointercancel', onGestureEnd);
|
120
|
+
return {
|
121
|
+
removeListeners: () => {
|
122
|
+
for (const removeListenerCallback of removeEventListenerCallbacks) {
|
123
|
+
removeListenerCallback();
|
124
|
+
}
|
125
|
+
},
|
126
|
+
};
|
127
|
+
};
|
128
|
+
export default makeDraggable;
|
@@ -8,6 +8,13 @@ export default class ActionButtonWidget extends BaseWidget {
|
|
8
8
|
protected clickAction: () => void;
|
9
9
|
protected mustBeToplevel: boolean;
|
10
10
|
constructor(editor: Editor, id: string, makeIcon: () => Element | null, title: string, clickAction: () => void, localizationTable?: ToolbarLocalization, mustBeToplevel?: boolean, autoDisableInReadOnlyEditors?: boolean);
|
11
|
+
/**
|
12
|
+
* Sets the text shown in a help overlay for this button.
|
13
|
+
*
|
14
|
+
* See {@link getHelpText}.
|
15
|
+
*/
|
16
|
+
setHelpText(helpText: string): void;
|
17
|
+
protected getHelpText(): string | undefined;
|
11
18
|
protected shouldAutoDisableInReadOnlyEditor(): boolean;
|
12
19
|
protected handleClick(): void;
|
13
20
|
protected getTitle(): string;
|
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
11
11
|
};
|
12
|
-
var _ActionButtonWidget_autoDisableInReadOnlyEditors;
|
12
|
+
var _ActionButtonWidget_autoDisableInReadOnlyEditors, _ActionButtonWidget_helpText;
|
13
13
|
import BaseWidget from './BaseWidget.mjs';
|
14
14
|
class ActionButtonWidget extends BaseWidget {
|
15
15
|
constructor(editor, id, makeIcon, title, clickAction, localizationTable, mustBeToplevel = false, autoDisableInReadOnlyEditors = true) {
|
@@ -19,8 +19,20 @@ class ActionButtonWidget extends BaseWidget {
|
|
19
19
|
this.clickAction = clickAction;
|
20
20
|
this.mustBeToplevel = mustBeToplevel;
|
21
21
|
_ActionButtonWidget_autoDisableInReadOnlyEditors.set(this, void 0);
|
22
|
+
_ActionButtonWidget_helpText.set(this, undefined);
|
22
23
|
__classPrivateFieldSet(this, _ActionButtonWidget_autoDisableInReadOnlyEditors, autoDisableInReadOnlyEditors, "f");
|
23
24
|
}
|
25
|
+
/**
|
26
|
+
* Sets the text shown in a help overlay for this button.
|
27
|
+
*
|
28
|
+
* See {@link getHelpText}.
|
29
|
+
*/
|
30
|
+
setHelpText(helpText) {
|
31
|
+
__classPrivateFieldSet(this, _ActionButtonWidget_helpText, helpText, "f");
|
32
|
+
}
|
33
|
+
getHelpText() {
|
34
|
+
return __classPrivateFieldGet(this, _ActionButtonWidget_helpText, "f");
|
35
|
+
}
|
24
36
|
shouldAutoDisableInReadOnlyEditor() {
|
25
37
|
return __classPrivateFieldGet(this, _ActionButtonWidget_autoDisableInReadOnlyEditors, "f");
|
26
38
|
}
|
@@ -40,5 +52,5 @@ class ActionButtonWidget extends BaseWidget {
|
|
40
52
|
return this.mustBeToplevel;
|
41
53
|
}
|
42
54
|
}
|
43
|
-
_ActionButtonWidget_autoDisableInReadOnlyEditors = new WeakMap();
|
55
|
+
_ActionButtonWidget_autoDisableInReadOnlyEditors = new WeakMap(), _ActionButtonWidget_helpText = new WeakMap();
|
44
56
|
export default ActionButtonWidget;
|
@@ -2,6 +2,7 @@ import Editor from '../../Editor';
|
|
2
2
|
import { KeyPressEvent } from '../../inputEvents';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import { WidgetContentLayoutManager } from './layout/types';
|
5
|
+
import HelpDisplay from '../utils/HelpDisplay';
|
5
6
|
export type SavedToolbuttonState = Record<string, any>;
|
6
7
|
/**
|
7
8
|
* A set of labels that allow toolbar themes to treat buttons differently.
|
@@ -66,7 +67,13 @@ export default abstract class BaseWidget {
|
|
66
67
|
getUniqueIdIn(container: Record<string, BaseWidget>): string;
|
67
68
|
protected abstract getTitle(): string;
|
68
69
|
protected abstract createIcon(): Element | null;
|
69
|
-
protected fillDropdown(dropdown: HTMLElement): boolean;
|
70
|
+
protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
|
71
|
+
/**
|
72
|
+
* Should return a 1-2 sentence description of the widget.
|
73
|
+
*
|
74
|
+
* At present, this is only used if this widget has an associated dropdown.
|
75
|
+
*/
|
76
|
+
protected getHelpText(): undefined | string;
|
70
77
|
/** @deprecated Renamed to `setUpButtonEventListeners`. */
|
71
78
|
protected setupActionBtnClickListener(button: HTMLElement): void;
|
72
79
|
protected setUpButtonEventListeners(button: HTMLElement): void;
|
@@ -15,6 +15,7 @@ import { keyPressEventFromHTMLEvent, keyUpEventFromHTMLEvent } from '../../inp
|
|
15
15
|
import { toolbarCSSPrefix } from '../constants.mjs';
|
16
16
|
import DropdownLayoutManager from './layout/DropdownLayoutManager.mjs';
|
17
17
|
import addLongPressOrHoverCssClasses from '../../util/addLongPressOrHoverCssClasses.mjs';
|
18
|
+
import HelpDisplay from '../utils/HelpDisplay.mjs';
|
18
19
|
/**
|
19
20
|
* A set of labels that allow toolbar themes to treat buttons differently.
|
20
21
|
*/
|
@@ -127,17 +128,30 @@ class BaseWidget {
|
|
127
128
|
}
|
128
129
|
// Add content to the widget's associated dropdown menu.
|
129
130
|
// Returns true if such a menu should be created, false otherwise.
|
130
|
-
fillDropdown(dropdown) {
|
131
|
+
fillDropdown(dropdown, helpDisplay) {
|
131
132
|
if (Object.keys(this.subWidgets).length === 0) {
|
132
133
|
return false;
|
133
134
|
}
|
134
135
|
for (const widgetId in this.subWidgets) {
|
135
136
|
const widget = this.subWidgets[widgetId];
|
136
|
-
widget.addTo(dropdown);
|
137
|
+
const widgetElement = widget.addTo(dropdown);
|
137
138
|
widget.setIsToplevel(false);
|
139
|
+
// Add help information
|
140
|
+
const helpText = widget.getHelpText();
|
141
|
+
if (helpText) {
|
142
|
+
helpDisplay?.registerTextHelpForElement(widgetElement, helpText);
|
143
|
+
}
|
138
144
|
}
|
139
145
|
return true;
|
140
146
|
}
|
147
|
+
/**
|
148
|
+
* Should return a 1-2 sentence description of the widget.
|
149
|
+
*
|
150
|
+
* At present, this is only used if this widget has an associated dropdown.
|
151
|
+
*/
|
152
|
+
getHelpText() {
|
153
|
+
return undefined;
|
154
|
+
}
|
141
155
|
/** @deprecated Renamed to `setUpButtonEventListeners`. */
|
142
156
|
setupActionBtnClickListener(button) {
|
143
157
|
return this.setUpButtonEventListeners(button);
|
@@ -229,10 +243,15 @@ class BaseWidget {
|
|
229
243
|
this.container.replaceChildren();
|
230
244
|
this.button.replaceChildren(this.icon, this.label);
|
231
245
|
this.container.appendChild(this.button);
|
246
|
+
const helpDisplay = new HelpDisplay(content => this.editor.createHTMLOverlay(content), this.editor);
|
247
|
+
const helpText = this.getHelpText();
|
248
|
+
if (helpText) {
|
249
|
+
helpDisplay.registerTextHelpForElement(this.dropdownContent, [this.getTitle(), helpText].join('\n\n'));
|
250
|
+
}
|
232
251
|
// Clear the dropdownContainer in case this element is being moved to another
|
233
252
|
// parent.
|
234
253
|
this.dropdownContent.replaceChildren();
|
235
|
-
__classPrivateFieldSet(this, _BaseWidget_hasDropdown, this.fillDropdown(this.dropdownContent), "f");
|
254
|
+
__classPrivateFieldSet(this, _BaseWidget_hasDropdown, this.fillDropdown(this.dropdownContent, helpDisplay), "f");
|
236
255
|
if (__classPrivateFieldGet(this, _BaseWidget_hasDropdown, "f")) {
|
237
256
|
this.button.classList.add('has-dropdown');
|
238
257
|
// We're re-creating the dropdown.
|
@@ -258,6 +277,9 @@ class BaseWidget {
|
|
258
277
|
this.focus();
|
259
278
|
}
|
260
279
|
});
|
280
|
+
if (helpDisplay.hasHelpText()) {
|
281
|
+
this.dropdown.appendChild(helpDisplay.createToggleButton());
|
282
|
+
}
|
261
283
|
this.dropdown.appendChild(this.dropdownContent);
|
262
284
|
}
|
263
285
|
this.setDropdownVisible(false);
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
2
|
import { ToolbarLocalization } from '../localization';
|
3
3
|
import BaseWidget from './BaseWidget';
|
4
|
+
import HelpDisplay from '../utils/HelpDisplay';
|
4
5
|
export default class DocumentPropertiesWidget extends BaseWidget {
|
5
6
|
private updateDropdownContent;
|
6
7
|
constructor(editor: Editor, localizationTable?: ToolbarLocalization);
|
@@ -18,6 +19,7 @@ export default class DocumentPropertiesWidget extends BaseWidget {
|
|
18
19
|
/** Returns the type of the topmost background component */
|
19
20
|
private getBackgroundType;
|
20
21
|
private updateImportExportRectSize;
|
22
|
+
protected getHelpText(): string;
|
21
23
|
private static idCounter;
|
22
|
-
protected fillDropdown(dropdown: HTMLElement): boolean;
|
24
|
+
protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
|
23
25
|
}
|
@@ -90,7 +90,10 @@ class DocumentPropertiesWidget extends BaseWidget {
|
|
90
90
|
this.editor.dispatch(this.editor.image.setImportExportRect(newRect));
|
91
91
|
this.editor.queueRerender();
|
92
92
|
}
|
93
|
-
|
93
|
+
getHelpText() {
|
94
|
+
return this.localizationTable.pageDropdown__baseHelpText;
|
95
|
+
}
|
96
|
+
fillDropdown(dropdown, helpDisplay) {
|
94
97
|
const container = document.createElement('div');
|
95
98
|
container.classList.add(`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`, `${toolbarCSSPrefix}document-properties-widget`);
|
96
99
|
// Background color input
|
@@ -98,7 +101,7 @@ class DocumentPropertiesWidget extends BaseWidget {
|
|
98
101
|
const backgroundColorRow = document.createElement('div');
|
99
102
|
const backgroundColorLabel = document.createElement('label');
|
100
103
|
backgroundColorLabel.innerText = this.localizationTable.backgroundColor;
|
101
|
-
const { input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue } = makeColorInput(this.editor, color => {
|
104
|
+
const { input: colorInput, container: backgroundColorInputContainer, setValue: setBgColorInputValue, registerWithHelpTextDisplay: registerHelpForInputs, } = makeColorInput(this.editor, color => {
|
102
105
|
if (!color.eq(this.getBackgroundColor())) {
|
103
106
|
this.setBackgroundColor(color);
|
104
107
|
}
|
@@ -106,9 +109,16 @@ class DocumentPropertiesWidget extends BaseWidget {
|
|
106
109
|
colorInput.id = `${toolbarCSSPrefix}docPropertiesColorInput-${DocumentPropertiesWidget.idCounter++}`;
|
107
110
|
backgroundColorLabel.htmlFor = colorInput.id;
|
108
111
|
backgroundColorRow.replaceChildren(backgroundColorLabel, backgroundColorInputContainer);
|
109
|
-
|
112
|
+
const registerWithHelp = (helpDisplay) => {
|
113
|
+
if (!helpDisplay) {
|
114
|
+
return;
|
115
|
+
}
|
116
|
+
helpDisplay?.registerTextHelpForElement(backgroundColorRow, this.localizationTable.pageDropdown__backgroundColorHelpText);
|
117
|
+
registerHelpForInputs(helpDisplay);
|
118
|
+
};
|
119
|
+
return { setBgColorInputValue, backgroundColorRow, registerWithHelp, };
|
110
120
|
};
|
111
|
-
const { backgroundColorRow, setBgColorInputValue } = makeBackgroundColorInput();
|
121
|
+
const { backgroundColorRow, setBgColorInputValue, registerWithHelp: registerBackgroundRowWithHelp, } = makeBackgroundColorInput();
|
112
122
|
const makeCheckboxRow = (labelText, onChange) => {
|
113
123
|
const rowContainer = document.createElement('div');
|
114
124
|
const labelElement = document.createElement('label');
|
@@ -189,6 +199,11 @@ class DocumentPropertiesWidget extends BaseWidget {
|
|
189
199
|
aboutButton.onclick = () => {
|
190
200
|
this.editor.showAboutDialog();
|
191
201
|
};
|
202
|
+
// Add help text
|
203
|
+
registerBackgroundRowWithHelp(helpDisplay);
|
204
|
+
helpDisplay?.registerTextHelpForElement(useGridRow, this.localizationTable.pageDropdown__gridCheckboxHelpText);
|
205
|
+
helpDisplay?.registerTextHelpForElement(auroresizeRow, this.localizationTable.pageDropdown__autoresizeCheckboxHelpText);
|
206
|
+
helpDisplay?.registerTextHelpForElement(aboutButton, this.localizationTable.pageDropdown__aboutButtonHelpText);
|
192
207
|
this.updateDropdownContent = () => {
|
193
208
|
setBgColorInputValue(this.getBackgroundColor());
|
194
209
|
const autoresize = this.editor.image.getAutoresizeEnabled();
|
@@ -3,6 +3,7 @@ import PanZoom from '../../tools/PanZoom';
|
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
4
|
import BaseToolWidget from './BaseToolWidget';
|
5
5
|
import { SavedToolbuttonState } from './BaseWidget';
|
6
|
+
import HelpDisplay from '../utils/HelpDisplay';
|
6
7
|
export default class HandToolWidget extends BaseToolWidget {
|
7
8
|
private allowTogglingBaseTool;
|
8
9
|
protected overridePanZoomTool: PanZoom;
|
@@ -13,7 +14,8 @@ export default class HandToolWidget extends BaseToolWidget {
|
|
13
14
|
protected getTitle(): string;
|
14
15
|
protected createIcon(): Element;
|
15
16
|
protected handleClick(): void;
|
16
|
-
protected
|
17
|
+
protected getHelpText(): string;
|
18
|
+
protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
|
17
19
|
setSelected(selected: boolean): void;
|
18
20
|
serializeState(): SavedToolbuttonState;
|
19
21
|
deserializeFrom(state: SavedToolbuttonState): void;
|
@@ -6,7 +6,7 @@ import { toolbarCSSPrefix } from '../constants.mjs';
|
|
6
6
|
import BaseToolWidget from './BaseToolWidget.mjs';
|
7
7
|
import BaseWidget from './BaseWidget.mjs';
|
8
8
|
import makeSeparator from './components/makeSeparator.mjs';
|
9
|
-
const makeZoomControl = (localizationTable, editor) => {
|
9
|
+
const makeZoomControl = (localizationTable, editor, helpDisplay) => {
|
10
10
|
const zoomLevelRow = document.createElement('div');
|
11
11
|
const increaseButton = document.createElement('button');
|
12
12
|
const decreaseButton = document.createElement('button');
|
@@ -55,15 +55,20 @@ const makeZoomControl = (localizationTable, editor) => {
|
|
55
55
|
const addToHistory = false;
|
56
56
|
editor.dispatch(Viewport.transformBy(editor.viewport.canvasToScreenTransform.inverse()), addToHistory);
|
57
57
|
};
|
58
|
+
helpDisplay?.registerTextHelpForElement(increaseButton, localizationTable.handDropdown__zoomInHelpText);
|
59
|
+
helpDisplay?.registerTextHelpForElement(decreaseButton, localizationTable.handDropdown__zoomOutHelpText);
|
60
|
+
helpDisplay?.registerTextHelpForElement(resetViewButton, localizationTable.handDropdown__resetViewHelpText);
|
61
|
+
helpDisplay?.registerTextHelpForElement(zoomLevelDisplay, localizationTable.handDropdown__zoomDisplayHelpText);
|
58
62
|
return zoomLevelRow;
|
59
63
|
};
|
60
64
|
class HandModeWidget extends BaseWidget {
|
61
|
-
constructor(editor, tool, flag, makeIcon, title, localizationTable) {
|
65
|
+
constructor(editor, tool, flag, makeIcon, title, helpText, localizationTable) {
|
62
66
|
super(editor, `pan-mode-${flag}`, localizationTable);
|
63
67
|
this.tool = tool;
|
64
68
|
this.flag = flag;
|
65
69
|
this.makeIcon = makeIcon;
|
66
70
|
this.title = title;
|
71
|
+
this.helpText = helpText;
|
67
72
|
editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
68
73
|
if (toolEvt.kind === EditorEventType.ToolUpdated && toolEvt.tool === tool) {
|
69
74
|
const allEnabled = !!(tool.getMode() & PanZoomMode.SinglePointerGestures);
|
@@ -93,6 +98,9 @@ class HandModeWidget extends BaseWidget {
|
|
93
98
|
fillDropdown(_dropdown) {
|
94
99
|
return false;
|
95
100
|
}
|
101
|
+
getHelpText() {
|
102
|
+
return this.helpText;
|
103
|
+
}
|
96
104
|
}
|
97
105
|
export default class HandToolWidget extends BaseToolWidget {
|
98
106
|
constructor(editor,
|
@@ -117,8 +125,8 @@ export default class HandToolWidget extends BaseToolWidget {
|
|
117
125
|
this.container.classList.add('dropdownShowable');
|
118
126
|
}
|
119
127
|
// Controls for the overriding hand tool.
|
120
|
-
const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable);
|
121
|
-
const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable);
|
128
|
+
const touchPanningWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.OneFingerTouchGestures, () => this.editor.icons.makeTouchPanningIcon(), localizationTable.touchPanning, localizationTable.handDropdown__touchPanningHelpText, localizationTable);
|
129
|
+
const rotationLockWidget = new HandModeWidget(editor, this.overridePanZoomTool, PanZoomMode.RotationLocked, () => this.editor.icons.makeRotationLockIcon(), localizationTable.lockRotation, localizationTable.handDropdown__lockRotationHelpText, localizationTable);
|
122
130
|
this.addSubWidget(touchPanningWidget);
|
123
131
|
this.addSubWidget(rotationLockWidget);
|
124
132
|
}
|
@@ -149,13 +157,17 @@ export default class HandToolWidget extends BaseToolWidget {
|
|
149
157
|
this.setDropdownVisible(!this.isDropdownVisible());
|
150
158
|
}
|
151
159
|
}
|
152
|
-
|
153
|
-
|
160
|
+
getHelpText() {
|
161
|
+
return this.localizationTable.handDropdown__baseHelpText;
|
162
|
+
}
|
163
|
+
fillDropdown(dropdown, helpDisplay) {
|
164
|
+
super.fillDropdown(dropdown, helpDisplay);
|
154
165
|
// The container for all actions that come after the toolbar buttons.
|
155
166
|
const nonbuttonActionContainer = document.createElement('div');
|
156
167
|
nonbuttonActionContainer.classList.add(`${toolbarCSSPrefix}nonbutton-controls-main-list`);
|
157
168
|
makeSeparator().addTo(nonbuttonActionContainer);
|
158
|
-
|
169
|
+
const zoomControl = makeZoomControl(this.localizationTable, this.editor, helpDisplay);
|
170
|
+
nonbuttonActionContainer.appendChild(zoomControl);
|
159
171
|
dropdown.appendChild(nonbuttonActionContainer);
|
160
172
|
return true;
|
161
173
|
}
|
@@ -95,6 +95,7 @@ class InsertImageWidget extends BaseWidget {
|
|
95
95
|
imageAltTextLabel.htmlFor = altTextInputId;
|
96
96
|
imageAltTextLabel.innerText = this.localizationTable.inputAltText;
|
97
97
|
this.imageAltTextInput.type = 'text';
|
98
|
+
this.imageAltTextInput.placeholder = this.localizationTable.describeTheImage;
|
98
99
|
this.statusView.setAttribute('aria-live', 'polite');
|
99
100
|
this.submitButton.innerText = this.localizationTable.submit;
|
100
101
|
this.selectedFiles.onUpdateAndNow(async (files) => {
|
@@ -5,6 +5,7 @@ import { KeyPressEvent } from '../../inputEvents';
|
|
5
5
|
import { ToolbarLocalization } from '../localization';
|
6
6
|
import BaseToolWidget from './BaseToolWidget';
|
7
7
|
import { SavedToolbuttonState } from './BaseWidget';
|
8
|
+
import HelpDisplay from '../utils/HelpDisplay';
|
8
9
|
export interface PenTypeRecord {
|
9
10
|
name: string;
|
10
11
|
id: string;
|
@@ -24,11 +25,12 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
24
25
|
private createIconForRecord;
|
25
26
|
protected createIcon(): Element;
|
26
27
|
private createPenTypeSelector;
|
27
|
-
protected createStrokeCorrectionOptions(): {
|
28
|
+
protected createStrokeCorrectionOptions(helpOverlay?: HelpDisplay): {
|
28
29
|
update: () => void;
|
29
30
|
addTo: (parent: HTMLElement) => void;
|
30
31
|
};
|
31
|
-
protected
|
32
|
+
protected getHelpText(): string;
|
33
|
+
protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
|
32
34
|
protected onKeyPress(event: KeyPressEvent): boolean;
|
33
35
|
serializeState(): SavedToolbuttonState;
|
34
36
|
deserializeFrom(state: SavedToolbuttonState): void;
|
@@ -116,7 +116,7 @@ class PenToolWidget extends BaseToolWidget {
|
|
116
116
|
return this.createIconForRecord(this.getCurrentPenType());
|
117
117
|
}
|
118
118
|
// Creates a widget that allows selecting different pen types
|
119
|
-
createPenTypeSelector() {
|
119
|
+
createPenTypeSelector(helpOverlay) {
|
120
120
|
const allChoices = this.penTypes.map((penType, index) => {
|
121
121
|
return {
|
122
122
|
id: index,
|
@@ -132,6 +132,7 @@ class PenToolWidget extends BaseToolWidget {
|
|
132
132
|
};
|
133
133
|
penSelector.value.onUpdate(onSelectorUpdate);
|
134
134
|
shapeSelector.value.onUpdate(onSelectorUpdate);
|
135
|
+
helpOverlay?.registerTextHelpForElements([penSelector.getRootElement(), shapeSelector.getRootElement()], this.localizationTable.penDropdown__penTypeHelpText);
|
135
136
|
return {
|
136
137
|
setValue: (penTypeIndex) => {
|
137
138
|
penSelector.value.set(penTypeIndex);
|
@@ -147,7 +148,7 @@ class PenToolWidget extends BaseToolWidget {
|
|
147
148
|
},
|
148
149
|
};
|
149
150
|
}
|
150
|
-
createStrokeCorrectionOptions() {
|
151
|
+
createStrokeCorrectionOptions(helpOverlay) {
|
151
152
|
const container = document.createElement('div');
|
152
153
|
container.classList.add('action-button-row', `${toolbarCSSPrefix}-pen-tool-toggle-buttons`);
|
153
154
|
const addToggleButton = (labelText, icon) => {
|
@@ -171,6 +172,9 @@ class PenToolWidget extends BaseToolWidget {
|
|
171
172
|
setOnInputListener(listener) {
|
172
173
|
onChangeListener = listener;
|
173
174
|
},
|
175
|
+
addHelpText(text) {
|
176
|
+
helpOverlay?.registerTextHelpForElement(button, text);
|
177
|
+
},
|
174
178
|
};
|
175
179
|
button.onclick = () => {
|
176
180
|
result.setChecked(!checked);
|
@@ -185,6 +189,9 @@ class PenToolWidget extends BaseToolWidget {
|
|
185
189
|
autocorrectOption.setOnInputListener(enabled => {
|
186
190
|
this.tool.setStrokeAutocorrectEnabled(enabled);
|
187
191
|
});
|
192
|
+
// Help text
|
193
|
+
autocorrectOption.addHelpText(this.localizationTable.penDropdown__autocorrectHelpText);
|
194
|
+
stabilizationOption.addHelpText(this.localizationTable.penDropdown__stabilizationHelpText);
|
188
195
|
return {
|
189
196
|
update: () => {
|
190
197
|
stabilizationOption.setChecked(!!this.tool.getInputMapper());
|
@@ -192,30 +199,42 @@ class PenToolWidget extends BaseToolWidget {
|
|
192
199
|
},
|
193
200
|
addTo: (parent) => {
|
194
201
|
parent.appendChild(container);
|
195
|
-
}
|
202
|
+
},
|
196
203
|
};
|
197
204
|
}
|
198
|
-
|
205
|
+
getHelpText() {
|
206
|
+
return this.localizationTable.penDropdown__baseHelpText;
|
207
|
+
}
|
208
|
+
fillDropdown(dropdown, helpDisplay) {
|
199
209
|
const container = document.createElement('div');
|
200
210
|
container.classList.add(`${toolbarCSSPrefix}spacedList`, `${toolbarCSSPrefix}nonbutton-controls-main-list`);
|
201
211
|
// Thickness: Value of the input is squared to allow for finer control/larger values.
|
202
212
|
const { container: thicknessRow, setValue: setThickness } = makeThicknessSlider(this.editor, thickness => {
|
203
213
|
this.tool.setThickness(thickness);
|
204
214
|
});
|
205
|
-
const penTypeSelect = this.createPenTypeSelector();
|
206
215
|
const colorRow = document.createElement('div');
|
207
216
|
const colorLabel = document.createElement('label');
|
208
|
-
const
|
217
|
+
const colorInputControl = makeColorInput(this.editor, color => {
|
209
218
|
this.tool.setColor(color);
|
210
219
|
});
|
220
|
+
const { input: colorInput, container: colorInputContainer } = colorInputControl;
|
211
221
|
colorInput.id = `${toolbarCSSPrefix}colorInput${PenToolWidget.idCounter++}`;
|
212
222
|
colorLabel.innerText = this.localizationTable.colorLabel;
|
213
223
|
colorLabel.setAttribute('for', colorInput.id);
|
214
224
|
colorRow.appendChild(colorLabel);
|
215
225
|
colorRow.appendChild(colorInputContainer);
|
216
|
-
|
226
|
+
// Autocorrect and stabilization options
|
227
|
+
const toggleButtonRow = this.createStrokeCorrectionOptions(helpDisplay);
|
228
|
+
const penTypeSelect = this.createPenTypeSelector(helpDisplay);
|
229
|
+
// Add help text for color and thickness last, as these are likely to be
|
230
|
+
// features users are least interested in.
|
231
|
+
helpDisplay?.registerTextHelpForElement(colorRow, this.localizationTable.penDropdown__colorHelpText);
|
232
|
+
if (helpDisplay) {
|
233
|
+
colorInputControl.registerWithHelpTextDisplay(helpDisplay);
|
234
|
+
}
|
235
|
+
helpDisplay?.registerTextHelpForElement(thicknessRow, this.localizationTable.penDropdown__thicknessHelpText);
|
217
236
|
this.updateInputs = () => {
|
218
|
-
|
237
|
+
colorInputControl.setValue(this.tool.getColor());
|
219
238
|
setThickness(this.tool.getThickness());
|
220
239
|
penTypeSelect.updateIcons();
|
221
240
|
// Update the selected stroke factory.
|
@@ -3,6 +3,7 @@ import SelectionTool from '../../tools/SelectionTool/SelectionTool';
|
|
3
3
|
import { KeyPressEvent } from '../../inputEvents';
|
4
4
|
import { ToolbarLocalization } from '../localization';
|
5
5
|
import BaseToolWidget from './BaseToolWidget';
|
6
|
+
import HelpDisplay from '../utils/HelpDisplay';
|
6
7
|
export default class SelectionToolWidget extends BaseToolWidget {
|
7
8
|
private tool;
|
8
9
|
private updateFormatMenu;
|
@@ -11,5 +12,6 @@ export default class SelectionToolWidget extends BaseToolWidget {
|
|
11
12
|
protected onKeyPress(event: KeyPressEvent): boolean;
|
12
13
|
protected getTitle(): string;
|
13
14
|
protected createIcon(): Element;
|
14
|
-
protected
|
15
|
+
protected getHelpText(): string;
|
16
|
+
protected fillDropdown(dropdown: HTMLElement, helpDisplay?: HelpDisplay): boolean;
|
15
17
|
}
|