1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-23 01:29:41 +01:00

open source section

This commit is contained in:
Matthias Kretschmann 2019-05-26 16:55:56 +02:00
parent 896d1737d4
commit 8d5e2d6a7e
Signed by: m
GPG Key ID: 606EEEF3C479A91F
30 changed files with 272 additions and 71 deletions

View File

@ -1,7 +1,6 @@
dist: xenial dist: xenial
language: node_js language: node_js
node_js: node_js: node
- '11'
cache: cache:
directories: directories:

View File

@ -1,34 +1,34 @@
title: Matthias Kretschmann - title: Matthias Kretschmann
tagline: Designer & Developer tagline: Designer & Developer
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann. description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
url: https://matthiaskretschmann.com url: https://matthiaskretschmann.com
email: m@kretschmann.io email: m@kretschmann.io
avatar: ../src/images/avatar.jpg avatar: ../src/images/avatar.jpg
img: ../src/images/twitter-card.png img: ../src/images/twitter-card.png
social: social:
Email: mailto:m@kretschmann.io Email: mailto:m@kretschmann.io
Blog: https://kremalicious.com Blog: https://kremalicious.com
Twitter: https://twitter.com/kremalicious Twitter: https://twitter.com/kremalicious
GitHub: https://github.com/kremalicious GitHub: https://github.com/kremalicious
Dribbble: https://dribbble.com/kremalicious Dribbble: https://dribbble.com/kremalicious
availability: availability:
status: false status: false
available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Lets talk</a>!' available: '👔 Available for new projects. <a href="mailto:m@kretschmann.io">Lets talk</a>!'
unavailable: Not available for new projects. unavailable: Not available for new projects.
# Footer actions # Footer actions
gpg: https://kretschmann.io/pub.gpg gpg: https://kretschmann.io/pub.gpg
addressbook: /matthias-kretschmann.vcf addressbook: /matthias-kretschmann.vcf
typekitID: dtg3zui typekitID: dtg3zui
# Analytics tools # Analytics tools
matomoUrl: https://analytics.kremalicious.com matomoUrl: https://analytics.kremalicious.com
matomoSite: 2 matomoSite: 2
allowedHosts: allowedHosts:
- matthiaskretschmann.com - matthiaskretschmann.com
- beta.matthiaskretschmann.com - beta.matthiaskretschmann.com
- localhost - localhost

11
content/repos.yml Normal file
View 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

View File

