apex-ui-kit 2.0.5 → 2.0.7
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.
- package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.css +1 -0
- package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.jsx +140 -0
- package/dist/ApexUI-Kit/templates/BluePrintBackground/BluePrintBackground.jsx +133 -0
- package/dist/ApexUI-Kit/templates/CardStack/CardStack.jsx +101 -0
- package/dist/ApexUI-Kit/templates/Carousel/ParallaxCarousel.jsx +0 -161
- package/dist/ApexUI-Kit/templates/DragCarousel/DragCarousel.jsx +38 -6
- package/dist/ApexUI-Kit/templates/MeteorCard/MeteorCard.css +95 -0
- package/dist/ApexUI-Kit/templates/MeteorCard/MeteorCard.jsx +93 -0
- package/dist/ApexUI-Kit/templates/ParallaxCarousel/ParallaxCarousel.jsx +54 -61
- package/dist/ApexUI-Kit/templates/ParallaxProfile/ParallaxProfile.jsx +30 -19
- package/dist/ApexUI-Kit/templates/PointerFollower/PointerFollower.jsx +27 -7
- package/dist/ApexUI-Kit/templates/ScrollMarquee/CardMarquee.jsx +0 -0
- package/dist/ApexUI-Kit/templates/SmokeBackground/SmokeBackground.jsx +23 -15
- package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.css +4 -0
- package/dist/cli/index.js +4 -1
- package/package.json +1 -1
- package/dist/ApexUI-Kit/templates/GlassCardStack/GlassCardStack.jsx +0 -111
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.meteor-bg {
|
|
2
|
+
position: absolute;
|
|
3
|
+
width: 2px;
|
|
4
|
+
height: 80px;
|
|
5
|
+
background: linear-gradient(to top, #fff, transparent); /* This line to change the meteor's tail color */
|
|
6
|
+
animation: meteor-fall-bg linear infinite;
|
|
7
|
+
opacity: 0;
|
|
8
|
+
transform: rotate(-45deg);
|
|
9
|
+
}
|
|
10
|
+
/* Meteor ka head (glowing circle) */
|
|
11
|
+
.meteor-bg::before {
|
|
12
|
+
content: "";
|
|
13
|
+
position: absolute;
|
|
14
|
+
bottom: -3px;
|
|
15
|
+
left: 50%;
|
|
16
|
+
transform: translateX(-50%);
|
|
17
|
+
width: 6px;
|
|
18
|
+
height: 6px;
|
|
19
|
+
background-color: #fff; /* This line to change the meteor's head color */
|
|
20
|
+
border-radius: 50%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@keyframes meteor-fall-bg {
|
|
24
|
+
0% {
|
|
25
|
+
transform: translateX(-200px) translateY(-200px) rotate(-45deg);
|
|
26
|
+
opacity: 1;
|
|
27
|
+
}
|
|
28
|
+
100% {
|
|
29
|
+
transform: translateX(500px) translateY(500px) rotate(-45deg);
|
|
30
|
+
opacity: 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Particle Styles */
|
|
35
|
+
.particle-bg {
|
|
36
|
+
position: absolute;
|
|
37
|
+
width: 3px;
|
|
38
|
+
height: 3px;
|
|
39
|
+
background-color: var(--particle-color);
|
|
40
|
+
border-radius: 50%;
|
|
41
|
+
box-shadow: 0 0 8px var(--particle-color), 0 0 12px var(--particle-color);
|
|
42
|
+
animation: particle-float linear infinite;
|
|
43
|
+
opacity: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@keyframes particle-float {
|
|
47
|
+
0%,
|
|
48
|
+
100% {
|
|
49
|
+
opacity: 0;
|
|
50
|
+
transform: translateY(0px);
|
|
51
|
+
}
|
|
52
|
+
50% {
|
|
53
|
+
opacity: 1;
|
|
54
|
+
transform: translateY(-20px);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Moving Border */
|
|
59
|
+
.moving-border-card {
|
|
60
|
+
position: relative;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.moving-border-card::before {
|
|
65
|
+
content: "";
|
|
66
|
+
position: absolute;
|
|
67
|
+
top: -50%;
|
|
68
|
+
left: -50%;
|
|
69
|
+
width: 200%;
|
|
70
|
+
height: 200%;
|
|
71
|
+
background: conic-gradient(
|
|
72
|
+
from var(--angle),
|
|
73
|
+
transparent 0deg 90deg,
|
|
74
|
+
rgb(0, 132, 209, 0.5) 180deg,
|
|
75
|
+
/* Sky Blue */ transparent 180deg 270deg,
|
|
76
|
+
rgb(0, 132, 209, 0.5) 360deg
|
|
77
|
+
);
|
|
78
|
+
animation: rotate 6s linear infinite;
|
|
79
|
+
z-index: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@property --angle {
|
|
83
|
+
syntax: "<angle>";
|
|
84
|
+
initial-value: 0deg;
|
|
85
|
+
inherits: false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@keyframes rotate {
|
|
89
|
+
from {
|
|
90
|
+
--angle: 0deg;
|
|
91
|
+
}
|
|
92
|
+
to {
|
|
93
|
+
--angle: 360deg;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import './MeteorCard.css';
|
|
3
|
+
const ArrowRightIcon = () => (
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
|
5
|
+
viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
6
|
+
strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
|
7
|
+
className="ml-2 h-5 w-5 transition-transform duration-300 ease-in-out group-hover:translate-x-1">
|
|
8
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
9
|
+
<polyline points="12 5 19 12 12 19" />
|
|
10
|
+
</svg>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// Meteor Effect Component
|
|
14
|
+
const Meteors = ({ number = 20 }) => {
|
|
15
|
+
const meteors = [...Array(number)].map((_, i) => (
|
|
16
|
+
<span
|
|
17
|
+
key={i}
|
|
18
|
+
className="meteor-bg"
|
|
19
|
+
style={{
|
|
20
|
+
top: `${Math.random() * 100}%`,
|
|
21
|
+
left: `${Math.random() * 100}%`,
|
|
22
|
+
animationDelay: `${Math.random() * 8}s`,
|
|
23
|
+
animationDuration: `${Math.floor(Math.random() * 6) + 4}s`,
|
|
24
|
+
}}
|
|
25
|
+
></span>
|
|
26
|
+
));
|
|
27
|
+
return <>{meteors}</>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Particle Effect Component
|
|
31
|
+
const Particles = ({ number = 50, color = '#ffffff', speed = 1 }) => {
|
|
32
|
+
const particles = [...Array(number)].map((_, i) => {
|
|
33
|
+
const animationDuration = (Math.random() * 5 + 3) / speed;
|
|
34
|
+
return (
|
|
35
|
+
<span
|
|
36
|
+
key={i}
|
|
37
|
+
className="particle-bg"
|
|
38
|
+
style={{
|
|
39
|
+
'--particle-color': color,
|
|
40
|
+
top: `${Math.random() * 100}%`,
|
|
41
|
+
left: `${Math.random() * 100}%`,
|
|
42
|
+
animationDelay: `${Math.random() * animationDuration}s`,
|
|
43
|
+
animationDuration: `${animationDuration}s`,
|
|
44
|
+
}}
|
|
45
|
+
></span>
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
return <>{particles}</>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Main MeteorCard Component
|
|
52
|
+
const MeteorCard = ({ data, particleNumber, particleColor, particleSpeed }) => {
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
{data.map((item, index) => (
|
|
56
|
+
<div key={index} className="relative w-full lg:w-[30vw] max-w-4xl mx-auto bg-slate-800/80 p-0.5 rounded-lg moving-border-card">
|
|
57
|
+
{/* Card ka content container */}
|
|
58
|
+
<div className="relative bg-slate-900 rounded-lg p-4 md:p-8 flex flex-col md:flex-row items-center gap-6 md:gap-8 overflow-hidden">
|
|
59
|
+
{/* Background Effects */}
|
|
60
|
+
<div className="absolute inset-0 w-full h-full z-0">
|
|
61
|
+
<Meteors number={5} />
|
|
62
|
+
<Particles
|
|
63
|
+
number={particleNumber}
|
|
64
|
+
color={particleColor}
|
|
65
|
+
speed={particleSpeed}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Spotlight Effect (hide on small screens to reduce visual noise) */}
|
|
70
|
+
<div className="hidden md:block absolute -top-1/3 -left-1/3 w-2/3 h-2/3 bg-blue-400/90 rounded-full blur-[120px] pointer-events-none z-0"></div>
|
|
71
|
+
|
|
72
|
+
{/* Left Side: Image */}
|
|
73
|
+
<div className="relative w-full md:w-1/2 h-64 md:h-full rounded-md overflow-hidden z-10">
|
|
74
|
+
<img src={item.imageUrl} alt={item.title} className="w-full h-full object-cover lg:aspect-square" />
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
{/* Right Side: Content */}
|
|
78
|
+
<div className="relative w-full md:w-2/3 text-center md:text-left z-10">
|
|
79
|
+
<h3 className="text-3xl font-bold mb-2 text-white">{item.title}</h3>
|
|
80
|
+
<p className="text-gray-400 mb-6">{item.description}</p>
|
|
81
|
+
<button className="group relative inline-flex md:inline-flex w-full md:w-auto items-center justify-center px-6 py-2 bg-lime-600 text-white font-semibold rounded-md overflow-hidden transition-all duration-300 hover:bg-lime-700">
|
|
82
|
+
<span className="mx-auto">{item.buttonText}</span>
|
|
83
|
+
<ArrowRightIcon />
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
))}
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default MeteorCard;
|
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
import React, { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { motion, useMotionValue, useTransform } from 'framer-motion';
|
|
4
4
|
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
5
|
-
|
|
6
|
-
// --- Card Component with Hover Parallax Effect ---
|
|
7
|
-
const CarouselCard = ({ slide, isCenter }) => {
|
|
5
|
+
const CarouselCard = ({ slide, isCenter, isScrolling }) => {
|
|
8
6
|
const cardRef = useRef(null);
|
|
9
7
|
const x = useMotionValue(0);
|
|
10
8
|
const y = useMotionValue(0);
|
|
11
9
|
|
|
12
|
-
// Mouse move ke hisab se rotation calculate karta hai (effect ko strong banaya gaya hai)
|
|
13
10
|
const rotateX = useTransform(y, [-200, 200], [15, -15]);
|
|
14
11
|
const rotateY = useTransform(x, [-200, 200], [-15, 15]);
|
|
12
|
+
|
|
15
13
|
const handleMouseMove = (event) => {
|
|
16
|
-
if (!cardRef.current || !isCenter) return;
|
|
14
|
+
if (!cardRef.current || !isCenter || isScrolling) return;
|
|
17
15
|
const rect = cardRef.current.getBoundingClientRect();
|
|
18
16
|
x.set(event.clientX - rect.left - rect.width / 2);
|
|
19
17
|
y.set(event.clientY - rect.top - rect.height / 2);
|
|
@@ -24,6 +22,13 @@ const CarouselCard = ({ slide, isCenter }) => {
|
|
|
24
22
|
y.set(0);
|
|
25
23
|
};
|
|
26
24
|
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (isScrolling) {
|
|
27
|
+
x.set(0);
|
|
28
|
+
y.set(0);
|
|
29
|
+
}
|
|
30
|
+
}, [isScrolling, x, y]);
|
|
31
|
+
|
|
27
32
|
return (
|
|
28
33
|
<motion.div
|
|
29
34
|
ref={cardRef}
|
|
@@ -36,33 +41,13 @@ const CarouselCard = ({ slide, isCenter }) => {
|
|
|
36
41
|
}}
|
|
37
42
|
onMouseMove={handleMouseMove}
|
|
38
43
|
onMouseLeave={handleMouseLeave}
|
|
39
|
-
|
|
40
44
|
>
|
|
41
45
|
{/* Slide Content */}
|
|
42
|
-
<div className="absolute inset-0 w-full h-full"
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
/>
|
|
46
|
+
<div className="absolute inset-0 w-full h-full" style={{ transformStyle: 'preserve-3d', perspective: '1500px' }}>
|
|
47
|
+
<img src={slide.imageUrl} alt={slide.title} className="w-full h-full object-cover group-hover:opacity-30 transition-all duration-300 ease-in-out rounded-2xl" />
|
|
48
|
+
<img src={slide.pngUrl} alt={slide.title} className="absolute inset-0 w-full h-full object-cover group-hover:translate-z-15 transition-all duration-300 ease-in-out rounded-2xl" />
|
|
58
49
|
</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
|
-
>
|
|
50
|
+
<div className="relative flex flex-col justify-end h-full text-white p-8" style={{ transformStyle: 'preserve-3d', perspective: '1500px' }}>
|
|
66
51
|
<h2 className="text-3xl font-bold group-hover:translate-z-18 transition-all duration-300 ease-in-out">{slide.title}</h2>
|
|
67
52
|
<p className="text-lime-400 mt-1 group-hover:translate-z-18 transition-all duration-300 ease-in-out">{slide.subtitle}</p>
|
|
68
53
|
<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>
|
|
@@ -75,37 +60,54 @@ const CarouselCard = ({ slide, isCenter }) => {
|
|
|
75
60
|
// --- Custom Parallax Carousel Component ---
|
|
76
61
|
const ParallaxCarousel = ({ slides }) => {
|
|
77
62
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
78
|
-
const
|
|
63
|
+
const animatingRef = useRef(false);
|
|
64
|
+
const animTimeoutRef = useRef(null);
|
|
79
65
|
|
|
66
|
+
const [isScrolling, setIsScrolling] = useState(false);
|
|
67
|
+
const scrollTimeoutRef = useRef(null);
|
|
68
|
+
|
|
69
|
+
// ✅ REMOVED: isHovered state hata diya gaya hai.
|
|
70
|
+
|
|
71
|
+
const NAV_ANIM_MS = 600;
|
|
80
72
|
const handleNext = () => {
|
|
73
|
+
if (animatingRef.current) return;
|
|
74
|
+
animatingRef.current = true;
|
|
75
|
+
clearTimeout(animTimeoutRef.current);
|
|
81
76
|
setActiveIndex((prevIndex) => (prevIndex + 1) % slides.length);
|
|
77
|
+
animTimeoutRef.current = setTimeout(() => {
|
|
78
|
+
animatingRef.current = false;
|
|
79
|
+
}, NAV_ANIM_MS);
|
|
82
80
|
};
|
|
83
81
|
|
|
84
82
|
const handlePrev = () => {
|
|
83
|
+
if (animatingRef.current) return;
|
|
84
|
+
animatingRef.current = true;
|
|
85
|
+
clearTimeout(animTimeoutRef.current);
|
|
85
86
|
setActiveIndex((prevIndex) => (prevIndex - 1 + slides.length) % slides.length);
|
|
87
|
+
animTimeoutRef.current = setTimeout(() => {
|
|
88
|
+
animatingRef.current = false;
|
|
89
|
+
}, NAV_ANIM_MS);
|
|
86
90
|
};
|
|
87
|
-
|
|
88
|
-
// Automatic sliding ke liye
|
|
89
91
|
useEffect(() => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
const handleScroll = () => {
|
|
93
|
+
setIsScrolling(true);
|
|
94
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
95
|
+
scrollTimeoutRef.current = setTimeout(() => {
|
|
96
|
+
setIsScrolling(false);
|
|
97
|
+
}, 150);
|
|
98
|
+
};
|
|
94
99
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
window.addEventListener('scroll', handleScroll, { passive: true });
|
|
101
|
+
|
|
102
|
+
return () => {
|
|
103
|
+
window.removeEventListener('scroll', handleScroll);
|
|
104
|
+
clearTimeout(scrollTimeoutRef.current);
|
|
105
|
+
};
|
|
106
|
+
}, []);
|
|
98
107
|
|
|
99
|
-
return () => clearInterval(interval); // Cleanup
|
|
100
|
-
}, [activeIndex, isHovered]); // isHovered state change hone par effect ko firse chalayega
|
|
101
108
|
|
|
102
109
|
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 */}
|
|
110
|
+
<div className="relative w-full flex flex-col items-center px-2">
|
|
109
111
|
<div
|
|
110
112
|
className="relative w-full h-[480px] portrait:h-[480px] portrait:px-0"
|
|
111
113
|
style={{ perspective: '1200px', transformStyle: 'preserve-3d' }}
|
|
@@ -125,33 +127,24 @@ const ParallaxCarousel = ({ slides }) => {
|
|
|
125
127
|
key={index}
|
|
126
128
|
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
129
|
initial={false}
|
|
128
|
-
style={{ transformStyle: 'preserve-3d' }}
|
|
130
|
+
style={{ transformStyle: 'preserve-3d', willChange: 'transform, opacity' }}
|
|
129
131
|
animate={{
|
|
130
132
|
transform: `translateX(${positionOffset * 50}%) translateZ(${-Math.abs(positionOffset) * 250}px) scale(${1 - Math.abs(positionOffset) * 0.15})`,
|
|
131
133
|
opacity: isVisible ? (1 - Math.abs(positionOffset) * 0.45) : 0,
|
|
132
134
|
zIndex: slides.length - Math.abs(positionOffset),
|
|
133
135
|
}}
|
|
134
|
-
transition={{ type: 'spring', stiffness:
|
|
136
|
+
transition={{ type: 'spring', stiffness: 160, damping: 22 }}
|
|
135
137
|
>
|
|
136
|
-
<CarouselCard slide={slide} isCenter={positionOffset === 0} />
|
|
138
|
+
<CarouselCard slide={slide} isCenter={positionOffset === 0} isScrolling={isScrolling} />
|
|
137
139
|
</motion.div>
|
|
138
140
|
);
|
|
139
141
|
})}
|
|
140
142
|
</div>
|
|
141
143
|
|
|
142
|
-
{
|
|
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
|
-
>
|
|
144
|
+
<motion.button onClick={handlePrev} 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" whileTap={{ scale: 0.9 }}>
|
|
148
145
|
<ArrowLeft size={32} />
|
|
149
146
|
</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
|
-
>
|
|
147
|
+
<motion.button onClick={handleNext} 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" whileTap={{ scale: 0.9 }}>
|
|
155
148
|
<ArrowRight size={32} />
|
|
156
149
|
</motion.button>
|
|
157
150
|
</div>
|
|
@@ -6,25 +6,24 @@ const GitHubIcon = () => (<svg width="24" height="24" fill="none" stroke="curren
|
|
|
6
6
|
const LinkedInIcon = () => (<svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" viewBox="0 0 24 24"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z" /><rect x="2" y="9" width="4" height="12" /><circle cx="4" cy="4" r="2" /></svg>);
|
|
7
7
|
const TwitterIcon = () => (<BsTwitterX size={24} />);
|
|
8
8
|
const InstagramIcon = () => (<svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" viewBox="0 0 24 24"><rect x="2" y="2" width="20" height="20" rx="5" ry="5" /><path d="M16 11.37A4 4 0 1112.63 8 4 4 0 0116 11.37z" /><line x1="17.5" y1="6.5" x2="17.5" y2="6.5" /></svg>);
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
onCvClick: () => alert("CV button clicked!"),
|
|
26
|
-
};
|
|
9
|
+
const defaultProfile = {
|
|
10
|
+
avatarUrl: "/assets/spiderman.png",
|
|
11
|
+
name: "Spider Man",
|
|
12
|
+
username: "spider_man",
|
|
13
|
+
dob: "10-08-2001",
|
|
14
|
+
title: "PhotoGrapher",
|
|
15
|
+
status: "Available for Hire",
|
|
16
|
+
socialLinks: {
|
|
17
|
+
github: "https://github.com/",
|
|
18
|
+
linkedin: "https://www.linkedin.com/",
|
|
19
|
+
twitter: "https://twitter.com/",
|
|
20
|
+
instagram: "https://www.instagram.com/",
|
|
21
|
+
},
|
|
22
|
+
onHireClick: () => alert("Hire Me button clicked!"),
|
|
23
|
+
onCvClick: () => alert("CV button clicked!"),
|
|
24
|
+
};
|
|
27
25
|
|
|
26
|
+
const ParallaxProfileCard = ({ profile }) => {
|
|
28
27
|
const {
|
|
29
28
|
avatarUrl, name, username, dob, title, status, socialLinks, onHireClick, onCvClick
|
|
30
29
|
} = profile || defaultProfile;
|
|
@@ -104,4 +103,16 @@ const ParallaxProfileCard = ({ profile }) => {
|
|
|
104
103
|
);
|
|
105
104
|
};
|
|
106
105
|
|
|
107
|
-
|
|
106
|
+
const ParallaxProfile = ({ profiles = [defaultProfile], className = '' }) => {
|
|
107
|
+
const safe = Array.isArray(profiles) && profiles.length > 0 ? profiles : [defaultProfile];
|
|
108
|
+
return (
|
|
109
|
+
<div className={`flex flex-wrap gap-6 justify-center ${className}`}>
|
|
110
|
+
{safe.map((p, i) => (
|
|
111
|
+
<ParallaxProfileCard key={p.id || p.username || i} profile={p} />
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export { ParallaxProfileCard };
|
|
118
|
+
export default ParallaxProfile;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useRef, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const defaultPeople = [
|
|
4
4
|
{ id: 1, name: "Captain America", job: "Leader of the Avengers", img: "/assets/captainamerica.png" },
|
|
5
5
|
{ id: 2, name: "Doctor Strange", job: "Sorcerer Supreme", img: "/assets/doctorStrange.png" },
|
|
6
6
|
{ id: 3, name: "Iron Man", job: "Leader Of Stark Industries", img: "/assets/ironman.png" },
|
|
@@ -9,7 +9,13 @@ const people = [
|
|
|
9
9
|
{ id: 6, name: "Thanos", job: "The Mad Titan", img: "/assets/thanos.png" },
|
|
10
10
|
];
|
|
11
11
|
|
|
12
|
-
const PointerFollower = ({
|
|
12
|
+
const PointerFollower = ({
|
|
13
|
+
cursorColor = "#fff",
|
|
14
|
+
interval = 3000,
|
|
15
|
+
badgeColor = "#fff",
|
|
16
|
+
badgeTextColor = "#212121",
|
|
17
|
+
people = defaultPeople,
|
|
18
|
+
}) => {
|
|
13
19
|
const containerRef = useRef(null);
|
|
14
20
|
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
15
21
|
const [hovered, setHovered] = useState(false);
|
|
@@ -20,11 +26,14 @@ const PointerFollower = ({ cursorColor = "#fff", interval = 3000, badgeColor = "
|
|
|
20
26
|
if (hovered) return;
|
|
21
27
|
|
|
22
28
|
const autoInterval = setInterval(() => {
|
|
23
|
-
setActiveIndex((prev) =>
|
|
29
|
+
setActiveIndex((prev) => {
|
|
30
|
+
if (!people || people.length === 0) return 0;
|
|
31
|
+
return (prev + 1) % people.length;
|
|
32
|
+
});
|
|
24
33
|
}, interval);
|
|
25
34
|
|
|
26
35
|
return () => clearInterval(autoInterval);
|
|
27
|
-
}, [hovered, interval]);
|
|
36
|
+
}, [hovered, interval, people]);
|
|
28
37
|
|
|
29
38
|
// 🖱️ Mouse tracking
|
|
30
39
|
useEffect(() => {
|
|
@@ -53,13 +62,24 @@ const PointerFollower = ({ cursorColor = "#fff", interval = 3000, badgeColor = "
|
|
|
53
62
|
};
|
|
54
63
|
}, []);
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
// Guard against empty people array
|
|
66
|
+
const safePeople = Array.isArray(people) && people.length > 0 ? people : defaultPeople;
|
|
67
|
+
const person = safePeople[activeIndex % safePeople.length]; // ✅ SYNCED HERE
|
|
68
|
+
|
|
69
|
+
// If the provided people list changes, ensure activeIndex is valid
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!Array.isArray(people) || people.length === 0) {
|
|
72
|
+
setActiveIndex(0);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
setActiveIndex((idx) => idx % people.length);
|
|
76
|
+
}, [people]);
|
|
57
77
|
|
|
58
78
|
return (
|
|
59
79
|
<div className="relative w-full h-fit flex items-center justify-center">
|
|
60
80
|
<div ref={containerRef} className="absolute flex items-center justify-center w-64 h-66 cursor-none">
|
|
61
81
|
{/* 🔁 Image stack with auto fade */}
|
|
62
|
-
{
|
|
82
|
+
{safePeople.map((p, i) => (
|
|
63
83
|
<img
|
|
64
84
|
key={i}
|
|
65
85
|
src={p.img}
|
|
@@ -67,7 +87,7 @@ const PointerFollower = ({ cursorColor = "#fff", interval = 3000, badgeColor = "
|
|
|
67
87
|
className={`absolute w-full h-full object-cover rounded-xl transition-opacity duration-1000 ease-in-out border border-white/10`}
|
|
68
88
|
style={{
|
|
69
89
|
transform: `translate(${i * -2}px, ${i * -2}px)`,
|
|
70
|
-
zIndex:
|
|
90
|
+
zIndex: safePeople.length - i,
|
|
71
91
|
opacity: activeIndex === i ? 1 : 0,
|
|
72
92
|
}}
|
|
73
93
|
/>
|
|
File without changes
|
|
@@ -87,30 +87,37 @@ void main() {
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
export const SmokeBackground = ({
|
|
90
|
-
color = "#
|
|
90
|
+
color = "#00c950",
|
|
91
91
|
speed = 1,
|
|
92
92
|
direction = "forward",
|
|
93
93
|
scale = 1,
|
|
94
94
|
opacity = 1,
|
|
95
95
|
mouseInteractive = true,
|
|
96
96
|
className = "",
|
|
97
|
+
maxWidth = 'max-w-4xl',
|
|
97
98
|
}) => {
|
|
98
99
|
const containerRef = useRef(null);
|
|
99
100
|
const mousePos = useRef({ x: 0, y: 0 });
|
|
100
101
|
|
|
101
102
|
useEffect(() => {
|
|
102
|
-
|
|
103
|
+
const mountNode = containerRef.current;
|
|
104
|
+
if (!mountNode) return;
|
|
103
105
|
|
|
104
106
|
const useCustomColor = color ? 1.0 : 0.0;
|
|
105
107
|
const customColorRgb = color ? hexToRgb(color) : [1, 1, 1];
|
|
106
108
|
|
|
107
109
|
let directionMultiplier = direction === "backward" ? -1.0 : 1.0;
|
|
108
110
|
|
|
111
|
+
// cap DPR depending on mount width to avoid heavy pixel loads on small devices
|
|
112
|
+
const mountWidth = mountNode.clientWidth || window.innerWidth;
|
|
113
|
+
const dprCap = mountWidth > 1200 ? 1.8 : (mountWidth > 768 ? 1.4 : 1.0);
|
|
114
|
+
const dpr = Math.min(window.devicePixelRatio || 1, dprCap);
|
|
115
|
+
|
|
109
116
|
const renderer = new Renderer({
|
|
110
117
|
webgl: 2,
|
|
111
118
|
alpha: true,
|
|
112
119
|
antialias: false,
|
|
113
|
-
dpr
|
|
120
|
+
dpr,
|
|
114
121
|
});
|
|
115
122
|
|
|
116
123
|
const gl = renderer.gl;
|
|
@@ -118,7 +125,7 @@ export const SmokeBackground = ({
|
|
|
118
125
|
canvas.style.display = "block";
|
|
119
126
|
canvas.style.width = "100%";
|
|
120
127
|
canvas.style.height = "100%";
|
|
121
|
-
|
|
128
|
+
mountNode.appendChild(canvas);
|
|
122
129
|
|
|
123
130
|
const geometry = new Triangle(gl);
|
|
124
131
|
|
|
@@ -145,17 +152,17 @@ export const SmokeBackground = ({
|
|
|
145
152
|
let mouseNeedsUpdate = false;
|
|
146
153
|
const handleMouseMove = (e) => {
|
|
147
154
|
if (!mouseInteractive) return;
|
|
148
|
-
const rect =
|
|
155
|
+
const rect = mountNode.getBoundingClientRect();
|
|
149
156
|
mousePos.current.x = e.clientX - rect.left;
|
|
150
157
|
mousePos.current.y = rect.height - (e.clientY - rect.top);
|
|
151
158
|
mouseNeedsUpdate = true;
|
|
152
159
|
};
|
|
153
160
|
if (mouseInteractive) {
|
|
154
|
-
|
|
161
|
+
mountNode.addEventListener("mousemove", handleMouseMove);
|
|
155
162
|
}
|
|
156
163
|
|
|
157
164
|
const setSize = () => {
|
|
158
|
-
const rect =
|
|
165
|
+
const rect = mountNode.getBoundingClientRect();
|
|
159
166
|
const width = Math.max(1, Math.floor(rect.width));
|
|
160
167
|
const height = Math.max(1, Math.floor(rect.height));
|
|
161
168
|
renderer.setSize(width, height);
|
|
@@ -167,7 +174,7 @@ export const SmokeBackground = ({
|
|
|
167
174
|
const ro = new ResizeObserver(() => {
|
|
168
175
|
requestAnimationFrame(setSize);
|
|
169
176
|
});
|
|
170
|
-
ro.observe(
|
|
177
|
+
ro.observe(mountNode);
|
|
171
178
|
setSize();
|
|
172
179
|
|
|
173
180
|
let raf = 0;
|
|
@@ -196,20 +203,21 @@ export const SmokeBackground = ({
|
|
|
196
203
|
return () => {
|
|
197
204
|
cancelAnimationFrame(raf);
|
|
198
205
|
ro.disconnect();
|
|
199
|
-
if (mouseInteractive &&
|
|
200
|
-
|
|
206
|
+
if (mouseInteractive && mountNode) {
|
|
207
|
+
mountNode.removeEventListener("mousemove", handleMouseMove);
|
|
201
208
|
}
|
|
202
209
|
try {
|
|
203
|
-
|
|
210
|
+
mountNode.removeChild(canvas);
|
|
204
211
|
} catch (error) { console.error("Error removing canvas:", error); }
|
|
205
212
|
};
|
|
206
213
|
}, [color, speed, direction, scale, opacity, mouseInteractive, className]);
|
|
207
214
|
|
|
208
215
|
return (
|
|
209
|
-
<div
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
<div className={`w-full ${className}`}>
|
|
217
|
+
<div className={`mx-auto w-full ${maxWidth} h-[100vh] lg:h-[90vh] overflow-hidden relative`}>
|
|
218
|
+
<div ref={containerRef} className="absolute inset-0 w-full h-full" />
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
213
221
|
);
|
|
214
222
|
};
|
|
215
223
|
|
|
@@ -95,6 +95,8 @@
|
|
|
95
95
|
--color-template-border: #fdc700;
|
|
96
96
|
--color-template-text: #fdc700;
|
|
97
97
|
--color-scan: #460809;
|
|
98
|
+
--selection-bg: #460809;
|
|
99
|
+
--selection-text: #fdc700;
|
|
98
100
|
|
|
99
101
|
/* Hero Component */
|
|
100
102
|
--right-color-bg: #e5c287;
|
|
@@ -246,6 +248,8 @@
|
|
|
246
248
|
--color-btn-hover-text: #d9f99d;
|
|
247
249
|
--color-stroke-color: #9ae600;
|
|
248
250
|
--color-scan: #9ae600;
|
|
251
|
+
--selection-bg: #9ae600;
|
|
252
|
+
--selection-text: #1e2939;
|
|
249
253
|
/* Hero Component */
|
|
250
254
|
--color-stroke-color: #9ae600;
|
|
251
255
|
--color-template: #101828;
|
package/dist/cli/index.js
CHANGED
|
@@ -12,20 +12,23 @@ const componentRoot = path.join(__dirname, "..", "ApexUI-Kit", "templates");
|
|
|
12
12
|
// Component name map
|
|
13
13
|
const componentMap = {
|
|
14
14
|
"avatar": "Avatar",
|
|
15
|
+
"accordion-marquee": "AccordionMarquee",
|
|
15
16
|
"beam-bar": "BeamBar",
|
|
17
|
+
"blueprint-background": "BluePrintBackground",
|
|
16
18
|
"carousel": "Carousel",
|
|
17
19
|
"parallax-carousel": "ParallaxCarousel",
|
|
18
20
|
"drag-carousel": "DragCarousel",
|
|
19
21
|
"basic-card": "Cards",
|
|
20
22
|
"edge-glow-from": "EdgeGlowFrom",
|
|
21
23
|
"glare-card": "GlareCard",
|
|
22
|
-
"
|
|
24
|
+
"card-stack": "CardStack",
|
|
23
25
|
"glass-profile": "GlassProfile",
|
|
24
26
|
"hover-text": "HoverText",
|
|
25
27
|
"hyper-card": "HyperCard",
|
|
26
28
|
"kinetic-threads-background": "KineticThreadsBackground",
|
|
27
29
|
"lens-flare-background": "LensFlareBackground",
|
|
28
30
|
"luminous-particle-ocean": "LuminousParticleOcean",
|
|
31
|
+
"meteor-card": "MeteorCard",
|
|
29
32
|
"nav-menu": "NavMenu",
|
|
30
33
|
"nav-dropdown": "NavDropDown",
|
|
31
34
|
"orbs-background": "OrbsBackground",
|