Skip to content

Commit 225571c

Browse files
authored
improvement(search): improved filters UI and search suggestions (#1387)
* improvement(search): improved filters UI and search suggestions * update tool input UI
1 parent a1c518e commit 225571c

File tree

7 files changed

+40
-38
lines changed

7 files changed

+40
-38
lines changed

apps/sim/app/workspace/[workspaceId]/logs/components/filters/filters.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,20 @@ export function Filters() {
5858

5959
<h2 className='mb-4 pl-2 font-medium text-sm'>Filters</h2>
6060

61-
{/* Timeline Filter */}
62-
<FilterSection title='Timeline' content={<Timeline />} />
63-
6461
{/* Level Filter */}
6562
<FilterSection title='Level' content={<Level />} />
6663

67-
{/* Trigger Filter */}
68-
<FilterSection title='Trigger' content={<Trigger />} />
64+
{/* Workflow Filter */}
65+
<FilterSection title='Workflow' content={<Workflow />} />
6966

7067
{/* Folder Filter */}
7168
<FilterSection title='Folder' content={<FolderFilter />} />
7269

73-
{/* Workflow Filter */}
74-
<FilterSection title='Workflow' content={<Workflow />} />
70+
{/* Trigger Filter */}
71+
<FilterSection title='Trigger' content={<Trigger />} />
72+
73+
{/* Timeline Filter */}
74+
<FilterSection title='Timeline' content={<Timeline />} />
7575
</div>
7676
)
7777
}

apps/sim/app/workspace/[workspaceId]/logs/components/search/search.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useEffect, useMemo } from 'react'
3+
import { useEffect, useMemo, useState } from 'react'
44
import { Loader2, Search, X } from 'lucide-react'
55
import { Badge } from '@/components/ui/badge'
66
import { Button } from '@/components/ui/button'
@@ -89,6 +89,16 @@ export function AutocompleteSearch({
8989
}
9090
}, [state.isOpen, state.highlightedIndex])
9191

