apex-ui-kit 2.0.6 → 2.1.0

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 (35) hide show
  1. package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.jsx +74 -53
  2. package/dist/ApexUI-Kit/templates/BeamBar/BeamBar.jsx +5 -3
  3. package/dist/ApexUI-Kit/templates/BluePrintBackground/BluePrintBackground.jsx +14 -2
  4. package/dist/ApexUI-Kit/templates/CardStack/CardStack.jsx +129 -0
  5. package/dist/ApexUI-Kit/templates/Cards/BasicCard.jsx +3 -3
  6. package/dist/ApexUI-Kit/templates/Carousel/ParallaxCarousel.jsx +0 -161
  7. package/dist/ApexUI-Kit/templates/DragCarousel/DragCarousel.jsx +39 -7
  8. package/dist/ApexUI-Kit/templates/EdgeGlowForm/EdgeGlowFrom.jsx +116 -98
  9. package/dist/ApexUI-Kit/templates/FlipProfile/FlipProfile.jsx +69 -37
  10. package/dist/ApexUI-Kit/templates/GlareCard/GlareCard.jsx +2 -2
  11. package/dist/ApexUI-Kit/templates/HoverText/HoverText.jsx +14 -13
  12. package/dist/ApexUI-Kit/templates/HyperCard/HyperCard.jsx +59 -42
  13. package/dist/ApexUI-Kit/templates/KineticThreadsBackground/KineticThreadBackground.jsx +17 -2
  14. package/dist/ApexUI-Kit/templates/LensFlareBackground/LensFlareBackground.jsx +18 -3
  15. package/dist/ApexUI-Kit/templates/LuminousParticleOcean/LuminousParticleOcean.jsx +15 -1
  16. package/dist/ApexUI-Kit/templates/MaskView/MaskView.jsx +345 -0
  17. package/dist/ApexUI-Kit/templates/MeteorCard/MeteorCard.jsx +34 -45
  18. package/dist/ApexUI-Kit/templates/NavDropDown/NavDropDown.jsx +272 -101
  19. package/dist/ApexUI-Kit/templates/NavMenu/NavMenu.jsx +8 -2
  20. package/dist/ApexUI-Kit/templates/ParallaxCard/ParallaxCard.jsx +13 -11
  21. package/dist/ApexUI-Kit/templates/ParallaxCarousel/ParallaxCarousel.jsx +60 -67
  22. package/dist/ApexUI-Kit/templates/ParallaxProfile/ParallaxProfile.jsx +69 -55
  23. package/dist/ApexUI-Kit/templates/PointerFollower/PointerFollower.jsx +34 -14
  24. package/dist/ApexUI-Kit/templates/ScrollMarquee/CardMarquee.jsx +0 -0
  25. package/dist/ApexUI-Kit/templates/SmartGridCard/SmartGridCard.jsx +4 -3
  26. package/dist/ApexUI-Kit/templates/SmokeBackground/SmokeBackground.jsx +35 -16
  27. package/dist/ApexUI-Kit/templates/Testimonials/Testimonials.jsx +11 -8
  28. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeAnimation.js +40 -69
  29. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.css +5 -1
  30. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.jsx +25 -7
  31. package/dist/ApexUI-Kit/templates/ToolTip/ToolTip.jsx +71 -52
  32. package/dist/cli/index.js +25 -14
  33. package/package.json +2 -2
  34. package/dist/ApexUI-Kit/templates/GlassCardStack/GlassCardStack.jsx +0 -111
  35. package/dist/ApexUI-Kit/templates/WaterDropReveal/WaterDropReveal.jsx +0 -151
@@ -24,12 +24,12 @@ const Testimonials = ({ testimonials }) => {
24
24
  };
25
25
 
