diff --git a/app/(pages)/(components)/lenis/index.js b/app/(pages)/(components)/lenis/index.js new file mode 100644 index 00000000..be5fd711 --- /dev/null +++ b/app/(pages)/(components)/lenis/index.js @@ -0,0 +1,21 @@ +'use client' + +import { ReactLenis } from 'libs/lenis' + +export function Lenis({ root, options }) { + return ( + { + console.log(node.id) + return ( + node.nodeName === 'VERCEL-LIVE-FEEDBACK' || + node.id === 'theatrejs-studio-root' + ) + }, + }} + /> + ) +} diff --git a/app/(pages)/(components)/wrapper/index.js b/app/(pages)/(components)/wrapper/index.js index bedab563..9825fd80 100644 --- a/app/(pages)/(components)/wrapper/index.js +++ b/app/(pages)/(components)/wrapper/index.js @@ -1,7 +1,7 @@ import cn from 'clsx' -import { Lenis } from 'libs/lenis' import { Canvas } from 'libs/webgl/components/canvas' import { Footer } from '../footer' +import { Lenis } from '../lenis' import { Navigation } from '../navigation' import s from './wrapper.module.scss' diff --git a/app/manifest.js b/app/manifest.js index 07998759..cf822acf 100644 --- a/app/manifest.js +++ b/app/manifest.js @@ -8,8 +8,8 @@ export default function manifest() { description: AppData.description, start_url: '/', display: 'standalone', - background_color: themes.red.primary, - theme_color: themes.red.contrast, + background_color: themes.light.primary, + theme_color: themes.light.contrast, icons: [ { src: '/icon', diff --git a/app/storyblok-sitemap.js b/app/storyblok-sitemap.js new file mode 100644 index 00000000..227d24d9 --- /dev/null +++ b/app/storyblok-sitemap.js @@ -0,0 +1,57 @@ +// this file is used to generate the sitemap websites using storyblok + +import { StoryblokApi } from 'libs/storyblok' + +const storyblokApi = new StoryblokApi() + +// replace with your website url +const URL = 'https://example.com' + +// add your predefined static routes +const predefinedStaticRoutes = ['', '/blog', '/about', '/careers'] + +async function fetchAllStories() { + let page = 1 + let hasMoreStories = true + let allStories = [] + + while (hasMoreStories) { + try { + const { data } = await storyblokApi.get('cdn/stories', { + version: 'published', + per_page: 100, + page: page, + excluding_fields: 'body', + }) + + allStories = [...allStories, ...data.stories] + + if (data.stories.length < 100) { + hasMoreStories = false + } else { + page++ + } + } catch (error) { + console.error('Error fetching stories:', error) + hasMoreStories = false + } + } + + return allStories +} + +export default async function sitemap() { + const stories = await fetchAllStories() + + const routes = stories.map((story) => ({ + url: `${URL}/${story.full_slug}`, + lastModified: new Date(story.published_at).toISOString(), + })) + + const staticRoutes = predefinedStaticRoutes.map((route) => ({ + url: `${URL}${route}`, + lastModified: new Date().toISOString(), + })) + + return [...staticRoutes, ...routes] +} diff --git a/components/image/index.js b/components/image/index.js index eaf09494..26121558 100644 --- a/components/image/index.js +++ b/components/image/index.js @@ -1,3 +1,5 @@ +'use client' + import cn from 'clsx' import NextImage from 'next/image' import { forwardRef } from 'react' @@ -19,6 +21,8 @@ export const Image = forwardRef(function Image( mobileSize = '100vw', desktopSize = '100vw', sizes = `(max-width: ${parseFloat(variables.breakpoints.mobile)}px) ${mobileSize}, ${desktopSize}`, + src, + unoptimized, ...props }, ref, @@ -38,6 +42,10 @@ export const Image = forwardRef(function Image( }} className={cn(className, block && s.block)} sizes={sizes} + src={src} + unoptimized={unoptimized ?? src?.includes('.svg')} + draggable="false" + onDragStart={(e) => e.preventDefault()} {...props} /> ) diff --git a/components/marquee/index.js b/components/marquee/index.js new file mode 100644 index 00000000..4c133ba3 --- /dev/null +++ b/components/marquee/index.js @@ -0,0 +1,82 @@ +'use client' + +import { + useFrame, + useIntersectionObserver, + useResizeObserver, +} from '@darkroom.engineering/hamo' +import cn from 'clsx' +import modulo from 'just-modulo' +import { useLenis } from 'libs/lenis' +import { useRef } from 'react' +import s from './marquee.module.scss' + +export function Marquee({ + children, + repeat = 2, + className, + speed = 0.1, + reversed, + pauseOnHover = false, + ...props +}) { + const [setRectRef, { contentRect: rect }] = useResizeObserver() + const elementsRef = useRef([]) + const transformRef = useRef(Math.random() * 1000) + const isHovered = useRef(false) + + const [setIntersectionRef, intersection] = useIntersectionObserver() + + const lenis = useLenis() // eslint-disable-line react-hooks/exhaustive-deps + + useFrame((_, deltaTime) => { + if (!intersection.isIntersecting) return + if (pauseOnHover && isHovered.current) return + + if (!rect.width) return + + const adjSpeed = speed * (1 + Math.abs(lenis.velocity / 5)) + + if (reversed) { + transformRef.current -= deltaTime * adjSpeed + } else { + transformRef.current += deltaTime * adjSpeed + } + + transformRef.current = modulo(transformRef.current, rect.width) + + elementsRef.current.forEach((node) => { + node.style.transform = `translate3d(${-transformRef.current}px,0,0)` + }) + }) + + return ( +
{ + isHovered.current = true + }} + onMouseLeave={() => { + isHovered.current = false + }} + > + {new Array(repeat).fill(children).map((_, i) => ( +
{ + elementsRef.current[i] = node + + if (i === 0) setRectRef(node) + }} + > + {children} +
+ ))} +
+ ) +} diff --git a/components/marquee/marquee.module.scss b/components/marquee/marquee.module.scss new file mode 100644 index 00000000..58a5d552 --- /dev/null +++ b/components/marquee/marquee.module.scss @@ -0,0 +1,14 @@ +.marquee { + display: flex; + overflow-x: clip; + + .inner { + display: flex; + white-space: nowrap; + transform: translate3d(0, 0, 0); + + > * { + flex-shrink: 0; + } + } +} diff --git a/libs/lenis/index.js b/libs/lenis/index.js index c731e2c7..bf51b477 100644 --- a/libs/lenis/index.js +++ b/libs/lenis/index.js @@ -1,3 +1,3 @@ 'use client' -export { Lenis, useLenis } from 'lenis/react' +export { Lenis, ReactLenis, useLenis } from 'lenis/react' diff --git a/package.json b/package.json index 1cf0fb90..096a8ea6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "private": true, "license": "MIT", "type": "module", + "packageManager": "pnpm@9.4.0", "scripts": { "dev:storyblok": "npm-run-all -p dev https", "dev": "next dev", @@ -29,7 +30,7 @@ "@theatre/studio": "^0.7.2", "clsx": "^2.1.1", "gsap": "^3.12.5", - "lenis": "^1.1.3", + "lenis": "1.1.4", "next": "14.2.4", "next-sitemap": "^4.2.3", "postprocessing": "^6.35.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78fcfd03..34c2b35b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,8 +50,8 @@ importers: specifier: ^3.12.5 version: 3.12.5 lenis: - specifier: ^1.1.3 - version: 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 1.1.4 + version: 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next: specifier: 14.2.4 version: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6) @@ -2752,8 +2752,8 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} - lenis@1.1.3: - resolution: {integrity: sha512-Ny62QbNKFdnYPutULPMCgXU6YADKgkICBke41snSXDH1p9iIA1w1+RiHSfJEoMaRCa1SXer4Rzv0f4txCQcQpg==} + lenis@1.1.4: + resolution: {integrity: sha512-VNbsie0YaP6946QpYw/n5zxyPyLKuqPO28T8FK7YBzIkG3FyfwEZkpPKqt6eQZJ9ofjxkfUM+A8YtPfNlTbSlQ==} peerDependencies: react: '>=17.0.0' react-dom: '>=17.0.0' @@ -6878,7 +6878,7 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 - lenis@1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + lenis@1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@darkroom.engineering/tempus': 0.0.46 optionalDependencies: diff --git a/styles/_reset.scss b/styles/_reset.scss index fc1900fc..5edd91f5 100644 --- a/styles/_reset.scss +++ b/styles/_reset.scss @@ -17,7 +17,8 @@ svg, video, audio, - #theatrejs-studio-root + #theatrejs-studio-root, + vercel-live-feedback ):not(svg *, symbol *) ) { all: unset; diff --git a/styles/_scroll.scss b/styles/_scroll.scss index b35b4d94..34e4e327 100644 --- a/styles/_scroll.scss +++ b/styles/_scroll.scss @@ -13,12 +13,13 @@ html { } } -html.lenis { +html.lenis, +html.lenis body { height: auto; } .lenis.lenis-smooth { - scroll-behavior: auto; + scroll-behavior: auto !important; } .lenis.lenis-smooth [data-lenis-prevent] { @@ -29,6 +30,6 @@ html.lenis { overflow: hidden; } -.lenis.lenis-scrolling iframe { +.lenis.lenis-smooth iframe { pointer-events: none; } diff --git a/styles/global.scss b/styles/global.scss index e29c7538..d653292d 100644 --- a/styles/global.scss +++ b/styles/global.scss @@ -59,3 +59,12 @@ svg.icon { } } } + +img { + user-drag: none; + -webkit-user-drag: none; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; +} diff --git a/turbo.json b/turbo.json index 1c621b3b..37ae5864 100644 --- a/turbo.json +++ b/turbo.json @@ -1,10 +1,16 @@ { "$schema": "https://turbo.build/schema.json", - "pipeline": { + "tasks": { "build": { - "outputs": [".next/**", "!.next/cache/**"], - "cache": false + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**", "!.next/cache/**"], + "cache": false, + "env": ["*"] }, - "lint": {} + "lint": {}, + "dev": { + "cache": false, + "persistent": true + } } }