apex-ui-kit 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/Licence.md +21 -0
  2. package/ReadMe.md +75 -0
  3. package/dist/ApexUI-Kit/templates/Avatar/Avatar.jsx +81 -0
  4. package/dist/ApexUI-Kit/templates/BeamBar/BeamBar.jsx +101 -0
  5. package/dist/ApexUI-Kit/templates/Cards/BasicCard.jsx +60 -0
  6. package/dist/ApexUI-Kit/templates/Carousel/Carousel.jsx +95 -0
  7. package/dist/ApexUI-Kit/templates/Carousel/DragCarousel.jsx +105 -0
  8. package/dist/ApexUI-Kit/templates/Carousel/ParallaxCarousel.jsx +161 -0
  9. package/dist/ApexUI-Kit/templates/DragCarousel/DragCarousel.jsx +105 -0
  10. package/dist/ApexUI-Kit/templates/EdgeGlowForm/EdgeGlowFrom.jsx +297 -0
  11. package/dist/ApexUI-Kit/templates/FlipProfile/FlipProfile.jsx +125 -0
  12. package/dist/ApexUI-Kit/templates/GlareCard/GlareCard.jsx +146 -0
  13. package/dist/ApexUI-Kit/templates/GlassCardStack/GlassCardStack.jsx +111 -0
  14. package/dist/ApexUI-Kit/templates/HoverText/HoverText.jsx +371 -0
  15. package/dist/ApexUI-Kit/templates/HyperCard/HyperCard.jsx +183 -0
  16. package/dist/ApexUI-Kit/templates/KineticThreadsBackground/KineticThreadBackground.jsx +249 -0
  17. package/dist/ApexUI-Kit/templates/LensFlareBackground/LensFlareBackground.jsx +268 -0
  18. package/dist/ApexUI-Kit/templates/LuminousParticleOcean/LuminousParticleOcean.jsx +155 -0
  19. package/dist/ApexUI-Kit/templates/NavDropDown/NavDropDown.jsx +114 -0
  20. package/dist/ApexUI-Kit/templates/NavMenu/NavMenu.jsx +153 -0
  21. package/dist/ApexUI-Kit/templates/OrbsBackground/FloatingOrbs.jsx +96 -0
  22. package/dist/ApexUI-Kit/templates/ParallaxCard/ParallaxCard.jsx +127 -0
  23. package/dist/ApexUI-Kit/templates/ParallaxCarousel/ParallaxCarousel.jsx +161 -0
  24. package/dist/ApexUI-Kit/templates/ParallaxProfile/ParallaxProfile.jsx +107 -0
  25. package/dist/ApexUI-Kit/templates/PointerFollower/PointerFollower.jsx +126 -0
  26. package/dist/ApexUI-Kit/templates/RainBackground/RainBackground.jsx +109 -0
  27. package/dist/ApexUI-Kit/templates/RippleBackground/RippleBackground.jsx +160 -0
  28. package/dist/ApexUI-Kit/templates/ScrollMarquee/ScrollMarquee.jsx +174 -0
  29. package/dist/ApexUI-Kit/templates/SmartGridCard/SmartGridCard.jsx +241 -0
  30. package/dist/ApexUI-Kit/templates/SmokeBackground/SmokeBackground.jsx +216 -0
  31. package/dist/ApexUI-Kit/templates/Testimonials/Testimonials.jsx +117 -0
  32. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeAnimation.js +252 -0
  33. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeConfig.js +8 -0
  34. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.css +560 -0
  35. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.jsx +118 -0
  36. package/dist/ApexUI-Kit/templates/ToolTip/ToolTip.jsx +85 -0
  37. package/dist/ApexUI-Kit/templates/WaterDropReveal/WaterDropReveal.jsx +151 -0
  38. package/dist/cli/index.d.ts +2 -0
  39. package/dist/cli/index.js +145 -0
  40. package/package.json +56 -0
