cm-chessboard 8.7.7 → 8.7.9

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.
@@ -28,4 +28,22 @@
28
28
  stroke-linecap: round;
29
29
  opacity: 0.5; }
30
30
 
31
+ /* custom colors for RightClickAnnotator */
32
+ .cm-chessboard .arrow-green .arrow-head {
33
+ fill: #2e7d32; /* green */
34
+ fill-rule: nonzero;
35
+ fill-opacity: 1; }
36
+ .cm-chessboard .arrow-green .arrow-line {
37
+ stroke: #2e7d32;
38
+ stroke-linecap: round;
39
+ opacity: 0.5; }
40
+ .cm-chessboard .arrow-orange .arrow-head {
41
+ fill: #ff9800; /* orange */
42
+ fill-rule: nonzero;
43
+ fill-opacity: 1; }
44
+ .cm-chessboard .arrow-orange .arrow-line {
45
+ stroke: #ff9800;
46
+ stroke-linecap: round;
47
+ opacity: 0.5; }
48
+
31
49
  /*# sourceMappingURL=arrows.css.map */
@@ -34,4 +34,14 @@
34
34
  fill: black;
35
35
  opacity: 0.2; }
36
36
 
37
- /*# sourceMappingURL=markers.css.map */
37
+ /* custom colors for RightClickAnnotator */
38
+ .cm-chessboard .markers .marker.marker-circle-green {
39
+ stroke: #2e7d32; /* green */
40
+ stroke-width: 3px;
41
+ opacity: 0.4; }
42
+ .cm-chessboard .markers .marker.marker-circle-orange {
43
+ stroke: #ff9800; /* orange */
44
+ stroke-width: 3px;
45
+ opacity: 0.4; }
46
+
47
+ /*# sourceMappingURL=markers.css.map */
@@ -53,7 +53,7 @@
53
53
  position: "rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR",
54
54
  assetsUrl: "../../assets/",
55
55
  style: {pieces: {file: "pieces/staunty.svg"}},
56
- extensions: [{class: Markers, autoMarkers: MARKER_TYPE.frame}]
56
+ extensions: [{class: Markers, props: {autoMarkers: MARKER_TYPE.frame}}]
57
57
  })
