cm-chessboard 8.14.0 → 8.14.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cm-chessboard",
3
- "version": "8.14.0",
3
+ "version": "8.14.1",
4
4
  "description": "A JavaScript chessboard which is lightweight, ES6 module based, responsive, SVG rendered and without dependencies.",
5
5
  "keywords": [
6
6
  "chess",
@@ -34,6 +34,9 @@ export const PROMOTION_DIALOG_RESULT_TYPE = {
34
34
  canceled: "canceled"
35
35
  }
36
36
 
37
+ // Keys the open dialog handles itself, see handleKeyDown
38
+ const HANDLED_KEYS = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Enter", " ", "Escape", "Tab"]
39
+
37
40
  export class PromotionDialog extends Extension {
38
41
 
39
42
  /** @constructor */
@@ -259,8 +262,12 @@ export class PromotionDialog extends Extension {
259
262
  this.promotionDialogOnClickPiece.bind(this))
260
263
  this.contextMenuListener = this.contextMenu.bind(this)
261
264
  this.chessboard.view.svg.addEventListener("contextmenu", this.contextMenuListener)
262
- // Add keyboard listener
263
- document.addEventListener("keydown", this.handleKeyDown)
265
+ // Keyboard listener in the capture phase: while the dialog is open its keys
266
+ // must not reach anything else. Applications commonly bind the arrow keys on
267
+ // document for history navigation (chess-console does), and those listeners
268
+ // are registered before the dialog opens, so in the bubble phase they would
269
+ // run first and change the position under the open dialog.
270
+ document.addEventListener("keydown", this.handleKeyDown, true)
264
271
  } else if (displayState === DISPLAY_STATE.hidden) {
265
272
  if (this.clickDelegate) {
266
273
  this.clickDelegate.remove()
@@ -271,7 +278,7 @@ export class PromotionDialog extends Extension {
271
278
  this.contextMenuListener = null
272
279
  }
273
280
  // Remove keyboard listener
274
- document.removeEventListener("keydown", this.handleKeyDown)
281
+ document.removeEventListener("keydown", this.handleKeyDown, true)
275
282
  // Restore focus (only if the dialog was actually shown before)
276
283
  if (prevState === DISPLAY_STATE.shown &&
277
284
  this.previouslyFocusedElement && this.previouslyFocusedElement.focus) {
@@ -292,6 +299,12 @@ export class PromotionDialog extends Extension {
292
299
  if (this.state.displayState !== DISPLAY_STATE.shown) {
293
300
  return
294
301
  }
302
+ // Keys the dialog owns are consumed here, they must not reach the page below.
303
+ // Without this the arrow keys would also drive an application's own history
304
+ // navigation and change the position while the dialog is waiting for an answer.
305
+ if (HANDLED_KEYS.includes(event.key)) {
306
+ event.stopPropagation()
307
+ }
295
308
  switch (event.key) {
296
309
  case "ArrowDown":
297
310
  event.preventDefault()
@@ -395,7 +408,7 @@ export class PromotionDialog extends Extension {
395
408
  clearTimeout(this.announceTimeoutId)
396
409
  this.announceTimeoutId = null
397
410
  }
398
- document.removeEventListener("keydown", this.handleKeyDown)
411
+ document.removeEventListener("keydown", this.handleKeyDown, true)
399
412
  if (this.liveRegion && this.liveRegion.parentNode) {
400
413
  this.liveRegion.parentNode.removeChild(this.liveRegion)
401
414
  this.liveRegion = null