mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(tool-input): search fixed
This commit is contained in:
+1
-1
@@ -347,7 +347,7 @@ export function CustomToolModal({
|
||||
<DialogHeader className="px-6 py-4 border-b">
|
||||
<div className="flex items-center justify-between">
|
||||
<DialogTitle className="text-lg font-medium">
|
||||
{isEditing ? 'Edit AgentTool' : 'Create Agent Tool'}
|
||||
{isEditing ? 'Edit Agent Tool' : 'Create Agent Tool'}
|
||||
</DialogTitle>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 p-0" onClick={handleClose}>
|
||||
<X className="h-4 w-4" />
|
||||
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
import React, {
|
||||
ReactNode,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { Search } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// Context for the command component
|
||||
type CommandContextType = {
|
||||
searchQuery: string
|
||||
setSearchQuery: (value: string) => void
|
||||
activeIndex: number
|
||||
setActiveIndex: (index: number) => void
|
||||
filteredItems: string[]
|
||||
registerItem: (id: string) => void
|
||||
unregisterItem: (id: string) => void
|
||||
selectItem: (id: string) => void
|
||||
}
|
||||
|
||||
const CommandContext = createContext<CommandContextType | undefined>(undefined)
|
||||
|
||||
// Hook to use the command context
|
||||
const useCommandContext = () => {
|
||||
const context = useContext(CommandContext)
|
||||
if (!context) {
|
||||
throw new Error('Command components must be used within a CommandProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
// Types for the components
|
||||
interface CommandProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
filter?: (value: string, search: string) => number
|
||||
}
|
||||
|
||||
interface CommandInputProps {
|
||||
placeholder?: string
|
||||
className?: string
|
||||
onValueChange?: (value: string) => void
|
||||
}
|
||||
|
||||
interface CommandListProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface CommandEmptyProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
interface CommandGroupProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
heading?: string
|
||||
}
|
||||
|
||||
interface CommandItemProps {
|
||||
children: ReactNode
|
||||
className?: string
|
||||
value: string
|
||||
onSelect?: () => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface CommandSeparatorProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
// Main Command component
|
||||
export function Command({ children, className, filter }: CommandProps) {
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [activeIndex, setActiveIndex] = useState(-1)
|
||||
const [items, setItems] = useState<string[]>([])
|
||||
const [filteredItems, setFilteredItems] = useState<string[]>([])
|
||||
|
||||
// Register and unregister items - memoize to prevent infinite loops
|
||||
const registerItem = useCallback((id: string) => {
|
||||
setItems((prev) => {
|
||||
// Only add if not already in the array
|
||||
if (prev.includes(id)) return prev
|
||||
return [...prev, id]
|
||||
})
|
||||
}, [])
|
||||
|
||||
const unregisterItem = useCallback((id: string) => {
|
||||
setItems((prev) => prev.filter((item) => item !== id))
|
||||
}, [])
|
||||
|
||||
// Handle item selection
|
||||
const selectItem = useCallback(
|
||||
(id: string) => {
|
||||
const index = filteredItems.indexOf(id)
|
||||
if (index >= 0) {
|
||||
setActiveIndex(index)
|
||||
}
|
||||
},
|
||||
[filteredItems]
|
||||
)
|
||||
|
||||
// Filter items based on search query
|
||||
useEffect(() => {
|
||||
if (!searchQuery) {
|
||||
setFilteredItems(items)
|
||||
return
|
||||
}
|
||||
|
||||
const filtered = items
|
||||
.map((item) => {
|
||||
const score = filter ? filter(item, searchQuery) : defaultFilter(item, searchQuery)
|
||||
return { item, score }
|
||||
})
|
||||
.filter((item) => item.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.map((item) => item.item)
|
||||
|
||||
setFilteredItems(filtered)
|
||||
setActiveIndex(filtered.length > 0 ? 0 : -1)
|
||||
}, [searchQuery, items, filter])
|
||||
|
||||
// Default filter function
|
||||
const defaultFilter = useCallback((value: string, search: string): number => {
|
||||
const normalizedValue = value.toLowerCase()
|
||||
const normalizedSearch = search.toLowerCase()
|
||||
|
||||
if (normalizedValue === normalizedSearch) return 1
|
||||
if (normalizedValue.startsWith(normalizedSearch)) return 0.8
|
||||
if (normalizedValue.includes(normalizedSearch)) return 0.6
|
||||
return 0
|
||||
}, [])
|
||||
|
||||
// Handle keyboard navigation
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (filteredItems.length === 0) return
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
setActiveIndex((prev) => (prev + 1) % filteredItems.length)
|
||||
break
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
setActiveIndex((prev) => (prev - 1 + filteredItems.length) % filteredItems.length)
|
||||
break
|
||||
case 'Enter':
|
||||
if (activeIndex >= 0) {
|
||||
e.preventDefault()
|
||||
document.getElementById(filteredItems[activeIndex])?.click()
|
||||
}
|
||||
break
|
||||
}
|
||||
},
|
||||
[filteredItems, activeIndex]
|
||||
)
|
||||
|
||||
// Memoize context value to prevent unnecessary re-renders
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
searchQuery,
|
||||
setSearchQuery,
|
||||
activeIndex,
|
||||
setActiveIndex,
|
||||
filteredItems,
|
||||
registerItem,
|
||||
unregisterItem,
|
||||
selectItem,
|
||||
}),
|
||||
[searchQuery, activeIndex, filteredItems, registerItem, unregisterItem, selectItem]
|
||||
)
|
||||
|
||||
return (
|
||||
<CommandContext.Provider value={contextValue}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[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-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5',
|
||||
className
|
||||
)}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</CommandContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// Command Input component
|
||||
export function CommandInput({
|
||||
placeholder = 'Search...',
|
||||
className,
|
||||
onValueChange,
|
||||
}: CommandInputProps) {
|
||||
const { searchQuery, setSearchQuery } = useCommandContext()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
setSearchQuery(value)
|
||||
onValueChange?.(value)
|
||||
}
|
||||
|
||||
// Focus input on mount
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="flex items-center border-b px-3">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<input
|
||||
ref={inputRef}
|
||||
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
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
value={searchQuery}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Command List component
|
||||
export function CommandList({ children, className }: CommandListProps) {
|
||||
return (
|
||||
<div className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Command Empty component
|
||||
export function CommandEmpty({ children, className }: CommandEmptyProps) {
|
||||
const { filteredItems } = useCommandContext()
|
||||
|
||||
if (filteredItems.length > 0) return null
|
||||
|
||||
return (
|
||||
<div className={cn('pt-3.5 pb-2 text-center text-sm text-muted-foreground', className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Command Group component
|
||||
export function CommandGroup({ children, className, heading }: CommandGroupProps) {
|
||||
return (
|
||||
<div
|
||||
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
|
||||
)}
|
||||
>
|
||||
{heading && (
|
||||
<div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">{heading}</div>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Command Item component
|
||||
export function CommandItem({
|
||||
children,
|
||||
className,
|
||||
value,
|
||||
onSelect,
|
||||
disabled = false,
|
||||
}: CommandItemProps) {
|
||||
const { activeIndex, filteredItems, registerItem, unregisterItem } = useCommandContext()
|
||||
const isActive = filteredItems.indexOf(value) === activeIndex
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
|
||||
// Register and unregister item
|
||||
useEffect(() => {
|
||||
// Only register if value is defined
|
||||
if (value) {
|
||||
registerItem(value)
|
||||
return () => unregisterItem(value)
|
||||
}
|
||||
}, [value, registerItem, unregisterItem])
|
||||
|
||||
// Check if item should be displayed based on search
|
||||
const shouldDisplay = filteredItems.includes(value)
|
||||
|
||||
if (!shouldDisplay) return null
|
||||
|
||||
return (
|
||||
<button
|
||||
id={value}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 hover:bg-accent hover:text-accent-foreground',
|
||||
isActive && 'bg-accent text-accent-foreground',
|
||||
className
|
||||
)}
|
||||
onClick={() => !disabled && onSelect?.()}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
data-selected={isActive || isHovered}
|
||||
data-disabled={disabled}
|
||||
disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
// Command Separator component
|
||||
export function CommandSeparator({ className }: CommandSeparatorProps) {
|
||||
return <div className={cn('-mx-1 h-px bg-border', className)} />
|
||||
}
|
||||
|
||||
// Export all components
|
||||
export const ToolCommand = {
|
||||
Root: Command,
|
||||
Input: CommandInput,
|
||||
List: CommandList,
|
||||
Empty: CommandEmpty,
|
||||
Group: CommandGroup,
|
||||
Item: CommandItem,
|
||||
Separator: CommandSeparator,
|
||||
}
|
||||
+71
-42
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { PencilIcon, PlusIcon, WrenchIcon, XIcon } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
@@ -26,6 +26,7 @@ import { getTool } from '@/tools'
|
||||
import { useSubBlockValue } from '../../hooks/use-sub-block-value'
|
||||
import { ShortInput } from '../short-input'
|
||||
import { CustomTool, CustomToolModal } from './components/custom-tool-modal'
|
||||
import { ToolCommand } from './components/tool-command'
|
||||
|
||||
interface ToolInputProps {
|
||||
blockId: string
|
||||
@@ -126,6 +127,26 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
|
||||
const toolBlocks = getAllBlocks().filter((block) => block.category === 'tools')
|
||||
|
||||
// Custom filter function for the Command component
|
||||
const customFilter = useCallback((value: string, search: string) => {
|
||||
if (!search.trim()) return 1
|
||||
|
||||
const normalizedValue = value.toLowerCase()
|
||||
const normalizedSearch = search.toLowerCase()
|
||||
|
||||
// Exact match gets highest priority
|
||||
if (normalizedValue === normalizedSearch) return 1
|
||||
|
||||
// Starts with search term gets high priority
|
||||
if (normalizedValue.startsWith(normalizedSearch)) return 0.8
|
||||
|
||||
// Contains search term gets medium priority
|
||||
if (normalizedValue.includes(normalizedSearch)) return 0.6
|
||||
|
||||
// No match
|
||||
return 0
|
||||
}, [])
|
||||
|
||||
const selectedTools: StoredTool[] =
|
||||
Array.isArray(value) && value.length > 0 && typeof value[0] === 'object'
|
||||
? (value as unknown as StoredTool[])
|
||||
@@ -300,12 +321,13 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0 w-[200px]" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search tools..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No tools found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
<ToolCommand.Root filter={customFilter}>
|
||||
<ToolCommand.Input placeholder="Search tools..." />
|
||||
<ToolCommand.List>
|
||||
<ToolCommand.Empty>No tools found</ToolCommand.Empty>
|
||||
<ToolCommand.Group>
|
||||
<ToolCommand.Item
|
||||
value="Create Tool"
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
setCustomToolModalOpen(true)
|
||||
@@ -316,19 +338,20 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<WrenchIcon className="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
<span>Create Tool</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
|
||||
{/* Display saved custom tools at the top */}
|
||||
{customTools.length > 0 && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<ToolCommand.Separator />
|
||||
<div className="px-2 pt-2.5 pb-0.5 text-xs font-medium text-muted-foreground">
|
||||
Custom Tools
|
||||
</div>
|
||||
<CommandGroup className="-mx-1 -px-1">
|
||||
<ToolCommand.Group className="-mx-1 -px-1">
|
||||
{customTools.map((customTool) => (
|
||||
<CommandItem
|
||||
<ToolCommand.Item
|
||||
key={customTool.id}
|
||||
value={customTool.title}
|
||||
onSelect={() => {
|
||||
const newTool: StoredTool = {
|
||||
type: 'custom-tool',
|
||||
@@ -363,10 +386,10 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<WrenchIcon className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="truncate max-w-[140px]">{customTool.title}</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
</ToolCommand.Group>
|
||||
<ToolCommand.Separator />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -374,10 +397,11 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<div className="px-2 pt-2.5 pb-0.5 text-xs font-medium text-muted-foreground">
|
||||
Built-in Tools
|
||||
</div>
|
||||
<CommandGroup className="-mx-1 -px-1">
|
||||
<ToolCommand.Group className="-mx-1 -px-1">
|
||||
{toolBlocks.map((block) => (
|
||||
<CommandItem
|
||||
<ToolCommand.Item
|
||||
key={block.type}
|
||||
value={block.name}
|
||||
onSelect={() => handleSelectTool(block)}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
>
|
||||
@@ -388,12 +412,12 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<IconComponent icon={block.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="truncate max-w-[140px]">{block.name}</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</ToolCommand.Group>
|
||||
</ToolCommand.Group>
|
||||
</ToolCommand.List>
|
||||
</ToolCommand.Root>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
) : (
|
||||
@@ -540,12 +564,13 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0 w-[200px]" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search tools..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No tools found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
<ToolCommand.Root filter={customFilter}>
|
||||
<ToolCommand.Input placeholder="Search tools..." />
|
||||
<ToolCommand.List>
|
||||
<ToolCommand.Empty>No tools found.</ToolCommand.Empty>
|
||||
<ToolCommand.Group>
|
||||
<ToolCommand.Item
|
||||
value="Create Tool"
|
||||
onSelect={() => {
|
||||
setOpen(false)
|
||||
setCustomToolModalOpen(true)
|
||||
@@ -556,19 +581,20 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<WrenchIcon className="w-4 h-4 text-muted-foreground" />
|
||||
</div>
|
||||
<span>Create Tool</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
|
||||
{/* Display saved custom tools at the top */}
|
||||
{customTools.length > 0 && (
|
||||
<>
|
||||
<CommandSeparator />
|
||||
<ToolCommand.Separator />
|
||||
<div className="px-2 pt-2.5 pb-0.5 text-xs font-medium text-muted-foreground">
|
||||
Custom Tools
|
||||
</div>
|
||||
<CommandGroup className="-mx-1 -px-1">
|
||||
<ToolCommand.Group className="-mx-1 -px-1">
|
||||
{customTools.map((customTool) => (
|
||||
<CommandItem
|
||||
<ToolCommand.Item
|
||||
key={customTool.id}
|
||||
value={customTool.title}
|
||||
onSelect={() => {
|
||||
const newTool: StoredTool = {
|
||||
type: 'custom-tool',
|
||||
@@ -606,10 +632,10 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<WrenchIcon className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="truncate max-w-[140px]">{customTool.title}</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
))}
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
</ToolCommand.Group>
|
||||
<ToolCommand.Separator />
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -617,10 +643,11 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<div className="px-2 pt-2.5 pb-0.5 text-xs font-medium text-muted-foreground">
|
||||
Built-in Tools
|
||||
</div>
|
||||
<CommandGroup className="-mx-1 -px-1">
|
||||
<ToolCommand.Group className="-mx-1 -px-1">
|
||||
{toolBlocks.map((block) => (
|
||||
<CommandItem
|
||||
<ToolCommand.Item
|
||||
key={block.type}
|
||||
value={block.name}
|
||||
onSelect={() => handleSelectTool(block)}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
>
|
||||
@@ -631,27 +658,29 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
<IconComponent icon={block.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="truncate max-w-[140px]">{block.name}</span>
|
||||
</CommandItem>
|
||||
</ToolCommand.Item>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</ToolCommand.Group>
|
||||
</ToolCommand.Group>
|
||||
</ToolCommand.List>
|
||||
</ToolCommand.Root>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Custom Tool Modal */}
|
||||
<CustomToolModal
|
||||
open={customToolModalOpen}
|
||||
onOpenChange={(open) => {
|
||||
setCustomToolModalOpen(open)
|
||||
if (!open) setEditingToolIndex(null)
|
||||
}}
|
||||
onSave={handleSaveCustomTool}
|
||||
onSave={editingToolIndex !== null ? handleSaveCustomTool : handleAddCustomTool}
|
||||
initialValues={
|
||||
editingToolIndex !== null && selectedTools[editingToolIndex]?.type === 'custom-tool'
|
||||
? {
|
||||
id: '',
|
||||
schema: selectedTools[editingToolIndex].schema,
|
||||
code: selectedTools[editingToolIndex].code || '',
|
||||
}
|
||||
|
||||
+10
-30
@@ -18,45 +18,20 @@ import { cn } from '@/lib/utils'
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
const Command = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive> & {
|
||||
children?: React.ReactNode
|
||||
filter?: (value: string, search: string) => number
|
||||
}
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className, filter, ...props }, ref) => (
|
||||
<CommandPrimitive
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',
|
||||
className
|
||||
)}
|
||||
filter={filter}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -78,8 +53,9 @@ const CommandInput = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive.Input>,
|
||||
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> & {
|
||||
placeholder?: string
|
||||
onValueChange?: (value: string) => void
|
||||
}
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className, onValueChange, ...props }, ref) => (
|
||||
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
@@ -89,6 +65,7 @@ const CommandInput = React.forwardRef<
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
onChange={(e) => onValueChange?.(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
@@ -158,14 +135,17 @@ const CommandItem = React.forwardRef<
|
||||
children?: React.ReactNode
|
||||
onSelect?: () => void
|
||||
className?: string
|
||||
value?: string
|
||||
keywords?: string
|
||||
}
|
||||
>(({ className, ...props }, ref) => (
|
||||
>(({ className, value, keywords, ...props }, ref) => (
|
||||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user