diff options
| author | codetorso <[email protected]> | 2024-07-18 23:14:48 +0530 |
|---|---|---|
| committer | codetorso <[email protected]> | 2024-07-18 23:14:48 +0530 |
| commit | ebf5b29cc7ab22e7d70574b1b2519df924878f85 (patch) | |
| tree | fd9b4bba0f067714322fa93a35fd35dc2d385315 | |
| parent | searchparams refactor (diff) | |
| download | supermemory-ebf5b29cc7ab22e7d70574b1b2519df924878f85.tar.xz supermemory-ebf5b29cc7ab22e7d70574b1b2519df924878f85.zip | |
improved textarea layout + home page imrpovemnts
| -rw-r--r-- | apps/web/app/(dash)/chat/chatWindow.tsx | 2 | ||||
| -rw-r--r-- | apps/web/app/(dash)/chat/queryinput.tsx | 83 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/filterSpaces.tsx | 110 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/heading.tsx | 62 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/headingVariants.ts | 50 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/queryinput.tsx | 45 | ||||
| -rw-r--r-- | packages/ui/icons/arrowright.svg | 4 | ||||
| -rw-r--r-- | packages/ui/shadcn/command.tsx | 2 | ||||
| -rw-r--r-- | packages/ui/shadcn/divider.tsx | 7 |
9 files changed, 185 insertions, 180 deletions
diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx index 3bc9fec6..066e7d20 100644 --- a/apps/web/app/(dash)/chat/chatWindow.tsx +++ b/apps/web/app/(dash)/chat/chatWindow.tsx @@ -2,7 +2,7 @@ import { AnimatePresence } from "framer-motion"; import React, { useEffect, useRef, useState } from "react"; -import QueryInput from "../home/queryinput"; +import QueryInput from "./queryinput"; import { cn } from "@repo/ui/lib/utils"; import { motion } from "framer-motion"; import { useRouter } from "next/navigation"; diff --git a/apps/web/app/(dash)/chat/queryinput.tsx b/apps/web/app/(dash)/chat/queryinput.tsx new file mode 100644 index 00000000..99f55986 --- /dev/null +++ b/apps/web/app/(dash)/chat/queryinput.tsx @@ -0,0 +1,83 @@ +"use client"; + +import { ArrowRightIcon } from "@repo/ui/icons"; +import Image from "next/image"; +import React, { useState } from "react"; + +function QueryInput({ + initialSpaces, + initialQuery = "", + disabled = false, + className, + mini = false, + handleSubmit, +}: { + initialQuery?: string; + initialSpaces?: { + id: number; + name: string; + }[]; + disabled?: boolean; + className?: string; + mini?: boolean; + handleSubmit: (q: string, spaces: { id: number; name: string }[]) => void; +}) { + const [q, setQ] = useState(initialQuery); + + const [selectedSpaces, setSelectedSpaces] = useState< + { id: number; name: string }[] + >([]); + + return ( + <div className={`${className}`}> + <div + className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} + > + {/* input and action button */} + <form + action={async () => { + if (q.trim().length === 0) { + return; + } + handleSubmit(q, selectedSpaces); + setQ(""); + }} + className="flex gap-4 p-3" + > + <textarea + autoFocus + name="q" + cols={30} + rows={mini ? 2 : 4} + className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4" + placeholder="Ask your second brain..." + onKeyDown={(e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + if (q.trim().length === 0) { + return; + } + handleSubmit(q, selectedSpaces); + setQ(""); + } + }} + onChange={(e) => setQ(e.target.value)} + value={q} + disabled={disabled} + /> + + <button + type="submit" + disabled={disabled} + className="h-12 w-12 rounded-[14px] bg-border all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90" + > + <Image src={ArrowRightIcon} alt="Right arrow icon" /> + </button> + </form>{" "} + </div> + {/* selected sources */} + </div> + ); +} + +export default QueryInput; diff --git a/apps/web/app/(dash)/home/filterSpaces.tsx b/apps/web/app/(dash)/home/filterSpaces.tsx index 6a8ad9ec..c7d1e2eb 100644 --- a/apps/web/app/(dash)/home/filterSpaces.tsx +++ b/apps/web/app/(dash)/home/filterSpaces.tsx @@ -1,4 +1,5 @@ import { ChevronUpDownIcon } from "@heroicons/react/24/outline"; +import { ArrowRightIcon } from "@repo/ui/icons"; import { Command, CommandGroup, @@ -7,6 +8,7 @@ import { CommandList, } from "@repo/ui/shadcn/command"; import { Check } from "lucide-react"; +import Image from "next/image"; import React, { useState } from "react"; type space = { @@ -17,11 +19,11 @@ type space = { export function FilterSpaces({ initialSpaces, selectedSpaces, - setSelectedSpaces + setSelectedSpaces, }: { initialSpaces: space[]; selectedSpaces: space[]; - setSelectedSpaces: React.Dispatch<React.SetStateAction<space[]>> + setSelectedSpaces: React.Dispatch<React.SetStateAction<space[]>>; }) { const [input, setInput] = useState<string>(""); @@ -35,55 +37,71 @@ export function FilterSpaces({ setSelectedSpaces((current) => current.some((space) => space.id === selectedSpace.id) ? current.filter((space) => space.id !== selectedSpace.id) - : [...current, selectedSpace] + : [...current, selectedSpace], ); }; return ( - <div className={`flex rounded-md overflow-hidden ${selectedSpaces.length ? "bg-[#2C3338]" : ""}`}> - <div className="flex rounded-lg items-center"> - {selectedSpaces.map((v) => ( - <button - key={v.id} - onClick={() => handleSelect(v)} - className="bg-[#3a4248] max-w-32 truncate-wor truncate whitespace-nowrap py-1 rounded-md px-2 mx-1 aria-selected:outline" - > - {v.name} - </button> - ))} - </div> - <Command className={`group border-0 bg-[rgb(44,51,56)] text-white outline-0 ${selectedSpaces.length ? "w-full" : "w-44"}`}> - <div className="relative"> - <CommandInput - placeholder={selectedSpaces.length ? "" : "Search in Spaces"} - onKeyDown={handleKeyDown} - className="text-white peer placeholder:text-white" - // @ts-ignore - trust me bro it works - onChange={(e) => setInput(e.currentTarget.value)} - value={input} - /> - <ChevronUpDownIcon - className={`h-6 w-6 text-[#858B92] absolute top-1/2 right-4 -translate-y-1/2 ${selectedSpaces.length && "opacity-0"}`} - /> + <div className="flex p-2 px-3 w-full items-center justify-between rounded-xl overflow-hidden"> + <div className="flex bg-[#2C3338] rounded-xl overflow-hidden pl-1"> + <div className="flex rounded-lg items-center"> + {selectedSpaces.map((v) => ( + <button + key={v.id} + onClick={() => handleSelect(v)} + className="bg-[#3a4248] max-w-32 truncate-wor truncate whitespace-nowrap py-1 rounded-md px-2 mx-1 aria-selected:outline" + > + {v.name} + </button> + ))} </div> - <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-b-xl max-w-64 bg-[#2C3338]"> - <CommandGroup className="hidden group-focus-within:block"> - {initialSpaces.map((space) => ( - <CommandItem - className="text-[#eaeaea] data-[disabled]:opacity-90" - value={space.name} - key={space.id} - onSelect={() => handleSelect(space)} - > - <Check - className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} - /> - {space.name} - </CommandItem> - ))} - </CommandGroup> - </CommandList> - </Command> + <Command + className={`group transition-all border-0 bg-[#2c3338] text-white outline-0 ${ + selectedSpaces.length ? "w-5 hover:w-24 focus-within:w-20" : "w-44" + }`} + > + <div className="relative"> + <CommandInput + placeholder={selectedSpaces.length ? "" : "Search in Spaces"} + onKeyDown={handleKeyDown} + className="text-white peer placeholder:text-white pl-2" + onChangeCapture={(e) => setInput(e.currentTarget.value)} + value={input} + /> + <ChevronUpDownIcon + className={`h-6 w-6 text-[#858B92] pointer-events-none absolute top-1/2 -translate-y-1/2 right-2 ${ + selectedSpaces.length && "opacity-0" + }`} + /> + </div> + <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-lg max-w-64 bg-[#2C3338]"> + <CommandGroup className="pointer-events-none opacity-0 group-focus-within:opacity-100 scale-50 scale-y-50 group-focus-within:scale-y-100 group-focus-within:scale-100 group-focus-within:pointer-events-auto transition-all origin-top"> + {initialSpaces.map((space) => ( + <CommandItem + className="text-[#eaeaea] data-[disabled]:opacity-90" + value={space.name} + key={space.id} + onSelect={() => handleSelect(space)} + > + <Check + className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} + /> + {space.name} + </CommandItem> + ))} + </CommandGroup> + </CommandList> + </Command> + </div> + {/* <button + type="submit" + className="h-12 w-12 rounded-[14px] all-center shrink-0 hover:brightness-125 outline-none bg-[#369DFD1A] p-3 active:scale-90" + > + <Image src={ArrowRightIcon} alt="Right arrow icon" /> + </button> */} + <button type="submit" className="rounded-lg bg-[#369DFD1A] p-3 transition-colors"> + <Image src={ArrowRightIcon} alt="Enter" /> + </button> </div> ); } diff --git a/apps/web/app/(dash)/home/heading.tsx b/apps/web/app/(dash)/home/heading.tsx index 1a120684..b61ac00b 100644 --- a/apps/web/app/(dash)/home/heading.tsx +++ b/apps/web/app/(dash)/home/heading.tsx @@ -1,48 +1,30 @@ import { useEffect, useState } from "react"; -import { headings } from "./headingVariants"; -import { motion } from "framer-motion"; +import { motion } from "framer-motion"; -const slap = { - initial: { - opacity: 0, - scale: 1.1, - }, - whileInView: { opacity: 1, scale: 1 }, - transition: { - duration: 0.5, - ease: "easeInOut", - }, - viewport: { once: true }, -}; +const headings = [ + "Unlock your digital brain", + "Save everything.", + " Connect anything.", + "Turn your bookmarks into insights.", + "The smart way to use your digital treasure.", +]; -export function Heading() { +export function Heading({ query = "" }: { query?: string }) { const [showHeading, setShowHeading] = useState<number>(0); - useEffect(()=> { + useEffect(() => { setShowHeading(Math.floor(Math.random() * headings.length)); - }) + }); return ( - <motion.h1 - {...{ - ...slap, - transition: { ...slap.transition, delay: 0.2 }, - }} - className="text-center mx-auto bg-[linear-gradient(180deg,_#FFF_0%,_rgba(255,_255,_255,_0.00)_202.08%)] bg-clip-text text-4xl tracking-tighter text-transparent md:text-5xl" - > - {headings[showHeading]!.map((v, i) => { - return ( - <span - key={i} - className={ - v.type === "highlighted" - ? "bg-gradient-to-r to-blue-200 from-zinc-300 text-transparent bg-clip-text" - : "" - } - > - {v.content} - </span> - ); - })} - </motion.h1> + <div className="h-[3.4rem] overflow-hidden text-white text-center"> + <motion.h1 + animate={{ opacity: query ? 0 : 1, y: query ? "20%" : 0 }} + className={`text-[2.45rem] font-semibold ${ + query ? "opacity-0 " : "opacity-100" + } transition-opacity`} + > + {headings[showHeading]} + </motion.h1> + </div> ); -}
\ No newline at end of file +} diff --git a/apps/web/app/(dash)/home/headingVariants.ts b/apps/web/app/(dash)/home/headingVariants.ts deleted file mode 100644 index 578b87c4..00000000 --- a/apps/web/app/(dash)/home/headingVariants.ts +++ /dev/null @@ -1,50 +0,0 @@ -export const headings = [ - [ - { - type: "text", - content: "Unlock your", - }, - { - type: "highlighted", - content: " digital brain", - }, - ], - [ - { - type: "text", - content: "Save", - }, - { - type: "highlighted", - content: " everything.", - }, - { - type: "text", - content: " Connect", - }, - { - type: "highlighted", - content: " anything.", - }, - ], - [ - { - type: "text", - content: "Turn your bookmarks into", - }, - { - type: "highlighted", - content: " insights.", - }, - ], - [ - { - type: "text", - content: "The smart way to use your", - }, - { - type: "highlighted", - content: " digital treasure.", - }, - ], -]; diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 225b5039..91f79e5f 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -1,30 +1,22 @@ "use client"; -import { ArrowRightIcon } from "@repo/ui/icons"; -import Image from "next/image"; import React, { useState } from "react"; -import Divider from "@repo/ui/shadcn/divider"; import { FilterSpaces } from "./filterSpaces"; function QueryInput({ initialSpaces, - initialQuery = "", - disabled = false, className, - mini = false, handleSubmit, }: { - initialQuery?: string; initialSpaces?: { id: number; name: string; }[]; - disabled?: boolean; className?: string; mini?: boolean; handleSubmit: (q: string, spaces: { id: number; name: string }[]) => void; }) { - const [q, setQ] = useState(initialQuery); + const [q, setQ] = useState(""); const [selectedSpaces, setSelectedSpaces] = useState< { id: number; name: string }[] @@ -33,7 +25,7 @@ function QueryInput({ return ( <div className={`${className}`}> <div - className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} + className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} > {/* input and action button */} <form @@ -44,14 +36,14 @@ function QueryInput({ handleSubmit(q, selectedSpaces); setQ(""); }} - className="flex gap-4 p-3" + className="" > <textarea autoFocus name="q" cols={30} - rows={mini ? 2 : 4} - className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] focus:text-gray-200 duration-200 tracking-[3%] outline-none resize-none w-full p-4" + rows={4} + className="bg-transparent pt-2.5 text-lg placeholder:text-[#9B9B9B] text-gray-200 tracking-[3%] outline-none resize-none w-full p-4" placeholder="Ask your second brain..." onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { @@ -65,29 +57,14 @@ function QueryInput({ }} onChange={(e) => setQ(e.target.value)} value={q} - disabled={disabled} /> - - <button - type="submit" - disabled={disabled} - className="h-12 w-12 rounded-[14px] bg-border all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90" - > - <Image src={ArrowRightIcon} alt="Right arrow icon" /> - </button> - </form>{" "} - {!mini && ( - <> - <Divider /> - <FilterSpaces - selectedSpaces={selectedSpaces} - setSelectedSpaces={setSelectedSpaces} - initialSpaces={initialSpaces || []} - /> - </> - )} + <FilterSpaces + selectedSpaces={selectedSpaces} + setSelectedSpaces={setSelectedSpaces} + initialSpaces={initialSpaces || []} + /> + </form> </div> - {/* selected sources */} </div> ); } diff --git a/packages/ui/icons/arrowright.svg b/packages/ui/icons/arrowright.svg index 2263251c..13d24bf5 100644 --- a/packages/ui/icons/arrowright.svg +++ b/packages/ui/icons/arrowright.svg @@ -1 +1,3 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 20 20"><path stroke="#989EA4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.25 16.25L17.5 10M17.5 10L11.25 3.75M17.5 10H2.5"/></svg>
\ No newline at end of file +<svg width="18" height="14" viewBox="0 0 18 14" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M10.25 13.25L16.5 7M16.5 7L10.25 0.75M16.5 7H1.5" stroke="#369DFD" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/packages/ui/shadcn/command.tsx b/packages/ui/shadcn/command.tsx index 94c4434f..9b95f4c7 100644 --- a/packages/ui/shadcn/command.tsx +++ b/packages/ui/shadcn/command.tsx @@ -38,7 +38,7 @@ const CommandInput = React.forwardRef< React.ElementRef<typeof CommandPrimitive.Input>, React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> >(({ className , ...props }, ref) => ( - <div className="flex items-center px-3" cmdk-input-wrapper=""> + <div className="" cmdk-input-wrapper=""> <CommandPrimitive.Input ref={ref} className={cn( diff --git a/packages/ui/shadcn/divider.tsx b/packages/ui/shadcn/divider.tsx deleted file mode 100644 index a6dc9933..00000000 --- a/packages/ui/shadcn/divider.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { cn } from "@repo/ui/lib/utils"; - -function Divider({ className }: { className?: string }) { - return <div className={cn("bg-[#2D343A] h-[1px] w-full", className)}></div>; -} - -export default Divider; |