@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ChessClock/parts/Root.tsx","../src/hooks/useChessClockContext.ts","../src/hooks/useChessClock.ts","../src/utils/timeControl.ts","../src/utils/formatTime.ts","../src/utils/timingMethods.ts","../src/utils/calculateSwitchTime.ts","../src/hooks/clockReducer.ts","../src/components/ChessClock/parts/Display.tsx","../src/components/ChessClock/parts/Switch.tsx","../src/components/ChessClock/parts/PlayPause.tsx","../src/components/ChessClock/parts/Reset.tsx","../src/components/ChessClock/index.ts","../src/utils/presets.ts"],"sourcesContent":["import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ChessClockContext } from \"../../../hooks/useChessClockContext\";\nimport { useChessClock } from \"../../../hooks/useChessClock\";\nimport type { TimeControlConfig } from \"../../../types\";\n\nexport interface ChessClockRootProps {\n timeControl: TimeControlConfig;\n children: ReactNode;\n}\n\n/**\n * ChessClock.Root - Context provider for chess clock components\n * Manages clock state and provides it to child components\n *\n * @example\n * ```tsx\n * <ChessClock.Root timeControl={{ time: \"5+3\" }}>\n * <ChessClock.Display color=\"white\" />\n * <ChessClock.Display color=\"black\" />\n * </ChessClock.Root>\n * ```\n */\nexport const Root: React.FC<React.PropsWithChildren<ChessClockRootProps>> = ({\n timeControl,\n children,\n}) => {\n const clockState = useChessClock(timeControl);\n\n return (\n <ChessClockContext.Provider value={clockState}>\n {children}\n </ChessClockContext.Provider>\n );\n};\n\nRoot.displayName = \"ChessClock.Root\";\n","import { createContext, useContext } from \"react\";\nimport type { UseChessClockReturn } from \"../types\";\n\n/**\n * Context for chess clock state\n * Provided by ChessClock.Root and consumed by child components\n */\nexport const ChessClockContext = createContext<UseChessClockReturn | null>(\n null,\n);\n\n/**\n * Hook to access chess clock context from child components\n * Must be used within a ChessClock.Root provider\n *\n * @throws Error if used outside of ChessClock.Root\n * @returns Chess clock state and methods\n */\nexport function useChessClockContext(): UseChessClockReturn {\n const context = useContext(ChessClockContext);\n\n if (!context) {\n throw new Error(\n \"useChessClockContext must be used within a ChessClock.Root component. \" +\n \"Make sure your component is wrapped with <ChessClock.Root>.\",\n );\n }\n\n return context;\n}\n\n/**\n * Type alias for the context value type\n */\nexport type ChessClockContextType = UseChessClockReturn;\n","import {\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from \"react\";\nimport type {\n ClockColor,\n ClockInfo,\n ClockMethods,\n ClockTimes,\n PeriodState,\n TimeControlConfig,\n TimeControlInput,\n UseChessClockReturn,\n} from \"../types\";\nimport { parseTimeControlConfig, getInitialTimes } from \"../utils/timeControl\";\nimport { formatClockTime } from \"../utils/formatTime\";\nimport { calculateSwitchTime } from \"../utils/calculateSwitchTime\";\nimport {\n getInitialActivePlayer,\n getInitialStatus,\n} from \"../utils/timingMethods\";\nimport { clockReducer, createInitialClockState } from \"./clockReducer\";\n\n/** Default config used internally when clock is disabled */\nconst DISABLED_CLOCK_CONFIG: TimeControlConfig = {\n time: { baseTime: 0 },\n};\n\n/**\n * Serializes time-relevant config for change detection.\n * Only includes properties that should trigger a clock reset.\n * Callbacks are intentionally excluded - they use the ref pattern.\n */\nfunction serializeTimeRelevantConfig(config: TimeControlConfig): string {\n return JSON.stringify({\n time: config.time,\n timingMethod: config.timingMethod,\n clockStart: config.clockStart,\n whiteTime: config.whiteTime,\n blackTime: config.blackTime,\n });\n}\n\nfunction calculateDisplayTime(\n baseTime: number,\n moveStartTime: number | null,\n elapsedAtPause: number,\n timingMethod: string,\n delay: number,\n): number {\n // When paused, use the elapsed time stored at pause moment\n if (moveStartTime === null) {\n let effectiveElapsed = elapsedAtPause;\n if (timingMethod === \"delay\") {\n effectiveElapsed = Math.max(0, elapsedAtPause - delay);\n }\n return Math.max(0, baseTime - effectiveElapsed);\n }\n\n const now = Date.now();\n const rawElapsed = now - moveStartTime;\n\n // Apply delay method: time doesn't decrement during delay period\n let effectiveElapsed = rawElapsed;\n if (timingMethod === \"delay\") {\n effectiveElapsed = Math.max(0, rawElapsed - delay);\n }\n\n return Math.max(0, baseTime - effectiveElapsed);\n}\n\n/**\n * Main hook for chess clock state management.\n * Provides timing functionality for chess games with various timing methods.\n *\n * For server-authoritative clocks, use `methods.setTime()` to sync server times.\n * The clock will restart its display interpolation from the new value on each call.\n *\n * **Auto-reset behavior:** The clock automatically resets when time-relevant\n * options change (`time`, `timingMethod`, `clockStart`, `whiteTime`, `blackTime`).\n * Callbacks (`onTimeout`, `onSwitch`, `onTimeUpdate`) do not trigger a reset\n * and can be changed without affecting clock state.\n *\n * @param options - Clock configuration options\n * @returns Clock state, info, and methods\n */\nexport function useChessClock(options: TimeControlConfig): UseChessClockReturn {\n // Initialize reducer with computed initial state.\n // All parsing happens inside the initializer function to ensure it only runs once\n // (on mount), avoiding wasteful re-computation when options is passed inline\n // as a new object reference on every render.\n const [state, dispatch] = useReducer(clockReducer, null, () => {\n const initialConfig = parseTimeControlConfig(options);\n const initialTimesValue = getInitialTimes(initialConfig);\n\n // Initialize period state for multi-period time controls\n const initialPeriodState: PeriodState | undefined =\n initialConfig.periods && initialConfig.periods.length > 1\n ? {\n periodIndex: { white: 0, black: 0 },\n periodMoves: { white: 0, black: 0 },\n periods: initialConfig.periods,\n }\n : undefined;\n\n return createInitialClockState(\n initialTimesValue,\n getInitialStatus(initialConfig.clockStart),\n getInitialActivePlayer(initialConfig.clockStart),\n initialConfig,\n initialPeriodState,\n );\n });\n\n // Options ref for callbacks (avoid stale closures)\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n // State ref for callbacks (avoid stale closures)\n const stateRef = useRef(state);\n stateRef.current = state;\n\n // ============================================================================\n // AUTO-RESET ON OPTIONS CHANGE\n // ============================================================================\n\n // Auto-reset when time-relevant options change (not callbacks)\n const configKey = serializeTimeRelevantConfig(options);\n const prevConfigRef = useRef<string>(configKey);\n\n useEffect(() => {\n if (prevConfigRef.current !== configKey) {\n prevConfigRef.current = configKey;\n dispatch({ type: \"RESET\", payload: { ...options, now: Date.now() } });\n }\n }, [configKey, options]);\n\n // ============================================================================\n // DISPLAY STATE\n // ============================================================================\n\n // Tick counter for triggering re-renders when clock is running\n const [tick, forceUpdate] = useState(0);\n\n // Display times derived during render (no useMemo — state values change frequently)\n const displayTimes: ClockTimes =\n state.activePlayer === null\n ? state.times\n : {\n ...state.times,\n [state.activePlayer]: calculateDisplayTime(\n state.times[state.activePlayer],\n state.moveStartTime,\n state.elapsedAtPause,\n state.config.timingMethod,\n state.config.delay,\n ),\n };\n\n // Derive primitive for timeout detection (prevents effect running on every render)\n const activePlayerTimedOut =\n state.status === \"running\" &&\n state.activePlayer !== null &&\n displayTimes[state.activePlayer] <= 0;\n\n // ============================================================================\n // DISPLAY UPDATE LOOP (100ms interval)\n // ============================================================================\n\n useEffect(() => {\n if (state.status !== \"running\" || state.activePlayer === null) {\n return;\n }\n\n const intervalId = setInterval(() => forceUpdate((c) => c + 1), 100);\n\n return () => clearInterval(intervalId);\n }, [state.status, state.activePlayer]);\n\n // Notify consumer of time updates (only on clock ticks, not on every render)\n useEffect(() => {\n if (state.status === \"running\" && state.activePlayer !== null) {\n optionsRef.current.onTimeUpdate?.(displayTimes);\n }\n }, [tick]);\n\n // Timeout Detection\n useEffect(() => {\n if (activePlayerTimedOut && state.activePlayer !== null) {\n dispatch({ type: \"TIMEOUT\", payload: { player: state.activePlayer } });\n optionsRef.current.onTimeout?.(state.activePlayer);\n }\n }, [activePlayerTimedOut, state.activePlayer]);\n\n // Active Player Change Callback\n const previousActivePlayerRef = useRef(state.activePlayer);\n useEffect(() => {\n if (\n state.activePlayer !== previousActivePlayerRef.current &&\n state.activePlayer !== null\n ) {\n optionsRef.current.onSwitch?.(state.activePlayer);\n }\n previousActivePlayerRef.current = state.activePlayer;\n }, [state.activePlayer]);\n\n // ============================================================================\n // COMPUTED INFO\n // ============================================================================\n\n const info = useMemo<ClockInfo>(\n () => ({\n isRunning: state.status === \"running\",\n isPaused: state.status === \"paused\",\n isFinished: state.status === \"finished\",\n isWhiteActive: state.activePlayer === \"white\",\n isBlackActive: state.activePlayer === \"black\",\n hasTimeout: state.timeout !== null,\n // Time odds is based on initial configuration, not current remaining time\n hasTimeOdds: state.initialTimes.white !== state.initialTimes.black,\n }),\n [\n state.status,\n state.activePlayer,\n state.timeout,\n state.initialTimes.white,\n state.initialTimes.black,\n ],\n );\n\n // ============================================================================\n // METHODS\n // ============================================================================\n\n const start = useCallback(() => {\n dispatch({ type: \"START\", payload: { now: Date.now() } });\n }, []);\n\n const pause = useCallback(() => {\n dispatch({ type: \"PAUSE\", payload: { now: Date.now() } });\n }, []);\n\n const resume = useCallback(() => {\n dispatch({ type: \"RESUME\", payload: { now: Date.now() } });\n }, []);\n\n const switchPlayer = useCallback(() => {\n const currentState = stateRef.current;\n const now = Date.now();\n\n // Calculate time adjustment for current player (not in delayed mode)\n let newTimes: ClockTimes | undefined;\n if (\n currentState.status !== \"delayed\" &&\n currentState.activePlayer &&\n currentState.moveStartTime !== null\n ) {\n const timeSpent = now - currentState.moveStartTime;\n const currentTime = currentState.times[currentState.activePlayer];\n\n const newTime = calculateSwitchTime(\n currentTime,\n timeSpent,\n currentState.config,\n );\n\n newTimes = {\n ...currentState.times,\n [currentState.activePlayer]: newTime,\n };\n }\n\n dispatch({ type: \"SWITCH\", payload: { newTimes, now } });\n }, []);\n\n const reset = useCallback((newTimeControl?: TimeControlInput) => {\n // Build the full config for the reducer\n const currentOptions = optionsRef.current;\n const resetConfig: TimeControlConfig = newTimeControl\n ? { ...currentOptions, time: newTimeControl }\n : currentOptions;\n\n // The reducer will parse the config, update state.config, and reset timing state\n dispatch({ type: \"RESET\", payload: { ...resetConfig, now: Date.now() } });\n }, []);\n\n const addTime = useCallback((player: ClockColor, milliseconds: number) => {\n dispatch({\n type: \"ADD_TIME\",\n payload: { player, milliseconds, now: Date.now() },\n });\n // Note: when adding time while clock is running, we update the base time\n // but moveStartTime stays the same, so the display interpolates correctly\n }, []);\n\n const setTime = useCallback((player: ClockColor, milliseconds: number) => {\n dispatch({\n type: \"SET_TIME\",\n payload: { player, milliseconds, now: Date.now() },\n });\n // Note: when setting time while clock is running, we update the base time\n // but moveStartTime stays the same, so the display interpolates correctly\n }, []);\n\n // Memoize methods\n const methods = useMemo<ClockMethods>(\n () => ({\n start,\n pause,\n resume,\n switch: switchPlayer,\n reset,\n addTime,\n setTime,\n }),\n [start, pause, resume, switchPlayer, reset, addTime, setTime],\n );\n\n // Computed period information\n const periodInfo = useMemo(() => {\n if (!state.periodState) {\n // Single period: return defaults\n return {\n currentPeriodIndex: { white: 0, black: 0 },\n totalPeriods: 1,\n currentPeriod: {\n white: {\n baseTime: state.config.baseTime / 1000,\n increment: state.config.increment / 1000,\n delay: state.config.delay / 1000,\n },\n black: {\n baseTime: state.config.baseTime / 1000,\n increment: state.config.increment / 1000,\n delay: state.config.delay / 1000,\n },\n },\n periodMoves: { white: 0, black: 0 },\n };\n }\n\n // Get current period for each player (with bounds checking)\n // Convert from internal milliseconds back to seconds for public API\n const periods = state.periodState.periods;\n const getCurrentPeriod = (periodIndex: number) => {\n const period =\n periodIndex >= 0 && periodIndex < periods.length\n ? periods[periodIndex]\n : periods[0];\n return {\n ...period,\n baseTime: period.baseTime / 1000,\n increment:\n period.increment !== undefined ? period.increment / 1000 : undefined,\n delay: period.delay !== undefined ? period.delay / 1000 : undefined,\n };\n };\n\n return {\n currentPeriodIndex: state.periodState.periodIndex,\n totalPeriods: periods.length,\n currentPeriod: {\n white: getCurrentPeriod(state.periodState.periodIndex.white),\n black: getCurrentPeriod(state.periodState.periodIndex.black),\n },\n periodMoves: state.periodState.periodMoves,\n };\n }, [state.periodState, state.config]);\n\n return {\n times: displayTimes,\n initialTimes: state.initialTimes,\n status: state.status,\n activePlayer: state.activePlayer,\n timeout: state.timeout,\n timingMethod: state.config.timingMethod,\n info,\n ...periodInfo,\n methods,\n };\n}\n\n/**\n * Optional chess clock hook for cases where clock may not be needed\n * Maintains hook order by always calling the implementation internally\n *\n * @param options - Clock configuration options, or undefined to disable\n * @returns Clock state, info, and methods, or null if disabled\n */\nexport function useOptionalChessClock(\n options?: TimeControlConfig,\n): ReturnType<typeof useChessClock> | null {\n // Always call useChessClock to maintain hook order\n const result = useChessClock(options ?? DISABLED_CLOCK_CONFIG);\n\n return options === undefined ? null : result;\n}\n\n/**\n * Export the format function for convenience\n */\nexport { formatClockTime };\n","import type {\n NormalizedTimeControl,\n TimeControl,\n TimeControlConfig,\n TimeControlInput,\n TimeControlPhase,\n TimeControlString,\n} from \"../types\";\n\n/**\n * Regex to match time control strings like \"5+3\", \"10\", or \"0.5\"\n */\nconst TIME_CONTROL_REGEX = /^(\\d+(?:\\.\\d+)?)(?:\\+(\\d+(?:\\.\\d+)?))?$/;\n\n/**\n * Regex to match a single period in multi-period time control\n * Matches: \"40/90+30\", \"sd/30+30\", \"SD/30+30\", \"G/30+30\", \"40/90\"\n * Group 1: moves count (e.g., \"40\" in \"40/90\")\n * Group 2: time in minutes (e.g., \"90\" in \"40/90+30\")\n * Group 3: increment in seconds (e.g., \"30\" in \"40/90+30\")\n */\nconst PERIOD_REGEX =\n /^(?:(?:(\\d+)|sd|g)\\/)?(\\d+(?:\\.\\d+)?)(?:\\+(\\d+(?:\\.\\d+)?))?$/i;\n\n/**\n * Parse a time control string into a TimeControl object\n * @param input - Time control string (e.g., \"5+3\", \"10\")\n * @returns Parsed time control with baseTime in seconds\n * @throws Error if input is invalid\n */\nexport function parseTimeControlString(input: TimeControlString): TimeControl {\n const match = input.match(TIME_CONTROL_REGEX);\n if (!match) {\n throw new Error(\n `Invalid time control string: \"${input}\". Expected format: \"5+3\" (minutes + increment) or \"10\" (minutes only)`,\n );\n }\n\n const minutes = parseFloat(match[1]);\n const increment = match[2] ? parseFloat(match[2]) : 0;\n\n // Convert minutes to seconds\n return {\n baseTime: Math.round(minutes * 60),\n increment: Math.round(increment),\n };\n}\n\n/**\n * Parse a single period string into a TimeControlPhase object\n * @param periodStr - Period string (e.g., \"40/90+30\", \"sd/30+30\", \"G/30\")\n * @returns Parsed period with baseTime in seconds\n * @throws Error if input is invalid\n */\nfunction parsePeriod(periodStr: string): TimeControlPhase {\n const trimmed = periodStr.trim();\n const match = trimmed.match(PERIOD_REGEX);\n\n if (!match) {\n throw new Error(\n `Invalid period format: \"${trimmed}\". Expected format: \"40/90+30\" or \"sd/30+30\"`,\n );\n }\n\n const [, movesStr, timeStr, incrementStr] = match;\n const moves = movesStr ? parseInt(movesStr, 10) : undefined;\n const minutes = parseFloat(timeStr);\n const increment = incrementStr ? parseFloat(incrementStr) : 0;\n\n return {\n baseTime: Math.round(minutes * 60),\n increment: increment > 0 ? Math.round(increment) : undefined,\n moves,\n };\n}\n\n/**\n * Parse a multi-period time control string into an array of TimeControlPhase\n * Format: \"40/90+30,sd/30+30\" or \"40/120+30,20/60+30,g/15+30\"\n *\n * Period format: [moves/]time[+increment] or [SD|G/]time[+increment]\n * - moves: Number of moves required for this period (e.g., \"40\" in \"40/90+30\")\n * - time: Time in minutes for this period (e.g., \"90\" in \"40/90+30\")\n * - increment: Increment in seconds after each move (e.g., \"30\" in \"40/90+30\")\n * - SD or G prefix: Sudden death period (no moves requirement, overrides any moves prefix)\n *\n * @param input - Multi-period time control string\n * @returns Array of TimeControlPhase with times in seconds\n * @throws Error if input is invalid\n *\n * @example\n * ```ts\n * parseMultiPeriodTimeControl(\"40/90+30,sd/30+30\")\n * // Returns: [\n * // { baseTime: 5400, increment: 30, moves: 40 },\n * // { baseTime: 1800, increment: 30 }\n * // ]\n * ```\n */\nexport function parseMultiPeriodTimeControl(input: string): TimeControlPhase[] {\n const parts = input.split(/\\s*,\\s*/);\n\n // Note: input.split() always returns at least one element, so this is safe\n return parts.map((part) => parsePeriod(part));\n}\n\n/**\n * Normalize any time control input into a NormalizedTimeControl\n * @param input - Time control string, object, or array of periods\n * @param timingMethod - Optional timing method (defaults to \"fischer\")\n * @param clockStart - Optional clock start mode (defaults to \"delayed\")\n * @returns Normalized time control with times in milliseconds\n */\nexport function normalizeTimeControl(\n input: TimeControlInput,\n timingMethod: NormalizedTimeControl[\"timingMethod\"],\n clockStart: NormalizedTimeControl[\"clockStart\"],\n): NormalizedTimeControl {\n // Handle multi-period time control\n if (Array.isArray(input)) {\n if (input.length === 0) {\n throw new Error(\n \"Multi-period time control must have at least one period\",\n );\n }\n\n // Clone the input to avoid mutating the caller's data\n // If the last period has a moves requirement, remove it (final period should be sudden death)\n // Convert all period times from seconds to milliseconds for internal consistency\n const periods: TimeControlPhase[] = input.map((period) => ({\n ...period,\n baseTime: period.baseTime * 1000,\n increment:\n period.increment !== undefined ? period.increment * 1000 : undefined,\n delay: period.delay !== undefined ? period.delay * 1000 : undefined,\n }));\n const lastPeriod = periods[periods.length - 1];\n if (lastPeriod.moves !== undefined) {\n lastPeriod.moves = undefined;\n }\n\n const firstPeriod = periods[0];\n return {\n baseTime: firstPeriod.baseTime,\n increment: firstPeriod.increment ?? 0,\n delay: firstPeriod.delay ?? 0,\n timingMethod,\n clockStart,\n periods,\n };\n }\n\n // Handle single period time control\n let parsed: TimeControl;\n\n if (typeof input === \"string\") {\n parsed = parseTimeControlString(input);\n } else {\n parsed = input;\n }\n\n return {\n baseTime: parsed.baseTime * 1000, // Convert to milliseconds\n increment: (parsed.increment ?? 0) * 1000, // Convert to milliseconds\n delay: (parsed.delay ?? 0) * 1000, // Convert to milliseconds\n timingMethod,\n clockStart,\n };\n}\n\n/**\n * Parse complete TimeControlConfig into NormalizedTimeControl\n * @param config - Time control configuration\n * @returns Fully normalized time control\n */\nexport function parseTimeControlConfig(\n config: TimeControlConfig,\n): NormalizedTimeControl {\n const normalized = normalizeTimeControl(\n config.time,\n config.timingMethod ?? \"fischer\",\n config.clockStart ?? \"delayed\",\n );\n\n return {\n ...normalized,\n whiteTimeOverride: config.whiteTime ? config.whiteTime * 1000 : undefined,\n blackTimeOverride: config.blackTime ? config.blackTime * 1000 : undefined,\n };\n}\n\n/**\n * Get initial times from a normalized time control\n * @param config - Normalized time control\n * @returns Initial times for white and black in milliseconds\n */\nexport function getInitialTimes(config: NormalizedTimeControl): {\n white: number;\n black: number;\n} {\n return {\n white: config.whiteTimeOverride ?? config.baseTime,\n black: config.blackTimeOverride ?? config.baseTime,\n };\n}\n","import type { TimeFormat } from \"../types\";\n\n/**\n * Threshold for auto-format switching (in seconds)\n * Below this, show seconds with decimal; above, show mm:ss\n */\nconst AUTO_FORMAT_THRESHOLD = 20;\n\n/**\n * Format milliseconds into a display string\n * @param milliseconds - Time in milliseconds\n * @param format - Format type\n * @returns Formatted time string\n */\nexport function formatClockTime(\n milliseconds: number,\n format: TimeFormat = \"auto\",\n): string {\n // Clamp to zero (no negative times)\n const clampedMs = Math.max(0, milliseconds);\n\n // Auto format: switch to ss.d when time is low\n if (format === \"auto\") {\n // Use milliseconds for comparison to avoid rounding issues\n format = clampedMs < AUTO_FORMAT_THRESHOLD * 1000 ? \"ss.d\" : \"mm:ss\";\n }\n\n const totalSeconds = Math.ceil(clampedMs / 1000);\n\n switch (format) {\n case \"ss.d\": {\n // Show seconds with one decimal place\n const secondsWithDecimal = clampedMs / 1000;\n return secondsWithDecimal.toFixed(1);\n }\n\n case \"mm:ss\": {\n const totalMinutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n // Show hours if time exceeds 60 minutes\n if (totalMinutes >= 60) {\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`;\n }\n return `${totalMinutes}:${seconds.toString().padStart(2, \"0\")}`;\n }\n\n case \"hh:mm:ss\": {\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`;\n }\n\n default:\n return String(totalSeconds);\n }\n}\n","import type { NormalizedTimeControl, TimingMethod } from \"../types\";\n\n/**\n * Apply Fischer increment\n * Adds the full increment amount after each move\n * @param currentTime - Current time in milliseconds\n * @param increment - Increment in milliseconds\n * @returns New time with increment added\n */\nexport function applyFischerIncrement(\n currentTime: number,\n increment: number,\n): number {\n return currentTime + increment;\n}\n\n/**\n * Calculate time to decrement for simple delay method\n * Clock doesn't decrement during the delay period\n * @param timeSpent - Time spent in current move in milliseconds\n * @param delay - Delay in milliseconds\n * @returns Actual time to decrement (0 if within delay period)\n */\nexport function applySimpleDelay(timeSpent: number, delay: number): number {\n if (timeSpent <= delay) {\n return 0; // Still within delay period, no decrement\n }\n return timeSpent - delay; // Decrement actual time spent after delay\n}\n\n/**\n * Calculate Bronstein delay adjustment\n * Adds back the actual time used, up to the delay amount\n * @param currentTime - Current time in milliseconds\n * @param timeSpent - Time spent in current move in milliseconds\n * @param delay - Delay in milliseconds\n * @returns New time with Bronstein adjustment\n */\nexport function applyBronsteinDelay(\n currentTime: number,\n timeSpent: number,\n delay: number,\n): number {\n const addBack = Math.min(timeSpent, delay);\n return currentTime + addBack;\n}\n\n/**\n * Calculate the time adjustment when switching players\n * @param timingMethod - The timing method to use\n * @param currentTime - Current time in milliseconds\n * @param timeSpent - Time spent in current move in milliseconds\n * @param config - Normalized time control config\n * @returns New time in milliseconds\n */\nexport function calculateSwitchAdjustment(\n timingMethod: TimingMethod,\n currentTime: number,\n timeSpent: number,\n config: NormalizedTimeControl,\n): number {\n switch (timingMethod) {\n case \"fischer\":\n return applyFischerIncrement(currentTime, config.increment);\n\n case \"delay\":\n // For delay, the adjustment happens during ticking, not on switch\n // Just return current time (delay is handled in tick calculation)\n return currentTime;\n\n case \"bronstein\":\n return applyBronsteinDelay(currentTime, timeSpent, config.delay);\n\n default:\n return currentTime;\n }\n}\n\n/**\n * Calculate initial active player based on clock start mode\n * @param clockStart - Clock start mode\n * @returns Initial active player or null\n */\nexport function getInitialActivePlayer(\n clockStart: \"delayed\" | \"immediate\" | \"manual\",\n): \"white\" | null {\n // For \"immediate\", white starts immediately\n // For \"delayed\" and \"manual\", no active player until first move/start\n return clockStart === \"immediate\" ? \"white\" : null;\n}\n\n/**\n * Get initial status based on clock start mode\n * @param clockStart - Clock start mode\n * @returns Initial clock status\n */\nexport function getInitialStatus(\n clockStart: \"delayed\" | \"immediate\" | \"manual\",\n): \"idle\" | \"delayed\" | \"running\" {\n if (clockStart === \"immediate\") return \"running\";\n if (clockStart === \"delayed\") return \"delayed\";\n return \"idle\";\n}\n","import type { NormalizedTimeControl } from \"../types\";\nimport { calculateSwitchAdjustment } from \"./timingMethods\";\n\n/**\n * Calculate the new time for a player after their move ends.\n *\n * This combines:\n * 1. Delay method logic (time doesn't decrement during delay period)\n * 2. Timing method adjustments (Fischer increment, Bronstein)\n *\n * @param currentTime - Current time in milliseconds for the player\n * @param timeSpent - Time spent on the move in milliseconds\n * @param config - Normalized time control configuration\n * @returns New time in milliseconds for the player\n */\nexport function calculateSwitchTime(\n currentTime: number,\n timeSpent: number,\n config: NormalizedTimeControl,\n): number {\n // Apply delay method logic: reduce effective elapsed by delay amount\n let effectiveElapsed = timeSpent;\n if (config.timingMethod === \"delay\") {\n effectiveElapsed = Math.max(0, timeSpent - config.delay);\n }\n\n // Decrement time, ensuring it doesn't go below zero\n const newTime = Math.max(0, currentTime - effectiveElapsed);\n\n // Apply timing method adjustments (Fischer increment, Bronstein)\n return calculateSwitchAdjustment(\n config.timingMethod,\n newTime,\n timeSpent,\n config,\n );\n}\n","import type {\n ClockColor,\n ClockTimes,\n ClockStatus,\n PeriodState,\n TimeControlConfig,\n NormalizedTimeControl,\n} from \"../types\";\nimport { parseTimeControlConfig, getInitialTimes } from \"../utils/timeControl\";\nimport {\n getInitialActivePlayer,\n getInitialStatus,\n} from \"../utils/timingMethods\";\n\n// ============================================================================\n// State\n// ============================================================================\n\nexport interface ClockState {\n /** The actual stored time values for each player. Updates when time is deducted (after delays) or added via increments. */\n times: ClockTimes;\n /** The starting time values for each player, used for reset operations. */\n initialTimes: ClockTimes;\n /** Current clock status - whether it's idle, running, paused, or finished. */\n status: ClockStatus;\n /** The player whose clock is currently counting down. Null when clock is idle. */\n activePlayer: ClockColor | null;\n /** Which player timed out, if any. Set when the clock reaches finished status. */\n timeout: ClockColor | null;\n /** Number of times the clock has switched between players. Used for move counting in some time controls. */\n switchCount: number;\n /** Multi-period state tracking (only present for multi-period time controls) */\n periodState?: PeriodState;\n /** The normalized time control configuration (kept in sync with resets) */\n config: NormalizedTimeControl;\n /**\n * Timestamp (ms since epoch) when the current player's move started.\n * Used to calculate elapsed time as `Date.now() - moveStartTime`.\n * Null when the clock hasn't started yet or is paused.\n */\n moveStartTime: number | null;\n /**\n * When the clock is paused, this stores the elapsed time (in ms) at the\n * moment of pause. This allows resuming without \"losing\" the paused time.\n */\n elapsedAtPause: number;\n}\n\n// ============================================================================\n// Actions\n// ============================================================================\n\nexport type ClockAction =\n | { type: \"START\"; payload?: { now?: number } }\n | { type: \"PAUSE\"; payload?: { now?: number } }\n | { type: \"RESUME\"; payload?: { now?: number } }\n | { type: \"SWITCH\"; payload: { newTimes?: ClockTimes; now?: number } }\n | { type: \"TIMEOUT\"; payload: { player: ClockColor } }\n | { type: \"RESET\"; payload: TimeControlConfig & { now?: number } }\n | {\n type: \"ADD_TIME\";\n payload: { player: ClockColor; milliseconds: number; now?: number };\n }\n | {\n type: \"SET_TIME\";\n payload: { player: ClockColor; milliseconds: number; now?: number };\n };\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * After a switch increments period moves, check if any player has completed\n * their required moves for the current period and should advance to the next.\n * Returns updated times and periodState, or the originals if no advancement.\n */\nfunction maybeAdvancePeriod(\n times: ClockTimes,\n periodState: PeriodState,\n): { times: ClockTimes; periodState: PeriodState } {\n let newTimes = times;\n let newPeriodState = periodState;\n\n ([\"white\", \"black\"] as const).forEach((player) => {\n const playerPeriodIndex = newPeriodState.periodIndex[player];\n\n if (\n playerPeriodIndex < 0 ||\n playerPeriodIndex >= newPeriodState.periods.length\n ) {\n return;\n }\n\n const currentPeriod = newPeriodState.periods[playerPeriodIndex];\n if (!currentPeriod?.moves) return; // Sudden death period, no transition\n\n const movesRequired = currentPeriod.moves;\n const playerMoves = newPeriodState.periodMoves[player];\n\n if (playerMoves >= movesRequired) {\n const nextPeriodIndex = playerPeriodIndex + 1;\n\n if (nextPeriodIndex >= newPeriodState.periods.length) {\n return; // Already at final period\n }\n\n const nextPeriod = newPeriodState.periods[nextPeriodIndex];\n if (!nextPeriod) return;\n\n const addedTime = nextPeriod.baseTime;\n\n newPeriodState = {\n ...newPeriodState,\n periodIndex: {\n ...newPeriodState.periodIndex,\n [player]: nextPeriodIndex,\n },\n periodMoves: {\n ...newPeriodState.periodMoves,\n [player]: 0,\n },\n };\n\n newTimes = {\n ...newTimes,\n [player]: newTimes[player] + addedTime,\n };\n }\n });\n\n return { times: newTimes, periodState: newPeriodState };\n}\n\n// ============================================================================\n// Reducer\n// ============================================================================\n\nexport function clockReducer(\n state: ClockState,\n action: ClockAction,\n): ClockState {\n switch (action.type) {\n case \"START\": {\n if (state.status === \"finished\") return state;\n // Don't interrupt delayed mode - it transitions to running after both players move\n if (state.status === \"delayed\") {\n return state; // No change in delayed mode\n }\n const now = action.payload?.now ?? Date.now();\n return {\n ...state,\n status: \"running\",\n activePlayer: state.activePlayer ?? \"white\",\n // Initialize move start time if not already set\n moveStartTime: state.moveStartTime ?? now,\n };\n }\n\n case \"PAUSE\": {\n if (state.status !== \"running\") return state;\n const now = action.payload?.now ?? Date.now();\n // Store elapsed time at pause moment\n const elapsedAtPause =\n state.moveStartTime !== null ? now - state.moveStartTime : 0;\n return {\n ...state,\n status: \"paused\",\n elapsedAtPause,\n moveStartTime: null,\n };\n }\n\n case \"RESUME\": {\n if (state.status !== \"paused\") return state;\n const now = action.payload?.now ?? Date.now();\n // Reset start time based on stored elapsed to resume seamlessly\n return {\n ...state,\n status: \"running\",\n moveStartTime: now - state.elapsedAtPause,\n elapsedAtPause: 0,\n };\n }\n\n case \"SWITCH\": {\n if (state.status === \"finished\") return state;\n // Track period moves for multi-period time controls\n let newPeriodState = state.periodState;\n const movedPlayer: ClockColor | null = state.activePlayer;\n const now = action.payload.now ?? Date.now();\n\n // Handle delayed start mode\n if (state.status === \"delayed\") {\n const newCount = state.switchCount + 1;\n const newPlayer: ClockColor = newCount % 2 === 1 ? \"black\" : \"white\";\n\n // In delayed mode, track which player is making the move\n // First switch: white moves, second switch: black moves\n const delayedMovePlayer: ClockColor =\n newCount % 2 === 1 ? \"white\" : \"black\";\n\n // Track period moves even during delayed mode\n if (state.periodState) {\n newPeriodState = {\n ...state.periodState,\n periodMoves: {\n ...state.periodState.periodMoves,\n [delayedMovePlayer]:\n state.periodState.periodMoves[delayedMovePlayer] + 1,\n },\n };\n }\n\n // Check for period advancement\n if (newPeriodState) {\n const advanced = maybeAdvancePeriod(state.times, newPeriodState);\n newPeriodState = advanced.periodState;\n }\n\n const newStateStatus = newCount >= 2 ? \"running\" : \"delayed\";\n\n return {\n ...state,\n activePlayer: newPlayer,\n switchCount: newCount,\n status: newStateStatus,\n periodState: newPeriodState,\n // Start timing when transitioning to running\n moveStartTime:\n newStateStatus === \"running\" ? now : state.moveStartTime,\n };\n }\n\n // Normal switch handling\n if (movedPlayer === null) return state;\n const { newTimes } = action.payload;\n const newPlayer = movedPlayer === \"white\" ? \"black\" : \"white\";\n\n // Track period moves for multi-period time controls\n if (state.periodState) {\n newPeriodState = {\n ...state.periodState,\n periodMoves: {\n ...state.periodState.periodMoves,\n [movedPlayer]: state.periodState.periodMoves[movedPlayer] + 1,\n },\n };\n }\n\n let resolvedTimes = newTimes ?? state.times;\n\n // Check for period advancement\n if (newPeriodState) {\n const advanced = maybeAdvancePeriod(resolvedTimes, newPeriodState);\n resolvedTimes = advanced.times;\n newPeriodState = advanced.periodState;\n }\n\n const newStatus = state.status === \"idle\" ? \"running\" : state.status;\n\n return {\n ...state,\n activePlayer: newPlayer,\n times: resolvedTimes,\n switchCount: state.switchCount + 1,\n periodState: newPeriodState,\n status: newStatus,\n // Reset timing for the new player\n moveStartTime: newStatus === \"running\" ? now : null,\n elapsedAtPause: 0,\n };\n }\n\n case \"TIMEOUT\": {\n const { player } = action.payload;\n return {\n ...state,\n status: \"finished\",\n timeout: player,\n times: { ...state.times, [player]: 0 },\n };\n }\n\n case \"RESET\": {\n const config = parseTimeControlConfig(action.payload);\n const initialTimes = getInitialTimes(config);\n const now = action.payload.now;\n\n // Compute period state for multi-period time controls\n const periodState: PeriodState | undefined = config.periods\n ? {\n periodIndex: { white: 0, black: 0 },\n periodMoves: { white: 0, black: 0 },\n periods: config.periods,\n }\n : undefined;\n\n return createInitialClockState(\n initialTimes,\n getInitialStatus(config.clockStart),\n getInitialActivePlayer(config.clockStart),\n config,\n periodState,\n now,\n );\n }\n\n case \"ADD_TIME\": {\n const { player, milliseconds } = action.payload;\n const now = action.payload.now ?? Date.now();\n const newTimes = {\n ...state.times,\n [player]: state.times[player] + milliseconds,\n };\n // Reset timing so display interpolation restarts from the new base time.\n // When paused and modifying the active player's time, reset elapsedAtPause\n // so RESUME doesn't use a stale offset that ignores the time change.\n const resetElapsed =\n state.status === \"paused\" && player === state.activePlayer;\n return {\n ...state,\n times: newTimes,\n moveStartTime: state.status === \"running\" ? now : null,\n ...(resetElapsed && { elapsedAtPause: 0 }),\n };\n }\n\n case \"SET_TIME\": {\n const { player, milliseconds } = action.payload;\n const now = action.payload.now ?? Date.now();\n const newTimes = {\n ...state.times,\n [player]: Math.max(0, milliseconds),\n };\n // Reset timing so display interpolation restarts from the new base time.\n // When paused and modifying the active player's time, reset elapsedAtPause\n // so RESUME doesn't use a stale offset that ignores the time change.\n const resetElapsed =\n state.status === \"paused\" && player === state.activePlayer;\n return {\n ...state,\n times: newTimes,\n moveStartTime: state.status === \"running\" ? now : null,\n ...(resetElapsed && { elapsedAtPause: 0 }),\n };\n }\n\n default:\n return state;\n }\n}\n\n// ============================================================================\n// Initial State Factory\n// ============================================================================\n\nexport function createInitialClockState(\n initialTimes: ClockTimes,\n status: ClockStatus,\n activePlayer: ClockColor | null,\n config: NormalizedTimeControl,\n periodState?: PeriodState,\n now?: number,\n): ClockState {\n return {\n times: initialTimes,\n initialTimes,\n status,\n activePlayer,\n timeout: null,\n switchCount: 0,\n periodState,\n config,\n // If starting immediately, initialize the move start time\n moveStartTime: status === \"running\" ? (now ?? Date.now()) : null,\n elapsedAtPause: 0,\n };\n}\n","import React from \"react\";\nimport type { ClockColor } from \"../../../types\";\nimport { useChessClockContext } from \"../../../hooks/useChessClockContext\";\nimport { formatClockTime } from \"../../../utils/formatTime\";\n\nexport interface ChessClockDisplayProps extends React.HTMLAttributes<HTMLDivElement> {\n color: ClockColor;\n format?: \"auto\" | \"mm:ss\" | \"ss.d\" | \"hh:mm:ss\";\n formatTime?: (milliseconds: number) => string;\n}\n\n/**\n * ChessClock.Display - Displays the current time for a player\n *\n * Renders an unstyled div with data attributes for custom styling.\n *\n * @example\n * ```tsx\n * <ChessClock.Display color=\"white\" format=\"auto\" />\n * <ChessClock.Display color=\"black\" format=\"ss.d\" />\n * <ChessClock.Display\n * color=\"white\"\n * formatTime={(ms) => `${Math.ceil(ms / 1000)}s`}\n * />\n * ```\n */\nexport const Display = React.forwardRef<HTMLDivElement, ChessClockDisplayProps>(\n (\n {\n color,\n format = \"auto\",\n formatTime: customFormatTime,\n className,\n style,\n ...rest\n },\n ref,\n ) => {\n const { times, activePlayer, status, timeout } = useChessClockContext();\n\n const time = times[color];\n const isActive = activePlayer === color;\n const hasTimeout = timeout === color;\n const isPaused = status === \"paused\";\n\n // Format the time for display\n const formattedTime = customFormatTime\n ? customFormatTime(time)\n : formatClockTime(time, format);\n\n return (\n <div\n ref={ref}\n className={className}\n style={style}\n data-clock-color={color}\n data-clock-active={isActive ? \"true\" : \"false\"}\n data-clock-paused={isPaused ? \"true\" : \"false\"}\n data-clock-timeout={hasTimeout ? \"true\" : \"false\"}\n data-clock-status={status}\n {...rest}\n >\n {formattedTime}\n </div>\n );\n },\n);\n\nDisplay.displayName = \"ChessClock.Display\";\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { useChessClockContext } from \"../../../hooks/useChessClockContext\";\n\nexport interface ChessClockControlProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onClick\"\n> {\n asChild?: boolean;\n children?: ReactNode;\n onClick?: React.MouseEventHandler<HTMLElement>;\n}\n\n/**\n * ChessClock.Switch - Button to manually switch the active clock\n *\n * Supports the asChild pattern for custom rendering.\n *\n * @example\n * ```tsx\n * <ChessClock.Switch>Switch Clock</ChessClock.Switch>\n *\n * // As child\n * <ChessClock.Switch asChild>\n * <div className=\"custom-switch\">Switch</div>\n * </ChessClock.Switch>\n * ```\n */\nexport const Switch = React.forwardRef<\n HTMLElement,\n React.PropsWithChildren<ChessClockControlProps>\n>(\n (\n {\n asChild = false,\n children,\n onClick,\n disabled,\n className,\n style,\n type,\n ...rest\n },\n ref,\n ) => {\n const { methods, status } = useChessClockContext();\n const isDisabled = disabled || status === \"finished\" || status === \"idle\";\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLElement>) => {\n methods.switch();\n onClick?.(e);\n },\n [methods, onClick],\n );\n\n return asChild ? (\n <Slot\n ref={ref}\n onClick={handleClick}\n className={className}\n style={style}\n {...{ ...rest, disabled: isDisabled }}\n >\n {children}\n </Slot>\n ) : (\n <button\n ref={ref as React.RefObject<HTMLButtonElement>}\n type={type || \"button\"}\n className={className}\n style={style}\n onClick={handleClick}\n disabled={isDisabled}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n\nSwitch.displayName = \"ChessClock.Switch\";\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { useChessClockContext } from \"../../../hooks/useChessClockContext\";\n\n// Default content for each clock state\nconst DEFAULT_CONTENT = {\n start: \"Start\",\n pause: \"Pause\",\n resume: \"Resume\",\n delayed: \"Start\",\n finished: \"Game Over\",\n} as const;\n\n/**\n * Resolves the appropriate content to display based on clock state\n * and custom content props. Custom content takes precedence over defaults.\n */\nconst resolveContent = (\n isFinished: boolean,\n isDelayed: boolean,\n shouldShowStart: boolean,\n isPaused: boolean,\n isRunning: boolean,\n customContent: {\n startContent?: ReactNode;\n pauseContent?: ReactNode;\n resumeContent?: ReactNode;\n delayedContent?: ReactNode;\n finishedContent?: ReactNode;\n },\n): ReactNode => {\n if (isFinished) {\n return customContent.finishedContent ?? DEFAULT_CONTENT.finished;\n }\n if (isDelayed) {\n return customContent.delayedContent ?? DEFAULT_CONTENT.delayed;\n }\n if (shouldShowStart) {\n return customContent.startContent ?? DEFAULT_CONTENT.start;\n }\n if (isPaused) {\n return customContent.resumeContent ?? DEFAULT_CONTENT.resume;\n }\n if (isRunning) {\n return customContent.pauseContent ?? DEFAULT_CONTENT.pause;\n }\n return DEFAULT_CONTENT.start;\n};\n\nexport interface ChessClockPlayPauseProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onClick\"\n> {\n asChild?: boolean;\n children?: ReactNode;\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Content shown when clock is idle (not yet started) - clicking will start the clock */\n startContent?: ReactNode;\n /** Content shown when clock is running - clicking will pause */\n pauseContent?: ReactNode;\n /** Content shown when clock is paused - clicking will resume */\n resumeContent?: ReactNode;\n /** Content shown when clock is in delayed mode - clicking will start the clock immediately */\n delayedContent?: ReactNode;\n /** Content shown when clock is finished - button is disabled but shows this content */\n finishedContent?: ReactNode;\n}\n\n/**\n * ChessClock.PlayPause - Button to start, pause, and resume the clock\n *\n * Supports the asChild pattern and conditional content based on clock state.\n * When no children or custom content is provided, sensible defaults are used for each state.\n *\n * @example\n * ```tsx\n * // With children (backward compatible)\n * <ChessClock.PlayPause>\n * <span>Toggle</span>\n * </ChessClock.PlayPause>\n *\n * // No props - uses defaults for all states\n * <ChessClock.PlayPause />\n * // Shows: \"Start\" → \"Pause\" → \"Resume\" → \"Game Over\"\n *\n * // Override just one state, others use defaults\n * <ChessClock.PlayPause pauseContent=\"⏸️ Stop\" />\n * // Shows: \"Start\" → \"⏸️ Stop\" → \"Resume\" → \"Game Over\"\n *\n * // As child\n * <ChessClock.PlayPause asChild>\n * <div className=\"custom-button\">Toggle</div>\n * </ChessClock.PlayPause>\n * ```\n */\nexport const PlayPause = React.forwardRef<\n HTMLElement,\n React.PropsWithChildren<ChessClockPlayPauseProps>\n>(\n (\n {\n asChild = false,\n startContent,\n pauseContent,\n resumeContent,\n delayedContent,\n finishedContent,\n children,\n onClick,\n disabled,\n className,\n style,\n type,\n ...rest\n },\n ref,\n ) => {\n const { status, methods } = useChessClockContext();\n const isIdle = status === \"idle\";\n const isDelayed = status === \"delayed\";\n const isPaused = status === \"paused\";\n const isRunning = status === \"running\";\n const isFinished = status === \"finished\";\n\n // Treat \"delayed\" like \"idle\" - clock hasn't started yet\n const shouldShowStart = isIdle || isDelayed;\n\n const isDisabled = disabled || isFinished || isDelayed;\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLElement>) => {\n if (shouldShowStart) {\n methods.start();\n } else if (isPaused) {\n methods.resume();\n } else if (isRunning) {\n methods.pause();\n }\n onClick?.(e);\n },\n [shouldShowStart, isPaused, isRunning, methods, onClick],\n );\n\n // Determine content to render\n // Priority: children > custom+defaults > all defaults\n const content =\n children ??\n resolveContent(\n isFinished,\n isDelayed,\n shouldShowStart,\n isPaused,\n isRunning,\n {\n startContent,\n pauseContent,\n resumeContent,\n delayedContent,\n finishedContent,\n },\n );\n\n return asChild ? (\n <Slot\n ref={ref}\n onClick={handleClick}\n className={className}\n style={style}\n {...{ ...rest, disabled: isDisabled }}\n >\n {content}\n </Slot>\n ) : (\n <button\n ref={ref as React.RefObject<HTMLButtonElement>}\n type={type || \"button\"}\n className={className}\n style={style}\n onClick={handleClick}\n disabled={isDisabled}\n {...rest}\n >\n {content}\n </button>\n );\n },\n);\n\nPlayPause.displayName = \"ChessClock.PlayPause\";\n","import React from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { useChessClockContext } from \"../../../hooks/useChessClockContext\";\nimport type { TimeControlInput } from \"../../../types\";\n\nexport interface ChessClockResetProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onClick\"\n> {\n asChild?: boolean;\n children?: ReactNode;\n onClick?: React.MouseEventHandler<HTMLElement>;\n timeControl?: TimeControlInput;\n}\n\n/**\n * ChessClock.Reset - Button to reset the clock\n *\n * Supports the asChild pattern and optional new time control on reset.\n *\n * @example\n * ```tsx\n * <ChessClock.Reset>Reset</ChessClock.Reset>\n *\n * // Reset with new time control\n * <ChessClock.Reset timeControl=\"10+5\">Change to 10+5</ChessClock.Reset>\n *\n * // As child\n * <ChessClock.Reset asChild>\n * <div className=\"custom-reset\">Reset</div>\n * </ChessClock.Reset>\n * ```\n */\nexport const Reset = React.forwardRef<\n HTMLElement,\n React.PropsWithChildren<ChessClockResetProps>\n>(\n (\n {\n asChild = false,\n timeControl,\n children,\n onClick,\n disabled,\n className,\n style,\n type,\n ...rest\n },\n ref,\n ) => {\n const { methods, status } = useChessClockContext();\n const isDisabled = disabled || status === \"idle\";\n\n const handleClick = React.useCallback(\n (e: React.MouseEvent<HTMLElement>) => {\n methods.reset(timeControl);\n onClick?.(e);\n },\n [methods, timeControl, onClick],\n );\n\n return asChild ? (\n <Slot\n ref={ref}\n onClick={handleClick}\n className={className}\n style={style}\n {...{ ...rest, disabled: isDisabled }}\n >\n {children}\n </Slot>\n ) : (\n <button\n ref={ref as React.RefObject<HTMLButtonElement>}\n type={type || \"button\"}\n className={className}\n style={style}\n onClick={handleClick}\n disabled={isDisabled}\n {...rest}\n >\n {children}\n </button>\n );\n },\n);\n\nReset.displayName = \"ChessClock.Reset\";\n","import { Root } from \"./parts/Root\";\nimport { Display } from \"./parts/Display\";\nimport { Switch } from \"./parts/Switch\";\nimport { PlayPause } from \"./parts/PlayPause\";\nimport { Reset } from \"./parts/Reset\";\n\n/**\n * ChessClock compound components\n *\n * @example\n * ```tsx\n * import { ChessClock } from \"@react-chess-tools/react-chess-clock\";\n *\n * function ClockApp() {\n * return (\n * <ChessClock.Root timeControl={{ time: \"5+3\" }}>\n * <ChessClock.Display color=\"black\" />\n * <ChessClock.Display color=\"white\" />\n * <ChessClock.Switch>Switch</ChessClock.Switch>\n * <ChessClock.PlayPause\n * startContent=\"Start\"\n * pauseContent=\"Pause\"\n * resumeContent=\"Resume\"\n * />\n * <ChessClock.Reset>Reset</ChessClock.Reset>\n * </ChessClock.Root>\n * );\n * }\n * ```\n */\nexport const ChessClock = {\n Root,\n Display,\n Switch,\n PlayPause,\n Reset,\n};\n\n// Re-export types for convenience\nexport type { ChessClockRootProps } from \"./parts/Root\";\nexport type { ChessClockDisplayProps } from \"./parts/Display\";\nexport type { ChessClockControlProps } from \"./parts/Switch\";\nexport type { ChessClockPlayPauseProps } from \"./parts/PlayPause\";\nexport type { ChessClockResetProps } from \"./parts/Reset\";\n","/**\n * Preset time controls for common chess formats\n *\n * @example\n * ```tsx\n * import { presets } from \"@react-chess-tools/react-chess-clock\";\n *\n * <ChessClock.Root timeControl={{ time: presets.blitz5_3 }}>\n * <ChessClock.Display color=\"white\" />\n * <ChessClock.Display color=\"black\" />\n * </ChessClock.Root>\n * ```\n */\nexport const presets = {\n // Bullet (< 3 minutes)\n bullet1_0: { baseTime: 60, increment: 0 },\n bullet1_1: { baseTime: 60, increment: 1 },\n bullet2_1: { baseTime: 120, increment: 1 },\n\n // Blitz (3-10 minutes)\n blitz3_0: { baseTime: 180, increment: 0 },\n blitz3_2: { baseTime: 180, increment: 2 },\n blitz5_0: { baseTime: 300, increment: 0 },\n blitz5_3: { baseTime: 300, increment: 3 },\n\n // Rapid (10-60 minutes)\n rapid10_0: { baseTime: 600, increment: 0 },\n rapid10_5: { baseTime: 600, increment: 5 },\n rapid15_10: { baseTime: 900, increment: 10 },\n\n // Classical (≥ 60 minutes)\n classical30_0: { baseTime: 1800, increment: 0 },\n classical90_30: { baseTime: 5400, increment: 30 },\n\n // Tournament (multi-period)\n fideClassical: [\n { baseTime: 5400, increment: 30, moves: 40 },\n { baseTime: 1800, increment: 30, moves: 20 },\n { baseTime: 900, increment: 30 },\n ],\n\n uscfClassical: [\n { baseTime: 7200, moves: 40 },\n { baseTime: 3600, moves: 20 },\n { baseTime: 1800 },\n ],\n} as const;\n"],"mappings":"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","names":["newPlayer","effectiveElapsed","React","React","React","React","React","Slot","React","Slot","React","Slot","React","Slot"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@react-chess-tools/react-chess-clock",
3
+ "version": "1.0.1",
4
+ "description": "A standalone chess clock component for React with support for multiple timing methods (Fischer, delay, Bronstein) and seamless integration with react-chess-game.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.cjs",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "scripts": {
18
+ "build": "tsup src/index.ts",
19
+ "test": "jest"
20
+ },
21
+ "keywords": [
22
+ "chess",
23
+ "react",
24
+ "clock",
25
+ "timer",
26
+ "chess-clock",
27
+ "increment",
28
+ "delay",
29
+ "bronstein",
30
+ "fischer",
31
+ "component",
32
+ "time-control"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "author": "Daniele Cammareri <daniele.cammareri@gmail.com>",
38
+ "license": "MIT",
39
+ "dependencies": {
40
+ "@radix-ui/react-slot": "^1.2.4"
41
+ },
42
+ "devDependencies": {
43
+ "@testing-library/jest-dom": "^6.9.1",
44
+ "@testing-library/react": "^16.3.1",
45
+ "@testing-library/user-event": "^14.6.1",
46
+ "@types/react": "^19.2.7",
47
+ "jest": "^30.2.0",
48
+ "jest-environment-jsdom": "^30.2.0",
49
+ "react": "^19.2.3",
50
+ "react-dom": "^19.2.3"
51
+ },
52
+ "peerDependencies": {
53
+ "react": ">=16.14.0"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "git+https://github.com/dancamma/react-chess-tools.git"
58
+ },
59
+ "bugs": {
60
+ "url": "https://github.com/dancamma/react-chess-tools/issues"
61
+ },
62
+ "homepage": "https://github.com/dancamma/react-chess-tools#readme"
63
+ }