react-book-reader 1.1.7 → 1.1.8

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.
@@ -0,0 +1,175 @@
1
+ const createSVGElement = tag =>
2
+ document.createElementNS('http://www.w3.org/2000/svg', tag)
3
+
4
+ export class Overlayer {
5
+ #svg = createSVGElement('svg')
6
+ #map = new Map()
7
+ constructor() {
8
+ Object.assign(this.#svg.style, {
9
+ position: 'absolute', top: '0', left: '0',
10
+ width: '100%', height: '100%',
11
+ pointerEvents: 'none',
12
+ })
13
+ }
14
+ get element() {
15
+ return this.#svg
16
+ }
17
+ add(key, range, draw, options) {
18
+ if (this.#map.has(key)) this.remove(key)
19
+ if (typeof range === 'function') range = range(this.#svg.getRootNode())
20
+ const rects = range.getClientRects()
21
+ const element = draw(rects, options)
22
+ this.#svg.append(element)
23
+ this.#map.set(key, { range, draw, options, element, rects })
24
+ }
25
+ remove(key) {
26
+ if (!this.#map.has(key)) return
27
+ this.#svg.removeChild(this.#map.get(key).element)
28
+ this.#map.delete(key)
29
+ }
30
+ redraw() {
31
+ for (const obj of this.#map.values()) {
32
+ const { range, draw, options, element } = obj
33
+ this.#svg.removeChild(element)
34
+ const rects = range.getClientRects()
35
+ const el = draw(rects, options)
36
+ this.#svg.append(el)
37
+ obj.element = el
38
+ obj.rects = rects
39
+ }
40
+ }
41
+ hitTest({ x, y }) {
42
+ const arr = Array.from(this.#map.entries())
43
+ // loop in reverse to hit more recently added items first
44
+ for (let i = arr.length - 1; i >= 0; i--) {
45
+ const [key, obj] = arr[i]
46
+ for (const { left, top, right, bottom } of obj.rects)
47
+ if (top <= y && left <= x && bottom > y && right > x)
48
+ return [key, obj.range]
49
+ }
50
+ return []
51
+ }
52
+ static underline(rects, options = {}) {
53
+ const { color = 'red', width: strokeWidth = 2, writingMode } = options
54
+ const g = createSVGElement('g')
55
+ g.setAttribute('fill', color)
56
+ if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
57
+ for (const { right, top, height } of rects) {
58
+ const el = createSVGElement('rect')
59
+ el.setAttribute('x', right - strokeWidth)
60
+ el.setAttribute('y', top)
61
+ el.setAttribute('height', height)
62
+ el.setAttribute('width', strokeWidth)
63
+ g.append(el)
64
+ }
65
+ else for (const { left, bottom, width } of rects) {
66
+ const el = createSVGElement('rect')
67
+ el.setAttribute('x', left)
68
+ el.setAttribute('y', bottom - strokeWidth)
69
+ el.setAttribute('height', strokeWidth)
70
+ el.setAttribute('width', width)
71
+ g.append(el)
72
+ }
73
+ return g
74
+ }
75
+ static strikethrough(rects, options = {}) {
76
+ const { color = 'red', width: strokeWidth = 2, writingMode } = options
77
+ const g = createSVGElement('g')
78
+ g.setAttribute('fill', color)
79
+ if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
80
+ for (const { right, left, top, height } of rects) {
81
+ const el = createSVGElement('rect')
82
+ el.setAttribute('x', (right + left) / 2)
83
+ el.setAttribute('y', top)
84
+ el.setAttribute('height', height)
85
+ el.setAttribute('width', strokeWidth)
86
+ g.append(el)
87
+ }
88
+ else for (const { left, top, bottom, width } of rects) {
89
+ const el = createSVGElement('rect')
90
+ el.setAttribute('x', left)
91
+ el.setAttribute('y', (top + bottom) / 2)
92
+ el.setAttribute('height', strokeWidth)
93
+ el.setAttribute('width', width)
94
+ g.append(el)
95
+ }
96
+ return g
97
+ }
98
+ static squiggly(rects, options = {}) {
99
+ const { color = 'red', width: strokeWidth = 2, writingMode } = options
100
+ const g = createSVGElement('g')
101
+ g.setAttribute('fill', 'none')
102
+ g.setAttribute('stroke', color)
103
+ g.setAttribute('stroke-width', strokeWidth)
104
+ const block = strokeWidth * 1.5
105
+ if (writingMode === 'vertical-rl' || writingMode === 'vertical-lr')
106
+ for (const { right, top, height } of rects) {
107
+ const el = createSVGElement('path')
108
+ const n = Math.round(height / block / 1.5)
109
+ const inline = height / n
110
+ const ls = Array.from({ length: n },
111
+ (_, i) => `l${i % 2 ? -block : block} ${inline}`).join('')
112
+ el.setAttribute('d', `M${right} ${top}${ls}`)
113
+ g.append(el)
114
+ }
115
+ else for (const { left, bottom, width } of rects) {
116
+ const el = createSVGElement('path')
117
+ const n = Math.round(width / block / 1.5)
118
+ const inline = width / n
119
+ const ls = Array.from({ length: n },
120
+ (_, i) => `l${inline} ${i % 2 ? block : -block}`).join('')
121
+ el.setAttribute('d', `M${left} ${bottom}${ls}`)
122
+ g.append(el)
123
+ }
124
+ return g
125
+ }
126
+ static highlight(rects, options = {}) {
127
+ const { color = 'red' } = options
128
+ const g = createSVGElement('g')
129
+ g.setAttribute('fill', color)
130
+ g.style.opacity = 'var(--overlayer-highlight-opacity, .3)'
131
+ g.style.mixBlendMode = 'var(--overlayer-highlight-blend-mode, normal)'
132
+ for (const { left, top, height, width } of rects) {
133
+ const el = createSVGElement('rect')
134
+ el.setAttribute('x', left)
135
+ el.setAttribute('y', top)
136
+ el.setAttribute('height', height)
137
+ el.setAttribute('width', width)
138
+ g.append(el)
139
+ }
140
+ return g
141
+ }
142
+ static outline(rects, options = {}) {
143
+ const { color = 'red', width: strokeWidth = 3, radius = 3 } = options
144
+ const g = createSVGElement('g')
145
+ g.setAttribute('fill', 'none')
146
+ g.setAttribute('stroke', color)
147
+ g.setAttribute('stroke-width', strokeWidth)
148
+ for (const { left, top, height, width } of rects) {
149
+ const el = createSVGElement('rect')
150
+ el.setAttribute('x', left)
151
+ el.setAttribute('y', top)
152
+ el.setAttribute('height', height)
153
+ el.setAttribute('width', width)
154
+ el.setAttribute('rx', radius)
155
+ g.append(el)
156
+ }
157
+ return g
158
+ }
159
+ // make an exact copy of an image in the overlay
160
+ // one can then apply filters to the entire element, without affecting them;
161
+ // it's a bit silly and probably better to just invert images twice
162
+ // (though the color will be off in that case if you do heu-rotate)
163
+ static copyImage([rect], options = {}) {
164
+ const { src } = options
165
+ const image = createSVGElement('image')
166
+ const { left, top, height, width } = rect
167
+ image.setAttribute('href', src)
168
+ image.setAttribute('x', left)
169
+ image.setAttribute('y', top)
170
+ image.setAttribute('height', height)
171
+ image.setAttribute('width', width)
172
+ return image
173
+ }
174
+ }
175
+