/
SVGMouseCursor.ts
145 lines (133 loc) · 4.28 KB
/
SVGMouseCursor.ts
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { ToolModes, AnnotationStyleStates } from '../enums';
import MouseCursor from './MouseCursor';
import ImageMouseCursor from './ImageMouseCursor';
import { getDefinedSVGCursorDescriptor } from './SVGCursorDescriptor';
import { getStyleProperty } from '../stateManagement/annotation/config/helpers';
import type { StyleSpecifier } from '../types/AnnotationStyle';
import type { SVGCursorDescriptor } from '../types';
const PROPERTY = 'color';
const STATE = AnnotationStyleStates.Highlighted;
const MODE = ToolModes.Active;
export default class SVGMouseCursor extends ImageMouseCursor {
constructor(
url: string,
x?: number,
y?: number,
name?: string | undefined,
fallback?: MouseCursor | undefined
) {
super(url, x, y, name, fallback);
}
/**
* Get a shared instance of the SVGMouseCursor class satisfying the given parameters.
*
* @param name - The name of the cursor (defined in SVGCursorDescriptor.ts);
* @param pointer - Should be true to use the version of the cursor containing
* a mouse pointer. Defaults to false (which does not add a pointer to the cursor);
* @param color - The color of the cursor. Defaults to tool.style.colorHighlightedActive;
* @returns a SVGMouseCursor instance or
* undefined if no SVG cursor descriptor was found with the given name;
*/
static getDefinedCursor(
name: string,
pointer = false,
color?: string
): MouseCursor {
if (!color) {
color = getStyleProperty(PROPERTY, {} as StyleSpecifier, STATE, MODE);
}
const urn = getCursorURN(name, pointer, color);
let cursor = super.getDefinedCursor(urn);
if (!cursor) {
const descriptor = getDefinedSVGCursorDescriptor(name);
if (descriptor) {
cursor = createSVGMouseCursor(
descriptor,
urn,
pointer,
color,
super.getDefinedCursor('default')
);
super.setDefinedCursor(urn, cursor);
}
}
return cursor;
}
}
/*
* Helpers
*/
function format(template: string, dictionary: Record<string, unknown>): string {
const dict = Object(dictionary);
const defined = Object.prototype.hasOwnProperty.bind(dict);
return (template + '').replace(/\{\{(\w+)\}\}/g, (match, key) => {
return defined(key) ? dict[key] + '' : '';
});
}
function getCursorURN(name: string, pointer: boolean, color: string) {
const type = pointer ? 'pointer' : 'cursor';
return `${type}:${name}/${color}`;
}
function createSVGMouseCursor(
descriptor: SVGCursorDescriptor,
name: string,
pointer: boolean,
color: string,
fallback: MouseCursor
): SVGMouseCursor {
const { x, y } = descriptor.mousePoint;
return new SVGMouseCursor(
createSVGIconUrl(descriptor, pointer, { color }),
x,
y,
name,
fallback
);
}
function createSVGIconUrl(
descriptor: SVGCursorDescriptor,
pointer: boolean,
options: Record<string, unknown>
): string {
return URL.createObjectURL(createSVGIconBlob(descriptor, pointer, options));
}
function createSVGIconBlob(
descriptor: SVGCursorDescriptor,
pointer: boolean,
options: Record<string, unknown>
): Blob {
const svgString = (pointer ? createSVGIconWithPointer : createSVGIcon)(
descriptor,
options
);
return new Blob([svgString], { type: 'image/svg+xml' });
}
function createSVGIcon(
descriptor: SVGCursorDescriptor,
options: Record<string, unknown>
): string {
const { iconContent, iconSize, viewBox } = descriptor;
const svgString = `
<svg data-icon="cursor" role="img" xmlns="http://www.w3.org/2000/svg"
width="${iconSize}" height="${iconSize}" viewBox="0 0
${viewBox.x} ${viewBox.y}">
${iconContent}
</svg>`;
return format(svgString, options);
}
function createSVGIconWithPointer(
descriptor: SVGCursorDescriptor,
options: Record<string, unknown>
) {
const { iconContent, iconSize, viewBox, mousePointerGroupString } =
descriptor;
const scale = iconSize / Math.max(viewBox.x, viewBox.y, 1);
const svgSize = 16 + iconSize;
const svgString = `
<svg data-icon="cursor" role="img" xmlns="http://www.w3.org/2000/svg"
width="${svgSize}" height="${svgSize}" viewBox="0 0 ${svgSize} ${svgSize}">
<g>${mousePointerGroupString}</g>
<g transform="translate(16, 16) scale(${scale})">${iconContent}</g>
</svg>`;
return format(svgString, options);
}