103 lines
2.5 KiB
TypeScript
103 lines
2.5 KiB
TypeScript
import { memo, type CSSProperties } from 'react'
|
||
import {
|
||
bindingLabel,
|
||
compactBindingLabel,
|
||
type ControllerIconStyle,
|
||
} from '../input'
|
||
|
||
const FACE_BUTTONS: Record<ControllerIconStyle, Partial<Record<number, { color: string; label: string }>>> = {
|
||
xbox: {
|
||
0: { color: '#107c10', label: 'A' },
|
||
1: { color: '#d13438', label: 'B' },
|
||
2: { color: '#0078d4', label: 'X' },
|
||
3: { color: '#ffb900', label: 'Y' },
|
||
},
|
||
playstation: {
|
||
0: { color: '#0070d1', label: '×' },
|
||
1: { color: '#df0024', label: '○' },
|
||
2: { color: '#f27ab8', label: '□' },
|
||
3: { color: '#00a35a', label: '△' },
|
||
},
|
||
nintendo: {
|
||
0: { color: '#e60012', label: 'B' },
|
||
1: { color: '#e60012', label: 'A' },
|
||
2: { color: '#e60012', label: 'Y' },
|
||
3: { color: '#e60012', label: 'X' },
|
||
},
|
||
}
|
||
|
||
function faceButtonFor(binding: string, iconStyle: ControllerIconStyle) {
|
||
if (!binding.startsWith('Button')) return null
|
||
return FACE_BUTTONS[iconStyle][Number(binding.slice(6))] ?? null
|
||
}
|
||
|
||
const FaceIcon = memo(function FaceIcon({
|
||
color,
|
||
iconStyle,
|
||
label,
|
||
title,
|
||
}: {
|
||
color: string
|
||
iconStyle: ControllerIconStyle
|
||
label: string
|
||
title: string
|
||
}) {
|
||
return (
|
||
<span
|
||
aria-label={title}
|
||
className={`controller-face-icon controller-face-${iconStyle}`}
|
||
role="img"
|
||
style={{ '--button-color': color } as CSSProperties}
|
||
>
|
||
{label}
|
||
</span>
|
||
)
|
||
})
|
||
|
||
export const ControllerBindingLabel = memo(function ControllerBindingLabel({
|
||
binding,
|
||
compact = false,
|
||
iconStyle,
|
||
}: {
|
||
binding: string
|
||
compact?: boolean
|
||
iconStyle: ControllerIconStyle
|
||
}) {
|
||
const faceButton = faceButtonFor(binding, iconStyle)
|
||
const title = bindingLabel(binding, iconStyle)
|
||
|
||
if (faceButton) {
|
||
return (
|
||
<FaceIcon
|
||
color={faceButton.color}
|
||
iconStyle={iconStyle}
|
||
label={faceButton.label}
|
||
title={title}
|
||
/>
|
||
)
|
||
}
|
||
|
||
return <>{compact ? compactBindingLabel(binding, iconStyle) : title}</>
|
||
})
|
||
|
||
export const ControllerStylePreview = memo(function ControllerStylePreview({ iconStyle }: { iconStyle: ControllerIconStyle }) {
|
||
return (
|
||
<span className="controller-style-preview" aria-hidden="true">
|
||
{[0, 1, 2, 3].map((button) => {
|
||
const faceButton = FACE_BUTTONS[iconStyle][button]
|
||
if (!faceButton) return null
|
||
|
||
return (
|
||
<FaceIcon
|
||
color={faceButton.color}
|
||
iconStyle={iconStyle}
|
||
key={button}
|
||
label={faceButton.label}
|
||
title=""
|
||
/>
|
||
)
|
||
})}
|
||
</span>
|
||
)
|
||
})
|