feat (v1.0.0): initial refactor and redesign
This commit is contained in:
parent
3058aa1ab4
commit
fe9b50b30e
134 changed files with 17792 additions and 3670 deletions
29
components/objects/AnimatedTitle.tsx
Normal file
29
components/objects/AnimatedTitle.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function AnimatedTitle() {
|
||||
useEffect(() => {
|
||||
const title = 'aidan.so';
|
||||
let index = 1;
|
||||
let forward = true;
|
||||
const interval = setInterval(() => {
|
||||
document.title = title.substring(0, index);
|
||||
if (forward) {
|
||||
index++;
|
||||
if (index > title.length) {
|
||||
forward = false;
|
||||
index = title.length - 1;
|
||||
}
|
||||
} else {
|
||||
index--;
|
||||
if (index < 1) {
|
||||
forward = true;
|
||||
index = 1;
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,22 +1,42 @@
|
|||
import { default as NextLink } from 'next/link'
|
||||
import { cn } from '@/lib/theme'
|
||||
import { externalLinkProps } from '@/lib/utils/styles'
|
||||
|
||||
interface LinkProps {
|
||||
href: string
|
||||
className?: string
|
||||
target?: string
|
||||
rel?: string
|
||||
variant?: 'default' | 'nav' | 'muted'
|
||||
external?: boolean
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export default function Link(props: LinkProps) {
|
||||
export default function Link({
|
||||
href,
|
||||
className,
|
||||
target,
|
||||
rel,
|
||||
variant = 'default',
|
||||
external,
|
||||
children
|
||||
}: LinkProps) {
|
||||
const isExternal = external || href.startsWith('http')
|
||||
|
||||
const variantStyles = {
|
||||
default: 'text-blue-400 hover:underline',
|
||||
nav: 'text-gray-300 hover:text-white',
|
||||
muted: 'text-gray-400 hover:text-gray-300'
|
||||
}
|
||||
|
||||
return (
|
||||
<NextLink
|
||||
href={props.href}
|
||||
className={`text-blue-400 hover:underline ${props.className}`}
|
||||
target={props.target}
|
||||
rel={props.rel}
|
||||
href={href}
|
||||
className={cn(variantStyles[variant], className)}
|
||||
target={target || (isExternal ? externalLinkProps.target : undefined)}
|
||||
rel={rel || (isExternal ? externalLinkProps.rel : undefined)}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</NextLink>
|
||||
)
|
||||
}
|
||||
26
components/objects/PageHeader.tsx
Normal file
26
components/objects/PageHeader.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { ReactNode } from 'react'
|
||||
|
||||
interface PageHeaderProps {
|
||||
icon: ReactNode
|
||||
title: string
|
||||
subtitle?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function PageHeader({ icon, title, subtitle, className }: PageHeaderProps) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex justify-center">
|
||||
{icon}
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold mt-2 text-center text-gray-200 glow">
|
||||
{title}
|
||||
</h1>
|
||||
{subtitle && (
|
||||
<p className="text-gray-400 text-center">{subtitle}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,83 +1,46 @@
|
|||
"use client"
|
||||
|
||||
import {
|
||||
SiNextdotjs,
|
||||
SiLucide,
|
||||
SiVercel,
|
||||
SiSimpleicons,
|
||||
SiFontawesome,
|
||||
SiShadcnui,
|
||||
SiTailwindcss
|
||||
} from "react-icons/si"
|
||||
import Link from 'next/link'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { footerMessages } from './footerMessages'
|
||||
|
||||
export const footerMessages = [
|
||||
[
|
||||
"Built with Next.js",
|
||||
"https://nextjs.org",
|
||||
<SiNextdotjs key="nextjs" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Icons by Lucide",
|
||||
"https://lucide.dev/",
|
||||
<SiLucide key="lucide" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Icons by Simple Icons",
|
||||
"https://simpleicons.org/",
|
||||
<SiSimpleicons key="simpleicons" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Font by Vercel",
|
||||
"https://vercel.com/font",
|
||||
<SiVercel key="vercel" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Icons by Font Awesome",
|
||||
"https://fontawesome.com/",
|
||||
<SiFontawesome key="fontawesome" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Components by Shadcn",
|
||||
"https://ui.shadcn.com/",
|
||||
<SiShadcnui key="shadcn" className="text-md mr-2" />
|
||||
],
|
||||
[
|
||||
"Styled with Tailwind",
|
||||
"https://tailwindcss.com/",
|
||||
<SiTailwindcss key="tailwind" className="text-md mr-2" />
|
||||
]
|
||||
]
|
||||
interface RandomFooterMsgProps {
|
||||
index?: number
|
||||
}
|
||||
|
||||
export default function RandomFooterMsg() {
|
||||
const [randomIndex, setRandomIndex] = useState(0)
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
const fallbackMessage = footerMessages[0] ?? null
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true)
|
||||
setRandomIndex(Math.floor(Math.random() * footerMessages.length))
|
||||
}, [])
|
||||
|
||||
if (!isMounted) {
|
||||
const [message, url, icon] = footerMessages[0]
|
||||
return (
|
||||
<Link href={String(url)} target="_blank" rel="noopener noreferrer" className="hover:text-white transition-colors mb-2 sm:mb-0">
|
||||
<div className="flex items-center justify-center">
|
||||
{icon}
|
||||
{message}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
const getMessageByIndex = (index: number | undefined) => {
|
||||
if (!footerMessages.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [message, url, icon] = footerMessages[randomIndex]
|
||||
if (typeof index !== 'number' || Number.isNaN(index)) {
|
||||
return fallbackMessage
|
||||
}
|
||||
|
||||
const safeIndex = ((Math.floor(index) % footerMessages.length) + footerMessages.length) % footerMessages.length
|
||||
return footerMessages[safeIndex] ?? fallbackMessage
|
||||
}
|
||||
|
||||
export default function RandomFooterMsg({ index }: RandomFooterMsgProps) {
|
||||
const message = getMessageByIndex(index)
|
||||
|
||||
if (!message) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { text, url, Icon } = message
|
||||
|
||||
return (
|
||||
<Link href={String(url)} target="_blank" rel="noopener noreferrer" className="hover:text-white transition-colors mb-2 sm:mb-0">
|
||||
<Link
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-white transition-colors mb-2 sm:mb-0"
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
{icon}
|
||||
{message}
|
||||
<Icon className="text-md mr-2" />
|
||||
{text}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
|
|
|
|||
54
components/objects/footerMessages.ts
Normal file
54
components/objects/footerMessages.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import type { IconType } from 'react-icons'
|
||||
import {
|
||||
SiFontawesome,
|
||||
SiLucide,
|
||||
SiNextdotjs,
|
||||
SiShadcnui,
|
||||
SiSimpleicons,
|
||||
SiTailwindcss,
|
||||
SiVercel
|
||||
} from 'react-icons/si'
|
||||
|
||||
export type FooterMessage = {
|
||||
text: string
|
||||
url: string
|
||||
Icon: IconType
|
||||
}
|
||||
|
||||
export const footerMessages: FooterMessage[] = [
|
||||
{
|
||||
text: 'Built with Next.js',
|
||||
url: 'https://nextjs.org',
|
||||
Icon: SiNextdotjs
|
||||
},
|
||||
{
|
||||
text: 'Icons by Lucide',
|
||||
url: 'https://lucide.dev/',
|
||||
Icon: SiLucide
|
||||
},
|
||||
{
|
||||
text: 'Icons by Simple Icons',
|
||||
url: 'https://simpleicons.org/',
|
||||
Icon: SiSimpleicons
|
||||
},
|
||||
{
|
||||
text: 'Font by Vercel',
|
||||
url: 'https://vercel.com/font',
|
||||
Icon: SiVercel
|
||||
},
|
||||
{
|
||||
text: 'Icons by Font Awesome',
|
||||
url: 'https://fontawesome.com/',
|
||||
Icon: SiFontawesome
|
||||
},
|
||||
{
|
||||
text: 'Components by Shadcn',
|
||||
url: 'https://ui.shadcn.com/',
|
||||
Icon: SiShadcnui
|
||||
},
|
||||
{
|
||||
text: 'Styled with Tailwind',
|
||||
url: 'https://tailwindcss.com/',
|
||||
Icon: SiTailwindcss
|
||||
}
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue