Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianwd committed Nov 25, 2024
1 parent 78b4e2a commit 35af35e
Show file tree
Hide file tree
Showing 17 changed files with 713 additions and 140 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@astrojs/tailwind": "^5.1.2",
"@astrojs/vercel": "^7.8.2",
"@fontsource/ia-writer-mono": "^5.0.7",
"@fontsource/inter": "^5.1.0",
"@fontsource/la-belle-aurore": "^5.0.18",
"@splinetool/react-spline": "4.0.0",
"@splinetool/runtime": "^1.9.35",
Expand All @@ -33,8 +34,10 @@
"astro-seo": "^0.8.4",
"cheerio": "1.0.0",
"dayjs": "^1.11.13",
"motion": "^11.11.17",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-use": "^17.5.1",
"swr": "^2.2.5",
"tailwind-merge": "^2.5.4",
"tailwindcss": "^3.4.14",
Expand Down
286 changes: 284 additions & 2 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/assets/devtool.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/hat.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/assets/home.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions src/components/Button.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
import type { HTMLAttributes } from 'astro/types'
import { twMerge } from 'tailwind-merge'
interface Props extends HTMLAttributes<'button'> {
variant?: 'primary' | 'secondary' | 'tertiary'
}
const { variant } = Astro.props
const variantClasses = {
primary: 'bg-primary/50',
secondary: 'bg-neutral-700/50 ',
tertiary: 'bg-neutral-800',
} as const
---

<button
class={twMerge(
'disabled:opacity-80 disabled:cursor-not-allowed shine flex w-fit items-center rounded-lg px-3 py-1.5 text-sm text-neutral-200 active:scale-95 hover:enabled:brightness-110 transition-all',
variantClasses[variant || 'primary']
)}
type='button'
>
<slot />
</button>
165 changes: 165 additions & 0 deletions src/components/Dock/Dock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import {
cloneElement,
createContext,
useContext,
useEffect,
useRef,
useState,
} from 'react'
import homeIcon from '../../assets/home.svg?raw'
import devtoolIcon from '../../assets/devtool.svg?raw'
import {
AnimatePresence,
motion,
useMotionValueEvent,
useSpring,
useTransform,
} from 'motion/react'
import { MouseProvider, useMouse } from './MouseProvider'
import { Tooltip } from '../Tooltip'

const DockItem = ({
icon,
href,
index,
total,
title,
}: {
icon: React.ReactElement
href: string
index: number
total: number
title: string
}) => {
const mouse = useMouse()

const dock = useContext(DockContext)
if (!mouse) {
throw new Error('MouseProvider not found')
}

const dimension = useTransform(mouse?.position.x, (mouseX) => {
if (!dock?.isHovered) {
return 48
}

const rangeStart = dock?.dock?.getBoundingClientRect()?.left ?? 0
const dockWidth = dock?.dock?.clientWidth ?? 1

// 0-1 range
const normalizedMouseX = (mouseX - rangeStart) / dockWidth

// Calculate normalized position of the current item's center
const itemCenterX = (index + 0.5) * (1 / total)

// Calculate the difference between mouse position and item center
const distanceFromCenter = Math.abs(normalizedMouseX - itemCenterX)

// Maximum extra scaling amount
const scalingMagnitude = 0.6
// How quickly the scale decreases according to distance
const distanceSensitivity = 3

const scaleFactor = Math.max(
1,
1 + scalingMagnitude * (1 - distanceFromCenter * distanceSensitivity)
)

// Apply scale factor to the base size (48)
return 48 * scaleFactor
})

const spring = useSpring(48, {
damping: 10,
stiffness: 150,
mass: 0.01,
})

console.log('1111', dimension.get())

useMotionValueEvent(dimension, 'change', (latest) => {
if (dock?.isHovered) {
spring.set(latest)
} else {
spring.set(48)
}
})

return (
<motion.a
className='relative flex items-center justify-center rounded-lg text-stone-400 shadow-[0_-1px_hsl(0_0%_0%/0.5)_inset,0_2px_4px_hsl(0_0%_0%_/_0.5),0_1px_hsl(0_0%_100%/0.5)_inset] transition-colors [background:linear-gradient(hsl(0_0%_100%/0.15),#0000),hsl(0_0%_4%)] hover:text-stone-300'
href={href}
style={{
width: spring,
height: spring,
}}
>
<Tooltip text={title}>
{cloneElement(icon, {
className: 'transition-colors size-3/5',
})}
</Tooltip>
</motion.a>
)
}

type DockContext = {
isHovered: boolean
dock: HTMLDivElement | null
}

const DockContext = createContext<DockContext | null>(null)

const dockItems = [
{
icon: <span dangerouslySetInnerHTML={{ __html: homeIcon }} />,
href: '/',
title: 'Home',
},
{
icon: <span dangerouslySetInnerHTML={{ __html: devtoolIcon }} />,
href: '/',
title: 'Projects',
},
]

