-
Notifications
You must be signed in to change notification settings - Fork 857
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a4c0ab7
commit f69ec0a
Showing
11 changed files
with
438 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
"use client" | ||
|
||
import { Home, Languages, Moon, Sun, SunMoon } from "lucide-react" | ||
|
||
import { useServerData } from "@/app/context/server-data-context" | ||
import { | ||
CommandDialog, | ||
CommandEmpty, | ||
CommandGroup, | ||
CommandInput, | ||
CommandItem, | ||
CommandList, | ||
CommandSeparator, | ||
} from "@/components/ui/command" | ||
import { localeItems } from "@/i18n-metadata" | ||
import { setUserLocale } from "@/i18n/locale" | ||
import { useTranslations } from "next-intl" | ||
import { useTheme } from "next-themes" | ||
import { useRouter } from "next/navigation" | ||
import { useEffect, useState } from "react" | ||
|
||
export function DashCommand() { | ||
const [open, setOpen] = useState(false) | ||
const [search, setSearch] = useState("") | ||
const { data } = useServerData() | ||
const router = useRouter() | ||
const { setTheme } = useTheme() | ||
const t = useTranslations("DashCommand") | ||
|
||
useEffect(() => { | ||
const down = (e: KeyboardEvent) => { | ||
if (e.key === "k" && (e.metaKey || e.ctrlKey)) { | ||
e.preventDefault() | ||
setOpen((open) => !open) | ||
} | ||
} | ||
|
||
document.addEventListener("keydown", down) | ||
return () => document.removeEventListener("keydown", down) | ||
}, []) | ||
|
||
if (!data?.result) return null | ||
|
||
const sortedServers = data.result.sort((a, b) => { | ||
const displayIndexDiff = (b.display_index || 0) - (a.display_index || 0) | ||
if (displayIndexDiff !== 0) return displayIndexDiff | ||
return a.id - b.id | ||
}) | ||
|
||
// 添加语言切换快捷方式 | ||
const languageShortcuts = localeItems.map((item) => ({ | ||
keywords: ["language", "locale", item.code.toLowerCase()], | ||
icon: <Languages />, | ||
label: item.name, | ||
action: () => setUserLocale(item.code), | ||
value: `language ${item.name.toLowerCase()} ${item.code}`, | ||
})) | ||
|
||
// 将语言快捷方式添加到现有的shortcuts数组中 | ||
const shortcuts = [ | ||
{ | ||
keywords: ["home", "homepage"], | ||
icon: <Home />, | ||
label: t("Home"), | ||
action: () => router.push("/"), | ||
}, | ||
{ | ||
keywords: ["light", "theme", "lightmode"], | ||
icon: <Sun />, | ||
label: t("ToggleLightMode"), | ||
action: () => setTheme("light"), | ||
}, | ||
{ | ||
keywords: ["dark", "theme", "darkmode"], | ||
icon: <Moon />, | ||
label: t("ToggleDarkMode"), | ||
action: () => setTheme("dark"), | ||
}, | ||
{ | ||
keywords: ["system", "theme", "systemmode"], | ||
icon: <SunMoon />, | ||
label: t("ToggleSystemMode"), | ||
action: () => setTheme("system"), | ||
}, | ||
...languageShortcuts, | ||
].map((item) => ({ | ||
...item, | ||
value: `${item.keywords.join(" ")} ${item.label}`, | ||
})) | ||
|
||
return ( | ||
<> | ||
<CommandDialog open={open} onOpenChange={setOpen}> | ||
<CommandInput placeholder={t("TypeCommand")} value={search} onValueChange={setSearch} /> | ||
<CommandList> | ||
<CommandEmpty>{t("NoResults")}</CommandEmpty> | ||
<CommandGroup heading={t("Servers")}> | ||
{sortedServers.map((server) => ( | ||
<CommandItem | ||
key={server.id} | ||
value={server.name} | ||
onSelect={() => { | ||
router.push(`/server/${server.id}`) | ||
setOpen(false) | ||
}} | ||
> | ||
{server.online_status ? ( | ||
<span className="h-2 w-2 shrink-0 rounded-full bg-green-500 self-center" /> | ||
) : ( | ||
<span className="h-2 w-2 shrink-0 rounded-full bg-red-500 self-center" /> | ||
)} | ||
<span>{server.name}</span> | ||
</CommandItem> | ||
))} | ||
</CommandGroup> | ||
<CommandSeparator /> | ||
|
||
<CommandGroup heading={t("Shortcuts")}> | ||
{shortcuts.map((item) => ( | ||
<CommandItem | ||
key={item.label} | ||
value={item.value} | ||
onSelect={() => { | ||
item.action() | ||
setOpen(false) | ||
}} | ||
> | ||
{item.icon} | ||
<span>{item.label}</span> | ||
</CommandItem> | ||
))} | ||
</CommandGroup> | ||
</CommandList> | ||
</CommandDialog> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
"use client" | ||
|
||
import { type DialogProps, DialogTitle } from "@radix-ui/react-dialog" | ||
import { Command as CommandPrimitive } from "cmdk" | ||
import { Search } from "lucide-react" | ||
import * as React from "react" | ||
|
||
import { Dialog, DialogContent } from "@/components/ui/dialog" | ||
import { cn } from "@/lib/utils" | ||
|
||
const Command = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive> | ||
>(({ className, ...props }, ref) => ( | ||
<CommandPrimitive | ||
ref={ref} | ||
className={cn( | ||
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
Command.displayName = CommandPrimitive.displayName | ||
|
||
const CommandDialog = ({ children, ...props }: DialogProps) => { | ||
return ( | ||
<Dialog {...props}> | ||
<DialogTitle /> | ||
<DialogContent className="overflow-hidden p-0 shadow-lg"> | ||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-4 [&_[cmdk-input-wrapper]_svg]:w-4 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-4 [&_[cmdk-item]_svg]:w-4"> | ||
{children} | ||
</Command> | ||
</DialogContent> | ||
</Dialog> | ||
) | ||
} | ||
|
||
const CommandInput = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.Input>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> | ||
>(({ className, ...props }, ref) => ( | ||
<div | ||
className="flex items-center border-b bg-stone-100 dark:bg-stone-900 px-3" | ||
cmdk-input-wrapper="" | ||
> | ||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> | ||
<CommandPrimitive.Input | ||
ref={ref} | ||
className={cn( | ||
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
</div> | ||
)) | ||
|
||
CommandInput.displayName = CommandPrimitive.Input.displayName | ||
|
||
const CommandList = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.List>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> | ||
>(({ className, ...props }, ref) => ( | ||
<CommandPrimitive.List | ||
ref={ref} | ||
className={cn("max-h-[300px] mb-1 overflow-y-auto overflow-x-hidden", className)} | ||
{...props} | ||
/> | ||
)) | ||
|
||
CommandList.displayName = CommandPrimitive.List.displayName | ||
|
||
const CommandEmpty = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.Empty>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> | ||
>((props, ref) => ( | ||
<CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} /> | ||
)) | ||
|
||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName | ||
|
||
const CommandGroup = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.Group>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> | ||
>(({ className, ...props }, ref) => ( | ||
<CommandPrimitive.Group | ||
ref={ref} | ||
className={cn( | ||
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
|
||
CommandGroup.displayName = CommandPrimitive.Group.displayName | ||
|
||
const CommandSeparator = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.Separator>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> | ||
>(({ className, ...props }, ref) => ( | ||
<CommandPrimitive.Separator | ||
ref={ref} | ||
className={cn("-mx-1 h-px bg-border", className)} | ||
{...props} | ||
/> | ||
)) | ||
CommandSeparator.displayName = CommandPrimitive.Separator.displayName | ||
|
||
const CommandItem = React.forwardRef< | ||
React.ElementRef<typeof CommandPrimitive.Item>, | ||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> | ||
>(({ className, ...props }, ref) => ( | ||
<CommandPrimitive.Item | ||
ref={ref} | ||
className={cn( | ||
"relative flex cursor-default gap-2 select-none items-center rounded-[8px] px-2 py-1.5 text-xs outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-stone-100 dark:data-[selected='true']:bg-stone-900 data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
|
||
CommandItem.displayName = CommandPrimitive.Item.displayName | ||
|
||
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => { | ||
return ( | ||
<span | ||
className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)} | ||
{...props} | ||
/> | ||
) | ||
} | ||
CommandShortcut.displayName = "CommandShortcut" | ||
|
||
export { | ||
Command, | ||
CommandDialog, | ||
CommandInput, | ||
CommandList, | ||
CommandEmpty, | ||
CommandGroup, | ||
CommandItem, | ||
CommandShortcut, | ||
CommandSeparator, | ||
} |
Oops, something went wrong.