@mirasen/chessboard 1.1.5 → 1.2.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 +37 -0
- package/README.md +1 -1
- package/dist/animation/render/fade.d.ts +2 -3
- package/dist/animation/render/fade.js +6 -6
- package/dist/animation/render/move.d.ts +2 -3
- package/dist/animation/render/move.js +6 -6
- package/dist/animation/render/plan.d.ts +3 -4
- package/dist/animation/render/plan.js +5 -5
- package/dist/animation/render/static.d.ts +2 -3
- package/dist/animation/render/static.js +4 -4
- package/dist/animation/render/types.d.ts +10 -4
- package/dist/extensions/build/index.d.ts +1 -2
- package/dist/extensions/build/index.js +1 -1
- package/dist/extensions/factory/main.js +11 -7
- package/dist/extensions/factory/runtime.js +24 -1
- package/dist/extensions/first-party/active-target/factory.js +18 -11
- package/dist/extensions/first-party/annotations/api.d.ts +16 -0
- package/dist/extensions/first-party/annotations/api.js +79 -0
- package/dist/extensions/first-party/annotations/committed.d.ts +3 -0
- package/dist/extensions/first-party/annotations/committed.js +11 -0
- package/dist/extensions/first-party/annotations/constants.d.ts +3 -0
- package/dist/extensions/first-party/annotations/constants.js +54 -0
- package/dist/extensions/first-party/annotations/denormalize.d.ts +4 -0
- package/dist/extensions/first-party/annotations/denormalize.js +22 -0
- package/dist/extensions/first-party/annotations/factory.d.ts +3 -0
- package/dist/extensions/first-party/annotations/factory.js +143 -0
- package/dist/extensions/first-party/annotations/interaction.d.ts +8 -0
- package/dist/extensions/first-party/annotations/interaction.js +129 -0
- package/dist/extensions/first-party/annotations/invalidation.d.ts +3 -0
- package/dist/extensions/first-party/annotations/invalidation.js +7 -0
- package/dist/extensions/first-party/annotations/normalize.d.ts +12 -0
- package/dist/extensions/first-party/annotations/normalize.js +54 -0
- package/dist/extensions/first-party/annotations/render/committed.d.ts +3 -0
- package/dist/extensions/first-party/annotations/render/committed.js +123 -0
- package/dist/extensions/first-party/annotations/render/helpers/arrows.d.ts +33 -0
- package/dist/extensions/first-party/annotations/render/helpers/arrows.js +93 -0
- package/dist/extensions/first-party/annotations/render/helpers/circles.d.ts +18 -0
- package/dist/extensions/first-party/annotations/render/helpers/circles.js +30 -0
- package/dist/extensions/first-party/annotations/render/preview.d.ts +3 -0
- package/dist/extensions/first-party/annotations/render/preview.js +96 -0
- package/dist/extensions/first-party/annotations/types/internal.d.ts +77 -0
- package/dist/extensions/first-party/annotations/types/internal.js +1 -0
- package/dist/extensions/first-party/annotations/types/main.d.ts +40 -0
- package/dist/extensions/first-party/annotations/types/main.js +6 -0
- package/dist/extensions/first-party/annotations/types/public.d.ts +48 -0
- package/dist/extensions/first-party/annotations/types/public.js +1 -0
- package/dist/extensions/first-party/annotations/update.d.ts +3 -0
- package/dist/extensions/first-party/annotations/update.js +45 -0
- package/dist/extensions/first-party/auto-promote/factory.js +6 -6
- package/dist/extensions/first-party/board-events/factory.js +6 -6
- package/dist/extensions/first-party/common/helpers.d.ts +2 -1
- package/dist/extensions/first-party/common/helpers.js +6 -5
- package/dist/extensions/first-party/common/types.d.ts +2 -0
- package/dist/extensions/first-party/last-move/factory.js +9 -9
- package/dist/extensions/first-party/legal-moves/factory.js +8 -8
- package/dist/extensions/first-party/main-renderer/animation/factory.d.ts +2 -2
- package/dist/extensions/first-party/main-renderer/animation/factory.js +4 -4
- package/dist/extensions/first-party/main-renderer/animation/render.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/animation/render.js +2 -2
- package/dist/extensions/first-party/main-renderer/animation/types.d.ts +3 -3
- package/dist/extensions/first-party/main-renderer/board/factory.js +2 -2
- package/dist/extensions/first-party/main-renderer/board/render.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/board/render.js +4 -4
- package/dist/extensions/first-party/main-renderer/board/types.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/coordinates/factory.js +2 -2
- package/dist/extensions/first-party/main-renderer/coordinates/render.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/coordinates/render.js +5 -5
- package/dist/extensions/first-party/main-renderer/coordinates/types.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/drag/factory.d.ts +2 -2
- package/dist/extensions/first-party/main-renderer/drag/factory.js +11 -8
- package/dist/extensions/first-party/main-renderer/drag/render.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/drag/render.js +7 -6
- package/dist/extensions/first-party/main-renderer/drag/types.d.ts +6 -5
- package/dist/extensions/first-party/main-renderer/drag/update.js +2 -2
- package/dist/extensions/first-party/main-renderer/factory.js +9 -5
- package/dist/extensions/first-party/main-renderer/piece-symbols.d.ts +19 -0
- package/dist/extensions/first-party/main-renderer/piece-symbols.js +45 -0
- package/dist/extensions/first-party/main-renderer/pieces/factory.d.ts +2 -2
- package/dist/extensions/first-party/main-renderer/pieces/factory.js +4 -4
- package/dist/extensions/first-party/main-renderer/pieces/render.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/pieces/render.js +8 -8
- package/dist/extensions/first-party/main-renderer/pieces/types.d.ts +4 -4
- package/dist/extensions/first-party/main-renderer/types/extension.d.ts +1 -1
- package/dist/extensions/first-party/main-renderer/types/extension.js +8 -1
- package/dist/extensions/first-party/main-renderer/types/instance.d.ts +2 -0
- package/dist/extensions/first-party/promotion/factory.js +10 -10
- package/dist/extensions/first-party/selected-square/factory.js +8 -8
- package/dist/extensions/first-party/watermark/factory.js +8 -8
- package/dist/extensions/types/basic/events.d.ts +32 -3
- package/dist/extensions/types/basic/events.js +2 -1
- package/dist/extensions/types/basic/mount.d.ts +5 -2
- package/dist/extensions/types/basic/mount.js +0 -2
- package/dist/extensions/types/basic/transient-visuals.d.ts +1 -1
- package/dist/extensions/types/context/animation.d.ts +3 -3
- package/dist/extensions/types/context/events.d.ts +2 -1
- package/dist/extensions/types/context/update.d.ts +6 -7
- package/dist/extensions/types/extension.d.ts +3 -0
- package/dist/extensions/types/main.d.ts +1 -0
- package/dist/extensions/types/surface/commands.d.ts +1 -1
- package/dist/extensions/types/surface/main.d.ts +6 -4
- package/dist/extensions/types/wrapper.d.ts +3 -1
- package/dist/extensions/types/wrapper.js +6 -2
- package/dist/extensions/update.js +6 -10
- package/dist/index.d.ts +1 -1
- package/dist/render/mount.js +3 -3
- package/dist/render/rendering/state.js +2 -2
- package/dist/render/svg/factory.js +40 -25
- package/dist/render/svg/helpers.d.ts +8 -4
- package/dist/render/svg/helpers.js +27 -12
- package/dist/render/svg/ids.d.ts +6 -0
- package/dist/render/svg/ids.js +11 -0
- package/dist/render/types.d.ts +4 -3
- package/dist/render/types.js +2 -1
- package/dist/runtime/factory/input.d.ts +3 -1
- package/dist/runtime/factory/input.js +11 -2
- package/dist/runtime/factory/main.js +10 -4
- package/dist/runtime/input/adapter/event.js +2 -2
- package/dist/runtime/input/adapter/pointer.d.ts +2 -2
- package/dist/runtime/input/controller/factory.d.ts +1 -1
- package/dist/runtime/input/controller/factory.js +35 -15
- package/dist/runtime/input/controller/helpers.d.ts +1 -1
- package/dist/runtime/input/controller/interaction.d.ts +3 -0
- package/dist/runtime/input/controller/interaction.js +14 -0
- package/dist/runtime/input/controller/pointer.d.ts +6 -6
- package/dist/runtime/input/controller/pointer.js +53 -27
- package/dist/runtime/input/controller/transient-visuals.d.ts +2 -3
- package/dist/runtime/input/controller/types.d.ts +6 -5
- package/dist/runtime/types/main.d.ts +1 -1
- package/dist/wrapper/factory.js +2 -7
- package/dist/wrapper/types.d.ts +3 -11
- package/package.json +1 -3
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { clearSvgElementChildren, createSvgElement, isLightSquare } from '../../../../render/svg/helpers.js';
|
|
2
2
|
import { SQUARE_COUNT } from '../../../../state/board/types/internal.js';
|
|
3
3
|
import { DirtyLayer } from '../types/extension.js';
|
|
4
|
-
export function rendererBoardRender(state, context,
|
|
4
|
+
export function rendererBoardRender(state, context, slot) {
|
|
5
5
|
// Check if we need to render
|
|
6
6
|
if ((context.invalidation.dirtyLayers & DirtyLayer.Board) === 0) {
|
|
7
7
|
return; // no-op
|
|
8
8
|
}
|
|
9
9
|
const geometry = context.currentFrame.layout.geometry;
|
|
10
|
-
|
|
10
|
+
clearSvgElementChildren(slot);
|
|
11
11
|
const { light, dark } = state.config;
|
|
12
12
|
for (let sq = 0; sq < SQUARE_COUNT; sq++) {
|
|
13
13
|
const r = geometry.getSquareRect(sq);
|
|
14
|
-
createSvgElement(
|
|
14
|
+
createSvgElement(slot, 'rect', {
|
|
15
15
|
'data-chessboard-id': `square-${sq}`,
|
|
16
16
|
x: r.x.toString(),
|
|
17
17
|
y: r.y.toString(),
|
|
@@ -6,5 +6,5 @@ export interface MainRendererBoardInternal {
|
|
|
6
6
|
}
|
|
7
7
|
export interface MainRendererBoard {
|
|
8
8
|
onUpdate(context: ExtensionUpdateContext): void;
|
|
9
|
-
render(context: ExtensionRenderContext,
|
|
9
|
+
render(context: ExtensionRenderContext, slot: SVGGElement): void;
|
|
10
10
|
}
|
|
@@ -2,8 +2,8 @@ import { rendererCoordinatesRender } from './render.js';
|
|
|
2
2
|
export function createMainRendererCoordinates(config) {
|
|
3
3
|
const internalState = { config };
|
|
4
4
|
return {
|
|
5
|
-
render(context,
|
|
6
|
-
rendererCoordinatesRender(internalState, context,
|
|
5
|
+
render(context, slot) {
|
|
6
|
+
rendererCoordinatesRender(internalState, context, slot);
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ExtensionRenderContext } from '../../../types/context/render.js';
|
|
2
2
|
import { MainRendererCoordinatesInternal } from './types.js';
|
|
3
|
-
export declare function rendererCoordinatesRender(state: MainRendererCoordinatesInternal, context: ExtensionRenderContext,
|
|
3
|
+
export declare function rendererCoordinatesRender(state: MainRendererCoordinatesInternal, context: ExtensionRenderContext, slot: SVGGElement): void;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { clearSvgElementChildren, createSvgElement, isLightSquare } from '../../../../render/svg/helpers.js';
|
|
2
2
|
import { squareOf } from '../../../../state/board/coords.js';
|
|
3
3
|
import { DirtyLayer } from '../types/extension.js';
|
|
4
4
|
function labelColorForSquare(sq, coordColors) {
|
|
5
5
|
return isLightSquare(sq) ? coordColors.dark : coordColors.light;
|
|
6
6
|
}
|
|
7
|
-
export function rendererCoordinatesRender(state, context,
|
|
7
|
+
export function rendererCoordinatesRender(state, context, slot) {
|
|
8
8
|
if ((context.invalidation.dirtyLayers & DirtyLayer.Coordinates) === 0) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
const geometry = context.currentFrame.layout.geometry;
|
|
12
|
-
|
|
12
|
+
clearSvgElementChildren(slot);
|
|
13
13
|
const coords = state.config;
|
|
14
14
|
const fontSize = geometry.squareSize * 0.12;
|
|
15
15
|
const offset = 3;
|
|
@@ -21,7 +21,7 @@ export function rendererCoordinatesRender(state, context, layer) {
|
|
|
21
21
|
const label = geometry.orientation === 0 /* ColorCode.White */ ? String(8 - visualRank) : String(1 + visualRank);
|
|
22
22
|
const r = geometry.getSquareRect(sq);
|
|
23
23
|
const color = labelColorForSquare(sq, coords);
|
|
24
|
-
const text = createSvgElement(
|
|
24
|
+
const text = createSvgElement(slot, 'text', {
|
|
25
25
|
'data-chessboard-id': `coord-rank-${label}`,
|
|
26
26
|
x: (r.x + offset).toString(),
|
|
27
27
|
y: (r.y + offset).toString(),
|
|
@@ -44,7 +44,7 @@ export function rendererCoordinatesRender(state, context, layer) {
|
|
|
44
44
|
: String.fromCharCode('h'.charCodeAt(0) - visualFile);
|
|
45
45
|
const r = geometry.getSquareRect(sq);
|
|
46
46
|
const color = labelColorForSquare(sq, coords);
|
|
47
|
-
const text = createSvgElement(
|
|
47
|
+
const text = createSvgElement(slot, 'text', {
|
|
48
48
|
'data-chessboard-id': `coord-file-${label}`,
|
|
49
49
|
x: (r.x + r.width - offset).toString(),
|
|
50
50
|
y: (r.y + r.height - offset).toString(),
|
|
@@ -4,5 +4,5 @@ export interface MainRendererCoordinatesInternal {
|
|
|
4
4
|
readonly config: ConfigColorPair;
|
|
5
5
|
}
|
|
6
6
|
export interface MainRendererCoordinates {
|
|
7
|
-
render(context: ExtensionRenderContext,
|
|
7
|
+
render(context: ExtensionRenderContext, slot: SVGGElement): void;
|
|
8
8
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ExtensionRuntimeSurface } from '../../../types/surface/main.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { PieceSymbolResolver } from '../piece-symbols.js';
|
|
3
3
|
import { MainRendererDrag } from './types.js';
|
|
4
|
-
export declare function createMainRendererDrag(
|
|
4
|
+
export declare function createMainRendererDrag(runtimeSurface: ExtensionRuntimeSurface, resolver: PieceSymbolResolver): MainRendererDrag;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { rendererDragRenderTransientVisuals } from './render.js';
|
|
2
2
|
import { rendererDragOnUpdate } from './update.js';
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export function createMainRendererDrag(runtimeSurface, resolver) {
|
|
4
|
+
const internalState = {
|
|
5
|
+
runtimeSurface,
|
|
6
|
+
resolver,
|
|
7
|
+
isDragActive: false,
|
|
8
|
+
pieceCode: null,
|
|
9
|
+
pieceNode: null
|
|
10
|
+
};
|
|
8
11
|
return {
|
|
9
12
|
onUpdate(context) {
|
|
10
13
|
rendererDragOnUpdate(internalState, context);
|
|
11
14
|
},
|
|
12
|
-
renderTransientVisuals(context,
|
|
13
|
-
rendererDragRenderTransientVisuals(internalState, context,
|
|
15
|
+
renderTransientVisuals(context, slot) {
|
|
16
|
+
rendererDragRenderTransientVisuals(internalState, context, slot);
|
|
14
17
|
},
|
|
15
18
|
unmount() {
|
|
16
19
|
internalState.pieceNode?.remove();
|
|
17
20
|
internalState.pieceNode = null;
|
|
18
|
-
internalState.
|
|
21
|
+
internalState.pieceCode = null;
|
|
19
22
|
internalState.isDragActive = false;
|
|
20
23
|
internalState.runtimeSurface.transientVisuals.unsubscribe();
|
|
21
24
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ExtensionRenderTransientVisualsContext } from '../../../types/context/transient-visuals.js';
|
|
2
2
|
import { MainRendererDragInternal } from './types.js';
|
|
3
|
-
export declare function rendererDragRenderTransientVisuals(state: MainRendererDragInternal, context: ExtensionRenderTransientVisualsContext,
|
|
3
|
+
export declare function rendererDragRenderTransientVisuals(state: MainRendererDragInternal, context: ExtensionRenderTransientVisualsContext, slot: SVGGElement): void;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import { createSvgElement,
|
|
2
|
-
export function rendererDragRenderTransientVisuals(state, context,
|
|
1
|
+
import { createSvgElement, updateSvgElementAttributes } from '../../../../render/svg/helpers.js';
|
|
2
|
+
export function rendererDragRenderTransientVisuals(state, context, slot) {
|
|
3
3
|
const geometry = context.currentFrame.layout.geometry;
|
|
4
4
|
const point = context.transientInput.boardClampedPoint;
|
|
5
5
|
const squareSize = geometry.squareSize.toString();
|
|
6
6
|
const x = (point.x - geometry.squareSize / 2).toString();
|
|
7
7
|
const y = (point.y - geometry.squareSize / 2).toString();
|
|
8
|
-
if (state.isDragActive && state.
|
|
8
|
+
if (state.isDragActive && state.pieceCode && !state.pieceNode) {
|
|
9
9
|
// Ok this is first render pass, let's create the piece node
|
|
10
|
-
|
|
10
|
+
const href = state.resolver.getHref(state.pieceCode);
|
|
11
|
+
state.pieceNode = createSvgElement(slot, 'use', {
|
|
11
12
|
'data-chessboard-id': 'dragged-piece',
|
|
12
13
|
width: squareSize,
|
|
13
14
|
height: squareSize,
|
|
14
15
|
x,
|
|
15
16
|
y,
|
|
16
|
-
href
|
|
17
|
+
href
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
else if (state.isDragActive && state.pieceNode) {
|
|
20
|
-
|
|
21
|
+
updateSvgElementAttributes(state.pieceNode, {
|
|
21
22
|
width: squareSize,
|
|
22
23
|
height: squareSize,
|
|
23
24
|
x,
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { NonEmptyPieceCode } from '../../../../state/board/types/internal.js';
|
|
1
2
|
import { ExtensionRenderTransientVisualsContext } from '../../../types/context/transient-visuals.js';
|
|
2
3
|
import { ExtensionUpdateContext } from '../../../types/context/update.js';
|
|
3
4
|
import { ExtensionRuntimeSurface } from '../../../types/surface/main.js';
|
|
4
|
-
import {
|
|
5
|
+
import type { PieceSymbolResolver } from '../piece-symbols.js';
|
|
5
6
|
export interface MainRendererDragInternal {
|
|
6
|
-
readonly config: PieceUrls;
|
|
7
7
|
readonly runtimeSurface: ExtensionRuntimeSurface;
|
|
8
|
+
readonly resolver: PieceSymbolResolver;
|
|
8
9
|
isDragActive: boolean;
|
|
9
|
-
|
|
10
|
-
pieceNode:
|
|
10
|
+
pieceCode: NonEmptyPieceCode | null;
|
|
11
|
+
pieceNode: SVGUseElement | null;
|
|
11
12
|
}
|
|
12
13
|
export interface MainRendererDrag {
|
|
13
14
|
onUpdate(context: ExtensionUpdateContext): void;
|
|
14
|
-
renderTransientVisuals(context: ExtensionRenderTransientVisualsContext,
|
|
15
|
+
renderTransientVisuals(context: ExtensionRenderTransientVisualsContext, slot: SVGGElement): void;
|
|
15
16
|
unmount(): void;
|
|
16
17
|
}
|
|
@@ -6,7 +6,7 @@ export function rendererDragOnUpdate(state, context) {
|
|
|
6
6
|
if (!state.isDragActive) {
|
|
7
7
|
const pieceCode = context.currentFrame.state.interaction.dragSession.sourcePieceCode;
|
|
8
8
|
assert(isNonEmptyPieceCode(pieceCode), 'Invalid piece code in drag session');
|
|
9
|
-
state.
|
|
9
|
+
state.pieceCode = pieceCode;
|
|
10
10
|
state.runtimeSurface.transientVisuals.subscribe();
|
|
11
11
|
}
|
|
12
12
|
state.isDragActive = true;
|
|
@@ -16,7 +16,7 @@ export function rendererDragOnUpdate(state, context) {
|
|
|
16
16
|
state.runtimeSurface.transientVisuals.unsubscribe();
|
|
17
17
|
state.pieceNode?.remove();
|
|
18
18
|
state.pieceNode = null;
|
|
19
|
-
state.
|
|
19
|
+
state.pieceCode = null;
|
|
20
20
|
}
|
|
21
21
|
state.isDragActive = false;
|
|
22
22
|
}
|
|
@@ -5,6 +5,7 @@ import { createMainRendererBoard } from './board/factory.js';
|
|
|
5
5
|
import { createMainRendererCoordinates } from './coordinates/factory.js';
|
|
6
6
|
import { createMainRendererDrag } from './drag/factory.js';
|
|
7
7
|
import { normalizeMainRendererConfig } from './normalize.js';
|
|
8
|
+
import { createPieceSymbolResolver, ensurePieceSymbolsDefined } from './piece-symbols.js';
|
|
8
9
|
import { createMainRendererPieces } from './pieces/factory.js';
|
|
9
10
|
import { EXTENSION_ID, EXTENSION_SLOTS } from './types/extension.js';
|
|
10
11
|
import { DEFAULT_MAIN_RENDERER_CONFIG } from './types/internal.js';
|
|
@@ -19,19 +20,21 @@ export function createMainRenderer(options = {}) {
|
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
function createMainRendererInternal(options, config) {
|
|
23
|
+
const pieceSymbolResolver = createPieceSymbolResolver(options.svgIds);
|
|
22
24
|
const board = createMainRendererBoard(config.colors.board);
|
|
23
25
|
const coordinates = createMainRendererCoordinates(config.colors.coordinates);
|
|
24
|
-
const pieces = createMainRendererPieces(
|
|
25
|
-
const drag = createMainRendererDrag(
|
|
26
|
-
const animation = createMainRendererAnimation(
|
|
26
|
+
const pieces = createMainRendererPieces(pieceSymbolResolver);
|
|
27
|
+
const drag = createMainRendererDrag(options.runtimeSurface, pieceSymbolResolver);
|
|
28
|
+
const animation = createMainRendererAnimation(options.runtimeSurface, pieceSymbolResolver);
|
|
27
29
|
return {
|
|
28
|
-
...extensionCreateInternalBase(),
|
|
30
|
+
...extensionCreateInternalBase(options),
|
|
29
31
|
board,
|
|
30
32
|
coordinates,
|
|
31
33
|
pieces,
|
|
32
34
|
drag,
|
|
33
35
|
animation,
|
|
34
|
-
runtimeSurface: options.runtimeSurface
|
|
36
|
+
runtimeSurface: options.runtimeSurface,
|
|
37
|
+
pieceSymbolResolver
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
40
|
function extensionUnmountLocal(state) {
|
|
@@ -52,6 +55,7 @@ function createMainRendererInstance(options, config) {
|
|
|
52
55
|
id: EXTENSION_ID,
|
|
53
56
|
mount(env) {
|
|
54
57
|
extensionMountBase(internalState, env.slotRoots);
|
|
58
|
+
ensurePieceSymbolsDefined(env.slotRoots.defs, config.pieceUrls, internalState.pieceSymbolResolver);
|
|
55
59
|
},
|
|
56
60
|
onUpdate(context) {
|
|
57
61
|
internalState.board.onUpdate(context);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SvgIdResolver } from '../../../render/svg/ids.js';
|
|
2
|
+
import { NonEmptyPieceCode } from '../../../state/board/types/internal.js';
|
|
3
|
+
import type { PieceUrls } from './types/internal.js';
|
|
4
|
+
export interface PieceSymbolResolver {
|
|
5
|
+
/** Returns the DOM id for a piece symbol (e.g., "cb0-renderer-p6") */
|
|
6
|
+
getId(pieceCode: NonEmptyPieceCode): string;
|
|
7
|
+
/** Returns the href suitable for <use href="..."> (e.g., "#cb0-renderer-p6") */
|
|
8
|
+
getHref(pieceCode: NonEmptyPieceCode): string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a PieceSymbolResolver backed by the shared SvgIdResolver.
|
|
12
|
+
* All ids use the "renderer" scope with a "p{pieceCode}" local id.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createPieceSymbolResolver(svgIds: SvgIdResolver): PieceSymbolResolver;
|
|
15
|
+
/**
|
|
16
|
+
* Ensures all 12 piece symbols exist in the defs element.
|
|
17
|
+
* Idempotent: checks the actual DOM for each symbol id before creating.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ensurePieceSymbolsDefined(defs: SVGDefsElement, config: PieceUrls, resolver: PieceSymbolResolver): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createSvgElement } from '../../../render/svg/helpers.js';
|
|
2
|
+
import { ALL_NON_EMPTY_PIECE_CODES } from '../../../state/board/types/internal.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a PieceSymbolResolver backed by the shared SvgIdResolver.
|
|
5
|
+
* All ids use the "renderer" scope with a "p{pieceCode}" local id.
|
|
6
|
+
*/
|
|
7
|
+
export function createPieceSymbolResolver(svgIds) {
|
|
8
|
+
const getId = (pieceCode) => {
|
|
9
|
+
return svgIds.makeId('renderer', `p${pieceCode}`);
|
|
10
|
+
};
|
|
11
|
+
const getHref = (pieceCode) => {
|
|
12
|
+
return svgIds.makeHref('renderer', `p${pieceCode}`);
|
|
13
|
+
};
|
|
14
|
+
return { getId, getHref };
|
|
15
|
+
}
|
|
16
|
+
// ─── Symbol DOM creation ──────────────────────────────────────────────────────
|
|
17
|
+
function getSymbolDataId(pieceCode) {
|
|
18
|
+
return `piece-symbol-${pieceCode}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Ensures all 12 piece symbols exist in the defs element.
|
|
22
|
+
* Idempotent: checks the actual DOM for each symbol id before creating.
|
|
23
|
+
*/
|
|
24
|
+
export function ensurePieceSymbolsDefined(defs, config, resolver) {
|
|
25
|
+
for (const pieceCode of ALL_NON_EMPTY_PIECE_CODES) {
|
|
26
|
+
const symbolId = resolver.getId(pieceCode);
|
|
27
|
+
// Idempotency: check if this symbol already exists in the DOM
|
|
28
|
+
if (defs.querySelector(`#${symbolId}`)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const symbol = createSvgElement(defs, 'symbol', {
|
|
32
|
+
id: symbolId,
|
|
33
|
+
'data-chessboard-id': getSymbolDataId(pieceCode),
|
|
34
|
+
viewBox: '0 0 1 1'
|
|
35
|
+
});
|
|
36
|
+
createSvgElement(symbol, 'image', {
|
|
37
|
+
'data-chessboard-id': `piece-symbol-image-${pieceCode}`,
|
|
38
|
+
href: config[pieceCode],
|
|
39
|
+
x: '0',
|
|
40
|
+
y: '0',
|
|
41
|
+
width: '1',
|
|
42
|
+
height: '1'
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PieceSymbolResolver } from '../piece-symbols.js';
|
|
2
2
|
import { MainRendererPieces } from './types.js';
|
|
3
|
-
export declare function createMainRendererPieces(
|
|
3
|
+
export declare function createMainRendererPieces(resolver: PieceSymbolResolver): MainRendererPieces;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { rendererPiecesRender } from './render.js';
|
|
2
2
|
import { rendererPiecesOnUpdate, rendererPiecesRefreshSuppressedSquares } from './update.js';
|
|
3
|
-
export function createMainRendererPieces(
|
|
3
|
+
export function createMainRendererPieces(resolver) {
|
|
4
4
|
const state = {
|
|
5
|
-
|
|
5
|
+
resolver,
|
|
6
6
|
pieceNodes: new Map(),
|
|
7
7
|
suppressedSquares: new Set()
|
|
8
8
|
};
|
|
@@ -13,8 +13,8 @@ export function createMainRendererPieces(config) {
|
|
|
13
13
|
refreshSuppressedSquares(context, animationSuppressedSquares) {
|
|
14
14
|
rendererPiecesRefreshSuppressedSquares(state, context, animationSuppressedSquares);
|
|
15
15
|
},
|
|
16
|
-
render(context,
|
|
17
|
-
rendererPiecesRender(state, context,
|
|
16
|
+
render(context, slot) {
|
|
17
|
+
rendererPiecesRender(state, context, slot);
|
|
18
18
|
},
|
|
19
19
|
unmount() {
|
|
20
20
|
for (const nodeRecord of state.pieceNodes.values()) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ExtensionRenderContext } from '../../../types/context/render.js';
|
|
2
2
|
import { MainRendererPiecesInternal } from './types.js';
|
|
3
|
-
export declare function rendererPiecesRender(state: MainRendererPiecesInternal, context: ExtensionRenderContext,
|
|
3
|
+
export declare function rendererPiecesRender(state: MainRendererPiecesInternal, context: ExtensionRenderContext, slot: SVGGElement): void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createSvgElement,
|
|
1
|
+
import { createSvgElement, updateSvgElementAttributes } from '../../../../render/svg/helpers.js';
|
|
2
2
|
import { isNonEmptyPieceCode } from '../../../../state/board/check.js';
|
|
3
3
|
import { SQUARE_COUNT } from '../../../../state/board/types/internal.js';
|
|
4
4
|
import { DirtyLayer } from '../types/extension.js';
|
|
5
|
-
export function rendererPiecesRender(state, context,
|
|
5
|
+
export function rendererPiecesRender(state, context, slot) {
|
|
6
6
|
if ((context.invalidation.dirtyLayers & DirtyLayer.Pieces) === 0) {
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
@@ -13,22 +13,22 @@ export function rendererPiecesRender(state, context, layer) {
|
|
|
13
13
|
const existing = state.pieceNodes.get(sq) || null;
|
|
14
14
|
const suppressed = state.suppressedSquares.has(sq);
|
|
15
15
|
if (isNonEmptyPieceCode(pieceCode) && !suppressed) {
|
|
16
|
-
const
|
|
16
|
+
const href = state.resolver.getHref(pieceCode);
|
|
17
17
|
const r = geometry.getSquareRect(sq);
|
|
18
18
|
if (existing !== null) {
|
|
19
|
-
|
|
19
|
+
updateSvgElementAttributes(existing.root, {
|
|
20
20
|
x: r.x.toString(),
|
|
21
21
|
y: r.y.toString(),
|
|
22
22
|
width: r.width.toString(),
|
|
23
23
|
height: r.height.toString(),
|
|
24
|
-
href
|
|
24
|
+
href
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
28
|
state.pieceNodes.set(sq, {
|
|
29
|
-
root: createSvgElement(
|
|
29
|
+
root: createSvgElement(slot, 'use', {
|
|
30
30
|
'data-chessboard-id': `piece-${pieceCode}-${sq}`,
|
|
31
|
-
href
|
|
31
|
+
href,
|
|
32
32
|
x: r.x.toString(),
|
|
33
33
|
y: r.y.toString(),
|
|
34
34
|
width: r.width.toString(),
|
|
@@ -38,7 +38,7 @@ export function rendererPiecesRender(state, context, layer) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
else if (existing !== null) {
|
|
41
|
-
|
|
41
|
+
slot.removeChild(existing.root);
|
|
42
42
|
state.pieceNodes.delete(sq);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -2,19 +2,19 @@ import { Square } from '../../../../state/board/types/internal.js';
|
|
|
2
2
|
import { ExtensionCleanAnimationContext } from '../../../types/context/animation.js';
|
|
3
3
|
import { ExtensionRenderContext } from '../../../types/context/render.js';
|
|
4
4
|
import { ExtensionUpdateContext } from '../../../types/context/update.js';
|
|
5
|
-
import {
|
|
5
|
+
import type { PieceSymbolResolver } from '../piece-symbols.js';
|
|
6
6
|
type PieceNodeRecord = {
|
|
7
|
-
root:
|
|
7
|
+
root: SVGUseElement;
|
|
8
8
|
};
|
|
9
9
|
export interface MainRendererPiecesInternal {
|
|
10
|
-
readonly
|
|
10
|
+
readonly resolver: PieceSymbolResolver;
|
|
11
11
|
readonly pieceNodes: Map<Square, PieceNodeRecord>;
|
|
12
12
|
suppressedSquares: ReadonlySet<Square>;
|
|
13
13
|
}
|
|
14
14
|
export interface MainRendererPieces {
|
|
15
15
|
onUpdate(context: ExtensionUpdateContext, animationSuppressedSquares: ReadonlySet<Square>): void;
|
|
16
16
|
refreshSuppressedSquares(context: ExtensionCleanAnimationContext, animationSuppressedSquares: ReadonlySet<Square>): void;
|
|
17
|
-
render(context: ExtensionRenderContext,
|
|
17
|
+
render(context: ExtensionRenderContext, slot: SVGGElement): void;
|
|
18
18
|
unmount(): void;
|
|
19
19
|
}
|
|
20
20
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ExtensionDefinition, ExtensionInstance } from '../../../types/extension.js';
|
|
2
2
|
import { MainRendererConfigInput } from './input.js';
|
|
3
3
|
export type MainRendererInitOptions = Partial<MainRendererConfigInput>;
|
|
4
|
-
export declare const EXTENSION_SLOTS: readonly ["board", "coordinates", "pieces", "animation", "drag"];
|
|
4
|
+
export declare const EXTENSION_SLOTS: readonly ["defs", "board", "coordinates", "pieces", "animation", "drag"];
|
|
5
5
|
export type ExtensionSlotsType = typeof EXTENSION_SLOTS;
|
|
6
6
|
export declare const EXTENSION_ID: "renderer";
|
|
7
7
|
export type MainRendererDefinition = ExtensionDefinition<typeof EXTENSION_ID, typeof EXTENSION_SLOTS, never>;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
export const EXTENSION_SLOTS = [
|
|
1
|
+
export const EXTENSION_SLOTS = [
|
|
2
|
+
'defs',
|
|
3
|
+
'board',
|
|
4
|
+
'coordinates',
|
|
5
|
+
'pieces',
|
|
6
|
+
'animation',
|
|
7
|
+
'drag'
|
|
8
|
+
];
|
|
2
9
|
export const EXTENSION_ID = 'renderer';
|
|
3
10
|
export var DirtyLayer;
|
|
4
11
|
(function (DirtyLayer) {
|
|
@@ -4,6 +4,7 @@ import { MainRendererAnimation } from '../animation/types.js';
|
|
|
4
4
|
import { MainRendererBoard } from '../board/types.js';
|
|
5
5
|
import { MainRendererCoordinates } from '../coordinates/types.js';
|
|
6
6
|
import { MainRendererDrag } from '../drag/types.js';
|
|
7
|
+
import type { PieceSymbolResolver } from '../piece-symbols.js';
|
|
7
8
|
import { MainRendererPieces } from '../pieces/types.js';
|
|
8
9
|
import { ExtensionSlotsType } from './extension.js';
|
|
9
10
|
export interface MainRendererInstanceInternal extends ExtensionInternalBase<ExtensionSlotsType> {
|
|
@@ -13,4 +14,5 @@ export interface MainRendererInstanceInternal extends ExtensionInternalBase<Exte
|
|
|
13
14
|
readonly drag: MainRendererDrag;
|
|
14
15
|
readonly animation: MainRendererAnimation;
|
|
15
16
|
readonly runtimeSurface: ExtensionRuntimeSurface;
|
|
17
|
+
readonly pieceSymbolResolver: PieceSymbolResolver;
|
|
16
18
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from '@ktarmyshov/assert';
|
|
2
|
-
import { createSvgElement,
|
|
2
|
+
import { createSvgElement, updateSvgElementAttributes } from '../../../render/svg/helpers.js';
|
|
3
3
|
import { fileOf, rankOf, squareOf } from '../../../state/board/coords.js';
|
|
4
4
|
import { toPieceCode } from '../../../state/board/piece.js';
|
|
5
5
|
import { isUpdateContextRenderable } from '../../types/context/update.js';
|
|
@@ -18,7 +18,7 @@ export function createPromotion(config = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
function createPromotionInternal(options, config) {
|
|
20
20
|
return {
|
|
21
|
-
...extensionCreateInternalBase(),
|
|
21
|
+
...extensionCreateInternalBase(options),
|
|
22
22
|
svgPromotionPieces: new Map(),
|
|
23
23
|
activePromotionSquares: new Map(),
|
|
24
24
|
svgHoverRect: null,
|
|
@@ -117,7 +117,7 @@ function createPromotionInstance(options, config) {
|
|
|
117
117
|
}
|
|
118
118
|
// Create or update a node for each promotion role
|
|
119
119
|
assert(internalState.slotRoots, 'Slot roots should be available when render is called');
|
|
120
|
-
const
|
|
120
|
+
const slot = internalState.slotRoots.animation;
|
|
121
121
|
for (let i = 0; i < promotedToSorted.length; i++) {
|
|
122
122
|
const roleCode = promotedToSorted[i];
|
|
123
123
|
const displayRank = (targetRank + i * rankStep);
|
|
@@ -144,15 +144,15 @@ function createPromotionInstance(options, config) {
|
|
|
144
144
|
internalState.activePromotionSquares.set(displaySquare, roleCode);
|
|
145
145
|
const existing = internalState.svgPromotionPieces.get(roleCode) ?? null;
|
|
146
146
|
if (existing !== null) {
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
updateSvgElementAttributes(existing.rect, rectAttrs);
|
|
148
|
+
updateSvgElementAttributes(existing.svg, imageAttrs);
|
|
149
149
|
}
|
|
150
150
|
else {
|
|
151
|
-
const rect = createSvgElement(
|
|
151
|
+
const rect = createSvgElement(slot, 'rect', {
|
|
152
152
|
'data-chessboard-id': `promotion-bg-${roleCode}`,
|
|
153
153
|
...rectAttrs
|
|
154
154
|
});
|
|
155
|
-
const svg = createSvgElement(
|
|
155
|
+
const svg = createSvgElement(slot, 'image', {
|
|
156
156
|
'data-chessboard-id': `promotion-piece-${roleCode}`,
|
|
157
157
|
...imageAttrs
|
|
158
158
|
});
|
|
@@ -176,12 +176,12 @@ function createPromotionInstance(options, config) {
|
|
|
176
176
|
'shape-rendering': 'crispEdges'
|
|
177
177
|
};
|
|
178
178
|
if (internalState.svgHoverRect) {
|
|
179
|
-
|
|
179
|
+
updateSvgElementAttributes(internalState.svgHoverRect, attrs);
|
|
180
180
|
}
|
|
181
181
|
else {
|
|
182
182
|
assert(internalState.slotRoots, 'Slot roots should be available when renderTransientVisuals is called');
|
|
183
|
-
const
|
|
184
|
-
internalState.svgHoverRect = createSvgElement(
|
|
183
|
+
const slot = internalState.slotRoots.animation;
|
|
184
|
+
internalState.svgHoverRect = createSvgElement(slot, 'rect', {
|
|
185
185
|
'data-chessboard-id': 'promotion-hover',
|
|
186
186
|
...attrs
|
|
187
187
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from '@ktarmyshov/assert';
|
|
2
2
|
import { toMerged } from 'es-toolkit';
|
|
3
|
-
import { createSvgElement,
|
|
3
|
+
import { createSvgElement, updateSvgElementAttributes } from '../../../render/svg/helpers.js';
|
|
4
4
|
import { isUpdateContextRenderable } from '../../types/context/update.js';
|
|
5
5
|
import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
|
|
6
6
|
import { DEFAULT_CONFIG, EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
|
|
@@ -9,14 +9,14 @@ export function createSelectedSquare(config = {}) {
|
|
|
9
9
|
return {
|
|
10
10
|
id: EXTENSION_ID,
|
|
11
11
|
slots: EXTENSION_SLOTS,
|
|
12
|
-
createInstance() {
|
|
13
|
-
return createSelectedSquareInstance(mergedConfig);
|
|
12
|
+
createInstance(options) {
|
|
13
|
+
return createSelectedSquareInstance(options, mergedConfig);
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
function createSelectedSquareInternal(config) {
|
|
17
|
+
function createSelectedSquareInternal(options, config) {
|
|
18
18
|
return {
|
|
19
|
-
...extensionCreateInternalBase(),
|
|
19
|
+
...extensionCreateInternalBase(options),
|
|
20
20
|
svgRect: null,
|
|
21
21
|
config
|
|
22
22
|
};
|
|
@@ -24,8 +24,8 @@ function createSelectedSquareInternal(config) {
|
|
|
24
24
|
function extensionClean(state) {
|
|
25
25
|
state.svgRect = null;
|
|
26
26
|
}
|
|
27
|
-
function createSelectedSquareInstance(config) {
|
|
28
|
-
const internalState = createSelectedSquareInternal(config);
|
|
27
|
+
function createSelectedSquareInstance(options, config) {
|
|
28
|
+
const internalState = createSelectedSquareInternal(options, config);
|
|
29
29
|
return {
|
|
30
30
|
id: EXTENSION_ID,
|
|
31
31
|
mount(env) {
|
|
@@ -73,7 +73,7 @@ function createSelectedSquareInstance(config) {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
-
|
|
76
|
+
updateSvgElementAttributes(internalState.svgRect, rectAttributes);
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
unmount() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from '@ktarmyshov/assert';
|
|
2
|
-
import { createSvgElement,
|
|
2
|
+
import { createSvgElement, updateSvgElementAttributes } from '../../../render/svg/helpers.js';
|
|
3
3
|
import { isUpdateContextRenderable } from '../../types/context/update.js';
|
|
4
4
|
import { extensionCreateInternalBase, extensionDestroyBase, extensionMountBase, extensionUnmountBase } from '../common/helpers.js';
|
|
5
5
|
import { EXTENSION_ID, EXTENSION_SLOTS } from './types.js';
|
|
@@ -7,14 +7,14 @@ export function createWatermark() {
|
|
|
7
7
|
return {
|
|
8
8
|
id: EXTENSION_ID,
|
|
9
9
|
slots: EXTENSION_SLOTS,
|
|
10
|
-
createInstance() {
|
|
11
|
-
return createWatermarkInstance();
|
|
10
|
+
createInstance(options) {
|
|
11
|
+
return createWatermarkInstance(options);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
function createWatermarkInternal() {
|
|
15
|
+
function createWatermarkInternal(options) {
|
|
16
16
|
return {
|
|
17
|
-
...extensionCreateInternalBase(),
|
|
17
|
+
...extensionCreateInternalBase(options),
|
|
18
18
|
svgWatermark: null
|
|
19
19
|
};
|
|
20
20
|
}
|
|
@@ -24,8 +24,8 @@ function extensionClean(state) {
|
|
|
24
24
|
const watermarkUrl = new URL('../../../../assets/watermark/mirasen.svg', import.meta.url).toString();
|
|
25
25
|
const squareRatio = 0.8; // watermark size relative to square size
|
|
26
26
|
const opacity = '0.3';
|
|
27
|
-
function createWatermarkInstance() {
|
|
28
|
-
const internalState = createWatermarkInternal();
|
|
27
|
+
function createWatermarkInstance(options) {
|
|
28
|
+
const internalState = createWatermarkInternal(options);
|
|
29
29
|
return {
|
|
30
30
|
id: EXTENSION_ID,
|
|
31
31
|
mount(env) {
|
|
@@ -67,7 +67,7 @@ function createWatermarkInstance() {
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
|
-
|
|
70
|
+
updateSvgElementAttributes(internalState.svgWatermark, watermarkAttributes);
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
unmount() {
|