Migration interaction map machinery to XState V5
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { createActorContext, useMachine } from '@xstate/react'
|
||||
import { INTERACTION_MAP_SEPARATOR } from 'lib/constants'
|
||||
import {
|
||||
isModifierKey,
|
||||
@ -30,161 +30,28 @@ type MachineContext<T extends AnyStateMachine> = {
|
||||
send: Prop<InterpreterFrom<T>, 'send'>
|
||||
}
|
||||
|
||||
export const InteractionMapMachineContext = createContext(
|
||||
{} as MachineContext<typeof interactionMapMachine>
|
||||
export const InteractionMapMachineContext = createActorContext(
|
||||
interactionMapMachine
|
||||
)
|
||||
|
||||
export function InteractionMapMachineProvider({
|
||||
export const InteractionMapMachineProvider = ({
|
||||
children,
|
||||
}: React.PropsWithChildren<{}>) {
|
||||
const [state, send] = useMachine(interactionMapMachine, {
|
||||
logger: (msg) => {
|
||||
console.log(msg)
|
||||
},
|
||||
actions: {
|
||||
'Add last interaction to sequence': assign({
|
||||
currentSequence: (context, event) => {
|
||||
const newSequence = event.data
|
||||
? context.currentSequence
|
||||
? context.currentSequence.concat(' ', event.data)
|
||||
: event.data
|
||||
: context.currentSequence
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
return (
|
||||
<InteractionMapMachineContext.Provider>
|
||||
<InteractionMapProviderInner>{children}</InteractionMapProviderInner>
|
||||
</InteractionMapMachineContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
console.log('newSequence', newSequence)
|
||||
return newSequence
|
||||
},
|
||||
}),
|
||||
'Clear sequence': assign({
|
||||
currentSequence: () => {
|
||||
console.log('clearing sequence')
|
||||
return ''
|
||||
},
|
||||
}),
|
||||
'Add to interactionMap': assign({
|
||||
interactionMap: (context, event) => {
|
||||
const newInteractions: Record<string, InteractionMapItem> =
|
||||
Object.fromEntries(
|
||||
Object.entries(event.data.items).map(([name, item]) => [
|
||||
name,
|
||||
{
|
||||
...item,
|
||||
sequence: normalizeSequence(item.sequence),
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
const newInteractionMap = {
|
||||
...context.interactionMap,
|
||||
[event.data.ownerId]: {
|
||||
...context.interactionMap[event.data.ownerId],
|
||||
...newInteractions,
|
||||
},
|
||||
}
|
||||
|
||||
// console.log('newInteractionMap', newInteractionMap)
|
||||
return newInteractionMap
|
||||
},
|
||||
}),
|
||||
'Remove from interactionMap': assign({
|
||||
interactionMap: (context, event) => {
|
||||
const newInteractionMap = { ...context.interactionMap }
|
||||
if (event.data instanceof Array) {
|
||||
event.data.forEach((key) => {
|
||||
const [ownerId, itemName] = key.split(INTERACTION_MAP_SEPARATOR)
|
||||
delete newInteractionMap[ownerId][itemName]
|
||||
})
|
||||
} else {
|
||||
delete newInteractionMap[event.data]
|
||||
}
|
||||
return newInteractionMap
|
||||
},
|
||||
}),
|
||||
'Merge into overrides': assign({
|
||||
overrides: (context, event) => {
|
||||
return {
|
||||
...context.overrides,
|
||||
...event.data,
|
||||
}
|
||||
},
|
||||
}),
|
||||
'Persist keybinding overrides': (context) => {
|
||||
console.log('Persisting keybinding overrides', context.overrides)
|
||||
},
|
||||
},
|
||||
services: {
|
||||
'Resolve hotkey by prefix': (context, event) => {
|
||||
const resolvedInteraction = resolveInteractionEvent(event.data)
|
||||
|
||||
// if the key is already a modifier key, skip everything else and reject
|
||||
if (resolvedInteraction.isModifier) {
|
||||
// We return an empty string so that we don't clear the currentSequence
|
||||
return Promise.reject('')
|
||||
}
|
||||
|
||||
// Find all the sequences that start with the current sequence
|
||||
const searchString =
|
||||
(context.currentSequence ? context.currentSequence + ' ' : '') +
|
||||
resolvedInteraction.asString
|
||||
const sortedInteractions = getSortedInteractionMapSequences(context)
|
||||
|
||||
const matches = sortedInteractions.filter(([sequence]) =>
|
||||
sequence.startsWith(searchString)
|
||||
)
|
||||
|
||||
console.log('matches', {
|
||||
matches,
|
||||
sortedInteractions,
|
||||
searchString,
|
||||
overrides: context.overrides,
|
||||
})
|
||||
|
||||
// If we have no matches, reject the promise
|
||||
if (matches.length === 0) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
const exactMatches = matches.filter(
|
||||
([sequence]) => sequence === searchString
|
||||
)
|
||||
console.log('exactMatches', exactMatches)
|
||||
if (!exactMatches.length) {
|
||||
// We have a prefix match.
|
||||
// Reject the promise and return the step
|
||||
// so we can add it to currentSequence
|
||||
return Promise.reject(resolvedInteraction.asString)
|
||||
}
|
||||
|
||||
// Resolve to just one exact match
|
||||
const availableExactMatches = exactMatches.filter(
|
||||
([_, item]) => !item.guard || item.guard(event.data)
|
||||
)
|
||||
|
||||
console.log('availableExactMatches', availableExactMatches)
|
||||
if (availableExactMatches.length === 0) {
|
||||
return Promise.reject()
|
||||
} else {
|
||||
// return the last-added, available exact match
|
||||
return Promise.resolve(
|
||||
availableExactMatches[availableExactMatches.length - 1][1]
|
||||
)
|
||||
}
|
||||
},
|
||||
'Execute keymap action': async (_context, event) => {
|
||||
try {
|
||||
console.log('Executing action', event.data)
|
||||
event.data.action()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error('There was an error executing the action.')
|
||||
}
|
||||
},
|
||||
},
|
||||
guards: {
|
||||
'There are prefix matches': (_context, event) => {
|
||||
return event.data !== undefined
|
||||
},
|
||||
},
|
||||
})
|
||||
function InteractionMapProviderInner({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const interactionMap = InteractionMapMachineContext.useActorRef()
|
||||
|
||||
// Setting up global event listeners
|
||||
useEffect(() => {
|
||||
@ -204,7 +71,7 @@ export function InteractionMapMachineProvider({
|
||||
'true'
|
||||
)
|
||||
) {
|
||||
send({ type: 'Fire event', data: event })
|
||||
interactionMap.send({ type: 'Fire event', data: { event } })
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,9 +84,5 @@ export function InteractionMapMachineProvider({
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<InteractionMapMachineContext.Provider value={{ state, send }}>
|
||||
{children}
|
||||
</InteractionMapMachineContext.Provider>
|
||||
)
|
||||
return children
|
||||
}
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { InteractionMapMachineContext } from 'components/InteractionMapMachineProvider'
|
||||
import { useContext } from 'react'
|
||||
|
||||
export const useInteractionMapContext = () => {
|
||||
return useContext(InteractionMapMachineContext)
|
||||
const interactionMapActor = InteractionMapMachineContext.useActorRef()
|
||||
const interactionMapState = InteractionMapMachineContext.useSelector((state) => state)
|
||||
return {
|
||||
actor: interactionMapActor,
|
||||
send: interactionMapActor.send,
|
||||
state: interactionMapState,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,8 +69,9 @@ type ResolveKeymapEvent = {
|
||||
asString: string
|
||||
}
|
||||
|
||||
export type InteractionEvent = MouseEvent | KeyboardEvent
|
||||
export function resolveInteractionEvent(
|
||||
event: MouseEvent | KeyboardEvent
|
||||
event: InteractionEvent
|
||||
): ResolveKeymapEvent {
|
||||
// First, determine if this is a key or mouse event
|
||||
const action =
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
import { INTERACTION_MAP_SEPARATOR } from 'lib/constants'
|
||||
import { mapKey, sortKeys } from 'lib/keyboard'
|
||||
import {
|
||||
InteractionEvent,
|
||||
mapKey,
|
||||
resolveInteractionEvent,
|
||||
sortKeys,
|
||||
} from 'lib/keyboard'
|
||||
import { interactionMapCategories } from 'lib/settings/initialKeybindings'
|
||||
import { ContextFrom, createMachine } from 'xstate'
|
||||
import toast from 'react-hot-toast'
|
||||
import { assign, ContextFrom, createMachine, fromPromise, setup } from 'xstate'
|
||||
|
||||
export type MouseButtonName = `${'Left' | 'Middle' | 'Right'}Button`
|
||||
|
||||
@ -25,43 +31,232 @@ export type InteractionMap = {
|
||||
>
|
||||
}
|
||||
|
||||
export const interactionMapMachine = createMachine({
|
||||
export type InteractionMapContext = {
|
||||
interactionMap: InteractionMap
|
||||
overrides: Record<string, string>
|
||||
currentSequence: string
|
||||
}
|
||||
|
||||
export type InteractionMapEvents =
|
||||
| {
|
||||
type: 'Add to interaction map'
|
||||
data: {
|
||||
ownerId: string
|
||||
items: {
|
||||
[key: string]: InteractionMapItem
|
||||
}
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: 'Remove from interaction map'
|
||||
data: string | string[]
|
||||
}
|
||||
| {
|
||||
type: 'Update overrides'
|
||||
data: Record<string, string>
|
||||
}
|
||||
| {
|
||||
type: 'Fire event'
|
||||
data: {
|
||||
event: KeyboardEvent | MouseEvent
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: 'Add last interaction to sequence'
|
||||
}
|
||||
| {
|
||||
type: 'Clear sequence'
|
||||
}
|
||||
| {
|
||||
type: 'xstate.done.actor.resolveHotkeyByPrefix'
|
||||
output: InteractionMapItem
|
||||
}
|
||||
| {
|
||||
type: 'xstate.error.actor.resolveHotkeyByPrefix'
|
||||
data: string | undefined
|
||||
}
|
||||
| {
|
||||
type: 'xstate.done.actor.executeKeymapAction'
|
||||
}
|
||||
| {
|
||||
type: 'xstate.error.actor.executeKeymapAction'
|
||||
}
|
||||
|
||||
export const interactionMapMachine = setup({
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QEkB2AXMAnAhgY3QEsB7VAAgFkcAHMgQQOKwGI6IIz1izCNt8ipMgFsaAbQAMAXUShqxWIUGpZIAB6IAzAE4AbADoATBIAcAdk0AWCQFYJmiRMMAaEAE9EAWgCMN7-t0JbRNdS0tDM29dbQBfGNc0TFwCEnIqWgYuFgAlMGFiADcwMgAzLGJhHj5k5RFxaVV5RWVVDQRow30TU29rbrsrb1cPBB8-AKDtXytg7R0TOITqgVTKGnpGLH0AGUJYTFReKFKmKqSV0mYAMUIsYrAijEkZJBAmpVTWxDmbfTMI7wmEyWYGGbThYZaUJGKw2SxwmwWSJmRYgRL8FJCdIbLL6XKwYgAGyKZAAFsR0ABrMBuZgQUhgfS8ArEan6O4E4lgAASFOpbgAQm4AAp3EqENTPRoKD6kL4IbzeTSaLreMyWXS6RWaXRmOaQhB2Aw2KyGawOEyInWo9E1VbYzJMPFwIkk8lUmnMbDlLbUQk4dAlJjCdkurm8j2CkViiVS17vFqvNreCSArq6Yw6iLaRyGGwGqK-bRzUKGbyGM0mYuGG3LTFpdaOrb413Fd38r1YH36P0BoNYEMc1sR-lC0VgcWS7wvOQyxOgZNOfxWeHRDPzMsGgFdPSObrKsxwywo+Jouu1B2bfQAUTUYDwAFdMGR+aJaA8wBg6QymagWWywDvR9MAAaRpN9MlSONZ2aT4k0QMIzH0YJvGCGYbGCVNdANTQ4X0DVUzsCswW6WJT1tC4GwyK9b3vJ9ilfdYPy-b0nV7QNg30QC6NA8CaEg0hoLeOc4IXBCQQCGxjDVawKz1eEt0tdMHDMboU20M0zBPJZznrNZqKyZgAFVqAgANikKb1CAgOAhITUT1EQbUVRBA9gRsEw1SGdwvHLExkLLCR1yCzVyxPU9UGIGz4FeCi9MvLJpVguV4NGbyRl8fRyx1XCTDBQxNH+MJa10i9GyvXZ9k-I4TiwM4MXnYTkpUVL-iQwwTFwzROvhM1bC3DTVSBXQHFsHVtBsEqGvtcrcRbLkyT5GkktlFqxIVY9fiCzyzXUk1DGwnyEGVfxyxzCxAhsXROu8Ka7SxWanVo4CGL499HnQFbGraY9FJVUJgQ1cJUP+CRLDiOIgA */
|
||||
context: {
|
||||
interactionMap: {} as InteractionMap,
|
||||
overrides: {} as Record<string, string>,
|
||||
currentSequence: '' as string,
|
||||
types: {
|
||||
context: {} as InteractionMapContext,
|
||||
events: {} as InteractionMapEvents,
|
||||
},
|
||||
predictableActionArguments: true,
|
||||
preserveActionOrder: true,
|
||||
tsTypes: {} as import('./interactionMapMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
events: {} as
|
||||
| {
|
||||
type: 'Add to interaction map'
|
||||
data: {
|
||||
ownerId: string
|
||||
items: {
|
||||
[key: string]: InteractionMapItem
|
||||
}
|
||||
}
|
||||
actions: {
|
||||
'Add last interaction to sequence': assign({
|
||||
currentSequence: ({ context, event }) => {
|
||||
if (event.type !== 'xstate.error.actor.resolveHotkeyByPrefix') {
|
||||
return context.currentSequence
|
||||
}
|
||||
| {
|
||||
type: 'Remove from interaction map'
|
||||
data: string | string[]
|
||||
const newSequence = event.data
|
||||
? context.currentSequence
|
||||
? context.currentSequence.concat(' ', event.data)
|
||||
: event.data
|
||||
: context.currentSequence
|
||||
|
||||
console.log('newSequence', newSequence)
|
||||
return newSequence
|
||||
},
|
||||
}),
|
||||
'Clear sequence': assign({
|
||||
currentSequence: () => {
|
||||
console.log('clearing sequence')
|
||||
return ''
|
||||
},
|
||||
}),
|
||||
'Add to interactionMap': assign({
|
||||
interactionMap: ({ context, event }) => {
|
||||
if (event.type !== 'Add to interaction map') {
|
||||
return context.interactionMap
|
||||
}
|
||||
| { type: 'Fire event'; data: MouseEvent | KeyboardEvent }
|
||||
| { type: 'Execute keymap action'; data: InteractionMapItem }
|
||||
| { type: 'Update prefix matrix' }
|
||||
| { type: 'Add last interaction to sequence' }
|
||||
| { type: 'Clear sequence' }
|
||||
| { type: 'Update overrides'; data: { [key: string]: string } }
|
||||
| { type: 'Resolve hotkey by prefix'; data: MouseEvent | KeyboardEvent }
|
||||
| { type: 'done.invoke.resolveHotkeyByPrefix'; data: InteractionMapItem }
|
||||
| {
|
||||
type: 'error.platform.resolveHotkeyByPrefix'
|
||||
data: string | undefined
|
||||
},
|
||||
const newInteractions: Record<string, InteractionMapItem> =
|
||||
Object.fromEntries(
|
||||
Object.entries(event.data.items).map(([name, item]) => [
|
||||
name,
|
||||
{
|
||||
...item,
|
||||
sequence: normalizeSequence(item.sequence),
|
||||
},
|
||||
])
|
||||
)
|
||||
|
||||
const newInteractionMap = {
|
||||
...context.interactionMap,
|
||||
[event.data.ownerId]: {
|
||||
...context.interactionMap[event.data.ownerId],
|
||||
...newInteractions,
|
||||
},
|
||||
}
|
||||
|
||||
// console.log('newInteractionMap', newInteractionMap)
|
||||
return newInteractionMap
|
||||
},
|
||||
}),
|
||||
'Remove from interactionMap': assign({
|
||||
interactionMap: ({ context, event }) => {
|
||||
if (event.type !== 'Remove from interaction map') {
|
||||
return context.interactionMap
|
||||
}
|
||||
const newInteractionMap = { ...context.interactionMap }
|
||||
if (event.data instanceof Array) {
|
||||
event.data.forEach((key) => {
|
||||
const [ownerId, itemName] = key.split(INTERACTION_MAP_SEPARATOR)
|
||||
delete newInteractionMap[ownerId][itemName]
|
||||
})
|
||||
} else {
|
||||
delete newInteractionMap[event.data]
|
||||
}
|
||||
return newInteractionMap
|
||||
},
|
||||
}),
|
||||
'Merge into overrides': assign({
|
||||
overrides: ({ context, event }) => {
|
||||
if (event.type !== 'Update overrides') {
|
||||
return context.overrides
|
||||
}
|
||||
return {
|
||||
...context.overrides,
|
||||
...event.data,
|
||||
}
|
||||
},
|
||||
}),
|
||||
'Persist keybinding overrides': ({context}) => {
|
||||
console.log('Persisting keybinding overrides', context.overrides)
|
||||
},
|
||||
},
|
||||
actors: {
|
||||
resolveHotkeyByPrefix: fromPromise(
|
||||
({
|
||||
input: { context, data },
|
||||
}: {
|
||||
input: { context: InteractionMapContext; data: InteractionEvent }
|
||||
}) => {
|
||||
return new Promise<InteractionMapItem>((resolve, reject) => {
|
||||
const resolvedInteraction = resolveInteractionEvent(data)
|
||||
|
||||
// if the key is already a modifier key, skip everything else and reject
|
||||
if (resolvedInteraction.isModifier) {
|
||||
// We return an empty string so that we don't clear the currentSequence
|
||||
reject('')
|
||||
}
|
||||
|
||||
// Find all the sequences that start with the current sequence
|
||||
const searchString =
|
||||
(context.currentSequence ? context.currentSequence + ' ' : '') +
|
||||
resolvedInteraction.asString
|
||||
const sortedInteractions = getSortedInteractionMapSequences(context)
|
||||
|
||||
const matches = sortedInteractions.filter(([sequence]) =>
|
||||
sequence.startsWith(searchString)
|
||||
)
|
||||
|
||||
console.log('matches', {
|
||||
matches,
|
||||
sortedInteractions,
|
||||
searchString,
|
||||
overrides: context.overrides,
|
||||
})
|
||||
|
||||
// If we have no matches, reject the promise
|
||||
if (matches.length === 0) {
|
||||
reject()
|
||||
}
|
||||
|
||||
const exactMatches = matches.filter(
|
||||
([sequence]) => sequence === searchString
|
||||
)
|
||||
console.log('exactMatches', exactMatches)
|
||||
if (!exactMatches.length) {
|
||||
// We have a prefix match.
|
||||
// Reject the promise and return the step
|
||||
// so we can add it to currentSequence
|
||||
reject(resolvedInteraction.asString)
|
||||
}
|
||||
|
||||
// Resolve to just one exact match
|
||||
const availableExactMatches = exactMatches.filter(
|
||||
([_, item]) => !item.guard || item.guard(data)
|
||||
)
|
||||
|
||||
console.log('availableExactMatches', availableExactMatches)
|
||||
if (availableExactMatches.length === 0) {
|
||||
reject()
|
||||
} else {
|
||||
// return the last-added, available exact match
|
||||
resolve(
|
||||
availableExactMatches[availableExactMatches.length - 1][1]
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
),
|
||||
'Execute keymap action': fromPromise(async ({ input }: { input: InteractionMapItem}) => {
|
||||
try {
|
||||
console.log('Executing action', input)
|
||||
input.action()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error('There was an error executing the action.')
|
||||
}
|
||||
}),
|
||||
},
|
||||
guards: {
|
||||
'There are prefix matches': ({ event }) => {
|
||||
return event.type === 'xstate.error.actor.resolveHotkeyByPrefix' && event.data !== undefined
|
||||
},
|
||||
},
|
||||
}).createMachine({
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QEkB2AXMAnAhgY3QEsB7VAAgFkcAHMgQQOKwGI6IIz1izCNt8ipMgFsaAbQAMAXUShqxWIUGpZIAB6IAHBICsAOgCcANgMBGTUc2nrAFgDMOgDQgAnogBM7g3rtGbAdjMjdx1TIzsDHQBfKOc0TFwCEnIqWgYuFgAlMGFiADcwMgAzLGJhHj5E5RFxaVV5RWVVDQR-TXc9U3d-HR1wmy6zG2c3BFM7Gz0JdyMJCM0Iu08jGLjKgWTKGnpGFgBVaggcTDJ87CxCCDhJGSQQBqVk5sRAzT0bCU0DfzmdOwlzJoRoguqsQPF+EkhKkdhk9AAZQiwTCoXhQYpMCoJDakZgAMUIWEKYAKGBu9QUj1IzwQBgcen83RC-n8NiM7IGwIQvUmZncEmM4T8tjBEKqmxh6SYemysGIABsCmQABbEdAAazALmYEFIYD0vDyxE1eiJcsVYAAEmrNS4AEIuAAKRKKhDU5LuDyadxa1jsdj0Vh6fwiwbCXJM-j07msbXcmh0rJsbNF6yhKW2UqwMrgCqVqo1WuY52l1HlxyKTGEptzFuthftTpdbo9ckp3tALQMEiMhhmOm6wRMXScrkQRlMEgZ9gkn2s3wiplT2PTWzSuxz5vzNqLJezZYrVZrW6tO8bzrArvdplubcaTx9IOmph8yYnbJ02h7pi5bI6iYcRNzH9KwbGXSFqklDcAFE1DAPAAFcTltURaBJMAMB1PUDVQI0TTAODEMwABpLVUPSZJW3udsH07RBkyjAwrG+b4BlCdxhjHbkJj0HRvhMPpIgsOxwPFaFMxgwikMKFDtnQzC9z0A90ErLBqwI+DpNIlxyPTKivVo9R6ICQxmMCVlTHYzjRhsTQoyFfw-G+PivGiMFUGIK54DuMUcQzdcMgpe9qUfBAAFofy4uxrAZX4wiWf1EtEvy11haVEWRDC0QxLAsQgwyDJCujWnpSyegBUxjAWUcbLpHw2gnTQbD6PojDctYV0giS4VlPNCgLW0gqpFRQvGMxA0nSzZiMRy-ncLk5ujdw7HaAVQnGbpktXKC4VgzTkLIuTSXQIaOyMhAAl-VkfBmWc2o+WdTBTGIoiAA */
|
||||
context: {
|
||||
interactionMap: {},
|
||||
overrides: {},
|
||||
currentSequence: '',
|
||||
},
|
||||
id: 'Interaction Map Actor',
|
||||
initial: 'Listening for interaction',
|
||||
@ -78,6 +273,19 @@ export const interactionMapMachine = createMachine({
|
||||
'Resolve hotkey': {
|
||||
invoke: {
|
||||
id: 'resolveHotkeyByPrefix',
|
||||
input: ({ context, event }) => {
|
||||
if (event.type === 'Fire event') {
|
||||
return {
|
||||
context,
|
||||
data: event.data.event,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
context,
|
||||
data: {} as InteractionEvent,
|
||||
}
|
||||
}
|
||||
},
|
||||
onDone: {
|
||||
target: 'Execute keymap event',
|
||||
},
|
||||
@ -87,7 +295,7 @@ export const interactionMapMachine = createMachine({
|
||||
actions: {
|
||||
type: 'Add last interaction to sequence',
|
||||
},
|
||||
cond: {
|
||||
guard: {
|
||||
type: 'There are prefix matches',
|
||||
},
|
||||
},
|
||||
@ -98,7 +306,7 @@ export const interactionMapMachine = createMachine({
|
||||
},
|
||||
},
|
||||
],
|
||||
src: 'Resolve hotkey by prefix',
|
||||
src: 'resolveHotkeyByPrefix',
|
||||
},
|
||||
},
|
||||
'Execute keymap event': {
|
||||
@ -107,6 +315,12 @@ export const interactionMapMachine = createMachine({
|
||||
},
|
||||
invoke: {
|
||||
id: 'executeKeymapAction',
|
||||
input: ({ event }) => {
|
||||
if (event.type !== 'xstate.done.actor.resolveHotkeyByPrefix') {
|
||||
return {} as InteractionMapItem
|
||||
}
|
||||
return event.output
|
||||
},
|
||||
onDone: {
|
||||
target: 'Listening for interaction',
|
||||
},
|
||||
@ -129,7 +343,7 @@ export const interactionMapMachine = createMachine({
|
||||
|
||||
'Remove from interaction map': {
|
||||
target: '#Interaction Map Actor',
|
||||
internal: true,
|
||||
reenter: false,
|
||||
actions: [
|
||||
{
|
||||
type: 'Remove from interactionMap',
|
||||
@ -139,7 +353,7 @@ export const interactionMapMachine = createMachine({
|
||||
|
||||
'Update overrides': {
|
||||
target: '#Interaction Map Actor',
|
||||
internal: true,
|
||||
reenter: false,
|
||||
actions: ['Merge into overrides', 'Persist keybinding overrides'],
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user