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.
@@ -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
- 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
- />
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 [isHovered, setIsHovered] = useState(false);
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
- // Agar hovered hai to interval start hi mat karo
91
- if (isHovered) {
92
- return;
93
- }
92
+ const handleScroll = () => {
93
+ setIsScrolling(true);
94
+ clearTimeout(scrollTimeoutRef.current);
95
+ scrollTimeoutRef.current = setTimeout(() => {
96
+ setIsScrolling(false);
97
+ }, 150);
98
+ };
94
99
 
95
- const interval = setInterval(() => {
96
- handleNext();
97
- }, 4000); // Har 4 second mein slide change hoga
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: 290, damping: 30 }}
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
- {/* 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
- >
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 ParallaxProfileCard = ({ profile }) => {
10
- const defaultProfile = {
11
- avatarUrl: "/assets/spiderman.png",
12
- name: "Spider Man",
13
- username: "spider_man",
14
- dob: "10-08-2001",
15
- title: "PhotoGrapher",
16
- status: "Available for Hire",
17
- socialLinks: {
18
- github: "https://github.com/",
19
- linkedin: "https://www.linkedin.com/",
20
- twitter: "https://twitter.com/",
21
- instagram: "https://www.instagram.com/",
22
- },
23
- // Button actions
24
- onHireClick: () => alert("Hire Me button clicked!"),
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
- export default ParallaxProfileCard;
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 people = [
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 = ({ cursorColor = "#fff", interval = 3000, badgeColor = "#fff", badgeTextColor = "#212121" }) => {
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) => (prev + 1) % people.length);
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
- const person = people[activeIndex]; // ✅ SYNCED HERE
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
- {people.map((p, i) => (
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: people.length - i,
90
+ zIndex: safePeople.length - i,
71
91
  opacity: activeIndex === i ? 1 : 0,
72
92
  }}
73
93
  />
@@ -87,30 +87,37 @@ void main() {
87
87
 
88
88
 
89
89
  export const SmokeBackground = ({
90
- color = "#ffffff",
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
- if (!containerRef.current) return;
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: Math.min(window.devicePixelRatio || 1, 1.8),
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
- containerRef.current.appendChild(canvas);
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 = containerRef.current.getBoundingClientRect();
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
- containerRef.current.addEventListener("mousemove", handleMouseMove);
161
+ mountNode.addEventListener("mousemove", handleMouseMove);
155
162
  }
156
163
 
157
164
  const setSize = () => {
158
- const rect = containerRef.current.getBoundingClientRect();
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(containerRef.current);
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 && containerRef.current) {
200
- containerRef.current.removeEventListener("mousemove", handleMouseMove);
206
+ if (mouseInteractive && mountNode) {
207
+ mountNode.removeEventListener("mousemove", handleMouseMove);
201
208
  }
202
209
  try {
203
- containerRef.current?.removeChild(canvas);
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
- ref={containerRef}
211
- className={`w-screen lg:h-full h-[80vh] overflow-hidden relative ${className}`}
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
- "glass-card-stack": "GlassCardStack",
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",