mirror of
https://github.com/walkxcode/dashboard-icons.git
synced 2025-10-26 21:19:04 +08:00
feat: add icon name combobox component
- Create combobox for icon name selection and creation - Integrate with PocketBase to fetch existing icon names - Add input sanitization for icon IDs (lowercase, hyphens only) - Implement existing icon detection and validation - Support both selection from existing icons and creation of new ones - Provide visual feedback for new vs existing icon submissions
This commit is contained in:
113
web/src/components/icon-name-combobox.tsx
Normal file
113
web/src/components/icon-name-combobox.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxContent,
|
||||
ComboboxCreateNew,
|
||||
ComboboxEmpty,
|
||||
ComboboxGroup,
|
||||
ComboboxInput,
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
ComboboxTrigger,
|
||||
} from "@/components/ui/shadcn-io/combobox"
|
||||
import { pb } from "@/lib/pb"
|
||||
|
||||
interface IconNameComboboxProps {
|
||||
value: string
|
||||
onValueChange: (value: string) => void
|
||||
onIsExisting: (isExisting: boolean) => void
|
||||
}
|
||||
|
||||
export function IconNameCombobox({ value, onValueChange, onIsExisting }: IconNameComboboxProps) {
|
||||
const [existingIcons, setExistingIcons] = useState<Array<{ label: string; value: string }>>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchExistingIcons() {
|
||||
try {
|
||||
const records = await pb.collection("community_gallery").getFullList({
|
||||
fields: "name",
|
||||
sort: "name",
|
||||
})
|
||||
|
||||
const uniqueNames = Array.from(new Set(records.map((r) => r.name)))
|
||||
const formattedIcons = uniqueNames.map((name) => ({
|
||||
label: name,
|
||||
value: name,
|
||||
}))
|
||||
|
||||
setExistingIcons(formattedIcons)
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch existing icons:", error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchExistingIcons()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const isExisting = existingIcons.some((icon) => icon.value === value)
|
||||
onIsExisting(isExisting)
|
||||
}, [value, existingIcons, onIsExisting])
|
||||
|
||||
const handleCreateNew = (inputValue: string) => {
|
||||
const sanitizedValue = inputValue
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^a-z0-9-]/g, "")
|
||||
onValueChange(sanitizedValue)
|
||||
}
|
||||
|
||||
const handleValueChange = (newValue: string) => {
|
||||
onValueChange(newValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<Combobox data={existingIcons} type="icon" value={value} onValueChange={handleValueChange}>
|
||||
<ComboboxTrigger className="w-full justify-start">
|
||||
{value ? (
|
||||
<span className="flex items-center justify-between w-full">
|
||||
<span className="font-mono">{value}</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">Select or create icon ID...</span>
|
||||
)}
|
||||
</ComboboxTrigger>
|
||||
<ComboboxContent>
|
||||
<ComboboxInput placeholder="Search or type new icon ID..." />
|
||||
<ComboboxList>
|
||||
<ComboboxEmpty>No existing icon found.</ComboboxEmpty>
|
||||
{existingIcons.length > 0 && (
|
||||
<ComboboxGroup heading="Existing Icons">
|
||||
{existingIcons.map((icon) => (
|
||||
<ComboboxItem key={icon.value} value={icon.value}>
|
||||
<span className="font-mono text-sm">{icon.label}</span>
|
||||
</ComboboxItem>
|
||||
))}
|
||||
</ComboboxGroup>
|
||||
)}
|
||||
<ComboboxCreateNew onCreateNew={handleCreateNew}>
|
||||
{(inputValue) => (
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-muted-foreground">Create new icon:</span>
|
||||
<span className="font-mono font-semibold">
|
||||
{inputValue
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^a-z0-9-]/g, "")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</ComboboxCreateNew>
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user