1+ 'use client'
2+
3+ import { useState , useRef , useEffect } from 'react'
4+ import { cn } from '@/lib/utils'
5+ import { Github , X , Send } from 'lucide-react'
6+
7+ interface FloatingNotificationProps {
8+ className ?: string
9+ title ?: string
10+ links ?: {
11+ github ?: string
12+ telegram ?: string
13+ close ?: ( ) => void
14+ }
15+ }
16+
17+ export function FloatingNotification ( {
18+ className,
19+ title = 'Learn more about this template:' ,
20+ links = { } ,
21+ } : FloatingNotificationProps ) {
22+ const [ isCollapsed , setIsCollapsed ] = useState ( false )
23+ const [ position , setPosition ] = useState ( { x : 20 , y : 20 } )
24+ const [ isDragging , setIsDragging ] = useState ( false )
25+ const [ dragOffset , setDragOffset ] = useState ( { x : 0 , y : 0 } )
26+ const containerRef = useRef < HTMLDivElement > ( null )
27+
28+ const handleMouseDown = ( e : React . MouseEvent ) => {
29+ if ( containerRef . current ) {
30+ const rect = containerRef . current . getBoundingClientRect ( )
31+ setDragOffset ( {
32+ x : e . clientX - rect . left ,
33+ y : e . clientY - rect . top ,
34+ } )
35+ setIsDragging ( true )
36+ }
37+ }
38+
39+ useEffect ( ( ) => {
40+ const handleMouseMove = ( e : MouseEvent ) => {
41+ if ( isDragging ) {
42+ setPosition ( {
43+ x : e . clientX - dragOffset . x ,
44+ y : e . clientY - dragOffset . y ,
45+ } )
46+ }
47+ }
48+
49+ const handleMouseUp = ( ) => {
50+ setIsDragging ( false )
51+ }
52+
53+ if ( isDragging ) {
54+ document . addEventListener ( 'mousemove' , handleMouseMove )
55+ document . addEventListener ( 'mouseup' , handleMouseUp )
56+ }
57+
58+ return ( ) => {
59+ document . removeEventListener ( 'mousemove' , handleMouseMove )
60+ document . removeEventListener ( 'mouseup' , handleMouseUp )
61+ }
62+ } , [ isDragging , dragOffset ] )
63+
64+ return (
65+ < div
66+ ref = { containerRef }
67+ className = { cn (
68+ 'fixed flex items-center gap-6 bg-[#262626] shadow-lg border-l-[3px] border-l-[#27F795] pl-4' ,
69+ isCollapsed ? 'w-auto' : 'w-[500px]' ,
70+ className
71+ ) }
72+ style = { {
73+ left : `${ position . x } px` ,
74+ top : `${ position . y } px` ,
75+ cursor : isDragging ? 'grabbing' : 'grab' ,
76+ } }
77+ onMouseDown = { handleMouseDown }
78+ >
79+ < div className = "flex items-center gap-6" >
80+ < div className = "text-sm text-muted-foreground" > ⓘ</ div >
81+ { ! isCollapsed && (
82+ < div className = "text-sm text-muted-foreground !-ml-2" > { title } </ div >
83+ ) }
84+ </ div >
85+
86+ { ! isCollapsed && (
87+ < div className = "flex items-center gap-2" >
88+ { links . telegram && (
89+ < a
90+ href = { links . telegram }
91+ target = "_blank"
92+ rel = "noopener noreferrer"
93+ className = "rounded-md p-2 text-muted-foreground transition-colors hover:bg-transparent hover:text-[var(--accent)] active:text-white"
94+ >
95+ < Send className = "h-4 w-4" />
96+ </ a >
97+ ) }
98+ { links . github && (
99+ < a
100+ href = { links . github }
101+ target = "_blank"
102+ rel = "noopener noreferrer"
103+ className = "rounded-md p-2 text-muted-foreground transition-colors hover:bg-transparent hover:text-[var(--accent)] active:text-white"
104+ >
105+ < Github className = "h-4 w-4" />
106+ </ a >
107+ ) }
108+ < button
109+ onClick = { ( ) => setIsCollapsed ( ! isCollapsed ) }
110+ className = "floating-notification-button text-muted-foreground transition-colors hover:bg-[#27F795] hover:text-[#262626] active:bg-[#267A52] active:border-none active:text-[#FFFFFF]"
111+ >
112+ { isCollapsed ? '+' : < X className = "h-4 w-4" /> }
113+ </ button >
114+ </ div >
115+ ) }
116+ { isCollapsed && (
117+ < button
118+ onClick = { ( ) => setIsCollapsed ( ! isCollapsed ) }
119+ className = "floating-notification-button text-muted-foreground transition-colors hover:bg-[#27F795] hover:text-[#262626] active:bg-[#267A52] active:border-none active:text-[#FFFFFF]"
120+ >
121+ < div className = "h-4 w-4 flex items-center justify-center" > +</ div >
122+ </ button >
123+ ) }
124+ </ div >
125+ )
126+ }
0 commit comments