start
|
@ -0,0 +1,29 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
|
||||
{
|
||||
"name": "Node.js & TypeScript",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "yarn install",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# Since the ".env" file is gitignored, you can use the ".env.example" file to
|
||||
# build a new ".env" file when you clone the repo. Keep this file up-to-date
|
||||
# when you add new variables to `.env`.
|
||||
|
||||
# This file will be committed to version control, so make sure not to have any
|
||||
# secrets in it. If you are cloning this repo, create a copy of this file named
|
||||
# ".env" and populate it with your secrets.
|
||||
|
||||
# When adding additional environment variables, the schema in "/src/env.mjs"
|
||||
# should be updated accordingly.
|
||||
|
||||
# Example:
|
||||
# SERVERVAR="foo"
|
||||
# NEXT_PUBLIC_CLIENTVAR="bar"
|
|
@ -0,0 +1,36 @@
|
|||
/** @type {import("eslint").Linter.Config} */
|
||||
const config = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: true,
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: [
|
||||
"next/core-web-vitals",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||
],
|
||||
rules: {
|
||||
// These opinionated rules are enabled in stylistic-type-checked above.
|
||||
// Feel free to reconfigure them to your own preference.
|
||||
"@typescript-eslint/array-type": "off",
|
||||
"@typescript-eslint/consistent-type-definitions": "off",
|
||||
|
||||
"@typescript-eslint/consistent-type-imports": [
|
||||
"warn",
|
||||
{
|
||||
prefer: "type-imports",
|
||||
fixStyle: "inline-type-imports",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
|
||||
"@typescript-eslint/no-misused-promises": [
|
||||
2,
|
||||
{
|
||||
checksVoidReturn: { attributes: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,42 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# database
|
||||
/prisma/db.sqlite
|
||||
/prisma/db.sqlite-journal
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
next-env.d.ts
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
||||
.env
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
|
@ -0,0 +1,28 @@
|
|||
# Create T3 App
|
||||
|
||||
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
|
||||
|
||||
## What's next? How do I make an app with this?
|
||||
|
||||
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
|
||||
|
||||
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
|
||||
|
||||
- [Next.js](https://nextjs.org)
|
||||
- [NextAuth.js](https://next-auth.js.org)
|
||||
- [Prisma](https://prisma.io)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [tRPC](https://trpc.io)
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
|
||||
|
||||
- [Documentation](https://create.t3.gg/)
|
||||
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
|
||||
|
||||
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
|
||||
|
||||
## How do I deploy this?
|
||||
|
||||
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
||||
* for Docker builds.
|
||||
*/
|
||||
await import("./src/env.mjs");
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
reactStrictMode: true,
|
||||
|
||||
/**
|
||||
* If you are using `appDir` then you must comment the below `i18n` config out.
|
||||
*
|
||||
* @see https://github.com/vercel/next.js/issues/41980
|
||||
*/
|
||||
i18n: {
|
||||
locales: ["en"],
|
||||
defaultLocale: "en",
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "typescript-node",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next dev",
|
||||
"lint": "next lint",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@t3-oss/env-nextjs": "^0.7.0",
|
||||
"next": "^13.5.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-pageflip": "^2.0.3",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/eslint": "^8.44.2",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||
"@typescript-eslint/parser": "^6.3.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-next": "^13.5.4",
|
||||
"postcss": "^8.4.27",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.22.0"
|
||||
},
|
||||
"packageManager": "npm@9.8.1"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').options} */
|
||||
const config = {
|
||||
plugins: ["prettier-plugin-tailwindcss"],
|
||||
};
|
||||
|
||||
export default config;
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 632 KiB |
After Width: | Height: | Size: 215 KiB |
After Width: | Height: | Size: 227 KiB |
After Width: | Height: | Size: 315 KiB |
After Width: | Height: | Size: 365 KiB |
After Width: | Height: | Size: 437 KiB |
After Width: | Height: | Size: 331 KiB |
After Width: | Height: | Size: 556 KiB |
After Width: | Height: | Size: 494 KiB |
After Width: | Height: | Size: 432 KiB |
After Width: | Height: | Size: 288 KiB |
After Width: | Height: | Size: 255 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 229 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 235 KiB |
After Width: | Height: | Size: 278 KiB |
After Width: | Height: | Size: 461 KiB |
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 310 KiB |
After Width: | Height: | Size: 496 KiB |
After Width: | Height: | Size: 236 KiB |
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 313 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 266 KiB |
|
@ -0,0 +1,91 @@
|
|||
import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons';
|
||||
import React, { useRef, useCallback } from 'react';
|
||||
import HTMLFlipBook from 'react-pageflip';
|
||||
|
||||
interface FlipbookProps {
|
||||
children: React.ReactNode[];
|
||||
width: number;
|
||||
height: number;
|
||||
className?: React.ComponentProps<'div'>['className'];
|
||||
}
|
||||
|
||||
interface PageFlip {
|
||||
getCurrentPageIndex(): number;
|
||||
getPageCount(): number;
|
||||
flip(page: number): void;
|
||||
flipPrev(): void;
|
||||
flipNext(): void;
|
||||
}
|
||||
|
||||
export default function Flipbook(props: FlipbookProps) {
|
||||
const flipbook = useRef<{ pageFlip(): PageFlip } | null>(null);
|
||||
|
||||
// Function to flip pages
|
||||
const flipPage = useCallback((direction: 'next' | 'prev') => {
|
||||
const flipbookCurrent = flipbook.current;
|
||||
if (flipbookCurrent) {
|
||||
const pageFlipObj = flipbookCurrent.pageFlip();
|
||||
const currentPageIndex = pageFlipObj.getCurrentPageIndex();
|
||||
const pageCount = pageFlipObj.getPageCount();
|
||||
|
||||
if (direction === 'next') {
|
||||
if (currentPageIndex + 2 === pageCount) {
|
||||
pageFlipObj.flip(0);
|
||||
} else {
|
||||
pageFlipObj.flipNext();
|
||||
}
|
||||
} else if (direction === 'prev') {
|
||||
if (currentPageIndex === 0) {
|
||||
pageFlipObj.flip(pageCount - 1);
|
||||
} else {
|
||||
pageFlipObj.flipPrev(); // Add this method to handle the previous page flip
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [flipbook]);
|
||||
|
||||
// Handler for the next button
|
||||
const nextButtonClick = useCallback(() => {
|
||||
flipPage('next');
|
||||
}, [flipPage]);
|
||||
|
||||
// Handler for the previous button
|
||||
const prevButtonClick = useCallback(() => {
|
||||
flipPage('prev');
|
||||
}, [flipPage]);
|
||||
|
||||
return (
|
||||
<div className={`flex justify-center items-center ${props.className}`}>
|
||||
<button onClick={prevButtonClick}>
|
||||
<ChevronLeftIcon height={50} width={50}/>
|
||||
</button>
|
||||
<HTMLFlipBook
|
||||
usePortrait={false}
|
||||
className="text-black"
|
||||
width={props.width} height={props.height}
|
||||
size="fixed"
|
||||
style={{}}
|
||||
minHeight={100} maxHeight={500}
|
||||
minWidth={100} maxWidth={500}
|
||||
startPage={0} startZIndex={0}
|
||||
drawShadow
|
||||
flippingTime={1000}
|
||||
autoSize
|
||||
maxShadowOpacity={1}
|
||||
showCover={false}
|
||||
mobileScrollSupport
|
||||
clickEventForward
|
||||
useMouseEvents
|
||||
swipeDistance={20}
|
||||
showPageCorners
|
||||
disableFlipByClick={false}
|
||||
ref={flipbook}
|
||||
>
|
||||
{props.children}
|
||||
</HTMLFlipBook>
|
||||
<button onClick={nextButtonClick}>
|
||||
<ChevronRightIcon height={50} width={50}/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
|
||||
import { useKeenSlider } from "keen-slider/react";
|
||||
import { useState, type MouseEvent } from "react";
|
||||
|
||||
interface SliderProps {
|
||||
className?: React.ComponentProps<'div'>['className'];
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function Slider({ className, children }: SliderProps) {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [sliderRef, instanceRef] = useKeenSlider<HTMLDivElement>({
|
||||
initial: 0,
|
||||
slideChanged(slider) {
|
||||
setCurrentSlide(slider.track.details.rel);
|
||||
},
|
||||
created() {
|
||||
setLoaded(true);
|
||||
},
|
||||
});
|
||||
|
||||
const goPrev = (e: MouseEvent<unknown>) => {
|
||||
e.stopPropagation();
|
||||
if (currentSlide !== 0) {
|
||||
instanceRef.current?.prev();
|
||||
}
|
||||
};
|
||||
|
||||
const goNext = (e: MouseEvent<unknown>) => {
|
||||
e.stopPropagation();
|
||||
if (instanceRef.current && currentSlide !== instanceRef.current.track.details.slides.length - 1) {
|
||||
instanceRef.current.next();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${className} relative`}>
|
||||
<div>
|
||||
<div ref={sliderRef} className="flex overflow-hidden relative w-full">
|
||||
{children}
|
||||
</div>
|
||||
{loaded && instanceRef.current && (
|
||||
<>
|
||||
<Arrow left onClick={goPrev} disabled={currentSlide === 0} />
|
||||
<Arrow onClick={goNext} disabled={instanceRef.current ? currentSlide === instanceRef.current.track.details.slides.length - 1 : true} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{loaded && instanceRef.current && (
|
||||
<div className="flex justify-center py-2">
|
||||
{Array.from({ length: instanceRef.current.track.details.slides.length }, (_, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
onClick={() => instanceRef.current?.moveToIdx(idx)}
|
||||
className={`w-2.5 h-2.5 bg-white rounded-full m-1 cursor-pointer ${currentSlide === idx ? "opacity-30" : ""}`}
|
||||
></button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ArrowProps {
|
||||
disabled: boolean;
|
||||
left?: boolean;
|
||||
onClick: (e: MouseEvent<unknown>) => void;
|
||||
}
|
||||
|
||||
function Arrow({ disabled, left, onClick }: ArrowProps) {
|
||||
const Icon = left ? ChevronLeftIcon : ChevronRightIcon;
|
||||
const position = left ? '-left-10' : 'left-auto -right-10';
|
||||
|
||||
return (
|
||||
<Icon
|
||||
onClick={onClick}
|
||||
height={50}
|
||||
width={50}
|
||||
className={`absolute ${position} top-1/2 cursor-pointer -translate-y-1/2 ${disabled ? "opacity-30" : ""}`}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
|
||||
export const env = createEnv({
|
||||
/**
|
||||
* Specify your server-side environment variables schema here. This way you can ensure the app
|
||||
* isn't built with invalid env vars.
|
||||
*/
|
||||
server: {
|
||||
NODE_ENV: z.enum(["development", "test", "production"]),
|
||||
},
|
||||
|
||||
/**
|
||||
* Specify your client-side environment variables schema here. This way you can ensure the app
|
||||
* isn't built with invalid env vars. To expose them to the client, prefix them with
|
||||
* `NEXT_PUBLIC_`.
|
||||
*/
|
||||
client: {
|
||||
// NEXT_PUBLIC_CLIENTVAR: z.string(),
|
||||
},
|
||||
|
||||
/**
|
||||
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
|
||||
* middlewares) or client-side so we need to destruct manually.
|
||||
*/
|
||||
runtimeEnv: {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
|
||||
},
|
||||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
||||
* This is especially useful for Docker builds.
|
||||
*/
|
||||
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||
/**
|
||||
* Makes it so that empty strings are treated as undefined.
|
||||
* `SOME_VAR: z.string()` and `SOME_VAR=''` will throw an error.
|
||||
*/
|
||||
emptyStringAsUndefined: true,
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
import { type AppType } from "next/dist/shared/lib/utils";
|
||||
|
||||
import "~/styles/globals.css";
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
};
|
||||
|
||||
export default MyApp;
|
|
@ -0,0 +1,32 @@
|
|||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
import Flipbook from "~/components/Flipbook";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Create T3 App</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className="">
|
||||
<Flipbook className="" width={350} height={460}>
|
||||
<div className="bg-gray-300">
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<a href="/download/GabrielDalle_Magazine.pdf" target="_blank" className="border-my-red border-[3px] p-4 rounded-full flex gap-2 cursor-pointer hover:border-black hover:bg-white font-mono font-bold text-sm tracking-wide">
|
||||
PDF DOWNLOAD
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{Array.from(Array(24), (_, i) => (
|
||||
<div className="bg-white" key={i}>
|
||||
<Image key={i} alt="pdf page" height={468} width={350} src={`/projects/fizik/pdf/page${i + 1}.png`} />
|
||||
</div>
|
||||
))}
|
||||
<div className="bg-gray-300" />
|
||||
</Flipbook>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -0,0 +1,14 @@
|
|||
import { type Config } from "tailwindcss";
|
||||
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||
|
||||
export default {
|
||||
content: ["./src/**/*.tsx"],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config;
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
},
|
||||
"plugins": [{ "name": "next" }]
|
||||
},
|
||||
"include": [
|
||||
".eslintrc.cjs",
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.cjs",
|
||||
"**/*.mjs",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|