58
58
  board2.enableMoveInput(() => {
59
59
  return true
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>cm-chessboard</title>
6
+ <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"/>
7
+ <link rel="stylesheet" href="../styles/examples.css"/>
8
+ <link rel="stylesheet" href="../../assets/chessboard.css"/>
9
+ <link rel="stylesheet" href="../../assets/extensions/markers/markers.css"/>
10
+ <link rel="stylesheet" href="../../assets/extensions/arrows/arrows.css"/>
11
+ </head>
12
+ <body>
13
+ <h1><a href="../..">cm-chessboard</a></h1>
14
+ <h2>Example: RightClickAnnotator extension</h2>
15
+ <p>Right-click to toggle green markers. Right-click + drag to draw green arrows. Use Alt for blue, Shift for red, Shift+Alt for orange.</p>
16
+ <div class="board" id="board"></div>
17
+ <script type="module">
18
+ import {Chessboard} from "../../src/Chessboard.js"
19
+ import {FEN} from "../../src/model/Position.js"
20
+ import {RightClickAnnotator} from "../../src/extensions/right-click-annotator/RightClickAnnotator.js"
21
+
22
+ const board = new Chessboard(document.getElementById("board"), {
23
+ position: FEN.start,
24
+ assetsUrl: "../../assets/",
25
+ extensions: [{class: RightClickAnnotator}]
26
+ })
27
+ </script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <!--
3
+ Testpage for https://github.com/shaack/cm-chessboard/pull/149
4
+ -->
5
+ <html lang="en">
6
+ <head>
7
+ <title>Arrows Example</title>
8
+ <link rel="stylesheet" href="../../assets/chessboard.css">
9
+ <link rel="stylesheet" href="../styles/examples.css"/>
10
+ <link rel="stylesheet" href="../../assets/extensions/arrows/arrows.css">
11
+ <style>
12
+ #board1 { width: 30%; }
13
+ #board2 { width: 30%; }
14
+ </style>
15
+
16
+ <script type='module'>
17
+ import {Chessboard} from '../../src/Chessboard.js'
18
+ import {Arrows, ARROW_TYPE} from "../../src/extensions/arrows/Arrows.js"
19
+ window.Chessboard = Chessboard;
20
+ window.Arrows = Arrows;
21
+ window.ARROW_TYPE = ARROW_TYPE;
22
+ </script>
23
+
24
+ <script type='text/javascript'>
25
+ let brd1;
26
+ let brd2;
27
+ function init() {
28
+ brd1 = new Chessboard(document.getElementById('board1'), {
29
+ assetsUrl: "../../assets/",
30
+ extensions: [
31
+ {class: Arrows},
32
+ ]
33
+ });
34
+ brd1.addArrow(ARROW_TYPE.pointy, 'a1', 'b3')
35
+ brd1.setPiece('a1', 'wn')
36
+
37
+ brd2 = new Chessboard(document.getElementById('board2'), {
38
+ assetsUrl: "../../assets/",
39
+ extensions: [
40
+ {class: Arrows},
41
+ ]
42
+ });
43
+ brd2.setOrientation('b');
44
+ brd2.addArrow(ARROW_TYPE.pointy, 'a1', 'b3')
45
+ brd2.setPiece('a1', 'wn')
46
+ }
47
+ </script>
48
+
49
+ </head>
50
+ <body onload='init()'>
51
+
52
+ <div id='board1' style="margin-bottom: 0.5rem"></div>
53
+ <button onclick="brd1.setOrientation(brd1.getOrientation() === 'w' ? 'b' : 'w')">Toggle1</button>
54
+
55
+ <div id='board2' style="margin-bottom: 0.5rem"></div>
56
+ <button onclick="brd2.setOrientation(brd2.getOrientation() === 'w' ? 'b' : 'w')">Toggle2</button>
57
+
58
+ </body>
59
+ </html>
package/index.html CHANGED
@@ -33,6 +33,7 @@ It works, it is cool and it is easy to use. 👍</p>
33
33
  <li><a href="examples/extensions/html-layer-extension.html">HTML Layer extension</a></li>
34
34
  <li><a href="examples/extensions/promotion-dialog-extension.html">PromotionDialog extension</a></li>
35
35
  <li><a href="examples/extensions/accessibility-extension.html">Accessibility extension</a></li>
36
+ <li><a href="examples/extensions/right-click-annotator.html">RightClickAnnotator extension</a></li>
36
37
  </ul>
37
38
  </div>
38
39
  <h2>Chessboard</h2>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cm-chessboard",
3
- "version": "8.7.7",
3
+ "version": "8.7.9",
4
4
  "description": "A JavaScript chessboard which is lightweight, ES6 module based, responsive, SVG rendered and without dependencies.",
5
5
  "keywords": [
6
6
  "chess",
@@ -46,12 +46,12 @@ export class Arrows extends Extension {
46
46
  }
47
47
 
48
48
  drawArrow(arrow) {
49
+ const view = this.chessboard.view
49
50
  const arrowsGroup = Svg.addElement(this.arrowGroup, "g")
50
51
  arrowsGroup.setAttribute("data-arrow", arrow.from + arrow.to)
51
52
  arrowsGroup.setAttribute("class", "arrow " + arrow.type.class)
52
- const view = this.chessboard.view
53
- const sqfrom = document.querySelectorAll('[data-square="' + arrow.from + '"]')[0]
54
- const sqto = document.querySelectorAll('[data-square="' + arrow.to + '"]')[0]
53
+ const ptFrom = view.squareToPoint(arrow.from)
54
+ const ptTo = view.squareToPoint(arrow.to)
55
55
  const spriteUrl = this.chessboard.props.assetsCache ? "" : this.getSpriteUrl()
56
56
  const defs = Svg.addElement(arrowsGroup, "defs")
57
57
  const id = "arrow-" + arrow.from + arrow.to
@@ -59,7 +59,6 @@ export class Arrows extends Extension {
59
59
  id: id,
60
60
  markerWidth: arrow.type.headSize,
61
61
  markerHeight: arrow.type.headSize,
62
- //markerUnits: "userSpaceOnUse",
63
62
  refX: 20,
64
63
  refY: 20,
65
64
  viewBox: "0 0 40 40",
@@ -67,14 +66,14 @@ export class Arrows extends Extension {
67
66
  class: "arrow-head",
68
67
  })
69
68
 
70
- const ignored = Svg.addElement(marker, "use", {
69
+ Svg.addElement(marker, "use", {
71
70
  href: `${spriteUrl}#${arrow.type.slice}`,
72
71
  })
73
72
 
74
- const x1 = sqfrom.x.baseVal.value + (sqfrom.width.baseVal.value / 2)
75
- const x2 = sqto.x.baseVal.value + (sqto.width.baseVal.value / 2)
76
- const y1 = sqfrom.y.baseVal.value + (sqfrom.height.baseVal.value / 2)
77
- const y2 = sqto.y.baseVal.value + (sqto.height.baseVal.value / 2)
73
+ const x1 = ptFrom.x + view.squareWidth / 2
74
+ const x2 = ptTo.x + view.squareHeight / 2
75
+ const y1 = ptFrom.y + view.squareWidth / 2
76
+ const y2 = ptTo.y + view.squareHeight / 2
78
77
 
79
78
  const width = ((view.scalingX + view.scalingY) / 2) * 4
80
79
  let lineFill = Svg.addElement(arrowsGroup, "line")
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Extension: RightClickAnnotator
3
+ * Combines Arrows and Markers to draw/toggle arrows and circle markers with right-click + modifiers.
4
+ * Colors:
5
+ * - Green: Right-click
6
+ * - Blue: Alt + Right-click
7
+ * - Red: Shift + Right-click
8
+ * - Orange: Shift + Alt + Right-click
9
+ * Redrawing the same arrow or marker removes it.
10
+ */
11
+ import {Extension, EXTENSION_POINT} from "../../model/Extension.js"
12
+ import {Arrows, ARROW_TYPE} from "../arrows/Arrows.js"
13
+ import {Markers, MARKER_TYPE} from "../markers/Markers.js"
14
+
15
+ // Define persistent type objects for matching (strict equality used in core extensions)
16
+ const ARROW_GREEN = {class: "arrow-green", slice: "arrowDefault", headSize: 7}
17
+ const ARROW_ORANGE = {class: "arrow-orange", slice: "arrowDefault", headSize: 7}
18
+
19
+ const CIRCLE_GREEN = {class: "marker-circle-green", slice: "markerCircle"}
20
+ const CIRCLE_ORANGE = {class: "marker-circle-orange", slice: "markerCircle"}
21
+
22
+ export class RightClickAnnotator extends Extension {
23
+ constructor(chessboard, props = {}) {
24
+ super(chessboard)
25
+ this.props = props || {}
26
+
27
+ // Ensure Arrows and Markers extensions are available
28
+ if (!this.chessboard.getExtension(Arrows)) {
29
+ this.chessboard.addExtension(Arrows)
30
+ }
31
+ if (!this.chessboard.getExtension(Markers)) {
32
+ this.chessboard.addExtension(Markers)
33
+ }
34
+
35
+ this.onContextMenu = this.onContextMenu.bind(this)
36
+ this.onMouseDown = this.onMouseDown.bind(this)
37
+ this.onMouseUp = this.onMouseUp.bind(this)
38
+
39
+ this.dragStart = undefined // {square, modifiers}
40
+
41
+ this.chessboard.context.addEventListener("contextmenu", this.onContextMenu)
42
+ this.chessboard.context.addEventListener("mousedown", this.onMouseDown)
43
+ this.chessboard.context.addEventListener("mouseup", this.onMouseUp)
44
+
45
+ this.registerExtensionPoint(EXTENSION_POINT.destroy, () => {
46
+ this.chessboard.context.removeEventListener("contextmenu", this.onContextMenu)
47
+ this.chessboard.context.removeEventListener("mousedown", this.onMouseDown)
48
+ this.chessboard.context.removeEventListener("mouseup", this.onMouseUp)
49
+ })
50
+ }
51
+
52
+ onContextMenu(event) {
53
+ // prevent default context menu on chessboard
54
+ event.preventDefault()
55
+ }
56
+
57
+ onMouseDown(event) {
58
+ // right button only
59
+ if (event.button !== 2) {
60
+ return
61
+ }
62
+ const square = this.findSquareFromEvent(event)
63
+ if (!square) {
64
+ return
65
+ }
66
+ this.dragStart = {
67
+ square,
68
+ modifiers: {
69
+ alt: event.altKey,
70
+ shift: event.shiftKey
71
+ }
72
+ }
73
+ }
74
+
75
+ onMouseUp(event) {
76
+ if (event.button !== 2) {
77
+ return
78
+ }
79
+ const start = this.dragStart
80
+ this.dragStart = undefined
81
+ if (!start) {
82
+ return
83
+ }
84
+ const endSquare = this.findSquareFromEvent(event) || start.square
85
+ const colorKey = this.modifiersToColorKey(start.modifiers)
86
+ const {arrowType, circleType} = this.typesForColorKey(colorKey)
87
+
88
+ if (start.square && endSquare && start.square !== endSquare) {
89
+ // toggle arrow
90
+ const existing = this.chessboard.getArrows(arrowType, start.square, endSquare)
91
+ if (existing && existing.length > 0) {
92
+ this.chessboard.removeArrows(arrowType, start.square, endSquare)
93
+ } else {
94
+ this.chessboard.addArrow(arrowType, start.square, endSquare)
95
+ }
96
+ } else if (start.square) {
97
+ // toggle marker on start square
98
+ const existingMarkers = this.chessboard.getMarkers(circleType, start.square)
99
+ if (existingMarkers && existingMarkers.length > 0) {
100
+ this.chessboard.removeMarkers(circleType, start.square)
101
+ } else {
102
+ this.chessboard.addMarker(circleType, start.square)
103
+ }
104
+ }
105
+ }
106
+
107
+ findSquareFromEvent(event) {
108
+ const target = /** @type {HTMLElement} */(event.target)
109
+ if (!target) return undefined
110
+ if (target.getAttribute && target.getAttribute("data-square")) {
111
+ return target.getAttribute("data-square")
112
+ }
113
+ const el = target.closest && target.closest("[data-square]")
114
+ return el ? el.getAttribute("data-square") : undefined
115
+ }
116
+
117
+ modifiersToColorKey(modifiers) {
118
+ if (modifiers.shift && modifiers.alt) return "orange"
119
+ if (modifiers.shift) return "red"
120
+ if (modifiers.alt) return "blue"
121
+ return "green"
122
+ }
123
+
124
+ typesForColorKey(colorKey) {
125
+ switch (colorKey) {
126
+ case "blue":
127
+ return {arrowType: ARROW_TYPE.default, circleType: MARKER_TYPE.circlePrimary}
128
+ case "red":
129
+ return {arrowType: ARROW_TYPE.danger, circleType: MARKER_TYPE.circleDanger}
130
+ case "orange":
131
+ return {arrowType: ARROW_ORANGE, circleType: CIRCLE_ORANGE}
132
+ case "green":
133
+ default:
134
+ return {arrowType: ARROW_GREEN, circleType: CIRCLE_GREEN}
135
+ }
136
+ }
137
+ }