Skip to content

Commit

Permalink
feat: finish home + header
Browse files Browse the repository at this point in the history
  • Loading branch information
CaliCastle committed Apr 30, 2023
1 parent a011173 commit cd40eb0
Show file tree
Hide file tree
Showing 60 changed files with 4,602 additions and 2,566 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
DATABASE_HOST=
DATABASE_USERNAME=
DATABASE_PASSWORD=
41 changes: 41 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path')

/** @type {import("eslint").Linter.Config} */
const config = {
overrides: [
{
extends: [
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
files: ['*.ts', '*.tsx'],
parserOptions: {
project: path.join(__dirname, 'tsconfig.json'),
},
},
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: path.join(__dirname, 'tsconfig.json'),
},
plugins: ['@typescript-eslint', 'simple-import-sort', 'unused-imports'],
extends: ['next/core-web-vitals', 'plugin:@typescript-eslint/recommended'],
rules: {
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
fixStyle: 'inline-type-imports',
},
],
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',

'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'warn',
'unused-imports/no-unused-imports': 'error',
},
}

module.exports = config
3 changes: 0 additions & 3 deletions .eslintrc.json

This file was deleted.

5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ yarn-error.log*

# local env files
.env*.local
.env

# vercel
.vercel
Expand All @@ -35,5 +36,5 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts

# contentlayer
.contentlayer
# vs code
.vscode
88 changes: 88 additions & 0 deletions app/(home)/Photos.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use client'

import { motion } from 'framer-motion'
import Image from 'next/image'
import React from 'react'

import image5 from '~/assets/highlights/highlight-cali.jpeg'
import image1 from '~/assets/highlights/highlight-cat.jpeg'
import image3 from '~/assets/highlights/highlight-controller.jpg'
import image6 from '~/assets/highlights/highlight-push.png'
import image2 from '~/assets/highlights/highlight-workshop.jpg'
import image4 from '~/assets/highlights/highlight-zolplay.jpg'

const images = [image1, image2, image3, image4, image5, image6]
const alts = [
'我的猫躺在我的工作台桌子上的键盘旁边',
'我在西雅图城市大学举办的技术演讲',
'Xbox 团队给我专属定制的控制器',
'佐玩的办公室大厅,背景墙挂着一个黑色的佐玩氛围布',
'我举着酒杯看着手机',
'我在用 Ableton Push 制作电子乐',
]

export function Photos() {
const [width, setWidth] = React.useState(0)
const [isCompact, setIsCompact] = React.useState(false)
const expandedWidth = React.useMemo(() => width * 1.38, [width])

React.useEffect(() => {
const handleResize = () => {
// 640px is the breakpoint for md
if (window.innerWidth < 640) {
setIsCompact(true)
return setWidth(window.innerWidth / 2 - 64)
}

setWidth(window.innerWidth / images.length - 4 * images.length)
}

window.addEventListener('resize', handleResize)
handleResize()

return () => {
window.removeEventListener('resize', handleResize)
}
}, [])

return (
<motion.div
className="mt-16 sm:mt-20"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
<div className="-my-4 flex w-full snap-x snap-proximity scroll-pl-4 gap-4 overflow-x-auto px-4 py-4 sm:gap-6 md:overflow-x-hidden md:px-0">
{images.map((image, idx) => (
<motion.div
key={image.src}
className="relative h-40 flex-none shrink-0 snap-start overflow-hidden rounded-xl bg-zinc-100 ring-2 ring-lime-900/50 dark:bg-zinc-800 dark:ring-lime-300/10 md:h-72 md:rounded-3xl"
animate={{
width,
opacity: isCompact ? 1 : 0.85,
filter: isCompact ? 'saturate(0.8)' : 'saturate(0.7)',
rotate: idx % 2 === 0 ? 2 : -1,
}}
whileHover={
isCompact
? {}
: {
width: expandedWidth,
opacity: 1,
filter: 'saturate(1)',
}
}
layout
>
<Image
src={image}
alt={alts[idx] ?? ''}
sizes="(min-width: 640px) 18rem, 11rem"
className="pointer-events-none absolute inset-0 h-full w-full select-none object-cover"
priority
/>
</motion.div>
))}
</div>
</motion.div>
)
}
10 changes: 10 additions & 0 deletions app/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'
import type { ThemeProviderProps } from 'next-themes/dist/types'

