diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c1ac02e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 + +[{*.json,.*.yml}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..422c391 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +.next +dist +node_modules/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..65cf70e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": ["next", "prettier", "plugin:tailwind/recommended"], + "rules": { + "import/prefer-default-export": "off", + "no-console": "warn", + "no-var": "error" + } +} diff --git a/.gitignore b/.gitignore index 62d9a6a..981520b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,50 @@ +# [MM] HEY HEY HEY +assets +pnpm-lock.yaml + +# dependencies node_modules web_modules .firebase .firebaserc .glitchdotcom.json + +# testing +coverage + +# next.js +.next +out + +# production build -# [MM] HEY HEY HEY -assets -pnpm-lock.yaml \ No newline at end of file +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +# next-pwa +public/workbox-*.js +public/sw.js + +# prefer yarn than npm +package-lock.json + +report.*.json + +# .vscode Debugging +.vscode/*-debug-profile diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7613cd0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +build/* +dist/* +public/* +.next/* +node_modules/* +package.json +*.log diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c5cef78 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,18 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxSingleQuote": true, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false, + "vueIndentScriptAndStyle": false +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..89293d7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true + }, + "eslint.validate": ["javascript"], + "eslint.workingDirectories": [{ "mode": "auto" }] +} diff --git a/README.md b/README.md index b06449e..07c59da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -================================================= -# threed-hasura-hello-remote-schema +=============================================================================== +# threed-hasura-hello-remote-schema from Hasura 1. Schema: @@ -15,8 +15,9 @@ type Query { 4. Go to GraphiQL tab, and try out `query { word }` . -================================================= -# Hello React! + +=============================================================================== +# Hello React! from Glitch This project contains a foundation for building and learning about React apps. The site includes two routes showing how navigation works in a single page app. We manage the page head and body using a standard React flow. The homepage features a click effect that demonstrates using state, and an animation you can try out yourself by following the steps in `TODO.md`. 💫 @@ -81,3 +82,103 @@ Take a look in `TODO.md` for next steps you can try out in your new site! - Want more details about React on Glitch? We've got a [Help Center article](https://help.glitch.com/kb/article/112) for you. - Need more help? [Check out our Help Center](https://help.glitch.com/) for answers to any common questions. - Ready to make it official? [Become a paid Glitch member](https://glitch.com/pricing) to boost your app with private sharing, more storage and memory, domains and more. + + +=============================================================================== +# React Three Fiber on NextJS + +[![Downloads](https://img.shields.io/npm/dt/create-r3f-app.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/create-r3f-app) [![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](https://discord.gg/ZZjjNvJ) +# :japanese_castle: React-Three-Next starter + +Minimalist starter, First Load JS of 85Kb. This starter will automatically pick the marked R3F components and inject them into a canvas layout so we can navigate seamlessly between the pages with some dynamic dom and canvas content without reloading or creating a new canvas every time. + +### ⚫ Demo : + +[![image](https://user-images.githubusercontent.com/15867665/127765411-68bf8f2d-f13b-42de-90db-d40b84d89e92.png)](https://react-three-next.vercel.app/) + +### How to use + +#### Installation + +_Tailwind is the default style. styled-components (styled) is also available._ + +```sh +yarn create r3f-app next my-app +# yarn create r3f-app my-app ? -ts? +``` + +or + +```sh +npx create-r3f-app next my-app +``` + +### :passport_control: Typescript + +For typescript add the parameter `-ts` or `--typescript`: + +```sh +yarn create r3f-app next my-app -ts +``` + +### :mount_fuji: Features + +- [x] Automatically inject r3f component in the Canvas +- [x] Support glsl imports +- [x] PWA Support +- [x] Layout for Canvas and DOM +- [x] Template for the meta data and header +- [x] Clean code using ESlint and Prettier +- [x] VSCode debug profiles for the server, Chrome, and Firefox + +### :bullettrain_side: Architecture + +Inform the nextjs page that the component is a Threejs component. For that, simply add the **r3f** property to the parent component. + +```jsx +const Page = (props) => { + return ( + <> +
Hello !
+ + + ) +} +// canvas components goes here +// It will receive same props as Page component (from getStaticProps, etc.) +Page.r3f = (props) => ( + <> + + +) + +export default Page +``` + +### :control_knobs: Available Scripts + +- `yarn dev` - Next dev +- `yarn analyze` - Generate bundle-analyzer +- `yarn lint` - Audit code quality +- `yarn build` - Next build +- `yarn start` - Next start +- `yarn export` - Export to static HTML + +### ⬛ Stack + +- [`threejs`](https://github.com/mrdoob/three.js/) – A lightweight, 3D library with a default WebGL renderer. +- [`@react-three/fiber`](https://github.com/pmndrs/react-three-fiber) – A React renderer for Threejs on the web and react-native. +- [`@react-three/drei` - Optional](https://github.com/pmndrs/drei) – useful helpers for react-three-fiber +- [`@react-three/a11y` - Optional](https://github.com/pmndrs/react-three-a11y/) – Accessibility tools for React Three Fiber +- [`r3f-perf` - Optional](https://github.com/RenaudRohlinger/r3f-perf) – Tool to easily monitor react threejs performances. + +### How to contribute : + +```bash +git clone https://github.com/pmndrs/react-three-next +&& cd react-three-next && yarn install +``` + +### Maintainers : + +- [`twitter 🐈‍⬛ @onirenaud`](https://twitter.com/onirenaud) diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..df18557 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +// @js-ignore +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + } +} diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..24babd5 --- /dev/null +++ b/next.config.js @@ -0,0 +1,69 @@ +const withBundleAnalyzer = require('@next/bundle-analyzer')({ + enabled: process.env.ANALYZE === 'true', +}) +const withPWA = require('next-pwa') +const runtimeCaching = require('next-pwa/cache') + +const nextConfig = { + webpack(config, { isServer }) { + // audio support + config.module.rules.push({ + test: /\.(ogg|mp3|wav|mpe?g)$/i, + exclude: config.exclude, + use: [ + { + loader: require.resolve('url-loader'), + options: { + limit: config.inlineImageLimit, + fallback: require.resolve('file-loader'), + publicPath: `${config.assetPrefix}/_next/static/images/`, + outputPath: `${isServer ? '../' : ''}static/images/`, + name: '[name]-[hash].[ext]', + esModule: config.esModule || false, + }, + }, + ], + }) + + // shader support + config.module.rules.push({ + test: /\.(glsl|vs|fs|vert|frag)$/, + exclude: /node_modules/, + use: ['raw-loader', 'glslify-loader'], + }) + + return config + }, +} + +// manage i18n +if (process.env.EXPORT !== 'true') { + nextConfig.i18n = { + locales: ['en-US'], + defaultLocale: 'en-US', + } +} + +module.exports = (_phase, { defaultConfig }) => { + const plugins = [ + [ + withPWA, + { + pwa: { + dest: 'public', + disable: process.env.NODE_ENV === 'development', + runtimeCaching, + }, + }, + ], + [withBundleAnalyzer, {}], + ] + + return plugins.reduce( + (acc, [plugin, config]) => plugin({ ...acc, ...config }), + { + ...defaultConfig, + ...nextConfig, + } + ) +} diff --git a/package-todo.json b/package-todo.json new file mode 100644 index 0000000..76ac6be --- /dev/null +++ b/package-todo.json @@ -0,0 +1,53 @@ +{ + "name": "react-three-next", + "version": "2.0.0", + "authors": [ + "Renaud ROHLINGER " + ], + "license": "MIT", + "private": true, + "engines": { + "node": ">=14" + }, + "scripts": { + "lint": "yarn prettier && yarn eslint", + "eslint": "next lint --fix --dir src", + "prettier": "prettier -l \"./src/**/*.{js,jsx,md}\"", + "dev": "next dev", + "build": "next build", + "export": "EXPORT=true next build && EXPORT=true next export", + "analyze": "ANALYZE=true next build", + "start": "next start" + }, + "dependencies": { + "@babel/plugin-transform-runtime": "^7.17.0", + "@react-three/drei": "^9.14.3", + "@react-three/fiber": "^8.0.27", + "babel-plugin-glsl": "^1.0.0", + "glsl-random": "^0.0.5", + "next": "^12.2.3", + "next-pwa": "^5.5.4", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "three": "^0.142.0", + "three-stdlib": "^2.12.1", + "zustand": "^4.0.0" + }, + "devDependencies": { + "@next/bundle-analyzer": "^12.2.3", + "autoprefixer": "^10.4.2", + "eslint": "^8.20.0", + "eslint-config-next": "^12.2.3", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-tailwind": "^0.2.1", + "file-loader": "^6.2.0", + "glslify": "^7.1.1", + "glslify-loader": "^2.0.0", + "next-offline": "^5.0.5", + "postcss": "^8.4.14", + "prettier": "^2.7.1", + "raw-loader": "^4.0.2", + "tailwindcss": "^3.1.6", + "url-loader": "^4.1.1" + } +} diff --git a/package.json b/package.json index f05ed75..52a4080 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,35 @@ }, "optionalDependencies": { "farmbot": "^15.3.3", - "threed-garden": "^0.10.2" + "threed-garden": "^0.10.2", + + "@babel/plugin-transform-runtime": "^7.17.0", + "@react-three/drei": "^9.14.3", + "@react-three/fiber": "^8.0.27", + "babel-plugin-glsl": "^1.0.0", + "glsl-random": "^0.0.5", + "next": "^12.2.3", + "next-pwa": "^5.5.4", + "three-stdlib": "^2.12.1", + "zustand": "^4.0.0", + + "@next/bundle-analyzer": "^12.2.3", + "autoprefixer": "^10.4.2", + "eslint": "^8.20.0", + "eslint-config-next": "^12.2.3", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-tailwind": "^0.2.1", + "file-loader": "^6.2.0", + "glslify": "^7.1.1", + "glslify-loader": "^2.0.0", + "next-offline": "^5.0.5", + "postcss": "^8.4.14", + "prettier": "^2.7.1", + "raw-loader": "^4.0.2", + "tailwindcss": "^3.1.6", + "url-loader": "^4.1.1", + + "webpack": ">=4.0.0 <6.0.0" }, "browserslist": [ ">0.2%", @@ -70,4 +98,4 @@ "threedgarden", "threedgarden.eth" ] -} \ No newline at end of file +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/sandbox.config.json b/sandbox.config.json new file mode 100644 index 0000000..784fc4d --- /dev/null +++ b/sandbox.config.json @@ -0,0 +1,8 @@ +{ + "infiniteLoopProtection": true, + "hardReloadOnChange": false, + "view": "browser", + "container": { + "node": "12" + } +} diff --git a/src/app.jsx b/src/app.jsx index 4d497fb..b1aedec 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -18,7 +18,7 @@ import "./styles/styles.css"; import PageRouter from "./components/router.jsx"; // The component that adds our Meta tags to the page -import Seo from './components/seo.jsx'; +import Seo from './components/seo/seo.jsx'; // Home function that is reflected across the site export default function Home() { diff --git a/src/components/canvas/Box.jsx b/src/components/canvas/Box.jsx new file mode 100644 index 0000000..520180d --- /dev/null +++ b/src/components/canvas/Box.jsx @@ -0,0 +1,35 @@ +import useStore from '@/helpers/store' +import { useFrame } from '@react-three/fiber' +import { useRef, useState } from 'react' + +const BoxComponent = ({ route }) => { + const router = useStore((s) => s.router) + // This reference will give us direct access to the THREE.Mesh object + const mesh = useRef(null) + // Set up state for the hovered and active state + const [hovered, setHover] = useState(false) + // Subscribe this component to the render-loop, rotate the mesh every frame + useFrame((state, delta) => + mesh.current + ? (mesh.current.rotation.y = mesh.current.rotation.x += 0.01) + : null + ) + // Return the view, these are regular Threejs elements expressed in JSX + return ( + <> + router.push(route)} + onPointerOver={() => setHover(true)} + onPointerOut={() => setHover(false)} + scale={hovered ? 1.1 : 1} + > + + + + + + + ) +} +export default BoxComponent diff --git a/src/components/canvas/Shader/Shader.jsx b/src/components/canvas/Shader/Shader.jsx new file mode 100644 index 0000000..d4de32a --- /dev/null +++ b/src/components/canvas/Shader/Shader.jsx @@ -0,0 +1,59 @@ +import * as THREE from 'three' +import { useFrame, extend } from '@react-three/fiber' +import { useRef, useState } from 'react' +import useStore from '@/helpers/store' +import { shaderMaterial } from '@react-three/drei' + +import vertex from './glsl/shader.vert' +import fragment from './glsl/shader.frag' + +const ColorShiftMaterial = shaderMaterial( + { + time: 0, + color: new THREE.Color(0.05, 0.0, 0.025), + }, + vertex, + fragment +) + +// This is the 🔑 that HMR will renew if this file is edited +// It works for THREE.ShaderMaterial as well as for drei/shaderMaterial +// @ts-ignore +ColorShiftMaterial.key = THREE.MathUtils.generateUUID() + +extend({ ColorShiftMaterial }) + +const Shader = (props) => { + const meshRef = useRef(null) + const [hovered, setHover] = useState(false) + const router = useStore((state) => state.router) + + useFrame((state, delta) => { + if (meshRef.current) { + meshRef.current.rotation.x = meshRef.current.rotation.y += 0.01 + } + if (meshRef.current.material) { + meshRef.current.material.uniforms.time.value += + Math.sin(delta / 2) * Math.cos(delta / 2) + } + }) + + return ( + { + router.push(`/box`) + }} + onPointerOver={(e) => setHover(true)} + onPointerOut={(e) => setHover(false)} + {...props} + > + + {/* @ts-ignore */} + + + ) +} + +export default Shader diff --git a/src/components/canvas/Shader/glsl/shader.frag b/src/components/canvas/Shader/glsl/shader.frag new file mode 100644 index 0000000..18f342a --- /dev/null +++ b/src/components/canvas/Shader/glsl/shader.frag @@ -0,0 +1,9 @@ + uniform float time; + uniform vec3 color; + varying vec2 vUv; + #pragma glslify: random = require(glsl-random) + + void main() { + gl_FragColor.rgba = vec4(0.5 + 0.3 * sin(vUv.yxx + time) + color, 1.0); + // gl_FragColor.rgba = vec4(vec3(0.), 1.); + } diff --git a/src/components/canvas/Shader/glsl/shader.vert b/src/components/canvas/Shader/glsl/shader.vert new file mode 100644 index 0000000..2e26c45 --- /dev/null +++ b/src/components/canvas/Shader/glsl/shader.vert @@ -0,0 +1,5 @@ +varying vec2 vUv; +void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); +} diff --git a/src/components/dom/Instructions.jsx b/src/components/dom/Instructions.jsx new file mode 100644 index 0000000..9d1b61f --- /dev/null +++ b/src/components/dom/Instructions.jsx @@ -0,0 +1,31 @@ +export default function Instructions() { + return ( +
+

+ This is a minimal starter for Nextjs + Threejs. Click on the cube to + navigate to the `/box` page. OrbitControls is enabled by default. +

+
+ Step 1 - update: + @/pages/index +
+ Step 2 - update: + @/components/canvas/Shader/Shader +
+ Step 3 - delete: + @/pages/box +
+ Step 4 - update header: + @/config +
+ Step 5 - delete: + @/components/dom/Instructions +
+
+ ) +} diff --git a/src/components/layout/canvas.jsx b/src/components/layout/canvas.jsx new file mode 100644 index 0000000..6ac2b16 --- /dev/null +++ b/src/components/layout/canvas.jsx @@ -0,0 +1,43 @@ +import { Canvas } from '@react-three/fiber' +import { OrbitControls, Preload } from '@react-three/drei' +import useStore from '@/helpers/store' +import { useEffect, useRef } from 'react' + +const LControl = () => { + const dom = useStore((state) => state.dom) + const control = useRef(null) + + useEffect(() => { + if (control.current) { + const domElement = dom.current + const originalTouchAction = domElement.style['touch-action'] + domElement.style['touch-action'] = 'none' + + return () => { + domElement.style['touch-action'] = originalTouchAction + } + } + }, [dom, control]) + // @ts-ignore + return +} +const LCanvas = ({ children }) => { + const dom = useStore((state) => state.dom) + + return ( + state.events.connect(dom.current)} + > + + + {children} + + ) +} + +export default LCanvas diff --git a/src/components/layout/dom.jsx b/src/components/layout/dom.jsx new file mode 100644 index 0000000..2c2eb67 --- /dev/null +++ b/src/components/layout/dom.jsx @@ -0,0 +1,20 @@ +import { setState } from '@/helpers/store' +import { useEffect, useRef } from 'react' + +const Dom = ({ children }) => { + const ref = useRef(null) + useEffect(() => { + setState({ dom: ref }) + }, []) + + return ( +
+ {children} +
+ ) +} + +export default Dom diff --git a/src/components/seo/config.jsx b/src/components/seo/config.jsx new file mode 100644 index 0000000..c9e216e --- /dev/null +++ b/src/components/seo/config.jsx @@ -0,0 +1,85 @@ +import Head from 'next/head' + +const titleDefault = 'React Three Next Starter' +const url = 'https://react-three-next.vercel.app/' +const description = + 'The easiest and fastest way to create a 3D website using React Three Fiber and NextJS' +const author = 'Author' + +const Header = ({ title = titleDefault }) => { + return ( + <> + + {/* Recommended Meta Tags */} + + + + + + + + {/* Search Engine Optimization Meta Tags */} + {title} + + + + + {/* + Facebook Open Graph meta tags + documentation: https://developers.facebook.com/docs/sharing/opengraph */} + + + + + + + + + + + + + + + + {/* Meta Tags for HTML pages on Mobile */} + {/* + */} + + + + + {/* + Twitter Summary card + documentation: https://dev.twitter.com/cards/getting-started + Be sure validate your Twitter card markup on the documentation site. */} + + + + + ) +} + +export default Header diff --git a/src/seo.json b/src/components/seo/seo.json similarity index 100% rename from src/seo.json rename to src/components/seo/seo.json diff --git a/src/components/seo.jsx b/src/components/seo/seo.jsx similarity index 97% rename from src/components/seo.jsx rename to src/components/seo/seo.jsx index 24ccb17..be0619c 100644 --- a/src/components/seo.jsx +++ b/src/components/seo/seo.jsx @@ -1,5 +1,5 @@ import * as React from "react"; -import SEO from "../seo.json"; +import SEO from "./seo.json"; import { Helmet } from 'react-helmet-async'; const Seo = () => { diff --git a/src/pages copy/404.jsx b/src/pages copy/404.jsx new file mode 100644 index 0000000..432bed5 --- /dev/null +++ b/src/pages copy/404.jsx @@ -0,0 +1,8 @@ +// custom pages/404.jsx !! Do not remove please or it will break build +export default function Error() { + return ( + <> +

404 - Something went wrong

+ + ) +} diff --git a/src/pages copy/500.jsx b/src/pages copy/500.jsx new file mode 100644 index 0000000..a774946 --- /dev/null +++ b/src/pages copy/500.jsx @@ -0,0 +1,8 @@ +// custom pages/500.js !! Do not remove please or it will break build +export default function Error() { + return ( + <> +

