@kolkrabbi/kol-component 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +8 -0
  2. package/package.json +2 -5
  3. package/src/atoms/RotaryDial.jsx +35 -21
  4. package/src/index.js +6 -18
  5. package/src/molecules/ButtonGroup.jsx +45 -0
  6. package/src/molecules/ColorInputRow.jsx +146 -126
  7. package/src/molecules/Slider.jsx +29 -14
  8. package/src/molecules/SplitToolButton.jsx +133 -0
  9. package/src/organisms/ContentFilters.jsx +2 -2
  10. package/src/organisms/GalleryCarousel.jsx +2 -1
  11. package/src/organisms/LoaderOverlay.jsx +11 -14
  12. package/src/organisms/MediaTileGallery.jsx +57 -0
  13. package/src/organisms/MediaViewer.jsx +89 -64
  14. package/src/atoms/PriceDisplay.jsx +0 -34
  15. package/src/atoms/TextPressure.jsx +0 -331
  16. package/src/atoms/TypeSample.jsx +0 -50
  17. package/src/atoms/TypeSpecCard.jsx +0 -42
  18. package/src/chess/ChessPiece.jsx +0 -191
  19. package/src/chess/apparatus/AlternativeControlsMock.jsx +0 -460
  20. package/src/chess/apparatus/ChessAnalysisLayout.jsx +0 -24
  21. package/src/chess/apparatus/ChessBoard.jsx +0 -210
  22. package/src/chess/apparatus/ChessBoardFullscreen.jsx +0 -49
  23. package/src/chess/apparatus/ChessBoardWithControls.jsx +0 -41
  24. package/src/chess/apparatus/ChessBoardWithSidebar.jsx +0 -122
  25. package/src/chess/apparatus/ChessSidebar.jsx +0 -131
  26. package/src/chess/apparatus/GameArchiveTable.jsx +0 -493
  27. package/src/chess/apparatus/GameSelector.jsx +0 -47
  28. package/src/chess/apparatus/NotationPanel.jsx +0 -70
  29. package/src/chess/apparatus/PlaybackControls.jsx +0 -52
  30. package/src/chess/apparatus/VariationTree.jsx +0 -137
  31. package/src/chess/assets/chess-extra-set/set-1/bishop-1.svg +0 -6
  32. package/src/chess/assets/chess-extra-set/set-1/bishop.svg +0 -6
  33. package/src/chess/assets/chess-extra-set/set-1/king-1.svg +0 -9
  34. package/src/chess/assets/chess-extra-set/set-1/king.svg +0 -7
  35. package/src/chess/assets/chess-extra-set/set-1/knight-1.svg +0 -8
  36. package/src/chess/assets/chess-extra-set/set-1/knight.svg +0 -7
  37. package/src/chess/assets/chess-extra-set/set-1/pawn-1.svg +0 -3
  38. package/src/chess/assets/chess-extra-set/set-1/pawn.svg +0 -3
  39. package/src/chess/assets/chess-extra-set/set-1/queen-1.svg +0 -11
  40. package/src/chess/assets/chess-extra-set/set-1/queen.svg +0 -6
  41. package/src/chess/assets/chess-extra-set/set-1/rook-1.svg +0 -7
  42. package/src/chess/assets/chess-extra-set/set-1/rook.svg +0 -11
  43. package/src/chess/assets/chess-extra-set/set-2/bishop-1.svg +0 -4
  44. package/src/chess/assets/chess-extra-set/set-2/bishop.svg +0 -4
  45. package/src/chess/assets/chess-extra-set/set-2/king-1.svg +0 -4
  46. package/src/chess/assets/chess-extra-set/set-2/king.svg +0 -4
  47. package/src/chess/assets/chess-extra-set/set-2/knight-1.svg +0 -5
  48. package/src/chess/assets/chess-extra-set/set-2/knight.svg +0 -5
  49. package/src/chess/assets/chess-extra-set/set-2/pawn-1.svg +0 -4
  50. package/src/chess/assets/chess-extra-set/set-2/pawn.svg +0 -4
  51. package/src/chess/assets/chess-extra-set/set-2/queen-1.svg +0 -4
  52. package/src/chess/assets/chess-extra-set/set-2/queen.svg +0 -4
  53. package/src/chess/assets/chess-extra-set/set-2/rook-1.svg +0 -5
  54. package/src/chess/assets/chess-extra-set/set-2/rook.svg +0 -5
  55. package/src/chess/assets/chess-extra-set/set-3/bishop-1.svg +0 -10
  56. package/src/chess/assets/chess-extra-set/set-3/bishop.svg +0 -3
  57. package/src/chess/assets/chess-extra-set/set-3/king-1.svg +0 -12
  58. package/src/chess/assets/chess-extra-set/set-3/king.svg +0 -3
  59. package/src/chess/assets/chess-extra-set/set-3/knight-1.svg +0 -3
  60. package/src/chess/assets/chess-extra-set/set-3/knight.svg +0 -10
  61. package/src/chess/assets/chess-extra-set/set-3/pawn-1.svg +0 -10
  62. package/src/chess/assets/chess-extra-set/set-3/pawn.svg +0 -10
  63. package/src/chess/assets/chess-extra-set/set-3/queen-1.svg +0 -10
  64. package/src/chess/assets/chess-extra-set/set-3/queen.svg +0 -3
  65. package/src/chess/assets/chess-extra-set/set-3/rook-1.svg +0 -3
  66. package/src/chess/assets/chess-extra-set/set-3/rook.svg +0 -11
  67. package/src/chess/assets/chess-vector-set/ABoard.svg +0 -164
  68. package/src/chess/assets/chess-vector-set/BBiskup.svg +0 -12
  69. package/src/chess/assets/chess-vector-set/BKing.svg +0 -15
  70. package/src/chess/assets/chess-vector-set/BKnight.svg +0 -13
  71. package/src/chess/assets/chess-vector-set/BPawn.svg +0 -7
  72. package/src/chess/assets/chess-vector-set/BQueen.svg +0 -6
  73. package/src/chess/assets/chess-vector-set/BRook.svg +0 -12
  74. package/src/chess/assets/chess-vector-set/WBiskup.svg +0 -14
  75. package/src/chess/assets/chess-vector-set/WKing.svg +0 -13
  76. package/src/chess/assets/chess-vector-set/WKnight.svg +0 -10
  77. package/src/chess/assets/chess-vector-set/WPawn.svg +0 -8
  78. package/src/chess/assets/chess-vector-set/WQueen.svg +0 -17
  79. package/src/chess/assets/chess-vector-set/WRook.svg +0 -13
  80. package/src/chess/context/ChessControlsContext.jsx +0 -379
  81. package/src/chess/dashboards/ChessHero.jsx +0 -303
  82. package/src/chess/index.js +0 -32
  83. package/src/chess/utils/parsePgnTree.js +0 -97
  84. package/src/dashboards/cards/DashAlertCard.jsx +0 -44
  85. package/src/dashboards/cards/DashChartCard.jsx +0 -44
  86. package/src/dashboards/cards/DashFeaturedCard.jsx +0 -42
  87. package/src/dashboards/cards/DashListCard.jsx +0 -87
  88. package/src/dashboards/cards/DashMetricCard.jsx +0 -59
  89. package/src/dashboards/cards/DashSlotCard.jsx +0 -45
  90. package/src/dashboards/cards/DashStackedBarCard.jsx +0 -70
  91. package/src/dashboards/cards/DashTableCard.jsx +0 -31
  92. package/src/dashboards/cards/_shared/CardHeader.jsx +0 -15
  93. package/src/dashboards/cards/_shared/LegendRow.jsx +0 -25
  94. package/src/dashboards/charts/Candlestick.jsx +0 -79
  95. package/src/dashboards/charts/DonutChart.jsx +0 -87
  96. package/src/dashboards/charts/Heatmap.jsx +0 -79
  97. package/src/dashboards/charts/Histogram.jsx +0 -56
  98. package/src/dashboards/charts/LineChart.jsx +0 -157
  99. package/src/dashboards/charts/ScatterPlot.jsx +0 -93
  100. package/src/dashboards/charts/Sparkline.jsx +0 -63
  101. package/src/dashboards/index.js +0 -28
  102. package/src/dashboards/layout/DashboardGrid.jsx +0 -30
  103. package/src/dashboards/layout/GridCard.jsx +0 -44
  104. package/src/dashboards/shared/DashTooltip.jsx +0 -6
  105. package/src/dashboards/shared/formatMetric.js +0 -26
  106. package/src/dashboards/shared/useChartTooltip.js +0 -25
  107. package/src/dashboards/shared/useCountUp.js +0 -107
  108. package/src/molecules/ArticleCard.jsx +0 -178
  109. package/src/molecules/WorkListItem.jsx +0 -83
  110. package/src/molecules/foundry/SpecimenSectionHeader.jsx +0 -89
  111. package/src/organisms/ArticleHeader.jsx +0 -95
  112. package/src/organisms/ColorLoader.jsx +0 -155
  113. package/src/organisms/DiagonalMarqueeRiver.jsx +0 -138
  114. package/src/organisms/ParallaxShelf.jsx +0 -141
  115. package/src/organisms/PortableTextRenderer.jsx +0 -115
  116. package/src/organisms/ProductDetailLayout.jsx +0 -189
  117. package/src/organisms/ScrollDriftGallery.jsx +0 -214
  118. package/src/organisms/StackHero.jsx +0 -83
  119. package/src/organisms/WorkCard.jsx +0 -120
  120. package/src/organisms/WorkViewToggle.jsx +0 -170
  121. package/src/organisms/foundry/FontPreviewSection.jsx +0 -187
  122. package/src/organisms/foundry/FoundryCharacterSets.jsx +0 -113
  123. package/src/organisms/foundry/GlyphMetricsGrid.jsx +0 -335
  124. package/src/organisms/foundry/TypefaceHero.jsx +0 -107
  125. package/src/organisms/foundry/TypefaceStyleSection.jsx +0 -163
  126. package/src/organisms/foundry/VariableFontSection.jsx +0 -158
  127. package/src/organisms/foundry/glyphData.js +0 -30
  128. package/src/organisms/foundry/index.js +0 -21
