Configure & Run prettier

This commit is contained in:
Kevin Kandlbinder 2021-07-25 13:03:57 +00:00 committed by GitHub
parent 79ab0bb9af
commit 420f8930fd
66 changed files with 31825 additions and 31500 deletions

View file

@ -28,9 +28,7 @@
"EditorConfig.EditorConfig" "EditorConfig.EditorConfig"
], ],
// Use 'forwardPorts' to make a list of ports inside the container available locally. // Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [ "forwardPorts": [8000],
8000
],
// Use 'postCreateCommand' to run commands after the container is created. // Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install", "postCreateCommand": "npm install",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.

View file

@ -1,15 +1,12 @@
{ {
"parser": "babel-eslint", "parser": "babel-eslint",
"rules": { "rules": {
"strict": 0 "strict": 0
}, },
"extends": [ "extends": ["eslint:recommended", "plugin:react/recommended"],
"eslint:recommended", "settings": {
"plugin:react/recommended" "react": {
], "version": "detect"
"settings": { }
"react": {
"version": "detect"
} }
}
} }

View file

@ -1,30 +1,30 @@
name: Build Site name: Build Site
on: on:
push: push:
jobs: jobs:
update: update:
name: Build Site name: Build Site
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
name: Checkout Repo name: Checkout Repo
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: '12.x' node-version: "12.x"
- run: npm install - run: npm install
name: Install Dependencies name: Install Dependencies
- run: npm run build - run: npm run build
name: Build Site name: Build Site
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload Artifacts name: Upload Artifacts
with: with:
name: site name: site
path: public path: public

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
/.cache
/.vscode
/node_modules
/public

8
.prettierrc.json Normal file
View file

@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": false,
"jsxBracketSameLine": true,
"endOfLine": "lf"
}

View file

@ -1,18 +1,18 @@
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
module.exports = { module.exports = {
siteName: "KevinK.dev", siteName: "KevinK.dev",
siteAuthor: "@Unkn0wnKevin", siteAuthor: "@Unkn0wnKevin",
siteURL: "https://kevink.dev", siteURL: "https://kevink.dev",
payPalMail: "kevin@1in9.net", payPalMail: "kevin@1in9.net",
siteKeywords: siteKeywords:
"Kevin Kandlbinder, Kevin, Kandlbinder, Web, Web Developer, Developer, JavaScript, PHP, Java, Photos, Fotos", "Kevin Kandlbinder, Kevin, Kandlbinder, Web, Web Developer, Developer, JavaScript, PHP, Java, Photos, Fotos",
iconPath: "src/images/fullbglogo@10x.png", iconPath: "src/images/fullbglogo@10x.png",
languages: ["en", "de"], languages: ["en", "de"],
contactEmail: "kevin@kevink.dev", contactEmail: "kevin@kevink.dev",
contactPhone: "+4941068068004", contactPhone: "+4941068068004",
mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2", mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2",
contactTwitter: "Unkn0wnKevin", contactTwitter: "Unkn0wnKevin",
contactGitHub: "Unkn0wnCat", contactGitHub: "Unkn0wnCat",
contactMastodon: "@kevin@1in1.net", contactMastodon: "@kevin@1in1.net",
contactMastodonHref: "https://mastodon.1in1.net/@kevin", contactMastodonHref: "https://mastodon.1in1.net/@kevin",
}; };

View file

