@kolkrabbi/kol-component 0.5.0 → 0.7.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.
- package/package.json +5 -3
- package/src/atoms/Button.jsx +9 -5
- package/src/atoms/Input.jsx +9 -6
- package/src/atoms/Textarea.jsx +36 -5
- package/src/atoms/ToggleSwitch.jsx +31 -4
- package/src/chess/ChessPiece.jsx +191 -0
- package/src/chess/apparatus/AlternativeControlsMock.jsx +460 -0
- package/src/chess/apparatus/ChessAnalysisLayout.jsx +24 -0
- package/src/chess/apparatus/ChessBoard.jsx +210 -0
- package/src/chess/apparatus/ChessBoardFullscreen.jsx +49 -0
- package/src/chess/apparatus/ChessBoardWithControls.jsx +41 -0
- package/src/chess/apparatus/ChessBoardWithSidebar.jsx +122 -0
- package/src/chess/apparatus/ChessSidebar.jsx +131 -0
- package/src/chess/apparatus/GameArchiveTable.jsx +493 -0
- package/src/chess/apparatus/GameSelector.jsx +47 -0
- package/src/chess/apparatus/NotationPanel.jsx +70 -0
- package/src/chess/apparatus/PlaybackControls.jsx +52 -0
- package/src/chess/apparatus/VariationTree.jsx +137 -0
- package/src/chess/assets/chess-extra-set/set-1/bishop-1.svg +6 -0
- package/src/chess/assets/chess-extra-set/set-1/bishop.svg +6 -0
- package/src/chess/assets/chess-extra-set/set-1/king-1.svg +9 -0
- package/src/chess/assets/chess-extra-set/set-1/king.svg +7 -0
- package/src/chess/assets/chess-extra-set/set-1/knight-1.svg +8 -0
- package/src/chess/assets/chess-extra-set/set-1/knight.svg +7 -0
- package/src/chess/assets/chess-extra-set/set-1/pawn-1.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-1/pawn.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-1/queen-1.svg +11 -0
- package/src/chess/assets/chess-extra-set/set-1/queen.svg +6 -0
- package/src/chess/assets/chess-extra-set/set-1/rook-1.svg +7 -0
- package/src/chess/assets/chess-extra-set/set-1/rook.svg +11 -0
- package/src/chess/assets/chess-extra-set/set-2/bishop-1.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/bishop.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/king-1.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/king.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/knight-1.svg +5 -0
- package/src/chess/assets/chess-extra-set/set-2/knight.svg +5 -0
- package/src/chess/assets/chess-extra-set/set-2/pawn-1.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/pawn.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/queen-1.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/queen.svg +4 -0
- package/src/chess/assets/chess-extra-set/set-2/rook-1.svg +5 -0
- package/src/chess/assets/chess-extra-set/set-2/rook.svg +5 -0
- package/src/chess/assets/chess-extra-set/set-3/bishop-1.svg +10 -0
- package/src/chess/assets/chess-extra-set/set-3/bishop.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-3/king-1.svg +12 -0
- package/src/chess/assets/chess-extra-set/set-3/king.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-3/knight-1.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-3/knight.svg +10 -0
- package/src/chess/assets/chess-extra-set/set-3/pawn-1.svg +10 -0
- package/src/chess/assets/chess-extra-set/set-3/pawn.svg +10 -0
- package/src/chess/assets/chess-extra-set/set-3/queen-1.svg +10 -0
- package/src/chess/assets/chess-extra-set/set-3/queen.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-3/rook-1.svg +3 -0
- package/src/chess/assets/chess-extra-set/set-3/rook.svg +11 -0
- package/src/chess/assets/chess-vector-set/ABoard.svg +164 -0
- package/src/chess/assets/chess-vector-set/BBiskup.svg +12 -0
- package/src/chess/assets/chess-vector-set/BKing.svg +15 -0
- package/src/chess/assets/chess-vector-set/BKnight.svg +13 -0
- package/src/chess/assets/chess-vector-set/BPawn.svg +7 -0
- package/src/chess/assets/chess-vector-set/BQueen.svg +6 -0
- package/src/chess/assets/chess-vector-set/BRook.svg +12 -0
- package/src/chess/assets/chess-vector-set/WBiskup.svg +14 -0
- package/src/chess/assets/chess-vector-set/WKing.svg +13 -0
- package/src/chess/assets/chess-vector-set/WKnight.svg +10 -0
- package/src/chess/assets/chess-vector-set/WPawn.svg +8 -0
- package/src/chess/assets/chess-vector-set/WQueen.svg +17 -0
- package/src/chess/assets/chess-vector-set/WRook.svg +13 -0
- package/src/chess/context/ChessControlsContext.jsx +379 -0
- package/src/chess/dashboards/ChessHero.jsx +303 -0
- package/src/chess/index.js +32 -0
- package/src/chess/utils/parsePgnTree.js +97 -0
- package/src/dashboards/cards/DashAlertCard.jsx +44 -0
- package/src/dashboards/cards/DashChartCard.jsx +44 -0
- package/src/dashboards/cards/DashFeaturedCard.jsx +42 -0
- package/src/dashboards/cards/DashListCard.jsx +87 -0
- package/src/dashboards/cards/DashMetricCard.jsx +59 -0
- package/src/dashboards/cards/DashSlotCard.jsx +45 -0
- package/src/dashboards/cards/DashStackedBarCard.jsx +70 -0
- package/src/dashboards/cards/DashTableCard.jsx +31 -0
- package/src/dashboards/cards/_shared/CardHeader.jsx +15 -0
- package/src/dashboards/cards/_shared/LegendRow.jsx +25 -0
- package/src/dashboards/charts/Candlestick.jsx +79 -0
- package/src/dashboards/charts/DonutChart.jsx +87 -0
- package/src/dashboards/charts/Heatmap.jsx +79 -0
- package/src/dashboards/charts/Histogram.jsx +56 -0
- package/src/dashboards/charts/LineChart.jsx +157 -0
- package/src/dashboards/charts/ScatterPlot.jsx +93 -0
- package/src/dashboards/charts/Sparkline.jsx +63 -0
- package/src/dashboards/index.js +28 -0
- package/src/dashboards/layout/DashboardGrid.jsx +30 -0
- package/src/dashboards/layout/GridCard.jsx +44 -0
- package/src/dashboards/shared/DashTooltip.jsx +6 -0
- package/src/dashboards/shared/formatMetric.js +26 -0
- package/src/dashboards/shared/useChartTooltip.js +25 -0
- package/src/dashboards/shared/useCountUp.js +107 -0
- package/src/molecules/Dropdown.jsx +50 -96
- package/src/molecules/Slider.jsx +4 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "KOL design-system components — atoms through organisms, emitting canonical kol-* classes. Pairs with @kolkrabbi/kol-theme for styling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"module": "./src/index.js",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": "./src/index.js",
|
|
11
|
-
"./foundry": "./src/organisms/foundry/index.js"
|
|
11
|
+
"./foundry": "./src/organisms/foundry/index.js",
|
|
12
|
+
"./dashboards": "./src/dashboards/index.js",
|
|
13
|
+
"./chess": "./src/chess/index.js"
|
|
12
14
|
},
|
|
13
15
|
"files": [
|
|
14
16
|
"src",
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
"dependencies": {
|
|
25
27
|
"@floating-ui/react": "^0.27.19",
|
|
26
28
|
"embla-carousel-react": "^8.6.0",
|
|
27
|
-
"@kolkrabbi/kol-icons": "0.
|
|
29
|
+
"@kolkrabbi/kol-icons": "0.5.0"
|
|
28
30
|
},
|
|
29
31
|
"peerDependencies": {
|
|
30
32
|
"react": "^18.3.0 || ^19.0.0",
|
package/src/atoms/Button.jsx
CHANGED
|
@@ -82,11 +82,15 @@ const Button = ({
|
|
|
82
82
|
|
|
83
83
|
// Add kol-btn-animate class if animateIcon is true to disable default hover states
|
|
84
84
|
const animateClass = animateIcon ? 'kol-btn-animate' : ''
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
85
|
+
// `selected` is a legacy alias of `pressed` — both express the aria-pressed
|
|
86
|
+
// toggle-on state and render through .kol-btn-pressed. (The old
|
|
87
|
+
// .kol-btn-selected class never had a CSS rule, so `selected` was a no-op.)
|
|
88
|
+
// Resolve to one flag so the quiet-drop and the pressed fill agree.
|
|
89
|
+
const isPressed = pressed !== undefined ? pressed : selected
|
|
90
|
+
const quietClass = quiet && !isPressed ? 'kol-btn-quiet' : ''
|
|
91
|
+
const pressedClass = isPressed ? 'kol-btn-pressed' : ''
|
|
92
|
+
|
|
93
|
+
const combinedClass = `kol-btn ${variantClass} ${sizeClass} ${animateClass} ${quietClass} ${pressedClass} ${className}`.trim().replace(/\s+/g, ' ')
|
|
90
94
|
|
|
91
95
|
// Render icon with optional hover state
|
|
92
96
|
const renderIcon = (iconName, iconHoverName) => {
|
package/src/atoms/Input.jsx
CHANGED
|
@@ -3,11 +3,11 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
3
3
|
/**
|
|
4
4
|
* Input — single-input atom built on the .kol-control shell.
|
|
5
5
|
*
|
|
6
|
-
* variant="filled" (default) — persistent solid bg
|
|
7
|
-
* variant="
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* variant="filled" (default) — persistent solid bg (= button primary chrome)
|
|
7
|
+
* variant="outline" — bordered, transparent bg — THE secondary
|
|
8
|
+
* treatment (2026-07-08 chrome law: one
|
|
9
|
+
* secondary, always subordinate to filled)
|
|
10
|
+
* variant="ghost" — legacy alias, resolves to outline
|
|
11
11
|
*
|
|
12
12
|
* size="sm" / "md" (default) / "lg" — matched padding + type class
|
|
13
13
|
*
|
|
@@ -57,9 +57,12 @@ export default function Input({
|
|
|
57
57
|
const fixedChars = typeof chars === 'number'
|
|
58
58
|
const resolvedIconSize = iconSize ?? ICON_SIZE[size] ?? 14
|
|
59
59
|
|
|
60
|
+
// ghost folds into outline (2026-07-08 chrome law): one secondary treatment.
|
|
61
|
+
const resolvedVariant = variant === 'ghost' ? 'outline' : variant
|
|
62
|
+
|
|
60
63
|
const shellCls = [
|
|
61
64
|
'kol-control',
|
|
62
|
-
`kol-control--${
|
|
65
|
+
`kol-control--${resolvedVariant}`,
|
|
63
66
|
`kol-control-${size}`,
|
|
64
67
|
SIZE_TYPE[size],
|
|
65
68
|
'cursor-text',
|
package/src/atoms/Textarea.jsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import { Icon } from '@kolkrabbi/kol-icons'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -11,8 +12,10 @@ import { Icon } from '@kolkrabbi/kol-icons'
|
|
|
11
12
|
* size="sm" / "md" (default) / "lg" — padding + type class
|
|
12
13
|
*
|
|
13
14
|
* Default rows = 3 (kept short — long content gets scrollbars instead of
|
|
14
|
-
* dominating the panel).
|
|
15
|
-
*
|
|
15
|
+
* dominating the panel). Resize is real (2026-07-08): native resize is
|
|
16
|
+
* OFF (Firefox's built-in grip cannot be hidden any other way) and the
|
|
17
|
+
* kol-icon-set-v1 `resize-grip` icon IS the drag handle — corner drag,
|
|
18
|
+
* both axes, min 120×40. One grip, every browser.
|
|
16
19
|
*
|
|
17
20
|
* Controlled OR uncontrolled:
|
|
18
21
|
* - pass `value` + `onChange` for controlled,
|
|
@@ -34,9 +37,12 @@ export default function Textarea({
|
|
|
34
37
|
className = '',
|
|
35
38
|
...props
|
|
36
39
|
}) {
|
|
40
|
+
// ghost folds into outline (2026-07-08 chrome law): one secondary treatment.
|
|
41
|
+
const resolvedVariant = variant === 'ghost' ? 'outline' : variant
|
|
42
|
+
|
|
37
43
|
const wrapperCls = [
|
|
38
44
|
'kol-control',
|
|
39
|
-
`kol-control--${
|
|
45
|
+
`kol-control--${resolvedVariant}`,
|
|
40
46
|
'kol-control--textarea',
|
|
41
47
|
`kol-control-${size}`,
|
|
42
48
|
SIZE_TYPE[size],
|
|
@@ -62,8 +68,33 @@ export default function Textarea({
|
|
|
62
68
|
style={{ resize: 'none' }}
|
|
63
69
|
{...props}
|
|
64
70
|
/>
|
|
65
|
-
<span
|
|
66
|
-
|
|
71
|
+
<span
|
|
72
|
+
aria-hidden="true"
|
|
73
|
+
className="kol-textarea-resize-icon"
|
|
74
|
+
onPointerDown={(e) => {
|
|
75
|
+
// The grip IS the resize handle — corner drag, both axes
|
|
76
|
+
// (min 120×40). Width lands on the shell, height on the textarea.
|
|
77
|
+
const shell = e.currentTarget.parentElement
|
|
78
|
+
const ta = shell?.querySelector('textarea')
|
|
79
|
+
if (!shell || !ta) return
|
|
80
|
+
e.preventDefault()
|
|
81
|
+
const startX = e.clientX
|
|
82
|
+
const startY = e.clientY
|
|
83
|
+
const startW = shell.offsetWidth
|
|
84
|
+
const startH = ta.offsetHeight
|
|
85
|
+
const move = (ev) => {
|
|
86
|
+
shell.style.width = `${Math.max(startW + ev.clientX - startX, 120)}px`
|
|
87
|
+
ta.style.height = `${Math.max(startH + ev.clientY - startY, 40)}px`
|
|
88
|
+
}
|
|
89
|
+
const up = () => {
|
|
90
|
+
window.removeEventListener('pointermove', move)
|
|
91
|
+
window.removeEventListener('pointerup', up)
|
|
92
|
+
}
|
|
93
|
+
window.addEventListener('pointermove', move)
|
|
94
|
+
window.addEventListener('pointerup', up)
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
<Icon name="resize-grip" size={12} />
|
|
67
98
|
</span>
|
|
68
99
|
</label>
|
|
69
100
|
)
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* ToggleSwitch — bare by default (2026-07-08 chrome law rewrite).
|
|
5
|
+
*
|
|
6
|
+
* variant="bare" (default) — label + track, no box
|
|
7
|
+
* variant="primary" — filled shell (surface-secondary), button geometry
|
|
8
|
+
* variant="outline" — bordered shell (border-oq-16), button geometry
|
|
9
|
+
*
|
|
10
|
+
* size="sm" / "md" (default) / "lg" — shells match button heights
|
|
11
|
+
* (26/32/40); the track scales with size in all variants.
|
|
12
|
+
*
|
|
13
|
+
* Legacy aliases: 'plain' → bare · 'default' (the old boxed look) → outline.
|
|
14
|
+
* Type via kol-mono-{12,14,16}; no auto-uppercase — casing is authored at
|
|
15
|
+
* the call site (KOL no-auto-casing rule).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const SIZE_TYPE = { sm: 'kol-mono-12', md: 'kol-mono-14', lg: 'kol-mono-16' }
|
|
19
|
+
const LEGACY_VARIANTS = { plain: 'bare', default: 'outline' }
|
|
20
|
+
|
|
3
21
|
const ToggleSwitch = ({
|
|
4
22
|
label,
|
|
5
23
|
checked = false,
|
|
6
24
|
onChange,
|
|
7
25
|
onToggle,
|
|
8
|
-
variant = '
|
|
26
|
+
variant = 'bare',
|
|
27
|
+
size = 'md',
|
|
9
28
|
className = '',
|
|
10
29
|
hint,
|
|
11
30
|
...props
|
|
@@ -15,12 +34,20 @@ const ToggleSwitch = ({
|
|
|
15
34
|
if (onChange) onChange(!checked)
|
|
16
35
|
}
|
|
17
36
|
|
|
18
|
-
const
|
|
37
|
+
const resolvedVariant = LEGACY_VARIANTS[variant] || variant
|
|
38
|
+
|
|
39
|
+
const cls = [
|
|
40
|
+
'toggle-switch',
|
|
41
|
+
`toggle-switch--${resolvedVariant}`,
|
|
42
|
+
`toggle-switch--${size}`,
|
|
43
|
+
SIZE_TYPE[size],
|
|
44
|
+
className,
|
|
45
|
+
].filter(Boolean).join(' ')
|
|
19
46
|
|
|
20
47
|
return (
|
|
21
48
|
<button
|
|
22
49
|
type="button"
|
|
23
|
-
className={
|
|
50
|
+
className={cls}
|
|
24
51
|
data-state={checked ? 'on' : 'off'}
|
|
25
52
|
onClick={handleClick}
|
|
26
53
|
aria-pressed={checked}
|
|
@@ -29,7 +56,7 @@ const ToggleSwitch = ({
|
|
|
29
56
|
<span className="toggle-switch-label">
|
|
30
57
|
{label}
|
|
31
58
|
{hint ? (
|
|
32
|
-
<span className="ml-2 opacity-60
|
|
59
|
+
<span className="ml-2 opacity-60 kol-helper-10">
|
|
33
60
|
{hint}
|
|
34
61
|
</span>
|
|
35
62
|
) : null}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Load default chess vector set
|
|
2
|
+
const svgModules = import.meta.glob('./assets/chess-vector-set/*.svg', {
|
|
3
|
+
eager: true,
|
|
4
|
+
query: '?raw',
|
|
5
|
+
import: 'default'
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
// Load extra chess sets (set-1, set-2, set-3)
|
|
9
|
+
const extraSet1Modules = import.meta.glob('./assets/chess-extra-set/set-1/*.svg', {
|
|
10
|
+
eager: true,
|
|
11
|
+
query: '?raw',
|
|
12
|
+
import: 'default'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const extraSet2Modules = import.meta.glob('./assets/chess-extra-set/set-2/*.svg', {
|
|
16
|
+
eager: true,
|
|
17
|
+
query: '?raw',
|
|
18
|
+
import: 'default'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const extraSet3Modules = import.meta.glob('./assets/chess-extra-set/set-3/*.svg', {
|
|
22
|
+
eager: true,
|
|
23
|
+
query: '?raw',
|
|
24
|
+
import: 'default'
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const PIECE_KEY_MAP = {
|
|
28
|
+
white: {
|
|
29
|
+
king: 'WKing',
|
|
30
|
+
queen: 'WQueen',
|
|
31
|
+
rook: 'WRook',
|
|
32
|
+
bishop: 'WBiskup',
|
|
33
|
+
knight: 'WKnight',
|
|
34
|
+
pawn: 'WPawn'
|
|
35
|
+
},
|
|
36
|
+
black: {
|
|
37
|
+
king: 'BKing',
|
|
38
|
+
queen: 'BQueen',
|
|
39
|
+
rook: 'BRook',
|
|
40
|
+
bishop: 'BBiskup',
|
|
41
|
+
knight: 'BKnight',
|
|
42
|
+
pawn: 'BPawn'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Map for extra sets (use simpler naming convention)
|
|
47
|
+
const EXTRA_SET_PIECE_MAP = {
|
|
48
|
+
white: {
|
|
49
|
+
king: 'king-1',
|
|
50
|
+
queen: 'queen-1',
|
|
51
|
+
rook: 'rook-1',
|
|
52
|
+
bishop: 'bishop-1',
|
|
53
|
+
knight: 'knight-1',
|
|
54
|
+
pawn: 'pawn-1'
|
|
55
|
+
},
|
|
56
|
+
black: {
|
|
57
|
+
king: 'king',
|
|
58
|
+
queen: 'queen',
|
|
59
|
+
rook: 'rook',
|
|
60
|
+
bishop: 'bishop',
|
|
61
|
+
knight: 'knight',
|
|
62
|
+
pawn: 'pawn'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const PIECE_CACHE = Object.entries(svgModules).reduce((acc, [path, svgContent]) => {
|
|
67
|
+
const fileName = path.split('/').pop() || ''
|
|
68
|
+
const key = fileName.replace('.svg', '')
|
|
69
|
+
acc[key] = svgContent
|
|
70
|
+
return acc
|
|
71
|
+
}, {})
|
|
72
|
+
|
|
73
|
+
// Cache for extra sets
|
|
74
|
+
const EXTRA_SET_1_CACHE = Object.entries(extraSet1Modules).reduce((acc, [path, svgContent]) => {
|
|
75
|
+
const fileName = path.split('/').pop() || ''
|
|
76
|
+
const key = fileName.replace('.svg', '')
|
|
77
|
+
acc[key] = svgContent
|
|
78
|
+
return acc
|
|
79
|
+
}, {})
|
|
80
|
+
|
|
81
|
+
const EXTRA_SET_2_CACHE = Object.entries(extraSet2Modules).reduce((acc, [path, svgContent]) => {
|
|
82
|
+
const fileName = path.split('/').pop() || ''
|
|
83
|
+
const key = fileName.replace('.svg', '')
|
|
84
|
+
acc[key] = svgContent
|
|
85
|
+
return acc
|
|
86
|
+
}, {})
|
|
87
|
+
|
|
88
|
+
const EXTRA_SET_3_CACHE = Object.entries(extraSet3Modules).reduce((acc, [path, svgContent]) => {
|
|
89
|
+
const fileName = path.split('/').pop() || ''
|
|
90
|
+
const key = fileName.replace('.svg', '')
|
|
91
|
+
acc[key] = svgContent
|
|
92
|
+
return acc
|
|
93
|
+
}, {})
|
|
94
|
+
|
|
95
|
+
const normalizeSize = (value) => {
|
|
96
|
+
if (typeof value === 'number') {
|
|
97
|
+
return `${value}px`
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
return value
|
|
101
|
+
}
|
|
102
|
+
return '64px'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const applySizeToMarkup = (markup) => {
|
|
106
|
+
let updated = markup
|
|
107
|
+
updated = updated.replace(/width="[^"]*"/i, 'width="100%"')
|
|
108
|
+
updated = updated.replace(/height="[^"]*"/i, 'height="100%"')
|
|
109
|
+
return updated
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const ChessPieceBase = ({
|
|
113
|
+
size = 64,
|
|
114
|
+
className = '',
|
|
115
|
+
style = {},
|
|
116
|
+
children
|
|
117
|
+
}) => {
|
|
118
|
+
const dimension = normalizeSize(size)
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div
|
|
122
|
+
className={`relative inline-flex aspect-square items-center justify-center ${className}`}
|
|
123
|
+
style={{
|
|
124
|
+
width: dimension,
|
|
125
|
+
height: dimension,
|
|
126
|
+
...style
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{children}
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const ChessPiece = ({
|
|
135
|
+
piece,
|
|
136
|
+
color = 'white',
|
|
137
|
+
size = 64,
|
|
138
|
+
className = '',
|
|
139
|
+
style = {},
|
|
140
|
+
pieceSet = 'default'
|
|
141
|
+
}) => {
|
|
142
|
+
const normalizedColor = color === 'black' ? 'black' : 'white'
|
|
143
|
+
|
|
144
|
+
let pieceKey
|
|
145
|
+
let cache
|
|
146
|
+
|
|
147
|
+
// Select piece set and cache
|
|
148
|
+
if (pieceSet === 'set-1') {
|
|
149
|
+
pieceKey = EXTRA_SET_PIECE_MAP[normalizedColor]?.[piece]
|
|
150
|
+
cache = EXTRA_SET_1_CACHE
|
|
151
|
+
} else if (pieceSet === 'set-2') {
|
|
152
|
+
pieceKey = EXTRA_SET_PIECE_MAP[normalizedColor]?.[piece]
|
|
153
|
+
cache = EXTRA_SET_2_CACHE
|
|
154
|
+
} else if (pieceSet === 'set-3') {
|
|
155
|
+
pieceKey = EXTRA_SET_PIECE_MAP[normalizedColor]?.[piece]
|
|
156
|
+
cache = EXTRA_SET_3_CACHE
|
|
157
|
+
} else {
|
|
158
|
+
// Default set
|
|
159
|
+
pieceKey = PIECE_KEY_MAP[normalizedColor]?.[piece]
|
|
160
|
+
cache = PIECE_CACHE
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const markup = pieceKey ? cache[pieceKey] : null
|
|
164
|
+
|
|
165
|
+
if (!markup) {
|
|
166
|
+
console.warn(`ChessPiece: No asset found for ${normalizedColor} ${piece} in set ${pieceSet}`)
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<ChessPieceBase
|
|
172
|
+
size={size}
|
|
173
|
+
className={`text-current ${className}`}
|
|
174
|
+
style={style}
|
|
175
|
+
>
|
|
176
|
+
<span
|
|
177
|
+
className="block h-full w-full"
|
|
178
|
+
style={{
|
|
179
|
+
display: 'inline-flex',
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
justifyContent: 'center'
|
|
182
|
+
}}
|
|
183
|
+
dangerouslySetInnerHTML={{ __html: applySizeToMarkup(markup) }}
|
|
184
|
+
/>
|
|
185
|
+
</ChessPieceBase>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ChessPiece.Base = ChessPieceBase
|
|
190
|
+
|
|
191
|
+
export default ChessPiece
|