import { CustomIcon } from 'components/CustomIcon'
import decamelize from 'decamelize'
import { useInteractionMapContext } from 'hooks/useInteractionMapContext'
import { resolveInteractionEvent } from 'lib/keyboard'
import {
InteractionMapItem,
makeOverrideKey,
} from 'machines/interactionMapMachine'
import { FormEvent, HTMLProps, useEffect, useRef, useState } from 'react'
export function AllKeybindingsFields() {
const { state } = useInteractionMapContext()
return (
{Object.entries(state.context.interactionMap).map(
([category, categoryItems]) => (
)
)}
)
}
function KeybindingSection({
category,
items,
...props
}: HTMLProps & {
category: string
items: Record
}) {
return (
{decamelize(category, { separator: ' ' })}
{Object.entries(items).map(([_, item]) => (
))}
)
}
function KeybindingField({ item }: { item: InteractionMapItem }) {
const { send, state } = useInteractionMapContext()
const [isEditing, setIsEditing] = useState(false)
const [newSequence, setNewSequence] = useState('')
const submitRef = useRef(null)
function handleSubmit(e: FormEvent) {
e.preventDefault()
if (newSequence !== item.sequence) {
send({
type: 'Update overrides',
data: {
[makeOverrideKey(item)]: newSequence,
},
})
}
setIsEditing(false)
}
useEffect(() => {
const blockOtherEvents = (e: KeyboardEvent | MouseEvent) => {
e.preventDefault()
e.stopPropagation()
e.stopImmediatePropagation()
}
const handleInteraction = (e: KeyboardEvent | MouseEvent) => {
if (e instanceof KeyboardEvent && e.key === 'Escape') {
blockOtherEvents(e)
setIsEditing(false)
return
} else if (e instanceof KeyboardEvent && e.key === 'Enter') {
return
} else if (e instanceof MouseEvent && e.target === submitRef.current) {
return
}
blockOtherEvents(e)
const resolvedInteraction = resolveInteractionEvent(e)
if (resolvedInteraction.isModifier) return
setNewSequence((prev) => {
const newSequence =
prev + (prev.length ? ' ' : '') + resolvedInteraction.asString
console.log('newSequence', newSequence)
return newSequence
})
}
const handleContextMenu = (e: MouseEvent) => {
blockOtherEvents(e)
}
if (!isEditing) {
setNewSequence('')
globalThis?.window?.removeEventListener('keydown', handleInteraction, {
capture: true,
})
globalThis?.window?.removeEventListener('mousedown', handleInteraction, {
capture: true,
})
globalThis?.window?.removeEventListener(
'contextmenu',
handleContextMenu,
{ capture: true }
)
} else {
globalThis?.window?.addEventListener('keydown', handleInteraction, {
capture: true,
})
globalThis?.window?.addEventListener('mousedown', handleInteraction, {
capture: true,
})
globalThis?.window?.addEventListener('contextmenu', handleContextMenu, {
capture: true,
})
}
return () => {
globalThis?.window?.removeEventListener('keydown', handleInteraction, {
capture: true,
})
globalThis?.window?.removeEventListener('mousedown', handleInteraction, {
capture: true,
})
globalThis?.window?.removeEventListener(
'contextmenu',
handleContextMenu,
{ capture: true }
)
}
}, [isEditing, setNewSequence])
return isEditing ? (
) : (
{item.title}
)
}
export function InteractionSequence({
sequence,
className = '',
showNoSequence = false,
...props
}: HTMLProps & { sequence: string; showNoSequence?: boolean }) {
return sequence.length ? (
{sequence.split(' ').map((chord, i) => (
{chord}
))}
) : (
showNoSequence && (
No sequence set
)
)
}