apex-ui-kit 2.0.7 → 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.
- package/dist/ApexUI-Kit/templates/AccordionMarquee/AccordionMarquee.jsx +74 -53
- package/dist/ApexUI-Kit/templates/BeamBar/BeamBar.jsx +5 -3
- package/dist/ApexUI-Kit/templates/BluePrintBackground/BluePrintBackground.jsx +14 -2
- package/dist/ApexUI-Kit/templates/CardStack/CardStack.jsx +56 -28
- package/dist/ApexUI-Kit/templates/Cards/BasicCard.jsx +3 -3
- package/dist/ApexUI-Kit/templates/DragCarousel/DragCarousel.jsx +1 -1
- package/dist/ApexUI-Kit/templates/EdgeGlowForm/EdgeGlowFrom.jsx +116 -98
- package/dist/ApexUI-Kit/templates/FlipProfile/FlipProfile.jsx +69 -37
- package/dist/ApexUI-Kit/templates/GlareCard/GlareCard.jsx +2 -2
- package/dist/ApexUI-Kit/templates/HoverText/HoverText.jsx +14 -13
- package/dist/ApexUI-Kit/templates/HyperCard/HyperCard.jsx +59 -42
- package/dist/ApexUI-Kit/templates/KineticThreadsBackground/KineticThreadBackground.jsx +17 -2
- package/dist/ApexUI-Kit/templates/LensFlareBackground/LensFlareBackground.jsx +18 -3
- package/dist/ApexUI-Kit/templates/LuminousParticleOcean/LuminousParticleOcean.jsx +15 -1
- package/dist/ApexUI-Kit/templates/MaskView/MaskView.jsx +345 -0
- package/dist/ApexUI-Kit/templates/MeteorCard/MeteorCard.jsx +34 -45
- package/dist/ApexUI-Kit/templates/NavDropDown/NavDropDown.jsx +272 -101
- package/dist/ApexUI-Kit/templates/NavMenu/NavMenu.jsx +8 -2
- package/dist/ApexUI-Kit/templates/ParallaxCard/ParallaxCard.jsx +13 -11
- package/dist/ApexUI-Kit/templates/ParallaxCarousel/ParallaxCarousel.jsx +9 -9
- package/dist/ApexUI-Kit/templates/ParallaxProfile/ParallaxProfile.jsx +41 -38
- package/dist/ApexUI-Kit/templates/PointerFollower/PointerFollower.jsx +7 -7
- package/dist/ApexUI-Kit/templates/SmartGridCard/SmartGridCard.jsx +4 -3
- package/dist/ApexUI-Kit/templates/SmokeBackground/SmokeBackground.jsx +13 -2
- package/dist/ApexUI-Kit/templates/Testimonials/Testimonials.jsx +11 -8
- package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeAnimation.js +40 -69
- package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.css +1 -1
- package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.jsx +25 -7
- package/dist/ApexUI-Kit/templates/ToolTip/ToolTip.jsx +71 -52
- package/dist/cli/index.js +24 -13
- package/package.json +2 -2
- package/dist/ApexUI-Kit/templates/WaterDropReveal/WaterDropReveal.jsx +0 -151
|
@@ -2,12 +2,15 @@ import React, { useRef, useState, useEffect } from 'react';
|
|
|
2
2
|
import gsap from 'gsap';
|
|
3
3
|
|
|
4
4
|
const fields = [
|
|
5
|
-
{ name: 'firstName', label: 'First Name', type: 'text' },
|
|
6
|
-
{ name: 'lastName', label: 'Last Name', type: 'text' },
|
|
5
|
+
{ name: 'firstName', label: 'First Name', type: 'text', half: true },
|
|
6
|
+
{ name: 'lastName', label: 'Last Name', type: 'text', half: true },
|
|
7
7
|
{ name: 'email', label: 'Email Address', type: 'email' },
|
|
8
8
|
{ name: 'mobile', label: 'Mobile Number', type: 'tel' },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
|
+
// '#a78bfa88' + 'cc' -> '#a78bfacc'; works for #rrggbb and #rrggbbaa colours.
|
|
12
|
+
const withAlpha = (hex, alpha) => `${hex.slice(0, 7)}${alpha}`;
|
|
13
|
+
|
|
11
14
|
const EdgeGlowForm = ({
|
|
12
15
|
GlowColor = 'violet',
|
|
13
16
|
borderGlowColor = '#a78bfa88',
|
|
@@ -24,49 +27,51 @@ const EdgeGlowForm = ({
|
|
|
24
27
|
const ballsRef = [useRef(), useRef(), useRef()];
|
|
25
28
|
const checkTick = useRef();
|
|
26
29
|
|
|
27
|
-
//
|
|
30
|
+
// Edge glow that follows the pointer (mouse, pen or touch)
|
|
28
31
|
const [radialPos, setRadialPos] = useState({ x: 50, y: 50 });
|
|
32
|
+
const [glowing, setGlowing] = useState(false);
|
|
29
33
|
const bgRef = useRef();
|
|
30
34
|
|
|
31
|
-
const
|
|
35
|
+
const edgeColor = withAlpha(borderGlowColor, '');
|
|
36
|
+
|
|
37
|
+
const handlePointerMove = e => {
|
|
32
38
|
if (!bgRef.current) return;
|
|
33
39
|
const rect = bgRef.current.getBoundingClientRect();
|
|
34
40
|
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
|
35
41
|
const y = ((e.clientY - rect.top) / rect.height) * 100;
|
|
36
42
|
setRadialPos({ x, y });
|
|
43
|
+
setGlowing(true);
|
|
37
44
|
};
|
|
38
|
-
const
|
|
45
|
+
const handlePointerLeave = () => {
|
|
39
46
|
setRadialPos({ x: 50, y: 50 });
|
|
47
|
+
setGlowing(false);
|
|
40
48
|
};
|
|
41
49
|
|
|
42
50
|
const floatLabel = (name, up, color = null) => {
|
|
43
51
|
const label = labelRefs.current[name];
|
|
44
52
|
if (label) {
|
|
45
53
|
gsap.to(label, {
|
|
46
|
-
top: up ? '
|
|
47
|
-
|
|
48
|
-
scale: up ? 0.
|
|
49
|
-
color: color ?? (up ?
|
|
50
|
-
transform: 'translateY(-4%)',
|
|
54
|
+
top: up ? '6px' : '50%',
|
|
55
|
+
yPercent: up ? 0 : -50,
|
|
56
|
+
scale: up ? 0.78 : 1,
|
|
57
|
+
color: color ?? (up ? edgeColor : '#a1a1aa'),
|
|
51
58
|
fontWeight: up ? 500 : 400,
|
|
52
59
|
duration: 0.2,
|
|
53
|
-
ease: '
|
|
54
|
-
zIndex: 10,
|
|
60
|
+
ease: 'power2.out',
|
|
55
61
|
});
|
|
56
62
|
}
|
|
57
63
|
};
|
|
58
64
|
|
|
59
|
-
const showBorder = (name, show, color =
|
|
60
|
-
|
|
61
|
-
[top, bottom].forEach(border => {
|
|
65
|
+
const showBorder = (name, show, color = borderGlowColor) => {
|
|
66
|
+
(borderRefs.current[name] || []).forEach(border => {
|
|
62
67
|
if (border) {
|
|
63
68
|
gsap.to(border, {
|
|
64
69
|
opacity: show ? 1 : 0,
|
|
65
|
-
scaleX: show ? 1 : 0.
|
|
70
|
+
scaleX: show ? 1 : 0.6,
|
|
66
71
|
background: `linear-gradient(90deg, transparent 0%, ${color} 50%, transparent 100%)`,
|
|
67
|
-
boxShadow: `0 0
|
|
68
|
-
duration: 0.
|
|
69
|
-
ease: show ? '
|
|
72
|
+
boxShadow: `0 0 10px 1px ${color === borderGlowColor ? borderGlowShadow : withAlpha(color, '44')}`,
|
|
73
|
+
duration: 0.25,
|
|
74
|
+
ease: show ? 'power2.out' : 'power2.in',
|
|
70
75
|
});
|
|
71
76
|
}
|
|
72
77
|
});
|
|
@@ -87,31 +92,34 @@ const EdgeGlowForm = ({
|
|
|
87
92
|
const handleChange = (e, name) => {
|
|
88
93
|
const v = e.target.value;
|
|
89
94
|
setValues(vals => ({ ...vals, [name]: v }));
|
|
95
|
+
if (errors[name] && v) setErrors(errs => ({ ...errs, [name]: false }));
|
|
90
96
|
if (v) floatLabel(name, true);
|
|
91
97
|
else if (focused !== name) floatLabel(name, false);
|
|
92
98
|
};
|
|
93
99
|
|
|
94
100
|
useEffect(() => {
|
|
95
101
|
fields.forEach(f => {
|
|
96
|
-
|
|
97
|
-
if (top) gsap.set(top, { opacity: 0, scaleX: 0.7 });
|
|
98
|
-
if (bottom) gsap.set(bottom, { opacity: 0, scaleX: 0.7 });
|
|
102
|
+
(borderRefs.current[f.name] || []).forEach(border => border && gsap.set(border, { opacity: 0, scaleX: 0.6 }));
|
|
99
103
|
if (labelRefs.current[f.name]) gsap.set(labelRefs.current[f.name], {
|
|
100
|
-
top: '
|
|
101
|
-
|
|
104
|
+
top: '50%',
|
|
105
|
+
yPercent: -50,
|
|
102
106
|
scale: 1,
|
|
103
|
-
color: '#
|
|
104
|
-
|
|
105
|
-
fontWeight: 400,
|
|
106
|
-
position: 'absolute',
|
|
107
|
-
pointerEvents: 'none',
|
|
108
|
-
backgroundColor: '',
|
|
109
|
-
padding: '0 4px',
|
|
110
|
-
zIndex: 10,
|
|
107
|
+
color: '#a1a1aa',
|
|
108
|
+
transformOrigin: 'left center',
|
|
111
109
|
});
|
|
112
110
|
});
|
|
113
111
|
}, []);
|
|
114
112
|
|
|
113
|
+
// Re-colour open borders and labels when the edge colour prop changes.
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
fields.forEach(f => {
|
|
116
|
+
const up = focused === f.name || values[f.name];
|
|
117
|
+
if (up && !errors[f.name]) floatLabel(f.name, true);
|
|
118
|
+
if (focused === f.name) showBorder(f.name, true);
|
|
119
|
+
});
|
|
120
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
121
|
+
}, [borderGlowColor, borderGlowShadow]);
|
|
122
|
+
|
|
115
123
|
const handleCheckbox = () => {
|
|
116
124
|
setChecked(v => {
|
|
117
125
|
const next = !v;
|
|
@@ -152,7 +160,7 @@ const EdgeGlowForm = ({
|
|
|
152
160
|
setBtnState('loading');
|
|
153
161
|
ballsRef.forEach((ref, i) => {
|
|
154
162
|
gsap.fromTo(ref.current, { y: 0 }, {
|
|
155
|
-
y: -
|
|
163
|
+
y: -10,
|
|
156
164
|
repeat: -1,
|
|
157
165
|
yoyo: true,
|
|
158
166
|
delay: i * 0.12,
|
|
@@ -162,57 +170,76 @@ const EdgeGlowForm = ({
|
|
|
162
170
|
});
|
|
163
171
|
setTimeout(() => {
|
|
164
172
|
setBtnState('success');
|
|
165
|
-
ballsRef.forEach(ref =>
|
|
173
|
+
ballsRef.forEach(ref => {
|
|
174
|
+
gsap.killTweensOf(ref.current);
|
|
175
|
+
gsap.set(ref.current, { y: 0 });
|
|
176
|
+
});
|
|
166
177
|
setTimeout(() => {
|
|
167
178
|
setBtnState('default');
|
|
168
179
|
}, 1200);
|
|
169
180
|
}, 1600);
|
|
170
181
|
};
|
|
171
182
|
|
|
172
|
-
|
|
183
|
+
const glow = `radial-gradient(circle at ${radialPos.x}% ${radialPos.y}%, ${GlowColor} 0%, transparent 55%)`;
|
|
173
184
|
|
|
174
|
-
|
|
185
|
+
return (
|
|
186
|
+
<div className="w-full">
|
|
175
187
|
<div
|
|
176
|
-
className="w-full max-w-sm mx-auto
|
|
188
|
+
className="relative w-full max-w-sm mx-auto rounded-[1.4rem] p-[2px] text-white touch-pan-y"
|
|
177
189
|
ref={bgRef}
|
|
178
|
-
|
|
179
|
-
|
|
190
|
+
onPointerMove={handlePointerMove}
|
|
191
|
+
onPointerLeave={handlePointerLeave}
|
|
180
192
|
>
|
|
181
|
-
{/*
|
|
182
|
-
<div
|
|
193
|
+
{/* Soft halo outside the card */}
|
|
194
|
+
<div
|
|
195
|
+
className="pointer-events-none absolute -inset-3 rounded-[2rem] blur-2xl transition-opacity duration-500"
|
|
196
|
+
style={{ background: glow, opacity: glowing ? 0.55 : 0.25 }}
|
|
197
|
+
/>
|
|
198
|
+
{/* 🎯 Edge glow: the gradient only shows through the 2px edge around the panel */}
|
|
199
|
+
<div
|
|
200
|
+
className="pointer-events-none absolute inset-0 rounded-[1.4rem]"
|
|
201
|
+
style={{ background: `${glow}, linear-gradient(rgba(255,255,255,0.08), rgba(255,255,255,0.08))` }}
|
|
202
|
+
/>
|
|
203
|
+
|
|
204
|
+
<div className="relative z-10 rounded-[calc(1.4rem-2px)] bg-[#0a0a0f] px-5 py-6 sm:px-7 sm:py-7 overflow-hidden">
|
|
205
|
+
{/* faint dotted texture */}
|
|
183
206
|
<div
|
|
184
|
-
className="absolute inset-0 opacity-
|
|
185
|
-
style={{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
className="pointer-events-none absolute inset-0 opacity-[0.07]"
|
|
208
|
+
style={{ backgroundImage: 'radial-gradient(rgba(255,255,255,0.9) 1px, transparent 1px)', backgroundSize: '14px 14px' }}
|
|
209
|
+
/>
|
|
210
|
+
|
|
211
|
+
<div className="relative mb-6 flex items-start gap-3">
|
|
212
|
+
<span
|
|
213
|
+
className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-white/10"
|
|
214
|
+
style={{ background: `linear-gradient(135deg, ${withAlpha(borderGlowColor, '40')}, transparent)`, color: edgeColor }}
|
|
215
|
+
>
|
|
216
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.6" stroke="currentColor" className="h-5 w-5">
|
|
217
|
+
<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" />
|
|
218
|
+
</svg>
|
|
219
|
+
</span>
|
|
220
|
+
<div>
|
|
221
|
+
<h2 className="text-xl font-semibold tracking-tight">Get in touch</h2>
|
|
222
|
+
<p className="mt-0.5 text-[0.8rem] text-zinc-400">We usually reply within a day.</p>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<form className="relative grid grid-cols-2 gap-3" onSubmit={handleBtnClick} noValidate>
|
|
198
227
|
{fields.map(f => (
|
|
199
|
-
<div key={f.name} className=
|
|
228
|
+
<div key={f.name} className={`relative ${f.half ? 'col-span-2 sm:col-span-1' : 'col-span-2'}`}>
|
|
200
229
|
<label
|
|
201
230
|
ref={el => (labelRefs.current[f.name] = el)}
|
|
202
|
-
|
|
231
|
+
htmlFor={`edge-glow-${f.name}`}
|
|
232
|
+
className="pointer-events-none absolute left-3.5 z-10 whitespace-nowrap text-sm"
|
|
203
233
|
>{f.label}</label>
|
|
204
234
|
<span
|
|
205
235
|
ref={el => {
|
|
206
236
|
if (!borderRefs.current[f.name]) borderRefs.current[f.name] = [];
|
|
207
237
|
borderRefs.current[f.name][0] = el;
|
|
208
238
|
}}
|
|
209
|
-
className="pointer-events-none absolute left-1/2 -
|
|
210
|
-
style={{
|
|
211
|
-
background: `linear-gradient(90deg, transparent 0%, yellow 20%, transparent 100%)`,
|
|
212
|
-
boxShadow: `0 0 8px 0 ${borderGlowShadow}`,
|
|
213
|
-
}}
|
|
239
|
+
className="pointer-events-none absolute left-1/2 top-0 z-20 h-[1.5px] w-[80%] -translate-x-1/2 rounded-full opacity-0"
|
|
214
240
|
/>
|
|
215
241
|
<input
|
|
242
|
+
id={`edge-glow-${f.name}`}
|
|
216
243
|
type={f.type}
|
|
217
244
|
name={f.name}
|
|
218
245
|
value={values[f.name]}
|
|
@@ -220,78 +247,69 @@ const EdgeGlowForm = ({
|
|
|
220
247
|
onFocus={() => handleFocus(f.name)}
|
|
221
248
|
onBlur={() => handleBlur(f.name)}
|
|
222
249
|
autoComplete="off"
|
|
223
|
-
className={`w-full px-
|
|
250
|
+
className={`h-[3.25rem] w-full rounded-xl border bg-white/[0.04] px-3.5 pt-4 text-sm text-white transition-colors focus:bg-white/[0.06] focus:outline-none ${errors[f.name] ? 'border-red-400/70' : 'border-white/10 hover:border-white/20'}`}
|
|
224
251
|
/>
|
|
225
252
|
<span
|
|
226
253
|
ref={el => {
|
|
227
254
|
if (!borderRefs.current[f.name]) borderRefs.current[f.name] = [];
|
|
228
255
|
borderRefs.current[f.name][1] = el;
|
|
229
256
|
}}
|
|
230
|
-
className="pointer-events-none absolute left-1/2 -
|
|
231
|
-
style={{
|
|
232
|
-
background: `linear-gradient(90deg, transparent 0%, ${borderGlowColor} 20%, transparent 100%)`,
|
|
233
|
-
boxShadow: `0 0 8px 0 ${borderGlowShadow}`,
|
|
234
|
-
}}
|
|
257
|
+
className="pointer-events-none absolute bottom-0 left-1/2 h-[1.5px] w-[80%] -translate-x-1/2 rounded-full opacity-0"
|
|
235
258
|
/>
|
|
236
259
|
</div>
|
|
237
260
|
))}
|
|
238
261
|
|
|
239
|
-
<div className="
|
|
262
|
+
<div className="relative col-span-2 mt-1 flex select-none items-center gap-3">
|
|
240
263
|
<button
|
|
241
264
|
type="button"
|
|
265
|
+
role="checkbox"
|
|
242
266
|
aria-checked={checked}
|
|
243
|
-
|
|
267
|
+
aria-label="I agree to the terms"
|
|
244
268
|
onClick={handleCheckbox}
|
|
245
|
-
className=
|
|
246
|
-
style={{
|
|
269
|
+
className="relative flex h-5 w-5 min-w-5 items-center justify-center rounded-md border border-white/25 bg-white/5 transition focus:outline-none focus-visible:ring-2"
|
|
270
|
+
style={{ borderColor: checked ? edgeColor : undefined, '--tw-ring-color': edgeColor }}
|
|
247
271
|
>
|
|
248
|
-
<svg
|
|
249
|
-
|
|
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" />
|
|
272
|
+
<svg ref={checkTick} width="14" height="14" viewBox="0 0 18 18" style={{ color: edgeColor }}>
|
|
273
|
+
<polyline points="3,10 7,14 15,6" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round" />
|
|
255
274
|
</svg>
|
|
256
275
|
</button>
|
|
257
|
-
<span className="text-
|
|
276
|
+
<span className="text-sm text-zinc-400">I agree to the <span className="text-zinc-200 underline decoration-white/20 underline-offset-2">terms</span></span>
|
|
258
277
|
{checkboxError && (
|
|
259
|
-
<div className="absolute -left-0 -
|
|
260
|
-
<div className="bg-red-500
|
|
261
|
-
<div className="
|
|
278
|
+
<div className="absolute -top-9 left-0 z-50 flex flex-col items-start">
|
|
279
|
+
<div className="rounded-md bg-red-500 px-2.5 py-1 text-xs font-medium text-white shadow-lg">Please accept the terms</div>
|
|
280
|
+
<div className="ml-1.5 -mt-1 h-2.5 w-2.5 rotate-45 bg-red-500" />
|
|
262
281
|
</div>
|
|
263
282
|
)}
|
|
264
283
|
</div>
|
|
284
|
+
|
|
265
285
|
{/* Animated Submit Button */}
|
|
266
286
|
<button
|
|
267
287
|
type="submit"
|
|
268
|
-
className="
|
|
269
|
-
style={{
|
|
270
|
-
onClick={handleBtnClick}
|
|
288
|
+
className="relative col-span-2 mt-3 flex min-h-12 w-full items-center justify-center overflow-hidden rounded-xl text-sm font-semibold text-white shadow-lg transition hover:brightness-110 active:scale-[0.99]"
|
|
289
|
+
style={{ background: `linear-gradient(110deg, ${GlowColor}, ${edgeColor})`, boxShadow: `0 10px 30px -12px ${edgeColor}` }}
|
|
271
290
|
disabled={btnState !== 'default'}
|
|
272
291
|
>
|
|
273
292
|
{/* Bouncing Balls */}
|
|
274
|
-
<span className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2
|
|
275
|
-
<span ref={ballsRef[0]} className="
|
|
276
|
-
<span ref={ballsRef[1]} className="
|
|
277
|
-
<span ref={ballsRef[2]} className="
|
|
293
|
+
<span className={`absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 gap-1 transition-opacity duration-200 ${btnState === 'loading' ? 'opacity-100' : 'opacity-0'}`}>
|
|
294
|
+
<span ref={ballsRef[0]} className="inline-block h-2 w-2 rounded-full bg-white/90" />
|
|
295
|
+
<span ref={ballsRef[1]} className="inline-block h-2 w-2 rounded-full bg-white/90" />
|
|
296
|
+
<span ref={ballsRef[2]} className="inline-block h-2 w-2 rounded-full bg-white/90" />
|
|
278
297
|
</span>
|
|
279
298
|
{/* Checkmark */}
|
|
280
|
-
<span
|
|
281
|
-
|
|
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>
|
|
299
|
+
<span className={`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transition-opacity duration-200 ${btnState === 'success' ? 'opacity-100' : 'opacity-0'}`}>
|
|
300
|
+
<svg width="22" height="22" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 13l4 4L19 7" /></svg>
|
|
285
301
|
</span>
|
|
286
302
|
{/* Default text */}
|
|
287
|
-
<span className={`transition-opacity duration-200 ${btnState === 'default' ? 'opacity-100' : 'opacity-0'}`}>
|
|
303
|
+
<span className={`flex items-center gap-2 transition-opacity duration-200 ${btnState === 'default' ? 'opacity-100' : 'opacity-0'}`}>
|
|
304
|
+
Send Message
|
|
305
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
|
|
306
|
+
</span>
|
|
288
307
|
</button>
|
|
289
308
|
</form>
|
|
290
309
|
</div>
|
|
291
310
|
</div>
|
|
292
311
|
</div>
|
|
293
|
-
|
|
294
312
|
);
|
|
295
|
-
}
|
|
313
|
+
};
|
|
296
314
|
|
|
297
315
|
export default EdgeGlowForm;
|
|
@@ -20,17 +20,21 @@ const TwitterIcon = () => (
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
const ACCENT = "#B0DC3C";
|
|
24
|
+
const stop = (e) => e.stopPropagation();
|
|
25
|
+
const socialBtn = "flex h-10 w-10 items-center justify-center rounded-xl border border-white/10 bg-white/[0.04] text-white/70 transition-colors hover:border-[#B0DC3C]/50 hover:text-[#B0DC3C]";
|
|
26
|
+
|
|
23
27
|
const FlipProfile = ({
|
|
24
28
|
name = "Iron Man",
|
|
25
29
|
role = "AI Engineer",
|
|
26
30
|
dob = "5th March, 1999",
|
|
27
|
-
avatar = "/assets/ironman.png",
|
|
31
|
+
avatar = "https://ik.imagekit.io/ny6yinyut/apexui/assets/ironman.png?tr=f-auto,q-80,w-480",
|
|
28
32
|
github = "#",
|
|
29
33
|
linkedin = "#",
|
|
30
34
|
twitter = "#",
|
|
31
35
|
resumeLink = "/Iron-Man-Resume.pdf",
|
|
32
36
|
hireLink = "#",
|
|
33
|
-
bgColor = "#0F172B"
|
|
37
|
+
bgColor = "#0F172B"
|
|
34
38
|
}) => {
|
|
35
39
|
const [isFlipped, setIsFlipped] = useState(false);
|
|
36
40
|
|
|
@@ -44,19 +48,28 @@ const FlipProfile = ({
|
|
|
44
48
|
hidden: { opacity: 0 },
|
|
45
49
|
visible: {
|
|
46
50
|
opacity: 1,
|
|
47
|
-
transition: { staggerChildren: 0.
|
|
51
|
+
transition: { staggerChildren: 0.08, delayChildren: 0.3 }
|
|
48
52
|
}
|
|
49
53
|
};
|
|
50
54
|
|
|
51
55
|
const itemVariants = {
|
|
52
|
-
hidden: { opacity: 0, y:
|
|
56
|
+
hidden: { opacity: 0, y: 16 },
|
|
53
57
|
visible: { opacity: 1, y: 0 }
|
|
54
58
|
};
|
|
55
59
|
|
|
60
|
+
// Shared face styles: accent glow at the top and a faint grid
|
|
61
|
+
const faceStyle = {
|
|
62
|
+
backgroundColor: bgColor,
|
|
63
|
+
backgroundImage: `radial-gradient(120% 60% at 50% 0%, ${ACCENT}26, transparent 60%), linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px)`,
|
|
64
|
+
backgroundSize: "auto, 22px 22px, 22px 22px",
|
|
65
|
+
backfaceVisibility: "hidden",
|
|
66
|
+
WebkitBackfaceVisibility: "hidden",
|
|
67
|
+
};
|
|
68
|
+
|
|
56
69
|
return (
|
|
57
|
-
<div className="w-full flex items-center justify-center p-4
|
|
70
|
+
<div className="w-full flex items-center justify-center p-4" style={{ perspective: "1200px" }}>
|
|
58
71
|
<motion.div
|
|
59
|
-
className="relative w-
|
|
72
|
+
className="relative w-[300px] max-w-full h-[420px] cursor-pointer"
|
|
60
73
|
style={{ transformStyle: "preserve-3d" }}
|
|
61
74
|
variants={cardVariants}
|
|
62
75
|
animate={isFlipped ? "flipped" : "initial"}
|
|
@@ -64,62 +77,81 @@ const FlipProfile = ({
|
|
|
64
77
|
onClick={() => setIsFlipped(!isFlipped)}
|
|
65
78
|
>
|
|
66
79
|
{/* --- FRONT OF THE CARD --- */}
|
|
67
|
-
<
|
|
68
|
-
className="absolute
|
|
69
|
-
style={
|
|
70
|
-
background: bgColor,
|
|
71
|
-
backfaceVisibility: "hidden"
|
|
72
|
-
}}
|
|
80
|
+
<div
|
|
81
|
+
className="absolute inset-0 rounded-[1.75rem] overflow-hidden border border-white/10 shadow-2xl shadow-black/50 flex flex-col items-center p-6 text-center text-white"
|
|
82
|
+
style={faceStyle}
|
|
73
83
|
>
|
|
74
|
-
<div className="
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
<div className="flex w-full items-center justify-between">
|
|
85
|
+
<span className="rounded-full border border-[#B0DC3C]/30 bg-[#B0DC3C]/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-[#B0DC3C]">Profile</span>
|
|
86
|
+
<span className="flex h-8 w-8 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white/60" aria-hidden="true">
|
|
87
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7L21 8" /><path d="M21 3v5h-5" /></svg>
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Avatar with a slowly spinning accent ring */}
|
|
92
|
+
<div className="relative mt-6 h-36 w-36">
|
|
93
|
+
<div className="absolute inset-0 rounded-full animate-[spin_6s_linear_infinite]" style={{ background: `conic-gradient(from 0deg, ${ACCENT}, transparent 35%, ${ACCENT}55 60%, transparent 80%, ${ACCENT})` }} />
|
|
94
|
+
<div className="absolute inset-[3px] rounded-full" style={{ backgroundColor: bgColor }} />
|
|
95
|
+
<img src={avatar} alt={name} className="absolute inset-[7px] h-[calc(100%-14px)] w-[calc(100%-14px)] rounded-full object-cover" />
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<h2 className="mt-5 text-3xl font-bold tracking-tight">{name}</h2>
|
|
99
|
+
<p className="mt-1.5 rounded-full bg-white/5 px-3 py-1 text-sm font-medium text-[#B0DC3C]">{role}</p>
|
|
100
|
+
|
|
101
|
+
<p className="mt-auto flex items-center gap-1.5 text-xs text-white/50">
|
|
102
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 11V6a3 3 0 0 1 6 0v8" /><path d="M5 15a7 7 0 0 0 14 0v-1a2 2 0 0 0-4 0" /></svg>
|
|
103
|
+
Tap to flip
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
80
106
|
|
|
81
107
|
{/* --- BACK OF THE CARD --- */}
|
|
82
|
-
<
|
|
83
|
-
className="absolute
|
|
84
|
-
style={{
|
|
85
|
-
background: bgColor,
|
|
86
|
-
backfaceVisibility: "hidden",
|
|
87
|
-
transform: "rotateY(180deg)"
|
|
88
|
-
}}
|
|
108
|
+
<div
|
|
109
|
+
className="absolute inset-0 rounded-[1.75rem] overflow-hidden border border-[#B0DC3C]/30 shadow-2xl shadow-black/50 p-6 text-white"
|
|
110
|
+
style={{ ...faceStyle, transform: "rotateY(180deg)" }}
|
|
89
111
|
>
|
|
90
112
|
<AnimatePresence>
|
|
91
113
|
{isFlipped && (
|
|
92
114
|
<motion.div
|
|
93
|
-
className="flex
|
|
115
|
+
className="flex h-full w-full flex-col"
|
|
94
116
|
variants={backContentVariants}
|
|
95
117
|
initial="hidden"
|
|
96
118
|
animate="visible"
|
|
97
119
|
exit="hidden"
|
|
98
120
|
>
|
|
99
|
-
<motion.
|
|
100
|
-
|
|
121
|
+
<motion.div variants={itemVariants} className="flex items-center gap-3">
|
|
122
|
+
<img src={avatar} alt="" className="h-12 w-12 rounded-full border-2 border-[#B0DC3C] object-cover" />
|
|
123
|
+
<div className="min-w-0 text-left">
|
|
124
|
+
<p className="truncate text-lg font-bold leading-tight">{name}</p>
|
|
125
|
+
<p className="truncate text-xs text-[#B0DC3C]">{role}</p>
|
|
126
|
+
</div>
|
|
127
|
+
</motion.div>
|
|
128
|
+
|
|
129
|
+
<motion.div variants={itemVariants} className="mt-5 rounded-2xl border border-white/10 bg-white/[0.04] p-4 text-left">
|
|
130
|
+
<p className="text-[10px] font-semibold uppercase tracking-[0.16em] text-white/40">Born</p>
|
|
131
|
+
<p className="mt-1 text-sm font-medium text-white/90">{dob}</p>
|
|
132
|
+
</motion.div>
|
|
101
133
|
|
|
102
|
-
<motion.div variants={itemVariants} className="flex
|
|
103
|
-
<a href={github} target="_blank" rel="noopener noreferrer"
|
|
104
|
-
<a href={linkedin} target="_blank" rel="noopener noreferrer"
|
|
105
|
-
<a href={twitter} target="_blank" rel="noopener noreferrer"
|
|
134
|
+
<motion.div variants={itemVariants} className="mt-4 flex items-center gap-2.5">
|
|
135
|
+
<a href={github} target="_blank" rel="noopener noreferrer" onClick={stop} aria-label="GitHub" className={socialBtn}><GitHubIcon /></a>
|
|
136
|
+
<a href={linkedin} target="_blank" rel="noopener noreferrer" onClick={stop} aria-label="LinkedIn" className={socialBtn}><LinkedInIcon /></a>
|
|
137
|
+
<a href={twitter} target="_blank" rel="noopener noreferrer" onClick={stop} aria-label="X (Twitter)" className={socialBtn}><TwitterIcon /></a>
|
|
106
138
|
</motion.div>
|
|
107
139
|
|
|
108
|
-
<motion.div variants={itemVariants} className="mt-
|
|
109
|
-
<motion.a whileTap={{ scale: 0.
|
|
140
|
+
<motion.div variants={itemVariants} className="mt-auto flex flex-col gap-2.5">
|
|
141
|
+
<motion.a whileTap={{ scale: 0.97 }} href={hireLink} onClick={stop} className="w-full rounded-xl bg-[#B0DC3C] py-2.5 text-center text-sm font-bold text-slate-950 transition hover:brightness-110">
|
|
110
142
|
Hire Me
|
|
111
143
|
</motion.a>
|
|
112
|
-
<motion.a whileTap={{ scale: 0.
|
|
144
|
+
<motion.a whileTap={{ scale: 0.97 }} href={resumeLink} download onClick={stop} className="w-full rounded-xl border border-white/15 bg-white/5 py-2.5 text-center text-sm font-semibold text-white transition hover:bg-white/10">
|
|
113
145
|
Download CV
|
|
114
146
|
</motion.a>
|
|
115
147
|
</motion.div>
|
|
116
148
|
</motion.div>
|
|
117
149
|
)}
|
|
118
150
|
</AnimatePresence>
|
|
119
|
-
</
|
|
151
|
+
</div>
|
|
120
152
|
</motion.div>
|
|
121
153
|
</div>
|
|
122
154
|
);
|
|
123
155
|
};
|
|
124
156
|
|
|
125
|
-
export default FlipProfile;
|
|
157
|
+
export default FlipProfile;
|
|
@@ -5,7 +5,7 @@ function cn(...classes) {
|
|
|
5
5
|
}
|
|
6
6
|
import React, { useRef } from "react";
|
|
7
7
|
import { motion, useMotionValue, useSpring, useMotionTemplate } from "framer-motion";
|
|
8
|
-
|
|
8
|
+
const bgHexa = 'https://ik.imagekit.io/ny6yinyut/apexui/assets/bg-hexa.jpg?tr=f-auto,q-80';
|
|
9
9
|
export default function GlareCardDemo() {
|
|
10
10
|
return (
|
|
11
11
|
<GlareCard className="flex flex-col items-center justify-center bg-gradient-to-br from-gray-900 via-slate-800 to-[#181825] p-8 border border-slate-800 shadow-2xl relative overflow-hidden group group-hover:scale-100 transition-transform duration-300 ease-in-out">
|
|
@@ -78,7 +78,7 @@ export function GlareCard({
|
|
|
78
78
|
return (
|
|
79
79
|
<motion.div
|
|
80
80
|
style={style}
|
|
81
|
-
className="relative isolate [contain:layout_style] [perspective:600px] transition-transform duration-[var(--duration)] ease-[var(--easing)] delay-[var(--delay)] will-change-transform w-[320px] [aspect-ratio:17/21]"
|
|
81
|
+
className="relative isolate [contain:layout_style] [perspective:600px] transition-transform duration-[var(--duration)] ease-[var(--easing)] delay-[var(--delay)] will-change-transform w-full max-w-[320px] [aspect-ratio:17/21]"
|
|
82
82
|
ref={refElement}
|
|
83
83
|
onPointerMove={(event) => {
|
|
84
84
|
const rotateFactor = 0.4;
|