summaryrefslogtreecommitdiff
path: root/apps/web/src/components/ui
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components/ui')
-rw-r--r--apps/web/src/components/ui/button.tsx2
-rw-r--r--apps/web/src/components/ui/draggable-item.tsx27
-rw-r--r--apps/web/src/components/ui/input-with-back.tsx86
-rw-r--r--apps/web/src/components/ui/input.tsx12
4 files changed, 110 insertions, 17 deletions
diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx
index 5558eee0..9096e415 100644
--- a/apps/web/src/components/ui/button.tsx
+++ b/apps/web/src/components/ui/button.tsx
@@ -18,7 +18,7 @@ const buttonVariants = cva(
destructive:
"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
outline:
- "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
+ "border border-input bg-background shadow-xs hover:opacity-75 transition-opacity hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
text: "bg-transparent p-0 rounded-none opacity-100 hover:opacity-50 transition-opacity", // Instead of ghost (matches app better)
diff --git a/apps/web/src/components/ui/draggable-item.tsx b/apps/web/src/components/ui/draggable-item.tsx
index ff6766fd..92aa677b 100644
--- a/apps/web/src/components/ui/draggable-item.tsx
+++ b/apps/web/src/components/ui/draggable-item.tsx
@@ -25,6 +25,7 @@ export interface DraggableMediaItemProps {
showLabel?: boolean;
rounded?: boolean;
variant?: "card" | "compact";
+ isDraggable?: boolean;
}
export function DraggableMediaItem({
@@ -39,11 +40,14 @@ export function DraggableMediaItem({
showLabel = true,
rounded = true,
variant = "card",
+ isDraggable = true,
}: DraggableMediaItemProps) {
const [isDragging, setIsDragging] = useState(false);
const [dragPosition, setDragPosition] = useState({ x: 0, y: 0 });
const dragRef = useRef<HTMLDivElement>(null);
- const currentTime = usePlaybackStore((state) => state.currentTime);
+ const currentTime = isDraggable
+ ? usePlaybackStore((state) => state.currentTime)
+ : 0;
const handleAddToTimeline = () => {
onAddToTimeline?.(currentTime);
@@ -100,11 +104,11 @@ export function DraggableMediaItem({
className={cn(
"bg-panel-accent relative overflow-hidden",
rounded && "rounded-md",
- "[&::-webkit-drag-ghost]:opacity-0" // Webkit-specific ghost hiding
+ isDraggable && "[&::-webkit-drag-ghost]:opacity-0" // Webkit-specific ghost hiding
)}
- draggable={true}
- onDragStart={handleDragStart}
- onDragEnd={handleDragEnd}
+ draggable={isDraggable}
+ onDragStart={isDraggable ? handleDragStart : undefined}
+ onDragEnd={isDraggable ? handleDragEnd : undefined}
>
{preview}
{!isDragging && (
@@ -132,12 +136,12 @@ export function DraggableMediaItem({
<div
className={cn(
"h-10 flex items-center gap-3 cursor-default w-full",
- "[&::-webkit-drag-ghost]:opacity-0",
+ isDraggable && "[&::-webkit-drag-ghost]:opacity-0",
className
)}
- draggable={true}
- onDragStart={handleDragStart}
- onDragEnd={handleDragEnd}
+ draggable={isDraggable}
+ onDragStart={isDraggable ? handleDragStart : undefined}
+ onDragEnd={isDraggable ? handleDragEnd : undefined}
>
<div className="w-6 h-6 flex-shrink-0 rounded overflow-hidden">
{preview}
@@ -148,7 +152,8 @@ export function DraggableMediaItem({
)}
{/* Custom drag preview */}
- {isDragging &&
+ {isDraggable &&
+ isDragging &&
typeof document !== "undefined" &&
createPortal(
<div
@@ -194,7 +199,7 @@ function PlusButton({
<Button
size="icon"
className={cn(
- "absolute bottom-2 right-2 size-4 bg-background text-foreground",
+ "absolute bottom-2 right-2 size-4 bg-background hover:bg-panel text-foreground",
className
)}
onClick={(e) => {
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>
+ );
+}
diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx
index 65c7660d..8f195a50 100644
--- a/apps/web/src/components/ui/input.tsx
+++ b/apps/web/src/components/ui/input.tsx
@@ -1,10 +1,12 @@
-import * as React from "react";
-import { Eye, EyeOff, X } from "lucide-react";
+"use client";
+import { Eye, EyeOff, X } from "lucide-react";
import { cn } from "../../lib/utils";
import { Button } from "./button";
+import { forwardRef, ComponentProps } from "react";
+import { useState } from "react";
-interface InputProps extends React.ComponentProps<"input"> {
+interface InputProps extends ComponentProps<"input"> {
showPassword?: boolean;
onShowPasswordChange?: (show: boolean) => void;
showClearIcon?: boolean;
@@ -12,7 +14,7 @@ interface InputProps extends React.ComponentProps<"input"> {
containerClassName?: string;
}
-const Input = React.forwardRef<HTMLInputElement, InputProps>(
+const Input = forwardRef<HTMLInputElement, InputProps>(
(
{
className,
@@ -29,7 +31,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
},
ref
) => {
- const [isFocused, setIsFocused] = React.useState(false);
+ const [isFocused, setIsFocused] = useState(false);
const isPassword = type === "password";
const showPasswordToggle = isPassword && onShowPasswordChange;