command-menu.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import React from 'react'
  2. import { useLocation, useNavigate } from '@tanstack/react-router'
  3. import { ArrowRight, ChevronRight, Laptop, Moon, Sun } from 'lucide-react'
  4. import { useTranslation } from 'react-i18next'
  5. import { useSearch } from '@/context/search-provider'
  6. import { useTheme } from '@/context/theme-provider'
  7. import { useSidebarData } from '@/hooks/use-sidebar-data'
  8. import {
  9. CommandDialog,
  10. CommandEmpty,
  11. CommandGroup,
  12. CommandInput,
  13. CommandItem,
  14. CommandList,
  15. CommandSeparator,
  16. } from '@/components/ui/command'
  17. import { getNavGroupsForPath } from './layout/lib/workspace-registry'
  18. import { ScrollArea } from './ui/scroll-area'
  19. export function CommandMenu() {
  20. const { t } = useTranslation()
  21. const navigate = useNavigate()
  22. const { setTheme } = useTheme()
  23. const { open, setOpen } = useSearch()
  24. const { pathname } = useLocation()
  25. const sidebarData = useSidebarData()
  26. // 根据当前路径从工作区注册表获取对应的侧边栏配置
  27. const navGroups = getNavGroupsForPath(pathname, t) || sidebarData.navGroups
  28. const runCommand = React.useCallback(
  29. (command: () => unknown) => {
  30. setOpen(false)
  31. command()
  32. },
  33. [setOpen]
  34. )
  35. return (
  36. <CommandDialog modal open={open} onOpenChange={setOpen}>
  37. <CommandInput placeholder={t('Type a command or search...')} />
  38. <CommandList>
  39. <ScrollArea type='hover' className='h-72 pe-1'>
  40. <CommandEmpty>{t('No results found.')}</CommandEmpty>
  41. {navGroups.map((group) => (
  42. <CommandGroup key={group.id || group.title} heading={group.title}>
  43. {group.items.map((navItem, i) => {
  44. if (navItem.url)
  45. return (
  46. <CommandItem
  47. key={`${navItem.url}-${i}`}
  48. value={navItem.title}
  49. onSelect={() => {
  50. runCommand(() => navigate({ to: navItem.url }))
  51. }}
  52. >
  53. <div className='flex size-4 items-center justify-center'>
  54. <ArrowRight className='text-muted-foreground/80 size-2' />
  55. </div>
  56. {navItem.title}
  57. </CommandItem>
  58. )
  59. return navItem.items?.map((subItem, i) => (
  60. <CommandItem
  61. key={`${navItem.title}-${subItem.url}-${i}`}
  62. value={`${navItem.title}-${subItem.url}`}
  63. onSelect={() => {
  64. runCommand(() => navigate({ to: subItem.url }))
  65. }}
  66. >
  67. <div className='flex size-4 items-center justify-center'>
  68. <ArrowRight className='text-muted-foreground/80 size-2' />
  69. </div>
  70. {navItem.title} <ChevronRight /> {subItem.title}
  71. </CommandItem>
  72. ))
  73. })}
  74. </CommandGroup>
  75. ))}
  76. <CommandSeparator />
  77. <CommandGroup heading='Theme'>
  78. <CommandItem onSelect={() => runCommand(() => setTheme('light'))}>
  79. <Sun /> <span>{t('Light')}</span>
  80. </CommandItem>
  81. <CommandItem onSelect={() => runCommand(() => setTheme('dark'))}>
  82. <Moon className='scale-90' />
  83. <span>{t('Dark')}</span>
  84. </CommandItem>
  85. <CommandItem onSelect={() => runCommand(() => setTheme('system'))}>
  86. <Laptop />
  87. <span>{t('System')}</span>
  88. </CommandItem>
  89. </CommandGroup>
  90. </ScrollArea>
  91. </CommandList>
  92. </CommandDialog>
  93. )
  94. }