feat: categories autocomplete
This commit is contained in:
parent
87b952dd20
commit
f82f695bae
|
|
@ -10,7 +10,6 @@ import {
|
|||
FormMessage,
|
||||
FormDescription,
|
||||
} from "@renderer/components/ui/form"
|
||||
import { Input } from "@renderer/components/ui/input"
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query"
|
||||
import {
|
||||
DialogContent,
|
||||
|
|
@ -31,6 +30,8 @@ import { Switch } from "@renderer/components/ui/switch"
|
|||
import { FollowSummary } from "../feed-summary"
|
||||
import { apiFetch } from "@renderer/lib/queries/api-fetch"
|
||||
import { SubscriptionResponse } from "@renderer/lib/types"
|
||||
import { AutoComplete } from "@renderer/components/ui/autocomplete"
|
||||
import { useSubscriptionCategories } from "@renderer/lib/queries/subscriptions"
|
||||
|
||||
const formSchema = z.object({
|
||||
view: z.string(),
|
||||
|
|
@ -96,6 +97,8 @@ export function FollowDialog({
|
|||
followMutation.mutate(values)
|
||||
}
|
||||
|
||||
const categories = useSubscriptionCategories(parseInt(form.watch("view")))
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
|
|
@ -148,7 +151,11 @@ export function FollowDialog({
|
|||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
<AutoComplete
|
||||
options={categories.data}
|
||||
emptyMessage="No resulsts."
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
import {
|
||||
Command,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandInput,
|
||||
} from "@renderer/components/ui/command"
|
||||
import { useState, useCallback } from "react"
|
||||
|
||||
import { cn } from "@renderer/lib/utils"
|
||||
import * as React from "react"
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
options: string[]
|
||||
emptyMessage: string
|
||||
value?: string
|
||||
}
|
||||
|
||||
export const AutoComplete = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
({ options, emptyMessage, className, value, ...props }, ref) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const onValueChange = useCallback(
|
||||
(value: string) => {
|
||||
props.onChange?.({
|
||||
target: {
|
||||
value,
|
||||
},
|
||||
} as React.ChangeEvent<HTMLInputElement>)
|
||||
},
|
||||
[props.onChange],
|
||||
)
|
||||
|
||||
const handleSelectOption = useCallback(
|
||||
(option: string) => {
|
||||
setOpen(false)
|
||||
onValueChange(option)
|
||||
},
|
||||
[props.onChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<Command className="rounded-lg border overflow-visible relative [&_[cmdk-input-wrapper]]:border-0 [&_.lucide-search]:hidden">
|
||||
<CommandInput
|
||||
{...props}
|
||||
onBlur={(e) => {
|
||||
setOpen(false)
|
||||
props.onBlur?.(e)
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setOpen(true)
|
||||
props.onFocus?.(e)
|
||||
}}
|
||||
value={value}
|
||||
onValueChange={(value) => {
|
||||
setOpen(true)
|
||||
onValueChange(value)
|
||||
}}
|
||||
ref={ref}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandGroup
|
||||
className={cn(
|
||||
"absolute top-full bg-white w-full z-10 border rounded-lg mt-1 hidden [&[hidden]]:!hidden",
|
||||
open && "block",
|
||||
)}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<CommandItem
|
||||
key={option}
|
||||
value={option}
|
||||
onSelect={() => handleSelectOption(option)}
|
||||
className="text-zinc-800"
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -115,7 +115,7 @@ const CommandItem = React.forwardRef<
|
|||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -88,3 +88,20 @@ export const useSubscriptions = (view?: number) =>
|
|||
} as Response
|
||||
},
|
||||
})
|
||||
|
||||
export const useSubscriptionCategories = (view?: number) =>
|
||||
useQuery({
|
||||
queryKey: ["subscription-categories", view],
|
||||
queryFn: async () => {
|
||||
const { data: categories } = await apiFetch<{
|
||||
code: number
|
||||
data: string[]
|
||||
}>("/subscriptions/categories", {
|
||||
query: {
|
||||
view,
|
||||
},
|
||||
})
|
||||
|
||||
return categories || []
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue