cm-chessboard 8.7.9 → 8.7.11
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.
|
Binary file
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<path d="M 0.535156,0.398438 C 0.398438,0.507812 0.316406,0.671875 0.3125,0.847656 0.316406,1.02344 0.398438,1.1875 0.535156,1.29688 L 3.01172,3.35938 3.72266,4.01563 3.01172,4.64453 0.535156,6.67969 C 0.394531,6.79297 0.3125,6.96094 0.3125,7.13672 c 0,0.17578 0.082031,0.34375 0.222656,0.45703 0.328125,0.25781 0.792974,0.25781 1.117184,0 L 5.45703,4.46094 C 5.59766,4.34766 5.68359,4.17969 5.69141,4 5.69141,3.82031 5.60547,3.65234 5.45703,3.54297 L 1.64453,0.394531 C 1.31641,0.148438 0.859375,0.148438 0.535156,0.398438 Z m 0,0"
|
|
14
|
-
class="head-fill"/>
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>arrows</title>
|
|
4
|
+
<g id="arrows" stroke="none" fill="none" fill-rule="evenodd" stroke-width="1">
|
|
5
|
+
<g id="cm-chessboard-arrowheads" transform="translate(5, 1)" fill="#000000">
|
|
6
|
+
<g id="arrowPointy" fill-rule="nonzero">
|
|
7
|
+
<path d="M13.5896955,18.4060262 L3.04774642,4.12789972 C2.71970374,3.68359527 2.81395252,3.05748421 3.25825697,2.72944153 C3.58591001,2.48752592 4.02754261,2.46849386 4.37479285,2.68132465 L29.6089137,18.1473987 C30.0797924,18.4360018 30.227556,19.0516834 29.9389529,19.5225621 C29.8565135,19.6570685 29.7434202,19.7701618 29.6089137,19.8526013 L4.37479285,35.3186754 C3.90391418,35.6072784 3.28823259,35.4595148 2.99962954,34.9886362 C2.78679874,34.6413859 2.80583081,34.1997533 3.04774642,33.8721003 L13.5896955,19.5939738 C13.850383,19.2408959 13.850383,18.7591041 13.5896955,18.4060262 Z" id="Path"></path>
|
|
8
|
+
</g>
|
|
9
|
+
<g id="arrowDefault" transform="translate(3, 0)" fill-rule="nonzero">
|
|
10
|
+
<path d="M13.6380471,11.3573439 L23.0265544,19.1369941 C23.4518122,19.4893778 23.5108883,20.1197808 23.1585046,20.5450386 C23.1090352,20.6047384 23.0527907,20.6584826 22.9909045,20.705188 L13.6023972,27.7906743 C13.1615654,28.1233691 12.5344983,28.0357067 12.2018035,27.594875 C12.0708444,27.4213498 12,27.2098744 12,26.9924778 L12,12.1273413 C12,11.5750565 12.4477153,11.1273413 13,11.1273413 C13.2329818,11.1273413 13.4586517,11.2086906 13.6380471,11.3573439 Z" id="Path"></path>
|
|
11
|
+
</g>
|
|
12
|
+
</g>
|
|
15
13
|
</g>
|
|
16
|
-
|
|
17
|
-
<path d="M 20,20 5,5 35,20 5,35 Z" class="head-fill"/>
|
|
18
|
-
</g>
|
|
19
|
-
</svg>
|
|
14
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -23,7 +23,13 @@ export class Arrows extends Extension {
|
|
|
23
23
|
this.onRedrawBoard()
|
|
24
24
|
})
|
|
25
25
|
this.props = {
|
|
26
|
-
sprite: "extensions/arrows/arrows.svg"
|
|
26
|
+
sprite: "extensions/arrows/arrows.svg",
|
|
27
|
+
// offset factors relative to half the square size (0.0 .. 1.0)
|
|
28
|
+
// 0.0 = start/end in the center; 1.0 = at the outer edge of the inscribed circle (half the square)
|
|
29
|
+
// default chosen to preserve previous appearance where radius = 0.36 * min(squareWidth, squareHeight)
|
|
30
|
+
// which corresponds to 0.72 of half the square size (0.36 / 0.5 = 0.72)
|
|
31
|
+
offsetFrom: 0,
|
|
32
|
+
offsetTo: 0.55
|
|
27
33
|
}
|
|
28
34
|
Object.assign(this.props, props)
|
|
29
35
|
if (this.chessboard.props.assetsCache) {
|
|
@@ -70,12 +76,29 @@ export class Arrows extends Extension {
|
|
|
70
76
|
href: `${spriteUrl}#${arrow.type.slice}`,
|
|
71
77
|
})
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
79
|
+
// Compute centers of start and end squares
|
|
80
|
+
const cx1 = ptFrom.x + view.squareWidth / 2
|
|
81
|
+
const cy1 = ptFrom.y + view.squareHeight / 2
|
|
82
|
+
const cx2 = ptTo.x + view.squareWidth / 2
|
|
83
|
+
const cy2 = ptTo.y + view.squareHeight / 2
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
// Offset the line so it starts/ends at the edge of an invisible circle inside each square
|
|
86
|
+
const dx = cx2 - cx1
|
|
87
|
+
const dy = cy2 - cy1
|
|
88
|
+
const len = Math.hypot(dx, dy) || 1
|
|
89
|
+
const ux = dx / len
|
|
90
|
+
const uy = dy / len
|
|
91
|
+
// compute radii from props: factor relative to half of the square size
|
|
92
|
+
const halfMin = Math.min(view.squareWidth, view.squareHeight) / 2
|
|
93
|
+
const clamp01 = (v) => Math.max(0, Math.min(1, v))
|
|
94
|
+
const rFrom = halfMin * clamp01(this.props.offsetFrom)
|
|
95
|
+
const rTo = halfMin * clamp01(this.props.offsetTo)
|
|
96
|
+
const x1 = cx1 + ux * rFrom
|
|
97
|
+
const y1 = cy1 + uy * rFrom
|
|
98
|
+
const x2 = cx2 - ux * rTo
|
|
99
|
+
const y2 = cy2 - uy * rTo
|
|
100
|
+
|
|
101
|
+
const width = ((view.scalingX + view.scalingY) / 2) * 8
|
|
79
102
|
let lineFill = Svg.addElement(arrowsGroup, "line")
|
|
80
103
|
lineFill.setAttribute('x1', x1.toString())
|
|
81
104
|
lineFill.setAttribute('x2', x2.toString())
|
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
* Redrawing the same arrow or marker removes it.
|
|
10
10
|
*/
|
|
11
11
|
import {Extension, EXTENSION_POINT} from "../../model/Extension.js"
|
|
12
|
-
import {Arrows
|
|
12
|
+
import {Arrows} from "../arrows/Arrows.js"
|
|
13
13
|
import {Markers, MARKER_TYPE} from "../markers/Markers.js"
|
|
14
|
+
import {Svg} from "../../lib/Svg.js"
|
|
15
|
+
|
|
16
|
+
export const ARROW_TYPE = {
|
|
17
|
+
default: {class: "arrow-default", slice: "arrowDefault", headSize: 7},
|
|
18
|
+
danger: {class: "arrow-danger", slice: "arrowDefault", headSize: 7}
|
|
19
|
+
}
|
|
14
20
|
|
|
15
21
|
// Define persistent type objects for matching (strict equality used in core extensions)
|
|
16
22
|
const ARROW_GREEN = {class: "arrow-green", slice: "arrowDefault", headSize: 7}
|
|
@@ -34,19 +40,33 @@ export class RightClickAnnotator extends Extension {
|
|
|
34
40
|
|
|
35
41
|
this.onContextMenu = this.onContextMenu.bind(this)
|
|
36
42
|
this.onMouseDown = this.onMouseDown.bind(this)
|
|
43
|
+
this.onMouseMove = this.onMouseMove.bind(this)
|
|
37
44
|
this.onMouseUp = this.onMouseUp.bind(this)
|
|
38
45
|
|
|
39
46
|
this.dragStart = undefined // {square, modifiers}
|
|
47
|
+
this.previewGroup = undefined
|
|
48
|
+
this.previewActiveTo = undefined // cache last to-square for preview
|
|
40
49
|
|
|
41
50
|
this.chessboard.context.addEventListener("contextmenu", this.onContextMenu)
|
|
42
51
|
this.chessboard.context.addEventListener("mousedown", this.onMouseDown)
|
|
52
|
+
this.chessboard.context.addEventListener("mousemove", this.onMouseMove)
|
|
43
53
|
this.chessboard.context.addEventListener("mouseup", this.onMouseUp)
|
|
54
|
+
this.chessboard.context.addEventListener("mouseleave", this.onMouseUp)
|
|
55
|
+
|
|
56
|
+
// ensure preview group exists after redraws
|
|
57
|
+
this.registerExtensionPoint(EXTENSION_POINT.afterRedrawBoard, () => {
|
|
58
|
+
this.ensurePreviewGroup()
|
|
59
|
+
})
|
|
44
60
|
|
|
45
61
|
this.registerExtensionPoint(EXTENSION_POINT.destroy, () => {
|
|
46
62
|
this.chessboard.context.removeEventListener("contextmenu", this.onContextMenu)
|
|
47
63
|
this.chessboard.context.removeEventListener("mousedown", this.onMouseDown)
|
|
64
|
+
this.chessboard.context.removeEventListener("mousemove", this.onMouseMove)
|
|
48
65
|
this.chessboard.context.removeEventListener("mouseup", this.onMouseUp)
|
|
66
|
+
this.chessboard.context.removeEventListener("mouseleave", this.onMouseUp)
|
|
49
67
|
})
|
|
68
|
+
// create preview group now
|
|
69
|
+
this.ensurePreviewGroup()
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
onContextMenu(event) {
|
|
@@ -73,12 +93,11 @@ export class RightClickAnnotator extends Extension {
|
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
onMouseUp(event) {
|
|
76
|
-
|
|
77
|
-
return
|
|
78
|
-
}
|
|
96
|
+
// clear preview regardless of button, but only act on right-button release
|
|
79
97
|
const start = this.dragStart
|
|
80
98
|
this.dragStart = undefined
|
|
81
|
-
|
|
99
|
+
this.clearPreview()
|
|
100
|
+
if (!start || event.button !== 2) {
|
|
82
101
|
return
|
|
83
102
|
}
|
|
84
103
|
const endSquare = this.findSquareFromEvent(event) || start.square
|
|
@@ -91,6 +110,7 @@ export class RightClickAnnotator extends Extension {
|
|
|
91
110
|
if (existing && existing.length > 0) {
|
|
92
111
|
this.chessboard.removeArrows(arrowType, start.square, endSquare)
|
|
93
112
|
} else {
|
|
113
|
+
this.chessboard.removeArrows(undefined, start.square, endSquare)
|
|
94
114
|
this.chessboard.addArrow(arrowType, start.square, endSquare)
|
|
95
115
|
}
|
|
96
116
|
} else if (start.square) {
|
|
@@ -99,6 +119,7 @@ export class RightClickAnnotator extends Extension {
|
|
|
99
119
|
if (existingMarkers && existingMarkers.length > 0) {
|
|
100
120
|
this.chessboard.removeMarkers(circleType, start.square)
|
|
101
121
|
} else {
|
|
122
|
+
this.chessboard.removeMarkers(undefined, start.square)
|
|
102
123
|
this.chessboard.addMarker(circleType, start.square)
|
|
103
124
|
}
|
|
104
125
|
}
|
|
@@ -114,6 +135,104 @@ export class RightClickAnnotator extends Extension {
|
|
|
114
135
|
return el ? el.getAttribute("data-square") : undefined
|
|
115
136
|
}
|
|
116
137
|
|
|
138
|
+
ensurePreviewGroup() {
|
|
139
|
+
// create or reset preview group used for transient arrow drawing
|
|
140
|
+
if (!this.chessboard?.view?.markersTopLayer) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
if (this.previewGroup && this.previewGroup.parentNode) {
|
|
144
|
+
// keep and clear existing
|
|
145
|
+
this.clearPreview()
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
this.previewGroup = Svg.addElement(this.chessboard.view.markersTopLayer, "g", {class: "right-click-annotator-preview"})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
clearPreview() {
|
|
152
|
+
if (!this.previewGroup) return
|
|
153
|
+
while (this.previewGroup.firstChild) {
|
|
154
|
+
this.previewGroup.removeChild(this.previewGroup.firstChild)
|
|
155
|
+
}
|
|
156
|
+
this.previewActiveTo = undefined
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
onMouseMove(event) {
|
|
160
|
+
if (!this.dragStart) {
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
// Only show preview for right-button drag if still pressed (best-effort); some browsers may not keep buttons state reliably
|
|
164
|
+
// We rely mainly on our dragStart flag and clear on mouseup/mouseleave
|
|
165
|
+
const toSquare = this.findSquareFromEvent(event)
|
|
166
|
+
if (!toSquare || toSquare === this.dragStart.square) {
|
|
167
|
+
this.clearPreview()
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
if (this.previewActiveTo === toSquare) {
|
|
171
|
+
return // no change
|
|
172
|
+
}
|
|
173
|
+
this.previewActiveTo = toSquare
|
|
174
|
+
const colorKey = this.modifiersToColorKey(this.dragStart.modifiers)
|
|
175
|
+
const {arrowType} = this.typesForColorKey(colorKey)
|
|
176
|
+
this.drawPreviewArrow(this.dragStart.square, toSquare, arrowType)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
drawPreviewArrow(from, to, type) {
|
|
180
|
+
this.ensurePreviewGroup()
|
|
181
|
+
this.clearPreview()
|
|
182
|
+
const view = this.chessboard.view
|
|
183
|
+
const arrowsGroup = Svg.addElement(this.previewGroup, "g", {class: "arrow " + type.class})
|
|
184
|
+
const ptFrom = view.squareToPoint(from)
|
|
185
|
+
const ptTo = view.squareToPoint(to)
|
|
186
|
+
const defs = Svg.addElement(arrowsGroup, "defs")
|
|
187
|
+
const id = "arrow-preview-" + from + to
|
|
188
|
+
const marker = Svg.addElement(defs, "marker", {
|
|
189
|
+
id: id,
|
|
190
|
+
markerWidth: type.headSize,
|
|
191
|
+
markerHeight: type.headSize,
|
|
192
|
+
refX: 20,
|
|
193
|
+
refY: 20,
|
|
194
|
+
viewBox: "0 0 40 40",
|
|
195
|
+
orient: "auto",
|
|
196
|
+
class: "arrow-head",
|
|
197
|
+
})
|
|
198
|
+
const spriteUrl = this.chessboard.props.assetsCache ? "" : this.chessboard.getExtension(Arrows)?.getSpriteUrl?.() || this.chessboard.props.assetsUrl + "extensions/arrows/arrows.svg"
|
|
199
|
+
Svg.addElement(marker, "use", {href: `${spriteUrl}#${type.slice}`})
|
|
200
|
+
// Compute centers of start and end squares
|
|
201
|
+
const cx1 = ptFrom.x + view.squareWidth / 2
|
|
202
|
+
const cy1 = ptFrom.y + view.squareHeight / 2
|
|
203
|
+
const cx2 = ptTo.x + view.squareWidth / 2
|
|
204
|
+
const cy2 = ptTo.y + view.squareHeight / 2
|
|
205
|
+
|
|
206
|
+
// Offset the line so it starts/ends at the edge of an invisible circle inside each square
|
|
207
|
+
const dx = cx2 - cx1
|
|
208
|
+
const dy = cy2 - cy1
|
|
209
|
+
const len = Math.hypot(dx, dy) || 1
|
|
210
|
+
const ux = dx / len
|
|
211
|
+
const uy = dy / len
|
|
212
|
+
// get offsets from Arrows extension props to match final arrow rendering
|
|
213
|
+
const arrowsExt = this.chessboard.getExtension(Arrows)
|
|
214
|
+
const offsetFrom = arrowsExt?.props?.offsetFrom ?? 0.72
|
|
215
|
+
const offsetTo = arrowsExt?.props?.offsetTo ?? 0.72
|
|
216
|
+
const halfMin = Math.min(view.squareWidth, view.squareHeight) / 2
|
|
217
|
+
const clamp01 = (v) => Math.max(0, Math.min(1, v))
|
|
218
|
+
const rFrom = halfMin * clamp01(offsetFrom)
|
|
219
|
+
const rTo = halfMin * clamp01(offsetTo)
|
|
220
|
+
const x1 = cx1 + ux * rFrom
|
|
221
|
+
const y1 = cy1 + uy * rFrom
|
|
222
|
+
const x2 = cx2 - ux * rTo
|
|
223
|
+
const y2 = cy2 - uy * rTo
|
|
224
|
+
|
|
225
|
+
const width = ((view.scalingX + view.scalingY) / 2) * 8
|
|
226
|
+
let lineFill = Svg.addElement(arrowsGroup, "line")
|
|
227
|
+
lineFill.setAttribute('x1', x1.toString())
|
|
228
|
+
lineFill.setAttribute('x2', x2.toString())
|
|
229
|
+
lineFill.setAttribute('y1', y1.toString())
|
|
230
|
+
lineFill.setAttribute('y2', y2.toString())
|
|
231
|
+
lineFill.setAttribute('class', 'arrow-line')
|
|
232
|
+
lineFill.setAttribute("marker-end", "url(#" + id + ")")
|
|
233
|
+
lineFill.setAttribute('stroke-width', width + "px")
|
|
234
|
+
}
|
|
235
|
+
|
|
117
236
|
modifiersToColorKey(modifiers) {
|
|
118
237
|
if (modifiers.shift && modifiers.alt) return "orange"
|
|
119
238
|
if (modifiers.shift) return "red"
|