Skip to content

Commit

Permalink
Search for brains through the dropdown (QuivrHQ#507)
Browse files Browse the repository at this point in the history
  • Loading branch information
adityanandanx authored Jul 5, 2023
1 parent e931d29 commit 02272ab
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 147 deletions.
2 changes: 1 addition & 1 deletion frontend/lib/components/NavBar/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const Header = ({
y: hidden ? "-100%" : "0%",
transition: { ease: "circOut" },
}}
className="sticky top-0 w-full border-b border-b-black/10 dark:border-b-white/25 bg-white dark:bg-black z-[1200]"
className="sticky top-0 w-full border-b border-b-black/10 dark:border-b-white/25 bg-white dark:bg-black z-20"
>
<nav className="max-w-screen-xl mx-auto py-1 flex items-center justify-between gap-8">
{children}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { FormEvent, useState } from "react";
import { MdAdd } from "react-icons/md";

import Button from "@/lib/components/ui/Button";
import Field from "@/lib/components/ui/Field";
import Modal from "@/lib/components/ui/Modal";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";

const AddBrainModal = (): JSX.Element => {
const [newBrainName, setNewBrainName] = useState("");
const [isPending, setIsPending] = useState(false);

const { createBrain } = useBrainContext();

const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (newBrainName.trim() === "" || isPending) {
return;
}
setIsPending(true);
await createBrain(newBrainName);
setNewBrainName("");
setIsPending(false);
};

return (
<Modal
Trigger={
<Button variant={"secondary"}>
Add New Brain
<MdAdd className="text-xl" />
</Button>
}
title="Add Brain"
desc="Add a new brain"
>
<form
onSubmit={(e) => void handleSubmit(e)}
className="my-10 flex items-center gap-2"
>
<Field
name="brainname"
label="Enter a brain name"
autoFocus
placeholder="E.g. History notes"
autoComplete="off"
value={newBrainName}
onChange={(e) => setNewBrainName(e.currentTarget.value)}
className="flex-1"
/>
<Button isLoading={isPending} className="self-end" type="submit">
Create
<MdAdd className="text-xl" />
</Button>
</form>
</Modal>
);
};

export { AddBrainModal };
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useState } from "react";
import { FaBrain } from "react-icons/fa";
import { MdCheck, MdDelete } from "react-icons/md";

import Button from "@/lib/components/ui/Button";
import Field from "@/lib/components/ui/Field";
import Popover from "@/lib/components/ui/Popover";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";

import { AddBrainModal } from "./AddBrainModal";

export const BrainsDropDown = (): JSX.Element => {
const [searchQuery, setSearchQuery] = useState("");
const { allBrains, setActiveBrain, currentBrain, deleteBrain } =
useBrainContext();

return (
<>
{/* Add the brain icon and dropdown */}
<div className="relative ml-auto px-4 py-2">
<Popover
Trigger={
<button
type="button"
className="flex items-center focus:outline-none"
>
<FaBrain className="w-6 h-6" />
</button>
}
ActionTrigger={<AddBrainModal />}
CloseTrigger={false}
>
<div>
<Field
name="brainsearch"
placeholder="Search for a brain"
autoFocus
autoComplete="off"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
<div className="overflow-auto scrollbar flex flex-col h-48 mt-5">
{/* List of brains */}
{allBrains.map((brain) => {
if (brain.name.includes(searchQuery)) {
return (
<div
key={brain.id}
className="relative flex group items-center"
>
<button
type="button"
className={`flex flex-1 items-center gap-2 w-full text-left px-4 py-2 text-sm leading-5 text-gray-900 dark:text-gray-300 group-hover:bg-gray-100 dark:group-hover:bg-gray-700 group-focus:bg-gray-100 dark:group-focus:bg-gray-700 group-focus:outline-none transition-colors`}
onClick={() => {
setActiveBrain({ ...brain });
setSearchQuery("");
}}
>
<span>
<MdCheck
style={{
opacity: currentBrain?.id === brain.id ? 1 : 0,
}}
className="text-xl transition-opacity"
width={32}
height={32}
/>
</span>
<span className="flex-1">{brain.name}</span>
</button>
<Button
className="group-hover:visible invisible absolute right-0 hover:text-red-500 transition-[colors,opacity]"
onClick={() => void deleteBrain(brain.id)}
variant={"tertiary"}
>
<MdDelete className="text-xl" />
</Button>
</div>
);
}
})}
</div>
</div>
</Popover>
</div>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { AddBrainModal } from "./AddBrainModal";
import { BrainsDropDown } from "./BrainsDropDown";

export { BrainsDropDown, AddBrainModal };
71 changes: 71 additions & 0 deletions frontend/lib/components/ui/Popover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use client";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import { AnimatePresence, motion } from "framer-motion";
import { ReactNode, useState } from "react";

import Button from "./Button";

interface PopoverProps {
children?: ReactNode;
Trigger: ReactNode;
ActionTrigger?: ReactNode;
CloseTrigger?: ReactNode;
}

const Popover = ({
children,
Trigger,
ActionTrigger,
CloseTrigger,
}: PopoverProps): JSX.Element => {
const [open, setOpen] = useState(false);

return (
<PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
<PopoverPrimitive.Trigger asChild>{Trigger}</PopoverPrimitive.Trigger>
<AnimatePresence>
{open && (
<PopoverPrimitive.Portal forceMount>
<PopoverPrimitive.Content forceMount asChild sideOffset={5}>
<motion.div
initial={{ opacity: 0, y: -32 }}
animate={{
opacity: 1,
y: 0,
}}
exit={{ opacity: 0, y: -32 }}
transition={{ duration: 0.2, ease: "easeInOut" }}
className="relative flex flex-col p-4 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg shadow-lg z-40"
>
<div className="flex-1">{children}</div>
<div className="mt-4 self-end flex gap-4">
{ActionTrigger !== undefined && (
<PopoverPrimitive.Close asChild>
{ActionTrigger}
</PopoverPrimitive.Close>
)}
<PopoverPrimitive.Close asChild>
{CloseTrigger === undefined ? (
<Button
variant={"secondary"}
className="px-3 py-2"
aria-label="Close"
>
Close
</Button>
) : (
CloseTrigger
)}
</PopoverPrimitive.Close>
</div>
<PopoverPrimitive.Arrow className="fill-white stroke-gray-300 stroke-2" />
</motion.div>
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
)}
</AnimatePresence>
</PopoverPrimitive.Root>
);
};

export default Popover;
Loading

0 comments on commit 02272ab

Please sign in to comment.