@mirasen/chessboard 1.2.3 → 1.2.4
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/CHANGELOG.md +10 -0
- package/dist/extensions/first-party/annotations/interaction.js +4 -2
- package/dist/extensions/types/basic/events.d.ts +2 -0
- package/dist/extensions/types/basic/interaction.d.ts +1 -0
- package/dist/runtime/factory/input.js +6 -4
- package/dist/runtime/input/controller/factory.js +2 -2
- package/dist/runtime/input/controller/helpers.d.ts +1 -0
- package/dist/runtime/input/controller/helpers.js +16 -0
- package/dist/runtime/input/controller/pointer.js +28 -36
- package/dist/runtime/input/controller/types.d.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @mirasen/chessboard
|
|
2
2
|
|
|
3
|
+
## 1.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- eccb340: Fix fast drag-and-drop reliability in Chrome and other browsers that may dispatch `lostpointercapture` before or instead of `pointerup`.
|
|
8
|
+
|
|
9
|
+
Active drag gestures now remember the pointer button that started them. When that button is released during `lostpointercapture`, the board resolves the gesture through the same terminal release path as `pointerup` instead of cancelling it prematurely.
|
|
10
|
+
|
|
11
|
+
This fixes fast drops that could previously return a piece to its source square even when the target was valid, while preserving proper cleanup for fast releases outside the board and for extension-owned drag gestures such as annotations.
|
|
12
|
+
|
|
3
13
|
## 1.2.3
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -41,7 +41,8 @@ export function handleAnnotationsEvent(state, context) {
|
|
|
41
41
|
type: ANNOTATIONS_DRAW_DRAG_TYPE,
|
|
42
42
|
sourceSquare: targetSquare,
|
|
43
43
|
sourcePieceCode: null,
|
|
44
|
-
targetSquare: targetSquare
|
|
44
|
+
targetSquare: targetSquare,
|
|
45
|
+
startButton: rawEvent.button
|
|
45
46
|
});
|
|
46
47
|
if (success) {
|
|
47
48
|
rawEvent.preventDefault();
|
|
@@ -68,7 +69,8 @@ export function handleAnnotationsEvent(state, context) {
|
|
|
68
69
|
type: ANNOTATIONS_IDLE_CLEAR_DRAG_TYPE,
|
|
69
70
|
sourceSquare: targetSquare,
|
|
70
71
|
sourcePieceCode: null,
|
|
71
|
-
targetSquare: targetSquare
|
|
72
|
+
targetSquare: targetSquare,
|
|
73
|
+
startButton: rawEvent.button
|
|
72
74
|
});
|
|
73
75
|
if (success) {
|
|
74
76
|
rawEvent.preventDefault();
|
|
@@ -15,11 +15,13 @@ export type RuntimeInteractionActionStartLiftedDrag = {
|
|
|
15
15
|
type: 'startLiftedDrag';
|
|
16
16
|
source: Square;
|
|
17
17
|
target: Square;
|
|
18
|
+
startButton: number;
|
|
18
19
|
};
|
|
19
20
|
export type RuntimeInteractionActionStartReleaseTargetingDrag = {
|
|
20
21
|
type: 'startReleaseTargetingDrag';
|
|
21
22
|
source: Square;
|
|
22
23
|
target: Square;
|
|
24
|
+
startButton: number;
|
|
23
25
|
};
|
|
24
26
|
export type RuntimeInteractionActionCompleteCoreDrag = {
|
|
25
27
|
type: 'completeCoreDragTo';
|
|
@@ -4,6 +4,7 @@ export interface ExtensionDragSessionBase {
|
|
|
4
4
|
sourceSquare: Square | null;
|
|
5
5
|
sourcePieceCode: PieceCode | null;
|
|
6
6
|
targetSquare: Square | null;
|
|
7
|
+
startButton: number;
|
|
7
8
|
}
|
|
8
9
|
export interface ExtensionDragSessionLiftedPiece extends ExtensionDragSessionBase {
|
|
9
10
|
type: 'lifted-piece-drag';
|
|
@@ -18,7 +18,7 @@ export function createRuntimeInteractionSurface(state) {
|
|
|
18
18
|
const internalState = state();
|
|
19
19
|
return internalState.state.board.getPieceCodeAt(square);
|
|
20
20
|
},
|
|
21
|
-
startLiftedDrag(source, target) {
|
|
21
|
+
startLiftedDrag(source, target, startButton) {
|
|
22
22
|
const internalState = state();
|
|
23
23
|
const interactionMutationSession = internalState.mutation.getSession();
|
|
24
24
|
const interaction = internalState.state.interaction;
|
|
@@ -34,7 +34,8 @@ export function createRuntimeInteractionSurface(state) {
|
|
|
34
34
|
type: 'lifted-piece-drag',
|
|
35
35
|
sourceSquare: interactionSource.square,
|
|
36
36
|
sourcePieceCode: interactionSource.pieceCode,
|
|
37
|
-
targetSquare: target
|
|
37
|
+
targetSquare: target,
|
|
38
|
+
startButton
|
|
38
39
|
};
|
|
39
40
|
interaction.setDragSession(dragSession, interactionMutationSession);
|
|
40
41
|
runtimeRunMutationPipeline(internalState);
|
|
@@ -66,7 +67,7 @@ export function createRuntimeInteractionSurface(state) {
|
|
|
66
67
|
mutationSession.addMutation('runtime.interaction.completeExtensionDragTo', true, dragSession);
|
|
67
68
|
runtimeRunMutationPipeline(internalState);
|
|
68
69
|
},
|
|
69
|
-
startReleaseTargetingDrag(source, target) {
|
|
70
|
+
startReleaseTargetingDrag(source, target, startButton) {
|
|
70
71
|
const internalState = state();
|
|
71
72
|
const mutationSession = internalState.mutation.getSession();
|
|
72
73
|
const interaction = internalState.state.interaction;
|
|
@@ -77,7 +78,8 @@ export function createRuntimeInteractionSurface(state) {
|
|
|
77
78
|
type: 'release-targeting',
|
|
78
79
|
sourceSquare: source,
|
|
79
80
|
sourcePieceCode: interaction.selected.pieceCode,
|
|
80
|
-
targetSquare: target
|
|
81
|
+
targetSquare: target,
|
|
82
|
+
startButton
|
|
81
83
|
};
|
|
82
84
|
interaction.setDragSession(dragSession, mutationSession);
|
|
83
85
|
runtimeRunMutationPipeline(internalState);
|
|
@@ -22,10 +22,10 @@ export function createInteractionController(options) {
|
|
|
22
22
|
if (action) {
|
|
23
23
|
switch (action.type) {
|
|
24
24
|
case 'startLiftedDrag':
|
|
25
|
-
internalState.surface.startLiftedDrag(action.source, action.target);
|
|
25
|
+
internalState.surface.startLiftedDrag(action.source, action.target, action.startButton);
|
|
26
26
|
break;
|
|
27
27
|
case 'startReleaseTargetingDrag':
|
|
28
|
-
internalState.surface.startReleaseTargetingDrag(action.source, action.target);
|
|
28
|
+
internalState.surface.startReleaseTargetingDrag(action.source, action.target, action.startButton);
|
|
29
29
|
break;
|
|
30
30
|
case 'completeCoreDragTo':
|
|
31
31
|
internalState.surface.completeCoreDragTo(action.target);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { Square } from '../../../state/board/types/internal.js';
|
|
2
2
|
import type { InteractionStateSnapshot } from '../../../state/interaction/types/main.js';
|
|
3
|
+
export declare function buttonToButtonsMask(button: number): number | null;
|
|
3
4
|
export declare function canMoveTo(snapshot: InteractionStateSnapshot, target: Square): boolean;
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
export function buttonToButtonsMask(button) {
|
|
2
|
+
switch (button) {
|
|
3
|
+
case 0:
|
|
4
|
+
return 1;
|
|
5
|
+
case 1:
|
|
6
|
+
return 4;
|
|
7
|
+
case 2:
|
|
8
|
+
return 2;
|
|
9
|
+
case 3:
|
|
10
|
+
return 8;
|
|
11
|
+
case 4:
|
|
12
|
+
return 16;
|
|
13
|
+
default:
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
1
17
|
export function canMoveTo(snapshot, target) {
|
|
2
18
|
const { movability } = snapshot;
|
|
3
19
|
if (movability.mode === 0 /* MovabilityModeCode.Disabled */) {
|
|
@@ -2,7 +2,7 @@ import assert from '@ktarmyshov/assert';
|
|
|
2
2
|
import { isEmptyPieceCode, isNonEmptyPieceCode } from '../../../state/board/check.js';
|
|
3
3
|
import { fromPieceCode } from '../../../state/board/piece.js';
|
|
4
4
|
import { isDragSessionCoreOwned } from '../../../state/interaction/helpers.js';
|
|
5
|
-
import { canMoveTo } from './helpers.js';
|
|
5
|
+
import { buttonToButtonsMask, canMoveTo } from './helpers.js';
|
|
6
6
|
export function determineActionPointerDown(state, context) {
|
|
7
7
|
assert(context.rawEvent.type === 'pointerdown', 'determineActionPointerDown should only be called for pointerdown events');
|
|
8
8
|
const rawEvent = context.rawEvent;
|
|
@@ -31,7 +31,8 @@ export function determineActionPointerDown(state, context) {
|
|
|
31
31
|
return {
|
|
32
32
|
type: 'startReleaseTargetingDrag',
|
|
33
33
|
source: interaction.selected.square,
|
|
34
|
-
target: sceneEvent.targetSquare
|
|
34
|
+
target: sceneEvent.targetSquare,
|
|
35
|
+
startButton: rawEvent.button
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
const isLegalMoveTarget = interaction.movability.mode === 1 /* MovabilityModeCode.Free */ ||
|
|
@@ -42,7 +43,8 @@ export function determineActionPointerDown(state, context) {
|
|
|
42
43
|
return {
|
|
43
44
|
type: 'startReleaseTargetingDrag',
|
|
44
45
|
source: interaction.selected.square,
|
|
45
|
-
target: sceneEvent.targetSquare
|
|
46
|
+
target: sceneEvent.targetSquare,
|
|
47
|
+
startButton: rawEvent.button
|
|
46
48
|
};
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -55,7 +57,8 @@ export function determineActionPointerDown(state, context) {
|
|
|
55
57
|
return {
|
|
56
58
|
type: 'startLiftedDrag',
|
|
57
59
|
source: sceneEvent.targetSquare,
|
|
58
|
-
target: sceneEvent.targetSquare
|
|
60
|
+
target: sceneEvent.targetSquare,
|
|
61
|
+
startButton: rawEvent.button
|
|
59
62
|
};
|
|
60
63
|
}
|
|
61
64
|
}
|
|
@@ -72,59 +75,43 @@ export function determineActionPointerMove(state, context) {
|
|
|
72
75
|
}
|
|
73
76
|
return null;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
assert(context.rawEvent.type === 'pointerup', 'determineActionPointerUp should only be called for pointerup events');
|
|
78
|
+
function determineActionTerminalRelease(state, context) {
|
|
77
79
|
const interaction = state.surface.getInteractionStateSnapshot();
|
|
78
80
|
const dragSession = interaction.dragSession;
|
|
79
81
|
if (!dragSession) {
|
|
80
|
-
// No active drag session, so nothing to do on pointer up
|
|
81
82
|
return null;
|
|
82
83
|
}
|
|
83
84
|
const sceneEvent = context.sceneEvent;
|
|
84
|
-
assert(sceneEvent, 'Scene event should be present for
|
|
85
|
+
assert(sceneEvent, 'Scene event should be present for terminal release');
|
|
85
86
|
if (!isDragSessionCoreOwned(dragSession)) {
|
|
86
|
-
// For extension-owned drag sessions, we simply end the session without attempting to make a move,
|
|
87
|
-
// since the runtime doesn't have enough information about the semantics of the drag session.
|
|
88
87
|
return {
|
|
89
88
|
type: 'completeExtensionDrag',
|
|
90
89
|
target: sceneEvent.targetSquare
|
|
91
90
|
};
|
|
92
91
|
}
|
|
93
|
-
// Check if the square is the same as the source square of the drag session.
|
|
94
|
-
// If it is, then we can end the drag session without making a move.
|
|
95
92
|
if (sceneEvent.targetSquare === dragSession.sourceSquare) {
|
|
96
|
-
return {
|
|
97
|
-
type: 'cancelActiveInteraction'
|
|
98
|
-
};
|
|
93
|
+
return { type: 'cancelActiveInteraction' };
|
|
99
94
|
}
|
|
100
|
-
// Check if the target square is a valid destination for the selected piece.
|
|
101
95
|
if (sceneEvent.targetSquare !== null && canMoveTo(interaction, sceneEvent.targetSquare)) {
|
|
102
|
-
return {
|
|
103
|
-
type: 'completeCoreDragTo',
|
|
104
|
-
target: sceneEvent.targetSquare
|
|
105
|
-
};
|
|
96
|
+
return { type: 'completeCoreDragTo', target: sceneEvent.targetSquare };
|
|
106
97
|
}
|
|
107
|
-
// Invalid target: piece returns to source for lifted drag (selection preserved),
|
|
108
|
-
// or selection is cleared for release targeting.
|
|
109
98
|
if (dragSession.type === 'lifted-piece-drag') {
|
|
110
|
-
return {
|
|
111
|
-
type: 'cancelActiveInteraction'
|
|
112
|
-
};
|
|
99
|
+
return { type: 'cancelActiveInteraction' };
|
|
113
100
|
}
|
|
114
101
|
else {
|
|
115
|
-
return {
|
|
116
|
-
type: 'cancelInteraction'
|
|
117
|
-
};
|
|
102
|
+
return { type: 'cancelInteraction' };
|
|
118
103
|
}
|
|
119
104
|
}
|
|
105
|
+
export function determineActionPointerUp(state, context) {
|
|
106
|
+
assert(context.rawEvent.type === 'pointerup', 'determineActionPointerUp should only be called for pointerup events');
|
|
107
|
+
return determineActionTerminalRelease(state, context);
|
|
108
|
+
}
|
|
120
109
|
export function determineActionPointerCancel(state, context) {
|
|
121
110
|
assert(context.rawEvent.type === 'pointercancel', 'determineActionPointerCancel should only be called for pointercancel events');
|
|
122
111
|
const interaction = state.surface.getInteractionStateSnapshot();
|
|
123
112
|
if (!interaction.dragSession) {
|
|
124
|
-
// No active drag session, so nothing to do on pointer cancel
|
|
125
113
|
return null;
|
|
126
114
|
}
|
|
127
|
-
// Cancel the active interaction on pointer cancel
|
|
128
115
|
return {
|
|
129
116
|
type: 'cancelActiveInteraction'
|
|
130
117
|
};
|
|
@@ -132,12 +119,17 @@ export function determineActionPointerCancel(state, context) {
|
|
|
132
119
|
export function determineActionLostPointerCapture(state, context) {
|
|
133
120
|
assert(context.rawEvent.type === 'lostpointercapture', 'determineActionLostPointerCapture should only be called for lostpointercapture events');
|
|
134
121
|
const interaction = state.surface.getInteractionStateSnapshot();
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
const dragSession = interaction.dragSession;
|
|
123
|
+
if (!dragSession) {
|
|
137
124
|
return null;
|
|
138
125
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
126
|
+
const rawEvent = context.rawEvent;
|
|
127
|
+
const mask = buttonToButtonsMask(dragSession.startButton);
|
|
128
|
+
if (mask === null) {
|
|
129
|
+
return { type: 'cancelActiveInteraction' };
|
|
130
|
+
}
|
|
131
|
+
if ((rawEvent.buttons & mask) === 0) {
|
|
132
|
+
return determineActionTerminalRelease(state, context);
|
|
133
|
+
}
|
|
134
|
+
return { type: 'cancelActiveInteraction' };
|
|
143
135
|
}
|
|
@@ -5,8 +5,8 @@ import type { InteractionStateSnapshot } from '../../../state/interaction/types/
|
|
|
5
5
|
export interface RuntimeInteractionSurface {
|
|
6
6
|
getInteractionStateSnapshot(): InteractionStateSnapshot;
|
|
7
7
|
getPieceCodeAt(square: Square): PieceCode;
|
|
8
|
-
startLiftedDrag(source: Square, target: Square): void;
|
|
9
|
-
startReleaseTargetingDrag(source: Square, target: Square): void;
|
|
8
|
+
startLiftedDrag(source: Square, target: Square, startButton: number): void;
|
|
9
|
+
startReleaseTargetingDrag(source: Square, target: Square, startButton: number): void;
|
|
10
10
|
completeCoreDragTo(target: Square): void;
|
|
11
11
|
completeExtensionDrag(target: Square | null): void;
|
|
12
12
|
updateDragSessionCurrentTarget(target: Square | null): void;
|
package/package.json
CHANGED