Make commands disable, not unregister, based on their machineActor (#5070)

* Make "Find and select command" global to commandBarMachine

* Make commands not removed based on their actor state, only disabled

* Sort commands better in CommandComboBox

* Break out sort logic, add a few unit tests

* Fix missed name change

* Needed to make one more change from source branch:
since `optionsFromContext` now only gets fired once, I/O-based options need to use the `options` config instead.

---------

Co-authored-by: 49fl <ircsurfer33@gmail.com>
This commit is contained in:
Frank Noirot
2025-01-16 12:08:48 -05:00
committed by GitHub
parent 0a1a6e50cf
commit 3f855d7bad
9 changed files with 149 additions and 36 deletions

View File

@ -4,6 +4,8 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
import { Command } from 'lib/commandTypes'
import { useEffect, useState } from 'react'
import { CustomIcon } from './CustomIcon'
import { getActorNextEvents } from 'lib/utils'
import { sortCommands } from 'lib/commandUtils'
function CommandComboBox({
options,
@ -18,8 +20,16 @@ function CommandComboBox({
const defaultOption =
options.find((o) => 'isCurrent' in o && o.isCurrent) || null
// sort disabled commands to the bottom
const sortedOptions = options
.map((command) => ({
command,
disabled: optionIsDisabled(command),
}))
.sort(sortCommands)
.map(({ command }) => command)
const fuse = new Fuse(options, {
const fuse = new Fuse(sortedOptions, {
keys: ['displayName', 'name', 'description'],
threshold: 0.3,
ignoreLocation: true,
@ -27,7 +37,7 @@ function CommandComboBox({
useEffect(() => {
const results = fuse.search(query).map((result) => result.item)
setFilteredOptions(query.length > 0 ? results : options)
setFilteredOptions(query.length > 0 ? results : sortedOptions)
}, [query])
function handleSelection(command: Command) {
@ -73,7 +83,8 @@ function CommandComboBox({
<Combobox.Option
key={option.groupId + option.name + (option.displayName || '')}
value={option}
className="flex items-center gap-4 px-4 py-1.5 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
className="flex items-center gap-4 px-4 py-1.5 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90 ui-disabled:!text-chalkboard-50"
disabled={optionIsDisabled(option)}
>
{'icon' in option && option.icon && (
<CustomIcon name={option.icon} className="w-5 h-5" />
@ -96,3 +107,11 @@ function CommandComboBox({
}
export default CommandComboBox
function optionIsDisabled(option: Command): boolean {
return (
'machineActor' in option &&
option.machineActor !== undefined &&
!getActorNextEvents(option.machineActor.getSnapshot()).includes(option.name)
)
}