Skip to content

Commit

Permalink
fixed annoying dialog and context menu open bug (shadcn-ui/ui#1859)
Browse files Browse the repository at this point in the history
  • Loading branch information
towelie committed Dec 17, 2024
1 parent ec1745c commit 2891d40
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 56 deletions.
1 change: 0 additions & 1 deletion src/actions/item-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export async function createItemType(typeData: Omit<ItemType, 'id' | 'created_at
}

export async function getItemTypes(): DBResult<ItemType[]> {
await new Promise((resolve) => setTimeout(resolve, 5000))
return (await createClient())
.from('types')
.select(`*`)
Expand Down
15 changes: 15 additions & 0 deletions src/actions/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,25 @@ export async function createItem(itemData: Omit<Item, 'id' | 'created_at' | 'las
return result
}

export async function updateItem(itemData: Omit<Item, 'created_at' | 'last_bought'>, revalidatePaths: RevalidationPaths): DBResult<Item> {
const supabase = await createClient()

const result = await supabase
.from('item')
.update(itemData)
.eq('id', itemData.id)
.single();

revalidateAll(revalidatePaths)

return result
}

export async function getItems(): DBResult<Item[]> {
return (await createClient())
.from('item')
.select(`*`)
.order('name', { ascending: true })
}


1 change: 0 additions & 1 deletion src/actions/stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export async function createStore(storeData: Omit<Store, 'id' | 'created_at'>, r
}

export async function getStores(): DBResult<Store[]> {
await new Promise((resolve) => setTimeout(resolve, 5000))
return (await createClient())
.from('stores')
.select(`*`)
Expand Down
1 change: 1 addition & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@tailwind utilities;

body {
pointer-events: auto !important;
font-family: Arial, Helvetica, sans-serif;
}

Expand Down
45 changes: 45 additions & 0 deletions src/components/DialogProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { createContext, useContext, useState, ReactNode } from 'react'
import { Dialog, DialogContent } from "./ui/dialog"

interface DialogContextType {
openDialog: (content: ReactNode) => void
closeDialog: () => void
}

const DialogContext = createContext<DialogContextType | undefined>(undefined)

export function useDialog() {
const context = useContext(DialogContext)
if (!context) {
throw new Error('useDialog must be used within a DialogProvider')
}
return context
}

export function DialogProvider({ children }: { children: ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
const [dialogContent, setDialogContent] = useState<ReactNode | null>(null)

const openDialog = (content: ReactNode) => {
setDialogContent(content)
setIsOpen(true)
}

const closeDialog = () => {
setIsOpen(false)
setDialogContent(null)
}

return (
<DialogContext.Provider value={{ openDialog, closeDialog }}>
{children}
<Dialog open={isOpen} onOpenChange={(open) => !open && closeDialog()}>
<DialogContent>
{dialogContent}
</DialogContent>
</Dialog>
</DialogContext.Provider>
)
}


115 changes: 99 additions & 16 deletions src/components/ItemList.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,110 @@
"use client"

import { Item } from "@/lib/supabase/complex_types"
import { Card, CardHeader } from "./ui/card"
import { EllipsisVertical } from "lucide-react"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./ui/card"
import { MoreHorizontal } from 'lucide-react'
import { Button } from "./ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { useCallback, useState } from "react"
import EditItemForm from "./forms/EditItemForm"
import { DialogProvider, useDialog } from "./DialogProvider"

interface ItemListProps {
items: Item[]
}

function ItemListContent({ items }: ItemListProps) {
const [open, setOpen] = useState(false)
const [openId, setOpenId] = useState("")
const { closeDialog } = useDialog()
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [editingItem, setEditingItem] = useState<Item | null>(null);

const handleEditClick = useCallback((item: Item) => {
setEditingItem(item);
setIsDialogOpen(true);
}, []);

const handleEditClose = useCallback(() => {
setIsDialogOpen(false);
setEditingItem(null);
closeDialog();
}, [closeDialog]);

export default function ItemList({ items }: { items: Item[] }) {
return (
<div className="grid grid-cols-6 gap-5">
{
items.map((item: Item) => (
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{items.map((item) => (
<Card key={item.id}>
<CardHeader>
<div className="flex flex-row gap-3 items-center">
<h1 className="flex-1">
{item.name}
</h1>
<EllipsisVertical />
</div>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
{item.name}
</CardTitle>
<DropdownMenu
open={item.id === openId && open}
onOpenChange={(isOpen) => {
setOpen(isOpen)
setOpenId(item.id)
}}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => {
setOpen(false)
}} onSelect={() => handleEditClick(item)}>
Edit
</DropdownMenuItem>
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardHeader>
<CardContent>
<CardDescription>Type: {item.type_id}</CardDescription>
<CardDescription>Store: {item.store_id}</CardDescription>
</CardContent>
<CardFooter>
<CardDescription>Favorite: {item.is_favorite ? 'Yes' : 'No'}</CardDescription>
</CardFooter>
</Card>
))
}
</div>
))}
</div>
<Dialog open={isDialogOpen} onOpenChange={(open) => {
if (!open) {
handleEditClose()
}
}}>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Item</DialogTitle>
</DialogHeader>
{editingItem && (
<EditItemForm
item={editingItem}
onClose={handleEditClose}
/>
)}
</DialogContent>
</Dialog>
</>
)
}

export default function ItemList(props: ItemListProps) {
return (
<DialogProvider>
<ItemListContent {...props} />
</DialogProvider>
)
}


28 changes: 18 additions & 10 deletions src/components/SearchableSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@ import {
} from "@/components/ui/popover"
import { useSearchableSelect } from "@/hooks/use-searchable-select"

interface SupabaseItem {
id: string
name: string
[key: string]: any // Allow for other properties
}

interface SearchableSelectProps {
options: Record<string, string>
items: SupabaseItem[]
onChange: (value: string) => void
placeholder?: string
initialValue?: string
}

export function SearchableSelect({
options,
items,
onChange,
placeholder = "Select an item...",
initialValue,
}: SearchableSelectProps) {
const {
open,
Expand All @@ -35,8 +43,8 @@ export function SearchableSelect({
setValue,
search,
setSearch,
filteredOptions,
} = useSearchableSelect(options)
filteredItems,
} = useSearchableSelect(items, initialValue)

return (
<Popover open={open} onOpenChange={setOpen}>
Expand All @@ -47,7 +55,7 @@ export function SearchableSelect({
aria-expanded={open}
className="w-full justify-between"
>
{value ? options[value] : placeholder}
{value ? items.find(item => item.id === value)?.name : placeholder}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
Expand All @@ -60,10 +68,10 @@ export function SearchableSelect({
/>
<CommandEmpty>No item found.</CommandEmpty>
<CommandGroup>
{filteredOptions.map(([key, label]) => (
{filteredItems.map((item) => (
<CommandItem
key={key}
value={key}
key={item.id}
value={item.id}
onSelect={(currentValue) => {
setValue(currentValue === value ? "" : currentValue)
onChange(currentValue)
Expand All @@ -73,10 +81,10 @@ export function SearchableSelect({
<Check
className={cn(
"mr-2 h-4 w-4",
value === key ? "opacity-100" : "opacity-0"
value === item.id ? "opacity-100" : "opacity-0"
)}
/>
{label}
{item.name}
</CommandItem>
))}
</CommandGroup>
Expand Down
Loading

0 comments on commit 2891d40

Please sign in to comment.