@mirasen/chessboard 0.1.1 → 1.0.0
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 +24 -0
- package/README.md +240 -45
- package/dist/animation/planner.d.ts +11 -6
- package/dist/animation/planner.js +164 -45
- package/dist/animation/render/fade.d.ts +2 -2
- package/dist/animation/render/fade.js +3 -3
- package/dist/animation/render/move.d.ts +3 -3
- package/dist/animation/render/move.js +5 -5
- package/dist/animation/render/plan.d.ts +3 -3
- package/dist/animation/render/static.d.ts +2 -2
- package/dist/animation/render/static.js +3 -3
- package/dist/animation/types.d.ts +14 -15
- package/dist/animation/types.js +1 -6
- package/dist/extensions/build/index.d.ts +21 -0
- package/dist/extensions/build/index.js +9 -0
- package/dist/extensions/factory/main.js +23 -2
- package/dist/extensions/factory/runtime.d.ts +2 -1
- package/dist/extensions/factory/runtime.js +48 -4
- package/dist/extensions/first-party/active-target/factory.js +9 -9
- package/dist/extensions/first-party/active-target/types.d.ts +2 -2
- package/dist/extensions/first-party/active-target/types.js +4 -1
- package/dist/extensions/first-party/auto-promote/factory.d.ts +2 -0
- package/dist/extensions/first-party/auto-promote/factory.js +51 -0
- package/dist/extensions/first-party/auto-promote/types.d.ts +12 -0
- package/dist/extensions/first-party/auto-promote/types.js +2 -0
- package/dist/extensions/first-party/board-events/factory.js +1 -16
- package/dist/extensions/first-party/board-events/types.d.ts +1 -4
- package/dist/extensions/first-party/common/helpers.d.ts +7 -7
- package/dist/extensions/first-party/common/helpers.js +11 -11
- package/dist/extensions/first-party/common/types.d.ts +2 -3
- package/dist/extensions/first-party/last-move/factory.js +10 -10
- package/dist/extensions/first-party/last-move/types.d.ts +2 -2
- package/dist/extensions/first-party/legal-moves/factory.js +5 -5
- package/dist/extensions/first-party/legal-moves/types.d.ts +2 -2
- package/dist/extensions/first-party/main-renderer/animation/render.js +1 -2
- package/dist/extensions/first-party/main-renderer/animation/update.js +9 -23
- package/dist/extensions/first-party/main-renderer/board/render.js +3 -3
- package/dist/extensions/first-party/main-renderer/board/update.js +5 -3
- package/dist/extensions/first-party/main-renderer/coordinates/render.js +4 -4
- package/dist/extensions/first-party/main-renderer/drag/render.js +1 -1
- package/dist/extensions/first-party/main-renderer/factory.js +4 -4
- package/dist/extensions/first-party/main-renderer/normalize.d.ts +2 -3
- package/dist/extensions/first-party/main-renderer/normalize.js +1 -1
- package/dist/extensions/first-party/main-renderer/pieces/render.js +5 -5
- package/dist/extensions/first-party/main-renderer/pieces/suppress.js +7 -2
- package/dist/extensions/first-party/main-renderer/types/extension.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/types/extension.js +1 -1
- package/dist/extensions/first-party/main-renderer/types/instance.d.ts +2 -2
- package/dist/extensions/first-party/promotion/factory.d.ts +3 -0
- package/dist/extensions/first-party/promotion/factory.js +219 -0
- package/dist/extensions/first-party/promotion/normalize.d.ts +3 -0
- package/dist/extensions/first-party/promotion/normalize.js +37 -0
- package/dist/extensions/first-party/promotion/types/input.d.ts +10 -0
- package/dist/extensions/first-party/promotion/types/internal.d.ts +12 -0
- package/dist/extensions/first-party/promotion/types/internal.js +22 -0
- package/dist/extensions/first-party/promotion/types/main.d.ts +24 -0
- package/dist/extensions/first-party/promotion/types/main.js +2 -0
- package/dist/extensions/first-party/selected-square/factory.js +7 -7
- package/dist/extensions/first-party/selected-square/types.d.ts +2 -2
- package/dist/extensions/index.d.ts +1 -8
- package/dist/extensions/index.js +1 -6
- package/dist/extensions/types/basic/events.d.ts +8 -31
- package/dist/extensions/types/basic/events.js +9 -5
- package/dist/extensions/types/basic/interaction.d.ts +21 -0
- package/dist/extensions/types/basic/transient-visuals.d.ts +2 -1
- package/dist/extensions/types/context/events.d.ts +5 -0
- package/dist/extensions/types/context/events.js +1 -0
- package/dist/extensions/types/context/ui-move.d.ts +3 -15
- package/dist/extensions/types/extension.d.ts +10 -8
- package/dist/extensions/types/main.d.ts +11 -6
- package/dist/extensions/types/surface/commands.d.ts +9 -0
- package/dist/extensions/types/surface/events.d.ts +2 -3
- package/dist/extensions/types/wrapper.d.ts +23 -22
- package/dist/extensions/types/wrapper.js +34 -6
- package/dist/extensions/update.d.ts +2 -2
- package/dist/extensions/update.js +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/layout/factory.js +3 -3
- package/dist/layout/geometry/factory.d.ts +2 -11
- package/dist/layout/geometry/factory.js +16 -17
- package/dist/layout/geometry/helpers.d.ts +5 -0
- package/dist/layout/geometry/helpers.js +33 -0
- package/dist/layout/geometry/types.d.ts +13 -12
- package/dist/layout/helpers.d.ts +3 -4
- package/dist/layout/helpers.js +5 -5
- package/dist/layout/reducers.js +9 -8
- package/dist/layout/types.d.ts +5 -5
- package/dist/render/mount.js +2 -2
- package/dist/render/rendering/state.js +7 -7
- package/dist/runtime/factory/events.d.ts +3 -0
- package/dist/runtime/factory/events.js +18 -0
- package/dist/runtime/factory/helpers.js +12 -12
- package/dist/runtime/factory/input.js +17 -10
- package/dist/runtime/factory/main.js +44 -4
- package/dist/runtime/input/adapter/event.d.ts +3 -0
- package/dist/runtime/input/adapter/event.js +19 -0
- package/dist/runtime/input/adapter/factory.js +19 -38
- package/dist/runtime/input/adapter/helpers.d.ts +3 -5
- package/dist/runtime/input/adapter/helpers.js +60 -12
- package/dist/runtime/input/adapter/pointer.d.ts +6 -10
- package/dist/runtime/input/adapter/pointer.js +37 -70
- package/dist/runtime/input/adapter/types.d.ts +6 -3
- package/dist/runtime/input/adapter/types.js +6 -1
- package/dist/runtime/input/controller/factory.js +12 -11
- package/dist/runtime/input/controller/pointer.d.ts +5 -6
- package/dist/runtime/input/controller/pointer.js +36 -35
- package/dist/runtime/input/controller/transient-visuals.d.ts +2 -2
- package/dist/runtime/input/controller/transient-visuals.js +12 -5
- package/dist/runtime/input/controller/types.d.ts +5 -4
- package/dist/runtime/lifecycle.js +6 -6
- package/dist/runtime/mutation/interaction.js +1 -5
- package/dist/runtime/mutation/types.d.ts +10 -8
- package/dist/runtime/types/main.d.ts +2 -0
- package/dist/state/change/{factory.d.ts → factory/main.d.ts} +1 -1
- package/dist/state/change/{factory.js → factory/main.js} +8 -5
- package/dist/state/change/factory/ui-move.d.ts +4 -0
- package/dist/{runtime → state/change}/factory/ui-move.js +9 -9
- package/dist/state/change/helpers.d.ts +4 -2
- package/dist/state/change/helpers.js +11 -1
- package/dist/state/change/mutation.d.ts +1 -1
- package/dist/state/change/reducers.d.ts +3 -3
- package/dist/state/change/reducers.js +4 -4
- package/dist/state/change/types/main.d.ts +18 -0
- package/dist/state/change/types/main.js +1 -0
- package/dist/state/change/types/ui-move.d.ts +25 -0
- package/dist/state/change/types/ui-move.js +1 -0
- package/dist/state/factory.js +1 -1
- package/dist/state/interaction/factory.js +4 -2
- package/dist/state/interaction/helpers.d.ts +2 -0
- package/dist/state/interaction/helpers.js +3 -0
- package/dist/state/interaction/types/internal.d.ts +9 -1
- package/dist/state/types.d.ts +1 -1
- package/dist/wrapper/factory.js +2 -26
- package/package.json +17 -11
- package/dist/runtime/factory/ui-move.d.ts +0 -4
- package/dist/runtime/types/ui-move.d.ts +0 -9
- package/dist/state/change/types.d.ts +0 -17
- /package/dist/{runtime/types/ui-move.js → extensions/first-party/promotion/types/input.js} +0 -0
- /package/dist/{state/change/types.js → extensions/types/basic/interaction.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
|
|
2
2
|
export function prepareFadeTrack(track, geometry, pieceUrls, layer) {
|
|
3
|
-
const r = geometry.
|
|
3
|
+
const r = geometry.getSquareRect(track.sq);
|
|
4
4
|
const url = pieceUrls[track.pieceCode];
|
|
5
5
|
const initialOpacity = track.effect === 'fade-in' ? '0' : '1';
|
|
6
6
|
const root = createSvgElement(layer, 'image', {
|
|
@@ -9,8 +9,8 @@ export function prepareFadeTrack(track, geometry, pieceUrls, layer) {
|
|
|
9
9
|
href: url,
|
|
10
10
|
x: r.x.toString(),
|
|
11
11
|
y: r.y.toString(),
|
|
12
|
-
width: r.
|
|
13
|
-
height: r.
|
|
12
|
+
width: r.width.toString(),
|
|
13
|
+
height: r.height.toString(),
|
|
14
14
|
opacity: initialOpacity
|
|
15
15
|
});
|
|
16
16
|
return { ...track, root };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
|
|
3
3
|
import type { AnimationTrackMove } from '../types.js';
|
|
4
4
|
import type { PreparedMoveNode } from './types.js';
|
|
5
|
-
export declare function prepareMoveTrack(track: AnimationTrackMove, geometry:
|
|
6
|
-
export declare function renderMoveTrack(node: PreparedMoveNode, geometry:
|
|
5
|
+
export declare function prepareMoveTrack(track: AnimationTrackMove, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedMoveNode;
|
|
6
|
+
export declare function renderMoveTrack(node: PreparedMoveNode, geometry: SceneRenderGeometry, progress: number): void;
|
|
7
7
|
export declare function cleanMoveTrack(node: PreparedMoveNode): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
|
|
2
2
|
export function prepareMoveTrack(track, geometry, pieceUrls, layer) {
|
|
3
|
-
const r = geometry.
|
|
3
|
+
const r = geometry.getSquareRect(track.fromSq);
|
|
4
4
|
const url = pieceUrls[track.pieceCode];
|
|
5
5
|
const root = createSvgElement(layer, 'image', {
|
|
6
6
|
'data-chessboard-id': `animation-move-${track.id}`,
|
|
@@ -8,14 +8,14 @@ export function prepareMoveTrack(track, geometry, pieceUrls, layer) {
|
|
|
8
8
|
href: url,
|
|
9
9
|
x: r.x.toString(),
|
|
10
10
|
y: r.y.toString(),
|
|
11
|
-
width: r.
|
|
12
|
-
height: r.
|
|
11
|
+
width: r.width.toString(),
|
|
12
|
+
height: r.height.toString()
|
|
13
13
|
});
|
|
14
14
|
return { ...track, root };
|
|
15
15
|
}
|
|
16
16
|
export function renderMoveTrack(node, geometry, progress) {
|
|
17
|
-
const from = geometry.
|
|
18
|
-
const to = geometry.
|
|
17
|
+
const from = geometry.getSquareRect(node.fromSq);
|
|
18
|
+
const to = geometry.getSquareRect(node.toSq);
|
|
19
19
|
const x = from.x + (to.x - from.x) * progress;
|
|
20
20
|
const y = from.y + (to.y - from.y) * progress;
|
|
21
21
|
updateElementAttributes(node.root, {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
|
|
3
3
|
import type { AnimationPlan } from '../types.js';
|
|
4
4
|
import type { PreparedNodeMap } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Prepare all tracks in the plan — creates SVG nodes in `layer` for each track.
|
|
7
7
|
* Returns a map keyed by track id for use in render/clean passes.
|
|
8
8
|
*/
|
|
9
|
-
export declare function prepareAnimationPlan(plan: AnimationPlan, geometry:
|
|
10
|
-
export declare function renderAnimationPlan(nodes: PreparedNodeMap, geometry:
|
|
9
|
+
export declare function prepareAnimationPlan(plan: AnimationPlan, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedNodeMap;
|
|
10
|
+
export declare function renderAnimationPlan(nodes: PreparedNodeMap, geometry: SceneRenderGeometry, progress: number): void;
|
|
11
11
|
/**
|
|
12
12
|
* Clean up all track nodes — removes SVG elements from the DOM.
|
|
13
13
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PieceUrls } from '../../extensions/first-party/main-renderer/types/internal.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SceneRenderGeometry } from '../../layout/geometry/types.js';
|
|
3
3
|
import type { AnimationTrackStatic } from '../types.js';
|
|
4
4
|
import type { PreparedStaticNode } from './types.js';
|
|
5
|
-
export declare function prepareStaticTrack(track: AnimationTrackStatic, geometry:
|
|
5
|
+
export declare function prepareStaticTrack(track: AnimationTrackStatic, geometry: SceneRenderGeometry, pieceUrls: PieceUrls, layer: SVGElement): PreparedStaticNode;
|
|
6
6
|
export declare function renderStaticTrack(_node: PreparedStaticNode): void;
|
|
7
7
|
export declare function cleanStaticTrack(node: PreparedStaticNode): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSvgElement } from '../../render/svg/helpers.js';
|
|
2
2
|
export function prepareStaticTrack(track, geometry, pieceUrls, layer) {
|
|
3
|
-
const r = geometry.
|
|
3
|
+
const r = geometry.getSquareRect(track.sq);
|
|
4
4
|
const url = pieceUrls[track.pieceCode];
|
|
5
5
|
const root = createSvgElement(layer, 'image', {
|
|
6
6
|
'data-chessboard-id': `animation-static-${track.id}`,
|
|
@@ -8,8 +8,8 @@ export function prepareStaticTrack(track, geometry, pieceUrls, layer) {
|
|
|
8
8
|
href: url,
|
|
9
9
|
x: r.x.toString(),
|
|
10
10
|
y: r.y.toString(),
|
|
11
|
-
width: r.
|
|
12
|
-
height: r.
|
|
11
|
+
width: r.width.toString(),
|
|
12
|
+
height: r.height.toString()
|
|
13
13
|
});
|
|
14
14
|
return { ...track, root };
|
|
15
15
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ReadonlyDeep } from 'type-fest';
|
|
2
2
|
import type { NonEmptyPieceCode, Square } from '../state/board/types/internal.js';
|
|
3
|
+
import { BoardStateSnapshot } from '../state/board/types/main.js';
|
|
4
|
+
import { ChangeStateSnapshot } from '../state/change/types/main.js';
|
|
5
|
+
import { InteractionStateSnapshot } from '../state/interaction/types/main.js';
|
|
3
6
|
export interface AnimationTrackMove {
|
|
4
7
|
id: number;
|
|
5
8
|
pieceCode: NonEmptyPieceCode;
|
|
@@ -20,27 +23,23 @@ export interface AnimationTrackStatic {
|
|
|
20
23
|
effect: 'static';
|
|
21
24
|
}
|
|
22
25
|
export type AnimationTrack = AnimationTrackMove | AnimationTrackFade | AnimationTrackStatic;
|
|
23
|
-
export interface AnimationPlan {
|
|
24
|
-
sessionId: number;
|
|
25
|
-
tracks: AnimationTrack[];
|
|
26
|
-
}
|
|
27
26
|
export interface AnimationSession {
|
|
28
27
|
id: number;
|
|
29
|
-
|
|
28
|
+
plan: AnimationPlan;
|
|
30
29
|
startTime: number;
|
|
31
30
|
duration: number;
|
|
32
31
|
}
|
|
33
32
|
export type AnimationSessionSnapshot = ReadonlyDeep<AnimationSession>;
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
export interface AnimationPlanningSnapshot {
|
|
34
|
+
readonly board: BoardStateSnapshot;
|
|
35
|
+
readonly change: ChangeStateSnapshot;
|
|
36
|
+
readonly interaction: InteractionStateSnapshot;
|
|
37
37
|
}
|
|
38
|
-
export interface
|
|
39
|
-
|
|
38
|
+
export interface AnimationPlanningInput {
|
|
39
|
+
previous: AnimationPlanningSnapshot;
|
|
40
|
+
current: AnimationPlanningSnapshot;
|
|
40
41
|
}
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export interface CalculateAnimationTracksOptions {
|
|
45
|
-
exclude?: AnimationTrackExclude[];
|
|
42
|
+
export interface AnimationPlan {
|
|
43
|
+
readonly tracks: readonly AnimationTrack[];
|
|
44
|
+
readonly suppressedSquares: ReadonlySet<Square>;
|
|
46
45
|
}
|
package/dist/animation/types.js
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
|
|
2
|
+
export type { RuntimeReadonlyMutationSession } from '../../runtime/mutation/types.js';
|
|
3
|
+
export { isNonEmptyPieceCode } from '../../state/board/check.js';
|
|
4
|
+
export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
|
|
5
|
+
export { denormalizeMove } from '../../state/board/denormalize.js';
|
|
6
|
+
export { normalizePiece } from '../../state/board/normalize.js';
|
|
7
|
+
export { fromPieceCode, toPieceCode } from '../../state/board/piece.js';
|
|
8
|
+
export type { ColorCode, EmptyPieceCode, NonEmptyPieceCode, PieceCode, RoleCode, RolePromotionCode, Square, SquareFile, SquareRank } from '../../state/board/types/internal.js';
|
|
9
|
+
export type { MoveOutput } from '../../state/board/types/output.js';
|
|
10
|
+
export type { MovabilityModeCode } from '../../state/interaction/types/internal.js';
|
|
11
|
+
export { extensionCreateInternalBase, extensionDestroyBase, extensionIsDestroyedBase, extensionIsMountedBase, extensionMountBase, extensionUnmountBase } from '../first-party/common/helpers.js';
|
|
12
|
+
export type { ExtensionInternalBase, OpaqueColor } from '../first-party/common/types.js';
|
|
13
|
+
export type { PieceUrlsInput } from '../first-party/main-renderer/types/input.js';
|
|
14
|
+
export type { PieceUrls } from '../first-party/main-renderer/types/internal.js';
|
|
15
|
+
export type { PromotionPieceCode } from '../first-party/promotion/types/internal.js';
|
|
16
|
+
export type { ExtensionSlotName, ExtensionSlotSvgRoots } from '../types/basic/mount.js';
|
|
17
|
+
export { isFrameMounted, isFrameRenderable } from '../types/basic/update.js';
|
|
18
|
+
export type { UpdateFrameSnapshot } from '../types/basic/update.js';
|
|
19
|
+
export { isUpdateContextMounted, isUpdateContextRenderable } from '../types/context/update.js';
|
|
20
|
+
export type { ExtensionCreateInstanceOptions, ExtensionDefinition, ExtensionInstance } from '../types/extension.js';
|
|
21
|
+
export type { ExtensionRuntimeSurface } from '../types/surface/main.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { clearElementChildren, createSvgElement, updateElementAttributes } from '../../render/svg/helpers.js';
|
|
2
|
+
export { isNonEmptyPieceCode } from '../../state/board/check.js';
|
|
3
|
+
export { fileOf, rankOf, squareOf } from '../../state/board/coords.js';
|
|
4
|
+
export { denormalizeMove } from '../../state/board/denormalize.js';
|
|
5
|
+
export { normalizePiece } from '../../state/board/normalize.js';
|
|
6
|
+
export { fromPieceCode, toPieceCode } from '../../state/board/piece.js';
|
|
7
|
+
export { extensionCreateInternalBase, extensionDestroyBase, extensionIsDestroyedBase, extensionIsMountedBase, extensionMountBase, extensionUnmountBase } from '../first-party/common/helpers.js';
|
|
8
|
+
export { isFrameMounted, isFrameRenderable } from '../types/basic/update.js';
|
|
9
|
+
export { isUpdateContextMounted, isUpdateContextRenderable } from '../types/context/update.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import assert from '@ktarmyshov/assert';
|
|
1
2
|
import { createExtensionAnimationController } from '../animation/factory.js';
|
|
2
3
|
import { createExtensionInvalidationState } from '../invalidation/factory.js';
|
|
3
4
|
import { extensionSystemUpdateState, extensionSystemUpdateUIMoveRequest } from '../update.js';
|
|
@@ -6,11 +7,14 @@ function createExtensionSystemInternal(getInternalState, options) {
|
|
|
6
7
|
const extensions = new Map();
|
|
7
8
|
const extensionsArray = options.extensions ?? [];
|
|
8
9
|
for (const extensionDef of extensionsArray) {
|
|
10
|
+
if (extensionDef.id === 'core') {
|
|
11
|
+
throw new Error('Extension id "core" is reserved and cannot be used by extensions');
|
|
12
|
+
}
|
|
9
13
|
if (extensions.has(extensionDef.id)) {
|
|
10
14
|
throw new Error(`Duplicate extension id found: ${extensionDef.id}`);
|
|
11
15
|
}
|
|
12
16
|
const instance = extensionDef.createInstance({
|
|
13
|
-
runtimeSurface: createExtensionRuntimeSurface(getInternalState, options.extensionRuntimeSurfaceCommands, extensionDef)
|
|
17
|
+
runtimeSurface: createExtensionRuntimeSurface(getInternalState, options.extensionRuntimeSurfaceCommands, options.extensionRuntimeSurfaceEvents, extensionDef)
|
|
14
18
|
});
|
|
15
19
|
const record = {
|
|
16
20
|
id: extensionDef.id,
|
|
@@ -72,6 +76,23 @@ export function createExtensionSystem(options) {
|
|
|
72
76
|
onUIMoveRequest(context) {
|
|
73
77
|
extensionSystemUpdateUIMoveRequest(internalState, context);
|
|
74
78
|
},
|
|
79
|
+
onEvent(context) {
|
|
80
|
+
const subscribers = internalState.eventSubscribers.get(context.rawEvent.type);
|
|
81
|
+
if (subscribers) {
|
|
82
|
+
for (const extensionId of subscribers) {
|
|
83
|
+
const extensionRec = internalState.extensions.get(extensionId);
|
|
84
|
+
if (extensionRec) {
|
|
85
|
+
extensionRec.instance.onEvent?.(context);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
completeDrag(session) {
|
|
91
|
+
const extensionRec = internalState.extensions.get(session.owner);
|
|
92
|
+
assert(extensionRec, `Extension record not found for completing drag session with owner ${session.owner}`);
|
|
93
|
+
assert(extensionRec.instance.completeDrag, `Extension instance does not have a completeDrag handler for completing drag session with owner ${session.owner}`);
|
|
94
|
+
extensionRec.instance.completeDrag(session);
|
|
95
|
+
},
|
|
75
96
|
onUnmount() {
|
|
76
97
|
internalState.transientVisualsSubscribers.clear();
|
|
77
98
|
internalState.eventSubscribers.clear();
|
|
@@ -84,7 +105,7 @@ export function createExtensionSystem(options) {
|
|
|
84
105
|
onDestroy() {
|
|
85
106
|
// We assume that the onUnmout was already called by runtime, but let's still validate
|
|
86
107
|
for (const extensionRec of internalState.extensions.values()) {
|
|
87
|
-
extensionRec.instance.destroy();
|
|
108
|
+
extensionRec.instance.destroy?.();
|
|
88
109
|
const onUnmountCalled = [
|
|
89
110
|
extensionRec.invalidation.dirtyLayers === 0,
|
|
90
111
|
extensionRec.animation.getAll().length === 0
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AnyExtensionDefinition } from '../types/extension.js';
|
|
2
2
|
import type { GetInternalState } from '../types/main.js';
|
|
3
3
|
import type { ExtensionRuntimeSurfaceCommands } from '../types/surface/commands.js';
|
|
4
|
+
import { ExtensionRuntimeSurfaceEvents } from '../types/surface/events.js';
|
|
4
5
|
import type { ExtensionRuntimeSurface } from '../types/surface/main.js';
|
|
5
|
-
export declare function createExtensionRuntimeSurface(getInternalState: GetInternalState, commands: ExtensionRuntimeSurfaceCommands, extensionDef: AnyExtensionDefinition): ExtensionRuntimeSurface;
|
|
6
|
+
export declare function createExtensionRuntimeSurface(getInternalState: GetInternalState, commands: ExtensionRuntimeSurfaceCommands, events: ExtensionRuntimeSurfaceEvents, extensionDef: AnyExtensionDefinition): ExtensionRuntimeSurface;
|
|
@@ -33,11 +33,55 @@ function createExtensionRuntimeSurfaceAnimation(getInternalState, extensionDef)
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
// @ts-expect-error We will implement events later, for now we just return empty object for it to satisfy the interface
|
|
36
|
+
function createExtensionRuntimeSurfaceEvents(getInternalState, runtimeSurfaceEvents, extensionDef) {
|
|
38
37
|
return {
|
|
39
|
-
|
|
38
|
+
subscribeEvent(type) {
|
|
39
|
+
const internalState = getInternalState();
|
|
40
|
+
const extensionRec = internalState.extensions.get(extensionDef.id);
|
|
41
|
+
assert(extensionRec, 'Extension record not found for subscribing to events');
|
|
42
|
+
assert(extensionRec.instance.onEvent, 'Extension instance does not have an onEvent handler for subscribing to events');
|
|
43
|
+
let subscribers = internalState.eventSubscribers.get(type);
|
|
44
|
+
if (!subscribers) {
|
|
45
|
+
subscribers = new Set();
|
|
46
|
+
internalState.eventSubscribers.set(type, subscribers);
|
|
47
|
+
}
|
|
48
|
+
subscribers.add(extensionDef.id);
|
|
49
|
+
runtimeSurfaceEvents.subscribeEvent(type);
|
|
50
|
+
},
|
|
51
|
+
unsubscribeEvent(type) {
|
|
52
|
+
const internalState = getInternalState();
|
|
53
|
+
const subscribers = internalState.eventSubscribers.get(type);
|
|
54
|
+
if (subscribers) {
|
|
55
|
+
subscribers.delete(extensionDef.id);
|
|
56
|
+
if (subscribers.size === 0) {
|
|
57
|
+
internalState.eventSubscribers.delete(type);
|
|
58
|
+
runtimeSurfaceEvents.unsubscribeEvent(type);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createExtensionRuntimeSurfaceCommands(getInternalState, runtimeSurfaceCommands, extensionDef) {
|
|
65
|
+
return {
|
|
66
|
+
...runtimeSurfaceCommands,
|
|
67
|
+
startDrag(session) {
|
|
68
|
+
const internalState = getInternalState();
|
|
69
|
+
const extensionRec = internalState.extensions.get(extensionDef.id);
|
|
70
|
+
assert(extensionRec, 'Extension record not found for starting drag session');
|
|
71
|
+
assert(extensionRec.instance.completeDrag, 'Extension instance does not have a completeDrag handler for starting drag session');
|
|
72
|
+
const extSession = {
|
|
73
|
+
owner: extensionDef.id,
|
|
74
|
+
...session
|
|
75
|
+
};
|
|
76
|
+
return runtimeSurfaceCommands.startDrag(extSession);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function createExtensionRuntimeSurface(getInternalState, commands, events, extensionDef) {
|
|
81
|
+
return {
|
|
82
|
+
commands: createExtensionRuntimeSurfaceCommands(getInternalState, commands, extensionDef),
|
|
40
83
|
animation: createExtensionRuntimeSurfaceAnimation(getInternalState, extensionDef),
|
|
41
|
-
transientVisuals: createExtensionRuntimeSurfaceTransientVisuals(getInternalState, extensionDef)
|
|
84
|
+
transientVisuals: createExtensionRuntimeSurfaceTransientVisuals(getInternalState, extensionDef),
|
|
85
|
+
events: createExtensionRuntimeSurfaceEvents(getInternalState, events, extensionDef)
|
|
42
86
|
};
|
|
43
87
|
}
|
|
@@ -2,7 +2,7 @@ import assert from '@ktarmyshov/assert';
|
|
|
2
2
|
import { toMerged } from 'es-toolkit';
|
|
3
3
|
import { createSvgElement, updateElementAttributes } from '../../../render/svg/helpers.js';
|
|
4
4
|
import { isUpdateContextRenderable } from '../../types/context/update.js';
|
|
5
|
-
import { extensionCreateInternalBase,
|
|
5
|
+
import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
|
|
6
6
|
import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
|
|
7
7
|
export function createActiveTarget(config = {}) {
|
|
8
8
|
const mergedConfig = toMerged(DEFAULT_CONFIG, config);
|
|
@@ -31,7 +31,7 @@ function createActiveTargetInstance(config) {
|
|
|
31
31
|
return {
|
|
32
32
|
id: EXTENSION_ID,
|
|
33
33
|
mount(env) {
|
|
34
|
-
|
|
34
|
+
extensionMountBase(internalState, env.slotRoots);
|
|
35
35
|
},
|
|
36
36
|
onUpdate(context) {
|
|
37
37
|
const needsRender = context.mutation.hasMutation({
|
|
@@ -60,19 +60,19 @@ function createActiveTargetInstance(config) {
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
const geometry = context.currentFrame.layout.geometry;
|
|
63
|
-
const r = geometry.
|
|
63
|
+
const r = geometry.getSquareRect(square);
|
|
64
64
|
const rectAttributes = {
|
|
65
65
|
x: r.x.toString(),
|
|
66
66
|
y: r.y.toString(),
|
|
67
|
-
width: r.
|
|
68
|
-
height: r.
|
|
67
|
+
width: r.width.toString(),
|
|
68
|
+
height: r.height.toString(),
|
|
69
69
|
fill: internalState.config.squareColor.color,
|
|
70
70
|
'fill-opacity': internalState.config.squareColor.opacity.toString(),
|
|
71
71
|
'shape-rendering': 'crispEdges'
|
|
72
72
|
};
|
|
73
73
|
const radius = geometry.squareSize * internalState.config.halo.radiusRatio;
|
|
74
|
-
const centerX = r.x + r.
|
|
75
|
-
const centerY = r.y + r.
|
|
74
|
+
const centerX = r.x + r.width / 2;
|
|
75
|
+
const centerY = r.y + r.height / 2;
|
|
76
76
|
const haloAttributes = {
|
|
77
77
|
cx: centerX.toString(),
|
|
78
78
|
cy: centerY.toString(),
|
|
@@ -98,11 +98,11 @@ function createActiveTargetInstance(config) {
|
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
unmount() {
|
|
101
|
-
|
|
101
|
+
extensionUnmountBase(internalState);
|
|
102
102
|
extensionClean(internalState);
|
|
103
103
|
},
|
|
104
104
|
destroy() {
|
|
105
|
-
|
|
105
|
+
extensionDestroyBase(internalState);
|
|
106
106
|
extensionClean(internalState);
|
|
107
107
|
}
|
|
108
108
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PartialDeep } from 'type-fest';
|
|
2
2
|
import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ExtensionInternalBase, OpaqueColor } from '../common/types.js';
|
|
4
4
|
export declare const EXTENSION_SLOTS: readonly ["underPieces", "overPieces"];
|
|
5
5
|
export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
|
|
6
6
|
export declare const EXTENSION_ID: "activeTarget";
|
|
@@ -15,7 +15,7 @@ export declare const DEFAULT_CONFIG: ActiveTargetConfig;
|
|
|
15
15
|
export type ActiveTargetInitConfig = PartialDeep<ActiveTargetConfig>;
|
|
16
16
|
export type ActiveTargetDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
|
|
17
17
|
export type ActiveTargetInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
|
|
18
|
-
export interface ActiveTargetInstanceInternal extends
|
|
18
|
+
export interface ActiveTargetInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
|
|
19
19
|
svgRect: SVGRectElement | null;
|
|
20
20
|
svgCircle: SVGCircleElement | null;
|
|
21
21
|
readonly config: ActiveTargetConfig;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { extensionCreateInternalBase } from '../common/helpers.js';
|
|
2
|
+
import { EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
|
|
3
|
+
export function createAutoPromote() {
|
|
4
|
+
return {
|
|
5
|
+
id: EXTENSION_ID,
|
|
6
|
+
slots: EXTENSION_SLOTS,
|
|
7
|
+
createInstance() {
|
|
8
|
+
return createAutoPromoteInstance();
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function createAutoPromoteInternal() {
|
|
13
|
+
return {
|
|
14
|
+
...extensionCreateInternalBase(),
|
|
15
|
+
toQueen: false
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function createAutoPromoteInstancePublic(state) {
|
|
19
|
+
return {
|
|
20
|
+
get toQueen() {
|
|
21
|
+
return state.toQueen;
|
|
22
|
+
},
|
|
23
|
+
set toQueen(value) {
|
|
24
|
+
state.toQueen = value;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createAutoPromoteInstance() {
|
|
29
|
+
const internalState = createAutoPromoteInternal();
|
|
30
|
+
const publicInterface = createAutoPromoteInstancePublic(internalState);
|
|
31
|
+
return {
|
|
32
|
+
id: EXTENSION_ID,
|
|
33
|
+
onUIMoveRequest(context) {
|
|
34
|
+
if (!internalState.toQueen)
|
|
35
|
+
return;
|
|
36
|
+
// Check if we can promote to queen
|
|
37
|
+
const pendingRequest = context.request;
|
|
38
|
+
const activeDestination = pendingRequest.destination;
|
|
39
|
+
if (activeDestination.promotedTo?.includes(5 /* RoleCode.Queen */)) {
|
|
40
|
+
pendingRequest.resolve({
|
|
41
|
+
from: pendingRequest.sourceSquare,
|
|
42
|
+
to: activeDestination.to,
|
|
43
|
+
promotedTo: 5 /* RoleCode.Queen */
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
getPublic() {
|
|
48
|
+
return publicInterface;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
|
|
2
|
+
export declare const EXTENSION_SLOTS: readonly [];
|
|
3
|
+
export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
|
|
4
|
+
export declare const EXTENSION_ID: "autoPromote";
|
|
5
|
+
export type AutoPromoteDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, AutoPromotePublic>;
|
|
6
|
+
export interface AutoPromotePublic {
|
|
7
|
+
toQueen: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type AutoPromoteInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, AutoPromotePublic>;
|
|
10
|
+
export interface AutoPromoteInstanceInternal {
|
|
11
|
+
toQueen: boolean;
|
|
12
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { denormalizeMove } from '../../../state/board/denormalize.js';
|
|
2
|
-
import { extensionCreateInternalBase
|
|
2
|
+
import { extensionCreateInternalBase } from '../common/helpers.js';
|
|
3
3
|
import { EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
|
|
4
4
|
export function createBoardEvents() {
|
|
5
5
|
return {
|
|
@@ -17,10 +17,6 @@ function createBoardEventsInternal() {
|
|
|
17
17
|
onRawUpdate: null
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
function extensionClean(state) {
|
|
21
|
-
state.onUIMove = null;
|
|
22
|
-
state.onRawUpdate = null;
|
|
23
|
-
}
|
|
24
20
|
function createBoardEventsInstancePublic(state) {
|
|
25
21
|
return {
|
|
26
22
|
setOnUIMove(callback) {
|
|
@@ -36,9 +32,6 @@ function createBoardEventsInstance() {
|
|
|
36
32
|
const publicInterface = createBoardEventsInstancePublic(internalState);
|
|
37
33
|
return {
|
|
38
34
|
id: EXTENSION_ID,
|
|
39
|
-
mount(env) {
|
|
40
|
-
extensionMount(internalState, env.slotRoots);
|
|
41
|
-
},
|
|
42
35
|
onUpdate(context) {
|
|
43
36
|
if (internalState.onRawUpdate) {
|
|
44
37
|
internalState.onRawUpdate({
|
|
@@ -54,14 +47,6 @@ function createBoardEventsInstance() {
|
|
|
54
47
|
},
|
|
55
48
|
getPublic() {
|
|
56
49
|
return publicInterface;
|
|
57
|
-
},
|
|
58
|
-
unmount() {
|
|
59
|
-
extensionUnmount(internalState);
|
|
60
|
-
extensionClean(internalState);
|
|
61
|
-
},
|
|
62
|
-
destroy() {
|
|
63
|
-
extensionDestroy(internalState);
|
|
64
|
-
extensionClean(internalState);
|
|
65
50
|
}
|
|
66
51
|
};
|
|
67
52
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { RuntimeReadonlyMutationSession } from '../../../runtime/mutation/types.js';
|
|
2
2
|
import { MoveOutput } from '../../../state/board/types/output.js';
|
|
3
|
-
import { ExtensionSlotSvgRoots } from '../../types/basic/mount.js';
|
|
4
3
|
import { UpdateFrameSnapshot } from '../../types/basic/update.js';
|
|
5
4
|
import { ExtensionDefinition, ExtensionInstance } from '../../types/extension.js';
|
|
6
|
-
import { ExtensionInternal } from '../common/types.js';
|
|
7
5
|
export declare const EXTENSION_SLOTS: readonly [];
|
|
8
6
|
export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
|
|
9
7
|
export declare const EXTENSION_ID: "events";
|
|
@@ -20,8 +18,7 @@ export interface BoardEventsPublic {
|
|
|
20
18
|
setOnRawUpdate(callback: OnRawUpdateCallback | null): void;
|
|
21
19
|
}
|
|
22
20
|
export type BoardEventsInstance = ExtensionInstance<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, BoardEventsPublic>;
|
|
23
|
-
export
|
|
24
|
-
export interface BoardEventsInstanceInternal extends ExtensionInternal<ExtensionSlotsType> {
|
|
21
|
+
export interface BoardEventsInstanceInternal {
|
|
25
22
|
onUIMove: OnUIMoveCallback | null;
|
|
26
23
|
onRawUpdate: OnRawUpdateCallback | null;
|
|
27
24
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ExtensionSlotName } from '../../types/basic/mount.js';
|
|
2
|
-
import {
|
|
3
|
-
export declare function extensionCreateInternalBase<TSlots extends readonly ExtensionSlotName[]>():
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
2
|
+
import { ExtensionInternalBase } from './types.js';
|
|
3
|
+
export declare function extensionCreateInternalBase<TSlots extends readonly ExtensionSlotName[]>(): ExtensionInternalBase<TSlots>;
|
|
4
|
+
export declare function extensionIsMountedBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): boolean;
|
|
5
|
+
export declare function extensionIsDestroyedBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): boolean;
|
|
6
|
+
export declare function extensionMountBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>, slotRoots: NonNullable<ExtensionInternalBase<TSlots>['slotRoots']>): void;
|
|
7
|
+
export declare function extensionUnmountBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): void;
|
|
8
|
+
export declare function extensionDestroyBase<TSlots extends readonly ExtensionSlotName[]>(state: ExtensionInternalBase<TSlots>): void;
|
|
@@ -6,28 +6,28 @@ export function extensionCreateInternalBase() {
|
|
|
6
6
|
destroyed: false
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
-
export function
|
|
9
|
+
export function extensionIsMountedBase(state) {
|
|
10
10
|
return state.slotRoots !== null;
|
|
11
11
|
}
|
|
12
|
-
export function
|
|
12
|
+
export function extensionIsDestroyedBase(state) {
|
|
13
13
|
return state.destroyed;
|
|
14
14
|
}
|
|
15
|
-
export function
|
|
16
|
-
assert(!
|
|
17
|
-
assert(!
|
|
15
|
+
export function extensionMountBase(state, slotRoots) {
|
|
16
|
+
assert(!extensionIsMountedBase(state), 'Extension is already mounted');
|
|
17
|
+
assert(!extensionIsDestroyedBase(state), 'Extension is destroyed');
|
|
18
18
|
state.slotRoots = slotRoots;
|
|
19
19
|
}
|
|
20
|
-
export function
|
|
21
|
-
assert(
|
|
20
|
+
export function extensionUnmountBase(state) {
|
|
21
|
+
assert(extensionIsMountedBase(state), 'Extension is not mounted');
|
|
22
22
|
for (const slotRoot of Object.values(state.slotRoots ?? {})) {
|
|
23
23
|
clearElementChildren(slotRoot);
|
|
24
24
|
}
|
|
25
25
|
state.slotRoots = null;
|
|
26
26
|
}
|
|
27
|
-
export function
|
|
28
|
-
assert(!
|
|
29
|
-
if (
|
|
30
|
-
|
|
27
|
+
export function extensionDestroyBase(state) {
|
|
28
|
+
assert(!extensionIsDestroyedBase(state), 'Extension is already destroyed');
|
|
29
|
+
if (extensionIsMountedBase(state)) {
|
|
30
|
+
extensionUnmountBase(state);
|
|
31
31
|
}
|
|
32
32
|
state.destroyed = true;
|
|
33
33
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { ExtensionSlotName, ExtensionSlotSvgRoots } from '../../types/basic/mount.js';
|
|
2
|
-
export interface
|
|
2
|
+
export interface ExtensionInternalBase<TSlots extends readonly ExtensionSlotName[]> {
|
|
3
3
|
slotRoots: ExtensionSlotSvgRoots<TSlots> | null;
|
|
4
4
|
destroyed: boolean;
|
|
5
5
|
}
|
|
6
|
-
export type AnyExtensionInternal = ExtensionInternal<readonly ExtensionSlotName[]>;
|
|
7
6
|
export interface OpaqueColor {
|
|
8
7
|
color: string;
|
|
9
|
-
opacity: number;
|
|
8
|
+
opacity: number | string;
|
|
10
9
|
}
|