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,179 +0,0 @@
|
|
1
|
-
import { MutableReactiveValue } from '../../../util/ReactiveValue';
|
2
|
-
import stopPropagationOfScrollingWheelEvents from '../../../util/stopPropagationOfScrollingWheelEvents';
|
3
|
-
import { IconElemType } from '../../IconProvider';
|
4
|
-
import { toolbarCSSPrefix } from '../../constants';
|
5
|
-
|
6
|
-
interface GridSelectChoice<ChoiceIdType> {
|
7
|
-
// `id` should be unique in all choices
|
8
|
-
id: ChoiceIdType;
|
9
|
-
makeIcon: ()=>IconElemType;
|
10
|
-
title: string;
|
11
|
-
}
|
12
|
-
|
13
|
-
interface GridSelector<ChoiceIdType> {
|
14
|
-
value: MutableReactiveValue<ChoiceIdType>,
|
15
|
-
linkWith: (other: GridSelector<ChoiceIdType>)=>void;
|
16
|
-
updateIcons: ()=>void;
|
17
|
-
addTo: (parent: HTMLElement)=>void;
|
18
|
-
|
19
|
-
/** Used internally @internal */
|
20
|
-
_radiogroupName: string;
|
21
|
-
}
|
22
|
-
|
23
|
-
let idCounter = 0;
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Creates a widget that allows users to select one of serveral items from a list.
|
27
|
-
*
|
28
|
-
* `ChoiceIdType` should be `string`, a `number`, or an `enum` (or similar).
|
29
|
-
*
|
30
|
-
* If this input is set to an ID that is not in `choices`, no item is selected.
|
31
|
-
*/
|
32
|
-
const makeGridSelector = <ChoiceIdType> (
|
33
|
-
// Text before the grid selector used as a label
|
34
|
-
labelText: string,
|
35
|
-
defaultId: ChoiceIdType,
|
36
|
-
choices: GridSelectChoice<ChoiceIdType>[],
|
37
|
-
): GridSelector<ChoiceIdType> => {
|
38
|
-
const outerContainer = document.createElement('div');
|
39
|
-
outerContainer.classList.add(`${toolbarCSSPrefix}grid-selector`);
|
40
|
-
|
41
|
-
const selectedValue = MutableReactiveValue.fromInitialValue(defaultId);
|
42
|
-
|
43
|
-
const menuContainer = document.createElement('div');
|
44
|
-
menuContainer.setAttribute('role', 'menu');
|
45
|
-
menuContainer.id = `${toolbarCSSPrefix}-grid-select-id-${idCounter++}`;
|
46
|
-
|
47
|
-
stopPropagationOfScrollingWheelEvents(menuContainer);
|
48
|
-
|
49
|
-
const label = document.createElement('label');
|
50
|
-
label.innerText = labelText;
|
51
|
-
label.htmlFor = menuContainer.id;
|
52
|
-
outerContainer.appendChild(label);
|
53
|
-
|
54
|
-
// All buttons in a radiogroup need the same name attribute.
|
55
|
-
let radiogroupName = `${toolbarCSSPrefix}-grid-selector-${idCounter++}`;
|
56
|
-
|
57
|
-
type ChoiceType = GridSelectChoice<ChoiceIdType>;
|
58
|
-
|
59
|
-
const createChoiceButton = (record: ChoiceType) => {
|
60
|
-
const buttonContainer = document.createElement('div');
|
61
|
-
buttonContainer.classList.add('choice-button');
|
62
|
-
|
63
|
-
const button = document.createElement('input');
|
64
|
-
button.type = 'radio';
|
65
|
-
button.id = `${toolbarCSSPrefix}-grid-select-button-${idCounter++}`;
|
66
|
-
|
67
|
-
// Clicking any part of labelContainer triggers the radio button.
|
68
|
-
const labelContainer = document.createElement('label');
|
69
|
-
|
70
|
-
const rebuildLabel = () => {
|
71
|
-
labelContainer.setAttribute('title', record.title);
|
72
|
-
|
73
|
-
const labelText = document.createElement('span');
|
74
|
-
labelText.classList.add('button-label-text');
|
75
|
-
|
76
|
-
const icon = record.makeIcon();
|
77
|
-
icon.classList.add('icon');
|
78
|
-
|
79
|
-
// The title of the record
|
80
|
-
labelText.innerText = record.title;
|
81
|
-
labelContainer.htmlFor = button.id;
|
82
|
-
|
83
|
-
labelContainer.replaceChildren(icon, labelText);
|
84
|
-
};
|
85
|
-
rebuildLabel();
|
86
|
-
|
87
|
-
// Mark the button as belonging to the current group (causes
|
88
|
-
// other buttons in the same group to automatically uncheck
|
89
|
-
// when this button is checked).
|
90
|
-
const updateButtonRadiogroupName = () => {
|
91
|
-
button.name = radiogroupName;
|
92
|
-
};
|
93
|
-
|
94
|
-
updateButtonRadiogroupName();
|
95
|
-
|
96
|
-
const updateButtonCSS = () => {
|
97
|
-
if (button.checked) {
|
98
|
-
buttonContainer.classList.add('checked');
|
99
|
-
} else {
|
100
|
-
buttonContainer.classList.remove('checked');
|
101
|
-
}
|
102
|
-
};
|
103
|
-
|
104
|
-
button.oninput = () => {
|
105
|
-
// Setting the selected value fires an event that causes the value
|
106
|
-
// of this button to be set.
|
107
|
-
if (button.checked) {
|
108
|
-
selectedValue.set(record.id);
|
109
|
-
}
|
110
|
-
|
111
|
-
updateButtonCSS();
|
112
|
-
};
|
113
|
-
|
114
|
-
buttonContainer.replaceChildren(button, labelContainer);
|
115
|
-
menuContainer.appendChild(buttonContainer);
|
116
|
-
|
117
|
-
// Set whether the current button is checked
|
118
|
-
const setChecked = (checked: boolean) => {
|
119
|
-
button.checked = checked;
|
120
|
-
updateButtonCSS();
|
121
|
-
};
|
122
|
-
setChecked(false);
|
123
|
-
|
124
|
-
// Updates the factory's icon based on the current style of the tool.
|
125
|
-
const updateIcon = () => {
|
126
|
-
rebuildLabel();
|
127
|
-
};
|
128
|
-
|
129
|
-
return {
|
130
|
-
choiceRecord: record,
|
131
|
-
setChecked,
|
132
|
-
updateIcon,
|
133
|
-
updateButtonRadiogroupName
|
134
|
-
};
|
135
|
-
};
|
136
|
-
|
137
|
-
const buttons: Array<ReturnType<typeof createChoiceButton>> = [];
|
138
|
-
for (const choice of choices) {
|
139
|
-
buttons.push(createChoiceButton(choice));
|
140
|
-
}
|
141
|
-
// invariant: buttons.length = choices.length
|
142
|
-
// However, it is still possible that selectedValue does not correspond
|
143
|
-
// to a choice in `choices`. This is acceptable.
|
144
|
-
|
145
|
-
outerContainer.appendChild(menuContainer);
|
146
|
-
|
147
|
-
selectedValue.onUpdateAndNow(choiceId => {
|
148
|
-
for (let i = 0; i < buttons.length; i++) {
|
149
|
-
buttons[i].setChecked(buttons[i].choiceRecord.id === choiceId);
|
150
|
-
}
|
151
|
-
});
|
152
|
-
|
153
|
-
const result = {
|
154
|
-
value: selectedValue,
|
155
|
-
|
156
|
-
_radiogroupName: radiogroupName,
|
157
|
-
|
158
|
-
linkWith: (other: GridSelector<ChoiceIdType>) => {
|
159
|
-
result._radiogroupName = other._radiogroupName;
|
160
|
-
radiogroupName = other._radiogroupName;
|
161
|
-
|
162
|
-
for (const button of buttons) {
|
163
|
-
button.updateButtonRadiogroupName();
|
164
|
-
}
|
165
|
-
},
|
166
|
-
|
167
|
-
updateIcons: () => {
|
168
|
-
buttons.forEach(button => button.updateIcon());
|
169
|
-
},
|
170
|
-
|
171
|
-
addTo: (parent: HTMLElement) => {
|
172
|
-
parent.appendChild(outerContainer);
|
173
|
-
},
|
174
|
-
};
|
175
|
-
|
176
|
-
return result;
|
177
|
-
};
|
178
|
-
|
179
|
-
export default makeGridSelector;
|
@@ -1,17 +0,0 @@
|
|
1
|
-
|
2
|
-
/**
|
3
|
-
* Creates a separator element that renders a line and, optionally, a header.
|
4
|
-
*/
|
5
|
-
const makeSeparator = (header: string = '') => {
|
6
|
-
const container = document.createElement('div');
|
7
|
-
container.classList.add('tool-dropdown-separator');
|
8
|
-
container.innerText = header;
|
9
|
-
|
10
|
-
return {
|
11
|
-
addTo: (parent: HTMLElement) => {
|
12
|
-
parent.appendChild(container);
|
13
|
-
},
|
14
|
-
};
|
15
|
-
};
|
16
|
-
|
17
|
-
export default makeSeparator;
|
@@ -1,62 +0,0 @@
|
|
1
|
-
import { toRoundedString } from '@js-draw/math';
|
2
|
-
import { toolbarCSSPrefix } from '../../constants';
|
3
|
-
import { ToolbarContext } from '../../types';
|
4
|
-
|
5
|
-
let idCounter = 0;
|
6
|
-
|
7
|
-
const makeThicknessSlider = (
|
8
|
-
context: ToolbarContext,
|
9
|
-
onChange: (value: number)=>void
|
10
|
-
) => {
|
11
|
-
const container = document.createElement('div');
|
12
|
-
|
13
|
-
const thicknessLabel = document.createElement('label');
|
14
|
-
const thicknessInput = document.createElement('input');
|
15
|
-
|
16
|
-
container.classList.add(`${toolbarCSSPrefix}thicknessSliderContainer`);
|
17
|
-
|
18
|
-
// Give inputs IDs so we can label them with a <label for=...>Label text</label>
|
19
|
-
thicknessInput.id = `${toolbarCSSPrefix}thicknessInput${idCounter++}`;
|
20
|
-
|
21
|
-
thicknessLabel.innerText = context.localization.thicknessLabel;
|
22
|
-
thicknessLabel.setAttribute('for', thicknessInput.id);
|
23
|
-
|
24
|
-
// Use a logarithmic scale for thicknessInput (finer control over thinner strokewidths.)
|
25
|
-
const inverseThicknessInputFn = (t: number) => Math.log10(t);
|
26
|
-
const thicknessInputFn = (t: number) => 10**t;
|
27
|
-
|
28
|
-
thicknessInput.type = 'range';
|
29
|
-
thicknessInput.oninput = () => {
|
30
|
-
onChange(thicknessInputFn(parseFloat(thicknessInput.value)));
|
31
|
-
};
|
32
|
-
container.appendChild(thicknessLabel);
|
33
|
-
container.appendChild(thicknessInput);
|
34
|
-
|
35
|
-
const setBounds = (min: number, max: number) => {
|
36
|
-
const round = (value: number, roundUp: boolean) => {
|
37
|
-
const roundFn = roundUp ? Math.ceil : Math.floor;
|
38
|
-
return roundFn(value * 100) / 100;
|
39
|
-
};
|
40
|
-
const sliderMin = round(inverseThicknessInputFn(min), false);
|
41
|
-
const sliderMax = round(inverseThicknessInputFn(max), true);
|
42
|
-
|
43
|
-
thicknessInput.min = `${sliderMin}`;
|
44
|
-
thicknessInput.max = `${sliderMax}`;
|
45
|
-
thicknessInput.step = `${toRoundedString((sliderMax - sliderMin) / 20)}`;
|
46
|
-
};
|
47
|
-
|
48
|
-
setBounds(2, 262);
|
49
|
-
|
50
|
-
return {
|
51
|
-
container,
|
52
|
-
addTo: (parent: HTMLElement) => {
|
53
|
-
parent.appendChild(container);
|
54
|
-
},
|
55
|
-
setBounds,
|
56
|
-
setValue: (thickness: number) => {
|
57
|
-
thicknessInput.value = inverseThicknessInputFn(thickness).toString();
|
58
|
-
},
|
59
|
-
};
|
60
|
-
};
|
61
|
-
|
62
|
-
export default makeThicknessSlider;
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import KeyboardShortcutManager from '../../shortcuts/KeyboardShortcutManager';
|
2
|
-
|
3
|
-
// Selection
|
4
|
-
export const resizeImageToSelectionKeyboardShortcut =
|
5
|
-
'jsdraw.toolbar.SelectionTool.resizeImageToSelection';
|
6
|
-
KeyboardShortcutManager.registerDefaultKeyboardShortcut(
|
7
|
-
resizeImageToSelectionKeyboardShortcut, [ 'ctrlOrMeta+r' ], 'Resize image to selection'
|
8
|
-
);
|
9
|
-
|
10
|
-
// Pen tool
|
11
|
-
export const selectStrokeTypeKeyboardShortcutIds: string[] =
|
12
|
-
[1, 2, 3, 4, 5, 6, 7].map(id => `jsdraw.toolbar.PenTool.select-pen-${id}`);
|
13
|
-
|
14
|
-
for (let i = 0; i < selectStrokeTypeKeyboardShortcutIds.length; i++) {
|
15
|
-
const id = selectStrokeTypeKeyboardShortcutIds[i];
|
16
|
-
KeyboardShortcutManager.registerDefaultKeyboardShortcut(
|
17
|
-
id, [ `CtrlOrMeta+Digit${(i + 1)}` ], 'Select pen style ' + (i + 1),
|
18
|
-
);
|
19
|
-
}
|
@@ -1,262 +0,0 @@
|
|
1
|
-
import { EditorEventType, EditorNotifier } from '../../../types';
|
2
|
-
import EventDispatcher, { DispatcherEventListener } from '../../../EventDispatcher';
|
3
|
-
import { ToolbarLocalization } from '../../localization';
|
4
|
-
import { ToolMenu, WidgetContentLayoutManager, ToolMenuParent } from './types';
|
5
|
-
import { toolbarCSSPrefix } from '../../constants';
|
6
|
-
import { MutableReactiveValue, ReactiveValue } from '../../../util/ReactiveValue';
|
7
|
-
|
8
|
-
enum DropdownEventType {
|
9
|
-
DropdownShown,
|
10
|
-
DropdownHidden,
|
11
|
-
}
|
12
|
-
|
13
|
-
interface DropdownShownEvent {
|
14
|
-
// If undefined, the event is forwarded from a different layout manager
|
15
|
-
dropdown?: Dropdown;
|
16
|
-
|
17
|
-
fromToplevelDropdown: boolean;
|
18
|
-
}
|
19
|
-
|
20
|
-
type NotifierType = EventDispatcher<DropdownEventType, DropdownShownEvent>;
|
21
|
-
|
22
|
-
class Dropdown implements ToolMenu {
|
23
|
-
private dropdownContainer: HTMLElement;
|
24
|
-
public readonly visible: MutableReactiveValue<boolean>;
|
25
|
-
|
26
|
-
private dropdownToggleListener: DispatcherEventListener|null = null;
|
27
|
-
|
28
|
-
public constructor(
|
29
|
-
public parent: ToolMenuParent,
|
30
|
-
private notifier: NotifierType,
|
31
|
-
private onDestroy: ()=>void,
|
32
|
-
) {
|
33
|
-
this.visible = ReactiveValue.fromInitialValue(false);
|
34
|
-
|
35
|
-
this.dropdownContainer = document.createElement('div');
|
36
|
-
this.dropdownContainer.classList.add(`${toolbarCSSPrefix}dropdown`);
|
37
|
-
this.dropdownContainer.classList.add('hidden');
|
38
|
-
|
39
|
-
parent.target.insertAdjacentElement('afterend', this.dropdownContainer);
|
40
|
-
|
41
|
-
// When another dropdown is shown,
|
42
|
-
this.dropdownToggleListener = this.notifier.on(DropdownEventType.DropdownShown, (evt) => {
|
43
|
-
if (
|
44
|
-
evt.dropdown !== this &&
|
45
|
-
|
46
|
-
// Don't hide if a submenu was shown (it might be a submenu of
|
47
|
-
// the current menu).
|
48
|
-
evt.fromToplevelDropdown
|
49
|
-
) {
|
50
|
-
this.setVisible(false);
|
51
|
-
}
|
52
|
-
});
|
53
|
-
}
|
54
|
-
|
55
|
-
public onActivated(): void {
|
56
|
-
// Do nothing.
|
57
|
-
}
|
58
|
-
|
59
|
-
protected repositionDropdown() {
|
60
|
-
const dropdownBBox = this.dropdownContainer.getBoundingClientRect();
|
61
|
-
const screenWidth = document.scrollingElement?.clientWidth ?? document.body.clientHeight;
|
62
|
-
const screenHeight = document.scrollingElement?.clientHeight ?? document.body.clientHeight;
|
63
|
-
|
64
|
-
let translateX = undefined;
|
65
|
-
let translateY = undefined;
|
66
|
-
|
67
|
-
if (dropdownBBox.left > screenWidth / 2) {
|
68
|
-
const targetElem = this.parent.target;
|
69
|
-
translateX = `calc(${targetElem.clientWidth + 'px'} - 100%)`;
|
70
|
-
}
|
71
|
-
|
72
|
-
// Shift the dropdown if it's off the screen, but only if doing so moves it on to the screen
|
73
|
-
// (prevents dropdowns from going almost completely offscreen on small screens).
|
74
|
-
if (dropdownBBox.bottom > screenHeight && (dropdownBBox.top - dropdownBBox.height > 0)) {
|
75
|
-
const targetElem = this.parent.target;
|
76
|
-
translateY = `calc(-${targetElem.clientHeight}px - 100%)`;
|
77
|
-
}
|
78
|
-
|
79
|
-
// Use .translate so as not to conflict with CSS animating the
|
80
|
-
// transform property.
|
81
|
-
if (translateX || translateY) {
|
82
|
-
this.dropdownContainer.style.translate = `${translateX ?? '0'} ${translateY ?? '0'}`;
|
83
|
-
} else {
|
84
|
-
this.dropdownContainer.style.translate = '';
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
private hideDropdownTimeout: any|null = null;
|
89
|
-
private setVisible(visible: boolean) {
|
90
|
-
const currentlyVisible = this.visible.get();
|
91
|
-
if (currentlyVisible === visible) {
|
92
|
-
return;
|
93
|
-
}
|
94
|
-
|
95
|
-
// If waiting to hide the dropdown, cancel it.
|
96
|
-
if (this.hideDropdownTimeout) {
|
97
|
-
clearTimeout(this.hideDropdownTimeout);
|
98
|
-
this.hideDropdownTimeout = null;
|
99
|
-
this.dropdownContainer.classList.remove('hiding');
|
100
|
-
this.repositionDropdown();
|
101
|
-
}
|
102
|
-
|
103
|
-
|
104
|
-
const animationDuration = 150; // ms
|
105
|
-
|
106
|
-
this.visible.set(visible);
|
107
|
-
if (visible) {
|
108
|
-
this.dropdownContainer.classList.remove('hidden');
|
109
|
-
|
110
|
-
this.notifier.dispatch(DropdownEventType.DropdownShown, {
|
111
|
-
dropdown: this,
|
112
|
-
fromToplevelDropdown: this.parent.isToplevel(),
|
113
|
-
});
|
114
|
-
|
115
|
-
this.repositionDropdown();
|
116
|
-
} else {
|
117
|
-
this.notifier.dispatch(DropdownEventType.DropdownHidden, {
|
118
|
-
dropdown: this,
|
119
|
-
fromToplevelDropdown: this.parent.isToplevel(),
|
120
|
-
});
|
121
|
-
|
122
|
-
this.dropdownContainer.classList.add('hiding');
|
123
|
-
|
124
|
-
// Hide the dropdown *slightly* before the animation finishes. This
|
125
|
-
// prevents flickering in some browsers.
|
126
|
-
const hideDelay = animationDuration * 0.95;
|
127
|
-
|
128
|
-
this.hideDropdownTimeout = setTimeout(() => {
|
129
|
-
this.dropdownContainer.classList.add('hidden');
|
130
|
-
this.dropdownContainer.classList.remove('hiding');
|
131
|
-
this.repositionDropdown();
|
132
|
-
}, hideDelay);
|
133
|
-
}
|
134
|
-
|
135
|
-
// Animate
|
136
|
-
const animationName = `var(--dropdown-${
|
137
|
-
visible ? 'show' : 'hide'
|
138
|
-
}-animation)`;
|
139
|
-
this.dropdownContainer.style.animation = `${animationDuration}ms ease ${animationName}`;
|
140
|
-
}
|
141
|
-
|
142
|
-
public requestShow(): void {
|
143
|
-
this.setVisible(true);
|
144
|
-
}
|
145
|
-
|
146
|
-
public requestHide(): void {
|
147
|
-
this.setVisible(false);
|
148
|
-
}
|
149
|
-
|
150
|
-
public appendChild(item: HTMLElement): void {
|
151
|
-
this.dropdownContainer.appendChild(item);
|
152
|
-
}
|
153
|
-
|
154
|
-
public clearChildren(): void {
|
155
|
-
this.dropdownContainer.replaceChildren();
|
156
|
-
}
|
157
|
-
|
158
|
-
public destroy(): void {
|
159
|
-
this.setVisible(false);
|
160
|
-
this.dropdownContainer.remove();
|
161
|
-
this.dropdownToggleListener?.remove();
|
162
|
-
|
163
|
-
// Allow children to be added to other parents
|
164
|
-
this.clearChildren();
|
165
|
-
this.onDestroy();
|
166
|
-
}
|
167
|
-
}
|
168
|
-
|
169
|
-
export default class DropdownLayoutManager implements WidgetContentLayoutManager {
|
170
|
-
private notifier: NotifierType;
|
171
|
-
private dropdowns: Set<Dropdown> = new Set();
|
172
|
-
|
173
|
-
public constructor(
|
174
|
-
announceForAccessibility: (text: string)=>void,
|
175
|
-
private localization: ToolbarLocalization,
|
176
|
-
) {
|
177
|
-
this.notifier = new EventDispatcher();
|
178
|
-
this.notifier.on(DropdownEventType.DropdownShown, ({ dropdown, fromToplevelDropdown }) => {
|
179
|
-
if (!dropdown) return;
|
180
|
-
|
181
|
-
announceForAccessibility(
|
182
|
-
this.localization.dropdownShown(dropdown.parent.getTitle())
|
183
|
-
);
|
184
|
-
|
185
|
-
// Share the event with other connected notifiers
|
186
|
-
this.connectedNotifiers.forEach(notifier => {
|
187
|
-
notifier.dispatch(EditorEventType.ToolbarDropdownShown, {
|
188
|
-
kind: EditorEventType.ToolbarDropdownShown,
|
189
|
-
fromToplevelDropdown,
|
190
|
-
layoutManager: this,
|
191
|
-
});
|
192
|
-
});
|
193
|
-
});
|
194
|
-
|
195
|
-
this.notifier.on(DropdownEventType.DropdownHidden, ({ dropdown }) => {
|
196
|
-
if (!dropdown) return;
|
197
|
-
|
198
|
-
announceForAccessibility(
|
199
|
-
this.localization.dropdownHidden(dropdown.parent.getTitle())
|
200
|
-
);
|
201
|
-
});
|
202
|
-
}
|
203
|
-
|
204
|
-
private listeners: DispatcherEventListener[] = [];
|
205
|
-
private connectedNotifiers: EditorNotifier[] = [];
|
206
|
-
public connectToEditorNotifier(notifier: EditorNotifier) {
|
207
|
-
this.connectedNotifiers.push(notifier);
|
208
|
-
this.refreshListeners();
|
209
|
-
}
|
210
|
-
|
211
|
-
/** Creates a dropdown within `parent`. */
|
212
|
-
public createToolMenu(parent: ToolMenuParent): ToolMenu {
|
213
|
-
const dropdown = new Dropdown(
|
214
|
-
parent,
|
215
|
-
this.notifier,
|
216
|
-
() => {
|
217
|
-
this.dropdowns.delete(dropdown);
|
218
|
-
|
219
|
-
this.refreshListeners();
|
220
|
-
}
|
221
|
-
);
|
222
|
-
this.dropdowns.add(dropdown);
|
223
|
-
this.refreshListeners();
|
224
|
-
|
225
|
-
return dropdown;
|
226
|
-
}
|
227
|
-
|
228
|
-
/**
|
229
|
-
* Adds/removes listeners based on whether we have any managed dropdowns.
|
230
|
-
*
|
231
|
-
* We attempt to clean up all resources when `dropdowns.size == 0`, at which
|
232
|
-
* point, an instance of this could be safely garbage collected.
|
233
|
-
*/
|
234
|
-
private refreshListeners() {
|
235
|
-
const clearListeners = () => {
|
236
|
-
// Remove all listeners & resources that won't be garbage collected.
|
237
|
-
this.listeners.forEach(l => l.remove());
|
238
|
-
this.listeners = [];
|
239
|
-
};
|
240
|
-
|
241
|
-
if (this.dropdowns.size === 0) {
|
242
|
-
clearListeners();
|
243
|
-
} else if (this.listeners.length !== this.connectedNotifiers.length) {
|
244
|
-
clearListeners();
|
245
|
-
|
246
|
-
this.listeners = this.connectedNotifiers.map(notifier => {
|
247
|
-
return notifier.on(EditorEventType.ToolbarDropdownShown, (evt) => {
|
248
|
-
if (evt.kind !== EditorEventType.ToolbarDropdownShown
|
249
|
-
|
250
|
-
// Don't forward to ourselves events that we originally triggered.
|
251
|
-
|| evt.layoutManager === this) {
|
252
|
-
return;
|
253
|
-
}
|
254
|
-
|
255
|
-
this.notifier.dispatch(DropdownEventType.DropdownShown, {
|
256
|
-
fromToplevelDropdown: evt.fromToplevelDropdown,
|
257
|
-
});
|
258
|
-
});
|
259
|
-
});
|
260
|
-
}
|
261
|
-
}
|
262
|
-
}
|
@@ -1,71 +0,0 @@
|
|
1
|
-
import { MutableReactiveValue, ReactiveValue } from '../../../util/ReactiveValue';
|
2
|
-
import { ToolbarLocalization } from '../../localization';
|
3
|
-
import { ToolMenu, WidgetContentLayoutManager, ToolMenuParent } from './types';
|
4
|
-
|
5
|
-
export default class EdgeToolbarLayoutManager implements WidgetContentLayoutManager {
|
6
|
-
private visibleWidgetContent: MutableReactiveValue<ToolMenu|null> = ReactiveValue.fromInitialValue(null);
|
7
|
-
|
8
|
-
// @internal
|
9
|
-
public constructor(
|
10
|
-
private setSidebarContent: (...content: HTMLElement[])=>void,
|
11
|
-
private sidebarTitle: MutableReactiveValue<string>,
|
12
|
-
private sidebarVisibility: MutableReactiveValue<boolean>,
|
13
|
-
private announceForAccessibility: (text: string)=>void,
|
14
|
-
private localization: ToolbarLocalization,
|
15
|
-
) {
|
16
|
-
|
17
|
-
}
|
18
|
-
|
19
|
-
/** Creates a dropdown within `parent`. */
|
20
|
-
public createToolMenu(parent: ToolMenuParent): ToolMenu {
|
21
|
-
const contentElem = document.createElement('div');
|
22
|
-
let result: ToolMenu|null = null;
|
23
|
-
|
24
|
-
const visible = ReactiveValue.fromCallback(() => {
|
25
|
-
return this.visibleWidgetContent.get() === result && this.sidebarVisibility.get();
|
26
|
-
}, [ this.visibleWidgetContent, this.sidebarVisibility ]);
|
27
|
-
|
28
|
-
result = {
|
29
|
-
visible,
|
30
|
-
requestShow: () => {
|
31
|
-
this.setSidebarContent(contentElem);
|
32
|
-
this.sidebarTitle.set(parent.getTitle());
|
33
|
-
|
34
|
-
// Set visibleWidgetContent first -- this causes the previously visible (if any)
|
35
|
-
// item to not be sent a shown event.
|
36
|
-
this.visibleWidgetContent.set(result);
|
37
|
-
this.sidebarVisibility.set(true);
|
38
|
-
|
39
|
-
this.announceForAccessibility(this.localization.dropdownShown(parent.getTitle()));
|
40
|
-
},
|
41
|
-
onActivated: () => {
|
42
|
-
// TODO: Only request show when in sidebar mode
|
43
|
-
//result?.requestShow();
|
44
|
-
},
|
45
|
-
requestHide: () => {
|
46
|
-
if (visible.get()) {
|
47
|
-
this.sidebarVisibility.set(false);
|
48
|
-
}
|
49
|
-
},
|
50
|
-
appendChild: (item: HTMLElement) => {
|
51
|
-
contentElem.appendChild(item);
|
52
|
-
},
|
53
|
-
clearChildren: () => {
|
54
|
-
contentElem.replaceChildren();
|
55
|
-
},
|
56
|
-
destroy: () => {
|
57
|
-
result?.requestHide();
|
58
|
-
|
59
|
-
if (contentElem.parentElement) {
|
60
|
-
contentElem.remove();
|
61
|
-
}
|
62
|
-
|
63
|
-
if (this.visibleWidgetContent.get() === result) {
|
64
|
-
this.visibleWidgetContent.set(null);
|
65
|
-
}
|
66
|
-
},
|
67
|
-
};
|
68
|
-
|
69
|
-
return result;
|
70
|
-
}
|
71
|
-
}
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import ReactiveValue from 'js-draw/src/util/ReactiveValue';
|
2
|
-
|
3
|
-
/**
|
4
|
-
* A class that manages whether/what content is shown for a widget.
|
5
|
-
*
|
6
|
-
* This might be a dropdown menu or a sidebar.
|
7
|
-
*
|
8
|
-
* TODO: Shouldn't be an interface, unless always internal.
|
9
|
-
* @internal
|
10
|
-
*/
|
11
|
-
export interface ToolMenu {
|
12
|
-
/**
|
13
|
-
* Request that the layout manager show the dropdown. In general,
|
14
|
-
* this makes the content of the dropdown visible.
|
15
|
-
*/
|
16
|
-
requestShow(): void;
|
17
|
-
|
18
|
-
/**
|
19
|
-
* Request that the layout manager hide the dropdown. Even after calling this,
|
20
|
-
* the dropdown may still be visible.
|
21
|
-
*/
|
22
|
-
requestHide(): void;
|
23
|
-
|
24
|
-
/** Whether the dropdown is visible (not hidden). */
|
25
|
-
readonly visible: ReactiveValue<boolean>;
|
26
|
-
|
27
|
-
/** Note that the tool associated with this dropdown has been activated. */
|
28
|
-
onActivated(): void;
|
29
|
-
|
30
|
-
/** Adds the given `child` to the content of the dropdown. */
|
31
|
-
appendChild(child: HTMLElement): void;
|
32
|
-
|
33
|
-
/** Removes all children from this dropdown. */
|
34
|
-
clearChildren(): void;
|
35
|
-
|
36
|
-
/**
|
37
|
-
* Destroy the dropdown and remove it from the document. This should be called when
|
38
|
-
* the creator of the dropdown is destroyed.
|
39
|
-
*/
|
40
|
-
destroy(): void;
|
41
|
-
}
|
42
|
-
|
43
|
-
/**
|
44
|
-
* Provides information about the element a tool menu is attached to.
|
45
|
-
*/
|
46
|
-
export interface ToolMenuParent {
|
47
|
-
/** The dropdown may be added **after** this element. */
|
48
|
-
target: HTMLElement;
|
49
|
-
|
50
|
-
/**
|
51
|
-
* @returns the title of the element the dropdown is associated with.
|
52
|
-
*
|
53
|
-
* This is used for accessibility announcements (and possibly to display
|
54
|
-
* a heading).
|
55
|
-
*/
|
56
|
-
getTitle(): string;
|
57
|
-
|
58
|
-
/**
|
59
|
-
* Returns true iff the parent is a toplevel element (not contained within
|
60
|
-
* a ContentLayoutManager of the same type as the current).
|
61
|
-
*/
|
62
|
-
isToplevel(): boolean;
|
63
|
-
}
|
64
|
-
|
65
|
-
export interface WidgetContentLayoutManager {
|
66
|
-
/**
|
67
|
-
* Creates a tool menu (e.g. a dropdown). The dropdown *may* be added to `parent` or addded
|
68
|
-
* elsewhere (this depends on the layout manager).
|
69
|
-
*
|
70
|
-
* Regardless, `parent` should be a place where an absolutely-positioned dropdown
|
71
|
-
* element could be added.
|
72
|
-
*/
|
73
|
-
createToolMenu(parent: ToolMenuParent): ToolMenu;
|
74
|
-
}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
|
2
|
-
export { default as ActionButtonWidget } from './ActionButtonWidget';
|
3
|
-
export { default as BaseToolWidget } from './BaseToolWidget';
|
4
|
-
export { default as BaseWidget, ToolbarWidgetTag } from './BaseWidget';
|
5
|
-
|
6
|
-
export { default as PenToolWidget } from './PenToolWidget';
|
7
|
-
export { default as TextToolWidget } from './TextToolWidget';
|
8
|
-
export { default as HandToolWidget } from './HandToolWidget';
|
9
|
-
export { default as SelectionToolWidget } from './SelectionToolWidget';
|
10
|
-
export { default as EraserToolWidget } from './EraserToolWidget';
|
11
|
-
|
12
|
-
export { default as InsertImageWidget } from './InsertImageWidget';
|
13
|
-
export { default as DocumentPropertiesWidget } from './DocumentPropertiesWidget';
|