選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

command-selector.tsx 3.1KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import type { FC } from 'react'
  2. import { useEffect } from 'react'
  3. import { Command } from 'cmdk'
  4. import { useTranslation } from 'react-i18next'
  5. import type { ActionItem } from './actions/types'
  6. type Props = {
  7. actions: Record<string, ActionItem>
  8. onCommandSelect: (commandKey: string) => void
  9. searchFilter?: string
  10. commandValue?: string
  11. onCommandValueChange?: (value: string) => void
  12. }
  13. const CommandSelector: FC<Props> = ({ actions, onCommandSelect, searchFilter, commandValue, onCommandValueChange }) => {
  14. const { t } = useTranslation()
  15. const filteredActions = Object.values(actions).filter((action) => {
  16. if (!searchFilter)
  17. return true
  18. const filterLower = searchFilter.toLowerCase()
  19. return action.shortcut.toLowerCase().includes(filterLower)
  20. })
  21. useEffect(() => {
  22. if (filteredActions.length > 0 && onCommandValueChange) {
  23. const currentValueExists = filteredActions.some(action => action.shortcut === commandValue)
  24. if (!currentValueExists)
  25. onCommandValueChange(filteredActions[0].shortcut)
  26. }
  27. }, [searchFilter, filteredActions.length])
  28. if (filteredActions.length === 0) {
  29. return (
  30. <div className="p-4">
  31. <div className="flex items-center justify-center py-8 text-center text-text-tertiary">
  32. <div>
  33. <div className="text-sm font-medium text-text-tertiary">
  34. {t('app.gotoAnything.noMatchingCommands')}
  35. </div>
  36. <div className="mt-1 text-xs text-text-quaternary">
  37. {t('app.gotoAnything.tryDifferentSearch')}
  38. </div>
  39. </div>
  40. </div>
  41. </div>
  42. )
  43. }
  44. return (
  45. <div className="p-4">
  46. <div className="mb-3 text-left text-sm font-medium text-text-secondary">
  47. {t('app.gotoAnything.selectSearchType')}
  48. </div>
  49. <Command.Group className="space-y-1">
  50. {filteredActions.map(action => (
  51. <Command.Item
  52. key={action.key}
  53. value={action.shortcut}
  54. className="flex cursor-pointer items-center rounded-md
  55. p-2.5
  56. transition-all
  57. duration-150 hover:bg-state-base-hover aria-[selected=true]:bg-state-base-hover-alt"
  58. onSelect={() => onCommandSelect(action.shortcut)}
  59. >
  60. <span className="min-w-[4.5rem] text-left font-mono text-xs text-text-tertiary">
  61. {action.shortcut}
  62. </span>
  63. <span className="ml-3 text-sm text-text-secondary">
  64. {(() => {
  65. const keyMap: Record<string, string> = {
  66. '/': 'app.gotoAnything.actions.slashDesc',
  67. '@app': 'app.gotoAnything.actions.searchApplicationsDesc',
  68. '@plugin': 'app.gotoAnything.actions.searchPluginsDesc',
  69. '@knowledge': 'app.gotoAnything.actions.searchKnowledgeBasesDesc',
  70. '@node': 'app.gotoAnything.actions.searchWorkflowNodesDesc',
  71. }
  72. return t(keyMap[action.key])
  73. })()}
  74. </span>
  75. </Command.Item>
  76. ))}
  77. </Command.Group>
  78. </div>
  79. )
  80. }
  81. export default CommandSelector