92+
const [showSpinner, setShowSpinner] = useState(false)
93+
useEffect(() => {
94+
if (!state.pendingQuery) {
95+
setShowSpinner(false)
96+
return
97+
}
98+
const t = setTimeout(() => setShowSpinner(true), 200)
99+
return () => clearTimeout(t)
100+
}, [state.pendingQuery])
101+
92102
const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
93103
const newValue = e.target.value
94104
const cursorPos = e.target.selectionStart || 0
@@ -126,7 +136,7 @@ export function AutocompleteSearch({
126136
state.isOpen && 'ring-1 ring-ring'
127137
)}
128138
>
129-
{state.pendingQuery ? (
139+
{showSpinner ? (
130140
<Loader2 className='h-4 w-4 flex-shrink-0 animate-spin text-muted-foreground' />
131141
) : (
132142
<Search className='h-4 w-4 flex-shrink-0 text-muted-foreground' strokeWidth={2} />

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/components/tool-command/tool-command.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { Search } from 'lucide-react'
1313
import { cn } from '@/lib/utils'
1414

15-
// Context for the command component
1615
type CommandContextType = {
1716
searchQuery: string
1817
setSearchQuery: (value: string) => void
@@ -26,7 +25,6 @@ type CommandContextType = {
2625

2726
const CommandContext = createContext<CommandContextType | undefined>(undefined)
2827

29-
// Hook to use the command context
3028
const useCommandContext = () => {
3129
const context = useContext(CommandContext)
3230
if (!context) {
@@ -35,7 +33,6 @@ const useCommandContext = () => {
3533
return context
3634
}
3735

38-
// Types for the components
3936
interface CommandProps {
4037
children: ReactNode
4138
className?: string
@@ -76,17 +73,14 @@ interface CommandSeparatorProps {
7673
className?: string
7774
}
7875

79-
// Main Command component
8076
export function Command({ children, className, filter }: CommandProps) {
8177
const [searchQuery, setSearchQuery] = useState('')
8278
const [activeIndex, setActiveIndex] = useState(-1)
8379
const [items, setItems] = useState<string[]>([])
8480
const [filteredItems, setFilteredItems] = useState<string[]>([])
8581

86-
// Register and unregister items - memoize to prevent infinite loops
8782
const registerItem = useCallback((id: string) => {
8883
setItems((prev) => {
89-
// Only add if not already in the array
9084
if (prev.includes(id)) return prev
9185
return [...prev, id]
9286
})
@@ -96,7 +90,6 @@ export function Command({ children, className, filter }: CommandProps) {
9690
setItems((prev) => prev.filter((item) => item !== id))
9791
}, [])
9892

99-
// Handle item selection
10093
const selectItem = useCallback(
10194
(id: string) => {
10295
const index = filteredItems.indexOf(id)
@@ -107,7 +100,6 @@ export function Command({ children, className, filter }: CommandProps) {
107100
[filteredItems]
108101
)
109102

110-
// Filter items based on search query
111103
useEffect(() => {
112104
if (!searchQuery) {
113105
setFilteredItems(items)
@@ -127,7 +119,6 @@ export function Command({ children, className, filter }: CommandProps) {
127119
setActiveIndex(filtered.length > 0 ? 0 : -1)
128120
}, [searchQuery, items, filter])
129121

130-
// Default filter function
131122
const defaultFilter = useCallback((value: string, search: string): number => {
132123
const normalizedValue = value.toLowerCase()
133124
const normalizedSearch = search.toLowerCase()
@@ -138,7 +129,6 @@ export function Command({ children, className, filter }: CommandProps) {
138129
return 0
139130
}, [])
140131

141-
// Handle keyboard navigation
142132
const handleKeyDown = useCallback(
143133
(e: React.KeyboardEvent) => {
144134
if (filteredItems.length === 0) return
@@ -163,7 +153,6 @@ export function Command({ children, className, filter }: CommandProps) {
163153
[filteredItems, activeIndex]
164154
)
165155

166-
// Memoize context value to prevent unnecessary re-renders
167156
const contextValue = useMemo(
168157
() => ({
169158
searchQuery,
@@ -193,7 +182,6 @@ export function Command({ children, className, filter }: CommandProps) {
193182
)
194183
}
195184

196-
// Command Input component
197185
export function CommandInput({
198186
placeholder = 'Search...',
199187
className,
@@ -208,7 +196,6 @@ export function CommandInput({
208196
onValueChange?.(value)
209197
}
210198

211-
// Focus input on mount
212199
useEffect(() => {
213200
inputRef.current?.focus()
214201
}, [])
@@ -230,7 +217,6 @@ export function CommandInput({
230217
)
231218
}
232219

233-
// Command List component
234220
export function CommandList({ children, className }: CommandListProps) {
235221
return (
236222
<div className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}>
@@ -239,7 +225,6 @@ export function CommandList({ children, className }: CommandListProps) {
239225
)
240226
}
241227

242-
// Command Empty component
243228
export function CommandEmpty({ children, className }: CommandEmptyProps) {
244229
const { filteredItems } = useCommandContext()
245230

@@ -252,7 +237,6 @@ export function CommandEmpty({ children, className }: CommandEmptyProps) {
252237
)
253238
}
254239

255-
// Command Group component
256240
export function CommandGroup({ children, className, heading }: CommandGroupProps) {
257241
return (
258242
<div
@@ -269,7 +253,6 @@ export function CommandGroup({ children, className, heading }: CommandGroupProps
269253
)
270254
}
271255

272-
// Command Item component
273256
export function CommandItem({
274257
children,
275258
className,
@@ -281,16 +264,13 @@ export function CommandItem({
281264
const isActive = filteredItems.indexOf(value) === activeIndex
282265
const [isHovered, setIsHovered] = useState(false)
283266

284-
// Register and unregister item
285267
useEffect(() => {
286-
// Only register if value is defined
287268
if (value) {
288269
registerItem(value)
289270
return () => unregisterItem(value)
290271
}
291272
}, [value, registerItem, unregisterItem])
292273

293-
// Check if item should be displayed based on search
294274
const shouldDisplay = filteredItems.includes(value)
295275

296276
if (!shouldDisplay) return null
@@ -315,12 +295,10 @@ export function CommandItem({
315295
)
316296
}
317297

318-
// Command Separator component
319298
export function CommandSeparator({ className }: CommandSeparatorProps) {
320299
return <div className={cn('-mx-1 h-px bg-border', className)} />
321300
}
322301

323-
// Export all components
324302
export const ToolCommand = {
325303
Root: Command,
326304
Input: CommandInput,

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,7 +1189,13 @@ export function ToolInput({
11891189
</div>
11901190
</div>
11911191
</PopoverTrigger>
1192-
<PopoverContent className='w-[200px] p-0' align='start'>
1192+
<PopoverContent
1193+
className='h-[360px] w-[200px] p-0'
1194+
align='start'
1195+
side='bottom'
1196+
sideOffset={6}
1197+
avoidCollisions={false}
1198+
>
11931199
<ToolCommand.Root filter={customFilter}>
11941200
<ToolCommand.Input placeholder='Search tools...' onValueChange={setSearchQuery} />
11951201
<ToolCommand.List>
@@ -1749,7 +1755,13 @@ export function ToolInput({
17491755
Add Tool
17501756
</Button>
17511757
</PopoverTrigger>
1752-
<PopoverContent className='w-[200px] p-0' align='start'>
1758+
<PopoverContent
1759+
className='h-[360px] w-[280px] p-0'
1760+
align='start'
1761+
side='bottom'
1762+
sideOffset={6}
1763+
avoidCollisions={false}
1764+
>
17531765
<ToolCommand.Root filter={customFilter}>
17541766
<ToolCommand.Input placeholder='Search tools...' onValueChange={setSearchQuery} />
17551767
<ToolCommand.List>

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/logs-filters/logs-filters.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import Workflow from '@/app/workspace/[workspaceId]/logs/components/filters/comp
1010

1111
export function LogsFilters() {
1212
const sections = [
13-
{ key: 'timeline', title: 'Timeline', component: <Timeline /> },
1413
{ key: 'level', title: 'Level', component: <Level /> },
15-
{ key: 'trigger', title: 'Trigger', component: <Trigger /> },
16-
{ key: 'folder', title: 'Folder', component: <FolderFilter /> },
1714
{ key: 'workflow', title: 'Workflow', component: <Workflow /> },
15+
{ key: 'folder', title: 'Folder', component: <FolderFilter /> },
16+
{ key: 'trigger', title: 'Trigger', component: <Trigger /> },
17+
{ key: 'timeline', title: 'Timeline', component: <Timeline /> },
1818
]
1919

2020
return (

apps/sim/components/ui/dropdown-menu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayNam
5656
const DropdownMenuContent = React.forwardRef<
5757
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
5858
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
59-
>(({ className, sideOffset = 4, ...props }, ref) => (
59+
>(({ className, sideOffset = 4, avoidCollisions = false, sticky = 'always', ...props }, ref) => (
6060
<DropdownMenuPrimitive.Portal>
6161
<DropdownMenuPrimitive.Content
6262
ref={ref}
6363
sideOffset={sideOffset}
64+
avoidCollisions={avoidCollisions}
65+
sticky={sticky as any}
6466
className={cn(
6567
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
6668
className

apps/sim/lib/logs/search-suggestions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ export class SearchSuggestions {
407407
}
408408

409409
/**
410-
* Generate preview text for a suggestion - SIMPLE APPROACH
410+
* Generate preview text for a suggestion
411411
* Show suggestion at the end of input, with proper spacing logic
412412
*/
413413
generatePreview(suggestion: Suggestion, currentValue: string, cursorPosition: number): string {

0 commit comments

Comments
 (0)