-
Notifications
You must be signed in to change notification settings - Fork 512
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
e6ea4de
commit 0ec8f9d
Showing
10 changed files
with
406 additions
and
106 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
import { clsx, type ClassValue } from "clsx" | ||
import { twMerge } from "tailwind-merge" | ||
import { clsx, type ClassValue } from "clsx"; | ||
import { twMerge } from "tailwind-merge"; | ||
|
||
export function cn(...inputs: ClassValue[]) { | ||
return twMerge(clsx(inputs)) | ||
return twMerge(clsx(inputs)); | ||
} |
64 changes: 64 additions & 0 deletions
64
screenpipe-app-tauri/components/app-window-auto-complete.tsx
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,64 @@ | ||
import React, { useState, useEffect } from "react"; | ||
import { Input } from "@/components/ui/input"; | ||
import { useAppWindowHistory } from "@/lib/hooks/use-sql-autocomplete"; | ||
import { Command } from "cmdk"; | ||
|
||
interface AppWindowAutocompleteProps { | ||
id: string; | ||
placeholder: string; | ||
value: string; | ||
onChange: (value: string) => void; | ||
type: "app" | "window"; | ||
icon: React.ReactNode; | ||
} | ||
|
||
export function AppWindowAutocomplete({ | ||
id, | ||
placeholder, | ||
value, | ||
onChange, | ||
type, | ||
icon, | ||
}: AppWindowAutocompleteProps) { | ||
const { history, isLoading, error } = useAppWindowHistory(type); | ||
const [open, setOpen] = useState(false); | ||
|
||
if (error) { | ||
console.error("error fetching history:", error); | ||
} | ||
|
||
return ( | ||
<div className="relative"> | ||
{icon} | ||
<Command> | ||
<Input | ||
id={id} | ||
type="text" | ||
placeholder={placeholder} | ||
value={value} | ||
onChange={(e) => onChange(e.target.value)} | ||
onFocus={() => setOpen(true)} | ||
onBlur={() => setOpen(false)} | ||
className="pl-8" | ||
/> | ||
{open && !isLoading && ( | ||
<Command.List className="absolute z-10 w-full bg-white border border-gray-300 rounded-md mt-1 max-h-60 overflow-auto"> | ||
<Command.Input /> | ||
{history.map((item) => ( | ||
<Command.Item | ||
key={item.name} | ||
value={item.name} | ||
onSelect={(selectedValue) => { | ||
onChange(selectedValue); | ||
setOpen(false); | ||
}} | ||
> | ||
{item.name} ({item.count}) | ||
</Command.Item> | ||
))} | ||
</Command.List> | ||
)} | ||
</Command> | ||
</div> | ||
); | ||
} |
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
137 changes: 137 additions & 0 deletions
137
screenpipe-app-tauri/components/sql-autocomplete-input.tsx
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 @@ | ||
import React, { useState, useRef, useCallback, useEffect } from "react"; | ||
import { useSqlAutocomplete } from "@/lib/hooks/use-sql-autocomplete"; | ||
import { Command } from "cmdk"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Loader2, Search, X } from "lucide-react"; | ||
|
||
interface SqlAutocompleteInputProps { | ||
id: string; | ||
placeholder: string; | ||
value: string; | ||
onChange: (value: string) => void; | ||
type: "app" | "window"; | ||
icon: React.ReactNode; | ||
} | ||
|
||
export function SqlAutocompleteInput({ | ||
id, | ||
placeholder, | ||
value, | ||
onChange, | ||
type, | ||
icon, | ||
}: SqlAutocompleteInputProps) { | ||
const { items, isLoading } = useSqlAutocomplete(type); | ||
const [open, setOpen] = useState(false); | ||
const [inputValue, setInputValue] = useState(value); | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
const commandRef = useRef<HTMLDivElement>(null); | ||
|
||
const handleSelect = (selectedValue: string) => { | ||
onChange(selectedValue); | ||
setInputValue(selectedValue); | ||
setOpen(false); | ||
inputRef.current?.focus(); | ||
}; | ||
|
||
const handleInputChange = useCallback( | ||
(e: React.ChangeEvent<HTMLInputElement>) => { | ||
setInputValue(e.target.value); | ||
onChange(e.target.value); | ||
}, | ||
[onChange] | ||
); | ||
|
||
const handleClearInput = useCallback(() => { | ||
setInputValue(""); | ||
onChange(""); | ||
inputRef.current?.focus(); | ||
}, [onChange]); | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (event: MouseEvent) => { | ||
if ( | ||
commandRef.current && | ||
!commandRef.current.contains(event.target as Node) | ||
) { | ||
setOpen(false); | ||
} | ||
}; | ||
|
||
document.addEventListener("mousedown", handleClickOutside); | ||
return () => { | ||
document.removeEventListener("mousedown", handleClickOutside); | ||
}; | ||
}, []); | ||
|
||
return ( | ||
<div className="relative" ref={commandRef}> | ||
<div className="absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-400 z-10 flex items-center"> | ||
{icon} | ||
<span className="w-2" /> | ||
</div> | ||
<Command className="relative w-full" shouldFilter={false}> | ||
<div className="relative"> | ||
<Input | ||
ref={inputRef} | ||
id={id} | ||
type="text" | ||
placeholder={placeholder} | ||
value={inputValue} | ||
onChange={handleInputChange} | ||
onFocus={() => setOpen(true)} | ||
className="pl-10 pr-8 w-full" | ||
autoCorrect="off" | ||
/> | ||
{inputValue && ( | ||
<button | ||
onClick={handleClearInput} | ||
className="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600" | ||
> | ||
<X className="h-4 w-4" /> | ||
</button> | ||
)} | ||
</div> | ||
{open && ( | ||
<Command.List className="absolute z-20 w-full bg-white border border-gray-300 rounded-md mt-1 max-h-60 overflow-auto shadow-lg text-sm"> | ||
<div className="flex items-center px-3 py-2 border-b border-gray-200"> | ||
<Search className="mr-2 h-4 w-4 text-gray-400" /> | ||
<Command.Input | ||
placeholder="search..." | ||
value={inputValue} | ||
onValueChange={setInputValue} | ||
className="border-none focus:ring-0 outline-none w-full" | ||
/> | ||
</div> | ||
{isLoading ? ( | ||
<Command.Loading> | ||
<div className="px-4 py-2 text-gray-500 flex items-center"> | ||
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> | ||
loading... | ||
</div> | ||
</Command.Loading> | ||
) : ( | ||
items | ||
.filter((item) => | ||
item.name.toLowerCase().includes(inputValue.toLowerCase()) | ||
) | ||
.map((item: any) => ( | ||
<Command.Item | ||
key={item.name} | ||
value={item.name} | ||
onSelect={handleSelect} | ||
className="px-4 py-2 hover:bg-gray-100 cursor-pointer border-b border-gray-200 last:border-b-0" | ||
> | ||
{item.name} ({item.count}) | ||
</Command.Item> | ||
)) | ||
)} | ||
{!isLoading && items.length === 0 && ( | ||
<div className="px-4 py-2 text-gray-500">no results found</div> | ||
)} | ||
</Command.List> | ||
)} | ||
</Command> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.