mirror of
https://github.com/kremalicious/portfolio.git
synced 2024-12-22 17:23:22 +01:00
open source section
This commit is contained in:
parent
896d1737d4
commit
8d5e2d6a7e
@ -1,7 +1,6 @@
|
||||
dist: xenial
|
||||
language: node_js
|
||||
node_js:
|
||||
- '11'
|
||||
node_js: node
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
@ -1,34 +1,34 @@
|
||||
title: Matthias Kretschmann
|
||||
tagline: Designer & Developer
|
||||
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
|
||||
url: https://matthiaskretschmann.com
|
||||
email: m@kretschmann.io
|
||||
avatar: ../src/images/avatar.jpg
|
||||
img: ../src/images/twitter-card.png
|
||||
- title: Matthias Kretschmann
|
||||
tagline: Designer & Developer
|
||||
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
|
||||
url: https://matthiaskretschmann.com
|
||||
email: m@kretschmann.io
|
||||
avatar: ../src/images/avatar.jpg
|
||||
img: ../src/images/twitter-card.png
|
||||
|
||||
social:
|
||||
Email: mailto:m@kretschmann.io
|
||||
Blog: https://kremalicious.com
|
||||
Twitter: https://twitter.com/kremalicious
|
||||
GitHub: https://github.com/kremalicious
|
||||
Dribbble: https://dribbble.com/kremalicious
|
||||
social:
|
||||
Email: mailto:m@kretschmann.io
|
||||
Blog: https://kremalicious.com
|
||||
Twitter: https://twitter.com/kremalicious
|
||||
GitHub: https://github.com/kremalicious
|
||||
Dribbble: https://dribbble.com/kremalicious
|
||||
|
||||
availability:
|
||||
status: false
|
||||
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Let’s talk</a>!'
|
||||
unavailable: Not available for new projects.
|
||||
availability:
|
||||
status: false
|
||||
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Let’s talk</a>!'
|
||||
unavailable: Not available for new projects.
|
||||
|
||||
# Footer actions
|
||||
gpg: https://kretschmann.io/pub.gpg
|
||||
addressbook: /matthias-kretschmann.vcf
|
||||
# Footer actions
|
||||
gpg: https://kretschmann.io/pub.gpg
|
||||
addressbook: /matthias-kretschmann.vcf
|
||||
|
||||
typekitID: dtg3zui
|
||||
typekitID: dtg3zui
|
||||
|
||||
# Analytics tools
|
||||
matomoUrl: https://analytics.kremalicious.com
|
||||
matomoSite: 2
|
||||
# Analytics tools
|
||||
matomoUrl: https://analytics.kremalicious.com
|
||||
matomoSite: 2
|
||||
|
||||
allowedHosts:
|
||||
- matthiaskretschmann.com
|
||||
- beta.matthiaskretschmann.com
|
||||
- localhost
|
||||
allowedHosts:
|
||||
- matthiaskretschmann.com
|
||||
- beta.matthiaskretschmann.com
|
||||
- localhost
|
||||
|
11
content/repos.yml
Normal file
11
content/repos.yml
Normal file
@ -0,0 +1,11 @@
|
||||
- user: kremalicious
|
||||
repos:
|
||||
- portfolio
|
||||
- blog
|
||||
- blowfish
|
||||
- gatsby-plugin-matomo
|
||||
- gatsby-redirect-from
|
||||
- hyper-mac-pro
|
||||
- appstorebadges
|
||||
- kbdfun
|
||||
- wp-icons-template
|
@ -2,7 +2,7 @@ const path = require('path')
|
||||
const fs = require('fs')
|
||||
const yaml = require('js-yaml')
|
||||
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8'))
|
||||
const { title, description, url, matomoSite, matomoUrl } = meta
|
||||
const { title, description, url, matomoSite, matomoUrl } = meta[0]
|
||||
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"contentYaml": {
|
||||
"metaYaml": {
|
||||
"title": "Matthias Kretschmann",
|
||||
"tagline": "Designer & Developer",
|
||||
"description": "Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.",
|
||||
|
@ -23,6 +23,7 @@
|
||||
"new": "babel-node ./scripts/new.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"classnames": "^2.2.6",
|
||||
"file-saver": "^2.0.1",
|
||||
"gatsby": "^2.7.1",
|
||||
|
@ -16,7 +16,7 @@ import styles from './Layout.module.scss'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
allowedHosts
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ export default class Layout extends PureComponent {
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const { allowedHosts } = data.contentYaml
|
||||
const { allowedHosts } = data.metaYaml
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -5,7 +5,7 @@
|
||||
width: 100%;
|
||||
color: $brand-cyan;
|
||||
text-align: center;
|
||||
border-radius: .25rem;
|
||||
border-radius: $border-radius;
|
||||
padding: $spacer / 4 $spacer / 2;
|
||||
transition-property: all;
|
||||
background: rgba(#fff, .15);
|
||||
|
@ -5,7 +5,7 @@ import { StaticQuery, graphql } from 'gatsby'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
@ -75,7 +75,7 @@ export default class SEO extends PureComponent {
|
||||
query={query}
|
||||
render={data => {
|
||||
const { project } = this.props
|
||||
const meta = data.contentYaml
|
||||
const meta = data.metaYaml
|
||||
const title = (project && project.title) || null
|
||||
const description =
|
||||
(project && project.fields.excerpt) || meta.description
|
||||
|
@ -19,7 +19,7 @@ const TypekitScript = typekitID => (
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
typekitID
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ const Typekit = () => (
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const { typekitID } = data.contentYaml
|
||||
const { typekitID } = data.metaYaml
|
||||
|
||||
return (
|
||||
typekitID && (
|
||||
|
@ -5,7 +5,7 @@ import vCard from 'vcf'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
@ -37,7 +37,7 @@ export default class Vcard extends PureComponent {
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const meta = data.contentYaml
|
||||
const meta = data.metaYaml
|
||||
|
||||
const handleAddressbookClick = e => {
|
||||
e.preventDefault()
|
||||
|
@ -24,14 +24,14 @@ describe('Vcard', () => {
|
||||
})
|
||||
|
||||
it('combined vCard download process finishes', async () => {
|
||||
await init(data.contentYaml)
|
||||
await init(data.metaYaml)
|
||||
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('vCard can be constructed', async () => {
|
||||
const vcard = await constructVcard(
|
||||
'data:image/jpeg;base64,00',
|
||||
data.contentYaml
|
||||
data.metaYaml
|
||||
)
|
||||
expect(vcard).toBeDefined()
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
@import 'variables';
|
||||
|
||||
.availability {
|
||||
border-radius: .25rem;
|
||||
border-radius: $border-radius;
|
||||
color: $text-color-light;
|
||||
z-index: 2;
|
||||
padding: $spacer / 2;
|
||||
|
@ -14,7 +14,7 @@ describe('Availability', () => {
|
||||
it('renders correctly when status: true', () => {
|
||||
useStaticQuery.mockImplementationOnce(() => {
|
||||
return {
|
||||
contentYaml: {
|
||||
metaYaml: {
|
||||
availability: {
|
||||
status: true,
|
||||
available: 'I am available.',
|
||||
@ -32,7 +32,7 @@ describe('Availability', () => {
|
||||
it('renders correctly when status: false', () => {
|
||||
useStaticQuery.mockImplementationOnce(() => {
|
||||
return {
|
||||
contentYaml: {
|
||||
metaYaml: {
|
||||
availability: {
|
||||
status: false,
|
||||
available: 'I am available.',
|
||||
|
@ -9,7 +9,7 @@ import styles from './LogoUnit.module.scss'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
title
|
||||
tagline
|
||||
}
|
||||
@ -35,7 +35,7 @@ export default class LogoUnit extends PureComponent {
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const { title, tagline } = data.contentYaml
|
||||
const { title, tagline } = data.metaYaml
|
||||
|
||||
return (
|
||||
<div className={this.wrapClasses}>
|
||||
|
@ -10,7 +10,7 @@ beforeEach(() => {
|
||||
|
||||
describe('LogoUnit', () => {
|
||||
it('renders correctly from data file values', () => {
|
||||
const { title, tagline } = data.contentYaml
|
||||
const { title, tagline } = data.metaYaml
|
||||
const { container, getByTestId } = render(<LogoUnit />)
|
||||
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
|
@ -10,7 +10,7 @@ beforeEach(() => {
|
||||
|
||||
describe('Networks', () => {
|
||||
it('renders correctly from data file values', () => {
|
||||
const { social } = data.contentYaml
|
||||
const { social } = data.metaYaml
|
||||
const { container, getByTestId } = render(<Networks />)
|
||||
|
||||
expect(container.firstChild).toBeInTheDocument()
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
@media (min-width: $projectImageMaxWidth) {
|
||||
max-width: $projectImageMaxWidth;
|
||||
border-radius: .25rem;
|
||||
border-radius: $border-radius;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
padding: $spacer / 4;
|
||||
text-align: center;
|
||||
background: rgba(#fff, .15);
|
||||
border-radius: .25rem;
|
||||
border-radius: $border-radius;
|
||||
border: .05rem solid transparent;
|
||||
color: $brand-grey-light;
|
||||
font-size: $font-size-small;
|
||||
|
46
src/components/molecules/Repository.jsx
Normal file
46
src/components/molecules/Repository.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ReactComponent as Star } from '../../images/star.svg'
|
||||
import styles from './Repository.module.scss'
|
||||
import LinkIcon from '../atoms/LinkIcon'
|
||||
|
||||
const Repository = ({ repo }) => {
|
||||
const { name, description, html_url, homepage, stargazers_count } = repo
|
||||
|
||||
// for blog & portfolio and if there's no homepage, use github url
|
||||
// else use homepage field
|
||||
const repoLink =
|
||||
name === 'blog' || name === 'portfolio' || !homepage ? html_url : homepage
|
||||
|
||||
return (
|
||||
<div className={styles.repo}>
|
||||
<h1 className={styles.repoTitle}>
|
||||
<a href={repoLink}>{name}</a>
|
||||
</h1>
|
||||
<p>{description}</p>
|
||||
<p className={styles.meta}>
|
||||
{name === 'portfolio' || name === 'blog'
|
||||
? null
|
||||
: homepage && (
|
||||
<a href={homepage}>
|
||||
<LinkIcon title="website" /> Learn more
|
||||
</a>
|
||||
)}
|
||||
|
||||
<a href={html_url}>
|
||||
<LinkIcon title="github" /> GitHub
|
||||
</a>
|
||||
|
||||
<a href={`${html_url}/stargazers`}>
|
||||
<Star /> {stargazers_count}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Repository.propTypes = {
|
||||
repo: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default Repository
|
59
src/components/molecules/Repository.module.scss
Normal file
59
src/components/molecules/Repository.module.scss
Normal file
@ -0,0 +1,59 @@
|
||||
@import 'variables';
|
||||
|
||||
.repo {
|
||||
padding: $spacer;
|
||||
border-radius: $border-radius;
|
||||
background: rgba(#fff, .15);
|
||||
box-shadow: 0 3px 5px rgba($brand-main, .1),
|
||||
0 5px 16px rgba($brand-main, .1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
|
||||
:global(.dark) & {
|
||||
background: darken($body-background-color--dark, 1%);
|
||||
box-shadow: 0 3px 5px rgba(darken($brand-main, 20%), .1),
|
||||
0 5px 16px rgba(darken($brand-main, 20%), .1);
|
||||
}
|
||||
|
||||
> * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.repoTitle {
|
||||
font-size: $font-size-h4;
|
||||
margin-bottom: $spacer / 2;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: $font-size-small;
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: $brand-grey-light;
|
||||
font-variant-numeric: lining-nums;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $brand-cyan;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
width: $font-size-mini;
|
||||
height: $font-size-mini;
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ const query = graphql`
|
||||
bugs
|
||||
}
|
||||
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
title
|
||||
url
|
||||
gpg
|
||||
@ -68,7 +68,7 @@ export default class Footer extends PureComponent {
|
||||
query={query}
|
||||
render={data => {
|
||||
const pkg = data.portfolioJson
|
||||
const meta = data.contentYaml
|
||||
const meta = data.metaYaml
|
||||
|
||||
return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
|
||||
}}
|
||||
|
@ -10,7 +10,7 @@ import styles from './Header.module.scss'
|
||||
|
||||
const query = graphql`
|
||||
query {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
availability {
|
||||
status
|
||||
}
|
||||
@ -30,7 +30,7 @@ export default class Header extends PureComponent {
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
const meta = data.contentYaml
|
||||
const meta = data.metaYaml
|
||||
|
||||
let headerClasses = classNames([styles.header], {
|
||||
[styles.minimal]: minimal
|
||||
|
47
src/components/organisms/Repositories.jsx
Normal file
47
src/components/organisms/Repositories.jsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import axios from 'axios'
|
||||
import Repository from '../molecules/Repository'
|
||||
import styles from './Repositories.module.scss'
|
||||
|
||||
export default class Repositories extends PureComponent {
|
||||
static propTypes = {
|
||||
user: PropTypes.string.isRequired,
|
||||
repos: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
state = { repos: [] }
|
||||
|
||||
async componentDidMount() {
|
||||
try {
|
||||
const repos = await this.getGithubRepos(this.props.user)
|
||||
this.setState({ repos })
|
||||
} catch (error) {
|
||||
console.error(error.message) // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
async getGithubRepos(user) {
|
||||
const allRepos = await axios.get(
|
||||
`https://api.github.com/users/${user}/repos?per_page=100`
|
||||
)
|
||||
const repos = allRepos.data
|
||||
.filter(({ name }) => this.props.repos.includes(name))
|
||||
.sort((a, b) => b.pushed_at.localeCompare(a.pushed_at)) // sort by pushed to, newest first
|
||||
|
||||
return repos
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h1 className={styles.sectionTitle}>Open Source Projects</h1>
|
||||
<div className={styles.repos}>
|
||||
{this.state.repos.map(repo => (
|
||||
<Repository key={repo.name} repo={repo} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
29
src/components/organisms/Repositories.module.scss
Normal file
29
src/components/organisms/Repositories.module.scss
Normal file
@ -0,0 +1,29 @@
|
||||
@import 'variables';
|
||||
|
||||
.section {
|
||||
max-width: calc(#{$projectImageMaxWidth} - #{$spacer * 2});
|
||||
margin: $spacer * 3 auto 0 auto;
|
||||
padding-left: $spacer;
|
||||
padding-right: $spacer;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: $font-size-h3;
|
||||
margin-bottom: $spacer * 2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.repos {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: $spacer * 2;
|
||||
|
||||
@media (min-width: $screen-sm) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: $spacer * 2;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-md) {
|
||||
grid-gap: $spacer * 3;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { useStaticQuery, graphql } from 'gatsby'
|
||||
|
||||
const query = graphql`
|
||||
query Meta {
|
||||
contentYaml {
|
||||
metaYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
@ -31,6 +31,6 @@ const query = graphql`
|
||||
`
|
||||
|
||||
export const useMeta = () => {
|
||||
const { contentYaml } = useStaticQuery(query)
|
||||
return contentYaml
|
||||
const { metaYaml } = useStaticQuery(query)
|
||||
return metaYaml
|
||||
}
|
||||
|
3
src/images/star.svg
Normal file
3
src/images/star.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<polygon points="22.136 19.625 32 12 20 12 16 0 12 12 0 12 9.875 19.594 6 32 16.016 24.32 26.008 32"/>
|
||||
</svg>
|
After Width: | Height: | Size: 196 B |
@ -5,6 +5,7 @@ import SEO from '../components/atoms/SEO'
|
||||
import ProjectImage from '../components/molecules/ProjectImage'
|
||||
import { ReactComponent as Images } from '../images/images.svg'
|
||||
import styles from './index.module.scss'
|
||||
import Repositories from '../components/organisms/Repositories'
|
||||
|
||||
function getImageCount(images, slug) {
|
||||
let array = []
|
||||
@ -56,6 +57,8 @@ export default class Home extends PureComponent {
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Repositories user={data.reposYaml.user} repos={data.reposYaml.repos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -88,5 +91,10 @@ export const IndexQuery = graphql`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reposYaml {
|
||||
user
|
||||
repos
|
||||
}
|
||||
}
|
||||
`
|
||||
|
@ -5,22 +5,23 @@ import { getLocationTimes } from '../utils/getLocationTimes'
|
||||
import { getCountry } from '../utils/getCountry'
|
||||
|
||||
export default class AppProvider extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired
|
||||
}
|
||||
|
||||
state = {
|
||||
dark: false,
|
||||
toggleDark: () => this.toggleDark(),
|
||||
geolocation: null
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.any.isRequired
|
||||
}
|
||||
|
||||
store = typeof localStorage === 'undefined' ? null : localStorage
|
||||
|
||||
mounted = false
|
||||
|
||||
async componentDidMount() {
|
||||
this.mounted = true
|
||||
|
||||
const geolocation = await getCountry()
|
||||
this.setState({ geolocation })
|
||||
this.checkDark()
|
||||
@ -30,16 +31,12 @@ export default class AppProvider extends PureComponent {
|
||||
this.mounted = false
|
||||
}
|
||||
|
||||
setDark() {
|
||||
this.mounted && this.setState({ dark: true })
|
||||
}
|
||||
|
||||
setLight() {
|
||||
this.mounted && this.setState({ dark: false })
|
||||
setDark(dark) {
|
||||
this.mounted && this.setState({ dark })
|
||||
}
|
||||
|
||||
darkLocalStorageMode(darkLocalStorage) {
|
||||
darkLocalStorage === 'true' ? this.setDark() : this.setLight()
|
||||
darkLocalStorage === 'true' ? this.setDark(true) : this.setDark(false)
|
||||
}
|
||||
|
||||
//
|
||||
@ -51,7 +48,7 @@ export default class AppProvider extends PureComponent {
|
||||
const now = new Date().getHours()
|
||||
const weWantItDarkTimes = now >= sunset || now <= sunrise
|
||||
|
||||
!dark && weWantItDarkTimes ? this.setDark() : this.setLight()
|
||||
!dark && weWantItDarkTimes ? this.setDark(true) : this.setDark(false)
|
||||
}
|
||||
|
||||
async checkDark() {
|
||||
|
@ -67,6 +67,7 @@ $color-headings--dark: $brand-main-light;
|
||||
/////////////////////////////////////
|
||||
|
||||
$spacer: ($font-size-base * $line-height);
|
||||
$border-radius: .25rem;
|
||||
|
||||
// Responsive breakpoints
|
||||
/////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user