Skip to content

Commit

Permalink
Use combobox for category selector
Browse files Browse the repository at this point in the history
  • Loading branch information
scastiel committed Jan 17, 2024
1 parent c4de3f6 commit 92156b2
Show file tree
Hide file tree
Showing 5 changed files with 507 additions and 31 deletions.
252 changes: 252 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@tailwindcss/typography": "^0.5.10",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
"dayjs": "^1.11.10",
"lucide-react": "^0.290.0",
"nanoid": "^5.0.4",
Expand Down
94 changes: 94 additions & 0 deletions src/components/category-selector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { ChevronDown } from 'lucide-react'

import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon'
import { Button } from '@/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from '@/components/ui/command'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import { Category } from '@prisma/client'
import { useState } from 'react'

type Props = {
categories: Category[]
onValueChange: (categoryId: Category['id']) => void
defaultValue: Category['id']
}

export function CategorySelector({
categories,
onValueChange,
defaultValue,
}: Props) {
const [open, setOpen] = useState(false)
const [value, setValue] = useState<number>(defaultValue)

const categoriesByGroup = categories.reduce<Record<string, Category[]>>(
(acc, category) => ({
...acc,
[category.grouping]: [...(acc[category.grouping] ?? []), category],
}),
{},
)

const selectedCategory =
categories.find((category) => category.id === value) ?? categories[0]

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="flex w-full justify-between"
>
<div className="flex items-center gap-3">
<CategoryIcon category={selectedCategory} className="w-4 h-4" />
{selectedCategory.name}
</div>
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<Command>
<CommandInput placeholder="Search category..." />
<CommandEmpty>No category found.</CommandEmpty>
<div className="w-full max-h-[300px] overflow-y-auto">
{Object.entries(categoriesByGroup).map(
([group, groupCategories], index) => (
<CommandGroup key={index} heading={group}>
{groupCategories.map((category) => (
<CommandItem
key={category.id}
value={`${category.id} ${category.grouping} ${category.name}`}
onSelect={(currentValue) => {
const id = Number(currentValue.split(' ')[0])
setValue(id)
onValueChange(id)
setOpen(false)
}}
>
<div className="flex items-center gap-3">
<CategoryIcon category={category} className="w-4 h-4" />
{category.name}
</div>
</CommandItem>
))}
</CommandGroup>
),
)}
</div>
</Command>
</PopoverContent>
</Popover>
)
}
Loading

0 comments on commit 92156b2

Please sign in to comment.