💄 update command bar UI

This commit is contained in:
Fred KISSIE
2026-06-16 19:28:22 +02:00
parent 3bc5ab2136
commit c0a9db92ef
2 changed files with 77 additions and 46 deletions
@@ -1,5 +1,9 @@
"use client"; "use client";
import type {
CommandBarNavSection,
SidebarNavSection
} from "@app/app/navigation";
import { import {
CommandDialog, CommandDialog,
CommandEmpty, CommandEmpty,
@@ -9,13 +13,9 @@ import {
CommandList, CommandList,
CommandSeparator CommandSeparator
} from "@app/components/ui/command"; } from "@app/components/ui/command";
import type { import { cn } from "@app/lib/cn";
CommandBarNavSection,
SidebarNavSection
} from "@app/app/navigation";
import { Badge } from "@app/components/ui/badge";
import { ListUserOrgsResponse } from "@server/routers/org"; import { ListUserOrgsResponse } from "@server/routers/org";
import { Loader2 } from "lucide-react"; import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React, { import React, {
createContext, createContext,
@@ -25,13 +25,10 @@ import React, {
useMemo, useMemo,
useState useState
} from "react"; } from "react";
import { useTranslations } from "next-intl";
import { useCommandPaletteActions } from "./useCommandPaletteActions"; import { useCommandPaletteActions } from "./useCommandPaletteActions";
import { useCommandPaletteNavigation } from "./useCommandPaletteNavigation"; import { useCommandPaletteNavigation } from "./useCommandPaletteNavigation";
import { useCommandPaletteOrganizations } from "./useCommandPaletteOrganizations";
import { useCommandPaletteSearch } from "./useCommandPaletteSearch"; import { useCommandPaletteSearch } from "./useCommandPaletteSearch";
import { useUserContext } from "@app/hooks/useUserContext"; import { search } from "jmespath";
import { cn } from "@app/lib/cn";
type CommandPaletteProps = { type CommandPaletteProps = {
orgId?: string; orgId?: string;
@@ -52,14 +49,16 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
const { open, setOpen } = useCommandPalette(); const { open, setOpen } = useCommandPalette();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const isActionMode = search.startsWith(">");
const navigationGroups = useCommandPaletteNavigation(navItems); const navigationGroups = useCommandPaletteNavigation(navItems);
// const organizations = useCommandPaletteOrganizations(orgs); // const organizations = useCommandPaletteOrganizations(orgs);
const actions = useCommandPaletteActions(orgId, orgs); const actions = useCommandPaletteActions(orgId, orgs);
const { shouldSearch, sites, resources, users, machineClients, isLoading } = const { shouldSearch, sites, resources, users, machineClients, isLoading } =
useCommandPaletteSearch({ useCommandPaletteSearch({
orgId, orgId,
query: search, query: search.substring(1),
enabled: open enabled: open && isActionMode
}); });
const handleOpenChange = useCallback( const handleOpenChange = useCallback(
@@ -96,9 +95,24 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
className="max-w-2xl **:data-[slot=command-input-wrapper]:h-15" className="max-w-2xl **:data-[slot=command-input-wrapper]:h-15"
commandProps={{ commandProps={{
loop: true, loop: true,
filter(value, search) { filter(value, query) {
if (value.toLowerCase().includes(search.toLowerCase())) let search = query;
if (query.startsWith(">")) {
search = query.substring(1);
console.log({
search,
value
});
}
if (
value
.toLowerCase()
.includes(search.trim().toLowerCase())
) {
return 1; return 1;
}
return 0; return 0;
} }
}} }}
@@ -107,8 +121,9 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
placeholder={t("commandPaletteSearchPlaceholder")} placeholder={t("commandPaletteSearchPlaceholder")}
value={search} value={search}
onValueChange={setSearch} onValueChange={setSearch}
// isLoading
/> />
<CommandList className="max-h-118 min-h-0 h-auto scroll-pb-2.5 scroll-pt-2"> <CommandList className="max-h-118 min-h-0 h-(--cmdk-list-height) scroll-pb-2.5 scroll-pt-2 transition-[height] duration-250 ease-in-out">
<CommandEmpty>{t("commandPaletteNoResults")}</CommandEmpty> <CommandEmpty>{t("commandPaletteNoResults")}</CommandEmpty>
<CommandGroup <CommandGroup
@@ -116,33 +131,42 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
className="[&_[cmdk-group-heading]]:text-sm" className="[&_[cmdk-group-heading]]:text-sm"
/> />
{navigationGroups.map((group, groupIndex) => ( {!isActionMode &&
<React.Fragment key={group.heading}> navigationGroups.map((group, groupIndex) => {
{groupIndex > 0 && <CommandSeparator />} console.log({
<CommandGroup groupIndex,
heading={group.heading} groupHeading: group.heading
className={cn( });
"[&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-sm pb-2.5", return (
groupIndex > 0 && <React.Fragment key={group.heading}>
"[&_[cmdk-group-heading]]:pt-3" {groupIndex > 0 && <CommandSeparator />}
)} <CommandGroup
> heading={group.heading}
{group.items.map((item, itemIndex) => ( className={cn(
<CommandItem "[&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-sm pb-2.5",
key={item.id} groupIndex > 0 &&
value={`${item.title} ${group.heading}`} "[&_[cmdk-group-heading]]:pt-3"
onSelect={() => )}
runCommand(() => router.push(item.href))
}
className="h-9"
> >
{item.icon} {group.items.map((item) => (
<span>{item.title}</span> <CommandItem
</CommandItem> key={item.id}
))} value={`${item.title} ${group.heading}`}
</CommandGroup> onSelect={() =>
</React.Fragment> runCommand(() =>
))} router.push(item.href)
)
}
className="h-9"
>
{item.icon}
<span>{item.title}</span>
</CommandItem>
))}
</CommandGroup>
</React.Fragment>
);
})}
{/* {organizations.length > 1 && ( {/* {organizations.length > 1 && (
<> <>
@@ -282,7 +306,7 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
</> </>
)} */} )} */}
{/* {actions.length > 0 && ( {isActionMode && actions.length > 0 && (
<> <>
<CommandSeparator /> <CommandSeparator />
<CommandGroup <CommandGroup
@@ -309,7 +333,7 @@ export function CommandPalette({ orgId, orgs, navItems }: CommandPaletteProps) {
))} ))}
</CommandGroup> </CommandGroup>
</> </>
)} */} )}
</CommandList> </CommandList>
</CommandDialog> </CommandDialog>
); );
+10 -3
View File
@@ -2,7 +2,7 @@
import * as React from "react"; import * as React from "react";
import { Command as CommandPrimitive } from "cmdk"; import { Command as CommandPrimitive } from "cmdk";
import { SearchIcon } from "lucide-react"; import { LoaderIcon, SearchIcon } from "lucide-react";
import { import {
Dialog, Dialog,
@@ -68,14 +68,21 @@ function CommandDialog({
function CommandInput({ function CommandInput({
className, className,
isLoading,
...props ...props
}: React.ComponentProps<typeof CommandPrimitive.Input>) { }: React.ComponentProps<typeof CommandPrimitive.Input> & {
isLoading?: boolean;
}) {
return ( return (
<div <div
data-slot="command-input-wrapper" data-slot="command-input-wrapper"
className="flex h-9 items-center gap-2 border-b px-3" className="flex h-9 items-center gap-2 border-b px-3"
> >
<SearchIcon className="size-4 shrink-0 opacity-50" /> {isLoading ? (
<LoaderIcon className="animate-spin size-4 shrink-0 opacity-50" />
) : (
<SearchIcon className="size-4 shrink-0 opacity-50" />
)}
<CommandPrimitive.Input <CommandPrimitive.Input
data-slot="command-input" data-slot="command-input"
className={cn( className={cn(