summaryrefslogtreecommitdiff
path: root/apps/web/src/components/ui/input-with-back.tsx
diff options
context:
space:
mode:
authorenkei64 <imenkei64@gmail.com>2025-08-15 10:11:21 +1000
committerGitHub <noreply@github.com>2025-08-15 02:11:21 +0200
commitc3f3345d7b1ac820b411a5adbca3172378642953 (patch)
tree6d80ca38eb908574ab97a2b1e1d30166a72b8b76 /apps/web/src/components/ui/input-with-back.tsx
parentdefff2fc46978072f2ead822cf8bbd34f7629192 (diff)
downloadopencut-c3f3345d7b1ac820b411a5adbca3172378642953.tar.gz
feat: stickers panel (#539)
* Stickers panel base complete * Improve dark mode stickers background for visibility * Prevent stickers from being too small or too big * Improve UI, added credit for Iconify API * Allow manually loading more collections * Add a maximum width of 200px so stickers aren't too big * cleanup * style: hover state of button * refactor: input component * fix: mark input component as client * so much stuff --------- Co-authored-by: Maze Winther <mazewinther@gmail.com>
Diffstat (limited to 'apps/web/src/components/ui/input-with-back.tsx')
-rw-r--r--apps/web/src/components/ui/input-with-back.tsx86
1 files changed, 86 insertions, 0 deletions
diff --git a/apps/web/src/components/ui/input-with-back.tsx b/apps/web/src/components/ui/input-with-back.tsx
new file mode 100644
index 00000000..ea225c14
--- /dev/null
+++ b/apps/web/src/components/ui/input-with-back.tsx
@@ -0,0 +1,86 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { ArrowLeft, Search } from "lucide-react";
+import { motion } from "motion/react";
+import { useState, useEffect } from "react";
+
+interface InputWithBackProps {
+ isExpanded: boolean;
+ setIsExpanded: (isExpanded: boolean) => void;
+ placeholder?: string;
+ value?: string;
+ onChange?: (value: string) => void;
+}
+
+export function InputWithBack({
+ isExpanded,
+ setIsExpanded,
+ placeholder = "Search anything",
+ value,
+ onChange,
+}: InputWithBackProps) {
+ const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null);
+ const [buttonOffset, setButtonOffset] = useState(-60);
+
+ const smoothTransition = {
+ duration: 0.35,
+ ease: [0.25, 0.1, 0.25, 1] as const,
+ };
+
+ useEffect(() => {
+ if (containerRef) {
+ const rect = containerRef.getBoundingClientRect();
+ setButtonOffset(-rect.left - 48);
+ }
+ }, [containerRef]);
+
+ return (
+ <div ref={setContainerRef} className="relative w-full">
+ <motion.div
+ className="absolute left-0 top-1/2 -translate-y-1/2 cursor-pointer hover:opacity-75 transition-opacity z-10"
+ initial={{
+ x: isExpanded ? 0 : buttonOffset,
+ opacity: isExpanded ? 1 : 0.5,
+ }}
+ animate={{
+ x: isExpanded ? 0 : buttonOffset,
+ opacity: isExpanded ? 1 : 0.5,
+ }}
+ transition={smoothTransition}
+ onClick={() => setIsExpanded(!isExpanded)}
+ >
+ <Button
+ variant="outline"
+ className="!size-9 rounded-full bg-panel-accent"
+ >
+ <ArrowLeft />
+ </Button>
+ </motion.div>
+ <div
+ className="relative flex-1"
+ style={{ marginLeft: "0px", paddingLeft: "0px" }}
+ >
+ <motion.div
+ className="relative"
+ initial={{
+ marginLeft: isExpanded ? 50 : 0,
+ }}
+ animate={{
+ marginLeft: isExpanded ? 50 : 0,
+ }}
+ transition={smoothTransition}
+ >
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
+ <Input
+ placeholder={placeholder}
+ className="pl-9 bg-panel-accent w-full"
+ value={value}
+ onChange={(e) => onChange?.(e.target.value)}
+ />
+ </motion.div>
+ </div>
+ </div>
+ );
+}