26
26
  return (
27
- <div className="w-full max-w-4xl mx-auto bg-slate-950 rounded-2xl p-8 md:p-12 border border-slate-800 grid md:grid-cols-2 gap-12 items-center overflow-hidden">
27
+ <div className="w-full max-w-4xl mx-auto bg-slate-950 rounded-2xl p-6 sm:p-8 md:p-12 border border-slate-800 grid md:grid-cols-2 gap-8 md:gap-12 items-center overflow-hidden">
28
28
 
29
29
  {/* Left Part: Text Content and Navigation */}
30
30
  <div className="flex flex-col justify-center h-full text-center md:text-left order-2 md:order-1">
31
- {/* Title and Subtitle with Animation */}
32
- <div className="relative h-32">
31
+ {/* Title, subtitle and quote with animation; the min height keeps the buttons from jumping. */}
32
+ <div className="relative min-h-[13rem] sm:min-h-[12rem]">
33
33
  <AnimatePresence initial={false} mode="wait">
34
34
  <motion.div
35
35
  key={activeIndex}
@@ -38,11 +38,11 @@ const Testimonials = ({ testimonials }) => {
38
38
  animate="center"
39
39
  exit="exit"
40
40
  transition={{ duration: 0.4, ease: "easeInOut" }}
41
- className="absolute w-full"
41
+ className="w-full"
42
42
  >
43
- <h2 className="text-3xl font-bold text-white tracking-tight">{testimonials[activeIndex].author}</h2>
43
+ <h2 className="text-2xl sm:text-3xl font-bold text-white tracking-tight">{testimonials[activeIndex].author}</h2>
44
44
  <p className="mt-2 text-lime-400 font-medium">{testimonials[activeIndex].title}</p>
45
- <p className="mt-4 text-slate-400 text-sm leading-relaxed hidden md:block">"{testimonials[activeIndex].text}"</p>
45
+ <p className="mt-4 text-slate-400 text-sm leading-relaxed">"{testimonials[activeIndex].text}"</p>
46
46
  </motion.div>
47
47
  </AnimatePresence>
48
48
  </div>
@@ -71,7 +71,9 @@ const Testimonials = ({ testimonials }) => {
71
71
  </div>
72
72
 
73
73
  {/* Right Part: Stacked Images */}
74
- <div className="relative h-80 w-full flex items-center justify-center order-1 md:order-2">
74
+ <div className="flex w-full items-center justify-center order-1 md:order-2">
75
+ {/* Sized to the card plus the fan, so the stack stays centred and inside the panel. */}
76
+ <div className="relative h-[calc(16rem+30px)] w-[calc(12rem+60px)] sm:h-[calc(20rem+30px)] sm:w-[calc(16rem+60px)]">
75
77
  {testimonials.map((testimonial, index) => {
76
78
  // Determine the position of the card in the stack
77
79
  const position = (index - activeIndex + testimonials.length) % testimonials.length;
@@ -82,7 +84,7 @@ const Testimonials = ({ testimonials }) => {
82
84
  return (
83
85
  <motion.div
84
86
  key={testimonial.id || index}
85
- className="absolute w-64 h-80 bg-slate-800 rounded-xl shadow-2xl overflow-hidden border-2 border-slate-700"
87
+ className="absolute w-48 h-64 sm:w-64 sm:h-80 bg-slate-800 rounded-xl shadow-2xl overflow-hidden border-2 border-slate-700"
86
88
  initial={false}
87
89
  animate={{
88
90
  scale: 1 - position * 0.08,
@@ -107,6 +109,7 @@ const Testimonials = ({ testimonials }) => {
107
109
  </motion.div>
108
110
  );
109
111
  })}
112
+ </div>
110
113
  </div>
111
114
  </div>
112
115
  );
@@ -1,14 +1,42 @@
1
1
  // config/animations.js
2
2
 
3
- // Small utility to escape quotes in data-URIs
4
- const esc = (s = "") => s.replace(/#/g, "%23").replace(/\n/g, "");
3
+ // Soft-edged circle that grows from `at` until it covers the screen. It animates
4
+ // a radial-gradient mask through a registered property, which stays smooth;
5
+ // scaling a blurred SVG mask to screen size re-rasterises every frame and flickers.
6
+ const circleReveal = (at) => `
7
+ @property --vt-r { syntax: '<length>'; inherits: false; initial-value: 0px; }
8
+ ::view-transition-new(root) {
9
+ -webkit-mask-image: radial-gradient(circle at ${at}, #000 var(--vt-r), transparent calc(var(--vt-r) + 12vmax));
10
+ mask-image: radial-gradient(circle at ${at}, #000 var(--vt-r), transparent calc(var(--vt-r) + 12vmax));
11
+ animation: vt-circle var(--vt-duration) var(--vt-ease);
12
+ }
13
+ ::view-transition-old(root) { animation: none; }
14
+ @keyframes vt-circle {
15
+ from { --vt-r: -12vmax; }
16
+ to { --vt-r: 150vmax; }
17
+ }
18
+ `;
19
+
20
+ // Holds the last frame until the transition ends (no flash of the old theme)
21
+ // and keeps the browser's default plus-lighter blending out of the reveal.
22
+ const footer = `
23
+ ::view-transition-old(root), ::view-transition-new(root) {
24
+ animation-fill-mode: both;
25
+ mix-blend-mode: normal;
26
+ }
27
+ `;
5
28
 
6
29
  export function buildAnimationCSS(animation, opts = {}) {
30
+ return presetCSS(animation, opts) + footer;
31
+ }
32
+
33
+ function presetCSS(animation, opts = {}) {
7
34
  const duration = opts.duration || "1.5s";
8
35
  const ease = opts.ease || "var(--vt-ease)";
9
36
 
37
+ // `var(--vt-ease)` would refer to itself here, so only set a real easing.
10
38
  const header = `
11
- :root { --vt-duration: ${duration}; --vt-ease: ${ease}; }
39
+ :root { --vt-duration: ${duration};${ease === "var(--vt-ease)" ? "" : ` --vt-ease: ${ease};`} }
12
40
  ::view-transition-group(root){ animation-timing-function: var(--vt-ease); }
13
41
  `;
14
42
 
@@ -37,72 +65,15 @@ export function buildAnimationCSS(animation, opts = {}) {
37
65
  const preset = typeof animation === "string" ? animation : animation?.type;
38
66
 
39
67
  switch (preset) {
40
- case "circle": {
41
- const circleSVG = esc(
42
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><circle cx="20" cy="20" r="20" fill="white"/></svg>`
43
- );
44
- return header + `
45
- ::view-transition-new(root) {
46
- mask: url('data:image/svg+xml,${circleSVG}') center / 0 no-repeat;
47
- -webkit-mask: url('data:image/svg+xml,${circleSVG}') center / 0 no-repeat;
48
- animation: vt-scale var(--vt-duration);
49
- }
50
- ::view-transition-old(root), .dark::view-transition-old(root) {
51
- animation: vt-scale var(--vt-duration);
52
- }
53
- @keyframes vt-scale {
54
- to { mask-size: 200vmax; -webkit-mask-size: 200vmax; }
55
- }
56
- `;
57
- }
68
+ case "circle":
69
+ // Grows from the clicked button (ThemeToggle sets --cx / --cy).
70
+ return header + circleReveal("var(--cx, 50%) var(--cy, 50%)");
58
71
 
59
- case "circle-left": {
60
- const svg = esc(
61
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><defs><filter id="b"><feGaussianBlur stdDeviation="2"/></filter></defs><circle cx="0" cy="0" r="18" fill="white" filter="url(%23b)"/></svg>`
62
- );
63
- return header + `
64
- ::view-transition-new(root) {
65
- mask: url('data:image/svg+xml,${svg}') top left / 0 no-repeat;
66
- -webkit-mask: url('data:image/svg+xml,${svg}') top left / 0 no-repeat;
67
- transform-origin: top left;
68
- animation: vt-scale-tl var(--vt-duration);
69
- }
70
- ::view-transition-old(root), .dark::view-transition-old(root) {
71
- transform-origin: top left;
72
- animation: vt-scale-tl var(--vt-duration);
73
- }
74
- @keyframes vt-scale-tl { to { mask-size: 350vmax; -webkit-mask-size: 350vmax; } }
75
- `;
76
- }
77
- case "circle-right": {
78
- const svg = esc(
79
- `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
80
- <defs><filter id="b"><feGaussianBlur stdDeviation="2"/></filter></defs>
81
- <circle cx="40" cy="0" r="18" fill="white" filter="url(%23b)"/>
82
- </svg>`
83
- );
84
-
85
- return header + `
86
- ::view-transition-new(root) {
87
- mask: url('data:image/svg+xml,${svg}') top right / 0 no-repeat;
88
- -webkit-mask: url('data:image/svg+xml,${svg}') top right / 0 no-repeat;
89
- transform-origin: top right;
90
- animation: vt-scale-tr 0.9s;
91
- }
72
+ case "circle-left":
73
+ return header + circleReveal("0 0");
92
74
 
93
- ::view-transition-old(root), .dark::view-transition-old(root) {
94
- transform-origin: top right;
95
- animation: vt-scale-tr 0.9s;
96
- }
97
-
98
- @keyframes vt-scale-tr {
99
- to {
100
- mask-size: 350vmax;
101
- -webkit-mask-size: 350vmax;
102
- }
103
- }
104
- `;
105
- }
75
+ case "circle-right":
76
+ return header + circleReveal("100% 0");
106
77
  case "split-reveal-horizontal": {
107
78
  return header + `
108
79
  ::view-transition-new(root) {
@@ -241,7 +212,7 @@ export function buildAnimationCSS(animation, opts = {}) {
241
212
  }
242
213
  `;
243
214
  }
244
- // Add more animation
215
+ // Add more animation
245
216
 
246
217
 
247
218
 
@@ -249,4 +220,4 @@ export function buildAnimationCSS(animation, opts = {}) {
249
220
  default:
250
221
  return header; // no-op if unknown
251
222
  }
252
- }
223
+ }
@@ -71,7 +71,7 @@
71
71
  --color-aside-beam: #fff;
72
72
 
73
73
  /* animation defaults (can be overridden) */
74
- --vt-duration: 1s;
74
+ --vt-duration: 1.7s;
75
75
  --vt-ease: ease-in-out;
76
76
  }
77
77
 
@@ -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;
@@ -1,7 +1,15 @@
1
1
  import { useState, useEffect, useRef, useId } from "react";
2
+ import { flushSync } from "react-dom";
2
3
  import { FiSun, FiMoon } from "react-icons/fi";
3
4
  import { buildAnimationCSS } from "./ThemeAnimation.js";
4
5
 
6
+ // Swaps the theme-* class on <html>.
7
+ function applyThemeClass(theme) {
8
+ const html = document.documentElement;
9
+ [...html.classList].forEach((c) => c.startsWith("theme-") && html.classList.remove(c));
10
+ html.classList.add(`theme-${theme}`);
11
+ }
12
+
5
13
  export default function ThemeToggle({
6
14
  animation = "circle",
7
15
  duration = "1.5s",
@@ -29,12 +37,19 @@ export default function ThemeToggle({
29
37
  // ------------------------------
30
38
  useEffect(() => {
31
39
  const rawCSS = buildAnimationCSS(animation, { duration, ease });
40
+ const suffix = uid.replace(/[^\w-]/g, "");
32
41
 
33
- // ✅ Scope selectors with html[vt-owner]
42
+ // ✅ Scope selectors with html[vt-owner]; :root variables and keyframe names
43
+ // too, so several toggles on one page don't override each other's settings.
34
44
  const scopedCSS = rawCSS
45
+ .replace(/(?<![-\w])vt-(?=[a-z])/g, `vt-${suffix}-`)
46
+ .replace(/:root\b/g, `html[vt-owner="${uid}"]`)
35
47
  .replaceAll("::view-transition-new", `html[vt-owner="${uid}"]::view-transition-new`)
36
48
  .replaceAll("::view-transition-old", `html[vt-owner="${uid}"]::view-transition-old`)
37
- .replaceAll("::view-transition-group", `html[vt-owner="${uid}"]::view-transition-group`);
49
+ .replaceAll("::view-transition-group", `html[vt-owner="${uid}"]::view-transition-group`)
50
+ // No CSS transitions while the view transition runs, otherwise elements are
51
+ // captured mid-fade in the old colours and snap to the new ones afterwards.
52
+ + `html[vt-owner="${uid}"] *, html[vt-owner="${uid}"] *::before, html[vt-owner="${uid}"] *::after { transition: none !important; }`;
38
53
 
39
54
  let styleEl = document.getElementById(`vt-style-${uid}`);
40
55
  if (!styleEl) {
@@ -49,9 +64,7 @@ export default function ThemeToggle({
49
64
  // Apply theme globally + notify
50
65
  // ------------------------------
51
66
  useEffect(() => {
52
- const html = document.documentElement;
53
- html.classList.forEach((c) => c.startsWith("theme-") && html.classList.remove(c));
54
- html.classList.add(`theme-${theme}`);
67
+ applyThemeClass(theme);
55
68
 
56
69
  try {
57
70
  localStorage.setItem("apexui-theme", theme);
@@ -88,7 +101,12 @@ export default function ThemeToggle({
88
101
  // ✅ Use html instead of body
89
102
  document.documentElement.setAttribute("vt-owner", uid);
90
103
 
91
- const run = () => setTheme(nextTheme);
104
+ // Apply the theme synchronously: the browser takes the transition's "new"
105
+ // snapshot as soon as this returns, before a normal React update would land.
106
+ const run = () => {
107
+ applyThemeClass(nextTheme);
108
+ flushSync(() => setTheme(nextTheme));
109
+ };
92
110
 
93
111
  if (document.startViewTransition) {
94
112
  const vt = document.startViewTransition(run);
@@ -115,4 +133,4 @@ export default function ThemeToggle({
115
133
  {isLight ? <FiMoon /> : <FiSun />}
116
134
  </button>
117
135
  );
118
- }
136
+ }
@@ -5,81 +5,100 @@ const ToolTip = ({ items }) => {
5
5
  const [hoveredIndex, setHoveredIndex] = useState(null);
6
6
  const tooltipRefs = useRef([]);
7
7
  const avatarRefs = useRef([]);
8
+ const last = items.length - 1;
8
9
 
9
10
  useEffect(() => {
10
- // Animate all avatars based on hover state
11
+ // Lift the active avatar, dim the others
11
12
  items.forEach((_, idx) => {
12
13
  const isHovered = hoveredIndex === idx;
13
-
14
- // Animate the avatar image
15
14
  gsap.to(avatarRefs.current[idx], {
16
- scale: isHovered ? 1.15 : 1,
17
- filter: isHovered ? "blur(0px)" : hoveredIndex !== null ? "blur(1.5px)" : "blur(0px)",
18
- duration: 0.25,
19
- ease: "power2.inOut",
15
+ y: isHovered ? -6 : 0,
16
+ scale: isHovered ? 1.12 : 1,
17
+ opacity: hoveredIndex === null || isHovered ? 1 : 0.55,
18
+ duration: 0.3,
19
+ ease: "power3.out",
20
20
  });
21
21
  });
22
22
 
23
- // Animate the tooltip for the hovered item
23
+ // Pop the tooltip in
24
24
  if (hoveredIndex !== null && tooltipRefs.current[hoveredIndex]) {
25
25
  gsap.fromTo(
26
26
  tooltipRefs.current[hoveredIndex],
27
- { opacity: 0, y: 10, scale: 0.8 },
28
- {
29
- opacity: 1,
30
- y: 0,
31
- scale: 1,
32
- duration: 0.4,
33
- ease: "power3.out",
34
- }
27
+ { opacity: 0, y: 12, scale: 0.85, rotate: 0 },
28
+ { opacity: 1, y: 0, scale: 1, duration: 0.45, ease: "back.out(1.8)" }
35
29
  );
36
30
  }
37
31
  }, [hoveredIndex, items]);
38
32
 
33
+ // The tooltip swings toward the pointer while it moves across the avatar.
34
+ const handlePointerMove = (e, idx) => {
35
+ const tip = tooltipRefs.current[idx];
36
+ if (!tip || e.pointerType !== "mouse") return;
37
+ const rect = e.currentTarget.getBoundingClientRect();
38
+ const offset = ((e.clientX - rect.left) / rect.width - 0.5) * 2; // -1 … 1
39
+ gsap.to(tip, { rotate: offset * 10, x: offset * 12, duration: 0.5, ease: "power3.out", overwrite: "auto" });
40
+ };
41
+
42
+ // Keep the end tooltips inside small screens: align them to their outer edge.
43
+ const placement = (idx) =>
44
+ idx === 0 && last > 0 ? { box: "left-0", arrow: "left-6" }
45
+ : idx === last && last > 0 ? { box: "right-0", arrow: "right-6" }
46
+ : { box: "left-1/2 -translate-x-1/2", arrow: "left-1/2 -translate-x-1/2" };
47
+
39
48
  return (
40
- <div className="flex items-end -space-x-4">
41
- {items.map((item, idx) => (
42
- <div
43
- key={item.name}
44
- onMouseEnter={() => setHoveredIndex(idx)}
45
- onMouseLeave={() => setHoveredIndex(null)}
46
- className="relative group cursor-pointer"
47
- style={{ zIndex: hoveredIndex === idx ? 50 : 10 }}
48
- >
49
- {hoveredIndex === idx && (
50
- <div
51
- ref={(el) => (tooltipRefs.current[idx] = el)}
52
- className="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 z-50 flex flex-col items-center justify-center rounded-lg border border-teal-500/50 bg-gradient-to-br from-black/90 to-zinc-900/80 px-4 py-2 shadow-2xl backdrop-blur-md"
53
- >
54
- <div className="absolute bottom-[-6px] left-1/2 -translate-x-1/2 w-0 h-0 border-t-[8px] border-l-[8px] border-r-[8px] border-t-teal-500/40 border-l-transparent border-r-transparent" />
55
- <span className="text-sm font-bold text-teal-300 drop-shadow-sm whitespace-nowrap">
56
- {item.name}
57
- </span>
58
- <span className="text-xs text-zinc-300 italic whitespace-nowrap">
59
- {item.designation}
60
- </span>
49
+ <div className="flex items-end -space-x-3 sm:-space-x-4">
50
+ {items.map((item, idx) => {
51
+ const { box, arrow } = placement(idx);
52
+ return (
53
+ <div
54
+ key={item.id ?? item.name}
55
+ onPointerEnter={(e) => e.pointerType === "mouse" && setHoveredIndex(idx)}
56
+ onPointerLeave={(e) => e.pointerType === "mouse" && setHoveredIndex(null)}
57
+ onPointerMove={(e) => handlePointerMove(e, idx)}
58
+ // Tap to toggle on touch screens
59
+ onClick={() => setHoveredIndex((cur) => (cur === idx ? null : idx))}
60
+ className="relative cursor-pointer"
61
+ style={{ zIndex: hoveredIndex === idx ? 50 : 10 + idx }}
62
+ >
63
+ {hoveredIndex === idx && (
64
+ <div className={`pointer-events-none absolute bottom-full mb-4 z-50 ${box}`}>
65
+ {/* The card and its arrow animate together, so the arrow follows every swing. */}
66
+ <div ref={(el) => (tooltipRefs.current[idx] = el)} className="relative origin-bottom">
67
+ <div className="relative overflow-hidden rounded-xl border border-white/10 bg-zinc-950 px-3.5 py-2.5 shadow-[0_16px_40px_-12px_rgba(0,0,0,0.7)]">
68
+ <span className="absolute inset-x-3 top-0 h-px bg-gradient-to-r from-transparent via-lime-400 to-cyan-400" />
69
+ <span className="flex items-center gap-1.5 whitespace-nowrap text-sm font-semibold text-white">
70
+ <span className="h-1.5 w-1.5 rounded-full bg-lime-400 shadow-[0_0_8px_#a3e635]" />
71
+ {item.name}
72
+ </span>
73
+ <span className="mt-0.5 block whitespace-nowrap text-xs text-zinc-400">{item.designation}</span>
74
+ </div>
75
+ {/* Downward arrow: filled triangle that covers the card's bottom border, with the border on its two sides only */}
76
+ <svg width="16" height="8" viewBox="0 0 16 8" className={`absolute top-full -mt-px ${arrow}`} aria-hidden="true">
77
+ <path d="M0 0 L8 8 L16 0 Z" fill="#09090b" />
78
+ <path d="M0.5 0 L8 7.5 L15.5 0" fill="none" stroke="rgba(255,255,255,0.1)" />
79
+ </svg>
80
+ </div>
81
+ </div>
82
+ )}
83
+ <div ref={(el) => (avatarRefs.current[idx] = el)} className="rounded-full bg-gradient-to-br from-lime-400 via-emerald-400 to-cyan-400 p-[2px] shadow-lg shadow-black/30">
84
+ <img
85
+ src={item.image}
86
+ alt={item.name}
87
+ draggable="false"
88
+ className="h-12 w-12 sm:h-14 sm:w-14 rounded-full border-2 border-zinc-950 object-cover object-top"
89
+ />
61
90
  </div>
62
- )}
63
- <img
64
- ref={(el) => (avatarRefs.current[idx] = el)}
65
- src={item.image}
66
- alt={item.name}
67
- className="h-14 w-14 object-cover object-center rounded-full border-2 border-white shadow-lg transition-transform"
68
- style={{
69
- zIndex: hoveredIndex === idx ? 50 : 10,
70
- transition: "all 0.25s ease-in-out",
71
- }}
72
- />
73
- </div>
74
- ))}
91
+ </div>
92
+ );
93
+ })}
75
94
  </div>
76
95
  );
77
96
  };
78
97
 
79
98
  export default function ToolTipInner({ items }) {
80
99
  return (
81
- <div className="flex flex-row items-center justify-center">
100
+ <div className="flex flex-row items-center justify-center pt-24">
82
101
  <ToolTip items={items} />
83
102
  </div>
84
103
  );
85
- }
104
+ }
package/dist/cli/index.js CHANGED
@@ -9,7 +9,7 @@ import { execSync } from "child_process";
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
11
  const componentRoot = path.join(__dirname, "..", "ApexUI-Kit", "templates");
12
- // Component name map
12
+ // Component name map (CLI name -> folder in templates). Older names stay as aliases.
13
13
  const componentMap = {
14
14
  "avatar": "Avatar",
15
15
  "accordion-marquee": "AccordionMarquee",
@@ -19,10 +19,10 @@ const componentMap = {
19
19
  "parallax-carousel": "ParallaxCarousel",
20
20
  "drag-carousel": "DragCarousel",
21
21
  "basic-card": "Cards",
22
- "edge-glow-from": "EdgeGlowFrom",
22
+ "edge-glow-form": "EdgeGlowForm",
23
+ "edge-glow-from": "EdgeGlowForm",
23
24
  "glare-card": "GlareCard",
24
- "glass-card-stack": "GlassCardStack",
25
- "glass-profile": "GlassProfile",
25
+ "card-stack": "CardStack",
26
26
  "hover-text": "HoverText",
27
27
  "hyper-card": "HyperCard",
28
28
  "kinetic-threads-background": "KineticThreadsBackground",
@@ -33,6 +33,7 @@ const componentMap = {
33
33
  "nav-dropdown": "NavDropDown",
34
34
  "orbs-background": "OrbsBackground",
35
35
  "pointer-follower": "PointerFollower",
36
+ "flip-profile": "FlipProfile",
36
37
  "flip-profiler": "FlipProfile",
37
38
  "parallax-profile": "ParallaxProfile",
38
39
  "parallax-card": "ParallaxCard",
@@ -41,10 +42,18 @@ const componentMap = {
41
42
  "scroll-marquee": "ScrollMarquee",
42
43
  "smart-grid-card": "SmartGridCard",
43
44
  "smoke-background": "SmokeBackground",
45
+ "tooltip": "ToolTip",
44
46
  "tool-tip": "ToolTip",
45
47
  "theme-toggle": "ThemeToggle",
46
48
  "testimonials": "Testimonials",
47
- "water-drop-reveal": "WaterDropReveal",
49
+ "mask-view": "MaskView",
50
+ "water-drop-reveal": "MaskView",
51
+ };
52
+ // Packages only some components need, installed on top of the shared ones.
53
+ const extraDeps = {
54
+ Cards: ["react-router-dom"],
55
+ LuminousParticleOcean: ["@react-three/fiber"],
56
+ SmokeBackground: ["ogl"],
48
57
  };
49
58
  async function main() {
50
59
  const args = process.argv.slice(2);
@@ -52,7 +61,7 @@ async function main() {
52
61
  const rawInput = args[1];
53
62
  if (command !== "add" || !rawInput) {
54
63
  console.log(chalk.yellow("Usage:"));
55
- console.log(" npx apex-ui-templete add <component-name>");
64
+ console.log(" npx apex-ui-kit add <component-name>");
56
65
  return;
57
66
  }
58
67
  const input = rawInput.toLowerCase();
@@ -60,7 +69,7 @@ async function main() {
60
69
  if (!matchedName) {
61
70
  console.log(chalk.red(`❌ Component '${rawInput}' not found in ApexUI-Kit.`));
62
71
  console.log(chalk.gray("👉 Available components:"));
63
- Object.values(componentMap).forEach((c) => console.log(` • ${c}`));
72
+ Object.keys(componentMap).forEach((c) => console.log(` • ${c}`));
64
73
  return;
65
74
  }
66
75
  // Validate package.json
@@ -74,7 +83,7 @@ async function main() {
74
83
  type: "text",
75
84
  name: "dirInput",
76
85
  message: "📁 Install in which folder? (use . for current)",
77
- initial: "."
86
+ initial: ".",
78
87
  });
79
88
  let basePath = "";
80
89
  if (dirInput.trim() === ".") {
@@ -84,8 +93,8 @@ async function main() {
84
93
  message: "📍 Are you currently inside the folder where ApexUI-Kit should be?",
85
94
  choices: [
86
95
  { title: "Yes (install here)", value: "yes" },
87
- { title: "No (I'll choose a path)", value: "no" }
88
- ]
96
+ { title: "No (I'll choose a path)", value: "no" },
97
+ ],
89
98
  });
90
99
  if (confirmInside === "yes") {
91
100
  basePath = process.cwd();
@@ -95,7 +104,7 @@ async function main() {
95
104
  type: "text",
96
105
  name: "targetPath",
97
106
  message: "📂 Enter target folder path (or use . for current)",
98
- initial: "."
107
+ initial: ".",
99
108
  });
100
109
  basePath = path.resolve(targetPath.trim());
101
110
  }
@@ -110,8 +119,8 @@ async function main() {
110
119
  message: "🧠 Choose language",
111
120
  choices: [
112
121
  { title: "JavaScript (JSX)", value: "jsx" },
113
- { title: "TypeScript (Coming Soon)", value: "ts", disabled: true }
114
- ]
122
+ { title: "TypeScript (Coming Soon)", value: "ts", disabled: true },
123
+ ],
115
124
  });
116
125
  // Install dependencies
117
126
  console.log(chalk.cyan("📦 Installing dependencies..."));
@@ -122,7 +131,9 @@ async function main() {
122
131
  "@fortawesome/fontawesome-svg-core",
123
132
  "@fortawesome/free-solid-svg-icons",
124
133
  "@fortawesome/react-fontawesome",
125
- "react-icons"
134
+ "react-icons",
135
+ "lucide-react",
136
+ ...(extraDeps[matchedName] ?? []),
126
137
  ];
127
138
  try {
128
139
  execSync(`npm install ${deps.join(" ")}`, { stdio: "inherit" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-ui-kit",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "description": "Apex UI Kit - A React component library for building modern user interfaces",
6
6
  "keywords": [
@@ -23,7 +23,7 @@
23
23
  "type": "module",
24
24
  "main": "dist/cli/index.js",
25
25
  "bin": {
26
- "apex-ui-kit": "./dist/cli/index.js"
26
+ "apex-ui-kit": "dist/cli/index.js"
27
27
  },
28
28
  "files": [
29
29
  "dist"