@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +697 -0
  3. package/dist/index.cjs +1014 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +528 -0
  6. package/dist/index.d.ts +528 -0
  7. package/dist/index.js +969 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +63 -0
  10. package/src/components/ChessClock/ChessClock.stories.tsx +782 -0
  11. package/src/components/ChessClock/index.ts +44 -0
  12. package/src/components/ChessClock/parts/Display.tsx +69 -0
  13. package/src/components/ChessClock/parts/PlayPause.tsx +190 -0
  14. package/src/components/ChessClock/parts/Reset.tsx +90 -0
  15. package/src/components/ChessClock/parts/Root.tsx +37 -0
  16. package/src/components/ChessClock/parts/Switch.tsx +84 -0
  17. package/src/components/ChessClock/parts/__tests__/Display.test.tsx +149 -0
  18. package/src/components/ChessClock/parts/__tests__/PlayPause.test.tsx +411 -0
  19. package/src/components/ChessClock/parts/__tests__/Reset.test.tsx +160 -0
  20. package/src/components/ChessClock/parts/__tests__/Root.test.tsx +49 -0
  21. package/src/components/ChessClock/parts/__tests__/Switch.test.tsx +204 -0
  22. package/src/hooks/__tests__/clockReducer.test.ts +985 -0
  23. package/src/hooks/__tests__/useChessClock.test.tsx +1080 -0
  24. package/src/hooks/clockReducer.ts +379 -0
  25. package/src/hooks/useChessClock.ts +406 -0
  26. package/src/hooks/useChessClockContext.ts +35 -0
  27. package/src/index.ts +65 -0
  28. package/src/types.ts +217 -0
  29. package/src/utils/__tests__/calculateSwitchTime.test.ts +150 -0
  30. package/src/utils/__tests__/formatTime.test.ts +83 -0
  31. package/src/utils/__tests__/timeControl.test.ts +414 -0
  32. package/src/utils/__tests__/timingMethods.test.ts +170 -0
  33. package/src/utils/calculateSwitchTime.ts +37 -0
  34. package/src/utils/formatTime.ts +59 -0
  35. package/src/utils/presets.ts +47 -0
  36. package/src/utils/timeControl.ts +205 -0
  37. package/src/utils/timingMethods.ts +103 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @react-chess-tools/react-chess-clock
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 93e1029: feat: add chess clock
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
+ [![npm version](https://img.shields.io/npm/v/@react-chess-tools/react-chess-clock.svg)](https://www.npmjs.com/package/@react-chess-tools/react-chess-clock)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@react-chess-tools/react-chess-clock.svg)](https://www.npmjs.com/package/@react-chess-tools/react-chess-clock)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](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!