refactor(v3): Initial revamp
parent
d85095095d
commit
e062b44e1c
@ -0,0 +1,8 @@
|
|||||||
|
**
|
||||||
|
|
||||||
|
!next.config.js
|
||||||
|
!tsconfig.json
|
||||||
|
!package.json
|
||||||
|
!yarn.lock
|
||||||
|
!public
|
||||||
|
!src
|
@ -1,5 +1 @@
|
|||||||
VITE_GITHUB_USERNAME=Guusvanmeerveld
|
NEXT_PUBLIC_GITHUB_USERNAME=Guusvanmeerveld
|
||||||
VITE_TWITTER_USERNAME=Guusvanmeerveld
|
|
||||||
VITE_KOFI_USERNAME=Guusvanmeerveld
|
|
||||||
VITE_YOUTUBE_CHANNEL_ID=UCYuqpoMay5SezCBrA_HKVWQ
|
|
||||||
VITE_MASTODON_URL=https://c.im/web/@guusvanmeerveld
|
|
@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 8
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"prettier",
|
||||||
|
"css-modules"
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"node_modules/*",
|
||||||
|
".next/*",
|
||||||
|
".out/*"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:css-modules/recommended"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"plugin:jsx-a11y/recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"react/prop-types": "off",
|
||||||
|
"react/react-in-jsx-scope": "off",
|
||||||
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
|
"jsx-a11y/no-autofocus": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error"
|
||||||
|
],
|
||||||
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"allowExpressions": true,
|
||||||
|
"allowConciseArrowFunctionExpressionsStartingWithVoid": true,
|
||||||
|
"allowTypedFunctionExpressions": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
@ -0,0 +1,20 @@
|
|||||||
|
name: Auto-merge dependabot
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
auto-merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
name: Checkout
|
||||||
|
|
||||||
|
- uses: ahmadnassri/action-dependabot-auto-merge@v2
|
||||||
|
name: Auto merge
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
@ -0,0 +1,45 @@
|
|||||||
|
name: deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
typescript:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Setup checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup NodeJS v12
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Check for syntax errors
|
||||||
|
run: yarn test-build
|
||||||
|
- name: ESlint check
|
||||||
|
run: yarn lint
|
||||||
|
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: typescript
|
||||||
|
steps:
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build Dockerfile and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: guusvanmeerveld/portfolio:latest
|
||||||
|
- name: Image digest
|
||||||
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
@ -1,42 +0,0 @@
|
|||||||
name: "Deploy website"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- src/**
|
|
||||||
- package.json
|
|
||||||
- yarn.lock
|
|
||||||
- tsconfig.*
|
|
||||||
- vite.config.ts
|
|
||||||
- .env
|
|
||||||
- index.html
|
|
||||||
- public
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy_to_pages:
|
|
||||||
name: Build pages
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Setup
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: "16.x"
|
|
||||||
cache: "yarn"
|
|
||||||
|
|
||||||
- name: Install NPM dependencies
|
|
||||||
run: yarn install
|
|
||||||
|
|
||||||
- name: Build client
|
|
||||||
run: yarn run build
|
|
||||||
|
|
||||||
- name: Deploy to pages
|
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
publish_dir: ./dist
|
|
||||||
cname: guusvanmeerveld.dev
|
|
@ -1,24 +1,4 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
.env.local
|
||||||
dist-ssr
|
.next
|
||||||
*.local
|
yarn-error.log
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "none",
|
|
||||||
"useTabs": true,
|
|
||||||
"semi": true,
|
|
||||||
"printWidth": 80,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"importOrderSeparation": true,
|
|
||||||
"importOrder": [
|
|
||||||
"@rollup/.*",
|
|
||||||
"rollup-.*",
|
|
||||||
"^..?/.*",
|
|
||||||
"^use-local-storage-state$",
|
|
||||||
"^styled-components$",
|
|
||||||
"^react.*",
|
|
||||||
"^preact$",
|
|
||||||
"^preact.*",
|
|
||||||
"^axios.*",
|
|
||||||
"^@src/.*",
|
|
||||||
"^@models/.*",
|
|
||||||
"^@interfaces/.*",
|
|
||||||
"^@styles/.*",
|
|
||||||
"^@shared/.*",
|
|
||||||
"^@utils/.*",
|
|
||||||
"^@components/.*",
|
|
||||||
"^@svg/.*"
|
|
||||||
]
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"useTabs": true,
|
||||||
|
"semi": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"stylelint-config-standard",
|
||||||
|
"stylelint-config-idiomatic-order"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indentation": "tab"
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"prettier.configPath": ".prettierrc"
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
FROM node:alpine AS deps
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json yarn.lock ./
|
||||||
|
RUN yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
FROM node:alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1;
|
||||||
|
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline
|
||||||
|
|
||||||
|
FROM node:alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
RUN addgroup -g 1001 -S nodejs
|
||||||
|
RUN adduser -S nextjs -u 1001
|
||||||
|
|
||||||
|
COPY --from=builder /app/next.config.js ./
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["yarn", "start"]
|
@ -1,5 +0,0 @@
|
|||||||
# Portfolio
|
|
||||||
|
|
||||||
This is my portfolio website to show off all of my projects and provide my socials.
|
|
||||||
|
|
||||||
It's build using Preact and Styled-Components and uses Vite as its build tool.
|
|
@ -0,0 +1,8 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: app
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Guus' Portfolio</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #000000;
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
<noscript>Javascript needs to be enabled to run this application</noscript>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
@ -0,0 +1,7 @@
|
|||||||
|
// @ts-check
|
||||||
|
/**
|
||||||
|
* @type {import('next/dist/next-server/server/config').NextConfig}
|
||||||
|
**/
|
||||||
|
module.exports = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
}
|
@ -1,39 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "portfolio",
|
"license": "MIT",
|
||||||
"private": true,
|
|
||||||
"version": "0.1.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier src --write",
|
"dev": "next dev",
|
||||||
"dev": "vite",
|
"build": "next build",
|
||||||
"build": "tsc && vite build",
|
"start": "next start",
|
||||||
"preview": "vite preview"
|
"export": "next build && next export",
|
||||||
|
"test-build": "tsc",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"stylelint": "npx stylelint **/*.scss",
|
||||||
|
"full-test": "yarn test-build && yarn lint && yarn stylelint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/montserrat": "^4.5.11",
|
"axios": "^1.1.2",
|
||||||
"@fontsource/prompt": "^4.5.8",
|
"configcat-node": "^8.0.0",
|
||||||
"@tanstack/react-query": "^4.0.10",
|
"next": "^12.1.0",
|
||||||
"@tanstack/react-query-devtools": "^4.0.10",
|
"next-seo": "^4.24.0",
|
||||||
"axios": "^0.27.2",
|
"next-themes": "^0.2.1",
|
||||||
"bootstrap": "^5.2.0",
|
"react": "^17.0.2",
|
||||||
"preact": "^10.9.0",
|
"react-dom": "^17.0.2",
|
||||||
"react-bootstrap": "^2.4.0",
|
"spectre.css": "^0.5.9"
|
||||||
"react-icons": "^4.4.0",
|
|
||||||
"react-router-dom": "6",
|
|
||||||
"react-spring": "^9.5.2",
|
|
||||||
"reset-css": "^5.0.1",
|
|
||||||
"styled-components": "^5.3.5",
|
|
||||||
"use-local-storage-state": "^18.1.0",
|
|
||||||
"vite-plugin-imagemin": "^0.6.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.3.0",
|
"@types/node": "^15.12.1",
|
||||||
"@rollup/plugin-alias": "^3.1.9",
|
"@types/react": "^17.0.9",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
"@types/react-dom": "^17.0.6",
|
||||||
"@types/styled-components": "^5.1.25",
|
"@typescript-eslint/eslint-plugin": "^4.26.0",
|
||||||
"prettier": "^2.7.1",
|
"@typescript-eslint/parser": "^4.26.0",
|
||||||
"typescript": "^4.6.4",
|
"eslint": "^7.28.0",
|
||||||
"vite": "^3.0.0",
|
"eslint-plugin-css-modules": "^2.11.0",
|
||||||
"vite-tsconfig-paths": "^3.5.0"
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
|
"eslint-plugin-prettier": "^3.4.0",
|
||||||
|
"eslint-plugin-react": "^7.24.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
|
"prettier": "^2.3.1",
|
||||||
|
"sass": "^1.34.1",
|
||||||
|
"stylelint": "^13.13.1",
|
||||||
|
"stylelint-config-idiomatic-order": "^8.1.0",
|
||||||
|
"stylelint-config-standard": "^22.0.0",
|
||||||
|
"typescript": "^4.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 89 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
import { BestRepository } from "@interfaces/repository";
|
||||||
|
|
||||||
|
const BestRepository: FC<{ repository: BestRepository }> = ({ repository }) => {
|
||||||
|
return (
|
||||||
|
<div className="hero bg-primary">
|
||||||
|
<div className="container">
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column col-8 col-mx-auto">
|
||||||
|
<h3 className="text-secondary">My most popular project:</h3>
|
||||||
|
<h1>{repository.name}</h1>
|
||||||
|
<h3 className="text-secondary">
|
||||||
|
{repository.stargazers_count} Star(s)
|
||||||
|
</h3>
|
||||||
|
<h5>{repository.description}</h5>
|
||||||
|
<p className="text-secondary">
|
||||||
|
Written in {repository.language}, has{" "}
|
||||||
|
{repository.open_issues_count} issue(s) and{" "}
|
||||||
|
{repository.forks_count} fork(s).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Link href={repository.url}>
|
||||||
|
<a className="btn mr-2">Github</a>
|
||||||
|
</Link>
|
||||||
|
{repository.homepage && (
|
||||||
|
<Link href={repository.homepage}>
|
||||||
|
<a className="btn">Website</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BestRepository;
|
@ -1,24 +0,0 @@
|
|||||||
import { createGlobalStyle } from "styled-components";
|
|
||||||
|
|
||||||
const Baseline = createGlobalStyle`
|
|
||||||
body {
|
|
||||||
color: ${({ theme }) => theme.palette.text.primary};
|
|
||||||
background-color: ${({ theme }) => theme.palette.background.primary};
|
|
||||||
font-family: "Montserrat", sans-serif;
|
|
||||||
|
|
||||||
background-image: url("/topography.svg");
|
|
||||||
background-repeat: repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: ${({ theme }) => theme.palette.text.primary};
|
|
||||||
text-decoration: none;
|
|
||||||
:hover {
|
|
||||||
color: ${({ theme }) => theme.palette.text.secondary};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CssBaseline = Baseline as () => JSX.Element;
|
|
||||||
|
|
||||||
export default CssBaseline;
|
|
@ -1,290 +0,0 @@
|
|||||||
import styled, { useTheme } from "styled-components";
|
|
||||||
|
|
||||||
import { FunctionalComponent } from "preact";
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
width: 100%;
|
|
||||||
transform: ${(props) => (props.left ? "scale(-1, -1)" : "none")};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Dots: FunctionalComponent<{
|
|
||||||
left?: boolean;
|
|
||||||
}> = ({ left }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container left={left}>
|
|
||||||
<svg
|
|
||||||
version="1.0"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 645.000000 456.000000"
|
|
||||||
preserveAspectRatio="xMidYMid meet"
|
|
||||||
>
|
|
||||||
<g
|
|
||||||
transform="translate(0.000000,456.000000) scale(0.100000,-0.100000)"
|
|
||||||
fill={theme.palette.secondary}
|
|
||||||
stroke="none"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M3825 4548 c-28 -16 -35 -28 -35 -64 0 -79 106 -88 133 -11 9 25 -18
|
|
||||||
73 -45 81 -29 7 -29 7 -53 -6z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4455 4548 c-28 -16 -35 -28 -35 -64 0 -39 27 -64 68 -64 58 0 90 76
|
|
||||||
49 117 -19 18 -60 24 -82 11z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5077 4540 c-20 -16 -27 -30 -27 -55 0 -64 80 -89 120 -38 50 64 -29
|
|
||||||
143 -93 93z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5706 4539 c-50 -39 -22 -119 43 -119 20 0 36 9 50 26 27 35 26 59
|
|
||||||
-4 89 -30 30 -54 31 -89 4z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6336 4539 c-49 -39 -22 -119 42 -119 33 0 45 8 61 37 17 33 14 51
|
|
||||||
-14 78 -30 30 -54 31 -89 4z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3825 3918 c-28 -16 -35 -28 -35 -64 0 -38 27 -64 67 -64 69 0 91
|
|
||||||
100 28 128 -31 14 -35 14 -60 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4447 3910 c-20 -16 -27 -31 -27 -54 0 -66 79 -91 120 -39 50 64 -29
|
|
||||||
143 -93 93z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5077 3910 c-20 -16 -27 -31 -27 -54 0 -66 79 -91 120 -39 50 64 -29
|
|
||||||
143 -93 93z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5706 3909 c-50 -39 -24 -119 39 -119 78 0 101 103 29 130 -34 13
|
|
||||||
-38 13 -68 -11z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6336 3909 c-50 -39 -22 -119 43 -119 75 0 95 104 25 130 -34 13 -38
|
|
||||||
13 -68 -11z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3816 3279 c-52 -41 -25 -119 41 -119 69 0 91 100 28 128 -34 16 -38
|
|
||||||
15 -69 -9z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4446 3279 c-32 -25 -36 -77 -7 -103 28 -25 79 -17 104 16 17 23 18
|
|
||||||
31 8 56 -11 26 -46 52 -70 52 -5 0 -20 -9 -35 -21z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5100 3293 c-50 -18 -68 -84 -32 -116 19 -17 68 -22 88 -9 21 13 34
|
|
||||||
52 28 77 -5 20 -51 58 -66 54 -2 0 -10 -3 -18 -6z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5730 3293 c-31 -12 -50 -37 -50 -68 0 -78 111 -91 135 -16 9 28 -12
|
|
||||||
70 -40 81 -15 6 -27 10 -28 9 -1 0 -9 -3 -17 -6z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6360 3293 c-73 -27 -62 -133 14 -133 42 0 64 17 72 55 9 48 -42 94
|
|
||||||
-86 78z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1939 2658 c-36 -12 -55 -58 -38 -90 16 -31 28 -38 65 -38 77 0 88
|
|
||||||
107 14 132 -8 3 -27 1 -41 -4z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2567 2658 c-27 -10 -45 -46 -38 -78 5 -28 34 -50 66 -50 78 0 90
|
|
||||||
107 15 132 -8 3 -27 1 -43 -4z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3210 2662 c-49 -16 -68 -83 -32 -115 25 -22 75 -21 95 1 43 47 -5
|
|
||||||
133 -63 114z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3840 2662 c-49 -15 -68 -83 -32 -115 24 -22 75 -22 95 1 19 22 23
|
|
||||||
40 14 74 -7 27 -49 49 -77 40z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4470 2662 c-30 -10 -50 -36 -50 -67 0 -39 26 -65 66 -65 35 0 47 8
|
|
||||||
63 38 25 47 -29 110 -79 94z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5100 2662 c-30 -10 -50 -36 -50 -67 0 -39 26 -65 66 -65 35 0 47 8
|
|
||||||
63 38 25 47 -29 110 -79 94z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5730 2662 c-30 -10 -50 -36 -50 -67 0 -78 111 -91 135 -16 14 44
|
|
||||||
-40 97 -85 83z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6360 2662 c-48 -15 -64 -67 -34 -110 32 -46 124 -16 124 41 0 46
|
|
||||||
-47 82 -90 69z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1924 2022 c-6 -4 -17 -18 -23 -30 -25 -48 23 -104 79 -93 48 9 68
|
|
||||||
82 32 114 -19 17 -68 22 -88 9z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2547 2013 c-19 -23 -23 -41 -14 -75 7 -28 43 -46 77 -39 48 9 68 82
|
|
||||||
32 114 -24 22 -75 22 -95 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3177 2012 c-39 -42 -8 -114 48 -114 57 0 88 78 47 115 -25 22 -75
|
|
||||||
21 -95 -1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3807 2012 c-33 -37 -14 -104 33 -113 34 -7 70 11 77 39 9 34 5 52
|
|
||||||
-14 75 -21 23 -75 22 -96 -1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4437 2012 c-33 -37 -14 -104 33 -113 56 -11 104 45 79 93 -6 12 -17
|
|
||||||
26 -23 30 -21 14 -73 8 -89 -10z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5067 2012 c-33 -37 -14 -104 33 -113 56 -11 104 45 79 93 -6 12 -17
|
|
||||||
26 -23 30 -21 14 -73 8 -89 -10z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5697 2012 c-22 -24 -22 -75 1 -95 54 -48 133 -1 116 69 -11 45 -86
|
|
||||||
61 -117 26z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6326 2008 c-21 -30 -20 -71 2 -91 45 -40 122 -10 122 48 0 59 -91
|
|
||||||
90 -124 43z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M20 1380 c-41 -41 -11 -113 48 -113 62 0 92 60 56 111 -20 29 -76 30
|
|
||||||
-104 2z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M656 1384 c-38 -37 -27 -96 20 -114 67 -26 128 66 76 113 -23 21 -76
|
|
||||||
22 -96 1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1294 1392 c-21 -13 -34 -52 -28 -77 7 -30 54 -58 81 -48 53 18 72
|
|
||||||
83 35 116 -19 17 -68 22 -88 9z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1911 1374 c-24 -31 -25 -35 -10 -67 31 -67 129 -46 129 28 0 64 -79
|
|
||||||
90 -119 39z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2547 1383 c-33 -38 -22 -94 24 -113 27 -11 33 -11 57 7 33 25 41 76
|
|
||||||
16 104 -22 24 -76 25 -97 2z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3177 1382 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
|
||||||
-10 89 -25 22 -75 21 -95 -1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3806 1381 c-25 -28 -17 -79 16 -104 64 -48 135 45 81 106 -21 23
|
|
||||||
-75 22 -97 -2z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4437 1382 c-48 -53 12 -140 77 -110 14 6 30 24 37 40 10 25 9 33 -8
|
|
||||||
56 -26 34 -81 42 -106 14z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5067 1382 c-51 -56 15 -142 82 -107 45 23 49 90 7 117 -21 14 -73 8
|
|
||||||
-89 -10z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5697 1382 c-48 -53 11 -138 78 -112 45 17 55 79 19 115 -21 21 -77
|
|
||||||
19 -97 -3z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6326 1378 c-36 -51 -6 -111 56 -111 36 0 68 32 68 69 0 58 -92 89
|
|
||||||
-124 42z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M26 754 c-40 -40 -24 -105 30 -118 62 -16 110 68 65 113 -25 25 -73
|
|
||||||
27 -95 5z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M656 754 c-61 -61 15 -157 83 -106 53 39 30 122 -34 122 -18 0 -41
|
|
||||||
-7 -49 -16z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1280 743 c-50 -64 29 -143 93 -93 20 16 27 31 27 54 0 66 -79 91
|
|
||||||
-120 39z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1910 743 c-50 -64 29 -143 93 -93 20 16 27 31 27 54 0 66 -79 91
|
|
||||||
-120 39z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2548 753 c-36 -43 -27 -93 22 -113 29 -12 65 -1 82 24 14 21 8 73
|
|
||||||
-10 89 -24 22 -75 22 -94 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3177 752 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
|
||||||
-10 89 -25 22 -75 21 -95 -1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3807 752 c-17 -19 -22 -68 -9 -88 4 -6 18 -17 30 -23 48 -25 104 23
|
|
||||||
93 79 -9 48 -82 68 -114 32z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4436 751 c-25 -27 -18 -79 15 -103 36 -27 76 -15 97 28 15 32 15 36
|
|
||||||
-5 62 -26 35 -81 42 -107 13z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5067 752 c-27 -29 -22 -77 10 -102 64 -50 143 29 93 93 -26 33 -78
|
|
||||||
37 -103 9z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5695 746 c-22 -33 -15 -75 16 -98 68 -51 144 45 83 106 -25 25 -81
|
|
||||||
20 -99 -8z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6325 746 c-22 -33 -15 -75 16 -98 68 -51 144 45 83 106 -25 25 -81
|
|
||||||
20 -99 -8z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M26 124 c-61 -61 14 -158 82 -107 35 26 41 74 13 102 -25 25 -73 27
|
|
||||||
-95 5z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M651 114 c-12 -15 -21 -35 -21 -46 0 -24 44 -68 68 -68 33 0 72 38
|
|
||||||
72 71 0 65 -80 93 -119 43z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1280 113 c-50 -64 29 -143 93 -93 20 16 27 30 27 55 0 64 -80 89
|
|
||||||
-120 38z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M1911 114 c-24 -31 -24 -34 -11 -69 26 -69 130 -47 130 29 0 65 -79
|
|
||||||
91 -119 40z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2548 123 c-25 -29 -29 -53 -13 -82 23 -45 90 -49 117 -7 14 21 8 73
|
|
||||||
-10 89 -24 22 -75 22 -94 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3177 122 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
|
||||||
-10 89 -25 22 -75 21 -95 -1z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M3807 122 c-17 -19 -22 -68 -9 -88 13 -21 52 -34 77 -28 30 7 58 54
|
|
||||||
48 81 -18 53 -83 72 -116 35z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4437 122 c-21 -23 -22 -75 -2 -96 38 -38 101 -23 118 28 20 61 -73
|
|
||||||
116 -116 68z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5067 122 c-27 -29 -22 -77 10 -102 64 -50 143 29 93 93 -26 33 -78
|
|
||||||
37 -103 9z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5700 120 c-41 -41 -7 -120 52 -120 24 0 68 44 68 68 0 59 -79 93
|
|
||||||
-120 52z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M6329 119 c-28 -28 -22 -76 13 -102 68 -51 143 46 82 107 -22 22 -70
|
|
||||||
20 -95 -5z"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Dots;
|
|
@ -1,10 +0,0 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Header = styled.h1`
|
|
||||||
font-family: "Prompt", sans-serif;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 3rem;
|
|
||||||
margin-bottom: ${(props) => (props.gutter ? "1rem" : "0")};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Header;
|
|
@ -1,11 +0,0 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Image = styled.img.attrs(() => ({ draggable: false }))`
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-o-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Image;
|
|
@ -0,0 +1,46 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
import styles from "./intro.module.scss";
|
||||||
|
|
||||||
|
const Intro: FC<{ isAvailable: boolean }> = ({ isAvailable }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.main}>
|
||||||
|
<div className="container">
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column col-8 col-mx-auto text-center">
|
||||||
|
<h1>Guus van Meerveld</h1>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
Open source <u>web developer</u>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
href={`https://github.com/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}`}
|
||||||
|
className="btn btn-primary mr-2"
|
||||||
|
>
|
||||||
|
Github
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="mailto:contact@guusvanmeerveld.dev"
|
||||||
|
className="btn btn-primary"
|
||||||
|
>
|
||||||
|
Contact
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-gray">
|
||||||
|
Availibility: {isAvailable && "Available"}
|
||||||
|
{!isAvailable && "Not available"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Intro;
|
@ -1,7 +0,0 @@
|
|||||||
import styled, { DefaultTheme } from "styled-components";
|
|
||||||
|
|
||||||
const Link = styled.a`
|
|
||||||
color: ${({ theme }: { theme: DefaultTheme }) => theme.palette.primary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Link;
|
|
@ -1,9 +0,0 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Paragraph = styled.p`
|
|
||||||
font-family: "Montserrat", sans-serif;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
margin-bottom: ${(props) => (props.gutter ? ".5rem" : "0")};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Paragraph;
|
|
@ -1,153 +0,0 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
|
|
||||||
import styled, { DefaultTheme } from "styled-components";
|
|
||||||
|
|
||||||
import { Spinner } from "react-bootstrap";
|
|
||||||
import Col from "react-bootstrap/Col";
|
|
||||||
import Row from "react-bootstrap/Row";
|
|
||||||
import { AiOutlineStar, AiOutlineFork } from "react-icons/ai";
|
|
||||||
|
|
||||||
import { FunctionalComponent } from "preact";
|
|
||||||
|
|
||||||
import axios, { AxiosError } from "axios";
|
|
||||||
|
|
||||||
import Repo from "@interfaces/repo";
|
|
||||||
|
|
||||||
import Header from "@components/Header";
|
|
||||||
import Link from "@components/Link";
|
|
||||||
import Paragraph from "@components/Paragraph";
|
|
||||||
|
|
||||||
const SpinnerContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ProjectItem = styled(Row)`
|
|
||||||
margin-bottom: 5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Hero = styled.div`
|
|
||||||
padding: 2rem;
|
|
||||||
background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
|
||||||
theme.palette.background.secondary};
|
|
||||||
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 2px solid
|
|
||||||
${({ theme }: { theme: DefaultTheme }) => theme.palette.border};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Heading = styled(Header)`
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Leading = styled.div`
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Subtitle = styled.h4`
|
|
||||||
color: ${({ theme }: { theme: DefaultTheme }) =>
|
|
||||||
theme.palette.text.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const IconsTray = styled.div`
|
|
||||||
display: flex;
|
|
||||||
margin-top: 1rem;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const IconItem = styled(Paragraph)`
|
|
||||||
margin-right: 1rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Icon = styled.span`
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
color: ${({ theme }: { theme: DefaultTheme }) => theme.palette.primary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const TopicsTray = styled.div`
|
|
||||||
margin-top: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Topic = styled.div`
|
|
||||||
background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
|
||||||
theme.palette.primary};
|
|
||||||
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ListItem: FunctionalComponent<{
|
|
||||||
repoFullName: string;
|
|
||||||
rtl?: boolean;
|
|
||||||
}> = ({ repoFullName, rtl }) => {
|
|
||||||
const { data, isLoading, error } = useQuery<Repo, AxiosError>(
|
|
||||||
["repo", repoFullName],
|
|
||||||
() =>
|
|
||||||
axios
|
|
||||||
.get(`https://api.github.com/repos/${repoFullName}`)
|
|
||||||
.then((res) => res.data)
|
|
||||||
);
|
|
||||||
|
|
||||||
const body = [
|
|
||||||
<Col md={8}>
|
|
||||||
<Hero>
|
|
||||||
{isLoading && !data && (
|
|
||||||
<SpinnerContainer>
|
|
||||||
<Spinner animation="border" role="status">
|
|
||||||
<span className="visually-hidden">Loading {repoFullName}</span>
|
|
||||||
</Spinner>
|
|
||||||
</SpinnerContainer>
|
|
||||||
)}
|
|
||||||
{data && !isLoading && (
|
|
||||||
<>
|
|
||||||
<Leading>
|
|
||||||
<Heading>
|
|
||||||
<a href={data.html_url}>{data.full_name}</a>
|
|
||||||
</Heading>
|
|
||||||
{data.homepage && (
|
|
||||||
<Subtitle>
|
|
||||||
Website: <Link href={data.homepage}>{data.homepage}</Link>
|
|
||||||
</Subtitle>
|
|
||||||
)}
|
|
||||||
</Leading>
|
|
||||||
<Paragraph gutter>{data.description}</Paragraph>
|
|
||||||
<IconsTray>
|
|
||||||
<IconItem>
|
|
||||||
{data.stargazers_count}
|
|
||||||
<Icon>
|
|
||||||
<AiOutlineStar />
|
|
||||||
</Icon>
|
|
||||||
</IconItem>
|
|
||||||
<IconItem>
|
|
||||||
{data.forks_count}
|
|
||||||
<Icon>
|
|
||||||
<AiOutlineFork />
|
|
||||||
</Icon>
|
|
||||||
</IconItem>
|
|
||||||
</IconsTray>
|
|
||||||
<TopicsTray>
|
|
||||||
<Paragraph>Tags:</Paragraph>
|
|
||||||
{data.topics.map((topic) => (
|
|
||||||
<Topic>{topic}</Topic>
|
|
||||||
))}
|
|
||||||
</TopicsTray>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Hero>
|
|
||||||
</Col>,
|
|
||||||
<Col md={4}></Col>
|
|
||||||
];
|
|
||||||
|
|
||||||
return <ProjectItem>{rtl ? body.reverse() : body}</ProjectItem>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListItem;
|
|
@ -1,76 +0,0 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
|
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
import { Spinner } from "react-bootstrap";
|
|
||||||
|
|
||||||
import { FunctionalComponent } from "preact";
|
|
||||||
|
|
||||||
import axios, { AxiosError } from "axios";
|
|
||||||
|
|
||||||
import PinnedRepo from "@interfaces/pinnedRepo";
|
|
||||||
|
|
||||||
import Paragraph from "@components/Paragraph";
|
|
||||||
import ListItem from "@components/Project/Item";
|
|
||||||
|
|
||||||
const SpinnerContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ErrorText = styled(Paragraph)`
|
|
||||||
text-align: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const List: FunctionalComponent = () => {
|
|
||||||
const { data, isLoading, error } = useQuery<PinnedRepo[], AxiosError>(
|
|
||||||
["pinnedRepos"],
|
|
||||||
() =>
|
|
||||||
axios
|
|
||||||
.get(
|
|
||||||
`https://gh-pinned-repos.egoist.sh/?username=${
|
|
||||||
import.meta.env.VITE_GITHUB_USERNAME
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
.then((res) => res.data)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isLoading)
|
|
||||||
return (
|
|
||||||
<SpinnerContainer>
|
|
||||||
<Spinner animation="border" role="status">
|
|
||||||
<span className="visually-hidden">Loading pinned repos...</span>
|
|
||||||
</Spinner>
|
|
||||||
</SpinnerContainer>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
return (
|
|
||||||
<ErrorText>
|
|
||||||
Status {error.response?.status ?? "Unknown"} - {error.response?.data}
|
|
||||||
</ErrorText>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!error && data) {
|
|
||||||
if (Array.isArray(data))
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{data.map((repo, i) => {
|
|
||||||
const repoFullName = `${repo.owner}/${repo.repo}`;
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
key={repoFullName}
|
|
||||||
rtl={!(i % 2)}
|
|
||||||
repoFullName={repoFullName}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
else return <ErrorText>Unknown data returned from api</ErrorText>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default List;
|
|
@ -0,0 +1,66 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { RecentRepository } from "@interfaces/repository";
|
||||||
|
|
||||||
|
import multipleClassNames from "@utils/multipleClassNames";
|
||||||
|
|
||||||
|
import styles from "./repositories.module.scss";
|
||||||
|
|
||||||
|
const RecentRepositories: FC<{ repositories: RecentRepository[] }> = ({
|
||||||
|
repositories
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className={multipleClassNames("container", styles.main)}>
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column col-6 col-mx-auto text-center">
|
||||||
|
<h3>Some of my recent projects:</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column col-9 col-mx-auto">
|
||||||
|
<div className="columns">
|
||||||
|
{repositories.map((repository) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={repository.name}
|
||||||
|
className="column col-3 col-md-12 col-mx-auto mb-2"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={multipleClassNames(
|
||||||
|
"card",
|
||||||
|
"text-center",
|
||||||
|
styles.card
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="card-header text-primary">
|
||||||
|
<div className="card-title h5">{repository.name}</div>
|
||||||
|
<div className="card-subtitle text-gray">
|
||||||
|
{repository.stargazers_count} Star(s)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">{repository.description}</div>
|
||||||
|
<div className="card-footer">
|
||||||
|
<Link href={repository.url}>
|
||||||
|
<a className="btn btn-primary">Github</a>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{repository.homepage && (
|
||||||
|
<Link href={repository.homepage}>
|
||||||
|
<a className="btn btn-primary ml-2">Website</a>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RecentRepositories;
|
@ -0,0 +1,30 @@
|
|||||||
|
import { FC, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
|
||||||
|
import styles from "./themeChanger.module.scss";
|
||||||
|
|
||||||
|
const ThemeChanger: FC = () => {
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!mounted) return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
||||||
|
<a
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={styles.main}
|
||||||
|
onClick={() => setTheme(theme == "light" ? "dark" : "light")}
|
||||||
|
>
|
||||||
|
{theme}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeChanger;
|
@ -0,0 +1,6 @@
|
|||||||
|
$margin: 1rem;
|
||||||
|
|
||||||
|
.main {
|
||||||
|
margin-top: $margin;
|
||||||
|
margin-bottom: $margin;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
.main {
|
||||||
|
padding-top: 10rem;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
.card {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
.main {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
export default interface PinnedRepo {
|
|
||||||
owner: string;
|
|
||||||
repo: string;
|
|
||||||
link: string;
|
|
||||||
description: string;
|
|
||||||
image: string;
|
|
||||||
website: string;
|
|
||||||
language: string;
|
|
||||||
languageColor: string;
|
|
||||||
stars: string;
|
|
||||||
forks: number;
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import "@fontsource/montserrat";
|
|
||||||
import "@fontsource/prompt";
|
|
||||||
import "bootstrap/dist/css/bootstrap.min.css";
|
|
||||||
import "reset-css";
|
|
||||||
|
|
||||||
import App from "./pages/app";
|
|
||||||
|
|
||||||
import { render } from "preact";
|
|
||||||
|
|
||||||
render(<App />, document.getElementById("app") as HTMLElement);
|
|
@ -0,0 +1,9 @@
|
|||||||
|
import type { DefaultSeoProps } from "next-seo";
|
||||||
|
|
||||||
|
const SEO: DefaultSeoProps = {
|
||||||
|
titleTemplate: "%s | Guus van Meerveld",
|
||||||
|
defaultTitle: "Guus van Meerveld",
|
||||||
|
description: "Guus van Meerveld's portfolio"
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SEO;
|
@ -0,0 +1,8 @@
|
|||||||
|
.main {
|
||||||
|
height: 100vh;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@ -1,5 +1,35 @@
|
|||||||
const NotFound = () => {
|
import { NextPage } from "next";
|
||||||
return <div>Page not found</div>;
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import Layout from "@components/Layout";
|
||||||
|
|
||||||
|
import multipleClassNames from "@utils/multipleClassNames";
|
||||||
|
|
||||||
|
import styles from "./404.module.scss";
|
||||||
|
|
||||||
|
const NotFound: NextPage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className={multipleClassNames("empty", styles.main)}>
|
||||||
|
<div>
|
||||||
|
<div className="empty-icon">
|
||||||
|
<i className="icon icon-stop"></i>
|
||||||
|
</div>
|
||||||
|
<p className="empty-title h5">Page not found</p>
|
||||||
|
<p className="empty-subtitle">
|
||||||
|
The page has either been deleted or moved
|
||||||
|
</p>
|
||||||
|
<div className="empty-action">
|
||||||
|
<button onClick={() => router.back()} className="btn btn-primary">
|
||||||
|
Go back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NotFound;
|
export default NotFound;
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import "@styles/globals.scss";
|
||||||
|
|
||||||
|
import SEO from "../next-seo.config";
|
||||||
|
|
||||||
|
import { DefaultSeo } from "next-seo";
|
||||||
|
import { ThemeProvider } from "next-themes";
|
||||||
|
|
||||||
|
import type { AppProps } from "next/app";
|
||||||
|
|
||||||
|
const App = ({ Component, pageProps }: AppProps): JSX.Element => (
|
||||||
|
<>
|
||||||
|
<DefaultSeo {...SEO} />
|
||||||
|
<ThemeProvider>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</ThemeProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default App;
|
@ -1,40 +0,0 @@
|
|||||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
||||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
||||||
|
|
||||||
import NotFound from "./404";
|
|
||||||
import Index from "./index";
|
|
||||||
|
|
||||||
import useLocalStorageState from "use-local-storage-state";
|
|
||||||
|
|
||||||
import { ThemeProvider } from "styled-components";
|
|
||||||
|
|
||||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
|
||||||
|
|
||||||
import { FunctionalComponent } from "preact";
|
|
||||||
|
|
||||||
import { lightTheme, darkTheme } from "@utils/theme";
|
|
||||||
|
|
||||||
import CssBaseline from "@components/CssBaseline";
|
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
|
||||||
|
|
||||||
const App: FunctionalComponent = () => {
|
|
||||||
const [darkMode] = useLocalStorageState("darkMode", { defaultValue: true });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
|
|
||||||
<CssBaseline />
|
|
||||||
<QueryClientProvider client={queryClient}>
|
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
|
||||||
<BrowserRouter>
|
|
||||||
<Routes>
|
|
||||||
<Route path="/" element={<Index />} />
|
|
||||||
<Route path="*" element={<NotFound />} />
|
|
||||||
</Routes>
|
|
||||||
</BrowserRouter>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
@ -1,147 +1,75 @@
|
|||||||
import useLocalStorageState from "use-local-storage-state";
|
import { NextSeo } from "next-seo";
|
||||||
|
|
||||||
import styled, { DefaultTheme } from "styled-components";
|
import { GetStaticProps, InferGetStaticPropsType, NextPage } from "next";
|
||||||
|
|
||||||
import Col from "react-bootstrap/Col";
|
import axios from "axios";
|
||||||
import Container from "react-bootstrap/Container";
|
|
||||||
import Row from "react-bootstrap/Row";
|
|
||||||
import { animated, useSpring } from "react-spring";
|
|
||||||
|
|
||||||
import { FunctionalComponent } from "preact";
|
import Intro from "@components/Intro";
|
||||||
|
|
||||||
import socials from "@utils/socials";
|
|
||||||
|
|
||||||
import Dots from "@components/Dots";
|
|
||||||
import Header from "@components/Header";
|
|
||||||
import Image from "@components/Image";
|
|
||||||
import Layout from "@components/Layout";
|
import Layout from "@components/Layout";
|
||||||
import Paragraph from "@components/Paragraph";
|
import RecentRepositories from "@components/RecentRepositories";
|
||||||
import ProjectsList from "@components/Project/List";
|
import BestRepository from "@components/BestRepository";
|
||||||
|
|
||||||
const Presentation = styled.div`
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Projects = styled.div`
|
|
||||||
margin-top: 5rem;
|
|
||||||
padding: 5rem 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Hero = styled.div`
|
|
||||||
/* background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
|
||||||
theme.palette.background.secondary}; */
|
|
||||||
justify-content: ${(props) => (props.right ? "right" : "left")};
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Logo = styled.div`
|
|
||||||
margin: 2rem 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ProjectsHeader = styled(Header)`
|
import { GithubAPIRepository } from "@interfaces/repository";
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 5rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const IconsTray = styled.div`
|
import createConfigCatClient from "@utils/createConfigCatClient";
|
||||||
display: flex;
|
|
||||||
justify-content: right;
|
|
||||||
margin-top: 1rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Icon = styled.div`
|
export const getStaticProps: GetStaticProps = async () => {
|
||||||
margin-left: 1.5rem;
|
const { data } = await axios.get<GithubAPIRepository[]>(
|
||||||
font-size: 2rem;
|
`https://api.github.com/users/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}/repos`
|
||||||
`;
|
);
|
||||||
|
|
||||||
const Index: FunctionalComponent = () => {
|
|
||||||
const [darkMode] = useLocalStorageState("darkMode");
|
|
||||||
|
|
||||||
const fadeIn = useSpring({
|
const configCatClient = createConfigCatClient();
|
||||||
to: { opacity: 1 },
|
|
||||||
from: { opacity: 0 },
|
const isAvailable: boolean =
|
||||||
config: {
|
(await configCatClient?.getValueAsync("amiavailable", true)) ?? true;
|
||||||
mass: 5
|
|
||||||
|
const bestRepository = data.sort(
|
||||||
|
(a, b) => b.stargazers_count - a.stargazers_count
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isAvailable,
|
||||||
|
repositories: data
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
|
||||||
|
)
|
||||||
|
.map(({ name, description, html_url, stargazers_count, homepage }) => ({
|
||||||
|
name,
|
||||||
|
url: html_url,
|
||||||
|
stargazers_count,
|
||||||
|
homepage,
|
||||||
|
description
|
||||||
|
}))
|
||||||
|
.slice(0, 3),
|
||||||
|
bestRepository: {
|
||||||
|
name: bestRepository.name,
|
||||||
|
description: bestRepository.description,
|
||||||
|
url: bestRepository.html_url,
|
||||||
|
homepage: bestRepository.homepage,
|
||||||
|
stargazers_count: bestRepository.stargazers_count,
|
||||||
|
forks_count: bestRepository.forks_count,
|
||||||
|
language: bestRepository.language,
|
||||||
|
open_issues_count: bestRepository.open_issues_count,
|
||||||
|
pushed_at: bestRepository.pushed_at
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
revalidate: 60 * 5
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const Index: NextPage = ({
|
||||||
|
repositories,
|
||||||
|
isAvailable,
|
||||||
|
bestRepository
|
||||||
|
}: InferGetStaticPropsType<typeof getStaticProps>) => (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Presentation>
|
<NextSeo title="Home" />
|
||||||
<Container>
|
<Intro isAvailable={isAvailable} />
|
||||||
<animated.div style={fadeIn}>
|
<RecentRepositories repositories={repositories} />
|
||||||
<Row>
|
<BestRepository repository={bestRepository} />
|
||||||
<Col md={8}>
|
|
||||||
<Hero>
|
|
||||||
<div>
|
|
||||||
<Header gutter>Welcome</Header>
|
|
||||||
<Paragraph gutter>
|
|
||||||
My name is Guus van Meerveld, and I am a web developer.
|
|
||||||
</Paragraph>
|
|
||||||
<Paragraph>
|
|
||||||
This is my portfolio website, to showcase my projects.
|
|
||||||
</Paragraph>
|
|
||||||
</div>
|
|
||||||
</Hero>
|
|
||||||
</Col>
|
|
||||||
<Col md={4}>
|
|
||||||
<Dots />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Logo>
|
|
||||||
<Image
|
|
||||||
// onClick={() => setDarkMode(!darkMode)}
|
|
||||||
// style={{ cursor: "pointer" }}
|
|
||||||
height={64}
|
|
||||||
src={darkMode ? "logo-dark.png" : "logo-light.png"}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
</Logo>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<Col md={4}>
|
|
||||||
<Dots left />
|
|
||||||
</Col>
|
|
||||||
<Col md={8}>
|
|
||||||
<Hero right>
|
|
||||||
<div>
|
|
||||||
<Header>Follow me:</Header>
|
|
||||||
<IconsTray>
|
|
||||||
{socials.map((social) => (
|
|
||||||
<Icon>
|
|
||||||
<a href={social.url}>{social.icon}</a>
|
|
||||||
</Icon>
|
|
||||||
))}
|
|
||||||
</IconsTray>
|
|
||||||
</div>
|
|
||||||
</Hero>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</animated.div>
|
|
||||||
</Container>
|
|
||||||
</Presentation>
|
|
||||||
|
|
||||||
<Projects>
|
|
||||||
<Container>
|
|
||||||
<Row>
|
|
||||||
<ProjectsHeader>Projects</ProjectsHeader>
|
|
||||||
</Row>
|
|
||||||
<Row>
|
|
||||||
<ProjectsList />
|
|
||||||
</Row>
|
|
||||||
</Container>
|
|
||||||
</Projects>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export default Index;
|
export default Index;
|
||||||
|
@ -1 +0,0 @@
|
|||||||
import JSX = preact.JSX;
|
|
@ -1,21 +0,0 @@
|
|||||||
import Theme from "./interfaces/theme";
|
|
||||||
|
|
||||||
import "styled-components";
|
|
||||||
|
|
||||||
declare module "styled-components" {
|
|
||||||
export interface DefaultTheme {
|
|
||||||
palette: {
|
|
||||||
primary: string;
|
|
||||||
secondary: string;
|
|
||||||
background: {
|
|
||||||
primary: string;
|
|
||||||
secondary: string;
|
|
||||||
};
|
|
||||||
text: {
|
|
||||||
primary: string;
|
|
||||||
secondary: string;
|
|
||||||
};
|
|
||||||
border: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,66 @@
|
|||||||
|
[data-theme="dark"] {
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: $bg-dark;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
background-color: $dark-color-secondary;
|
||||||
|
color: $text-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: $bg-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: $primary-color-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: $primary-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-primary {
|
||||||
|
background-color: $primary-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: $bg-dark-secondary;
|
||||||
|
color: $primary-color;
|
||||||
|
|
||||||
|
border-color: $primary-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: $primary-color;
|
||||||
|
border-color: $primary-color;
|
||||||
|
|
||||||
|
color: $text-primary;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $primary-color-dark;
|
||||||
|
border-color: $primary-color-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: $primary-color-dark;
|
||||||
|
border-color: $primary-color-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
$light-color: #eee;
|
||||||
|
$dark-color: #212121;
|
||||||
|
$primary-color: #7e57c2;
|
||||||
|
|
||||||
|
$dark-color-secondary: lighten($dark-color, 2%);
|
||||||
|
|
||||||
|
$primary-color-dark: darken($primary-color, 10%);
|
||||||
|
|
||||||
|
$bg-dark: $dark-color;
|
||||||
|
$bg-dark-secondary: $dark-color-secondary;
|
||||||
|
|
||||||
|
$text-primary: $light-color;
|
||||||
|
$text-secondary: darken($light-color, 10%);
|
@ -0,0 +1,6 @@
|
|||||||
|
@use "spectre.css/src/spectre.scss";
|
||||||
|
@use "spectre.css/src/spectre-icons.scss";
|
||||||
|
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
@import "dark";
|
@ -0,0 +1,21 @@
|
|||||||
|
import * as configcat from "configcat-node";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
const createConfigCatClient = () => {
|
||||||
|
if (!process.env.CONFIG_CAT_SDK_KEY) return;
|
||||||
|
|
||||||
|
const logger = configcat.createConsoleLogger(
|
||||||
|
process.env.NODE_ENV == "production" ? 0 : 3
|
||||||
|
);
|
||||||
|
|
||||||
|
const configCatClient = configcat.createClient(
|
||||||
|
process.env.CONFIG_CAT_SDK_KEY,
|
||||||
|
{
|
||||||
|
logger
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return configCatClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createConfigCatClient;
|
@ -0,0 +1,5 @@
|
|||||||
|
const multipleClassNames = (...classNames: string[]): string => {
|
||||||
|
return classNames.join(" ");
|
||||||
|
};
|
||||||
|
|
||||||
|
export default multipleClassNames;
|
@ -1,34 +0,0 @@
|
|||||||
import { BsTwitter, BsYoutube, BsMastodon, BsGithub } from "react-icons/bs";
|
|
||||||
import { SiKofi } from "react-icons/si";
|
|
||||||
|
|
||||||
const socials: { name: string; url: string; icon: JSX.Element }[] = [
|
|
||||||
{
|
|
||||||
name: "Twitter",
|
|
||||||
url: `https://twitter.com/${import.meta.env.VITE_TWITTER_USERNAME}`,
|
|
||||||
icon: <BsTwitter />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Youtube",
|
|
||||||
url: `https://youtube.com/channel/${
|
|
||||||
import.meta.env.VITE_YOUTUBE_CHANNEL_ID
|
|
||||||
}`,
|
|
||||||
icon: <BsYoutube />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Mastodon",
|
|
||||||
url: import.meta.env.VITE_MASTODON_URL,
|
|
||||||
icon: <BsMastodon />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ko-fi",
|
|
||||||
url: `https://ko-fi.com/${import.meta.env.VITE_KOFI_USERNAME}`,
|
|
||||||
icon: <SiKofi />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Github",
|
|
||||||
url: `https://github.com/${import.meta.env.VITE_GITHUB_USERNAME}`,
|
|
||||||
icon: <BsGithub />
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export default socials;
|
|
@ -1,33 +0,0 @@
|
|||||||
import { DefaultTheme } from "styled-components";
|
|
||||||
|
|
||||||
export const lightTheme: DefaultTheme = {
|
|
||||||
palette: {
|
|
||||||
background: {
|
|
||||||
primary: "#FBF8F1",
|
|
||||||
secondary: "#F7ECDE"
|
|
||||||
},
|
|
||||||
border: "#dcdcdc",
|
|
||||||
primary: "#E9DAC1",
|
|
||||||
secondary: "#54BAB9",
|
|
||||||
text: {
|
|
||||||
primary: "#111111",
|
|
||||||
secondary: "#4d4d4d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const darkTheme: DefaultTheme = {
|
|
||||||
palette: {
|
|
||||||
background: {
|
|
||||||
primary: "#000000",
|
|
||||||
secondary: "#1d1d1d"
|
|
||||||
},
|
|
||||||
border: "#454545",
|
|
||||||
primary: "#DC0067",
|
|
||||||
secondary: "#00dc75",
|
|
||||||
text: {
|
|
||||||
primary: "#eee",
|
|
||||||
secondary: "#ccc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,11 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly VITE_GITHUB_USERNAME: string;
|
|
||||||
readonly VITE_TWITTER_USERNAME: string;
|
|
||||||
readonly VITE_YOUTUBE_CHANNEL_ID: string;
|
|
||||||
readonly VITE_MASTODON_URL: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
readonly env: ImportMetaEnv;
|
|
||||||
}
|
|
@ -1,30 +1,29 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "es5",
|
||||||
"useDefineForClassFields": true,
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"allowJs": true,
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": false,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"module": "ESNext",
|
"noEmit": true,
|
||||||
"moduleResolution": "Node",
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
|
||||||
"paths": {
|
|
||||||
"@components/*": ["./src/components/*"],
|
|
||||||
"@interfaces/*": ["./src/interfaces/*"],
|
|
||||||
"@utils/*": ["./src/utils/*"],
|
|
||||||
"react": ["./node_modules/preact/compat/"],
|
|
||||||
"react-dom": ["./node_modules/preact/compat/"]
|
|
||||||
},
|
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxFactory": "h",
|
"incremental": true,
|
||||||
"jsxFragmentFactory": "Fragment"
|
"tsBuildInfoFile": ".next/tsbuildinfo.json",
|
||||||
|
"baseUrl": "src",
|
||||||
|
"paths": {
|
||||||
|
"@styles/*": ["styles/*"],
|
||||||
|
"@components/*": ["components/*"],
|
||||||
|
"@interfaces/*": ["interfaces/*"],
|
||||||
|
"@utils/*": ["utils/*"],
|
||||||
|
"@src/*": ["src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
import preact from "@preact/preset-vite";
|
|
||||||
import { defineConfig } from "vite";
|
|
||||||
import viteImagemin from "vite-plugin-imagemin";
|
|
||||||
import tsconfigPaths from "vite-tsconfig-paths";
|
|
||||||
|
|
||||||
import alias from "@rollup/plugin-alias";
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [
|
|
||||||
preact(),
|
|
||||||
alias({
|
|
||||||
entries: [
|
|
||||||
{ find: "react", replacement: "preact/compat" },
|
|
||||||
{ find: "react-dom/test-utils", replacement: "preact/test-utils" },
|
|
||||||
{ find: "react-dom", replacement: "preact/compat" },
|
|
||||||
{ find: "react/jsx-runtime", replacement: "preact/jsx-runtime" }
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
tsconfigPaths(),
|
|
||||||
viteImagemin({
|
|
||||||
optipng: {
|
|
||||||
optimizationLevel: 7
|
|
||||||
},
|
|
||||||
mozjpeg: {
|
|
||||||
quality: 20
|
|
||||||
},
|
|
||||||
pngquant: {
|
|
||||||
quality: [0.8, 0.9],
|
|
||||||
speed: 4
|
|
||||||
},
|
|
||||||
svgo: {
|
|
||||||
plugins: [
|
|
||||||
{
|
|
||||||
name: "removeViewBox"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "removeEmptyAttrs",
|
|
||||||
active: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
|
||||||
esbuild: {
|
|
||||||
logOverride: { "this-is-undefined-in-esm": "silent" }
|
|
||||||
}
|
|
||||||
});
|
|
Loading…
Reference in new issue