500 - Something went wrong

+ + ) +} diff --git a/src/pages copy/_app.jsx b/src/pages copy/_app.jsx new file mode 100644 index 0000000..a916fb4 --- /dev/null +++ b/src/pages copy/_app.jsx @@ -0,0 +1,31 @@ +import { useRouter } from 'next/router' +import { setState } from '@/helpers/store' +import { useEffect } from 'react' +import Header from '@/config' +import Dom from '@/components/layout/dom' +import '@/styles/index.css' +import dynamic from 'next/dynamic' + +const LCanvas = dynamic(() => import('@/components/layout/canvas'), { + ssr: true, +}) + +function App({ Component, pageProps = { title: 'index' } }) { + const router = useRouter() + + useEffect(() => { + setState({ router }) + }, [router]) + + return ( + <> +
+ + + + {Component?.r3f && {Component.r3f(pageProps)}} + + ) +} + +export default App diff --git a/src/pages copy/box.jsx b/src/pages copy/box.jsx new file mode 100644 index 0000000..3f3529e --- /dev/null +++ b/src/pages copy/box.jsx @@ -0,0 +1,31 @@ +import Instructions from '@/components/dom/Instructions' +import dynamic from 'next/dynamic' + +const Box = dynamic(() => import('@/components/canvas/Box'), { + ssr: false, +}) + +// Step 5 - delete Instructions components +const Page = (props) => { + return ( + <> + + + ) +} + +Page.r3f = (props) => ( + <> + + +) + +export default Page + +export async function getStaticProps() { + return { + props: { + title: 'Box', + }, + } +} diff --git a/src/pages copy/index.jsx b/src/pages copy/index.jsx new file mode 100644 index 0000000..ec1c076 --- /dev/null +++ b/src/pages copy/index.jsx @@ -0,0 +1,39 @@ +import dynamic from 'next/dynamic' +// Step 5 - delete Instructions components +import Instructions from '@/components/dom/Instructions' +// import Shader from '@/components/canvas/Shader/Shader' + +// Dynamic import is used to prevent a payload when the website start that will include threejs r3f etc.. +// WARNING ! errors might get obfuscated by using dynamic import. +// If something goes wrong go back to a static import to show the error. +// https://github.com/pmndrs/react-three-next/issues/49 +const Shader = dynamic(() => import('@/components/canvas/Shader/Shader'), { + ssr: false, +}) + +// dom components goes here +const Page = (props) => { + return ( + <> + + + ) +} + +// canvas components goes here +// It will receive same props as Page component (from getStaticProps, etc.) +Page.r3f = (props) => ( + <> + + +) + +export default Page + +export async function getStaticProps() { + return { + props: { + title: 'Index', + }, + } +} diff --git a/src/stores/store.js b/src/stores/store.js new file mode 100644 index 0000000..1826548 --- /dev/null +++ b/src/stores/store.js @@ -0,0 +1,17 @@ +import create from 'zustand' +import { shallow } from 'zustand/shallow' + +const useStoreImpl = create(() => { + return { + router: null, + dom: null, + } +}) + +const useStore = (sel) => useStoreImpl(sel, shallow) +Object.assign(useStore, useStoreImpl) + +const { getState, setState } = useStoreImpl + +export { getState, setState } +export default useStore diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 0000000..53d6513 --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,19 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + * { + box-sizing: border-box; + } + + html, + body, + #__next { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + } +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..c12f7da --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,15 @@ +module.exports = { + mode: 'jit', + content: [ + './src/pages/**/*.{js,ts,jsx,tsx}', + './src/components/**/*.{js,ts,jsx,tsx}', + ], // remove unused styles in production + darkMode: 'media', // or 'media' or 'class' + theme: { + extend: {}, + }, + variants: { + extend: {}, + }, + plugins: [], +}