@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs') const fs = require('fs')
const yaml = require('js-yaml') const yaml = require('js-yaml')
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8')) 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 = { module.exports = {
siteMetadata: { siteMetadata: {

View File

@ -1,5 +1,5 @@
{ {
"contentYaml": { "metaYaml": {
"title": "Matthias Kretschmann", "title": "Matthias Kretschmann",
"tagline": "Designer & Developer", "tagline": "Designer & Developer",
"description": "Portfolio of web &amp; ui designer/developer hybrid Matthias Kretschmann.", "description": "Portfolio of web &amp; ui designer/developer hybrid Matthias Kretschmann.",

View File

@ -23,6 +23,7 @@
"new": "babel-node ./scripts/new.js" "new": "babel-node ./scripts/new.js"
}, },
"dependencies": { "dependencies": {
"axios": "^0.18.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"file-saver": "^2.0.1", "file-saver": "^2.0.1",
"gatsby": "^2.7.1", "gatsby": "^2.7.1",

View File

@ -16,7 +16,7 @@ import styles from './Layout.module.scss'
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
allowedHosts allowedHosts
} }
} }
@ -41,7 +41,7 @@ export default class Layout extends PureComponent {
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => {
const { allowedHosts } = data.contentYaml const { allowedHosts } = data.metaYaml
return ( return (
<> <>

View File

@ -5,7 +5,7 @@
width: 100%; width: 100%;
color: $brand-cyan; color: $brand-cyan;
text-align: center; text-align: center;
border-radius: .25rem; border-radius: $border-radius;
padding: $spacer / 4 $spacer / 2; padding: $spacer / 4 $spacer / 2;
transition-property: all; transition-property: all;
background: rgba(#fff, .15); background: rgba(#fff, .15);

View File

@ -5,7 +5,7 @@ import { StaticQuery, graphql } from 'gatsby'
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
title title
tagline tagline
description description
@ -75,7 +75,7 @@ export default class SEO extends PureComponent {
query={query} query={query}
render={data => { render={data => {
const { project } = this.props const { project } = this.props
const meta = data.contentYaml const meta = data.metaYaml
const title = (project && project.title) || null const title = (project && project.title) || null
const description = const description =
(project && project.fields.excerpt) || meta.description (project && project.fields.excerpt) || meta.description

View File

@ -19,7 +19,7 @@ const TypekitScript = typekitID => (
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
typekitID typekitID
} }
} }
@ -29,7 +29,7 @@ const Typekit = () => (
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => {
const { typekitID } = data.contentYaml const { typekitID } = data.metaYaml
return ( return (
typekitID && ( typekitID && (

View File

@ -5,7 +5,7 @@ import vCard from 'vcf'
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
title title
tagline tagline
description description
@ -37,7 +37,7 @@ export default class Vcard extends PureComponent {
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => {
const meta = data.contentYaml const meta = data.metaYaml
const handleAddressbookClick = e => { const handleAddressbookClick = e => {
e.preventDefault() e.preventDefault()

View File

@ -24,14 +24,14 @@ describe('Vcard', () => {
}) })
it('combined vCard download process finishes', async () => { it('combined vCard download process finishes', async () => {
await init(data.contentYaml) await init(data.metaYaml)
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1) expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
}) })
it('vCard can be constructed', async () => { it('vCard can be constructed', async () => {
const vcard = await constructVcard( const vcard = await constructVcard(
'data:image/jpeg;base64,00', 'data:image/jpeg;base64,00',
data.contentYaml data.metaYaml
) )
expect(vcard).toBeDefined() expect(vcard).toBeDefined()
}) })

View File

@ -1,7 +1,7 @@
@import 'variables'; @import 'variables';
.availability { .availability {
border-radius: .25rem; border-radius: $border-radius;
color: $text-color-light; color: $text-color-light;
z-index: 2; z-index: 2;
padding: $spacer / 2; padding: $spacer / 2;

View File

@ -14,7 +14,7 @@ describe('Availability', () => {
it('renders correctly when status: true', () => { it('renders correctly when status: true', () => {
useStaticQuery.mockImplementationOnce(() => { useStaticQuery.mockImplementationOnce(() => {
return { return {
contentYaml: { metaYaml: {
availability: { availability: {
status: true, status: true,
available: 'I am available.', available: 'I am available.',
@ -32,7 +32,7 @@ describe('Availability', () => {
it('renders correctly when status: false', () => { it('renders correctly when status: false', () => {
useStaticQuery.mockImplementationOnce(() => { useStaticQuery.mockImplementationOnce(() => {
return { return {
contentYaml: { metaYaml: {
availability: { availability: {
status: false, status: false,
available: 'I am available.', available: 'I am available.',

View File

@ -9,7 +9,7 @@ import styles from './LogoUnit.module.scss'
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
title title
tagline tagline
} }
@ -35,7 +35,7 @@ export default class LogoUnit extends PureComponent {
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => {
const { title, tagline } = data.contentYaml const { title, tagline } = data.metaYaml
return ( return (
<div className={this.wrapClasses}> <div className={this.wrapClasses}>

View File

@ -10,7 +10,7 @@ beforeEach(() => {
describe('LogoUnit', () => { describe('LogoUnit', () => {
it('renders correctly from data file values', () => { it('renders correctly from data file values', () => {
const { title, tagline } = data.contentYaml const { title, tagline } = data.metaYaml
const { container, getByTestId } = render(<LogoUnit />) const { container, getByTestId } = render(<LogoUnit />)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()

View File

@ -10,7 +10,7 @@ beforeEach(() => {
describe('Networks', () => { describe('Networks', () => {
it('renders correctly from data file values', () => { it('renders correctly from data file values', () => {
const { social } = data.contentYaml const { social } = data.metaYaml
const { container, getByTestId } = render(<Networks />) const { container, getByTestId } = render(<Networks />)
expect(container.firstChild).toBeInTheDocument() expect(container.firstChild).toBeInTheDocument()

View File

@ -9,7 +9,7 @@
@media (min-width: $projectImageMaxWidth) { @media (min-width: $projectImageMaxWidth) {
max-width: $projectImageMaxWidth; max-width: $projectImageMaxWidth;
border-radius: .25rem; border-radius: $border-radius;
overflow: hidden; overflow: hidden;
} }

View File

@ -15,7 +15,7 @@
padding: $spacer / 4; padding: $spacer / 4;
text-align: center; text-align: center;
background: rgba(#fff, .15); background: rgba(#fff, .15);
border-radius: .25rem; border-radius: $border-radius;
border: .05rem solid transparent; border: .05rem solid transparent;
color: $brand-grey-light; color: $brand-grey-light;
font-size: $font-size-small; font-size: $font-size-small;

View 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

View 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;
}
}

View File

@ -14,7 +14,7 @@ const query = graphql`
bugs bugs
} }
contentYaml { metaYaml {
title title
url url
gpg gpg
@ -68,7 +68,7 @@ export default class Footer extends PureComponent {
query={query} query={query}
render={data => { render={data => {
const pkg = data.portfolioJson const pkg = data.portfolioJson
const meta = data.contentYaml const meta = data.metaYaml
return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} /> return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
}} }}

View File

@ -10,7 +10,7 @@ import styles from './Header.module.scss'
const query = graphql` const query = graphql`
query { query {
contentYaml { metaYaml {
availability { availability {
status status
} }
@ -30,7 +30,7 @@ export default class Header extends PureComponent {
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => {
const meta = data.contentYaml const meta = data.metaYaml
let headerClasses = classNames([styles.header], { let headerClasses = classNames([styles.header], {
[styles.minimal]: minimal [styles.minimal]: minimal

View 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>
)
}
}

View 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;
}
}

View File

@ -2,7 +2,7 @@ import { useStaticQuery, graphql } from 'gatsby'
const query = graphql` const query = graphql`
query Meta { query Meta {
contentYaml { metaYaml {
title title
tagline tagline
description description
@ -31,6 +31,6 @@ const query = graphql`
` `
export const useMeta = () => { export const useMeta = () => {
const { contentYaml } = useStaticQuery(query) const { metaYaml } = useStaticQuery(query)
return contentYaml return metaYaml
} }

3
src/images/star.svg Normal file
View 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

View File

@ -5,6 +5,7 @@ import SEO from '../components/atoms/SEO'
import ProjectImage from '../components/molecules/ProjectImage' import ProjectImage from '../components/molecules/ProjectImage'
import { ReactComponent as Images } from '../images/images.svg' import { ReactComponent as Images } from '../images/images.svg'
import styles from './index.module.scss' import styles from './index.module.scss'
import Repositories from '../components/organisms/Repositories'
function getImageCount(images, slug) { function getImageCount(images, slug) {
let array = [] let array = []
@ -56,6 +57,8 @@ export default class Home extends PureComponent {
) )
})} })}
</div> </div>
<Repositories user={data.reposYaml.user} repos={data.reposYaml.repos} />
</> </>
) )
} }
@ -88,5 +91,10 @@ export const IndexQuery = graphql`
} }
} }
} }
reposYaml {
user
repos
}
} }
` `

View File

@ -5,22 +5,23 @@ import { getLocationTimes } from '../utils/getLocationTimes'
import { getCountry } from '../utils/getCountry' import { getCountry } from '../utils/getCountry'
export default class AppProvider extends PureComponent { export default class AppProvider extends PureComponent {
static propTypes = {
children: PropTypes.any.isRequired
}
state = { state = {
dark: false, dark: false,
toggleDark: () => this.toggleDark(), toggleDark: () => this.toggleDark(),
geolocation: null geolocation: null
} }
static propTypes = {
children: PropTypes.any.isRequired
}
store = typeof localStorage === 'undefined' ? null : localStorage store = typeof localStorage === 'undefined' ? null : localStorage
mounted = false mounted = false
async componentDidMount() { async componentDidMount() {
this.mounted = true this.mounted = true
const geolocation = await getCountry() const geolocation = await getCountry()
this.setState({ geolocation }) this.setState({ geolocation })
this.checkDark() this.checkDark()
@ -30,16 +31,12 @@ export default class AppProvider extends PureComponent {
this.mounted = false this.mounted = false
} }
setDark() { setDark(dark) {
this.mounted && this.setState({ dark: true }) this.mounted && this.setState({ dark })
}
setLight() {
this.mounted && this.setState({ dark: false })
} }
darkLocalStorageMode(darkLocalStorage) { 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 now = new Date().getHours()
const weWantItDarkTimes = now >= sunset || now <= sunrise const weWantItDarkTimes = now >= sunset || now <= sunrise
!dark && weWantItDarkTimes ? this.setDark() : this.setLight() !dark && weWantItDarkTimes ? this.setDark(true) : this.setDark(false)
} }
async checkDark() { async checkDark() {

View File

@ -67,6 +67,7 @@ $color-headings--dark: $brand-main-light;
///////////////////////////////////// /////////////////////////////////////
$spacer: ($font-size-base * $line-height); $spacer: ($font-size-base * $line-height);
$border-radius: .25rem;
// Responsive breakpoints // Responsive breakpoints
///////////////////////////////////// /////////////////////////////////////