@ -2,106 +2,112 @@
const extConfig = require("./config"); const extConfig = require("./config");
module.exports = { module.exports = {
siteMetadata: { siteMetadata: {
title: extConfig.siteName, title: extConfig.siteName,
author: extConfig.siteAuthor, author: extConfig.siteAuthor,
siteUrl: extConfig.siteURL,
keywords: extConfig.siteKeywords,
payPalMail: extConfig.payPalMail,
contactEmail: extConfig.contactEmail,
contactPhone: extConfig.contactPhone,
mapsLink: extConfig.mapsLink,
contactTwitter: extConfig.contactTwitter,
contactGitHub: extConfig.contactGitHub,
contactMastodon: extConfig.contactMastodon,
contactMastodonHref: extConfig.contactMastodonHref,
},
assetPrefix: "/assets",
plugins: [
`gatsby-plugin-eslint`,
{
resolve: "gatsby-plugin-asset-path",
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/textblocks`,
name: `textblocks`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/projectTextblocks`,
name: `projectTextblocks`,
},
},
"gatsby-plugin-mdx",
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./content/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/locales`,
name: `locale`,
},
},
`gatsby-plugin-sass`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: extConfig.siteName,
short_name: extConfig.siteName,
start_url: `/`,
background_color: `#000710`,
theme_color: `#000710`,
display: `minimal-ui`,
icon: extConfig.iconPath, // This path is relative to the root of the site.
cache_busting_mode: "none",
},
},
`gatsby-plugin-robots-txt`,
{
resolve: `gatsby-plugin-offline`,
options: {
precachePages: ["/", "/en", "/en/projects", "/de", "/de/projects"],
workboxConfig: {
globPatterns: ["**/*"],
},
},
},
`gatsby-plugin-sitemap`,
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`,
languages: extConfig.languages,
defaultLanguage: `en`,
generateDefaultLanguagePage: true,
siteUrl: extConfig.siteURL, siteUrl: extConfig.siteURL,
i18nextOptions: { keywords: extConfig.siteKeywords,
interpolation: { payPalMail: extConfig.payPalMail,
escapeValue: false, // not needed for react as it escapes by default contactEmail: extConfig.contactEmail,
}, contactPhone: extConfig.contactPhone,
keySeparator: false, mapsLink: extConfig.mapsLink,
nsSeparator: false, contactTwitter: extConfig.contactTwitter,
}, contactGitHub: extConfig.contactGitHub,
pages: [ contactMastodon: extConfig.contactMastodon,
{ contactMastodonHref: extConfig.contactMastodonHref,
matchPath: "/:lang/projects/:urlname",
getLanguageFromPath: true,
excludeLanguages: extConfig.languages,
},
],
},
}, },
], assetPrefix: "/assets",
plugins: [
`gatsby-plugin-eslint`,
{
resolve: "gatsby-plugin-asset-path",
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/textblocks`,
name: `textblocks`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/projectTextblocks`,
name: `projectTextblocks`,
},
},
"gatsby-plugin-mdx",
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./content/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/locales`,
name: `locale`,
},
},
`gatsby-plugin-sass`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: extConfig.siteName,
short_name: extConfig.siteName,
start_url: `/`,
background_color: `#000710`,
theme_color: `#000710`,
display: `minimal-ui`,
icon: extConfig.iconPath, // This path is relative to the root of the site.
cache_busting_mode: "none",
},
},
`gatsby-plugin-robots-txt`,
{
resolve: `gatsby-plugin-offline`,
options: {
precachePages: [
"/",
"/en",
"/en/projects",
"/de",
"/de/projects",
],
workboxConfig: {
globPatterns: ["**/*"],
},
},
},
`gatsby-plugin-sitemap`,
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`,
languages: extConfig.languages,
defaultLanguage: `en`,
generateDefaultLanguagePage: true,
siteUrl: extConfig.siteURL,
i18nextOptions: {
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
keySeparator: false,
nsSeparator: false,
},
pages: [
{
matchPath: "/:lang/projects/:urlname",
getLanguageFromPath: true,
excludeLanguages: extConfig.languages,
},
],
},
},
],
}; };

View file

@ -3,172 +3,177 @@ const path = require(`path`);
const fs = require("fs"); const fs = require("fs");
exports.createPages = async ({ actions, graphql, reporter }) => { exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions; const { createPage } = actions;
const projectTemplate = path.resolve(`src/templates/project.js`); const projectTemplate = path.resolve(`src/templates/project.js`);
const result = await graphql(` const result = await graphql(`
query AllPagesQuery { query AllPagesQuery {
allProjectsJson { allProjectsJson {
nodes { nodes {
lang lang
urlname urlname
}
}
} }
} `);
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
} }
`);
if (result.errors) { result.data.allProjectsJson.nodes.forEach((node) => {
reporter.panicOnBuild(`Error while running GraphQL query.`); // eslint-disable-next-line no-undef
return; console.log(
} "Creating Page: ",
`/${node.lang}/projects/${node.urlname}`
);
result.data.allProjectsJson.nodes.forEach((node) => { if (node.lang !== "ignoreme")
// eslint-disable-next-line no-undef createPage({
console.log("Creating Page: ", `/${node.lang}/projects/${node.urlname}`); path: `/${node.lang}/projects/${node.urlname}`,
component: projectTemplate,
if (node.lang !== "ignoreme") context: {
createPage({ lang: node.lang,
path: `/${node.lang}/projects/${node.urlname}`, urlname: node.urlname,
component: projectTemplate, },
context: { });
lang: node.lang, });
urlname: node.urlname,
},
});
});
}; };
const config = require("./config.js"); const config = require("./config.js");
exports.onPostBuild = async ({ graphql, reporter }) => { exports.onPostBuild = async ({ graphql, reporter }) => {
console.log("Building static api..."); console.log("Building static api...");
const apiPrefix = "./public/api"; const apiPrefix = "./public/api";
if (!fs.existsSync(apiPrefix)) fs.mkdirSync(apiPrefix); if (!fs.existsSync(apiPrefix)) fs.mkdirSync(apiPrefix);
fs.writeFileSync(
`${apiPrefix}.json`,
JSON.stringify({
success: true,
endpoints: {
projects: [
{
name: "Projects Overview",
description: "Returns overview of all available projects",
path: "/api/projects.json",
},
{
name: "Projects Overview for Language",
description:
"Returns overview of all available projects in a specified language",
path: "/api/projects/:lang.json",
},
{
name: "Get specific Project",
description: "Returns specific project in specified language",
path: "/api/projects/:lang/:slug.json",
},
],
},
})
);
const projectsPrefix = apiPrefix + "/projects";
if (!fs.existsSync(projectsPrefix)) fs.mkdirSync(projectsPrefix);
await graphql(`
query {
allProjectsJson {
nodes {
urlname
shortDescription
name
links {
github
website
}
lang
image {
publicURL
}
featured
}
}
}
`).then((res) => {
if (res.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
let projects = res.data.allProjectsJson.nodes.filter((project) => {
return project.lang !== "ignoreme";
});
fs.writeFileSync( fs.writeFileSync(
`${projectsPrefix}.json`, `${apiPrefix}.json`,
JSON.stringify({ JSON.stringify({
success: true, success: true,
projects: projects.map((project) => { endpoints: {
return { projects: [
slug: project.urlname, {
lang: project.lang, name: "Projects Overview",
api: `/api/projects/${project.lang}/${project.urlname}.json`, description:
}; "Returns overview of all available projects",
}), path: "/api/projects.json",
}) },
{
name: "Projects Overview for Language",
description:
"Returns overview of all available projects in a specified language",
path: "/api/projects/:lang.json",
},
{
name: "Get specific Project",
description:
"Returns specific project in specified language",
path: "/api/projects/:lang/:slug.json",
},
],
},
})
); );
config.languages.forEach((lang) => { const projectsPrefix = apiPrefix + "/projects";
if (!fs.existsSync(`${projectsPrefix}/${lang}`))
fs.mkdirSync(`${projectsPrefix}/${lang}`);
fs.writeFileSync( if (!fs.existsSync(projectsPrefix)) fs.mkdirSync(projectsPrefix);
`${projectsPrefix}/${lang}.json`,
JSON.stringify({ await graphql(`
success: true, query {
projects: projects allProjectsJson {
.filter((project) => { nodes {
return project.lang == lang; urlname
shortDescription
name
links {
github
website
}
lang
image {
publicURL
}
featured
}
}
}
`).then((res) => {
if (res.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
let projects = res.data.allProjectsJson.nodes.filter((project) => {
return project.lang !== "ignoreme";
});
fs.writeFileSync(
`${projectsPrefix}.json`,
JSON.stringify({
success: true,
projects: projects.map((project) => {
return {
slug: project.urlname,
lang: project.lang,
api: `/api/projects/${project.lang}/${project.urlname}.json`,
};
}),
}) })
.map((project) => { );
return {
slug: project.urlname,
lang: project.lang,
api: `/api/projects/${project.lang}/${project.urlname}.json`,
};
}),
})
);
});
projects.forEach((project) => { config.languages.forEach((lang) => {
fs.writeFileSync( if (!fs.existsSync(`${projectsPrefix}/${lang}`))
`${projectsPrefix}/${project.lang}/${project.urlname}.json`, fs.mkdirSync(`${projectsPrefix}/${lang}`);
JSON.stringify({
success: true, fs.writeFileSync(
project: { `${projectsPrefix}/${lang}.json`,
slug: project.urlname, JSON.stringify({
lang: project.lang, success: true,
name: project.name, projects: projects
shortDescription: project.shortDescription, .filter((project) => {
longDescription: project.longDescription, return project.lang == lang;
links: })
project.links !== null .map((project) => {
? { return {
github: project.links.github, slug: project.urlname,
website: project.links.website, lang: project.lang,
} api: `/api/projects/${project.lang}/${project.urlname}.json`,
: null, };
image: project.image.publicURL, }),
featured: project.featured, })
frontend: `/${project.lang}/projects/${project.urlname}`, );
}, });
})
); projects.forEach((project) => {
fs.writeFileSync(
`${projectsPrefix}/${project.lang}/${project.urlname}.json`,
JSON.stringify({
success: true,
project: {
slug: project.urlname,
lang: project.lang,
name: project.name,
shortDescription: project.shortDescription,
longDescription: project.longDescription,
links:
project.links !== null
? {
github: project.links.github,
website: project.links.website,
}
: null,
image: project.image.publicURL,
featured: project.featured,
frontend: `/${project.lang}/projects/${project.urlname}`,
},
})
);
});
}); });
});
}; };

View file

@ -11,9 +11,7 @@
"develop": "gatsby develop", "develop": "gatsby develop",
"start": "gatsby develop", "start": "gatsby develop",
"build": "gatsby build --prefix-paths", "build": "gatsby build --prefix-paths",
"build:fab": "npm run build && npm run fab:build", "prettier": "npx prettier --write .",
"fab:build": "fab build",
"fab:serve": "fab serve fab.zip",
"serve": "gatsby serve", "serve": "gatsby serve",
"clean": "gatsby clean" "clean": "gatsby clean"
}, },
@ -63,6 +61,7 @@
"eslint-loader": "4.0.2", "eslint-loader": "4.0.2",
"eslint-plugin-import": "2.23.4", "eslint-plugin-import": "2.23.4",
"eslint-plugin-react": "7.24.0", "eslint-plugin-react": "7.24.0",
"gatsby-plugin-eslint": "2.0.8" "gatsby-plugin-eslint": "2.0.8",
"prettier": "2.3.2"
} }
} }

View file

@ -4,28 +4,28 @@ import { Link, Trans, useI18next } from "gatsby-plugin-react-i18next";
import * as styles from "./languageSwitcher.module.scss"; import * as styles from "./languageSwitcher.module.scss";
export default function LanguageSwitcher() { export default function LanguageSwitcher() {
const { languages, originalPath } = useI18next(); const { languages, originalPath } = useI18next();
return ( return (
<div className={styles.languageModal} id="languageChooser"> <div className={styles.languageModal} id="languageChooser">
<div className={styles.languageModalInner}> <div className={styles.languageModalInner}>
<h2> <h2>
Languages ( Languages (
<a href="#top" className={styles.modalCloseLink}> <a href="#top" className={styles.modalCloseLink}>
&times; &times;
</a> </a>
) )
</h2> </h2>
<ul> <ul>
{languages.map((lng) => ( {languages.map((lng) => (
<li key={lng}> <li key={lng}>
<Link to={originalPath} language={lng}> <Link to={originalPath} language={lng}>
<Trans>{lng}</Trans> <Trans>{lng}</Trans>
</Link> </Link>
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
</div> </div>
); );
} }

View file

@ -6,86 +6,87 @@ import { graphql, StaticQuery } from "gatsby";
import * as styles from "./navigation.module.scss"; import * as styles from "./navigation.module.scss";
const Navigation = ({ isHome }) => { const Navigation = ({ isHome }) => {
let [atTop, setAtTop] = useState(false); let [atTop, setAtTop] = useState(false);
const updateTransparency = () => { const updateTransparency = () => {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
if (window.scrollY < 15) { if (window.scrollY < 15) {
if (!atTop) setAtTop(true); if (!atTop) setAtTop(true);
} else { } else {
if (atTop) setAtTop(false); if (atTop) setAtTop(false);
} }
};
useEffect(() => {
if (typeof window === "undefined") return;
// eslint-disable-next-line no-undef
window.addEventListener("scroll", updateTransparency);
// eslint-disable-next-line no-undef
window.addEventListener("navigate", updateTransparency);
updateTransparency();
// eslint-disable-next-line no-undef
let int = window.setInterval(updateTransparency, 10000);
return () => {
// eslint-disable-next-line no-undef
window.removeEventListener("scroll", updateTransparency);
// eslint-disable-next-line no-undef
window.removeEventListener("navigate", updateTransparency);
// eslint-disable-next-line no-undef
window.clearInterval(int);
}; };
});
return ( useEffect(() => {
<div if (typeof window === "undefined") return;
className={
styles.topBar + // eslint-disable-next-line no-undef
(isHome ? " " + styles.homeBar : "") + window.addEventListener("scroll", updateTransparency);
(atTop ? " " + styles.homeBarTransparent : "") // eslint-disable-next-line no-undef
} window.addEventListener("navigate", updateTransparency);
>
<nav className={styles.topBarInner}> updateTransparency();
<StaticQuery
query={graphql` // eslint-disable-next-line no-undef
query { let int = window.setInterval(updateTransparency, 10000);
site {
siteMetadata { return () => {
title // eslint-disable-next-line no-undef
} window.removeEventListener("scroll", updateTransparency);
} // eslint-disable-next-line no-undef
} window.removeEventListener("navigate", updateTransparency);
`}
render={(data) => ( // eslint-disable-next-line no-undef
<Link to="/" activeClassName={styles.active}> window.clearInterval(int);
{data.site.siteMetadata.title} };
</Link> });
)}
/> return (
<div className="flexSpacer"></div> <div
<Link className={
id="navBtnProjects" styles.topBar +
to="/projects" (isHome ? " " + styles.homeBar : "") +
activeClassName={styles.active} (atTop ? " " + styles.homeBarTransparent : "")
> }>
<Trans>projects</Trans> <nav className={styles.topBarInner}>
</Link> <StaticQuery
<Link id="navBtnSocial" to="/social" activeClassName={styles.active}> query={graphql`
<Trans>social</Trans> query {
</Link> site {
</nav> siteMetadata {
</div> title
); }
}
}
`}
render={(data) => (
<Link to="/" activeClassName={styles.active}>
{data.site.siteMetadata.title}
</Link>
)}
/>
<div className="flexSpacer"></div>
<Link
id="navBtnProjects"
to="/projects"
activeClassName={styles.active}>
<Trans>projects</Trans>
</Link>
<Link
id="navBtnSocial"
to="/social"
activeClassName={styles.active}>
<Trans>social</Trans>
</Link>
</nav>
</div>
);
}; };
Navigation.propTypes = { Navigation.propTypes = {
isHome: PropTypes.bool.isRequired, isHome: PropTypes.bool.isRequired,
}; };
export default Navigation; export default Navigation;

View file

@ -5,89 +5,89 @@ import { useStaticQuery, graphql } from "gatsby";
import { useTranslation } from "gatsby-plugin-react-i18next"; import { useTranslation } from "gatsby-plugin-react-i18next";
function SEO({ description, meta, title }) { function SEO({ description, meta, title }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { site } = useStaticQuery( const { site } = useStaticQuery(
graphql` graphql`
query { query {
site { site {
siteMetadata { siteMetadata {
title title
author author
keywords keywords
} }
} }
} }
` `
); );
const metaDescription = description || t("siteDescription"); const metaDescription = description || t("siteDescription");
return ( return (
<Helmet <Helmet
title={title} title={title}
titleTemplate={`%s | ${site.siteMetadata.title}`} titleTemplate={`%s | ${site.siteMetadata.title}`}
meta={[ meta={[
{ {
name: `description`, name: `description`,
content: metaDescription, content: metaDescription,
}, },
{ {
property: `og:title`, property: `og:title`,
content: title, content: title,
}, },
{ {
property: `og:description`, property: `og:description`,
content: metaDescription, content: metaDescription,
}, },
{ {
property: `og:type`, property: `og:type`,
content: `website`, content: `website`,
}, },
{ {
name: `twitter:card`, name: `twitter:card`,
content: `summary`, content: `summary`,
}, },
{ {
name: `twitter:creator`, name: `twitter:creator`,
content: site.siteMetadata.author, content: site.siteMetadata.author,
}, },
{ {
name: `twitter:title`, name: `twitter:title`,
content: title, content: title,
}, },
{ {
name: `twitter:description`, name: `twitter:description`,
content: metaDescription, content: metaDescription,
}, },
{ {
name: "keywords", name: "keywords",
content: site.siteMetadata.keywords, content: site.siteMetadata.keywords,
}, },
].concat(meta)} ].concat(meta)}>
> <script
<script src="https://kit.fontawesome.com/1377f925e0.js"
src="https://kit.fontawesome.com/1377f925e0.js" crossOrigin="anonymous"></script>
crossOrigin="anonymous" <link rel="preconnect" href="https://fonts.gstatic.com" />
></script> <link
<link rel="preconnect" href="https://fonts.gstatic.com" /> href="https://fonts.googleapis.com/css2?family=Anonymous+Pro:wght@400;700&family=Roboto&display=swap"
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Anonymous+Pro:wght@400;700&family=Roboto&display=swap" />
rel="stylesheet" <meta
/> name="battery-savings"
<meta name="battery-savings" content="allow-reduced-framerate"></meta> content="allow-reduced-framerate"></meta>
</Helmet> </Helmet>
); );
} }
SEO.defaultProps = { SEO.defaultProps = {
meta: [], meta: [],
description: ``, description: ``,
}; };
SEO.propTypes = { SEO.propTypes = {
description: PropTypes.string, description: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object), meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
}; };
export default SEO; export default SEO;

View file

@ -8,55 +8,55 @@ import { Link, Trans } from "gatsby-plugin-react-i18next";
import LanguageSwitcher from "../components/languageSwitcher"; import LanguageSwitcher from "../components/languageSwitcher";
class Layout extends React.Component { class Layout extends React.Component {
render() { render() {
return ( return (
<> <>
<SEO <SEO
description={this.props.description} description={this.props.description}
lang={this.props.lang} lang={this.props.lang}
meta={this.props.meta} meta={this.props.meta}
title={this.props.title} title={this.props.title}
/> />
<Navigation isHome={this.props.transparentTopbar} /> <Navigation isHome={this.props.transparentTopbar} />
<div id="content" role="main"> <div id="content" role="main">
{this.props.children} {this.props.children}
</div> </div>
<footer role="contentinfo"> <footer role="contentinfo">
CC-BY 4.0 Kevin Kandlbinder,{" "} CC-BY 4.0 Kevin Kandlbinder,{" "}
<Link to="/legal/about" className="spf-link"> <Link to="/legal/about" className="spf-link">
<Trans i18nKey="imprint">Imprint</Trans> <Trans i18nKey="imprint">Imprint</Trans>
</Link>{" "} </Link>{" "}
|{" "} |{" "}
<Link to="/legal/datasec" className="spf-link"> <Link to="/legal/datasec" className="spf-link">
<Trans i18nKey="datasec">Data Protection</Trans> <Trans i18nKey="datasec">Data Protection</Trans>
</Link>{" "} </Link>{" "}
|{" "} |{" "}
<Link to="/legal/disclaimer" className="spf-link"> <Link to="/legal/disclaimer" className="spf-link">
<Trans i18nKey="disclaimer">Disclaimer</Trans> <Trans i18nKey="disclaimer">Disclaimer</Trans>
</Link>{" "} </Link>{" "}
| <a href="#languageChooser">Language</a> | <a href="#languageChooser">Language</a>
</footer> </footer>
<LanguageSwitcher /> <LanguageSwitcher />
</> </>
); );
} }
} }
Layout.defaultProps = { Layout.defaultProps = {
module: `none`, module: `none`,
meta: [], meta: [],
description: ``, description: ``,
transparentTopbar: false, transparentTopbar: false,
}; };
Layout.propTypes = { Layout.propTypes = {
description: PropTypes.string, description: PropTypes.string,
lang: PropTypes.string, lang: PropTypes.string,
meta: PropTypes.arrayOf(PropTypes.object), meta: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string.isRequired, title: PropTypes.string.isRequired,
transparentTopbar: PropTypes.bool, transparentTopbar: PropTypes.bool,
children: PropTypes.any.isRequired, children: PropTypes.any.isRequired,
}; };
export default Layout; export default Layout;

View file

@ -3,19 +3,19 @@ import { Link } from "gatsby-plugin-react-i18next";
import Layout from "../layouts/default"; import Layout from "../layouts/default";
const NotFoundPage = () => { const NotFoundPage = () => {
return ( return (
<Layout title="Not found"> <Layout title="Not found">
<section> <section>
<article> <article>
<h1>Page not found</h1> <h1>Page not found</h1>
<p> <p>
Whoops... That page doesn&apos;t exist, so you may as well{" "} Whoops... That page doesn&apos;t exist, so you may as
<Link to="/">go home</Link>. well <Link to="/">go home</Link>.
</p> </p>
</article> </article>
</section> </section>
</Layout> </Layout>
); );
}; };
export default NotFoundPage; export default NotFoundPage;

View file

@ -8,128 +8,135 @@ import GitHubButton from "react-github-btn";
import * as styles from "./donate.module.scss"; import * as styles from "./donate.module.scss";
export const query = graphql` export const query = graphql`
query($language: String!) { query ($language: String!) {
site { site {
siteMetadata { siteMetadata {
title title
siteUrl siteUrl
payPalMail payPalMail
contactGitHub contactGitHub
} }
}
file(relativePath: { eq: "images/pplogo.png" }) {
childImageSharp {
resize(width: 240, height: 240, fit: CONTAIN) {
src
} }
} file(relativePath: { eq: "images/pplogo.png" }) {
} childImageSharp {
locales: allLocale(filter: { language: { eq: $language } }) { resize(width: 240, height: 240, fit: CONTAIN) {
edges { src
node { }
ns }
data }
language locales: allLocale(filter: { language: { eq: $language } }) {
edges {
node {
ns
data
language
}
}
} }
}
} }
}
`; `;
const DonatePage = (props) => { const DonatePage = (props) => {
const [amount, setAmount] = useState(5); const [amount, setAmount] = useState(5);
const { t } = useI18next(); const { t } = useI18next();
const { path } = React.useContext(I18nextContext); const { path } = React.useContext(I18nextContext);
const { site, file } = props.data; const { site, file } = props.data;
return ( return (
<Layout title={t("donate")} description={t("donationCatchphrase")}> <Layout title={t("donate")} description={t("donationCatchphrase")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>donate</Trans> <Trans>donate</Trans>
</h1> </h1>
<p> <p>
<Trans>donateDescription</Trans> <Trans>donateDescription</Trans>
</p> </p>
<p> <p>
<Trans>donateGitHub</Trans> <Trans>donateGitHub</Trans>
</p> </p>
<p style={{ display: "block", textAlign: "center" }}> <p style={{ display: "block", textAlign: "center" }}>
<GitHubButton <GitHubButton
href={ href={
"https://github.com/sponsors/" + site.siteMetadata.contactGitHub "https://github.com/sponsors/" +
} site.siteMetadata.contactGitHub
data-color-scheme="no-preference: light; light: dark; dark: dark;" }
data-icon="octicon-heart" data-color-scheme="no-preference: light; light: dark; dark: dark;"
data-size="large" data-icon="octicon-heart"
aria-label="Sponsor @Unkn0wnCat on GitHub" data-size="large"
> aria-label="Sponsor @Unkn0wnCat on GitHub">
<Trans>sponsorGitHub</Trans> <Trans>sponsorGitHub</Trans>
</GitHubButton> </GitHubButton>
</p> </p>
<p> <p>
<Trans>donatePayPal</Trans> <Trans>donatePayPal</Trans>
</p> </p>
<div className={styles.priceAmount}> <div className={styles.priceAmount}>
<label htmlFor="priceInput" className={styles.sronly}> <label htmlFor="priceInput" className={styles.sronly}>
Amount Amount
</label> </label>
<input <input
type="number" type="number"
min="1" min="1"
placeholder="10.00" placeholder="10.00"
step="1" step="1"
value={amount} value={amount}
onChange={(ev) => { onChange={(ev) => {
setAmount(ev.target.value); setAmount(ev.target.value);
}} }}
name="priceInput" name="priceInput"
id="priceInput" id="priceInput"
/> />
<div></div> <div></div>
</div> </div>
<a <a
className={styles.donateButton} className={styles.donateButton}
rel="noopener" rel="noopener"
id="payPalBtn" id="payPalBtn"
href={ href={
"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=" + "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=" +
encodeURIComponent(site.siteMetadata.payPalMail) + encodeURIComponent(site.siteMetadata.payPalMail) +
"&item_name=" + "&item_name=" +
encodeURIComponent(site.siteMetadata.title) + encodeURIComponent(site.siteMetadata.title) +
"&currency_code=EUR&image_url=" + "&currency_code=EUR&image_url=" +
encodeURIComponent( encodeURIComponent(
site.siteMetadata.siteUrl + file.childImageSharp.resize.src site.siteMetadata.siteUrl +
) + file.childImageSharp.resize.src
"&return=" + ) +
encodeURIComponent( "&return=" +
site.siteMetadata.siteUrl + "/" + path + "thank-you/" encodeURIComponent(
) + site.siteMetadata.siteUrl +
"&rm=0&cancel_return=" + "/" +
encodeURIComponent(site.siteMetadata.siteUrl + "/" + path) + path +
"&amount=" + "thank-you/"
amount ) +
} "&rm=0&cancel_return=" +
> encodeURIComponent(
<span>Donate using PayPal</span> site.siteMetadata.siteUrl + "/" + path
<i className="fas fa-fw fa-chevron-right" aria-hidden="true"></i> ) +
</a> "&amount=" +
</article> amount
</section> }>
</Layout> <span>Donate using PayPal</span>
); <i
className="fas fa-fw fa-chevron-right"
aria-hidden="true"></i>
</a>
</article>
</section>
</Layout>
);
}; };
DonatePage.propTypes = { DonatePage.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default DonatePage; export default DonatePage;

View file

@ -5,51 +5,55 @@ import { graphql } from "gatsby";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
export const query = graphql` export const query = graphql`
query GetThankYouPage($language: String!) { query GetThankYouPage($language: String!) {
site { site {
siteMetadata { siteMetadata {
contactEmail contactEmail
} }
} }
locales: allLocale(filter: { language: { eq: $language } }) { locales: allLocale(filter: { language: { eq: $language } }) {
edges { edges {
node { node {
ns ns
data data
language language
}
}
} }
}
} }
}
`; `;
const ThankYouPage = (props) => { const ThankYouPage = (props) => {
const { site } = props.data; const { site } = props.data;
let contactEmail = site.siteMetadata.contactEmail; let contactEmail = site.siteMetadata.contactEmail;
const { t } = useI18next(); const { t } = useI18next();
return ( return (
<Layout title={t("donate")}> <Layout title={t("donate")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>donateThanks</Trans> <Trans>donateThanks</Trans>
</h1> </h1>
<p> <p>
<Trans contactEmail={contactEmail} i18nKey="donateThanksText"> <Trans
donateThanksText contactEmail={contactEmail}
<a href={"mailto:" + contactEmail}>{{ contactEmail }}</a> i18nKey="donateThanksText">
</Trans> donateThanksText
</p> <a href={"mailto:" + contactEmail}>
</article> {{ contactEmail }}
</section> </a>
</Layout> </Trans>
); </p>
</article>
</section>
</Layout>
);
}; };
ThankYouPage.propTypes = { ThankYouPage.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default ThankYouPage; export default ThankYouPage;

View file

@ -7,31 +7,31 @@ import PropTypes from "prop-types";
import * as styles from "./friends.module.scss"; import * as styles from "./friends.module.scss";
export const query = graphql` export const query = graphql`
query AllFriendsQuery($language: String!) { query AllFriendsQuery($language: String!) {
allFriendsJson { allFriendsJson {
nodes { nodes {
name name
profession profession
url url
imageURL imageURL
} }
} }
locales: allLocale(filter: { language: { eq: $language } }) { locales: allLocale(filter: { language: { eq: $language } }) {
edges { edges {
node { node {
ns ns
data data
language language
}
}
} }
}
} }
}
`; `;
const FriendsPage = ({ data }) => { const FriendsPage = ({ data }) => {
const { t } = useI18next(); const { t } = useI18next();
/*function shuffle(a) { /*function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) { for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1)); const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]]; [a[i], a[j]] = [a[j], a[i]];
@ -39,59 +39,102 @@ const FriendsPage = ({ data }) => {
return a; return a;
}*/ }*/
return ( return (
<Layout title={t("friends")} description={t("friendsDescription")}> <Layout title={t("friends")} description={t("friendsDescription")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>social</Trans> <Trans>social</Trans>
</h1> </h1>
<p> <p>
<Trans>friendsDescription</Trans> <Trans>friendsDescription</Trans>
</p> </p>
<div className={styles.friendsList}> <div className={styles.friendsList}>
{/*shuffle(*/data.allFriendsJson.nodes/*)*/.map((friend) => { {
return ( /*shuffle(*/ data.allFriendsJson.nodes /*)*/
<div .map((friend) => {
className={styles.friendProfile} return (
key={friend.url + "#" + friend.name} <div
> className={styles.friendProfile}
<div key={
className={styles.friendImage} friend.url + "#" + friend.name
style={{ backgroundImage: "url(" + friend.imageURL + ")" }} }>
key={friend.url + "#" + friend.name + "#image"} <div
> className={styles.friendImage}
<span className={styles.friendName} key={friend.url + "#" + friend.name + "#name"}>{friend.name}</span> style={{
<span className={styles.friendTitle} key={friend.url + "#" + friend.name + "#profession"}> backgroundImage:
{friend.profession} "url(" +
</span> friend.imageURL +
</div> ")",
}}
key={
friend.url +
"#" +
friend.name +
"#image"
}>
<span
className={
styles.friendName
}
key={
friend.url +
"#" +
friend.name +
"#name"
}>
{friend.name}
</span>
<span
className={
styles.friendTitle
}
key={
friend.url +
"#" +
friend.name +
"#profession"
}>
{friend.profession}
</span>
</div>
<div className={styles.contactLinks} key={friend.url + "#" + friend.name + "#links"}> <div
<a className={styles.contactLinks}
className={styles.contactLink} key={
href={friend.url} friend.url +
target="_blank" "#" +
rel="noreferrer" friend.name +
> "#links"
<i className="fas fa-globe-europe" aria-hidden="true"></i>{" "} }>
{friend.url} <a
</a> className={
</div> styles.contactLink
</div> }
); href={friend.url}
})} target="_blank"
</div> rel="noreferrer">
</article> <i
</section> className="fas fa-globe-europe"
</Layout> aria-hidden="true"></i>{" "}
); {friend.url}
</a>
</div>
</div>
);
})
}
</div>
</article>
</section>
</Layout>
);
}; };
FriendsPage.propTypes = { FriendsPage.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default FriendsPage; export default FriendsPage;

View file

@ -12,244 +12,253 @@ import { MDXRenderer } from "gatsby-plugin-mdx";
import anime from "animejs"; import anime from "animejs";
import { tsParticles } from "tsparticles"; import { tsParticles } from "tsparticles";
import * as particleConfig from "./index.particles.json"; import * as particleConfig from "./index.particles.json";
export const query = graphql` export const query = graphql`
query GetMetaAndProjects($language: String) { query GetMetaAndProjects($language: String) {
site { site {
siteMetadata { siteMetadata {
contactEmail contactEmail
contactPhone contactPhone
mapsLink mapsLink
contactTwitter contactTwitter
contactGitHub contactGitHub
contactMastodon contactMastodon
contactMastodonHref contactMastodonHref
}
}
allProjectsJson(
filter: { lang: { eq: $language }, featured: { gte: 0 } }
sort: { fields: featured, order: ASC }
) {
nodes {
lang
urlname
name
image {
childImageSharp {
resize(width: 400, quality: 90) {
src
} }
}
} }
shortDescription allProjectsJson(
featured filter: { lang: { eq: $language }, featured: { gte: 0 } }
} sort: { fields: featured, order: ASC }
} ) {
locales: allLocale(filter: { language: { eq: $language } }) { nodes {
edges { lang
node { urlname
ns name
data image {
language childImageSharp {
resize(width: 400, quality: 90) {
src
}
}
}
shortDescription
featured
}
}
locales: allLocale(filter: { language: { eq: $language } }) {
edges {
node {
ns
data
language
}
}
}
file(
sourceInstanceName: { eq: "textblocks" }
relativeDirectory: { eq: "home/about" }
name: { eq: $language }
) {
id
childMdx {
body
}
name
} }
}
} }
file(
sourceInstanceName: {eq: "textblocks"}, relativeDirectory: {eq: "home/about"}, name: {eq: $language}
) {
id
childMdx {
body
}
name
}
}
`; `;
const IndexPage = (props) => { const IndexPage = (props) => {
React.useEffect(() => { React.useEffect(() => {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
anime({ anime({
targets: [ targets: [
"." + styles.profileCard + " > span", "." + styles.profileCard + " > span",
"." + styles.profileCard + " a", "." + styles.profileCard + " a",
], ],
opacity: [0, 1], opacity: [0, 1],
translateX: [100, 0], translateX: [100, 0],
duration: 250, duration: 250,
delay: anime.stagger(20), delay: anime.stagger(20),
easing: "easeInOutCirc", easing: "easeInOutCirc",
}); });
anime({ anime({
targets: ["." + styles.profileImageDummy], targets: ["." + styles.profileImageDummy],
translateX: [0, -3], translateX: [0, -3],
translateY: [0, 3], translateY: [0, 3],
duration: 250, duration: 250,
easing: "easeInOutCirc", easing: "easeInOutCirc",
}); });
anime({ anime({
targets: ["." + styles.profileImage], targets: ["." + styles.profileImage],
translateX: [0, 4], translateX: [0, 4],
translateY: [0, -4], translateY: [0, -4],
duration: 250, duration: 250,
easing: "easeInOutCirc", easing: "easeInOutCirc",
}); });
tsParticles.load("particle-container", particleConfig); tsParticles.load("particle-container", particleConfig);
}, []); }, []);
let meta = props.data.site.siteMetadata; let meta = props.data.site.siteMetadata;
let file = props.data.file; let file = props.data.file;
return ( return (
<Layout title="Kevin Kandlbinder" transparentTopbar={true}> <Layout title="Kevin Kandlbinder" transparentTopbar={true}>
<section className={styles.heroSection}> <section className={styles.heroSection}>
<div className={styles.heroSectionBg} id="particle-container"></div> <div
<div className={styles.heroSectionBgOver}></div> className={styles.heroSectionBg}
<div className={styles.profile + " profile"}> id="particle-container"></div>
<div <div className={styles.heroSectionBgOver}></div>
data-bg="url(https://cdn.kevink.dev/images/kevin/kevin-kandlbinder-03.jpg)" <div className={styles.profile + " profile"}>
style={{ <div
backgroundImage: data-bg="url(https://cdn.kevink.dev/images/kevin/kevin-kandlbinder-03.jpg)"
"url(https://cdn.kevink.dev/images/kevin/kevin-kandlbinder-03.jpg)", style={{
}} backgroundImage:
className={styles.profileImage + " lazy"} "url(https://cdn.kevink.dev/images/kevin/kevin-kandlbinder-03.jpg)",
></div> }}
<div className={styles.profileImageDummy}></div> className={styles.profileImage + " lazy"}></div>
<div className={styles.profileCard}> <div className={styles.profileImageDummy}></div>
<span className={styles.hello}> <div className={styles.profileCard}>
<Trans>homeHello</Trans> <span className={styles.hello}>
</span> <Trans>homeHello</Trans>
<span className={styles.name}>Kevin Kandlbinder</span> </span>
<span className={styles.description}> <span className={styles.name}>Kevin Kandlbinder</span>
<Trans>homeMe</Trans>{" "} <span className={styles.description}>
<span id="descriptionType"> <Trans>homeMe</Trans>{" "}
<Trans>homeWebDeveloper</Trans> <span id="descriptionType">
</span> <Trans>homeWebDeveloper</Trans>
. </span>
</span> .
</span>
<div className={styles.contactLinks}> <div className={styles.contactLinks}>
<a <a
className={styles.contactLink} className={styles.contactLink}
href={"tel:" + meta.contactPhone} href={"tel:" + meta.contactPhone}
rel="me" rel="me">
> <i className="fas fa-fw fa-phone"></i>
<i className="fas fa-fw fa-phone"></i> {meta.contactPhone}
{meta.contactPhone} </a>
</a> <a
<a className={styles.contactLink}
className={styles.contactLink} href={"mailto:" + meta.contactEmail}
href={"mailto:" + meta.contactEmail} rel="me">
rel="me" <i className="far fa-fw fa-envelope"></i>
> {meta.contactEmail}
<i className="far fa-fw fa-envelope"></i> </a>
{meta.contactEmail} <a
</a> className={styles.contactLink}
<a href={meta.mapsLink}
className={styles.contactLink} rel="noreferrer "
href={meta.mapsLink} target="_blank">
rel="noreferrer " <i className="fas fa-fw fa-map-marker-alt"></i>
target="_blank" <Trans>homeMyLocation</Trans>
> </a>
<i className="fas fa-fw fa-map-marker-alt"></i> <a
<Trans>homeMyLocation</Trans> className={styles.contactLink}
</a> href={meta.contactMastodonHref}
<a rel="noreferrer me"
className={styles.contactLink} target="_blank">
href={meta.contactMastodonHref} <i className="fab fa-fw fa-mastodon"></i>
rel="noreferrer me" {meta.contactMastodon}
target="_blank" </a>
> <a
<i className="fab fa-fw fa-mastodon"></i> className={styles.contactLink}
{meta.contactMastodon} href={
</a> "https://github.com/" + meta.contactGitHub
<a }
className={styles.contactLink} rel="noreferrer me"
href={"https://github.com/" + meta.contactGitHub} target="_blank">
rel="noreferrer me" <i className="fab fa-fw fa-github"></i>
target="_blank" {meta.contactGitHub}
> </a>
<i className="fab fa-fw fa-github"></i> </div>
{meta.contactGitHub}
</a>
</div>
</div>
</div>
</section>
<section className="aboutSection">
<article>
<MDXRenderer>{file.childMdx.body}</MDXRenderer>
</article>
</section>
<a
className={styles.creditSection}
href="https://unsplash.com/@jannikkiel"
target="_blank"
rel="noreferrer"
>
<div>
<span>
<i className="fas fa-fw fa-camera"></i>{" "}
<Trans>homeImageCredit</Trans>
</span>
<i className="fas fa-fw fa-chevron-right"></i>
</div>
</a>
<section className="featuredSection">
<article>
<h1>
<Trans>featuredProjects</Trans>
</h1>
<div className={projectStyles.projectList}>
{props.data.allProjectsJson.nodes.map((project) => {
return (
<Link
className={projectStyles.projectCard}
key={project.lang + "/" + project.urlname}
to={"/projects/" + project.urlname}
>
<div
className={projectStyles.projectCardImage}
style={{
backgroundImage:
"url(" + project.image.childImageSharp.resize.src + ")",
}}
>
<div className={projectStyles.projectCardMeta}>
<span className={projectStyles.projectCardTitle}>
{project.name}
</span>
<span>{project.shortDescription}</span>
</div> </div>
</div> </div>
</Link> </section>
); <section className="aboutSection">
})} <article>
</div> <MDXRenderer>{file.childMdx.body}</MDXRenderer>
<Link to="/projects" className={styles.seeMoreButton}> </article>
<Trans>seeMore</Trans>{" "} </section>
<i className="fas fa-fw fa-chevron-right"></i> <a
</Link> className={styles.creditSection}
</article> href="https://unsplash.com/@jannikkiel"
</section> target="_blank"
<Link className={styles.donationSection} to="/donate"> rel="noreferrer">
<div> <div>
<span> <span>
<Trans>donationCatchphrase</Trans> <i className="fas fa-fw fa-camera"></i>{" "}
</span> <Trans>homeImageCredit</Trans>
<i className="fas fa-fw fa-chevron-right"></i> </span>
</div> <i className="fas fa-fw fa-chevron-right"></i>
</Link> </div>
</Layout> </a>
); <section className="featuredSection">
<article>
<h1>
<Trans>featuredProjects</Trans>
</h1>
<div className={projectStyles.projectList}>
{props.data.allProjectsJson.nodes.map((project) => {
return (
<Link
className={projectStyles.projectCard}
key={project.lang + "/" + project.urlname}
to={"/projects/" + project.urlname}>
<div
className={
projectStyles.projectCardImage
}
style={{
backgroundImage:
"url(" +
project.image.childImageSharp
.resize.src +
")",
}}>
<div
className={
projectStyles.projectCardMeta
}>
<span
className={
projectStyles.projectCardTitle
}>
{project.name}
</span>
<span>
{project.shortDescription}
</span>
</div>
</div>
</Link>
);
})}
</div>
<Link to="/projects" className={styles.seeMoreButton}>
<Trans>seeMore</Trans>{" "}
<i className="fas fa-fw fa-chevron-right"></i>
</Link>
</article>
</section>
<Link className={styles.donationSection} to="/donate">
<div>
<span>
<Trans>donationCatchphrase</Trans>
</span>
<i className="fas fa-fw fa-chevron-right"></i>
</div>
</Link>
</Layout>
);
}; };
IndexPage.propTypes = { IndexPage.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default IndexPage; export default IndexPage;

View file

@ -6,7 +6,8 @@
height: 600px; height: 600px;
overflow: hidden; overflow: hidden;
.heroSectionBg, .heroSectionBgOver { .heroSectionBg,
.heroSectionBgOver {
position: absolute; position: absolute;
width: 100%; width: 100%;
max-width: unset; max-width: unset;
@ -22,7 +23,20 @@
/*background: radial-gradient(ellipse at top left, #1f0ba659, transparent), /*background: radial-gradient(ellipse at top left, #1f0ba659, transparent),
radial-gradient(ellipse at bottom right, #4a086829, transparent);*/ radial-gradient(ellipse at bottom right, #4a086829, transparent);*/
background: linear-gradient(45deg, #000850 0%, #000320 100%), radial-gradient(100% 225% at 100% 0%, #FF6928 0%, #000000 100%), linear-gradient(225deg, #FF7A00 0%, #000000 100%), linear-gradient(135deg, #CDFFEB 10%, #CDFFEB 35%, #009F9D 35%, #009F9D 60%, #07456F 60%, #07456F 67%, #0F0A3C 67%, #0F0A3C 100%); background: linear-gradient(45deg, #000850 0%, #000320 100%),
radial-gradient(100% 225% at 100% 0%, #ff6928 0%, #000000 100%),
linear-gradient(225deg, #ff7a00 0%, #000000 100%),
linear-gradient(
135deg,
#cdffeb 10%,
#cdffeb 35%,
#009f9d 35%,
#009f9d 60%,
#07456f 60%,
#07456f 67%,
#0f0a3c 67%,
#0f0a3c 100%
);
background-blend-mode: screen, overlay, hard-light, normal; background-blend-mode: screen, overlay, hard-light, normal;
} }

View file

@ -1,107 +1,107 @@
{ {
"particles": { "particles": {
"number": { "number": {
"value": 33, "value": 33,
"density": { "density": {
"enable": true, "enable": true,
"value_area": 800 "value_area": 800
} }
}, },
"color": { "color": {
"value": "#1b1e34" "value": "#1b1e34"
}, },
"shape": { "shape": {
"type": "circle", "type": "circle",
"stroke": { "stroke": {
"width": 0, "width": 0,
"color": "#000" "color": "#000"
}, },
"polygon": { "polygon": {
"nb_sides": 3 "nb_sides": 3
} }
}, },
"opacity": { "opacity": {
"value": 0.4, "value": 0.4,
"random": true, "random": true,
"anim": { "anim": {
"enable": false, "enable": false,
"speed": 1, "speed": 1,
"opacity_min": 0.1, "opacity_min": 0.1,
"sync": false "sync": false
} }
}, },
"size": { "size": {
"value": 27.620603391810075, "value": 27.620603391810075,
"random": true, "random": true,
"anim": { "anim": {
"enable": false, "enable": false,
"speed": 10, "speed": 10,
"size_min": 40, "size_min": 40,
"sync": false "sync": false
} }
}, },
"line_linked": {
"enable": false,
"distance": 200,
"color": "#ffffff",
"opacity": 0.14994041841268327,
"width": 2
},
"move": {
"enable": true,
"speed": 0.2,
"direction": "top",
"random": true,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "window",
"events": {
"onhover": {
"enable": false,
"mode": "repulse"
},
"onclick": {
"enable": false,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 400,
"line_linked": { "line_linked": {
"opacity": 1 "enable": false,
"distance": 200,
"color": "#ffffff",
"opacity": 0.14994041841268327,
"width": 2
},
"move": {
"enable": true,
"speed": 0.2,
"direction": "top",
"random": true,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
} }
}, },
"bubble": { "interactivity": {
"distance": 400, "detect_on": "window",
"size": 40, "events": {
"duration": 2, "onhover": {
"opacity": 8, "enable": false,
"speed": 3 "mode": "repulse"
}, },
"repulse": { "onclick": {
"distance": 200, "enable": false,
"duration": 0.4 "mode": "push"
}, },
"push": { "resize": true
"particles_nb": 4 },
}, "modes": {
"remove": { "grab": {
"particles_nb": 2 "distance": 400,
} "line_linked": {
} "opacity": 1
}, }
"detectRetina": true, },
"pauseOnBlur": true, "bubble": {
"pauseOnOutsideViewport": true "distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"detectRetina": true,
"pauseOnBlur": true,
"pauseOnOutsideViewport": true
} }

View file

@ -4,53 +4,56 @@ import { Trans, useI18next } from "gatsby-plugin-react-i18next";
import { graphql } from "gatsby"; import { graphql } from "gatsby";
export const query = graphql` export const query = graphql`
query($language: String!) { query ($language: String!) {
locales: allLocale(filter: { language: { eq: $language } }) { locales: allLocale(filter: { language: { eq: $language } }) {
edges { edges {
node { node {
ns ns
data data
language language
}
}
} }
}
} }
}
`; `;
export default function ImprintPage() { export default function ImprintPage() {
const { t } = useI18next(); const { t } = useI18next();
return ( return (
<Layout title={t("imprint")}> <Layout title={t("imprint")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>imprint</Trans> <Trans>imprint</Trans>
</h1> </h1>
<p>Angaben gemäß § 5 TMG</p> <p>Angaben gemäß § 5 TMG</p>
<p> <p>
Kevin Kandlbinder Kevin Kandlbinder
<br /> <br />
Eichenweg 48 Eichenweg 48
<br /> <br />
25451 Quickborn <br /> 25451 Quickborn <br />
</p> </p>
<p> <p>
{" "} {" "}
<strong>Vertreten durch: </strong> <strong>Vertreten durch: </strong>
<br /> <br />
Kevin Kandlbinder Kevin Kandlbinder
<br /> <br />
</p> </p>
<p> <p>
<strong>Kontakt:</strong> <br /> <strong>Kontakt:</strong> <br />
Telefon: +49 4106 8068004 Telefon: +49 4106 8068004
<br /> <br />
E-Mail: <a href="mailto:contact@kevink.dev">contact@kevink.dev</a> E-Mail:{" "}
<br /> <a href="mailto:contact@kevink.dev">
</p> contact@kevink.dev
</article> </a>
</section> <br />
</Layout> </p>
); </article>
</section>
</Layout>
);
} }

File diff suppressed because it is too large Load diff

View file

@ -4,91 +4,98 @@ import { Trans, useI18next } from "gatsby-plugin-react-i18next";
import { graphql } from "gatsby"; import { graphql } from "gatsby";
export const query = graphql` export const query = graphql`
query($language: String!) { query ($language: String!) {
locales: allLocale(filter: { language: { eq: $language } }) { locales: allLocale(filter: { language: { eq: $language } }) {
edges { edges {
node { node {
ns ns
data data
language language
}
}
} }
}
} }
}
`; `;
export default function DisclaimerPage() { export default function DisclaimerPage() {
const { t } = useI18next(); const { t } = useI18next();
return ( return (
<Layout title={t("disclaimer")}> <Layout title={t("disclaimer")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>disclaimer</Trans> <Trans>disclaimer</Trans>
</h1> </h1>
<h2>Haftung für Inhalte</h2> <h2>Haftung für Inhalte</h2>
<p> <p>
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für
auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach eigene Inhalte auf diesen Seiten nach den allgemeinen
§§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir
verpflichtet, übermittelte oder gespeicherte fremde Informationen zu als Diensteanbieter jedoch nicht verpflichtet,
überwachen oder nach Umständen zu forschen, die auf eine übermittelte oder gespeicherte fremde Informationen zu
rechtswidrige Tätigkeit hinweisen. überwachen oder nach Umständen zu forschen, die auf eine
</p> rechtswidrige Tätigkeit hinweisen.
</p>
<p> <p>
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Verpflichtungen zur Entfernung oder Sperrung der Nutzung
Informationen nach den allgemeinen Gesetzen bleiben hiervon von Informationen nach den allgemeinen Gesetzen bleiben
unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem hiervon unberührt. Eine diesbezügliche Haftung ist
Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei jedoch erst ab dem Zeitpunkt der Kenntnis einer
Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese konkreten Rechtsverletzung möglich. Bei Bekanntwerden
Inhalte umgehend entfernen. von entsprechenden Rechtsverletzungen werden wir diese
</p> Inhalte umgehend entfernen.
</p>
<h2>Haftung für Links</h2> <h2>Haftung für Links</h2>
<p> <p>
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Unser Angebot enthält Links zu externen Websites
Inhalte wir keinen Einfluss haben. Deshalb können wir für diese Dritter, auf deren Inhalte wir keinen Einfluss haben.
fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der Deshalb können wir für diese fremden Inhalte auch keine
verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber Gewähr übernehmen. Für die Inhalte der verlinkten Seiten
der Seiten verantwortlich. Die verlinkten Seiten wurden zum ist stets der jeweilige Anbieter oder Betreiber der
Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Seiten verantwortlich. Die verlinkten Seiten wurden zum
Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße
erkennbar. überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der
</p> Verlinkung nicht erkennbar.
</p>
<p> <p>
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist Eine permanente inhaltliche Kontrolle der verlinkten
jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht Seiten ist jedoch ohne konkrete Anhaltspunkte einer
zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von
derartige Links umgehend entfernen. Rechtsverletzungen werden wir derartige Links umgehend
</p> entfernen.
</p>
<h2>Urheberrecht</h2> <h2>Urheberrecht</h2>
<p> <p>
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf Die durch die Seitenbetreiber erstellten Inhalte und
diesen Seiten unterliegen dem deutschen Urheberrecht. Die Werke auf diesen Seiten unterliegen dem deutschen
Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Urheberrecht. Die Vervielfältigung, Bearbeitung,
Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der Verbreitung und jede Art der Verwertung außerhalb der
schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Grenzen des Urheberrechtes bedürfen der schriftlichen
Downloads und Kopien dieser Seite sind nur für den privaten, nicht Zustimmung des jeweiligen Autors bzw. Erstellers.
kommerziellen Gebrauch gestattet. Downloads und Kopien dieser Seite sind nur für den
</p> privaten, nicht kommerziellen Gebrauch gestattet.
</p>
<p> <p>
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt Soweit die Inhalte auf dieser Seite nicht vom Betreiber
wurden, werden die Urheberrechte Dritter beachtet. Insbesondere erstellt wurden, werden die Urheberrechte Dritter
werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie beachtet. Insbesondere werden Inhalte Dritter als solche
trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten gekennzeichnet. Sollten Sie trotzdem auf eine
wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Urheberrechtsverletzung aufmerksam werden, bitten wir um
Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen. einen entsprechenden Hinweis. Bei Bekanntwerden von
</p> Rechtsverletzungen werden wir derartige Inhalte umgehend
</article> entfernen.
</section> </p>
</Layout> </article>
); </section>
</Layout>
);
} }

View file

@ -7,82 +7,88 @@ import PropTypes from "prop-types";
import * as styles from "./projects.module.scss"; import * as styles from "./projects.module.scss";
export const query = graphql` export const query = graphql`
query GetProjects($language: String) { query GetProjects($language: String) {
allProjectsJson(filter: { lang: { eq: $language } }) { allProjectsJson(filter: { lang: { eq: $language } }) {
nodes { nodes {
lang lang
urlname urlname
name name
image { image {
childImageSharp { childImageSharp {
resize(width: 400, quality: 90) { resize(width: 400, quality: 90) {
src src
}
}
}
shortDescription
} }
}
} }
shortDescription locales: allLocale(filter: { language: { eq: $language } }) {
} edges {
} node {
locales: allLocale(filter: { language: { eq: $language } }) { ns
edges { data
node { language
ns }
data }
language
} }
}
} }
}
`; `;
const ProjectsPage = ({ data }) => { const ProjectsPage = ({ data }) => {
const { t } = useI18next(); const { t } = useI18next();
return ( return (
<Layout title={t("projects")} description={t("projectsDescription")}> <Layout title={t("projects")} description={t("projectsDescription")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>projects</Trans> <Trans>projects</Trans>
</h1> </h1>
<p> <p>
<Trans>projectsDescription</Trans> <Trans>projectsDescription</Trans>
</p> </p>
<div className={styles.projectList}> <div className={styles.projectList}>
{data.allProjectsJson.nodes.map((project) => { {data.allProjectsJson.nodes.map((project) => {
return ( return (
<Link <Link
className={styles.projectCard} className={styles.projectCard}
key={project.lang + project.urlname} key={project.lang + project.urlname}
to={"/projects/" + project.urlname} to={"/projects/" + project.urlname}>
> <div
<div className={styles.projectCardImage}
className={styles.projectCardImage} style={{
style={{ backgroundImage:
backgroundImage: "url(" +
"url(" + project.image.childImageSharp.resize.src + ")", project.image.childImageSharp
}} .resize.src +
> ")",
<div className={styles.projectCardMeta}> }}>
<span className={styles.projectCardTitle}> <div className={styles.projectCardMeta}>
{project.name} <span
</span> className={
<span>{project.shortDescription}</span> styles.projectCardTitle
}>
{project.name}
</span>
<span>
{project.shortDescription}
</span>
</div>
</div>
</Link>
);
})}
</div> </div>
</div> </article>
</Link> </section>
); </Layout>
})} );
</div>
</article>
</section>
</Layout>
);
}; };
ProjectsPage.propTypes = { ProjectsPage.propTypes = {
data: PropTypes.object, data: PropTypes.object,
}; };
export default ProjectsPage; export default ProjectsPage;

View file

@ -7,76 +7,77 @@ import PropTypes from "prop-types";
import * as styles from "./social.module.scss"; import * as styles from "./social.module.scss";
export const query = graphql` export const query = graphql`
query AllSocialsQuery($language: String!) { query AllSocialsQuery($language: String!) {
allSocialsJson { allSocialsJson {
nodes { nodes {
image image
platformHandle platformHandle
platformName platformName
url url
} }
} }
locales: allLocale(filter: { language: { eq: $language } }) { locales: allLocale(filter: { language: { eq: $language } }) {
edges { edges {
node { node {
ns ns
data data
language language
}
}
} }
}
} }
}
`; `;
const SocialPage = ({ data }) => { const SocialPage = ({ data }) => {
const { t } = useI18next(); const { t } = useI18next();
return ( return (
<Layout title={t("social")} description={t("socialDescription")}> <Layout title={t("social")} description={t("socialDescription")}>
<section> <section>
<article> <article>
<h1> <h1>
<Trans>social</Trans> <Trans>social</Trans>
</h1> </h1>
<p> <p>
<Trans i18nKey="socialDescriptionWithLink"> <Trans i18nKey="socialDescriptionWithLink">
socialDescriptionWith<Link to="/friends">Link</Link> socialDescriptionWith<Link to="/friends">Link</Link>
</Trans> </Trans>
</p> </p>
<div className={styles.socialList}> <div className={styles.socialList}>
{data.allSocialsJson.nodes.map((social) => { {data.allSocialsJson.nodes.map((social) => {
return ( return (
<a <a
className={styles.socialCard} className={styles.socialCard}
href={social.url} href={social.url}
target="_blank" target="_blank"
rel="noreferrer me" rel="noreferrer me"
key={social.url} key={social.url}>
> <div
<div className={styles.socialImage}
className={styles.socialImage} style={{
style={{ backgroundImage: "url(" + social.image + ")" }} backgroundImage:
> "url(" + social.image + ")",
<span className={styles.socialName}> }}>
{social.platformName} <span className={styles.socialName}>
</span> {social.platformName}
<span className={styles.socialUsername}> </span>
{social.platformHandle} <span className={styles.socialUsername}>
</span> {social.platformHandle}
</div> </span>
</a> </div>
); </a>
})} );
</div> })}
</article> </div>
</section> </article>
</Layout> </section>
); </Layout>
);
}; };
SocialPage.propTypes = { SocialPage.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default SocialPage; export default SocialPage;

View file

@ -8,115 +8,121 @@ import * as styles from "./project.module.scss";
import { MDXRenderer } from "gatsby-plugin-mdx"; import { MDXRenderer } from "gatsby-plugin-mdx";
export const query = graphql` export const query = graphql`
query GetProject($urlname: String!, $lang: String!, $language: String!) { query GetProject($urlname: String!, $lang: String!, $language: String!) {
allProjectsJson( allProjectsJson(
filter: { urlname: { eq: $urlname }, lang: { eq: $lang } } filter: { urlname: { eq: $urlname }, lang: { eq: $lang } }
) { ) {
nodes { nodes {
lang lang
urlname urlname
name name
links { links {
github github
website website
}
image {
publicURL
}
shortDescription
}
} }
image { locales: allLocale(filter: { language: { eq: $language } }) {
publicURL edges {
node {
ns
data
language
}
}
} }
shortDescription file(
} sourceInstanceName: { eq: "projectTextblocks" }
} relativeDirectory: { eq: $urlname }
locales: allLocale(filter: { language: { eq: $language } }) { name: { eq: $language }
edges { ) {
node { id
ns childMdx {
data body
language }
name
} }
}
} }
file(
sourceInstanceName: {eq: "projectTextblocks"}, relativeDirectory: {eq: $urlname}, name: {eq: $language}
) {
id
childMdx {
body
}
name
}
}
`; `;
const ProjectTemplate = ({ data }) => { const ProjectTemplate = ({ data }) => {
const { t } = useTranslation(); const { t } = useTranslation();
let project = data.allProjectsJson.nodes[0]; let project = data.allProjectsJson.nodes[0];
let projectName = project.name; let projectName = project.name;
let file = data.file; let file = data.file;
return ( return (
<Layout <Layout
description={project.shortDescription} description={project.shortDescription}
title={t("project") + ": " + projectName} title={t("project") + ": " + projectName}
transparentTopbar={true} transparentTopbar={true}>
> <section className={styles.projectHeader}>
<section className={styles.projectHeader}> <div style={{ paddingTop: 0 }}>
<div style={{ paddingTop: 0 }}> <div
<div className={styles.headerBackground}
className={styles.headerBackground} style={{
style={{ backgroundImage: "url(" + project.image.publicURL + ")" }} backgroundImage:
></div> "url(" + project.image.publicURL + ")",
<header> }}></div>
<div className={styles.headerInner}> <header>
<h1> <div className={styles.headerInner}>
<Trans>project</Trans>: {projectName} <h1>
</h1> <Trans>project</Trans>: {projectName}
<span>{project.shortDescription}</span> </h1>
</div> <span>{project.shortDescription}</span>
</header> </div>
<div className={styles.headerPlaceholder}></div> </header>
</div> <div className={styles.headerPlaceholder}></div>
</section> </div>
{file != null && file.childMdx != null ? ( </section>
<section className={styles.projectAbout}> {file != null && file.childMdx != null ? (
<article> <section className={styles.projectAbout}>
<MDXRenderer>{file.childMdx.body}</MDXRenderer> <article>
</article> <MDXRenderer>{file.childMdx.body}</MDXRenderer>
</section> </article>
) : null} </section>
{project.links !== null ? ( ) : null}
<section className={styles.projectLinks}> {project.links !== null ? (
<div> <section className={styles.projectLinks}>
<h1>Links</h1> <div>
<div className={styles.linkList}> <h1>Links</h1>
{project.links.github !== null ? ( <div className={styles.linkList}>
<a href={project.links.github} target="_blank" rel="noreferrer"> {project.links.github !== null ? (
<i className="fab fa-github" aria-hidden="true"></i>{" "} <a
<Trans>projectViewGitHub</Trans> href={project.links.github}
</a> target="_blank"
) : null} rel="noreferrer">
{project.links.website !== null ? ( <i
<a className="fab fa-github"
href={project.links.website} aria-hidden="true"></i>{" "}
target="_blank" <Trans>projectViewGitHub</Trans>
rel="noreferrer" </a>
> ) : null}
<i {project.links.website !== null ? (
className="fas fa-external-link-alt" <a
aria-hidden="true" href={project.links.website}
></i>{" "} target="_blank"
<Trans>projectViewWebsite</Trans> rel="noreferrer">
</a> <i
) : null} className="fas fa-external-link-alt"
</div> aria-hidden="true"></i>{" "}
</div> <Trans>projectViewWebsite</Trans>
</section> </a>
) : null} ) : null}
</Layout> </div>
); </div>
</section>
) : null}
</Layout>
);
}; };
ProjectTemplate.propTypes = { ProjectTemplate.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
}; };
export default ProjectTemplate; export default ProjectTemplate;