added MoreSection component to profile page
This commit is contained in:
44
components/Links.tsx
Normal file
44
components/Links.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { PROFILE } from "@/data/content";
|
||||
import LinkIcon from "./LinkIcon";
|
||||
import { useStaggerReveal } from "@/hooks/useAnimations";
|
||||
|
||||
export default function Links() {
|
||||
const [hovered, setHovered] = useState<number | null>(null);
|
||||
const visible = useStaggerReveal(PROFILE.links.length + PROFILE.contactMethods.length, 50);
|
||||
return (
|
||||
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
|
||||
{PROFILE.links.map((link, i) => (
|
||||
<a key={link.label}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onMouseEnter={() => setHovered(i)}
|
||||
onMouseLeave={() => setHovered(null)}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
padding: "10px 18px",
|
||||
fontFamily: "var(--mono)",
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
color: hovered === i ? "var(--accent)" : "var(--fg-secondary)",
|
||||
background: hovered === i ? "var(--accent-bg)" : "var(--surface)",
|
||||
border: `1px solid ${hovered === i ? "var(--accent)" : "var(--border)"}`,
|
||||
borderRadius: 6,
|
||||
textDecoration: "none",
|
||||
transition: "all var(--duration-slow)",
|
||||
transform: hovered === i ? "translateY(-2px)" : "none",
|
||||
opacity: visible.has(i) ? 1.0 : 0
|
||||
}}
|
||||
>
|
||||
<LinkIcon type={link.icon} />
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { PROFILE } from "@/data/content";
|
||||
import LinkIcon from "./LinkIcon";
|
||||
import ContactMethods from "./ContactMethods";
|
||||
import { useStaggerReveal } from "@/hooks/useAnimations";
|
||||
import Links from "./Links";
|
||||
import ProfileMore from "./ProfileMore";
|
||||
|
||||
export default function Profile() {
|
||||
const [hovered, setHovered] = useState<number | null>(null);
|
||||
const visible = useStaggerReveal(PROFILE.links.length + PROFILE.contactMethods.length, 100);
|
||||
return (
|
||||
<div style={{ padding: "48px 0", maxWidth: 640 }}>
|
||||
@@ -59,37 +58,8 @@ export default function Profile() {
|
||||
>
|
||||
{PROFILE.bio}
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
|
||||
{PROFILE.links.map((link, i) => (
|
||||
<a key={link.label}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onMouseEnter={() => setHovered(i)}
|
||||
onMouseLeave={() => setHovered(null)}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
padding: "10px 18px",
|
||||
fontFamily: "var(--mono)",
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
color: hovered === i ? "var(--accent)" : "var(--fg-secondary)",
|
||||
background: hovered === i ? "var(--accent-bg)" : "var(--surface)",
|
||||
border: `1px solid ${hovered === i ? "var(--accent)" : "var(--border)"}`,
|
||||
borderRadius: 6,
|
||||
textDecoration: "none",
|
||||
transition: "all var(--duration-slow)",
|
||||
transform: hovered === i ? "translateY(-2px)" : "none",
|
||||
opacity: visible.has(i) ? 1.0 : 0
|
||||
}}
|
||||
>
|
||||
<LinkIcon type={link.icon} />
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<ProfileMore/>
|
||||
<Links/>
|
||||
<ContactMethods methods={PROFILE.contactMethods} visibleCount={PROFILE.links.length} visibleComponent={visible} isVisible={visible.has(PROFILE.links.length-1)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
114
components/ProfileMore.tsx
Normal file
114
components/ProfileMore.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { MoreSection, PROFILE } from "@/data/content";
|
||||
import { useStaggerReveal, useWiggle } from "@/hooks/useAnimations";
|
||||
import "@/styles/more-section.css"
|
||||
|
||||
export default function ProfileMore() {
|
||||
const [delayTimer, setDelayTimer] = useState<number | null>(null);
|
||||
const [hovered, setHovered] = useState<boolean | null>(null);
|
||||
const [morePosition, setMorePosition] = useState<number>(PROFILE.moreSectionsStart ?? 0);
|
||||
|
||||
|
||||
|
||||
const { wiggling, progress, handlers } = useWiggle(500, () => {});
|
||||
|
||||
function handleSectionExpand(){
|
||||
setMorePosition(morePosition + 1);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="more-section-container"
|
||||
style={{
|
||||
minHeight: "100px",
|
||||
width: "100%",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
{PROFILE.moreSections.map((item: MoreSection, i)=>{
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
style={{
|
||||
// display: i < morePosition ? "block" : "none",
|
||||
opacity: i < morePosition ? 1.0 : 0.0,
|
||||
transition: "all 0.3s ease",
|
||||
overflow: "hidden",
|
||||
minHeight: i < morePosition ? "5em" : "0em",
|
||||
height: i < morePosition ? "auto" : "0",
|
||||
}}>
|
||||
{item.title && (<h3
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
fontFamily: "var(--mono)",
|
||||
fontSize: 11,
|
||||
color: "var(--muted)",
|
||||
letterSpacing: "0.08em",
|
||||
textTransform: "uppercase",
|
||||
}}>
|
||||
{item.title}
|
||||
</h3>)}
|
||||
<p style={{
|
||||
fontFamily: "var(--sans)",
|
||||
fontSize: 16,
|
||||
lineHeight: 1.7,
|
||||
color: "var(--fg-secondary)",
|
||||
margin: "0 0 40px 0",
|
||||
maxWidth: 560,
|
||||
}}>{item.text}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div {...handlers} className={`drop-handle ${wiggling ? "wiggle" : ""}`}
|
||||
onMouseEnter={() => {
|
||||
if (delayTimer) return;
|
||||
setHovered(true);
|
||||
|
||||
let timer = window.setTimeout(() => {
|
||||
handleSectionExpand();
|
||||
}, 500);
|
||||
setDelayTimer(timer);
|
||||
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (delayTimer){
|
||||
clearTimeout(delayTimer);
|
||||
setDelayTimer(null);
|
||||
}
|
||||
setHovered(false);
|
||||
}}
|
||||
style={{
|
||||
opacity: morePosition < PROFILE.moreSections.length ? 1.0 : 0.0,
|
||||
backgroundColor: hovered ? "var(--surface)" : "var(--bg-raised",
|
||||
width: "100%",
|
||||
height: "2em",
|
||||
display: "flex",
|
||||
gap: 12,
|
||||
margin: "0 0 40px 0",
|
||||
border: `1px solid ${hovered ? "var(--accent)" : "var(--border)"}`,
|
||||
bottom: hovered ? "-10px" : "0px",
|
||||
position: "relative",
|
||||
}}>
|
||||
|
||||
<svg height={"30px"} width={"30px"} fill="var(--fg-secondary)" version="1.1" id="Capa_1" style={{scale:"0.5"}} >
|
||||
<g>
|
||||
<path d="M29.994,10.183L15.363,24.812L0.733,10.184c-0.977-0.978-0.977-2.561,0-3.536c0.977-0.977,2.559-0.976,3.536,0
|
||||
l11.095,11.093L26.461,6.647c0.977-0.976,2.559-0.976,3.535,0C30.971,7.624,30.971,9.206,29.994,10.183z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<span style={{
|
||||
fontFamily: "var(--sans)",
|
||||
fontSize: 12,
|
||||
lineHeight: 0.5,
|
||||
color: "var(--fg-secondary)",
|
||||
alignSelf: "center",
|
||||
textTransform: "uppercase"
|
||||
}}>more?</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -65,18 +65,12 @@ export default function SimpleGallery({
|
||||
}}
|
||||
onClick={handleEnlarge}
|
||||
title="Enlarge image"
|
||||
onMouseEnter={(e) =>
|
||||
(e.currentTarget.style.background = "rgba(0,0,0,0.8)")
|
||||
}
|
||||
onMouseLeave={(e) =>
|
||||
(e.currentTarget.style.background = "rgba(0,0,0,0.55)")
|
||||
}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 5V1H5" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M13 9V13H9" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M1 1L5.5 5.5" stroke="white" strokeWidth="1.5" strokeLinecap="round" />
|
||||
<path d="M13 13L8.5 8.5" stroke="white" strokeWidth="1.5" strokeLinecap="round" />
|
||||
<svg className="action-button-svg" stroke="current" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 5V1H5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M13 9V13H9" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
<path d="M1 1L5.5 5.5" strokeWidth="1.5" strokeLinecap="round" />
|
||||
<path d="M13 13L8.5 8.5" strokeWidth="1.5" strokeLinecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
@@ -91,13 +85,7 @@ export default function SimpleGallery({
|
||||
right: 8
|
||||
}}
|
||||
onClick={handleShrink}
|
||||
title="Shrink image"
|
||||
onMouseEnter={(e) =>
|
||||
(e.currentTarget.style.background = "rgba(0,0,0,0.8)")
|
||||
}
|
||||
onMouseLeave={(e) =>
|
||||
(e.currentTarget.style.background = "rgba(0,0,0,0.55)")
|
||||
}
|
||||
title="Shrink image"
|
||||
>
|
||||
<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" width="14" height="14">
|
||||
<line x1="15" y1="15" x2="45" y2="45" stroke="white" strokeWidth="6" strokeLinecap="round" />
|
||||
|
||||
Reference in New Issue
Block a user