@@ -1,210 +0,0 @@
1
- import { useMemo } from 'react'
2
- import { Chess } from 'chess.js'
3
- import { ChessPiece } from '../index.js'
4
-
5
- const FILES = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
6
- const PIECE_TYPE_MAP = {
7
- p: 'pawn',
8
- r: 'rook',
9
- n: 'knight',
10
- b: 'bishop',
11
- q: 'queen',
12
- k: 'king'
13
- }
14
-
15
- const BOARD_THEMES = {
16
- 'blue-gray': { light: '#E8EDF9', dark: '#B7C0D8' },
17
- 'gray': { light: '#EFEFEF', dark: '#A6A6A6' },
18
- 'green-white': { light: '#ffffff', dark: '#0a682a' },
19
- 'olive': { light: '#EFEED2', dark: '#779557' },
20
- 'dark': { light: '#4D4D51', dark: '#242427' },
21
- 'brown': { light: '#F0D9B5', dark: '#B58863' }
22
- }
23
-
24
- const buildBoardState = (fen) => {
25
- const chess = new Chess()
26
- if (fen) {
27
- /* chess.js v1 load() returns void and THROWS on invalid FENs — and it
28
- * validates playability (kings required), not renderability. Display
29
- * states like the cleared board are legitimate here, so on throw we
30
- * place the FEN's board portion piece-by-piece instead of reverting
31
- * to the start position. */
32
- try {
33
- chess.load(fen)
34
- } catch {
35
- chess.clear()
36
- const ranks = fen.split(' ')[0].split('/')
37
- ranks.forEach((rank, r) => {
38
- let file = 0
39
- for (const ch of rank) {
40
- if (/\d/.test(ch)) { file += Number(ch); continue }
41
- const square = 'abcdefgh'[file] + (8 - r)
42
- try {
43
- chess.put({ type: ch.toLowerCase(), color: ch === ch.toLowerCase() ? 'b' : 'w' }, square)
44
- } catch { /* unplaceable char — leave square empty */ }
45
- file += 1
46
- }
47
- })
48
- }
49
- }
50
- return chess.board()
51
- }
52
-
53
- const getSquareTone = (fileIndex, rankIndex) =>
54
- (fileIndex + rankIndex) % 2 === 0 ? 'light' : 'dark'
55
-
56
- const getBoardSize = (size) => {
57
- if (size === 'fluid') return '100%'
58
- const sizeMap = {
59
- mobile: '384px',
60
- tablet: '520px',
61
- desktop: '760px'
62
- }
63
- return sizeMap[size] || size
64
- }
65
-
66
- const getPieceSize = (size) => {
67
- if (size === 'fluid') return '75%'
68
- const sizeMap = {
69
- sm: '44px',
70
- md: '64px',
71
- lg: '76px',
72
- mobile: '40px',
73
- tablet: '52px',
74
- desktop: '76px'
75
- }
76
- return sizeMap[size] || sizeMap.lg
77
- }
78
-
79
- const ChessBoard = ({ fen, size = 'desktop', orientation = 'white', showPieces = true, lastMove = null, pieceSet = 'default', boardTheme = 'green-white' }) => {
80
- const boardState = useMemo(() => buildBoardState(fen), [fen])
81
- const isFluid = size === 'fluid'
82
- const boardPixelSize = getBoardSize(size)
83
- const piecePixelSize = getPieceSize(size)
84
- const squarePixelSize = isFluid ? '100%' : `${parseInt(boardPixelSize) / 8}px`
85
- const coordinatePaddingClass = size === 'mobile' ? 'p-1' : size === 'tablet' ? '!p-1.5' : 'p-2'
86
- const coordinateTypographyClass = size === 'mobile' ? 'kol-helper-xxxs' : 'kol-helper-xs'
87
- const rankIndices =
88
- orientation === 'white'
89
- ? [...Array(boardState.length).keys()]
90
- : [...Array(boardState.length).keys()].reverse()
91
- const fileIndices =
92
- orientation === 'white'
93
- ? [...Array(FILES.length).keys()]
94
- : [...Array(FILES.length).keys()].reverse()
95
-
96
- const filesForOrientation = orientation === 'white' ? FILES : [...FILES].reverse()
97
- const ranksForOrientation =
98
- orientation === 'white'
99
- ? [...Array(8).keys()].map((i) => 8 - i)
100
- : [...Array(8).keys()].map((i) => i + 1)
101
-
102
- const theme = BOARD_THEMES[boardTheme] || BOARD_THEMES['green-white']
103
-
104
- return (
105
- <div className="chess-board-wrapper">
106
- <div
107
- className={`chess-board${isFluid ? ' chess-board--fluid' : ''}`}
108
- style={isFluid ? undefined : { width: boardPixelSize, height: boardPixelSize }}
109
- >
110
- <div className="chess-board__grid">
111
- {rankIndices.map((rankIndex) =>
112
- fileIndices.map((fileIndex) => {
113
- const square = boardState[rankIndex][fileIndex]
114
- const tone = getSquareTone(fileIndex, rankIndex)
115
- const coordinate = `${FILES[fileIndex]}${8 - rankIndex}`
116
- const pieceType = square?.type ?? null
117
- const pieceColor = square?.color === 'b' ? 'black' : 'white'
118
- const pieceName = pieceType ? PIECE_TYPE_MAP[pieceType] : null
119
-
120
- const isLastMoveSquare = lastMove && (coordinate === lastMove.from || coordinate === lastMove.to)
121
-
122
- const squareClass =
123
- tone === 'light'
124
- ? 'chess-square chess-square--light'
125
- : 'chess-square chess-square--dark'
126
-
127
- const highlightClass = isLastMoveSquare ? 'chess-square--highlighted' : ''
128
-
129
- const rankNum = 8 - rankIndex
130
- const fileLetter = FILES[fileIndex]
131
- const isDarkSquare = tone === 'dark'
132
- const textColor = isDarkSquare ? 'text-white' : 'text-[#166534]'
133
- const isFirstRank = rankNum === 1
134
- const isFirstRankFromBlack = rankNum === 8
135
- const isAFile = fileIndex === 0
136
-
137
- const squareColor = tone === 'light' ? theme.light : theme.dark
138
-
139
- return (
140
- <div
141
- key={coordinate}
142
- className={`${squareClass} ${highlightClass}`}
143
- data-square={coordinate}
144
- style={{ backgroundColor: squareColor }}
145
- >
146
- {showPieces && pieceName ? (
147
- <div className="relative z-10 flex items-center justify-center" style={{ width: squarePixelSize, height: squarePixelSize }}>
148
- <ChessPiece piece={pieceName} color={pieceColor} size={piecePixelSize} pieceSet={pieceSet} />
149
- </div>
150
- ) : null}
151
-
152
- {/* Coordinate labels - absolutely positioned to overlay on pieces */}
153
- <div className="absolute inset-0 pointer-events-none">
154
- {orientation === 'white' ? (
155
- // White orientation labels
156
- isFirstRank ? (
157
- coordinate === 'a1' ? (
158
- <div className={`${coordinatePaddingClass} flex justify-between w-full h-full`}>
159
- <div className={`flex items-start text-white ${coordinateTypographyClass} uppercase`}>
160
- <span>1</span>
161
- </div>
162
- <div className={`flex items-end text-white ${coordinateTypographyClass} uppercase`}>
163
- <span>A</span>
164
- </div>
165
- </div>
166
- ) : (
167
- <div className={`${coordinatePaddingClass} flex justify-end items-end ${textColor} ${coordinateTypographyClass} uppercase w-full h-full`}>
168
- <span>{fileLetter}</span>
169
- </div>
170
- )
171
- ) : isAFile ? (
172
- <div className={`${coordinatePaddingClass} flex justify-start items-start ${textColor} ${coordinateTypographyClass} uppercase w-full h-full`}>
173
- <span>{rankNum}</span>
174
- </div>
175
- ) : null
176
- ) : (
177
- // Black orientation labels (flipped)
178
- isFirstRankFromBlack ? (
179
- coordinate === 'a8' ? (
180
- <div className={`${coordinatePaddingClass} flex justify-between w-full h-full`}>
181
- <div className={`flex items-start text-white ${coordinateTypographyClass} uppercase`}>
182
- <span>8</span>
183
- </div>
184
- <div className={`flex items-end text-white ${coordinateTypographyClass} uppercase`}>
185
- <span>A</span>
186
- </div>
187
- </div>
188
- ) : (
189
- <div className={`${coordinatePaddingClass} flex justify-end items-end ${textColor} ${coordinateTypographyClass} uppercase w-full h-full`}>
190
- <span>{fileLetter}</span>
191
- </div>
192
- )
193
- ) : isAFile ? (
194
- <div className={`${coordinatePaddingClass} flex justify-start items-start ${textColor} ${coordinateTypographyClass} uppercase w-full h-full`}>
195
- <span>{rankNum}</span>
196
- </div>
197
- ) : null
198
- )}
199
- </div>
200
- </div>
201
- )
202
- })
203
- )}
204
- </div>
205
- </div>
206
- </div>
207
- )
208
- }
209
-
210
- export default ChessBoard
@@ -1,49 +0,0 @@
1
- import ChessBoard from './ChessBoard'
2
- import ChessSidebar from './ChessSidebar'
3
-
4
- const ChessBoardFullscreen = ({
5
- boardProps = {},
6
- sidebarProps = {},
7
- wrapperClassName = '',
8
- sidebarWidth = '400px'
9
- }) => {
10
- const {
11
- className: boardContainerClassName = '',
12
- size: boardSize = 'desktop',
13
- ...restBoardProps
14
- } = boardProps
15
-
16
- const {
17
- className: sidebarClassName = 'w-full h-full bg-fg-04 text-auto',
18
- size: sidebarSize = 'desktop',
19
- variant: sidebarVariant = 'minimal',
20
- ...restSidebarProps
21
- } = sidebarProps
22
-
23
- const rootClassName = ['relative w-full', wrapperClassName].filter(Boolean).join(' ')
24
-
25
- return (
26
- <div className={rootClassName} style={{ minHeight: '100dvh' }}>
27
- <div
28
- className={`flex h-full w-full items-center justify-center ${boardContainerClassName}`}
29
- style={{ paddingRight: sidebarWidth, paddingLeft: 0, minHeight: '100dvh' }}
30
- >
31
- <ChessBoard size={boardSize} {...restBoardProps} />
32
- </div>
33
-
34
- <div
35
- className="absolute inset-y-0 right-0 flex"
36
- style={{ width: sidebarWidth }}
37
- >
38
- <ChessSidebar
39
- size={sidebarSize}
40
- variant={sidebarVariant}
41
- className={`h-full w-full ${sidebarClassName}`}
42
- {...restSidebarProps}
43
- />
44
- </div>
45
- </div>
46
- )
47
- }
48
-
49
- export default ChessBoardFullscreen
@@ -1,41 +0,0 @@
1
- import { useChessControls, ChessControlsProvider } from '../context/ChessControlsContext'
2
- import ChessBoard from './ChessBoard'
3
- import AlternativeControlsMock from './AlternativeControlsMock'
4
- import PlaybackControls from './PlaybackControls'
5
-
6
- const ChessBoardView = () => {
7
- const { snapshots, moveIndex, orientation, lastMove, pieceSet, boardTheme } = useChessControls()
8
- return (
9
- <ChessBoard
10
- fen={snapshots[moveIndex]?.fen}
11
- size="fluid"
12
- orientation={orientation}
13
- lastMove={lastMove}
14
- pieceSet={pieceSet}
15
- boardTheme={boardTheme}
16
- />
17
- )
18
- }
19
-
20
- const ChessBoardWithControlsContent = () => {
21
- return (
22
- <div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
23
- <div className="flex-1 min-w-0">
24
- <ChessBoardView />
25
- </div>
26
- <div className="w-full lg:w-[440px] lg:flex-shrink-0">
27
- <AlternativeControlsMock />
28
- </div>
29
- </div>
30
- )
31
- }
32
-
33
- const ChessBoardWithControls = ({ externalGame = null, chessData }) => {
34
- return (
35
- <ChessControlsProvider externalGame={externalGame} chessData={chessData}>
36
- <ChessBoardWithControlsContent />
37
- </ChessControlsProvider>
38
- )
39
- }
40
-
41
- export default ChessBoardWithControls
@@ -1,122 +0,0 @@
1
- import { ChessControlsProvider, useChessControls } from '../context/ChessControlsContext'
2
- import ChessBoard from './ChessBoard'
3
- import ChessBoardFullscreen from './ChessBoardFullscreen'
4
- import ChessSidebar from './ChessSidebar'
5
-
6
- const ToolbarButton = ({ label, onClick }) => (
7
- <button type="button" onClick={onClick} className="board-toolbar__button">
8
- {label}
9
- </button>
10
- )
11
-
12
- const ChessBoardWithSidebarContent = ({ className = '', onToggleFullscreen = null, isFullscreen = false }) => {
13
- const {
14
- snapshots,
15
- moveIndex,
16
- orientation,
17
- selectedGame,
18
- selectedGameId,
19
- filteredGames,
20
- setSelectedGameId,
21
- goToStart,
22
- goToEnd,
23
- stepBackward,
24
- stepForward,
25
- togglePlayback
26
- } = useChessControls()
27
-
28
- const handleSelectGame = (event) => {
29
- setSelectedGameId(event.target.value || null)
30
- }
31
-
32
- if (isFullscreen) {
33
- const fullscreenWrapperClassName = ['min-h-dvh', className].filter(Boolean).join(' ')
34
- return (
35
- <ChessBoardFullscreen
36
- wrapperClassName={fullscreenWrapperClassName}
37
- boardProps={{
38
- size: 'desktop',
39
- fen: snapshots[moveIndex]?.fen,
40
- orientation
41
- }}
42
- sidebarProps={{
43
- selectedGame,
44
- selectedGameId,
45
- sampleGames: filteredGames,
46
- moveIndex,
47
- onSelectGame: handleSelectGame,
48
- onGoToStart: goToStart,
49
- onStepBackward: stepBackward,
50
- onStepForward: stepForward,
51
- onGoToEnd: goToEnd,
52
- onTogglePlayback: togglePlayback,
53
- size: 'desktop',
54
- variant: 'minimal',
55
- className: 'w-full h-full bg-fg-04 text-auto',
56
- onToggleFullscreen,
57
- isFullscreen
58
- }}
59
- />
60
- )
61
- }
62
-
63
- const rootClassName = ['board-playback', className].filter(Boolean).join(' ')
64
-
65
- return (
66
- <div className={rootClassName}>
67
- <div className="board-playback__toolbar board-playback__toolbar--left">
68
- <ToolbarButton label="⏮" onClick={goToStart} />
69
- <ToolbarButton label="▶" onClick={togglePlayback} />
70
- <ToolbarButton label="⏭" onClick={goToEnd} />
71
- </div>
72
-
73
- <div className="board-playback__toolbar board-playback__toolbar--right">
74
- <ToolbarButton label="⏮" onClick={goToStart} />
75
- <ToolbarButton label="◀" onClick={stepBackward} />
76
- <ToolbarButton label="▶" onClick={togglePlayback} />
77
- <ToolbarButton label="▸" onClick={stepForward} />
78
- <ToolbarButton label="⏭" onClick={goToEnd} />
79
- </div>
80
-
81
- <div className="board-playback__content">
82
- <ChessBoard fen={snapshots[moveIndex]?.fen} size="fluid" orientation={orientation} />
83
-
84
- <ChessSidebar
85
- selectedGame={selectedGame}
86
- selectedGameId={selectedGameId}
87
- sampleGames={filteredGames}
88
- moveIndex={moveIndex}
89
- onSelectGame={handleSelectGame}
90
- onGoToStart={goToStart}
91
- onStepBackward={stepBackward}
92
- onStepForward={stepForward}
93
- onGoToEnd={goToEnd}
94
- onTogglePlayback={togglePlayback}
95
- size="desktop"
96
- onToggleFullscreen={onToggleFullscreen}
97
- isFullscreen={isFullscreen}
98
- />
99
- </div>
100
- </div>
101
- )
102
- }
103
-
104
- const ChessBoardWithSidebar = ({
105
- className = '',
106
- onToggleFullscreen = null,
107
- isFullscreen = false,
108
- externalGame = null,
109
- chessData
110
- }) => {
111
- return (
112
- <ChessControlsProvider externalGame={externalGame} chessData={chessData}>
113
- <ChessBoardWithSidebarContent
114
- className={className}
115
- onToggleFullscreen={onToggleFullscreen}
116
- isFullscreen={isFullscreen}
117
- />
118
- </ChessControlsProvider>
119
- )
120
- }
121
-
122
- export default ChessBoardWithSidebar
@@ -1,131 +0,0 @@
1
- import { ChessPiece } from '../index.js'
2
-
3
- const getPieceSize = (size) => {
4
- const sizeMap = {
5
- sm: '28px',
6
- md: '40px',
7
- lg: '52px'
8
- }
9
- return sizeMap[size] || sizeMap.lg
10
- }
11
-
12
- const ChessSidebar = ({
13
- selectedGame = null,
14
- selectedGameId = null,
15
- sampleGames = [],
16
- moveIndex = 0,
17
- onSelectGame = () => {},
18
- onGoToStart = () => {},
19
- onStepBackward = () => {},
20
- onStepForward = () => {},
21
- onGoToEnd = () => {},
22
- onTogglePlayback = () => {},
23
- size = 'desktop',
24
- className = '',
25
- variant = 'default',
26
- onToggleFullscreen = null,
27
- isFullscreen = false
28
- }) => {
29
- const piecePixelSize = getPieceSize(size)
30
- const getSidebarClass = (variant) => {
31
- switch (variant) {
32
- case 'minimal':
33
- return 'board-playback__sidebar--minimal'
34
- case 'plain':
35
- return 'board-playback__sidebar--plain'
36
- default:
37
- return 'board-playback__sidebar'
38
- }
39
- }
40
- const sidebarClass = getSidebarClass(variant)
41
- const showFullscreenToggle = typeof onToggleFullscreen === 'function'
42
- const fullscreenLabel = isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'
43
- const toolbarButton = (label, onClick) => (
44
- <button type="button" onClick={onClick} className="board-toolbar__button">
45
- {label}
46
- </button>
47
- )
48
-
49
- return (
50
- <aside className={`${sidebarClass} ${className}`}>
51
- <header className="board-playback__sidebar-header">
52
- <div className="flex items-center justify-between gap-3">
53
- <span className="board-playback__badge">{'{}'} Setup Position</span>
54
- {showFullscreenToggle ? (
55
- <button
56
- type="button"
57
- onClick={onToggleFullscreen}
58
- className="board-toolbar__button board-toolbar__button--compact"
59
- >
60
- {fullscreenLabel}
61
- </button>
62
- ) : null}
63
- </div>
64
-
65
- <div className="space-y-2">
66
- <h3 className="board-playback__title">
67
- {selectedGame?.player?.username ?? 'Player'} vs{' '}
68
- {selectedGame?.opponent?.username ?? 'Opponent'}
69
- </h3>
70
- <p className="board-playback__meta">
71
- {selectedGame?.timeClass ?? 'blitz'} · {selectedGame?.timeControl ?? '180'} · move{' '}
72
- {moveIndex}
73
- </p>
74
- </div>
75
-
76
- <select
77
- value={selectedGameId ?? ''}
78
- onChange={(e) => onSelectGame(e.target.value || null)}
79
- className="board-playback__select"
80
- >
81
- {(sampleGames || []).map((game) => (
82
- <option value={game.id} key={game.id}>
83
- {game.player?.username} vs {game.opponent?.username}
84
- </option>
85
- ))}
86
- </select>
87
- </header>
88
-
89
- <div className="board-playback__palette">
90
- <div className="board-playback__palette-row">
91
- {['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook'].map(
92
- (piece, index) => (
93
- <ChessPiece key={`white-${piece}-${index}`} piece={piece} color="white" size={piecePixelSize} />
94
- )
95
- )}
96
- </div>
97
- <div className="board-playback__palette-row">
98
- {['rook', 'knight', 'bishop', 'queen', 'king', 'bishop', 'knight', 'rook'].map(
99
- (piece, index) => (
100
- <ChessPiece key={`black-${piece}-${index}`} piece={piece} color="black" size={piecePixelSize} />
101
- )
102
- )}
103
- </div>
104
- </div>
105
-
106
- <div className="board-playback__controls">
107
- {toolbarButton('⏮', onGoToStart)}
108
- {toolbarButton('◀', onStepBackward)}
109
- {toolbarButton('▶', onTogglePlayback)}
110
- {toolbarButton('▸', onStepForward)}
111
- {toolbarButton('⏭', onGoToEnd)}
112
- </div>
113
-
114
- <div className="board-playback__footer">
115
- <span>⚙</span>
116
- <span>♜</span>
117
- <span>♘</span>
118
- <span>♞</span>
119
- <div className="board-playback__avatar">
120
- <div className="board-playback__avatar-img" />
121
- <div>
122
- <div className="board-playback__avatar-name">Tór Grimsson</div>
123
- <div className="board-playback__avatar-meta">Oct 11, 2025</div>
124
- </div>
125
- </div>
126
- </div>
127
- </aside>
128
- )
129
- }
130
-
131
- export default ChessSidebar