diff --git a/package-lock.json b/package-lock.json index 15e1adc..1442629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@next/mdx": "^15.3.4", "@types/mdx": "^2.0.13", "clsx": "^2.1.1", - "dayjs": "^1.11.13", + "date-fns": "^4.1.0", "next": "^14.2.35", "react": "^18", "react-dom": "^18" @@ -2112,11 +2112,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", - "license": "MIT" + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } }, "node_modules/debug": { "version": "4.4.0", diff --git a/package.json b/package.json index 94c2d49..17bae08 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@next/mdx": "^15.3.4", "@types/mdx": "^2.0.13", "clsx": "^2.1.1", - "dayjs": "^1.11.13", + "date-fns": "^4.1.0", "next": "^14.2.35", "react": "^18", "react-dom": "^18" diff --git a/public/images/event_santahat01.png b/public/images/event_santahat01.png new file mode 100644 index 0000000..02d94ec Binary files /dev/null and b/public/images/event_santahat01.png differ diff --git a/src/app/page.tsx b/src/app/page.tsx index c10287f..5fb1c82 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -6,10 +6,12 @@ import HomeText from "@/components/texts/home.mdx" import Link from "@/components/link"; import { FakeWindow, HomeWindows } from "@/components/windows"; import { WindowManagerProvider } from "@/hooks/window-manager"; +import { SnowfallBackground } from "@/components/events/christmas"; export default function Home() { return (<>
+ diff --git a/src/components/events/christmas.tsx b/src/components/events/christmas.tsx new file mode 100644 index 0000000..8104065 --- /dev/null +++ b/src/components/events/christmas.tsx @@ -0,0 +1,168 @@ +'use client' + +import { getEvent } from '@/lib/utils'; +import clsx from 'clsx'; +import Image from 'next/image'; +import { ReactNode, useEffect, useRef, useState } from 'react'; + +interface Snowflake { + x: number; + y: number; + radius: number; + speed: number; + sway: number; + swayOffset: number; + opacity: number; +} + +interface ChristmasProps { + left: number; + top: number; + size: number; + className?: string; + flip?: boolean; + absolute?: boolean; +} + +export const ChristmasExclusive = ({ children }: { children: ReactNode }) => { + const isItChristmas = getEvent()?.name == 'christmas'; + + if (isItChristmas) return children; + return null; +} + +export const ChristmasHat: React.FC = ({ + left, + top, + size, + className, + flip +}) => { + return ( + +
+ Santa Hat +
+
+ ) +} + +function SnowfallRawBackground() { + const canvasRef = useRef(null); + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + const snowflakesRef = useRef([]); + const animationFrameRef = useRef(0); + + // Update dimensions on resize + useEffect(() => { + const updateDimensions = () => { + setDimensions({ + width: window.innerWidth, + height: window.innerHeight, + }); + }; + + updateDimensions(); + window.addEventListener('resize', updateDimensions); + + return () => window.removeEventListener('resize', updateDimensions); + }, []); + + useEffect(() => { + if (!canvasRef.current) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + canvas.width = dimensions.width; + canvas.height = dimensions.height; + + const createSnowflake = (init?: boolean): Snowflake => { + let posy = -10; + let radius = Math.random() * 4 + 2; + let speed = Math.random() * 1 + 0.5; + let sway = Math.random() * 0.5 + 0.2; + + if (init) { + const initHeight = (dimensions.height / 1.1); + posy = Math.random() * initHeight + posy; + radius = (posy / (initHeight + posy) - 1) * -radius; + } + + if (window.innerWidth < 768) { + radius /= 1.4; + } + + return { + x: Math.random() * dimensions.width, + y: posy, + radius, + speed, + sway, + swayOffset: Math.random() * Math.PI * 2, + opacity: Math.random() * 0.5 + 0.5, + } + }; + + + const snowflakes: Snowflake[] = []; + for (let i = 0; i < 200; i++) { + snowflakes.push(createSnowflake(true)); + } + snowflakesRef.current = snowflakes; + + const animate = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + snowflakesRef.current.forEach((flake, index) => { + flake.y += flake.speed; + flake.x += Math.sin(flake.swayOffset + flake.y * 0.02) * flake.sway; + + const shrink = dimensions.height < 768 ? 0.001 : 0.005 + flake.radius = Math.max(0, flake.radius - shrink); + + if (flake.y > dimensions.height || flake.radius <= 0.5) { + snowflakesRef.current[index] = createSnowflake(); + return; + } + + ctx.beginPath(); + ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`; + ctx.fill(); + }); + + animationFrameRef.current = requestAnimationFrame(animate); + }; + + animationFrameRef.current = requestAnimationFrame(animate); + + return () => cancelAnimationFrame(animationFrameRef.current); + }, [dimensions]); + + return ( + + ); +} + +export const SnowfallBackground = () => ( + + + +) diff --git a/src/components/events/eventprops.tsx b/src/components/events/eventprops.tsx new file mode 100644 index 0000000..9ea6d64 --- /dev/null +++ b/src/components/events/eventprops.tsx @@ -0,0 +1 @@ +'use client' \ No newline at end of file diff --git a/src/components/windows.tsx b/src/components/windows.tsx index 17f86d5..b60c13c 100644 --- a/src/components/windows.tsx +++ b/src/components/windows.tsx @@ -3,6 +3,7 @@ import { FloatingLabel } from "@/components/floating-label"; import { FakeRelativeWindow, RestoreWindowsButton } from "./client-windows"; import Image from "next/image" import Link from "next/link"; +import { ChristmasHat } from "./events/christmas"; export const FakeWindow = ({ windowText, children @@ -10,7 +11,8 @@ export const FakeWindow = ({ windowText: string children: React.ReactNode }) => ( -
+
+
@@ -45,13 +47,16 @@ export const HomeWindows = () => ( draggable > - Nola +
+ + Nola +
{ + const today = new Date(Date.now()); + const year = today.getFullYear(); + + const start = new Date(year, e.start[0] - 1, e.start[1]); + const end = new Date(year, e.end[0] - 1, e.end[1]); + + return isWithinInterval(today, { start, end }) + }) +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index e49962f..0769835 100644 --- a/yarn.lock +++ b/yarn.lock @@ -320,10 +320,10 @@ dependencies: source-map "^0.7.0" -"@next/swc-linux-x64-gnu@14.2.33": +"@next/swc-win32-x64-msvc@14.2.33": version "14.2.33" - resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz" - integrity sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg== + resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz" + integrity sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1003,10 +1003,10 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -dayjs@^1.11.13: - version "1.11.19" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz" - integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== +date-fns@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz" + integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== debug@^3.2.7: version "3.2.7"