@@ -0,0 +1,161 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import React, { useState, useEffect, useRef } from 'react';
3
+ import { motion, useMotionValue, useTransform } from 'framer-motion';
4
+ import { ArrowLeft, ArrowRight } from 'lucide-react';
5
+
6
+ // --- Card Component with Hover Parallax Effect ---
7
+ const CarouselCard = ({ slide, isCenter }) => {
8
+ const cardRef = useRef(null);
9
+ const x = useMotionValue(0);
10
+ const y = useMotionValue(0);
11
+
12
+ // Mouse move ke hisab se rotation calculate karta hai (effect ko strong banaya gaya hai)
13
+ const rotateX = useTransform(y, [-200, 200], [15, -15]);
14
+ const rotateY = useTransform(x, [-200, 200], [-15, 15]);
15
+ const handleMouseMove = (event) => {
16
+ if (!cardRef.current || !isCenter) return;
17
+ const rect = cardRef.current.getBoundingClientRect();
18
+ x.set(event.clientX - rect.left - rect.width / 2);
19
+ y.set(event.clientY - rect.top - rect.height / 2);
20
+ };
21
+
22
+ const handleMouseLeave = () => {
23
+ x.set(0);
24
+ y.set(0);
25
+ };
26
+
27
+ return (
28
+ <motion.div
29
+ ref={cardRef}
30
+ className="w-full h-full bg-slate-900 rounded-2xl group"
31
+ style={{
32
+ transformStyle: 'preserve-3d',
33
+ perspective: '1500px',
34
+ rotateX: isCenter ? rotateX : 0,
35
+ rotateY: isCenter ? rotateY : 0,
36
+ }}
37
+ onMouseMove={handleMouseMove}
38
+ onMouseLeave={handleMouseLeave}
39
+
40
+ >
41
+ {/* Slide Content */}
42
+ <div className="absolute inset-0 w-full h-full"
43
+ style={{
44
+ transformStyle: 'preserve-3d',
45
+ perspective: '1500px',
46
+ }}
47
+ >
48
+ <img
49
+ src={slide.imageUrl}
50
+ alt={slide.title}
51
+ className="w-full h-full object-cover group-hover:opacity-30 transition-all duration-300 ease-in-out rounded-2xl"
52
+ />
53
+ <img
54
+ src={slide.pngUrl}
55
+ alt={slide.title}
56
+ className="absolute inset-0 w-full h-full object-cover group-hover:translate-z-15 transition-all duration-300 ease-in-out rounded-2xl"
57
+ />
58
+ </div>
59
+ <div
60
+ className="relative flex flex-col justify-end h-full text-white p-8"
61
+ style={{
62
+ transformStyle: 'preserve-3d',
63
+ perspective: '1500px',
64
+ }}
65
+ >
66
+ <h2 className="text-3xl font-bold group-hover:translate-z-18 transition-all duration-300 ease-in-out">{slide.title}</h2>
67
+ <p className="text-lime-400 mt-1 group-hover:translate-z-18 transition-all duration-300 ease-in-out">{slide.subtitle}</p>
68
+ <p className="text-slate-300 mt-4 text-sm leading-relaxed group-hover:translate-z-18 transition-all duration-300 ease-in-out">{slide.text}</p>
69
+ </div>
70
+ </motion.div>
71
+ );
72
+ };
73
+
74
+
75
+ // --- Custom Parallax Carousel Component ---
76
+ const ParallaxCarousel = ({ slides }) => {
77
+ const [activeIndex, setActiveIndex] = useState(0);
78
+ const [isHovered, setIsHovered] = useState(false);
79
+
80
+ const handleNext = () => {
81
+ setActiveIndex((prevIndex) => (prevIndex + 1) % slides.length);
82
+ };
83
+
84
+ const handlePrev = () => {
85
+ setActiveIndex((prevIndex) => (prevIndex - 1 + slides.length) % slides.length);
86
+ };
87
+
88
+ // Automatic sliding ke liye
89
+ useEffect(() => {
90
+ // Agar hovered hai to interval start hi mat karo
91
+ if (isHovered) {
92
+ return;
93
+ }
94
+
95
+ const interval = setInterval(() => {
96
+ handleNext();
97
+ }, 4000); // Har 4 second mein slide change hoga
98
+
99
+ return () => clearInterval(interval); // Cleanup
100
+ }, [activeIndex, isHovered]); // isHovered state change hone par effect ko firse chalayega
101
+
102
+ return (
103
+ <div
104
+ className="relative w-full flex flex-col items-center px-2"
105
+ onMouseEnter={() => setIsHovered(true)}
106
+ onMouseLeave={() => setIsHovered(false)}
107
+ >
108
+ {/* 3D Carousel Container */}
109
+ <div
110
+ className="relative w-full h-[480px] portrait:h-[480px] portrait:px-0"
111
+ style={{ perspective: '1200px', transformStyle: 'preserve-3d' }}
112
+ >
113
+ {slides.map((slide, index) => {
114
+ let positionOffset = index - activeIndex;
115
+ if (positionOffset > slides.length / 2) {
116
+ positionOffset -= slides.length;
117
+ } else if (positionOffset < -slides.length / 2) {
118
+ positionOffset += slides.length;
119
+ }
120
+
121
+ const isVisible = Math.abs(positionOffset) <= 2;
122
+
123
+ return (
124
+ <motion.div
125
+ key={index}
126
+ className={`absolute w-full h-[340px] portrait:w-full portrait:h-[480px] top-0 left-0 portrait:left-0 portrait:top-0 ${positionOffset === 0 ? '' : ' pointer-events-none'} md:w-[50%] md:h-[400px] md:top-[50px] md:left-1/2 lg:-translate-x-1/2`}
127
+ initial={false}
128
+ style={{ transformStyle: 'preserve-3d' }}
129
+ animate={{
130
+ transform: `translateX(${positionOffset * 50}%) translateZ(${-Math.abs(positionOffset) * 250}px) scale(${1 - Math.abs(positionOffset) * 0.15})`,
131
+ opacity: isVisible ? (1 - Math.abs(positionOffset) * 0.45) : 0,
132
+ zIndex: slides.length - Math.abs(positionOffset),
133
+ }}
134
+ transition={{ type: 'spring', stiffness: 290, damping: 30 }}
135
+ >
136
+ <CarouselCard slide={slide} isCenter={positionOffset === 0} />
137
+ </motion.div>
138
+ );
139
+ })}
140
+ </div>
141
+
142
+ {/* Side Navigation Buttons */}
143
+ <motion.button
144
+ onClick={handlePrev}
145
+ className="absolute -left-10 lg:left-0 top-1/2 -translate-y-1/2 z-30 p-3 rounded-full text-slate-400 hover:text-lime-400 transition-colors"
146
+ whileTap={{ scale: 0.9 }}
147
+ >
148
+ <ArrowLeft size={32} />
149
+ </motion.button>
150
+ <motion.button
151
+ onClick={handleNext}
152
+ className="absolute -right-10 lg:right-0 top-1/2 -translate-y-1/2 z-30 p-3 rounded-full text-slate-400 hover:text-lime-400 transition-colors"
153
+ whileTap={{ scale: 0.9 }}
154
+ >
155
+ <ArrowRight size={32} />
156
+ </motion.button>
157
+ </div>
158
+ );
159
+ };
160
+
161
+ export default ParallaxCarousel;
@@ -0,0 +1,105 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+
3
+ // To use GSAP in this environment, we'll import it from a CDN.
4
+ // In a local project: `npm install gsap`
5
+ import { gsap } from 'gsap';
6
+ import { Draggable } from 'gsap/Draggable';
7
+
8
+ // GSAP Draggable plugin ko register karna zaroori hai
9
+ gsap.registerPlugin(Draggable);
10
+
11
+ // --- GSAP Drag Carousel Component ---
12
+ const DragCarousel = ({ images }) => {
13
+ const [activeIndex, setActiveIndex] = useState(0);
14
+ const carouselRef = useRef(null);
15
+ const cardsRef = useRef([]);
16
+ const dragProxyRef = useRef(null);
17
+ useEffect(() => {
18
+ const cards = cardsRef.current;
19
+ gsap.to(cards, {
20
+ duration: 0.4,
21
+ ease: 'power4.Out',
22
+ stagger: 0.05,
23
+ // Har card ke liye naya transform calculate karta hai
24
+ transform: (i) => {
25
+ let positionOffset = i - activeIndex;
26
+ if (positionOffset > images.length / 2) {
27
+ positionOffset -= images.length;
28
+ } else if (positionOffset < -images.length / 2) {
29
+ positionOffset += images.length;
30
+ }
31
+
32
+ const translateX = positionOffset * 50;
33
+ const translateZ = -Math.abs(positionOffset) * 250;
34
+ const scale = 1 - Math.abs(positionOffset) * 0.15;
35
+
36
+ return `translateX(${translateX}%) translateZ(${translateZ}px) scale(${scale})`;
37
+ },
38
+ zIndex: (i) => {
39
+ let positionOffset = i - activeIndex;
40
+ if (positionOffset > images.length / 2) positionOffset -= images.length;
41
+ if (positionOffset < -images.length / 2) positionOffset += images.length;
42
+ return images.length - Math.abs(positionOffset);
43
+ },
44
+ opacity: (i) => {
45
+ let positionOffset = i - activeIndex;
46
+ if (positionOffset > images.length / 2) positionOffset -= images.length;
47
+ if (positionOffset < -images.length / 2) positionOffset += images.length;
48
+ return Math.abs(positionOffset) > 2 ? 0 : 1;
49
+ }
50
+ });
51
+ }, [activeIndex, images.length]);
52
+
53
+ // GSAP Draggable ko setup karta hai
54
+ useEffect(() => {
55
+ // BUG FIX: Draggable ab proxy element par lagaya gaya hai
56
+ const draggableInstance = Draggable.create(dragProxyRef.current, {
57
+ type: "x",
58
+ trigger: carouselRef.current,
59
+ inertia: true,
60
+ cursor: 'grab',
61
+ activeCursor: 'grabbing',
62
+ onDragEnd: function () {
63
+ gsap.to(this.target, { x: 0, duration: 0.3 });
64
+ if (this.getDirection("start") === "left") {
65
+ setActiveIndex(prev => (prev + 1) % images.length);
66
+ } else if (this.getDirection("start") === "right") {
67
+ setActiveIndex(prev => (prev - 1 + images.length) % images.length);
68
+ }
69
+ }
70
+ });
71
+
72
+ // Cleanup function
73
+ return () => {
74
+ if (draggableInstance && draggableInstance[0]) {
75
+ draggableInstance[0].kill();
76
+ }
77
+ };
78
+ }, [images.length]);
79
+
80
+ return (
81
+ <div
82
+ ref={carouselRef}
83
+ className="relative w-full h-[490px] lg:h-[590px] cursor-grab"
84
+ style={{ perspective: '1200px', transformStyle: 'preserve-3d' }}
85
+ >
86
+ <div ref={dragProxyRef} className="absolute inset-0 z-20"></div>
87
+
88
+ {images.map((src, index) => (
89
+ <div
90
+ key={index}
91
+ ref={el => cardsRef.current[index] = el}
92
+ className="card-item absolute h-[480px] w-[300px] lg:h-[550px] lg:w-[350px] rounded-xl overflow-hidden shadow-2xl top-[25px] -left-4 md:left-36 lg:left-70"
93
+ >
94
+ <img
95
+ src={src}
96
+ alt={`carousel-item-${index}`}
97
+ className="w-full h-full object-cover pointer-events-none"
98
+ />
99
+ </div>
100
+ ))}
101
+ </div>
102
+ );
103
+ };
104
+
105
+ export default DragCarousel;
@@ -0,0 +1,297 @@
1
+ import React, { useRef, useState, useEffect } from 'react';
2
+ import gsap from 'gsap';
3
+
4
+ const fields = [
5
+ { name: 'firstName', label: 'First Name', type: 'text' },
6
+ { name: 'lastName', label: 'Last Name', type: 'text' },
7
+ { name: 'email', label: 'Email Address', type: 'email' },
8
+ { name: 'mobile', label: 'Mobile Number', type: 'tel' },
9
+ ];
10
+
11
+ const EdgeGlowForm = ({
12
+ GlowColor = 'violet',
13
+ borderGlowColor = '#a78bfa88',
14
+ borderGlowShadow = '#a78bfa33',
15
+ }) => {
16
+ const [values, setValues] = useState({ firstName: '', lastName: '', email: '', mobile: '' });
17
+ const [focused, setFocused] = useState('');
18
+ const [checked, setChecked] = useState(false);
19
+ const [btnState, setBtnState] = useState('default');
20
+ const [errors, setErrors] = useState({});
21
+ const [checkboxError, setCheckboxError] = useState(false);
22
+ const labelRefs = useRef({});
23
+ const borderRefs = useRef({});
24
+ const ballsRef = [useRef(), useRef(), useRef()];
25
+ const checkTick = useRef();
26
+
27
+ // Mouse movement for glassy background
28
+ const [radialPos, setRadialPos] = useState({ x: 50, y: 50 });
29
+ const bgRef = useRef();
30
+
31
+ const handleBgMouseMove = e => {
32
+ if (!bgRef.current) return;
33
+ const rect = bgRef.current.getBoundingClientRect();
34
+ const x = ((e.clientX - rect.left) / rect.width) * 100;
35
+ const y = ((e.clientY - rect.top) / rect.height) * 100;
36
+ setRadialPos({ x, y });
37
+ };
38
+ const handleBgMouseLeave = () => {
39
+ setRadialPos({ x: 50, y: 50 });
40
+ };
41
+
42
+ const floatLabel = (name, up, color = null) => {
43
+ const label = labelRefs.current[name];
44
+ if (label) {
45
+ gsap.to(label, {
46
+ top: up ? '-2px' : '23%',
47
+ left: '5px',
48
+ scale: up ? 0.85 : 1,
49
+ color: color ?? (up ? '#a78bfa' : '#bdbdbd'),
50
+ transform: 'translateY(-4%)',
51
+ fontWeight: up ? 500 : 400,
52
+ duration: 0.2,
53
+ ease: 'easeInOut',
54
+ zIndex: 10,
55
+ });
56
+ }
57
+ };
58
+
59
+ const showBorder = (name, show, color = '#a78bfa88') => {
60
+ const [top, bottom] = borderRefs.current[name] || [];
61
+ [top, bottom].forEach(border => {
62
+ if (border) {
63
+ gsap.to(border, {
64
+ opacity: show ? 1 : 0,
65
+ scaleX: show ? 1 : 0.7,
66
+ background: `linear-gradient(90deg, transparent 0%, ${color} 50%, transparent 100%)`,
67
+ boxShadow: `0 0 8px 0 ${color.replace('88', '33')}`,
68
+ duration: 0.2,
69
+ ease: show ? 'ease.out' : 'ease.in',
70
+ });
71
+ }
72
+ });
73
+ };
74
+
75
+ const handleFocus = name => {
76
+ setFocused(name);
77
+ floatLabel(name, true);
78
+ showBorder(name, true);
79
+ };
80
+
81
+ const handleBlur = name => {
82
+ setFocused('');
83
+ if (!values[name]) floatLabel(name, false);
84
+ showBorder(name, false);
85
+ };
86
+
87
+ const handleChange = (e, name) => {
88
+ const v = e.target.value;
89
+ setValues(vals => ({ ...vals, [name]: v }));
90
+ if (v) floatLabel(name, true);
91
+ else if (focused !== name) floatLabel(name, false);
92
+ };
93
+
94
+ useEffect(() => {
95
+ fields.forEach(f => {
96
+ const [top, bottom] = borderRefs.current[f.name] || [];
97
+ if (top) gsap.set(top, { opacity: 0, scaleX: 0.7 });
98
+ if (bottom) gsap.set(bottom, { opacity: 0, scaleX: 0.7 });
99
+ if (labelRefs.current[f.name]) gsap.set(labelRefs.current[f.name], {
100
+ top: '23%',
101
+ left: '5px',
102
+ scale: 1,
103
+ color: '#bdbdbd',
104
+ transform: 'translateY(-4%)',
105
+ fontWeight: 400,
106
+ position: 'absolute',
107
+ pointerEvents: 'none',
108
+ backgroundColor: '',
109
+ padding: '0 4px',
110
+ zIndex: 10,
111
+ });
112
+ });
113
+ }, []);
114
+
115
+ const handleCheckbox = () => {
116
+ setChecked(v => {
117
+ const next = !v;
118
+ if (checkTick.current) {
119
+ gsap.to(checkTick.current, {
120
+ scale: next ? 1 : 0,
121
+ opacity: next ? 1 : 0,
122
+ duration: 0.3,
123
+ ease: 'expo.out',
124
+ });
125
+ }
126
+ return next;
127
+ });
128
+ };
129
+
130
+ useEffect(() => {
131
+ if (checkTick.current) gsap.set(checkTick.current, { scale: checked ? 1 : 0, opacity: checked ? 1 : 0 });
132
+ }, [checked]);
133
+
134
+ const handleBtnClick = e => {
135
+ e.preventDefault();
136
+ const newErrors = {};
137
+ fields.forEach(f => {
138
+ if (!values[f.name]) newErrors[f.name] = true;
139
+ });
140
+ setErrors(newErrors);
141
+ Object.keys(newErrors).forEach(name => {
142
+ floatLabel(name, true, '#f87171');
143
+ showBorder(name, true, '#f87171cc');
144
+ });
145
+ if (!checked) {
146
+ setCheckboxError(true);
147
+ setTimeout(() => setCheckboxError(false), 2000);
148
+ }
149
+ if (Object.keys(newErrors).length > 0 || !checked) return;
150
+ if (btnState !== 'default') return;
151
+
152
+ setBtnState('loading');
153
+ ballsRef.forEach((ref, i) => {
154
+ gsap.fromTo(ref.current, { y: 0 }, {
155
+ y: -12,
156
+ repeat: -1,
157
+ yoyo: true,
158
+ delay: i * 0.12,
159
+ duration: 0.38,
160
+ ease: 'power1.inOut',
161
+ });
162
+ });
163
+ setTimeout(() => {
164
+ setBtnState('success');
165
+ ballsRef.forEach(ref => gsap.set(ref.current, { y: 0 }));
166
+ setTimeout(() => {
167
+ setBtnState('default');
168
+ }, 1200);
169
+ }, 1600);
170
+ };
171
+
172
+ return (
173
+
174
+ <div className=" rounded-xl w-full">
175
+ <div
176
+ className="w-full max-w-sm mx-auto rounded-xl shadow-2xl p-1 flex flex-col justify-center text-white relative overflow-hidden"
177
+ ref={bgRef}
178
+ onMouseMove={handleBgMouseMove}
179
+ onMouseLeave={handleBgMouseLeave}
180
+ >
181
+ {/* 🎯 Glassy Background */}
182
+ <div className="absolute inset-0 z-0 hidden md:block pointer-events-none rounded-xl">
183
+ <div
184
+ className="absolute inset-0 opacity-95"
185
+ style={{
186
+ background: `radial-gradient(circle at ${radialPos.x}% ${radialPos.y}%, ${GlowColor} 10%, transparent 60%)`
187
+ }}
188
+ ></div>
189
+ </div>
190
+ <div className={`relative z-10 bg-black h-full w-full p-6 border border-purple-600 rounded-xl`}>
191
+ <h2 className="text-2xl font-bold mb-6 flex items-center gap-2">
192
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor" className="w-6 h-6 text-purple-400">
193
+ <path strokeLinecap="round" strokeLinejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25H4.5a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5H4.5a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-.659 1.591l-7.5 7.5a2.25 2.25 0 01-3.182 0l-7.5-7.5A2.25 2.25 0 012.25 6.993V6.75" />
194
+ </svg>
195
+ Get In Touch
196
+ </h2>
197
+ <form className="space-y-6 w-full" onSubmit={handleBtnClick}>
198
+ {fields.map(f => (
199
+ <div key={f.name} className="relative flex flex-col justify-center">
200
+ <label
201
+ ref={el => (labelRefs.current[f.name] = el)}
202
+ className="absolute text-white/60 text-sm transition-all pointer-events-none"
203
+ >{f.label}</label>
204
+ <span
205
+ ref={el => {
206
+ if (!borderRefs.current[f.name]) borderRefs.current[f.name] = [];
207
+ borderRefs.current[f.name][0] = el;
208
+ }}
209
+ className="pointer-events-none absolute left-1/2 -translate-x-1/2 top-0 w-[80%] h-[2px] rounded-full opacity-0 scale-x-70 origin-center z-20"
210
+ style={{
211
+ background: `linear-gradient(90deg, transparent 0%, yellow 20%, transparent 100%)`,
212
+ boxShadow: `0 0 8px 0 ${borderGlowShadow}`,
213
+ }}
214
+ />
215
+ <input
216
+ type={f.type}
217
+ name={f.name}
218
+ value={values[f.name]}
219
+ onChange={e => handleChange(e, f.name)}
220
+ onFocus={() => handleFocus(f.name)}
221
+ onBlur={() => handleBlur(f.name)}
222
+ autoComplete="off"
223
+ className={`w-full px-4 pt-5 pb-2 rounded-lg bg-white/10 border ${errors[f.name] ? 'border-red-400' : 'border-white/20'} text-sm text-white focus:outline-none backdrop-blur-md transition font-light`}
224
+ />
225
+ <span
226
+ ref={el => {
227
+ if (!borderRefs.current[f.name]) borderRefs.current[f.name] = [];
228
+ borderRefs.current[f.name][1] = el;
229
+ }}
230
+ className="pointer-events-none absolute left-1/2 -translate-x-1/2 bottom-0 w-[80%] h-[2px] rounded-full opacity-0 scale-x-70 origin-center"
231
+ style={{
232
+ background: `linear-gradient(90deg, transparent 0%, ${borderGlowColor} 20%, transparent 100%)`,
233
+ boxShadow: `0 0 8px 0 ${borderGlowShadow}`,
234
+ }}
235
+ />
236
+ </div>
237
+ ))}
238
+
239
+ <div className="flex items-center gap-3 mt-2 select-none relative">
240
+ <button
241
+ type="button"
242
+ aria-checked={checked}
243
+ tabIndex={0}
244
+ onClick={handleCheckbox}
245
+ className={`relative w-6 h-6 rounded-md border-2 border-white/30 bg-white/10 flex items-center justify-center focus:outline-none focus:ring-2 focus:ring-purple-400 transition`}
246
+ style={{ minWidth: 24 }}
247
+ >
248
+ <svg
249
+ ref={checkTick}
250
+ width="18" height="18" viewBox="0 0 18 18"
251
+ className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-purple-400"
252
+ style={{ scale: checked ? 1 : 0, opacity: checked ? 1 : 0, transition: 'all 0.2s' }}
253
+ >
254
+ <polyline points="3,10 7,14 15,6" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
255
+ </svg>
256
+ </button>
257
+ <span className="text-white/80 text-sm">I agree to the terms</span>
258
+ {checkboxError && (
259
+ <div className="absolute -left-0 -top-10 flex flex-col items-center " style={{ zIndex: 999 }}>
260
+ <div className="bg-red-500 text-white text-xs rounded px-3 py-1 shadow-lg font-medium">check this box</div>
261
+ <div className="w-3 h-3 bg-red-500 rotate-135 -mt-1" style={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%)' }}></div>
262
+ </div>
263
+ )}
264
+ </div>
265
+ {/* Animated Submit Button */}
266
+ <button
267
+ type="submit"
268
+ className="mt-6 py-3 px-6 bg-gradient-to-r from-purple-700 via-purple-500 to-indigo-800 rounded-md hover:brightness-110 transition shadow-lg w-full font-semibold text-base flex items-center justify-center gap-2 relative overflow-hidden"
269
+ style={{ minHeight: 48 }}
270
+ onClick={handleBtnClick}
271
+ disabled={btnState !== 'default'}
272
+ >
273
+ {/* Bouncing Balls */}
274
+ <span className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex gap-1 ${btnState === 'loading' ? 'opacity-100' : 'opacity-0'}`} style={{ transition: 'opacity 0.2s' }}>
275
+ <span ref={ballsRef[0]} className="w-2.5 h-2.5 rounded-full bg-white/80 inline-block" />
276
+ <span ref={ballsRef[1]} className="w-2.5 h-2.5 rounded-full bg-white/80 inline-block" />
277
+ <span ref={ballsRef[2]} className="w-2.5 h-2.5 rounded-full bg-white/80 inline-block" />
278
+ </span>
279
+ {/* Checkmark */}
280
+ <span
281
+ className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-2xl text-green-400 ${btnState === 'success' ? 'opacity-100' : 'opacity-0'}`}
282
+ style={{ transition: 'opacity 0.2s' }}
283
+ >
284
+ <svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 13l4 4L19 7" /></svg>
285
+ </span>
286
+ {/* Default text */}
287
+ <span className={`transition-opacity duration-200 ${btnState === 'default' ? 'opacity-100' : 'opacity-0'}`}>Send Message</span>
288
+ </button>
289
+ </form>
290
+ </div>
291
+ </div>
292
+ </div>
293
+
294
+ );
295
+ }
296
+
297
+ export default EdgeGlowForm;