@react-chess-tools/react-chess-clock 1.0.1
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 +7 -0
- package/README.md +697 -0
- package/dist/index.cjs +1014 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +528 -0
- package/dist/index.d.ts +528 -0
- package/dist/index.js +969 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
- package/src/components/ChessClock/ChessClock.stories.tsx +782 -0
- package/src/components/ChessClock/index.ts +44 -0
- package/src/components/ChessClock/parts/Display.tsx +69 -0
- package/src/components/ChessClock/parts/PlayPause.tsx +190 -0
- package/src/components/ChessClock/parts/Reset.tsx +90 -0
- package/src/components/ChessClock/parts/Root.tsx +37 -0
- package/src/components/ChessClock/parts/Switch.tsx +84 -0
- package/src/components/ChessClock/parts/__tests__/Display.test.tsx +149 -0
- package/src/components/ChessClock/parts/__tests__/PlayPause.test.tsx +411 -0
- package/src/components/ChessClock/parts/__tests__/Reset.test.tsx +160 -0
- package/src/components/ChessClock/parts/__tests__/Root.test.tsx +49 -0
- package/src/components/ChessClock/parts/__tests__/Switch.test.tsx +204 -0
- package/src/hooks/__tests__/clockReducer.test.ts +985 -0
- package/src/hooks/__tests__/useChessClock.test.tsx +1080 -0
- package/src/hooks/clockReducer.ts +379 -0
- package/src/hooks/useChessClock.ts +406 -0
- package/src/hooks/useChessClockContext.ts +35 -0
- package/src/index.ts +65 -0
- package/src/types.ts +217 -0
- package/src/utils/__tests__/calculateSwitchTime.test.ts +150 -0
- package/src/utils/__tests__/formatTime.test.ts +83 -0
- package/src/utils/__tests__/timeControl.test.ts +414 -0
- package/src/utils/__tests__/timingMethods.test.ts +170 -0
- package/src/utils/calculateSwitchTime.ts +37 -0
- package/src/utils/formatTime.ts +59 -0
- package/src/utils/presets.ts +47 -0
- package/src/utils/timeControl.ts +205 -0
- package/src/utils/timingMethods.ts +103 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@react-chess-tools/react-chess-clock</h1>
|
|
3
|
+
<p>A standalone chess clock component for React with support for multiple timing methods and tournament controls</p>
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@react-chess-tools/react-chess-clock)
|
|
6
|
+
[](https://www.npmjs.com/package/@react-chess-tools/react-chess-clock)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Overview](#overview)
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Demo](#demo)
|
|
19
|
+
- [API Reference](#api-reference)
|
|
20
|
+
- [ChessClock.Root](#chessclockroot)
|
|
21
|
+
- [ChessClock.Display](#chessclockdisplay)
|
|
22
|
+
- [ChessClock.Switch](#chessclockswitch)
|
|
23
|
+
- [ChessClock.PlayPause](#chessclockplaypause)
|
|
24
|
+
- [ChessClock.Reset](#chessclockreset)
|
|
25
|
+
- [Hooks](#hooks)
|
|
26
|
+
- [useChessClock](#usechessclock)
|
|
27
|
+
- [useChessClockContext](#usechessclockcontext)
|
|
28
|
+
- [useOptionalChessClock](#useoptionalchessclock)
|
|
29
|
+
- [Time Control Formats](#time-control-formats)
|
|
30
|
+
- [Timing Methods](#timing-methods)
|
|
31
|
+
- [Clock Start Modes](#clock-start-modes)
|
|
32
|
+
- [Examples](#examples)
|
|
33
|
+
- [License](#license)
|
|
34
|
+
|
|
35
|
+
## Overview
|
|
36
|
+
|
|
37
|
+
`@react-chess-tools/react-chess-clock` is a React component for creating chess clocks with support for multiple timing methods (Fischer increment, simple delay, Bronstein delay), multi-period tournament controls, and various clock start modes.
|
|
38
|
+
|
|
39
|
+
Built using a compound component pattern (similar to [Radix UI](https://www.radix-ui.com/)), it provides an unstyled, fully customizable clock with sensible defaults.
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Multiple Timing Methods** - Fischer increment, simple delay, and Bronstein delay
|
|
44
|
+
- **Tournament Controls** - Multi-period time controls with move counts (FIDE/USCF style)
|
|
45
|
+
- **Clock Start Modes** - Delayed (Lichess-style), immediate (Chess.com-style), or manual
|
|
46
|
+
- **Time Odds** - Support for different starting times per player
|
|
47
|
+
- **Flexible Time Control** - String notation (`"5+3"`), object config, or multi-period array
|
|
48
|
+
- **Server Sync** - Built-in methods for syncing clock state with a server
|
|
49
|
+
- **TypeScript** - Full TypeScript support with comprehensive type definitions
|
|
50
|
+
- **Unstyled** - Zero runtime CSS with data attributes for easy styling
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install @react-chess-tools/react-chess-clock
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
yarn add @react-chess-tools/react-chess-clock
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pnpm add @react-chess-tools/react-chess-clock
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { ChessClock } from "@react-chess-tools/react-chess-clock";
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
return (
|
|
73
|
+
<ChessClock.Root timeControl={{ time: "5+3" }}>
|
|
74
|
+
<ChessClock.Display color="white" />
|
|
75
|
+
<ChessClock.Display color="black" />
|
|
76
|
+
<ChessClock.Switch>Switch</ChessClock.Switch>
|
|
77
|
+
<ChessClock.PlayPause />
|
|
78
|
+
<ChessClock.Reset>Reset</ChessClock.Reset>
|
|
79
|
+
</ChessClock.Root>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Demo
|
|
85
|
+
|
|
86
|
+
Visit the [live demo](https://react-chess-tools.vercel.app/) to see the component in action.
|
|
87
|
+
|
|
88
|
+
## API Reference
|
|
89
|
+
|
|
90
|
+
### ChessClock.Root
|
|
91
|
+
|
|
92
|
+
The root component that provides `ChessClockContext` to all child components.
|
|
93
|
+
|
|
94
|
+
**Note:** This is a logic-only component (Context Provider). It does not render any DOM elements.
|
|
95
|
+
|
|
96
|
+
#### Props
|
|
97
|
+
|
|
98
|
+
| Name | Type | Default | Description |
|
|
99
|
+
| ------------- | ------------------- | ------- | ------------------------------------- |
|
|
100
|
+
| `timeControl` | `TimeControlConfig` | - | Time control configuration (required) |
|
|
101
|
+
| `children` | `ReactNode` | - | Child components |
|
|
102
|
+
|
|
103
|
+
#### TimeControlConfig
|
|
104
|
+
|
|
105
|
+
| Property | Type | Default | Description |
|
|
106
|
+
| -------------- | --------------------------------------------------- | ----------- | --------------------------------------------------------- |
|
|
107
|
+
| `time` | `TimeControlInput` | - | Time specification (string, object, or array) |
|
|
108
|
+
| `timingMethod` | `"fischer" \| "delay" \| "bronstein"` | `"fischer"` | How time is added/removed after each move |
|
|
109
|
+
| `clockStart` | `"delayed" \| "immediate" \| "manual"` | `"delayed"` | When the clock starts running |
|
|
110
|
+
| `whiteTime` | `number` | - | Override White's starting time in seconds (for time odds) |
|
|
111
|
+
| `blackTime` | `number` | - | Override Black's starting time in seconds (for time odds) |
|
|
112
|
+
| `onTimeout` | `(loser: ClockColor) => void` | - | Callback when a player's time runs out |
|
|
113
|
+
| `onSwitch` | `(activePlayer: ClockColor) => void` | - | Callback when active player switches |
|
|
114
|
+
| `onTimeUpdate` | `(times: { white: number; black: number }) => void` | - | Callback on each time update |
|
|
115
|
+
|
|
116
|
+
#### Example
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<ChessClock.Root
|
|
120
|
+
timeControl={{
|
|
121
|
+
time: "5+3",
|
|
122
|
+
timingMethod: "fischer",
|
|
123
|
+
clockStart: "delayed",
|
|
124
|
+
onTimeout: (loser) => console.log(`${loser} loses on time`),
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<ChessClock.Display color="white" />
|
|
128
|
+
<ChessClock.Display color="black" />
|
|
129
|
+
</ChessClock.Root>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### ChessClock.Display
|
|
133
|
+
|
|
134
|
+
Displays the current time for a player. Renders an unstyled div with data attributes for styling.
|
|
135
|
+
|
|
136
|
+
Supports **ref forwarding** and all standard **HTML div attributes** (className, style, id, data-_, aria-_, etc.).
|
|
137
|
+
|
|
138
|
+
#### Props
|
|
139
|
+
|
|
140
|
+
| Name | Type | Description |
|
|
141
|
+
| ------------ | ------------------------------------------- | -------------------------------------------------- |
|
|
142
|
+
| `color` | `"white" \| "black"` | Player color to display (required) |
|
|
143
|
+
| `format` | `"auto" \| "mm:ss" \| "ss.d" \| "hh:mm:ss"` | Time format (default: `"auto"`) |
|
|
144
|
+
| `formatTime` | `(milliseconds: number) => string` | Custom time formatting function (overrides format) |
|
|
145
|
+
| `ref` | `Ref<HTMLDivElement>` | Forwarded ref to the wrapper div element |
|
|
146
|
+
| `className` | `string` | Custom CSS class names |
|
|
147
|
+
| `style` | `CSSProperties` | Custom inline styles |
|
|
148
|
+
| `...` | `HTMLAttributes<HTMLDivElement>` | All standard HTML div attributes |
|
|
149
|
+
|
|
150
|
+
#### Data Attributes
|
|
151
|
+
|
|
152
|
+
| Attribute | Value | Description |
|
|
153
|
+
| -------------------- | ---------------------- | ------------------------------------------------ |
|
|
154
|
+
| `data-clock-color` | `"white"` or `"black"` | Which player's time is shown |
|
|
155
|
+
| `data-clock-active` | `"true"` or `"false"` | Whether this player's clock is currently running |
|
|
156
|
+
| `data-clock-paused` | `"true"` or `"false"` | Whether the clock is paused |
|
|
157
|
+
| `data-clock-timeout` | `"true"` or `"false"` | Whether this player has run out of time |
|
|
158
|
+
| `data-clock-status` | Clock status string | Current clock status |
|
|
159
|
+
|
|
160
|
+
#### Example
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<ChessClock.Display
|
|
164
|
+
color="white"
|
|
165
|
+
format="auto"
|
|
166
|
+
className="clock-display"
|
|
167
|
+
style={{ fontFamily: "monospace", fontSize: "24px" }}
|
|
168
|
+
/>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### ChessClock.Switch
|
|
172
|
+
|
|
173
|
+
A button that manually switches the active player's clock.
|
|
174
|
+
|
|
175
|
+
Supports **ref forwarding**, **asChild pattern**, and all standard **HTML button attributes** (className, style, disabled, etc.).
|
|
176
|
+
|
|
177
|
+
#### Props
|
|
178
|
+
|
|
179
|
+
| Name | Type | Description |
|
|
180
|
+
| ----------- | ----------------------------------------- | -------------------------------------- |
|
|
181
|
+
| `asChild` | `boolean` | Render as child element (slot pattern) |
|
|
182
|
+
| `ref` | `Ref<HTMLButtonElement>` | Forwarded ref to the button element |
|
|
183
|
+
| `className` | `string` | Custom CSS class names |
|
|
184
|
+
| `...` | `ButtonHTMLAttributes<HTMLButtonElement>` | All standard HTML button attributes |
|
|
185
|
+
|
|
186
|
+
**Note:** The button is disabled when the clock status is `"idle"` or `"finished"`.
|
|
187
|
+
|
|
188
|
+
#### Example
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
<ChessClock.Switch>Switch Turn</ChessClock.Switch>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### Using asChild Pattern
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<ChessClock.Switch asChild>
|
|
198
|
+
<div className="custom-switch">Switch</div>
|
|
199
|
+
</ChessClock.Switch>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### ChessClock.PlayPause
|
|
203
|
+
|
|
204
|
+
A button to start, pause, and resume the clock. Content changes based on clock state.
|
|
205
|
+
|
|
206
|
+
Supports **ref forwarding**, **asChild pattern**, and all standard **HTML button attributes** (className, style, disabled, etc.).
|
|
207
|
+
|
|
208
|
+
#### Props
|
|
209
|
+
|
|
210
|
+
| Name | Type | Default | Description |
|
|
211
|
+
| ----------------- | ----------------------------------------- | ----------------------------------- | ------------------------------------------- |
|
|
212
|
+
| `startContent` | `ReactNode` | `"Start"` | Content shown when clock is idle |
|
|
213
|
+
| `pauseContent` | `ReactNode` | `"Pause"` | Content shown when clock is running |
|
|
214
|
+
| `resumeContent` | `ReactNode` | `"Resume"` | Content shown when clock is paused |
|
|
215
|
+
| `delayedContent` | `ReactNode` | `"Start"` | Content shown when clock is in delayed mode |
|
|
216
|
+
| `finishedContent` | `ReactNode` | `"Game Over"` | Content shown when clock is finished |
|
|
217
|
+
| `asChild` | `boolean` | `false` | Render as child element (slot pattern) |
|
|
218
|
+
| `ref` | `Ref<HTMLButtonElement>` | Forwarded ref to the button element |
|
|
219
|
+
| `className` | `string` | Custom CSS class names |
|
|
220
|
+
| `...` | `ButtonHTMLAttributes<HTMLButtonElement>` | All standard HTML button attributes |
|
|
221
|
+
|
|
222
|
+
**Note:** The button is disabled when the clock status is `"finished"` or `"delayed"`.
|
|
223
|
+
|
|
224
|
+
#### Example
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
<ChessClock.PlayPause
|
|
228
|
+
startContent="Start Game"
|
|
229
|
+
pauseContent="Pause"
|
|
230
|
+
resumeContent="Resume"
|
|
231
|
+
finishedContent="Game Over"
|
|
232
|
+
/>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### Using asChild Pattern
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
<ChessClock.PlayPause asChild>
|
|
239
|
+
<button className="custom-play-pause">Toggle</button>
|
|
240
|
+
</ChessClock.PlayPause>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### ChessClock.Reset
|
|
244
|
+
|
|
245
|
+
A button that resets the clock. Optionally accepts a new time control.
|
|
246
|
+
|
|
247
|
+
Supports **ref forwarding**, **asChild pattern**, and all standard **HTML button attributes** (className, style, disabled, etc.).
|
|
248
|
+
|
|
249
|
+
#### Props
|
|
250
|
+
|
|
251
|
+
| Name | Type | Description |
|
|
252
|
+
| ------------- | ----------------------------------------- | --------------------------------------------- |
|
|
253
|
+
| `timeControl` | `TimeControlInput` | New time control to apply on reset (optional) |
|
|
254
|
+
| `asChild` | `boolean` | Render as child element (slot pattern) |
|
|
255
|
+
| `ref` | `Ref<HTMLButtonElement>` | Forwarded ref to the button element |
|
|
256
|
+
| `className` | `string` | Custom CSS class names |
|
|
257
|
+
| `...` | `ButtonHTMLAttributes<HTMLButtonElement>` | All standard HTML button attributes |
|
|
258
|
+
|
|
259
|
+
**Note:** The button is disabled when the clock status is `"idle"`.
|
|
260
|
+
|
|
261
|
+
#### Example
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
{
|
|
265
|
+
/* Reset with same time control */
|
|
266
|
+
}
|
|
267
|
+
<ChessClock.Reset>Reset</ChessClock.Reset>;
|
|
268
|
+
|
|
269
|
+
{
|
|
270
|
+
/* Reset with new time control */
|
|
271
|
+
}
|
|
272
|
+
<ChessClock.Reset timeControl="10+5">Change to 10+5</ChessClock.Reset>;
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Using asChild Pattern
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
<ChessClock.Reset asChild>
|
|
279
|
+
<div className="custom-reset">Reset</div>
|
|
280
|
+
</ChessClock.Reset>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Hooks
|
|
284
|
+
|
|
285
|
+
### useChessClock
|
|
286
|
+
|
|
287
|
+
Create clock state without the Root component (for custom integrations).
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { useChessClock } from "@react-chess-tools/react-chess-clock";
|
|
291
|
+
|
|
292
|
+
function MyClock() {
|
|
293
|
+
const clock = useChessClock({
|
|
294
|
+
time: "5+3",
|
|
295
|
+
onTimeout: (loser) => console.log(`${loser} loses`),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div>
|
|
300
|
+
<div>White: {clock.times.white}ms</div>
|
|
301
|
+
<div>Black: {clock.times.black}ms</div>
|
|
302
|
+
<button onClick={clock.methods.switch}>Switch</button>
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### Return Values
|
|
309
|
+
|
|
310
|
+
| Property | Type | Description |
|
|
311
|
+
| -------------------- | ------------------------------------------------------ | ----------------------------------- |
|
|
312
|
+
| `times` | `{ white: number; black: number }` | Current times in milliseconds |
|
|
313
|
+
| `initialTimes` | `{ white: number; black: number }` | Initial times in milliseconds |
|
|
314
|
+
| `status` | `ClockStatus` | Current clock status |
|
|
315
|
+
| `activePlayer` | `"white" \| "black" \| null` | Currently active player |
|
|
316
|
+
| `timeout` | `"white" \| "black" \| null` | Which player timed out |
|
|
317
|
+
| `timingMethod` | `TimingMethod` | Configured timing method |
|
|
318
|
+
| `info` | `ClockInfo` | Computed clock information |
|
|
319
|
+
| `currentPeriodIndex` | `{ white: number; black: number }` | Current period index (multi-period) |
|
|
320
|
+
| `totalPeriods` | `number` | Total number of periods |
|
|
321
|
+
| `currentPeriod` | `{ white: TimeControlPhase; black: TimeControlPhase }` | Current periods |
|
|
322
|
+
| `periodMoves` | `{ white: number; black: number }` | Moves in current period |
|
|
323
|
+
| `methods` | `ClockMethods` | Methods to control the clock |
|
|
324
|
+
|
|
325
|
+
#### Methods
|
|
326
|
+
|
|
327
|
+
| Method | Type | Description |
|
|
328
|
+
| --------- | ---------------------------------------------------- | -------------------------------------------------- |
|
|
329
|
+
| `start` | `() => void` | Start the clock |
|
|
330
|
+
| `pause` | `() => void` | Pause the clock |
|
|
331
|
+
| `resume` | `() => void` | Resume the clock |
|
|
332
|
+
| `switch` | `() => void` | Switch active player |
|
|
333
|
+
| `reset` | `(timeControl?: TimeControlInput) => void` | Reset the clock (optionally with new time control) |
|
|
334
|
+
| `addTime` | `(player: ClockColor, milliseconds: number) => void` | Add time to a player |
|
|
335
|
+
| `setTime` | `(player: ClockColor, milliseconds: number) => void` | Set a player's time |
|
|
336
|
+
|
|
337
|
+
### useChessClockContext
|
|
338
|
+
|
|
339
|
+
Access the clock state from any child component within a `ChessClock.Root`.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
import { useChessClockContext } from "@react-chess-tools/react-chess-clock";
|
|
343
|
+
|
|
344
|
+
function ClockInfo() {
|
|
345
|
+
const { times, activePlayer, status, methods } = useChessClockContext();
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<div>
|
|
349
|
+
<p>Active: {activePlayer}</p>
|
|
350
|
+
<p>Status: {status}</p>
|
|
351
|
+
<button onClick={() => methods.pause()}>Pause</button>
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### useOptionalChessClock
|
|
358
|
+
|
|
359
|
+
Safely access clock context when the component may be used outside `ChessClock.Root`.
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
import { useOptionalChessClock } from "@react-chess-tools/react-chess-clock";
|
|
363
|
+
|
|
364
|
+
function MaybeInsideClock() {
|
|
365
|
+
const clock = useOptionalChessClock();
|
|
366
|
+
|
|
367
|
+
if (!clock) {
|
|
368
|
+
return <div>No clock context</div>;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return <div>White time: {clock.times.white}</div>;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Time Control Formats
|
|
376
|
+
|
|
377
|
+
### String Notation
|
|
378
|
+
|
|
379
|
+
Compact string format for simple time controls:
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
// "minutes+seconds" format
|
|
383
|
+
timeControl={{ time: "5+3" }} // 5 minutes, 3 second increment
|
|
384
|
+
timeControl={{ time: "10" }} // 10 minutes, no increment
|
|
385
|
+
timeControl={{ time: "3+2" }} // 3 minutes, 2 second increment
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Object Configuration
|
|
389
|
+
|
|
390
|
+
Full control over timing parameters:
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
timeControl={{
|
|
394
|
+
time: {
|
|
395
|
+
baseTime: 300, // Base time in seconds
|
|
396
|
+
increment: 3, // Increment in seconds (Fischer/Bronstein)
|
|
397
|
+
delay: 5, // Delay in seconds (delay/Bronstein methods)
|
|
398
|
+
},
|
|
399
|
+
}}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Multi-Period Tournament Controls
|
|
403
|
+
|
|
404
|
+
FIDE/USCF style tournament time controls with move counts:
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
timeControl={{
|
|
408
|
+
time: [
|
|
409
|
+
{ baseTime: 5400, increment: 30, moves: 40 }, // 90min + 30sec/move for 40 moves
|
|
410
|
+
{ baseTime: 1800, increment: 30, moves: 20 }, // 30min + 30sec/move for 20 moves
|
|
411
|
+
{ baseTime: 900, increment: 30 }, // 15min + 30sec/move sudden death
|
|
412
|
+
],
|
|
413
|
+
clockStart: "manual",
|
|
414
|
+
}}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Presets
|
|
418
|
+
|
|
419
|
+
Common time control presets are available:
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
import { presets } from "@react-chess-tools/react-chess-clock";
|
|
423
|
+
|
|
424
|
+
timeControl={{ time: presets.blitz5_3 }}
|
|
425
|
+
timeControl={{ time: presets.fideClassical }}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Available presets:
|
|
429
|
+
|
|
430
|
+
- Bullet: `bullet1_0`, `bullet1_1`, `bullet2_1`
|
|
431
|
+
- Blitz: `blitz3_0`, `blitz3_2`, `blitz5_0`, `blitz5_3`
|
|
432
|
+
- Rapid: `rapid10_0`, `rapid10_5`, `rapid15_10`
|
|
433
|
+
- Classical: `classical30_0`, `classical90_30`
|
|
434
|
+
- Tournament: `fideClassical`, `uscfClassical`
|
|
435
|
+
|
|
436
|
+
## Timing Methods
|
|
437
|
+
|
|
438
|
+
### Fischer (Default)
|
|
439
|
+
|
|
440
|
+
Standard increment - time is added after each move completes.
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
timeControl={{
|
|
444
|
+
time: { baseTime: 300, increment: 3 },
|
|
445
|
+
timingMethod: "fischer",
|
|
446
|
+
}}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Simple Delay
|
|
450
|
+
|
|
451
|
+
Countdown waits for the delay period before decrementing. If you move within the delay, no time is used.
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
timeControl={{
|
|
455
|
+
time: { baseTime: 300, delay: 5 },
|
|
456
|
+
timingMethod: "delay",
|
|
457
|
+
}}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Bronstein Delay
|
|
461
|
+
|
|
462
|
+
Adds back the actual time used, up to the delay amount. Like delay but you always get at least the delay amount back.
|
|
463
|
+
|
|
464
|
+
```tsx
|
|
465
|
+
timeControl={{
|
|
466
|
+
time: { baseTime: 300, delay: 3 },
|
|
467
|
+
timingMethod: "bronstein",
|
|
468
|
+
}}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Clock Start Modes
|
|
472
|
+
|
|
473
|
+
### Delayed (Default - Lichess style)
|
|
474
|
+
|
|
475
|
+
Clock doesn't start until after Black's first move.
|
|
476
|
+
|
|
477
|
+
```tsx
|
|
478
|
+
timeControl={{ time: "5+3", clockStart: "delayed" }}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Sequence: White moves → Black moves → **Clock starts for White**
|
|
482
|
+
|
|
483
|
+
### Immediate (Chess.com style)
|
|
484
|
+
|
|
485
|
+
White's clock starts immediately on the first switch.
|
|
486
|
+
|
|
487
|
+
```tsx
|
|
488
|
+
timeControl={{ time: "5+3", clockStart: "immediate" }}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Manual
|
|
492
|
+
|
|
493
|
+
Clock starts only when user explicitly calls `start()`.
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
timeControl={{ time: "5+3", clockStart: "manual" }}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Examples
|
|
500
|
+
|
|
501
|
+
### Basic Clock
|
|
502
|
+
|
|
503
|
+
```tsx
|
|
504
|
+
import { ChessClock } from "@react-chess-tools/react-chess-clock";
|
|
505
|
+
|
|
506
|
+
function BasicClock() {
|
|
507
|
+
return (
|
|
508
|
+
<ChessClock.Root timeControl={{ time: "5+3" }}>
|
|
509
|
+
<ChessClock.Display color="white" />
|
|
510
|
+
<ChessClock.Display color="black" />
|
|
511
|
+
<ChessClock.PlayPause />
|
|
512
|
+
<ChessClock.Reset>Reset</ChessClock.Reset>
|
|
513
|
+
</ChessClock.Root>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Time Odds
|
|
519
|
+
|
|
520
|
+
Give one player less time:
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
function TimeOddsClock() {
|
|
524
|
+
return (
|
|
525
|
+
<ChessClock.Root
|
|
526
|
+
timeControl={{
|
|
527
|
+
time: "5+3",
|
|
528
|
+
whiteTime: 180, // White gets 3 minutes
|
|
529
|
+
blackTime: 300, // Black gets 5 minutes
|
|
530
|
+
}}
|
|
531
|
+
>
|
|
532
|
+
<ChessClock.Display color="white" />
|
|
533
|
+
<ChessClock.Display color="black" />
|
|
534
|
+
<ChessClock.PlayPause />
|
|
535
|
+
</ChessClock.Root>
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Tournament Control
|
|
541
|
+
|
|
542
|
+
FIDE Classical time control:
|
|
543
|
+
|
|
544
|
+
```tsx
|
|
545
|
+
function TournamentClock() {
|
|
546
|
+
return (
|
|
547
|
+
<ChessClock.Root
|
|
548
|
+
timeControl={{
|
|
549
|
+
time: [
|
|
550
|
+
{ baseTime: 5400, increment: 30, moves: 40 },
|
|
551
|
+
{ baseTime: 1800, increment: 30, moves: 20 },
|
|
552
|
+
{ baseTime: 900, increment: 30 },
|
|
553
|
+
],
|
|
554
|
+
clockStart: "manual",
|
|
555
|
+
}}
|
|
556
|
+
>
|
|
557
|
+
<ChessClock.Display color="white" />
|
|
558
|
+
<ChessClock.Display color="black" />
|
|
559
|
+
<ChessClock.PlayPause />
|
|
560
|
+
<ChessClock.Reset>Reset</ChessClock.Reset>
|
|
561
|
+
</ChessClock.Root>
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### Styled Clock
|
|
567
|
+
|
|
568
|
+
Using data attributes for styling:
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
function StyledClock() {
|
|
572
|
+
return (
|
|
573
|
+
<ChessClock.Root timeControl={{ time: "5+3" }}>
|
|
574
|
+
<ChessClock.Display
|
|
575
|
+
color="white"
|
|
576
|
+
className="clock-display"
|
|
577
|
+
style={{ padding: "10px 20px", fontSize: "24px" }}
|
|
578
|
+
/>
|
|
579
|
+
<ChessClock.Display
|
|
580
|
+
color="black"
|
|
581
|
+
className="clock-display"
|
|
582
|
+
style={{ padding: "10px 20px", fontSize: "24px" }}
|
|
583
|
+
/>
|
|
584
|
+
<ChessClock.PlayPause />
|
|
585
|
+
</ChessClock.Root>
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
```css
|
|
591
|
+
/* CSS */
|
|
592
|
+
.clock-display {
|
|
593
|
+
font-family: monospace;
|
|
594
|
+
border: 2px solid #333;
|
|
595
|
+
border-radius: 4px;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
[data-clock-color="white"] {
|
|
599
|
+
background: white;
|
|
600
|
+
color: black;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
[data-clock-color="black"] {
|
|
604
|
+
background: black;
|
|
605
|
+
color: white;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
[data-clock-active="true"] {
|
|
609
|
+
border-color: gold;
|
|
610
|
+
box-shadow: 0 0 10px gold;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
[data-clock-timeout="true"] {
|
|
614
|
+
background: red;
|
|
615
|
+
color: white;
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Server Synchronization
|
|
620
|
+
|
|
621
|
+
For online play, sync clock state with server times:
|
|
622
|
+
|
|
623
|
+
```tsx
|
|
624
|
+
function ServerSyncedClock() {
|
|
625
|
+
const { times, methods } = useChessClockContext();
|
|
626
|
+
const [serverTime, setServerTime] = useState({
|
|
627
|
+
white: 300000,
|
|
628
|
+
black: 300000,
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Sync time from server
|
|
632
|
+
useEffect(() => {
|
|
633
|
+
const interval = setInterval(() => {
|
|
634
|
+
// Fetch from server...
|
|
635
|
+
methods.setTime("white", serverTime.white);
|
|
636
|
+
methods.setTime("black", serverTime.black);
|
|
637
|
+
}, 1000);
|
|
638
|
+
return () => clearInterval(interval);
|
|
639
|
+
}, [serverTime, methods]);
|
|
640
|
+
|
|
641
|
+
return (
|
|
642
|
+
<>
|
|
643
|
+
<ChessClock.Display color="white" />
|
|
644
|
+
<ChessClock.Display color="black" />
|
|
645
|
+
</>
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Custom Status Display
|
|
651
|
+
|
|
652
|
+
```tsx
|
|
653
|
+
import { useChessClockContext } from "@react-chess-tools/react-chess-clock";
|
|
654
|
+
|
|
655
|
+
function ClockStatus() {
|
|
656
|
+
const { status, activePlayer, times } = useChessClockContext();
|
|
657
|
+
|
|
658
|
+
const formatTime = (ms: number) => {
|
|
659
|
+
const seconds = Math.floor(ms / 1000);
|
|
660
|
+
const minutes = Math.floor(seconds / 60);
|
|
661
|
+
const secs = seconds % 60;
|
|
662
|
+
return `${minutes}:${secs.toString().padStart(2, "0")}`;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
if (status === "finished") {
|
|
666
|
+
return <div className="status">Game Over - Time forfeit</div>;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return (
|
|
670
|
+
<div className="status">
|
|
671
|
+
<p>Active: {activePlayer === "white" ? "White" : "Black"}</p>
|
|
672
|
+
<p>White: {formatTime(times.white)}</p>
|
|
673
|
+
<p>Black: {formatTime(times.black)}</p>
|
|
674
|
+
</div>
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function ClockWithStatus() {
|
|
679
|
+
return (
|
|
680
|
+
<ChessClock.Root timeControl={{ time: "5+3" }}>
|
|
681
|
+
<ClockStatus />
|
|
682
|
+
<ChessClock.Display color="white" />
|
|
683
|
+
<ChessClock.Display color="black" />
|
|
684
|
+
<ChessClock.PlayPause />
|
|
685
|
+
<ChessClock.Reset>Reset</ChessClock.Reset>
|
|
686
|
+
</ChessClock.Root>
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
## License
|
|
692
|
+
|
|
693
|
+
This project is [MIT](https://opensource.org/licenses/MIT) licensed.
|
|
694
|
+
|
|
695
|
+
## Show Your Support
|
|
696
|
+
|
|
697
|
+
Give a star if this project helped you!
|