Reputation: 1
This is my UI I have a Command Input inside the Popover for MultiSelect but I am not able to click on the Input and type anything. It treats the CommandInput as if its not even there. I added pointer-events-auto and did a console.log when clicked on CommandInput so it was printing but still I am not able to type in the Input and search.
Here is my Sheet component and I am using my own MultiSelect component.
I have also attached my MultiSelect component.
import * as React from "react";
import { cva } from "class-variance-authority";
import { CheckIcon, XCircle, ChevronDown, XIcon, WandSparkles } from "lucide-react";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
const multiSelectVariants = cva("m-1 transition ease-in-out duration-300", {
variants: {
variant: {
default: "border-foreground/10 text-foreground bg-card hover:bg-card/80",
secondary: "border-foreground/10 bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
inverted: "inverted",
},
},
defaultVariants: {
variant: "default",
},
});
export const MultiSelect = React.forwardRef(
(
{
options,
onValueChange,
variant,
defaultValue = [],
placeholder = "Select options",
animation = 0,
maxCount = 3,
asChild = false,
className,
...props
},
ref
) => {
const [selectedValues, setSelectedValues] = React.useState(defaultValue);
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
const [isAnimating, setIsAnimating] = React.useState(false);
const [triggerWidth, setTriggerWidth] = React.useState(0);
const triggerRef = React.useRef(null);
React.useEffect(() => {
if (triggerRef.current) {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
setTriggerWidth(entry.contentRect.width);
}
});
resizeObserver.observe(triggerRef.current);
return () => {
resizeObserver.disconnect();
};
}
}, [triggerRef]);
React.useEffect(() => {
if (JSON.stringify(selectedValues) !== JSON.stringify(defaultValue)) {
setSelectedValues(defaultValue);
}
}, [defaultValue, selectedValues]);
const handleInputKeyDown = (event) => {
if (event.key === "Enter") {
setIsPopoverOpen(true);
} else if (event.key === "Backspace" && !event.currentTarget.value) {
const newSelectedValues = [...selectedValues];
newSelectedValues.pop();
setSelectedValues(newSelectedValues);
onValueChange(newSelectedValues);
}
};
const toggleOption = (value) => {
const newSelectedValues = selectedValues.includes(value)
? selectedValues.filter((v) => v !== value)
: [...selectedValues, value];
setSelectedValues(newSelectedValues);
onValueChange(newSelectedValues);
};
const handleClear = () => {
setSelectedValues([]);
onValueChange([]);
};
const handleTogglePopover = () => {
setIsPopoverOpen((prev) => !prev);
};
const clearExtraOptions = () => {
const newSelectedValues = selectedValues.slice(0, maxCount);
setSelectedValues(newSelectedValues);
onValueChange(newSelectedValues);
};
return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<PopoverTrigger asChild>
<Button
ref={triggerRef}
{...props}
onClick={handleTogglePopover}
className={cn(
"flex w-full p-1 rounded-md border min-h-9 h-auto items-center justify-between bg-inherit hover:bg-inherit",
className
)}
>
{selectedValues.length > 0 ? (
<div className="flex justify-between items-center w-full">
<div className="flex flex-wrap items-center">
{selectedValues.slice(0, maxCount).map((value) => {
const option = options.find((o) => o.value === value);
const IconComponent = option?.icon;
return (
<Badge
key={value}
className={cn(
isAnimating ? "animate-bounce" : "",
multiSelectVariants({ variant, className })
)}
style={{ animationDuration: `${animation}s` }}
>
{IconComponent && <IconComponent className="h-4 w-4 mr-2" />}
{option?.label}
<XCircle
className="ml-2 h-4 w-4 cursor-pointer"
onClick={(event) => {
event.stopPropagation();
toggleOption(value);
}}
/>
</Badge>
);
})}
{selectedValues.length > maxCount && (
<Badge
className={cn(
"bg-transparent text-foreground border-foreground/1 hover:bg-transparent",
isAnimating ? "animate-bounce" : "",
multiSelectVariants({ variant, className })
)}
style={{ animationDuration: `${animation}s` }}
>
{`+ ${selectedValues.length - maxCount} more`}
<XCircle
className="ml-2 h-4 w-4 cursor-pointer"
onClick={(event) => {
event.stopPropagation();
clearExtraOptions();
}}
/>
</Badge>
)}
</div>
<div className="flex items-center justify-between">
<XIcon
className="h-4 mx-2 cursor-pointer text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
handleClear();
}}
/>
<Separator orientation="vertical" className="flex min-h-6 h-full" />
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" />
</div>
</div>
) : (
<div className="flex items-center justify-between w-full mx-auto">
<span className="text-sm text-muted-foreground mx-3">{placeholder}</span>
<ChevronDown className="h-4 cursor-pointer text-muted-foreground mx-2" />
</div>
)}
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
align="start"
onEscapeKeyDown={() => setIsPopoverOpen(false)}
style={{ width: `${triggerWidth + 10}px`, maxWidth: "92vw" }}
>
<Command>
<CommandInput placeholder="Search..." onKeyDown={handleInputKeyDown} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
<CommandItem
onSelect={() => {
const allValues = options.map((option) => option.value);
const allSelected = allValues.every((value) => selectedValues.includes(value));
setSelectedValues(allSelected ? [] : allValues);
onValueChange(allSelected ? [] : allValues);
}}
style={{ pointerEvents: "auto", opacity: 1 }}
className="cursor-pointer"
>
<div
className={cn(
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
options.map((option) => option.value).every((value) => selectedValues.includes(value))
? "bg-primary text-primary-foreground"
: "opacity-50 [&_svg]:invisible"
)}
>
<CheckIcon className="h-4 w-4" />
</div>
<span>Select All</span>
</CommandItem>
{options.map((option) => {
const isSelected = selectedValues.includes(option.value);
return (
<CommandItem
key={option.value}
onSelect={() => toggleOption(option.value)}
style={{ pointerEvents: "auto", opacity: 1 }}
className="cursor-pointer"
>
<div
className={cn(
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"
)}
>
<CheckIcon className="h-4 w-4" />
</div>
{option.icon && <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />}
<span>{option.label}</span>
</CommandItem>
);
})}
</CommandGroup>
<CommandSeparator />
<CommandGroup>
<div className="flex items-center justify-between">
{selectedValues.length > 0 && (
<>
<CommandItem
onSelect={handleClear}
style={{ pointerEvents: "auto", opacity: 1 }}
className="flex-1 justify-center cursor-pointer"
>
Clear
</CommandItem>
<Separator orientation="vertical" className="flex min-h-6 h-full" />
</>
)}
<CommandSeparator />
<CommandItem
onSelect={() => setIsPopoverOpen(false)}
style={{ pointerEvents: "auto", opacity: 1 }}
className="flex-1 justify-center cursor-pointer"
>
Close
</CommandItem>
</div>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
{animation > 0 && selectedValues.length > 0 && (
<WandSparkles
className={cn(
"cursor-pointer my-2 text-foreground bg-background w-3 h-3",
isAnimating ? "" : "text-muted-foreground"
)}
onClick={() => setIsAnimating(!isAnimating)}
/>
)}
</Popover>
);
}
);
MultiSelect.displayName = "MultiSelect";
import React, { useState } from "react";
import { Sheet, SheetContent, SheetHeader, SheetFooter, SheetTitle } from "@/components/ui/sheet";
import { MultiSelect } from "@/components/ui/multi-select";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { saveApprovalRequest } from "@/utils/api/client/orders/create/approval/api";
import MultiSelectInSheet from "./MultiSelectInSheet";
const ApprovalSheet = ({ open, approvalData, setOpen }) => {
const [selectedUsers, setSelectedUsers] = useState([]);
const userDetails = approvalData?.[0]?.permission_user_details || [];
const userOptions = userDetails.map((user) => ({
value: user.user_id,
label: user.user_name,
}));
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetContent className="w-[50vw]">
<SheetHeader>
<SheetTitle>Manage Approvals</SheetTitle>
</SheetHeader>
<div className="flex flex-col gap-2 my-4">
<Label>Users</Label>
<div>
<MultiSelect
options={userOptions}
onValueChange={setSelectedUsers}
defaultValue={selectedUsers}
placeholder="Select Users"
variant="inverted"
className="shadow-sm overflow-auto"
disabled={userOptions.length === 0}
/>
</div>
</div>
<div className="flex justify-end ">
<Button onClick={() => setOpen(false)}>Request for approval</Button>
</div>
</SheetContent>
</Sheet>
);
};
export default ApprovalSheet;
I have already tried creating a new MultiSelect and tried using "pointer-events-auto". Also when I try clicking on the part of Input below which there is a button it seems that the button gets clicked. I have also tried z-indexing them both.
Upvotes: 0
Views: 267
Reputation: 1
I found a solution. The issue is because I have used a DropdownMenu inside a DropdownMenu. I redirect to a new URL when user clicks on the inner DropdownMenu item and close the outer DropdownMenu. In that case, the outer dropdown closes but the inner is still open somewhere in the html. So I am assuming that was the issue.
I have used DropdownMenuSub instead of DropdownMenu and it works.
Upvotes: 0