export const Dock = () => {
const ref = useRef<HTMLDivElement | null>(null)

const [isHovered, setIsHovered] = useState(false)

const [dock, setDock] = useState<HTMLDivElement | null>(null)

useEffect(() => {
setDock(ref.current)
}, [])

return (
<MouseProvider>
<DockContext.Provider value={{ isHovered, dock }}>
<footer className='fixed -bottom-3 left-1/2 z-50 -translate-x-1/2 -translate-y-1/2'>
<motion.div
className='box-content flex h-12 items-end justify-center gap-4 rounded-xl border border-solid border-zinc-800 bg-neutral-800/60 px-4 py-1.5 backdrop-blur-sm'
onHoverStart={() => {
setIsHovered(true)
}}
onHoverEnd={() => {
setIsHovered(false)
}}
ref={ref}
>
{dockItems.map((item, index) => (
<DockItem
key={index}
{...item}
title={item.title}
index={index}
total={dockItems.length}
/>
))}
</motion.div>
</footer>
</DockContext.Provider>
</MouseProvider>
)
}
52 changes: 52 additions & 0 deletions src/components/Dock/MouseProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { type MotionValue, useMotionValue, useVelocity } from 'motion/react'
import { createContext, useContext, useMemo, type ReactNode } from 'react'
import { useEvent } from 'react-use'

type MouseType = {
position: {
x: MotionValue<number>
y: MotionValue<number>
}
velocity: {
x: MotionValue<number>
y: MotionValue<number>
}
}

const useMousePosition = () => {
const x = useMotionValue(0)
const y = useMotionValue(0)

useEvent('mousemove', (e) => {
x.set(e.clientX)
y.set(e.clientY)
})

return useMemo(() => ({ x, y }), [x, y])
}

const MouseContext = createContext<MouseType | null>(null)

export const useMouse = () => {
if (!MouseContext) {
throw new Error('useMouse must be used within a MouseProvider')
}

return useContext(MouseContext)
}

export const MouseProvider = ({ children }: { children: ReactNode }) => {
const { x, y } = useMousePosition()
const velocityX = useVelocity(x)
const velocityY = useVelocity(y)

const mouse = useMemo(
() => ({
position: { x, y },
velocity: { x: velocityX, y: velocityY },
}),
[x, y, velocityX, velocityY]
)

return <MouseContext.Provider value={mouse}>{children}</MouseContext.Provider>
}
18 changes: 18 additions & 0 deletions src/components/FeaturedProject.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
import type { HTMLAttributes } from 'astro/types'
import { twMerge } from 'tailwind-merge'
interface Props extends HTMLAttributes<'button'> {
variant?: 'primary' | 'secondary' | 'tertiary'
}
const { variant } = Astro.props
const variantClasses = {
primary: 'bg-primary/50',
secondary: 'bg-neutral-700/50 ',
tertiary: 'bg-neutral-800',
} as const
---

<div></div>
6 changes: 3 additions & 3 deletions src/components/PageViews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const PageViews = () => {
{error && <div>Error: {String(error)}</div>}
{data && (
<div className='flex flex-col'>
<h2 className='mb-1 font-clvtc text-2xl text-primary'>Visits</h2>
<p className='flex font-iamono'>
<h2 className='mb-1 font-clvtc text-xl text-primary'>Visits</h2>
<p className='font-primary flex'>
Total:{' '}
<span className='relative ml-auto'>
<span className='absolute right-0 text-surface-200'>000000</span>
Expand All @@ -26,7 +26,7 @@ export const PageViews = () => {
</span>
</span>
</p>
<p className='flex font-iamono'>
<p className='font-primary flex'>
Unique:{' '}
<span className='relative ml-auto'>
<span className='absolute right-0 text-surface-200'>000000</span>
Expand Down
23 changes: 14 additions & 9 deletions src/components/SpotifyActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Suspense, useRef, useState } from 'react'
import useSWR from 'swr'
import { twMerge } from 'tailwind-merge'
import { ScrollyText } from './ScrollyText'
import spotifyIcon from '../assets/spotify.svg'
import { WaveIcon } from './icons/WaveIcon'

export const SpotifyActivity = () => {
Expand All @@ -19,15 +20,23 @@ export const SpotifyActivity = () => {
{error && <div>Error: {String(error)}</div>}
{data && (
<>
<div className='flex items-end gap-2 text-primary'>
<img src={spotifyIcon.src} className='h-9 w-fit object-cover' />
<h2 className='mr-auto font-clvtc text-xl leading-none text-primary'>
{data.isPlaying ? "I'm listening to:" : 'Recently played:'}
</h2>
<WaveIcon className='h-4 shrink-0' />
</div>
<div className='flex gap-3 md:flex-wrap lg:flex-nowrap'>
<img
className='h-20 w-auto min-w-0 shrink-0 rounded-md border border-solid border-zinc-800 md:h-auto md:w-full lg:h-[72px] lg:w-auto'
src={String(data.coverImg)}
/>
<div className='mr-auto flex min-w-0 flex-col'>
<h2 className='mb-1 font-clvtc text-2xl text-primary'>
{data.isPlaying ? "I'm listening to:" : 'Recently played:'}
</h2>
<div className='mt-auto'>
<div className='my-auto'>
<ScrollyText
animationClassName='animate-marquee'
className='font-iamono font-bold'
className='font-iamono text-sm font-bold'
text={String(data.song)}
timePerChar={30}
/>
Expand All @@ -39,10 +48,6 @@ export const SpotifyActivity = () => {
/>
</div>
</div>
<img
className='h-20 w-auto min-w-0 shrink-0 rounded-md md:h-auto md:w-full lg:h-20 lg:w-auto'
src={String(data.coverImg)}
/>
</div>
</>
)}
Expand Down
Loading

0 comments on commit 35af35e

Please sign in to comment.