@mirasen/chessboard 1.2.2 → 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 +20 -0
- package/README.md +20 -1
- package/dist/extensions/first-party/annotations/interaction.js +4 -2
- package/dist/extensions/first-party/legal-moves/factory.js +1 -1
- package/dist/extensions/types/basic/events.d.ts +7 -2
- package/dist/extensions/types/basic/events.js +5 -3
- package/dist/extensions/types/basic/interaction.d.ts +1 -0
- package/dist/runtime/factory/input.js +6 -4
- package/dist/runtime/factory/main.js +0 -5
- package/dist/runtime/input/adapter/pointer.d.ts +1 -0
- package/dist/runtime/input/adapter/pointer.js +9 -0
- package/dist/runtime/input/adapter/types.js +2 -1
- 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/interaction.js +3 -1
- package/dist/runtime/input/controller/pointer.d.ts +1 -0
- package/dist/runtime/input/controller/pointer.js +34 -30
- package/dist/runtime/input/controller/types.d.ts +2 -2
- package/package.json +8 -3
- package/dist/runtime/mutation/invalidation.d.ts +0 -1
- package/dist/runtime/mutation/invalidation.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
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
|
+
|
|
13
|
+
## 1.2.3
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- d8df60e: Fix stale lifted-piece drag cleanup when pointer capture is lost.
|
|
18
|
+
|
|
19
|
+
Fast drag gestures released outside the board could leave an active lifted-piece drag session alive if the browser delivered `lostpointercapture` instead of a normal `pointerup`. The input adapter now handles `lostpointercapture`, clears stale pointer tracking, and routes the event through the interaction controller so active drag sessions are cancelled cleanly.
|
|
20
|
+
|
|
21
|
+
This prevents dragged pieces from remaining visually attached to the pointer after an outside-board release.
|
|
22
|
+
|
|
3
23
|
## 1.2.2
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -319,16 +319,35 @@ board = createBoard({
|
|
|
319
319
|
|
|
320
320
|
Configured built-in extensions can be provided either as declarative `{ builtin, options }` objects or through the built-in extension factory map. If you use custom piece assets, pass the same `pieceUrls` map to renderer and promotion when you want the board and promotion UI to use the same piece set.
|
|
321
321
|
|
|
322
|
-
### Default visual identity
|
|
322
|
+
### Default visual identity and attribution
|
|
323
323
|
|
|
324
324
|
The default first-party extension baseline includes a subtle Mirasen board watermark.
|
|
325
325
|
|
|
326
326
|
If you want full control over the visual baseline, use an explicit extension list and omit the `watermark` extension.
|
|
327
327
|
|
|
328
|
+
Attribution is not required by the MIT license, but it is appreciated. If Mirasen Chessboard helps your project, please consider keeping the default watermark or linking to `@mirasen/chessboard`.
|
|
329
|
+
|
|
328
330
|
## Documentation
|
|
329
331
|
|
|
330
332
|
See [Documentation](./documentation.md) for the documentation hub.
|
|
331
333
|
|
|
334
|
+
## Versioning and breaking changes
|
|
335
|
+
|
|
336
|
+
`@mirasen/chessboard` follows semantic versioning for public API changes.
|
|
337
|
+
|
|
338
|
+
Breaking public API changes are released as major versions.
|
|
339
|
+
|
|
340
|
+
We do not add backward-compatibility aliases or runtime compatibility bridges by default. When a public contract changes, the new API is kept explicit and clean, and the required migration is documented in the release notes / changelog.
|
|
341
|
+
|
|
342
|
+
Migration notes for breaking changes are included directly in the corresponding changelog entry, so users can see:
|
|
343
|
+
|
|
344
|
+
- what changed
|
|
345
|
+
- why it changed
|
|
346
|
+
- what old usage looked like
|
|
347
|
+
- what new usage should look like
|
|
348
|
+
|
|
349
|
+
See the [CHANGELOG](./CHANGELOG.md) for release history and migration notes.
|
|
350
|
+
|
|
332
351
|
## Architecture
|
|
333
352
|
|
|
334
353
|
`@mirasen/chessboard` is built around explicit boundaries:
|
|
@@ -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();
|
|
@@ -68,7 +68,7 @@ function createLegalMovesInstance(options, config) {
|
|
|
68
68
|
'stroke-opacity': internalState.config.captureTarget.color.opacity.toString(),
|
|
69
69
|
'stroke-width': (geometry.squareSize * internalState.config.captureTarget.strokeWidthRatio).toString(),
|
|
70
70
|
fill: 'none'
|
|
71
|
-
};
|
|
71
|
+
};
|
|
72
72
|
for (const [square, destination] of interaction.activeDestinations) {
|
|
73
73
|
const rect = geometry.getSquareRect(square);
|
|
74
74
|
const circleX = rect.x + geometry.squareSize / 2;
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import type { Square } from '../../../state/board/types/internal.js';
|
|
2
2
|
import type { ScenePoint } from '../basic/transient-visuals.js';
|
|
3
|
+
declare const ALL_SCENE_POINTER_EVENT_TYPES_ARRAY: readonly ["pointercancel", "pointerdown", "pointerenter", "pointerleave", "pointermove", "pointerout", "pointerover", "pointerrawupdate", "pointerup", "contextmenu", "lostpointercapture"];
|
|
4
|
+
export type ScenePointerEventType = (typeof ALL_SCENE_POINTER_EVENT_TYPES_ARRAY)[number];
|
|
5
|
+
export declare const ALL_SCENE_POINTER_EVENT_TYPES: ReadonlySet<string>;
|
|
3
6
|
export interface ScenePointerEvent {
|
|
4
|
-
readonly type:
|
|
7
|
+
readonly type: ScenePointerEventType;
|
|
5
8
|
readonly point: ScenePoint;
|
|
6
9
|
readonly clampedPoint: ScenePoint;
|
|
7
10
|
readonly boardClampedPoint: ScenePoint | null;
|
|
8
11
|
readonly targetSquare: Square | null;
|
|
9
12
|
}
|
|
10
|
-
export declare const ALL_SCENE_POINTER_EVENT_TYPES: ReadonlySet<string>;
|
|
11
13
|
export type SceneEvent = ScenePointerEvent;
|
|
12
14
|
export type RuntimeInteractionActionStartLiftedDrag = {
|
|
13
15
|
type: 'startLiftedDrag';
|
|
14
16
|
source: Square;
|
|
15
17
|
target: Square;
|
|
18
|
+
startButton: number;
|
|
16
19
|
};
|
|
17
20
|
export type RuntimeInteractionActionStartReleaseTargetingDrag = {
|
|
18
21
|
type: 'startReleaseTargetingDrag';
|
|
19
22
|
source: Square;
|
|
20
23
|
target: Square;
|
|
24
|
+
startButton: number;
|
|
21
25
|
};
|
|
22
26
|
export type RuntimeInteractionActionCompleteCoreDrag = {
|
|
23
27
|
type: 'completeCoreDragTo';
|
|
@@ -38,3 +42,4 @@ export type RuntimeInteractionActionCancelInteraction = {
|
|
|
38
42
|
type: 'cancelInteraction';
|
|
39
43
|
};
|
|
40
44
|
export type RuntimeInteractionAction = RuntimeInteractionActionStartLiftedDrag | RuntimeInteractionActionStartReleaseTargetingDrag | RuntimeInteractionActionCompleteCoreDrag | RuntimeInteractionActionCompleteExtensionDrag | RuntimeInteractionActionUpdateDragSessionCurrentTarget | RuntimeInteractionActionCancelActiveInteraction | RuntimeInteractionActionCancelInteraction;
|
|
45
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const ALL_SCENE_POINTER_EVENT_TYPES_ARRAY = [
|
|
2
2
|
'pointercancel',
|
|
3
3
|
'pointerdown',
|
|
4
4
|
'pointerenter',
|
|
@@ -8,5 +8,7 @@ export const ALL_SCENE_POINTER_EVENT_TYPES = new Set([
|
|
|
8
8
|
'pointerover',
|
|
9
9
|
'pointerrawupdate',
|
|
10
10
|
'pointerup',
|
|
11
|
-
'contextmenu'
|
|
12
|
-
|
|
11
|
+
'contextmenu',
|
|
12
|
+
'lostpointercapture'
|
|
13
|
+
];
|
|
14
|
+
export const ALL_SCENE_POINTER_EVENT_TYPES = new Set(ALL_SCENE_POINTER_EVENT_TYPES_ARRAY);
|
|
@@ -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);
|
|
@@ -195,11 +195,6 @@ export function createRuntime(options) {
|
|
|
195
195
|
internalState = createRuntimeInternal(optionsInternal);
|
|
196
196
|
// Initial creation, so we mark board position as mutated to trigger mutation pipeline
|
|
197
197
|
internalState.mutation.addMutation('state.board.setPosition', true);
|
|
198
|
-
// We will have two different interfaces here
|
|
199
|
-
// One is returned, one is for controller - with different methods
|
|
200
|
-
// At the moment our assumption that controller will use Runtime but not opposite direction
|
|
201
|
-
// So we can construct these two objects separately so they would not be huge!
|
|
202
|
-
// TODO: const inputControllerSurface = createRuntimeInputControllerSurface(internalState);
|
|
203
198
|
// Initial run to process initial mutations and set up previousContext for the next runs
|
|
204
199
|
internalStatus = 'unmounted';
|
|
205
200
|
internalState.mutation.run(internalState);
|
|
@@ -6,4 +6,5 @@ export declare function releaseCapture(state: InputAdapterInternal): void;
|
|
|
6
6
|
export declare function adapterOnPointerDown(state: InputAdapterInternal, e: PointerEvent): void;
|
|
7
7
|
export declare function adapterOnPointerUp(state: InputAdapterInternal, e: PointerEvent): void;
|
|
8
8
|
export declare function adapterOnPointerCancel(state: InputAdapterInternal, e: PointerEvent): void;
|
|
9
|
+
export declare function adapterOnLostPointerCapture(state: InputAdapterInternal, e: PointerEvent): void;
|
|
9
10
|
export declare function pointerEventDestroy(state: InputAdapterInternal): void;
|
|
@@ -24,6 +24,9 @@ function pointerEventAdapterHandler(state, e) {
|
|
|
24
24
|
case 'pointercancel':
|
|
25
25
|
adapterOnPointerCancel(state, e);
|
|
26
26
|
break;
|
|
27
|
+
case 'lostpointercapture':
|
|
28
|
+
adapterOnLostPointerCapture(state, e);
|
|
29
|
+
break;
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
/** Release capture for the currently tracked pointer and clear tracking state. */
|
|
@@ -56,6 +59,12 @@ export function adapterOnPointerCancel(state, e) {
|
|
|
56
59
|
return;
|
|
57
60
|
releaseCapture(state);
|
|
58
61
|
}
|
|
62
|
+
export function adapterOnLostPointerCapture(state, e) {
|
|
63
|
+
assert(e.type === 'lostpointercapture', `Expected lostpointercapture event, got ${e.type}`);
|
|
64
|
+
if (e.pointerId !== state.activePointerId)
|
|
65
|
+
return;
|
|
66
|
+
releaseCapture(state);
|
|
67
|
+
}
|
|
59
68
|
export function pointerEventDestroy(state) {
|
|
60
69
|
if (state.activePointerId !== null) {
|
|
61
70
|
releaseCapture(state);
|
|
@@ -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 */) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { determineActionPointerCancel, determineActionPointerDown, determineActionPointerMove, determineActionPointerUp } from './pointer.js';
|
|
1
|
+
import { determineActionLostPointerCapture, determineActionPointerCancel, determineActionPointerDown, determineActionPointerMove, determineActionPointerUp } from './pointer.js';
|
|
2
2
|
export function determineRuntimeInteractionAction(state, context) {
|
|
3
3
|
switch (context.rawEvent.type) {
|
|
4
4
|
case 'pointerdown':
|
|
@@ -9,6 +9,8 @@ export function determineRuntimeInteractionAction(state, context) {
|
|
|
9
9
|
return determineActionPointerUp(state, context);
|
|
10
10
|
case 'pointercancel':
|
|
11
11
|
return determineActionPointerCancel(state, context);
|
|
12
|
+
case 'lostpointercapture':
|
|
13
|
+
return determineActionLostPointerCapture(state, context);
|
|
12
14
|
}
|
|
13
15
|
return null;
|
|
14
16
|
}
|
|
@@ -4,3 +4,4 @@ export declare function determineActionPointerDown(state: InteractionControllerI
|
|
|
4
4
|
export declare function determineActionPointerMove(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
|
|
5
5
|
export declare function determineActionPointerUp(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
|
|
6
6
|
export declare function determineActionPointerCancel(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
|
|
7
|
+
export declare function determineActionLostPointerCapture(state: InteractionControllerInternal, context: InteractionControllerOnEventContext): RuntimeInteractionAction | null;
|
|
@@ -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,60 +75,61 @@ 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
|
};
|
|
131
118
|
}
|
|
119
|
+
export function determineActionLostPointerCapture(state, context) {
|
|
120
|
+
assert(context.rawEvent.type === 'lostpointercapture', 'determineActionLostPointerCapture should only be called for lostpointercapture events');
|
|
121
|
+
const interaction = state.surface.getInteractionStateSnapshot();
|
|
122
|
+
const dragSession = interaction.dragSession;
|
|
123
|
+
if (!dragSession) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
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' };
|
|
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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirasen/chessboard",
|
|
3
3
|
"description": "Framework-agnostic TypeScript chessboard platform with real chess interaction built in.",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.4",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Mirasen",
|
|
@@ -16,8 +16,13 @@
|
|
|
16
16
|
"typescript",
|
|
17
17
|
"framework-agnostic",
|
|
18
18
|
"svg",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
"drag-and-drop",
|
|
20
|
+
"click-to-move",
|
|
21
|
+
"animation",
|
|
22
|
+
"annotations",
|
|
23
|
+
"chessjs",
|
|
24
|
+
"legal-moves",
|
|
25
|
+
"touch"
|
|
21
26
|
],
|
|
22
27
|
"homepage": "https://mirasen.io/chessboard",
|
|
23
28
|
"repository": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|