Skip to content

Commit bccf5d4

Browse files
authored
feat: expose mouse up and down handlers (#212)
1 parent 1b5d332 commit bccf5d4

File tree

5 files changed

+217
-0
lines changed

5 files changed

+217
-0
lines changed

docs/D_OptionsApi.mdx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import * as OnPieceClickStories from './stories/options/OnPieceClick.stories';
3232
import * as OnPieceDragStories from './stories/options/OnPieceDrag.stories';
3333
import * as OnPieceDropStories from './stories/options/OnPieceDrop.stories';
3434
import * as OnSquareClickStories from './stories/options/OnSquareClick.stories';
35+
import * as OnSquareMouseDownStories from './stories/options/OnSquareMouseDown.stories';
36+
import * as OnSquareMouseUpStories from './stories/options/OnSquareMouseUp.stories';
3537
import * as OnSquareRightClickStories from './stories/options/OnSquareRightClick.stories';
3638
import * as PiecesStories from './stories/options/Pieces.stories';
3739
import * as PositionStories from './stories/options/Position.stories';
@@ -84,6 +86,8 @@ The code for these examples can be viewed by clicking the "Show code" button in
8486
| [onPieceDrag](#optionsonpiecedrag) | Handler for starting to drag a piece |
8587
| [onPieceDrop](#optionsonpiecedrop) | Handler for dropping a piece |
8688
| [onSquareClick](#optionsonsquareclick) | Handler for clicking a square |
89+
| [onSquareMouseDown](#optionsonsquaremousedown) | Handler for mouse button down on a square |
90+
| [onSquareMouseUp](#optionsonsquaremouseup) | Handler for mouse button up on a square |
8791
| [onSquareRightClick](#optionsonsquarerightclick) | Handler for right-clicking a square |
8892
| [pieces](#optionspieces) | Object mapping piece types to React components |
8993
| [position](#optionsposition) | Current position on the board (FEN string or position object) |
@@ -691,6 +695,44 @@ Callback function triggered when a square is clicked.
691695

692696
<Canvas of={OnSquareClickStories.OnSquareClick} />
693697

698+
### `options.onSquareMouseDown`
699+
700+
Callback function triggered when a mouse button is pressed down on a square.
701+
702+
**Default value:** `undefined`
703+
704+
**TypeScript type:**
705+
706+
```typescript
707+
({
708+
piece: { pieceType: string } | null;
709+
square: string;
710+
}, e: React.MouseEvent) => void;
711+
```
712+
713+
**Standard use case:** Implementing custom interactions or visual feedback on mouse down events.
714+
715+
<Canvas of={OnSquareMouseDownStories.OnSquareMouseDown} />
716+
717+
### `options.onSquareMouseUp`
718+
719+
Callback function triggered when a mouse button is released on a square.
720+
721+
**Default value:** `undefined`
722+
723+
**TypeScript type:**
724+
725+
```typescript
726+
({
727+
piece: { pieceType: string } | null;
728+
square: string;
729+
}, e: React.MouseEvent) => void;
730+
```
731+
732+
**Standard use case:** Implementing custom interactions or visual feedback on mouse up events.
733+
734+
<Canvas of={OnSquareMouseUpStories.OnSquareMouseUp} />
735+
694736
### `options.onSquareRightClick`
695737

696738
Callback function triggered when a square is right-clicked.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import React, { useState } from 'react';
3+
4+
import defaultMeta from '../basic-examples/Default.stories';
5+
import { Chessboard } from '../../../src';
6+
import type { SquareHandlerArgs } from '../../../src/types';
7+
8+
const meta: Meta<typeof Chessboard> = {
9+
...defaultMeta,
10+
title: 'stories/Options/OnSquareClick',
11+
} satisfies Meta<typeof Chessboard>;
12+
13+
export default meta;
14+
type Story = StoryObj<typeof meta>;
15+
16+
export const OnSquareMouseDown: Story = {
17+
render: () => {
18+
const [mouseDownSquare, setMouseDownSquare] = useState<string | null>(null);
19+
const [mouseDownPiece, setMouseDownPiece] = useState<string | null>(null);
20+
const [buttonPressed, setButtonPressed] = useState<string | null>(null);
21+
22+
// handle square click
23+
const onSquareMouseDown = (
24+
{ square, piece }: SquareHandlerArgs,
25+
e: React.MouseEvent,
26+
) => {
27+
setMouseDownSquare(square);
28+
setMouseDownPiece(piece?.pieceType || null);
29+
setButtonPressed(
30+
e.button === 0
31+
? 'Left'
32+
: e.button === 1
33+
? 'Middle'
34+
: e.button === 2
35+
? 'Right'
36+
: `Button ${e.button}`,
37+
);
38+
};
39+
40+
// chessboard options
41+
const chessboardOptions = {
42+
onSquareMouseDown,
43+
id: 'on-square-mouse-down',
44+
};
45+
46+
// render
47+
return (
48+
<div
49+
style={{
50+
display: 'flex',
51+
flexDirection: 'column',
52+
gap: '1rem',
53+
alignItems: 'center',
54+
}}
55+
>
56+
<div
57+
style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}
58+
>
59+
<div>Mouse last pressed in: {mouseDownSquare || 'None'}</div>
60+
<div>Piece in square: {mouseDownPiece || 'None'}</div>
61+
<div>Button pressed: {buttonPressed || 'None'}</div>
62+
</div>
63+
64+
<Chessboard options={chessboardOptions} />
65+
66+
<p style={{ fontSize: '0.8rem', color: '#666' }}>
67+
Press mouse button down on squares to see the mouse down events in
68+
action
69+
</p>
70+
</div>
71+
);
72+
},
73+
};
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
import { useState } from 'react';
3+
4+
import defaultMeta from '../basic-examples/Default.stories';
5+
import { Chessboard } from '../../../src';
6+
import type { SquareHandlerArgs } from '../../../src/types';
7+
8+
const meta: Meta<typeof Chessboard> = {
9+
...defaultMeta,
10+
title: 'stories/Options/OnSquareClick',
11+
} satisfies Meta<typeof Chessboard>;
12+
13+
export default meta;
14+
type Story = StoryObj<typeof meta>;
15+
16+
export const OnSquareMouseUp: Story = {
17+
render: () => {
18+
const [mouseUpSquare, setMouseUpSquare] = useState<string | null>(null);
19+
const [mouseUpPiece, setMouseUpPiece] = useState<string | null>(null);
20+
const [buttonReleased, setButtonReleased] = useState<string | null>(null);
21+
22+
// handle square click
23+
const onSquareMouseUp = (
24+
{ square, piece }: SquareHandlerArgs,
25+
e: React.MouseEvent,
26+
) => {
27+
setMouseUpSquare(square);
28+
setMouseUpPiece(piece?.pieceType || null);
29+
setButtonReleased(
30+
e.button === 0
31+
? 'Left'
32+
: e.button === 1
33+
? 'Middle'
34+
: e.button === 2
35+
? 'Right'
36+
: `Button ${e.button}`,
37+
);
38+
};
39+
40+
// chessboard options
41+
const chessboardOptions = {
42+
onSquareMouseUp,
43+
id: 'on-square-mouse-up',
44+
};
45+
46+
// render
47+
return (
48+
<div
49+
style={{
50+
display: 'flex',
51+
flexDirection: 'column',
52+
gap: '1rem',
53+
alignItems: 'center',
54+
}}
55+
>
56+
<div
57+
style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}
58+
>
59+
<div>Mouse released in square: {mouseUpSquare || 'None'}</div>
60+
<div>Piece in square: {mouseUpPiece || 'None'}</div>
61+
<div>Button released: {buttonReleased || 'None'}</div>
62+
</div>
63+
64+
<Chessboard options={chessboardOptions} />
65+
66+
<p style={{ fontSize: '0.8rem', color: '#666' }}>
67+
Release mouse button on squares to see the mouse up events in action
68+
</p>
69+
</div>
70+
);
71+
},
72+
};

src/ChessboardProvider.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ type ContextType = {
101101
onMouseOverSquare: ChessboardOptions['onMouseOverSquare'];
102102
onPieceClick: ChessboardOptions['onPieceClick'];
103103
onSquareClick: ChessboardOptions['onSquareClick'];
104+
onSquareMouseDown: ChessboardOptions['onSquareMouseDown'];
105+
onSquareMouseUp: ChessboardOptions['onSquareMouseUp'];
104106
onSquareRightClick: ChessboardOptions['onSquareRightClick'];
105107
squareRenderer: ChessboardOptions['squareRenderer'];
106108

@@ -188,6 +190,14 @@ export type ChessboardOptions = {
188190
targetSquare,
189191
}: PieceDropHandlerArgs) => boolean;
190192
onSquareClick?: ({ piece, square }: SquareHandlerArgs) => void;
193+
onSquareMouseDown?: (
194+
{ piece, square }: SquareHandlerArgs,
195+
e: React.MouseEvent,
196+
) => void;
197+
onSquareMouseUp?: (
198+
{ piece, square }: SquareHandlerArgs,
199+
e: React.MouseEvent,
200+
) => void;
191201
onSquareRightClick?: ({ piece, square }: SquareHandlerArgs) => void;
192202
squareRenderer?: ({
193203
piece,
@@ -255,6 +265,8 @@ export function ChessboardProvider({
255265
onPieceDrag,
256266
onPieceDrop,
257267
onSquareClick,
268+
onSquareMouseDown,
269+
onSquareMouseUp,
258270
onSquareRightClick,
259271
squareRenderer,
260272
} = options || {};
@@ -642,6 +654,8 @@ export function ChessboardProvider({
642654
onMouseOverSquare,
643655
onPieceClick,
644656
onSquareClick,
657+
onSquareMouseDown,
658+
onSquareMouseUp,
645659
onSquareRightClick,
646660
squareRenderer,
647661

src/Square.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export const Square = memo(function Square({
4747
onMouseOutSquare,
4848
onMouseOverSquare,
4949
onSquareClick,
50+
onSquareMouseDown,
51+
onSquareMouseUp,
5052
onSquareRightClick,
5153
squareRenderer,
5254
newArrowStartSquare,
@@ -103,6 +105,13 @@ export const Square = memo(function Square({
103105
if (e.button === 2 && allowDrawingArrows) {
104106
setNewArrowStartSquare(squareId);
105107
}
108+
onSquareMouseDown?.(
109+
{
110+
piece: currentPosition[squareId] ?? null,
111+
square: squareId,
112+
},
113+
e,
114+
);
106115
}}
107116
onMouseUp={(e) => {
108117
if (e.button === 2) {
@@ -113,6 +122,13 @@ export const Square = memo(function Square({
113122
});
114123
}
115124
}
125+
onSquareMouseUp?.(
126+
{
127+
piece: currentPosition[squareId] ?? null,
128+
square: squareId,
129+
},
130+
e,
131+
);
116132
}}
117133
onMouseOver={(e) => {
118134
// right mouse button is held down and we are drawing an arrow

0 commit comments

Comments
 (0)