@readium/navigator-html-injectables 2.2.1 → 2.2.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/dist/index.js +855 -783
- package/dist/index.umd.cjs +12 -11
- package/package.json +3 -3
- package/src/helpers/color.ts +190 -99
- package/src/modules/Decorator.ts +70 -9
- package/types/src/helpers/color.d.ts +16 -6
- package/types/src/modules/Decorator.d.ts +3 -0
package/src/helpers/color.ts
CHANGED
|
@@ -1,114 +1,205 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
// Lazy canvas/offscreen canvas initialization for color conversion
|
|
2
|
+
let canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
|
|
3
|
+
let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null = null;
|
|
4
|
+
|
|
5
|
+
// Track which pixel to use next for color sampling (0-24 for 5x5 grid)
|
|
6
|
+
let currentPixelIndex = 0;
|
|
7
|
+
|
|
8
|
+
// Default color for failed conversions
|
|
9
|
+
const DEFAULT_COLOR = { r: 255, g: 255, b: 255, a: 1 };
|
|
10
|
+
|
|
11
|
+
// Cache for computed color conversions
|
|
12
|
+
const colorCache = new Map<string, { r: number; g: number; b: number; a: number; } | null>();
|
|
13
|
+
|
|
14
|
+
const getCanvasContext = (): CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null => {
|
|
15
|
+
if (!canvas) {
|
|
16
|
+
// Try to use OffscreenCanvas if available
|
|
17
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
18
|
+
canvas = new OffscreenCanvas(5, 5);
|
|
19
|
+
ctx = canvas.getContext("2d", {
|
|
20
|
+
willReadFrequently: true,
|
|
21
|
+
desynchronized: true
|
|
22
|
+
});
|
|
23
|
+
} else {
|
|
24
|
+
// Fall back to regular canvas
|
|
25
|
+
const htmlCanvas = document.createElement("canvas");
|
|
26
|
+
htmlCanvas.width = 5;
|
|
27
|
+
htmlCanvas.height = 5;
|
|
28
|
+
canvas = htmlCanvas;
|
|
29
|
+
ctx = htmlCanvas.getContext("2d", {
|
|
30
|
+
willReadFrequently: true,
|
|
31
|
+
desynchronized: true
|
|
32
|
+
});
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
|
-
return
|
|
35
|
+
return ctx;
|
|
31
36
|
};
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const isSpecialColorValue = (color: string): boolean => {
|
|
39
|
+
if (!color) return true;
|
|
40
|
+
|
|
41
|
+
const normalizedColor = color.trim().toLowerCase();
|
|
42
|
+
|
|
43
|
+
// Check for CSS variables
|
|
44
|
+
if (normalizedColor.startsWith("var(")) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check for CSS color keywords
|
|
49
|
+
const cssKeywords = [
|
|
50
|
+
"transparent",
|
|
51
|
+
"currentcolor",
|
|
52
|
+
"inherit",
|
|
53
|
+
"initial",
|
|
54
|
+
"revert",
|
|
55
|
+
"unset",
|
|
56
|
+
"revert-layer"
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
if (cssKeywords.includes(normalizedColor)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check for gradients
|
|
64
|
+
const gradientTypes = [
|
|
65
|
+
"linear-gradient",
|
|
66
|
+
"radial-gradient",
|
|
67
|
+
"conic-gradient",
|
|
68
|
+
"repeating-linear-gradient",
|
|
69
|
+
"repeating-radial-gradient",
|
|
70
|
+
"repeating-conic-gradient"
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
return gradientTypes.some(grad => normalizedColor.includes(grad));
|
|
41
74
|
};
|
|
42
75
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const rgba2 = colorToRgba(color2);
|
|
48
|
-
const lum1 = getLuminance(rgba1);
|
|
49
|
-
const lum2 = getLuminance(rgba2);
|
|
50
|
-
const brightest = Math.max(lum1, lum2);
|
|
51
|
-
const darkest = Math.min(lum1, lum2);
|
|
52
|
-
return (brightest + 0.05) / (darkest + 0.05);
|
|
76
|
+
const warnAboutInvalidColor = (color: string, reason: string): void => {
|
|
77
|
+
console.warn(
|
|
78
|
+
`[Decorator] Could not parse color: "${color}". ${reason} Falling back to ${JSON.stringify(DEFAULT_COLOR)} to compute contrast. Please use a CSS color value that can be computed to RGB(A).`
|
|
79
|
+
);
|
|
53
80
|
};
|
|
54
81
|
|
|
55
|
-
export const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (contrastRatio >= contrast) {
|
|
65
|
-
return [
|
|
66
|
-
`rgba(${c1.r}, ${c1.g}, ${c1.b}, ${c1.a})`,
|
|
67
|
-
`rgba(${c2.r}, ${c2.g}, ${c2.b}, ${c2.a})`
|
|
68
|
-
];
|
|
82
|
+
export const colorToRgba = (
|
|
83
|
+
color: string,
|
|
84
|
+
backgroundColor: string | null = null
|
|
85
|
+
): { r: number; g: number; b: number; a: number } => {
|
|
86
|
+
// Check cache with background key if provided
|
|
87
|
+
const cacheKey = backgroundColor ? `${color}|${backgroundColor}` : color;
|
|
88
|
+
const cached = colorCache.get(cacheKey);
|
|
89
|
+
if (cached !== undefined) {
|
|
90
|
+
return cached ?? DEFAULT_COLOR;
|
|
69
91
|
}
|
|
70
92
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const delta = ((contrast - contrastRatio) * 255) / (contrastRatio + 0.05);
|
|
79
|
-
let correctedColor: { r: number; g: number; b: number; a: number };
|
|
80
|
-
let otherColor: { r: number; g: number; b: number; a: number };
|
|
81
|
-
if (lum1 < lum2) {
|
|
82
|
-
correctedColor = c1;
|
|
83
|
-
otherColor = c2;
|
|
84
|
-
} else {
|
|
85
|
-
correctedColor = c2;
|
|
86
|
-
otherColor = c1;
|
|
93
|
+
// Check for special color values
|
|
94
|
+
if (isSpecialColorValue(color)) {
|
|
95
|
+
warnAboutInvalidColor(color, "Unsupported color format or special value.");
|
|
96
|
+
colorCache.set(cacheKey, null);
|
|
97
|
+
return DEFAULT_COLOR;
|
|
87
98
|
}
|
|
88
99
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const updatedDelta = ((contrast - newContrastRatio) * 255) / (newContrastRatio + 0.05);
|
|
95
|
-
const otherColorAdjusted = adjustColor(otherColor, updatedDelta);
|
|
96
|
-
return [
|
|
97
|
-
lum1 < lum2
|
|
98
|
-
? `rgba(${correctedColorAdjusted.r}, ${correctedColorAdjusted.g}, ${correctedColorAdjusted.b}, ${correctedColorAdjusted.a})`
|
|
99
|
-
: `rgba(${otherColorAdjusted.r}, ${otherColorAdjusted.g}, ${otherColorAdjusted.b}, ${otherColorAdjusted.a})`,
|
|
100
|
-
lum1 < lum2
|
|
101
|
-
? `rgba(${otherColorAdjusted.r}, ${otherColorAdjusted.g}, ${otherColorAdjusted.b}, ${otherColorAdjusted.a})`
|
|
102
|
-
: `rgba(${correctedColorAdjusted.r}, ${correctedColorAdjusted.g}, ${correctedColorAdjusted.b}, ${correctedColorAdjusted.a})`,
|
|
103
|
-
];
|
|
100
|
+
const context = getCanvasContext();
|
|
101
|
+
if (!context) {
|
|
102
|
+
warnAboutInvalidColor(color, "Could not get canvas context.");
|
|
103
|
+
colorCache.set(cacheKey, null);
|
|
104
|
+
return DEFAULT_COLOR;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
try {
|
|
108
|
+
// Clear and initialize canvas at the start of each cycle
|
|
109
|
+
if (currentPixelIndex === 0) {
|
|
110
|
+
context.clearRect(0, 0, 5, 5);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Calculate which pixel to use for this operation
|
|
114
|
+
const x = currentPixelIndex % 5;
|
|
115
|
+
const y = Math.floor(currentPixelIndex / 5);
|
|
116
|
+
|
|
117
|
+
// Clear just this pixel to ensure clean state
|
|
118
|
+
context.clearRect(x, y, 1, 1);
|
|
119
|
+
|
|
120
|
+
// Fill background color if provided
|
|
121
|
+
if (backgroundColor) {
|
|
122
|
+
context.fillStyle = backgroundColor;
|
|
123
|
+
context.fillRect(x, y, 1, 1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Draw the color at the current pixel
|
|
127
|
+
context.fillStyle = color;
|
|
128
|
+
context.fillRect(x, y, 1, 1);
|
|
129
|
+
|
|
130
|
+
// Get the pixel data for this specific pixel
|
|
131
|
+
const imageData = context.getImageData(x, y, 1, 1);
|
|
132
|
+
|
|
133
|
+
// Move to next pixel for next call
|
|
134
|
+
currentPixelIndex = (currentPixelIndex + 1) % 25;
|
|
135
|
+
|
|
136
|
+
// Get the pixel data for the pixel we just sampled
|
|
137
|
+
const [r, g, b, a] = imageData.data;
|
|
138
|
+
|
|
139
|
+
// If the color is completely transparent, return default
|
|
140
|
+
if (a === 0) {
|
|
141
|
+
warnAboutInvalidColor(color, "Fully transparent color.");
|
|
142
|
+
colorCache.set(cacheKey, null);
|
|
143
|
+
return DEFAULT_COLOR;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const result = { r, g, b, a: a / 255 };
|
|
147
|
+
colorCache.set(cacheKey, result);
|
|
148
|
+
return result;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
warnAboutInvalidColor(color, `Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
151
|
+
colorCache.set(cacheKey, null);
|
|
152
|
+
return DEFAULT_COLOR;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const toLinear = (c: number): number => {
|
|
157
|
+
const normalized = c / 255;
|
|
158
|
+
return normalized <= 0.03928
|
|
159
|
+
? normalized / 12.92
|
|
160
|
+
: Math.pow((normalized + 0.055) / 1.055, 2.4);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const getLuminance = (color: { r: number; g: number; b: number; a?: number }): number => {
|
|
164
|
+
// Convert sRGB to linear RGB and apply WCAG 2.2 formula
|
|
165
|
+
const r = toLinear(color.r);
|
|
166
|
+
const g = toLinear(color.g);
|
|
167
|
+
const b = toLinear(color.b);
|
|
168
|
+
|
|
169
|
+
// WCAG 2.2 relative luminance formula (returns 0-1)
|
|
170
|
+
// Note: Alpha is ignored for contrast calculations. WCAG 2.2 only defines contrast for opaque colors,
|
|
171
|
+
// and semi-transparent colors have a range of possible contrast ratios depending on background.
|
|
172
|
+
// For text readability decisions, we use the base color as the most conservative approach.
|
|
173
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
174
|
+
return luminance;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const checkContrast = (
|
|
178
|
+
color1: string | { r: number; g: number; b: number; a?: number },
|
|
179
|
+
color2: string | { r: number; g: number; b: number; a?: number }
|
|
180
|
+
): number => {
|
|
181
|
+
const rgba1 = typeof color1 === "string" ? colorToRgba(color1) : color1;
|
|
182
|
+
const rgba2 = typeof color2 === "string" ? colorToRgba(color2) : color2;
|
|
183
|
+
|
|
184
|
+
const l1 = getLuminance(rgba1);
|
|
185
|
+
const l2 = getLuminance(rgba2);
|
|
186
|
+
|
|
187
|
+
const lighter = Math.max(l1, l2);
|
|
188
|
+
const darker = Math.min(l1, l2);
|
|
189
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
114
190
|
};
|
|
191
|
+
|
|
192
|
+
export const isDarkColor = (color: string, blendedWith: string | null = null): boolean => {
|
|
193
|
+
const blended = colorToRgba(color, blendedWith);
|
|
194
|
+
const contrastWithWhite = checkContrast(blended, { r: 255, g: 255, b: 255, a: 1 });
|
|
195
|
+
const contrastWithBlack = checkContrast(blended, { r: 0, g: 0, b: 0, a: 1 });
|
|
196
|
+
return contrastWithWhite > contrastWithBlack;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const isLightColor = (color: string, blendedWith: string | null = null): boolean => {
|
|
200
|
+
return !isDarkColor(color, blendedWith);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const getContrastingTextColor = (color: string, blendedWith: string | null = null): "black" | "white" => {
|
|
204
|
+
return isDarkColor(color, blendedWith) ? "white" : "black";
|
|
205
|
+
};
|
package/src/modules/Decorator.ts
CHANGED
|
@@ -6,7 +6,9 @@ import { ModuleName } from "./ModuleLibrary";
|
|
|
6
6
|
import { Rect, getClientRectsNoOverlap } from "../helpers/rect";
|
|
7
7
|
import { getProperty } from "../helpers/css";
|
|
8
8
|
import { ReadiumWindow } from "../helpers/dom";
|
|
9
|
-
import { isDarkColor } from "../helpers/color";
|
|
9
|
+
import { isDarkColor, getContrastingTextColor } from "../helpers/color";
|
|
10
|
+
|
|
11
|
+
const DEFAULT_HIGHLIGHT_COLOR = "#FFFF00"; // Yellow in HEX
|
|
10
12
|
|
|
11
13
|
export enum Width {
|
|
12
14
|
Wrap = "wrap", // Smallest width fitting the CSS border box.
|
|
@@ -103,8 +105,10 @@ class DecorationGroup {
|
|
|
103
105
|
// The common ancestor is an element that definitely cannot be highlighted
|
|
104
106
|
this.notTextFlag?.set(id, true);
|
|
105
107
|
}
|
|
106
|
-
if
|
|
107
|
-
|
|
108
|
+
// Check if the range itself contains elements that cannot be highlighted
|
|
109
|
+
const rangeFragment = range.cloneContents();
|
|
110
|
+
if(rangeFragment.querySelector(cannotNativeHighlight.join(", ").toLowerCase())) {
|
|
111
|
+
// Range contains elements that definitely cannot be highlighted
|
|
108
112
|
this.notTextFlag?.set(id, true);
|
|
109
113
|
}
|
|
110
114
|
if((ancestor.textContent?.trim() || "").length === 0) {
|
|
@@ -180,11 +184,15 @@ class DecorationGroup {
|
|
|
180
184
|
const [stylesheet, highlighter]: [HTMLStyleElement, any] = this.requireContainer(true) as [HTMLStyleElement, unknown];
|
|
181
185
|
highlighter.add(item.range);
|
|
182
186
|
|
|
187
|
+
const backgroundColor = getProperty(this.wnd, "--USER__backgroundColor") ||
|
|
188
|
+
this.wnd.getComputedStyle(this.wnd.document.documentElement).getPropertyValue("background-color");
|
|
189
|
+
const tint = item.decoration?.style?.tint ?? DEFAULT_HIGHLIGHT_COLOR;
|
|
190
|
+
|
|
183
191
|
// TODO add caching layer ("vdom") to this so we aren't completely replacing the CSS every time
|
|
184
192
|
stylesheet.innerHTML = `
|
|
185
193
|
::highlight(${this.id}) {
|
|
186
|
-
color:
|
|
187
|
-
background-color: ${
|
|
194
|
+
color: ${getContrastingTextColor(tint, backgroundColor)};
|
|
195
|
+
background-color: ${tint};
|
|
188
196
|
}`;
|
|
189
197
|
}
|
|
190
198
|
|
|
@@ -251,14 +259,14 @@ class DecorationGroup {
|
|
|
251
259
|
// template.innerHTML = item.decoration.element.trim();
|
|
252
260
|
// TODO more styles logic
|
|
253
261
|
|
|
254
|
-
const isDarkMode =
|
|
255
|
-
isDarkColor(getProperty(this.wnd, "--USER__backgroundColor"));
|
|
262
|
+
const isDarkMode = this.getCurrentDarkMode();
|
|
256
263
|
|
|
257
264
|
template.innerHTML = `
|
|
258
265
|
<div
|
|
259
|
-
|
|
266
|
+
data-readium="true"
|
|
267
|
+
class="readium-highlight"
|
|
260
268
|
style="${[
|
|
261
|
-
`background-color: ${item.decoration?.style?.tint ??
|
|
269
|
+
`background-color: ${item.decoration?.style?.tint ?? DEFAULT_HIGHLIGHT_COLOR} !important`,
|
|
262
270
|
//"opacity: 0.3 !important",
|
|
263
271
|
`mix-blend-mode: ${isDarkMode ? "exclusion" : "multiply"} !important`,
|
|
264
272
|
"opacity: 1 !important",
|
|
@@ -359,6 +367,12 @@ class DecorationGroup {
|
|
|
359
367
|
return this.container;
|
|
360
368
|
}
|
|
361
369
|
|
|
370
|
+
getCurrentDarkMode(): boolean {
|
|
371
|
+
return getProperty(this.wnd, "--USER__appearance") === "readium-night-on" ||
|
|
372
|
+
isDarkColor(getProperty(this.wnd, "--USER__backgroundColor")) ||
|
|
373
|
+
isDarkColor(this.wnd.getComputedStyle(this.wnd.document.documentElement).getPropertyValue("background-color"));
|
|
374
|
+
}
|
|
375
|
+
|
|
362
376
|
/**
|
|
363
377
|
* Removes the group container.
|
|
364
378
|
*/
|
|
@@ -376,6 +390,7 @@ class DecorationGroup {
|
|
|
376
390
|
export class Decorator extends Module {
|
|
377
391
|
static readonly moduleName: ModuleName = "decorator";
|
|
378
392
|
private resizeObserver!: ResizeObserver;
|
|
393
|
+
private backgroundObserver!: MutationObserver;
|
|
379
394
|
private wnd!: ReadiumWindow;
|
|
380
395
|
/*private readonly lastSize = {
|
|
381
396
|
width: 0,
|
|
@@ -392,6 +407,19 @@ export class Decorator extends Module {
|
|
|
392
407
|
this.groups.clear();
|
|
393
408
|
}
|
|
394
409
|
|
|
410
|
+
private updateHighlightStyles() {
|
|
411
|
+
this.groups.forEach(group => {
|
|
412
|
+
group.requestLayout();
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private extractCustomProperty(style: string | null, propertyName: string): string | null {
|
|
417
|
+
if (!style) return null;
|
|
418
|
+
|
|
419
|
+
const match = style.match(new RegExp(`${propertyName}:\\s*([^;]+)`));
|
|
420
|
+
return match ? match[1].trim() : null;
|
|
421
|
+
}
|
|
422
|
+
|
|
395
423
|
private handleResize() {
|
|
396
424
|
this.wnd.clearTimeout(this.resizeFrame);
|
|
397
425
|
this.resizeFrame = this.wnd.setTimeout(() => {
|
|
@@ -442,6 +470,38 @@ export class Decorator extends Module {
|
|
|
442
470
|
wnd.addEventListener("orientationchange", this.handleResizer);
|
|
443
471
|
wnd.addEventListener("resize", this.handleResizer);
|
|
444
472
|
|
|
473
|
+
// Set up MutationObserver to watch for CSS custom property changes
|
|
474
|
+
this.backgroundObserver = new MutationObserver((mutations) => {
|
|
475
|
+
const shouldUpdate = mutations.some(mutation => {
|
|
476
|
+
if (mutation.type === "attributes" && mutation.attributeName === "style") {
|
|
477
|
+
const element = mutation.target as Element;
|
|
478
|
+
const oldStyle = mutation.oldValue;
|
|
479
|
+
const newStyle = element.getAttribute("style");
|
|
480
|
+
|
|
481
|
+
// Check if the relevant CSS custom properties actually changed
|
|
482
|
+
const oldAppearance = this.extractCustomProperty(oldStyle, "--USER__appearance");
|
|
483
|
+
const newAppearance = this.extractCustomProperty(newStyle, "--USER__appearance");
|
|
484
|
+
const oldBgColor = this.extractCustomProperty(oldStyle, "--USER__backgroundColor");
|
|
485
|
+
const newBgColor = this.extractCustomProperty(newStyle, "--USER__backgroundColor");
|
|
486
|
+
|
|
487
|
+
return oldAppearance !== newAppearance ||
|
|
488
|
+
oldBgColor !== newBgColor;
|
|
489
|
+
}
|
|
490
|
+
return false;
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
if (shouldUpdate) {
|
|
494
|
+
this.updateHighlightStyles();
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
this.backgroundObserver.observe(wnd.document.documentElement, {
|
|
499
|
+
attributes: true,
|
|
500
|
+
attributeFilter: ["style"],
|
|
501
|
+
attributeOldValue: true,
|
|
502
|
+
subtree: true
|
|
503
|
+
});
|
|
504
|
+
|
|
445
505
|
comms.log("Decorator Mounted");
|
|
446
506
|
return true;
|
|
447
507
|
}
|
|
@@ -452,6 +512,7 @@ export class Decorator extends Module {
|
|
|
452
512
|
|
|
453
513
|
comms.unregisterAll(Decorator.moduleName);
|
|
454
514
|
this.resizeObserver.disconnect();
|
|
515
|
+
this.backgroundObserver.disconnect();
|
|
455
516
|
this.cleanup();
|
|
456
517
|
|
|
457
518
|
comms.log("Decorator Unmounted");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const colorToRgba: (color: string) => {
|
|
1
|
+
export declare const colorToRgba: (color: string, backgroundColor?: string | null) => {
|
|
2
2
|
r: number;
|
|
3
3
|
g: number;
|
|
4
4
|
b: number;
|
|
@@ -8,9 +8,19 @@ export declare const getLuminance: (color: {
|
|
|
8
8
|
r: number;
|
|
9
9
|
g: number;
|
|
10
10
|
b: number;
|
|
11
|
-
a
|
|
11
|
+
a?: number;
|
|
12
|
+
}) => number;
|
|
13
|
+
export declare const checkContrast: (color1: string | {
|
|
14
|
+
r: number;
|
|
15
|
+
g: number;
|
|
16
|
+
b: number;
|
|
17
|
+
a?: number;
|
|
18
|
+
}, color2: string | {
|
|
19
|
+
r: number;
|
|
20
|
+
g: number;
|
|
21
|
+
b: number;
|
|
22
|
+
a?: number;
|
|
12
23
|
}) => number;
|
|
13
|
-
export declare const isDarkColor: (color: string) => boolean;
|
|
14
|
-
export declare const isLightColor: (color: string) => boolean;
|
|
15
|
-
export declare const
|
|
16
|
-
export declare const ensureContrast: (color1: string, color2: string, contrast?: number) => string[];
|
|
24
|
+
export declare const isDarkColor: (color: string, blendedWith?: string | null) => boolean;
|
|
25
|
+
export declare const isLightColor: (color: string, blendedWith?: string | null) => boolean;
|
|
26
|
+
export declare const getContrastingTextColor: (color: string, blendedWith?: string | null) => "black" | "white";
|
|
@@ -31,11 +31,14 @@ export interface DecoratorRequest {
|
|
|
31
31
|
export declare class Decorator extends Module {
|
|
32
32
|
static readonly moduleName: ModuleName;
|
|
33
33
|
private resizeObserver;
|
|
34
|
+
private backgroundObserver;
|
|
34
35
|
private wnd;
|
|
35
36
|
private resizeFrame;
|
|
36
37
|
private lastGroupId;
|
|
37
38
|
private groups;
|
|
38
39
|
private cleanup;
|
|
40
|
+
private updateHighlightStyles;
|
|
41
|
+
private extractCustomProperty;
|
|
39
42
|
private handleResize;
|
|
40
43
|
private readonly handleResizer;
|
|
41
44
|
mount(wnd: ReadiumWindow, comms: Comms): boolean;
|