js-draw 1.1.0 → 1.2.1
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/dist/Editor.css +6 -4
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +4 -3
- package/dist/cjs/lib.d.ts +15 -8
- package/dist/cjs/lib.js +13 -6
- package/dist/cjs/toolbar/DropdownToolbar.d.ts +24 -0
- package/dist/cjs/toolbar/DropdownToolbar.js +24 -0
- package/dist/cjs/toolbar/EdgeToolbar.d.ts +28 -0
- package/dist/cjs/toolbar/EdgeToolbar.js +30 -2
- package/dist/cjs/util/ReactiveValue.js +1 -1
- package/dist/cjs/util/adjustEditorThemeForContrast.d.ts +55 -0
- package/dist/cjs/util/adjustEditorThemeForContrast.js +128 -0
- package/dist/cjs/util/adjustEditorThemeForContrast.test.d.ts +1 -0
- package/dist/cjs/util/lib.d.ts +1 -0
- package/dist/cjs/util/lib.js +8 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +4 -3
- package/dist/mjs/lib.d.ts +15 -8
- package/dist/mjs/lib.mjs +15 -8
- package/dist/mjs/toolbar/DropdownToolbar.d.ts +24 -0
- package/dist/mjs/toolbar/DropdownToolbar.mjs +24 -0
- package/dist/mjs/toolbar/EdgeToolbar.d.ts +28 -0
- package/dist/mjs/toolbar/EdgeToolbar.mjs +30 -2
- package/dist/mjs/util/ReactiveValue.mjs +1 -1
- package/dist/mjs/util/adjustEditorThemeForContrast.d.ts +55 -0
- package/dist/mjs/util/adjustEditorThemeForContrast.mjs +126 -0
- package/dist/mjs/util/adjustEditorThemeForContrast.test.d.ts +1 -0
- package/dist/mjs/util/lib.d.ts +1 -0
- package/dist/mjs/util/lib.mjs +1 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +3 -3
- package/src/toolbar/EdgeToolbar.scss +6 -4
@@ -0,0 +1,128 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const math_1 = require("@js-draw/math");
|
4
|
+
/**
|
5
|
+
* Adjusts the current editor theme such that colors have appropriate contrast.
|
6
|
+
*
|
7
|
+
* As this method overrides CSS variables using the `.style` property,
|
8
|
+
* **assumes** all original theme variables are set using CSS and not the `.style` property.
|
9
|
+
*
|
10
|
+
* If the editor changes theme in response to the system theme, this method should be
|
11
|
+
* called whenever the system theme changes (e.g. by using [the `matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
12
|
+
* method).
|
13
|
+
*
|
14
|
+
* @example
|
15
|
+
* ```ts,runnable
|
16
|
+
* import { Editor, adjustEditorThemeForContrast } from 'js-draw';
|
17
|
+
*
|
18
|
+
* const editor = new Editor(document.body);
|
19
|
+
* editor.addToolbar();
|
20
|
+
*
|
21
|
+
* const css = `
|
22
|
+
* :root .imageEditorContainer {
|
23
|
+
* --background-color-1: #ffff77;
|
24
|
+
* --foreground-color-1: #fff;
|
25
|
+
* --background-color-2: #ffff99;
|
26
|
+
* --foreground-color-2: #ffff88;
|
27
|
+
* --background-color-3: #ddffff;
|
28
|
+
* --foreground-color-3: #eeffff;
|
29
|
+
* --selection-background-color: #9f7;
|
30
|
+
* --selection-foreground-color: #98f;
|
31
|
+
* }
|
32
|
+
*
|
33
|
+
* @media screen and (prefers-color-scheme: dark) {
|
34
|
+
* :root .imageEditorContainer {
|
35
|
+
* --background-color-1: black;
|
36
|
+
* }
|
37
|
+
* }
|
38
|
+
* `;
|
39
|
+
* editor.addStyleSheet(css);
|
40
|
+
*
|
41
|
+
* adjustEditorThemeForContrast(editor);
|
42
|
+
*
|
43
|
+
* // Because adjustEditorThemeForContrast overrides the current theme, it should be called again
|
44
|
+
* // to allow the editor to switch between light/dark themes.
|
45
|
+
* window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
46
|
+
* adjustEditorThemeForContrast(editor);
|
47
|
+
* });
|
48
|
+
*
|
49
|
+
* window.matchMedia('print').addEventListener('change', () => {
|
50
|
+
* adjustEditorThemeForContrast(editor);
|
51
|
+
* });
|
52
|
+
* ```
|
53
|
+
*/
|
54
|
+
const adjustEditorThemeForContrast = (editor, options) => {
|
55
|
+
const editorElem = editor.getRootElement();
|
56
|
+
// Each set of entries in colorPairs should resolve to colors with sufficient
|
57
|
+
// contrast.
|
58
|
+
const colorPairs = [
|
59
|
+
['--background-color-1', '--foreground-color-1', true, true],
|
60
|
+
['--background-color-2', '--foreground-color-2', true, true],
|
61
|
+
['--background-color-3', '--foreground-color-3', true, true],
|
62
|
+
['--selection-background-color', '--selection-foreground-color', false, true],
|
63
|
+
];
|
64
|
+
if (!options?.dontClearOverrides) {
|
65
|
+
// Clear any overrides
|
66
|
+
for (const [backgroundVar, foregroundVar] of colorPairs) {
|
67
|
+
editorElem.style.setProperty(backgroundVar, null);
|
68
|
+
editorElem.style.setProperty(foregroundVar, null);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
const styles = getComputedStyle(editorElem);
|
72
|
+
const updatedColors = Object.create(null);
|
73
|
+
const adjustVariablesForContrast = (var1, var2, minContrast,
|
74
|
+
// true if the variable can be updated
|
75
|
+
updateVar1, updateVar2) => {
|
76
|
+
// Fetch from updatedColors if available -- styles isn't updated dynamically.
|
77
|
+
let color1 = updatedColors[var1] ? updatedColors[var1] : math_1.Color4.fromString(styles.getPropertyValue(var1));
|
78
|
+
let color2 = updatedColors[var2] ? updatedColors[var2] : math_1.Color4.fromString(styles.getPropertyValue(var2));
|
79
|
+
// Ensure that color1 has the lesser luminance
|
80
|
+
if (color1.relativeLuminance() < color2.relativeLuminance()) {
|
81
|
+
const tmp = color1;
|
82
|
+
color1 = color2;
|
83
|
+
color2 = tmp;
|
84
|
+
const oldVar2 = var2;
|
85
|
+
var2 = var1;
|
86
|
+
var1 = oldVar2;
|
87
|
+
const oldUpdateVar1 = updateVar1;
|
88
|
+
updateVar1 = updateVar2;
|
89
|
+
updateVar2 = oldUpdateVar1;
|
90
|
+
}
|
91
|
+
let colorsUpdated = false;
|
92
|
+
let currentContrast = math_1.Color4.contrastRatio(color1, color2);
|
93
|
+
let iterations = 0;
|
94
|
+
// Step the brightness of color1 and color2 in different directions while necessary
|
95
|
+
while (currentContrast < minContrast && iterations < 8) {
|
96
|
+
const step = math_1.Vec3.of(0.1, 0.1, 0.1);
|
97
|
+
if (updateVar1) {
|
98
|
+
if (color2.eq(math_1.Color4.white) && !updateVar2) {
|
99
|
+
color2 = math_1.Color4.black;
|
100
|
+
}
|
101
|
+
color1 = math_1.Color4.fromRGBVector(color1.rgb.plus(step));
|
102
|
+
}
|
103
|
+
if (updateVar2) {
|
104
|
+
if (color2.eq(math_1.Color4.black) && !updateVar1) {
|
105
|
+
color2 = math_1.Color4.white;
|
106
|
+
}
|
107
|
+
color2 = math_1.Color4.fromRGBVector(color2.rgb.minus(step));
|
108
|
+
}
|
109
|
+
currentContrast = math_1.Color4.contrastRatio(color1, color2);
|
110
|
+
colorsUpdated = true;
|
111
|
+
iterations++;
|
112
|
+
}
|
113
|
+
// Update the CSS variables if necessary
|
114
|
+
if (colorsUpdated) {
|
115
|
+
editorElem.style.setProperty(var1, color1.toHexString());
|
116
|
+
editorElem.style.setProperty(var2, color2.toHexString());
|
117
|
+
updatedColors[var1] = color1;
|
118
|
+
updatedColors[var2] = color2;
|
119
|
+
}
|
120
|
+
};
|
121
|
+
// Also adjust the selection background
|
122
|
+
adjustVariablesForContrast('--selection-background-color', '--background-color-2', 1.29, true, false);
|
123
|
+
for (const [backgroundVar, foregroundVar, updateBackground, updateForeground] of colorPairs) {
|
124
|
+
const minContrast = 4.5;
|
125
|
+
adjustVariablesForContrast(backgroundVar, foregroundVar, minContrast, updateBackground, updateForeground);
|
126
|
+
}
|
127
|
+
};
|
128
|
+
exports.default = adjustEditorThemeForContrast;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as adjustEditorThemeForContrast } from './adjustEditorThemeForContrast';
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.adjustEditorThemeForContrast = void 0;
|
7
|
+
var adjustEditorThemeForContrast_1 = require("./adjustEditorThemeForContrast");
|
8
|
+
Object.defineProperty(exports, "adjustEditorThemeForContrast", { enumerable: true, get: function () { return __importDefault(adjustEditorThemeForContrast_1).default; } });
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.d.ts
CHANGED
@@ -16,6 +16,7 @@ import KeyBinding from './shortcuts/KeyBinding';
|
|
16
16
|
import AbstractToolbar from './toolbar/AbstractToolbar';
|
17
17
|
import RenderablePathSpec from './rendering/RenderablePathSpec';
|
18
18
|
import { AboutDialogEntry } from './dialogs/makeAboutDialog';
|
19
|
+
/** Provides settings to an instance of an editor. See the Editor {@link Editor.constructor}. */
|
19
20
|
export interface EditorSettings {
|
20
21
|
/** Defaults to `RenderingMode.CanvasRenderer` */
|
21
22
|
renderingMode: RenderingMode;
|
@@ -27,9 +28,9 @@ export interface EditorSettings {
|
|
27
28
|
* Defaults to true.
|
28
29
|
*/
|
29
30
|
wheelEventsEnabled: boolean | 'only-if-focused';
|
30
|
-
/** Minimum zoom fraction (e.g. 0.5 → 50% zoom). */
|
31
|
+
/** Minimum zoom fraction (e.g. 0.5 → 50% zoom). Defaults to $2 \cdot 10^{-10}$. */
|
31
32
|
minZoom: number;
|
32
|
-
/** Maximum zoom fraction (e.g. 2 → 200% zoom). */
|
33
|
+
/** Maximum zoom fraction (e.g. 2 → 200% zoom). Defaults to $1 \cdot 10^{12}$. */
|
33
34
|
maxZoom: number;
|
34
35
|
/**
|
35
36
|
* Overrides for keyboard shortcuts. For example,
|
@@ -48,7 +49,7 @@ export interface EditorSettings {
|
|
48
49
|
* See, for example, the `@js-draw/material-icons` package.
|
49
50
|
*
|
50
51
|
* @example
|
51
|
-
* ```ts
|
52
|
+
* ```ts,runnable
|
52
53
|
* import * as jsdraw from 'js-draw';
|
53
54
|
* import MaterialIconProvider from '@js-draw/material-icons';
|
54
55
|
* import 'js-draw/styles';
|
package/dist/mjs/lib.d.ts
CHANGED
@@ -6,13 +6,18 @@
|
|
6
6
|
* ```ts,runnable
|
7
7
|
* import { Editor, Vec3, Mat33, ToolbarWidgetTag } from 'js-draw';
|
8
8
|
*
|
9
|
+
* // Use the Material Icon pack.
|
10
|
+
* import { MaterialIconProvider } from '@js-draw/material-icons';
|
11
|
+
*
|
9
12
|
* // Apply js-draw CSS
|
10
13
|
* import 'js-draw/styles';
|
11
14
|
* // If your bundler doesn't support the above, try
|
12
15
|
* // import 'js-draw/bundledStyles';
|
13
16
|
*
|
14
17
|
* (async () => {
|
15
|
-
* const editor = new Editor(document.body
|
18
|
+
* const editor = new Editor(document.body, {
|
19
|
+
* iconProvider: new MaterialIconProvider(),
|
20
|
+
* });
|
16
21
|
* const toolbar = editor.addToolbar();
|
17
22
|
*
|
18
23
|
* // Increases the minimum height of the editor
|
@@ -22,8 +27,8 @@
|
|
22
27
|
* await editor.loadFromSVG(`
|
23
28
|
* <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
24
29
|
* <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
|
25
|
-
* <path d="M500,500L500,0L0,0L0,500L500,500" fill="#
|
26
|
-
* <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill:
|
30
|
+
* <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background"></path>
|
31
|
+
* <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;">Testing...</text>
|
27
32
|
* </svg>
|
28
33
|
* `);
|
29
34
|
*
|
@@ -45,9 +50,10 @@
|
|
45
50
|
* ```
|
46
51
|
*
|
47
52
|
* @see
|
48
|
-
* {@link Editor}
|
49
|
-
* {@link Editor.loadFromSVG}
|
50
|
-
* {@link AbstractToolbar.addActionButton }
|
53
|
+
* - {@link Editor}
|
54
|
+
* - {@link Editor.loadFromSVG}
|
55
|
+
* - {@link AbstractToolbar.addActionButton }
|
56
|
+
* - {@link EditorSettings}
|
51
57
|
*
|
52
58
|
* @packageDocumentation
|
53
59
|
*/
|
@@ -70,13 +76,14 @@ export * from './shortcuts/lib';
|
|
70
76
|
export { default as EventDispatcher } from './EventDispatcher';
|
71
77
|
export { default as Pointer, PointerDevice } from './Pointer';
|
72
78
|
export { default as UndoRedoHistory } from './UndoRedoHistory';
|
79
|
+
export * from './util/lib';
|
73
80
|
export { default as __js_draw__version } from './version';
|
74
81
|
import AbstractToolbar from './toolbar/AbstractToolbar';
|
75
|
-
export { Editor, EditorSettings, AbstractToolbar,
|
82
|
+
export { Editor, EditorSettings, AbstractToolbar, };
|
76
83
|
/**
|
77
84
|
* Using the HTMLToolbar alias is deprecated. Use
|
78
85
|
* `AbstractToolbar` instead.
|
79
86
|
* @deprecated
|
80
87
|
*/
|
81
|
-
AbstractToolbar as HTMLToolbar
|
88
|
+
export { AbstractToolbar as HTMLToolbar };
|
82
89
|
export default Editor;
|
package/dist/mjs/lib.mjs
CHANGED
@@ -6,13 +6,18 @@
|
|
6
6
|
* ```ts,runnable
|
7
7
|
* import { Editor, Vec3, Mat33, ToolbarWidgetTag } from 'js-draw';
|
8
8
|
*
|
9
|
+
* // Use the Material Icon pack.
|
10
|
+
* import { MaterialIconProvider } from '@js-draw/material-icons';
|
11
|
+
*
|
9
12
|
* // Apply js-draw CSS
|
10
13
|
* import 'js-draw/styles';
|
11
14
|
* // If your bundler doesn't support the above, try
|
12
15
|
* // import 'js-draw/bundledStyles';
|
13
16
|
*
|
14
17
|
* (async () => {
|
15
|
-
* const editor = new Editor(document.body
|
18
|
+
* const editor = new Editor(document.body, {
|
19
|
+
* iconProvider: new MaterialIconProvider(),
|
20
|
+
* });
|
16
21
|
* const toolbar = editor.addToolbar();
|
17
22
|
*
|
18
23
|
* // Increases the minimum height of the editor
|
@@ -22,8 +27,8 @@
|
|
22
27
|
* await editor.loadFromSVG(`
|
23
28
|
* <svg viewBox="0 0 500 500" width="500" height="500" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
24
29
|
* <style id="js-draw-style-sheet">path{stroke-linecap:round;stroke-linejoin:round;}text{white-space:pre;}</style>
|
25
|
-
* <path d="M500,500L500,0L0,0L0,500L500,500" fill="#
|
26
|
-
* <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill:
|
30
|
+
* <path d="M500,500L500,0L0,0L0,500L500,500" fill="#aaa" class="js-draw-image-background"></path>
|
31
|
+
* <text style="transform: matrix(1, 0, 0, 1, 57, 192); font-family: serif; font-size: 32px; fill: #111;">Testing...</text>
|
27
32
|
* </svg>
|
28
33
|
* `);
|
29
34
|
*
|
@@ -45,9 +50,10 @@
|
|
45
50
|
* ```
|
46
51
|
*
|
47
52
|
* @see
|
48
|
-
* {@link Editor}
|
49
|
-
* {@link Editor.loadFromSVG}
|
50
|
-
* {@link AbstractToolbar.addActionButton }
|
53
|
+
* - {@link Editor}
|
54
|
+
* - {@link Editor.loadFromSVG}
|
55
|
+
* - {@link AbstractToolbar.addActionButton }
|
56
|
+
* - {@link EditorSettings}
|
51
57
|
*
|
52
58
|
* @packageDocumentation
|
53
59
|
*/
|
@@ -70,14 +76,15 @@ export * from './shortcuts/lib.mjs';
|
|
70
76
|
export { default as EventDispatcher } from './EventDispatcher.mjs';
|
71
77
|
export { default as Pointer, PointerDevice } from './Pointer.mjs';
|
72
78
|
export { default as UndoRedoHistory } from './UndoRedoHistory.mjs';
|
79
|
+
export * from './util/lib.mjs';
|
73
80
|
// @internal
|
74
81
|
export { default as __js_draw__version } from './version.mjs';
|
75
82
|
import AbstractToolbar from './toolbar/AbstractToolbar.mjs';
|
76
|
-
export { Editor, AbstractToolbar,
|
83
|
+
export { Editor, AbstractToolbar, };
|
77
84
|
/**
|
78
85
|
* Using the HTMLToolbar alias is deprecated. Use
|
79
86
|
* `AbstractToolbar` instead.
|
80
87
|
* @deprecated
|
81
88
|
*/
|
82
|
-
AbstractToolbar as HTMLToolbar
|
89
|
+
export { AbstractToolbar as HTMLToolbar };
|
83
90
|
export default Editor;
|
@@ -2,6 +2,30 @@ import Editor from '../Editor';
|
|
2
2
|
import { ToolbarLocalization } from './localization';
|
3
3
|
import BaseWidget from './widgets/BaseWidget';
|
4
4
|
import AbstractToolbar, { SpacerOptions } from './AbstractToolbar';
|
5
|
+
/**
|
6
|
+
* @example
|
7
|
+
*
|
8
|
+
* ```ts,runnable
|
9
|
+
* import { makeDropdownToolbar, Editor } from 'js-draw';
|
10
|
+
*
|
11
|
+
* const editor = new Editor(document.body);
|
12
|
+
* const toolbar = makeDropdownToolbar(editor);
|
13
|
+
* toolbar.addDefaults();
|
14
|
+
*
|
15
|
+
* toolbar.addExitButton(editor => {
|
16
|
+
* // TODO
|
17
|
+
* });
|
18
|
+
*
|
19
|
+
* toolbar.addSaveButton(editor => {
|
20
|
+
* // TODO
|
21
|
+
* });
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* @see
|
25
|
+
* - {@link makeEdgeToolbar}
|
26
|
+
* - {@link AbstractToolbar.addSaveButton}
|
27
|
+
* - {@link AbstractToolbar.addExitButton}
|
28
|
+
*/
|
5
29
|
export declare const makeDropdownToolbar: (editor: Editor) => AbstractToolbar;
|
6
30
|
export default class DropdownToolbar extends AbstractToolbar {
|
7
31
|
protected container: HTMLElement;
|
@@ -2,6 +2,30 @@ import { defaultToolbarLocalization } from './localization.mjs';
|
|
2
2
|
import OverflowWidget from './widgets/OverflowWidget.mjs';
|
3
3
|
import AbstractToolbar from './AbstractToolbar.mjs';
|
4
4
|
import { toolbarCSSPrefix } from './constants.mjs';
|
5
|
+
/**
|
6
|
+
* @example
|
7
|
+
*
|
8
|
+
* ```ts,runnable
|
9
|
+
* import { makeDropdownToolbar, Editor } from 'js-draw';
|
10
|
+
*
|
11
|
+
* const editor = new Editor(document.body);
|
12
|
+
* const toolbar = makeDropdownToolbar(editor);
|
13
|
+
* toolbar.addDefaults();
|
14
|
+
*
|
15
|
+
* toolbar.addExitButton(editor => {
|
16
|
+
* // TODO
|
17
|
+
* });
|
18
|
+
*
|
19
|
+
* toolbar.addSaveButton(editor => {
|
20
|
+
* // TODO
|
21
|
+
* });
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* @see
|
25
|
+
* - {@link makeEdgeToolbar}
|
26
|
+
* - {@link AbstractToolbar.addSaveButton}
|
27
|
+
* - {@link AbstractToolbar.addExitButton}
|
28
|
+
*/
|
5
29
|
export const makeDropdownToolbar = (editor) => {
|
6
30
|
return new DropdownToolbar(editor, editor.getRootElement());
|
7
31
|
};
|
@@ -2,6 +2,34 @@ import Editor from '../Editor';
|
|
2
2
|
import { ToolbarLocalization } from './localization';
|
3
3
|
import BaseWidget from './widgets/BaseWidget';
|
4
4
|
import AbstractToolbar, { SpacerOptions } from './AbstractToolbar';
|
5
|
+
/**
|
6
|
+
* Creates an `EdgeToolbar`.
|
7
|
+
*
|
8
|
+
* [Credit for the original design of this UI](https://www.figma.com/file/NA5F2AMWO3wUuaoDfUaAb8/Material-3-wireframes?type=design&node-id=54490%3A1103&mode=design&t=Ee0UwnPnQ2bNC2uM-1).
|
9
|
+
*
|
10
|
+
* @example
|
11
|
+
*
|
12
|
+
* ```ts,runnable
|
13
|
+
* import { makeEdgeToolbar, Editor } from 'js-draw';
|
14
|
+
*
|
15
|
+
* const editor = new Editor(document.body);
|
16
|
+
* const toolbar = makeEdgeToolbar(editor);
|
17
|
+
* toolbar.addDefaults();
|
18
|
+
*
|
19
|
+
* toolbar.addSaveButton(editor => {
|
20
|
+
* // TODO
|
21
|
+
* });
|
22
|
+
*
|
23
|
+
* toolbar.addExitButton(editor => {
|
24
|
+
* // TODO
|
25
|
+
* });
|
26
|
+
* ```
|
27
|
+
*
|
28
|
+
* @see
|
29
|
+
* - {@link makeDropdownToolbar}
|
30
|
+
* - {@link AbstractToolbar.addSaveButton}
|
31
|
+
* - {@link AbstractToolbar.addExitButton}
|
32
|
+
*/
|
5
33
|
export declare const makeEdgeToolbar: (editor: Editor) => AbstractToolbar;
|
6
34
|
export default class EdgeToolbar extends AbstractToolbar {
|
7
35
|
private toolbarContainer;
|
@@ -4,6 +4,34 @@ import EdgeToolbarLayoutManager from './widgets/layout/EdgeToolbarLayoutManage
|
|
4
4
|
import { MutableReactiveValue, ReactiveValue } from '../util/ReactiveValue.mjs';
|
5
5
|
import AbstractToolbar from './AbstractToolbar.mjs';
|
6
6
|
import stopPropagationOfScrollingWheelEvents from '../util/stopPropagationOfScrollingWheelEvents.mjs';
|
7
|
+
/**
|
8
|
+
* Creates an `EdgeToolbar`.
|
9
|
+
*
|
10
|
+
* [Credit for the original design of this UI](https://www.figma.com/file/NA5F2AMWO3wUuaoDfUaAb8/Material-3-wireframes?type=design&node-id=54490%3A1103&mode=design&t=Ee0UwnPnQ2bNC2uM-1).
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
*
|
14
|
+
* ```ts,runnable
|
15
|
+
* import { makeEdgeToolbar, Editor } from 'js-draw';
|
16
|
+
*
|
17
|
+
* const editor = new Editor(document.body);
|
18
|
+
* const toolbar = makeEdgeToolbar(editor);
|
19
|
+
* toolbar.addDefaults();
|
20
|
+
*
|
21
|
+
* toolbar.addSaveButton(editor => {
|
22
|
+
* // TODO
|
23
|
+
* });
|
24
|
+
*
|
25
|
+
* toolbar.addExitButton(editor => {
|
26
|
+
* // TODO
|
27
|
+
* });
|
28
|
+
* ```
|
29
|
+
*
|
30
|
+
* @see
|
31
|
+
* - {@link makeDropdownToolbar}
|
32
|
+
* - {@link AbstractToolbar.addSaveButton}
|
33
|
+
* - {@link AbstractToolbar.addExitButton}
|
34
|
+
*/
|
7
35
|
export const makeEdgeToolbar = (editor) => {
|
8
36
|
return new EdgeToolbar(editor, editor.getRootElement(), editor.localization);
|
9
37
|
};
|
@@ -45,12 +73,12 @@ export default class EdgeToolbar extends AbstractToolbar {
|
|
45
73
|
this.sidebarY.onUpdateAndNow(y => {
|
46
74
|
const belowEdgeClassName = 'dropdown-below-edge';
|
47
75
|
if (y > 0) {
|
48
|
-
this.sidebarContainer.style.
|
76
|
+
this.sidebarContainer.style.transform = `translate(0, ${y}px)`;
|
49
77
|
this.sidebarContainer.style.paddingBottom = '';
|
50
78
|
this.menuContainer.classList.add(belowEdgeClassName);
|
51
79
|
}
|
52
80
|
else {
|
53
|
-
this.sidebarContainer.style.
|
81
|
+
this.sidebarContainer.style.transform = '';
|
54
82
|
this.sidebarContainer.style.paddingBottom = `${-y}px`;
|
55
83
|
this.menuContainer.classList.remove(belowEdgeClassName);
|
56
84
|
}
|
@@ -94,7 +94,7 @@ export class ReactiveValue {
|
|
94
94
|
export class MutableReactiveValue extends ReactiveValue {
|
95
95
|
static fromProperty(sourceValue, propertyName) {
|
96
96
|
const child = ReactiveValue.fromInitialValue(sourceValue.get()[propertyName]);
|
97
|
-
const childRef = new WeakRef(child);
|
97
|
+
const childRef = window.WeakRef ? new WeakRef(child) : { deref: () => child };
|
98
98
|
// When the source is updated...
|
99
99
|
const sourceListener = sourceValue.onUpdate(newValue => {
|
100
100
|
const childValue = childRef.deref();
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import Editor from '../Editor';
|
2
|
+
/**
|
3
|
+
* Adjusts the current editor theme such that colors have appropriate contrast.
|
4
|
+
*
|
5
|
+
* As this method overrides CSS variables using the `.style` property,
|
6
|
+
* **assumes** all original theme variables are set using CSS and not the `.style` property.
|
7
|
+
*
|
8
|
+
* If the editor changes theme in response to the system theme, this method should be
|
9
|
+
* called whenever the system theme changes (e.g. by using [the `matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
10
|
+
* method).
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts,runnable
|
14
|
+
* import { Editor, adjustEditorThemeForContrast } from 'js-draw';
|
15
|
+
*
|
16
|
+
* const editor = new Editor(document.body);
|
17
|
+
* editor.addToolbar();
|
18
|
+
*
|
19
|
+
* const css = `
|
20
|
+
* :root .imageEditorContainer {
|
21
|
+
* --background-color-1: #ffff77;
|
22
|
+
* --foreground-color-1: #fff;
|
23
|
+
* --background-color-2: #ffff99;
|
24
|
+
* --foreground-color-2: #ffff88;
|
25
|
+
* --background-color-3: #ddffff;
|
26
|
+
* --foreground-color-3: #eeffff;
|
27
|
+
* --selection-background-color: #9f7;
|
28
|
+
* --selection-foreground-color: #98f;
|
29
|
+
* }
|
30
|
+
*
|
31
|
+
* @media screen and (prefers-color-scheme: dark) {
|
32
|
+
* :root .imageEditorContainer {
|
33
|
+
* --background-color-1: black;
|
34
|
+
* }
|
35
|
+
* }
|
36
|
+
* `;
|
37
|
+
* editor.addStyleSheet(css);
|
38
|
+
*
|
39
|
+
* adjustEditorThemeForContrast(editor);
|
40
|
+
*
|
41
|
+
* // Because adjustEditorThemeForContrast overrides the current theme, it should be called again
|
42
|
+
* // to allow the editor to switch between light/dark themes.
|
43
|
+
* window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
44
|
+
* adjustEditorThemeForContrast(editor);
|
45
|
+
* });
|
46
|
+
*
|
47
|
+
* window.matchMedia('print').addEventListener('change', () => {
|
48
|
+
* adjustEditorThemeForContrast(editor);
|
49
|
+
* });
|
50
|
+
* ```
|
51
|
+
*/
|
52
|
+
declare const adjustEditorThemeForContrast: (editor: Editor, options?: {
|
53
|
+
dontClearOverrides: boolean;
|
54
|
+
}) => void;
|
55
|
+
export default adjustEditorThemeForContrast;
|
@@ -0,0 +1,126 @@
|
|
1
|
+
import { Color4, Vec3 } from '@js-draw/math';
|
2
|
+
/**
|
3
|
+
* Adjusts the current editor theme such that colors have appropriate contrast.
|
4
|
+
*
|
5
|
+
* As this method overrides CSS variables using the `.style` property,
|
6
|
+
* **assumes** all original theme variables are set using CSS and not the `.style` property.
|
7
|
+
*
|
8
|
+
* If the editor changes theme in response to the system theme, this method should be
|
9
|
+
* called whenever the system theme changes (e.g. by using [the `matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia)
|
10
|
+
* method).
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts,runnable
|
14
|
+
* import { Editor, adjustEditorThemeForContrast } from 'js-draw';
|
15
|
+
*
|
16
|
+
* const editor = new Editor(document.body);
|
17
|
+
* editor.addToolbar();
|
18
|
+
*
|
19
|
+
* const css = `
|
20
|
+
* :root .imageEditorContainer {
|
21
|
+
* --background-color-1: #ffff77;
|
22
|
+
* --foreground-color-1: #fff;
|
23
|
+
* --background-color-2: #ffff99;
|
24
|
+
* --foreground-color-2: #ffff88;
|
25
|
+
* --background-color-3: #ddffff;
|
26
|
+
* --foreground-color-3: #eeffff;
|
27
|
+
* --selection-background-color: #9f7;
|
28
|
+
* --selection-foreground-color: #98f;
|
29
|
+
* }
|
30
|
+
*
|
31
|
+
* @media screen and (prefers-color-scheme: dark) {
|
32
|
+
* :root .imageEditorContainer {
|
33
|
+
* --background-color-1: black;
|
34
|
+
* }
|
35
|
+
* }
|
36
|
+
* `;
|
37
|
+
* editor.addStyleSheet(css);
|
38
|
+
*
|
39
|
+
* adjustEditorThemeForContrast(editor);
|
40
|
+
*
|
41
|
+
* // Because adjustEditorThemeForContrast overrides the current theme, it should be called again
|
42
|
+
* // to allow the editor to switch between light/dark themes.
|
43
|
+
* window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
44
|
+
* adjustEditorThemeForContrast(editor);
|
45
|
+
* });
|
46
|
+
*
|
47
|
+
* window.matchMedia('print').addEventListener('change', () => {
|
48
|
+
* adjustEditorThemeForContrast(editor);
|
49
|
+
* });
|
50
|
+
* ```
|
51
|
+
*/
|
52
|
+
const adjustEditorThemeForContrast = (editor, options) => {
|
53
|
+
const editorElem = editor.getRootElement();
|
54
|
+
// Each set of entries in colorPairs should resolve to colors with sufficient
|
55
|
+
// contrast.
|
56
|
+
const colorPairs = [
|
57
|
+
['--background-color-1', '--foreground-color-1', true, true],
|
58
|
+
['--background-color-2', '--foreground-color-2', true, true],
|
59
|
+
['--background-color-3', '--foreground-color-3', true, true],
|
60
|
+
['--selection-background-color', '--selection-foreground-color', false, true],
|
61
|
+
];
|
62
|
+
if (!options?.dontClearOverrides) {
|
63
|
+
// Clear any overrides
|
64
|
+
for (const [backgroundVar, foregroundVar] of colorPairs) {
|
65
|
+
editorElem.style.setProperty(backgroundVar, null);
|
66
|
+
editorElem.style.setProperty(foregroundVar, null);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
const styles = getComputedStyle(editorElem);
|
70
|
+
const updatedColors = Object.create(null);
|
71
|
+
const adjustVariablesForContrast = (var1, var2, minContrast,
|
72
|
+
// true if the variable can be updated
|
73
|
+
updateVar1, updateVar2) => {
|
74
|
+
// Fetch from updatedColors if available -- styles isn't updated dynamically.
|
75
|
+
let color1 = updatedColors[var1] ? updatedColors[var1] : Color4.fromString(styles.getPropertyValue(var1));
|
76
|
+
let color2 = updatedColors[var2] ? updatedColors[var2] : Color4.fromString(styles.getPropertyValue(var2));
|
77
|
+
// Ensure that color1 has the lesser luminance
|
78
|
+
if (color1.relativeLuminance() < color2.relativeLuminance()) {
|
79
|
+
const tmp = color1;
|
80
|
+
color1 = color2;
|
81
|
+
color2 = tmp;
|
82
|
+
const oldVar2 = var2;
|
83
|
+
var2 = var1;
|
84
|
+
var1 = oldVar2;
|
85
|
+
const oldUpdateVar1 = updateVar1;
|
86
|
+
updateVar1 = updateVar2;
|
87
|
+
updateVar2 = oldUpdateVar1;
|
88
|
+
}
|
89
|
+
let colorsUpdated = false;
|
90
|
+
let currentContrast = Color4.contrastRatio(color1, color2);
|
91
|
+
let iterations = 0;
|
92
|
+
// Step the brightness of color1 and color2 in different directions while necessary
|
93
|
+
while (currentContrast < minContrast && iterations < 8) {
|
94
|
+
const step = Vec3.of(0.1, 0.1, 0.1);
|
95
|
+
if (updateVar1) {
|
96
|
+
if (color2.eq(Color4.white) && !updateVar2) {
|
97
|
+
color2 = Color4.black;
|
98
|
+
}
|
99
|
+
color1 = Color4.fromRGBVector(color1.rgb.plus(step));
|
100
|
+
}
|
101
|
+
if (updateVar2) {
|
102
|
+
if (color2.eq(Color4.black) && !updateVar1) {
|
103
|
+
color2 = Color4.white;
|
104
|
+
}
|
105
|
+
color2 = Color4.fromRGBVector(color2.rgb.minus(step));
|
106
|
+
}
|
107
|
+
currentContrast = Color4.contrastRatio(color1, color2);
|
108
|
+
colorsUpdated = true;
|
109
|
+
iterations++;
|
110
|
+
}
|
111
|
+
// Update the CSS variables if necessary
|
112
|
+
if (colorsUpdated) {
|
113
|
+
editorElem.style.setProperty(var1, color1.toHexString());
|
114
|
+
editorElem.style.setProperty(var2, color2.toHexString());
|
115
|
+
updatedColors[var1] = color1;
|
116
|
+
updatedColors[var2] = color2;
|
117
|
+
}
|
118
|
+
};
|
119
|
+
// Also adjust the selection background
|
120
|
+
adjustVariablesForContrast('--selection-background-color', '--background-color-2', 1.29, true, false);
|
121
|
+
for (const [backgroundVar, foregroundVar, updateBackground, updateForeground] of colorPairs) {
|
122
|
+
const minContrast = 4.5;
|
123
|
+
adjustVariablesForContrast(backgroundVar, foregroundVar, minContrast, updateBackground, updateForeground);
|
124
|
+
}
|
125
|
+
};
|
126
|
+
export default adjustEditorThemeForContrast;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as adjustEditorThemeForContrast } from './adjustEditorThemeForContrast';
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as adjustEditorThemeForContrast } from './adjustEditorThemeForContrast.mjs';
|
package/dist/mjs/version.mjs
CHANGED