// https://github.com/pacocoursey/next-themes/issues/152#issuecomment-1364280564

export function ThemeProvider(props: ThemeProviderProps) {
return <NextThemesProvider {...props} />
}
69 changes: 69 additions & 0 deletions app/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'

import { useTheme } from 'next-themes'
import React from 'react'
import { TbCircleHalf2, TbMoon, TbSun } from 'react-icons/tb'

import { Tooltip } from '~/components/ui/Tooltip'

const themes = [
{
label: '浅色模式',
value: 'light',
icon: TbSun,
},
{
label: '深色模式',
value: 'dark',
icon: TbMoon,
},
{
label: '跟随系统',
value: 'system',
icon: TbCircleHalf2,
},
]
export function ThemeSwitcher() {
const [mounted, setMounted] = React.useState(false)
const { setTheme, theme } = useTheme()
const ThemeIcon = React.useMemo(
() => themes.find((t) => t.value === theme)?.icon ?? TbCircleHalf2,
[theme]
)

React.useEffect(() => setMounted(true), [])

function toggleTheme() {
if (theme === 'system') {
setTheme('dark')
} else if (theme === 'dark') {
setTheme('light')
} else {
setTheme('system')
}
}

if (!mounted) {
return null
}

return (
<Tooltip.Provider>
<Tooltip.Root disableHoverableContent>
<Tooltip.Trigger asChild>
<button
type="button"
aria-label="切换颜色主题"
className="group rounded-full bg-gradient-to-b from-zinc-50/20 to-white/80 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:from-zinc-900/30 dark:to-zinc-800/80 dark:ring-white/10 dark:hover:ring-white/20"
onClick={toggleTheme}
>
<ThemeIcon className="h-6 w-6 stroke-zinc-500 transition group-hover:stroke-zinc-700 dark:group-hover:stroke-zinc-200" />
</button>
</Tooltip.Trigger>
<Tooltip.Content>
{themes.find((t) => t.value === theme)?.label}
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
)
}
9 changes: 9 additions & 0 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Container } from '~/components/ui/Container'

export default function AboutPage() {
return (
<Container>
<h1 className="mt-10">给我点时间开发一下...</h1>
</Container>
)
}
16 changes: 16 additions & 0 deletions app/app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type MdxFrontMatter = {
title: string
publishedAt: string
tags: string[]
image?: string
summary?: string
}
type MDXComponent = {
default: React.FC
meta: MdxFrontMatter
}

declare module '*.mdx' {
export default MDXComponent
export const meta: MdxFrontMatter
}
17 changes: 17 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

::selection {
background-color: theme('colors.lime.500');
color: theme('colors.lime.950');
}

@keyframes typing-pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
45 changes: 45 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import '~/app/globals.css'

import { ClerkProvider } from '@clerk/nextjs/app-beta'

import { ThemeProvider } from '~/app/ThemeProvider'
import { Header } from '~/components/layouts/Header'
import { sansFont } from '~/lib/font'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html
lang="zh"
className={`${sansFont.variable} m-0 h-full p-0 font-sans antialiased`}
suppressHydrationWarning
>
<body className="flex h-full flex-col bg-zinc-50 bg-[url('/grid-black.svg')] bg-top bg-repeat dark:bg-primary-900 dark:bg-[url('/grid.svg')]">
<span className="pointer-events-none fixed top-0 block h-[800px] w-full select-none bg-[radial-gradient(103.72%_46.58%_at_50%_0%,rgba(5,5,5,0.045)_0%,rgba(0,0,0,0)_100%)] dark:bg-[radial-gradient(103.72%_46.58%_at_50%_0%,rgba(255,255,255,0.09)_0%,rgba(255,255,255,0)_100%)]" />
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<ClerkProvider>
<div className="fixed inset-0 flex justify-center sm:px-8">
<div className="flex w-full max-w-7xl lg:px-8">
<div className="w-full bg-zinc-50/90 ring-1 ring-zinc-100 dark:bg-zinc-900/80 dark:ring-zinc-400/20" />
</div>
</div>

<div className="relative text-slate-50">
<Header />

<main>{children}</main>
</div>
</ClerkProvider>
</ThemeProvider>
</body>
</html>
)
}
Loading

0 comments on commit cd40eb0

Please sign in to comment.