1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 17:23:50 +01:00

Merge pull request #180 from kremalicious/refactor

refactor & restyling
This commit is contained in:
Matthias Kretschmann 2019-10-29 08:03:09 +01:00 committed by GitHub
commit f928e13207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 428 additions and 569 deletions

View File

@ -68,12 +68,12 @@ As a fallback, QR codes are generated with [react-qr-svg](https://github.com/no2
If you want to know how this works, have a look at the respective components under
- [`src/components/Web3Donation/index.jsx`](src/components/Web3Donation/index.jsx)
- [`src/components/Web3Donation/Account.jsx`](src/components/Web3Donation/Account.jsx)
- [`src/components/Web3Donation/InputGroup.jsx`](src/components/Web3Donation/InputGroup.jsx)
- [`src/components/Web3Donation/Conversion.jsx`](src/components/Web3Donation/Conversion.jsx)
- [`src/components/Web3Donation/Alerts.jsx`](src/components/Web3Donation/Alerts.jsx)
- [`src/components/Web3Donation/utils.jsx`](src/components/Web3Donation/utils.jsx)
- [`src/components/molecules/Web3Donation/index.jsx`](src/components/molecules/Web3Donation/index.jsx)
- [`src/components/molecules/Web3Donation/Account.jsx`](src/components/molecules/Web3Donation/Account.jsx)
- [`src/components/molecules/Web3Donation/InputGroup.jsx`](src/components/molecules/Web3Donation/InputGroup.jsx)
- [`src/components/molecules/Web3Donation/Conversion.jsx`](src/components/molecules/Web3Donation/Conversion.jsx)
- [`src/components/molecules/Web3Donation/Alerts.jsx`](src/components/molecules/Web3Donation/Alerts.jsx)
- [`src/components/molecules/Web3Donation/utils.jsx`](src/components/molecules/Web3Donation/utils.jsx)
- [`src/components/atoms/Qr.jsx`](src/components/atoms/Qr.jsx)
### 🔍 Search
@ -84,9 +84,9 @@ A global search is provided with [gatsby-plugin-lunr](https://github.com/humanse
If you want to know how this works, have a look at the respective components under
- [`src/components/Search/Search.jsx`](src/components/Search/Search.jsx)
- [`src/components/Search/SearchResults.jsx`](src/components/Search/SearchResults.jsx)
- more in [`src/components/Search/`](src/components/Search/)
- [`src/components/molecules/Search/Search.jsx`](src/components/molecules/Search/Search.jsx)
- [`src/components/molecules/Search/SearchResults.jsx`](src/components/molecules/Search/SearchResults.jsx)
- more in [`src/components/molecules/Search/`](src/components/molecules/Search/)
### 🕸 Related Posts

View File

@ -140,9 +140,9 @@ So it's a pretty good idea to make this backwards compatible with some quick if
if ( (function_exists('has_post_thumbnail')) && (has_post_thumbnail()) ) {
the_post_thumbnail();
} else {
$postimage = get_post_meta($post->ID, 'post-image', true);
if ($postimage) {
echo '<img src="'.$postimage.'" alt="" />';
$Image = get_post_meta($post->ID, 'post-image', true);
if ($Image) {
echo '<img src="'.$Image.'" alt="" />';
}
}
```

View File

@ -10,6 +10,8 @@ tags:
- goodies
- gatsby
- matomo
featured: true
---
Plugin for [Gatsby](https://www.gatsbyjs.org) to add tracking with the open-source analytics platform [Matomo](https://matomo.org) (formerly Piwik) onto a site, prioritizing user experience & privacy with sensible defaults.

View File

@ -35,7 +35,6 @@ module.exports = {
options: {
maxWidth: 630,
quality: 80,
withWebp: true,
linkImagesToOriginal: true,
showCaptions: true,
backgroundColor: 'none',

View File

@ -7,7 +7,7 @@ const redirects = [
]
exports.generatePostPages = (createPage, posts, numPages) => {
const postTemplate = path.resolve('src/templates/Post.tsx')
const postTemplate = path.resolve('src/templates/Post/index.tsx')
// Create Post pages
posts.forEach(post => {

View File

@ -35,18 +35,18 @@
"dms2dec": "^1.1.0",
"fast-exif": "^1.0.1",
"fraction.js": "^4.0.12",
"gatsby": "^2.17.1",
"gatsby-image": "^2.2.29",
"gatsby": "^2.17.6",
"gatsby-image": "^2.2.30",
"gatsby-plugin-catch-links": "^2.1.15",
"gatsby-plugin-feed": "^2.3.19",
"gatsby-plugin-lunr": "^1.5.2",
"gatsby-plugin-manifest": "^2.2.23",
"gatsby-plugin-manifest": "^2.2.25",
"gatsby-plugin-matomo": "^0.7.2",
"gatsby-plugin-meta-redirect": "^1.1.1",
"gatsby-plugin-offline": "^3.0.16",
"gatsby-plugin-offline": "^3.0.17",
"gatsby-plugin-react-helmet": "^3.1.13",
"gatsby-plugin-sass": "^2.1.20",
"gatsby-plugin-sharp": "^2.2.32",
"gatsby-plugin-sharp": "^2.2.34",
"gatsby-plugin-sitemap": "^2.2.19",
"gatsby-plugin-svgr": "^2.0.2",
"gatsby-plugin-typescript": "^2.1.15",
@ -58,10 +58,10 @@
"gatsby-remark-images": "^3.1.28",
"gatsby-remark-smartypants": "^2.1.14",
"gatsby-remark-vscode": "^1.2.0",
"gatsby-source-filesystem": "^2.1.33",
"gatsby-source-graphql": "^2.1.20",
"gatsby-transformer-remark": "^2.6.30",
"gatsby-transformer-sharp": "^2.3.0",
"gatsby-source-filesystem": "^2.1.35",
"gatsby-source-graphql": "^2.1.21",
"gatsby-transformer-remark": "^2.6.32",
"gatsby-transformer-sharp": "^2.3.1",
"graphql": "^14.5.8",
"intersection-observer": "^0.7.0",
"js-scrypt": "^0.2.0",
@ -73,7 +73,7 @@
"react-clipboard.js": "^2.0.13",
"react-dom": "^16.11.0",
"react-helmet": "^5.2.1",
"react-modal": "^3.10.1",
"react-modal": "^3.11.1",
"react-pose": "^4.0.9",
"react-qr-svg": "^2.2.1",
"react-transition-group": "^4.3.0",
@ -88,25 +88,25 @@
"@babel/preset-env": "^7.6.3",
"@babel/preset-typescript": "^7.6.0",
"@svgr/webpack": "^4.3.3",
"@testing-library/jest-dom": "^4.1.2",
"@testing-library/jest-dom": "^4.2.0",
"@testing-library/react": "^9.3.0",
"@types/classnames": "^2.2.9",
"@types/jest": "^24.0.19",
"@types/jest": "^24.0.20",
"@types/lunr": "^2.3.2",
"@types/node": "^12.11.5",
"@types/react": "^16.9.9",
"@types/react-dom": "^16.9.2",
"@types/node": "^12.11.7",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.3",
"@types/react-helmet": "^5.0.13",
"@types/react-modal": "^3.10.0",
"@types/react-transition-group": "^4.2.3",
"@types/shortid": "0.0.29",
"@types/web3": "^1.0.20",
"@typescript-eslint/eslint-plugin": "^2.5.0",
"@typescript-eslint/parser": "^2.5.0",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.4.0",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-loader": "^3.0.2",
"eslint-plugin-graphql": "^3.1.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
@ -117,7 +117,7 @@
"jest": "^24.9.0",
"markdownlint-cli": "^0.19.0",
"node-iptc": "^1.0.5",
"node-sass": "^4.12.0",
"node-sass": "^4.13.0",
"npm-run-all": "^4.1.5",
"ora": "^4.0.0",
"pify": "^4.0.1",

30
src/@types/Image.d.ts vendored Normal file
View File

@ -0,0 +1,30 @@
import { FixedObject, FluidObject } from 'gatsby-image'
export interface ImageProps {
title?: string
fluid?: FluidObject
fixed?: FixedObject
alt: string
original?: { src: string }
}
export interface ImageNode {
childImageSharp: ImageProps
fields: {
exif: Exif
}
}
export interface Exif {
iso: string
model: string
fstop: string
shutterspeed: string
focalLength: string
lensModel: string
exposure: string
gps: {
latitude: string
longitude: string
}
}

32
src/@types/Post.d.ts vendored Normal file
View File

@ -0,0 +1,32 @@
import { ImageNode } from './Image'
export interface Fields {
slug: string
date: string
githubLink?: string
}
export interface Frontmatter {
title: string
type?: string
description?: string
image?: ImageNode
author?: string
updated?: string
tags?: string[]
linkurl?: string
style?: {
publicURL?: string
}
changelog?: string
}
export interface Post {
id?: string
html?: string
excerpt?: string
frontmatter: Frontmatter
fields?: Fields
rawMarkdownBody?: string
fileAbsolutePath?: string
}

View File

@ -1,53 +0,0 @@
import { FluidObject } from 'gatsby-image'
export interface PostMetadataFields {
slug: string
date: string
githubLink: string
}
export interface PostMetadataImageExif {
iso: string
model: string
fstop: string
shutterspeed: string
focalLength: string
lensModel: string
exposure: string
gps: {
latitude: string
longitude: string
}
}
export interface PostMetadataImage {
childImageSharp: { fluid: FluidObject }
fields: {
exif: PostMetadataImageExif
}
}
export interface PostMetadataFrontmatter {
type?: string
title: string
description?: string
image?: PostMetadataImage
author?: string
updated?: string
tags?: string[]
linkurl?: string
style?: {
publicURL?: string
}
changelog?: string
}
export interface PostMetadata {
id?: string
html?: string
excerpt?: string
frontmatter: PostMetadataFrontmatter
fields?: PostMetadataFields
rawMarkdownBody?: string
fileAbsolutePath?: string
}

View File

@ -14,7 +14,7 @@ export interface Author {
ether: string
}
export interface SiteMetadata {
export interface Site {
siteTitle: string
siteTitleShort: string
siteDescription: string

View File

@ -1,83 +0,0 @@
import React, { useState } from 'react'
import ModalThanks from '../molecules/ModalThanks'
import styles from './PostActions.module.scss'
import { ReactComponent as Twitter } from '../../images/twitter.svg'
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
import { ReactComponent as GitHub } from '../../images/github.svg'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
const ActionContent = ({ title, text }: { title: string; text: string }) => (
<>
<h1 className={styles.actionTitle}>{title}</h1>
<p className={styles.actionText}>{text}</p>
</>
)
const ActionTwitter = ({ slug }: { slug: string }) => {
const { siteUrl } = useSiteMetadata()
return (
<a
className={styles.action}
href={`https://twitter.com/intent/tweet?text=@kremalicious&url=${siteUrl}${slug}`}
>
<Twitter />
<ActionContent title="Have a comment?" text="Hit me up @kremalicious" />
</a>
)
}
const ActionCrypto = ({ toggleModal }: { toggleModal(): void }) => (
<button className={styles.action} onClick={toggleModal}>
<Bitcoin />
<ActionContent
title="Found something useful?"
text="Say thanks with Bitcoins or Ether"
/>
</button>
)
const ActionGitHub = ({ githubLink }: { githubLink: string }) => (
<a className={styles.action} href={githubLink}>
<GitHub />
<ActionContent
title="Edit on GitHub"
text="Contribute to this post on GitHub"
/>
</a>
)
export default function PostActions({
slug,
githubLink
}: {
slug: string
githubLink: string
}) {
const [showModal, setShowModal] = useState(false)
const toggleModal = () => {
setShowModal(!showModal)
}
return (
<aside className={styles.actions}>
<div>
<ActionTwitter slug={slug} />
</div>
<div>
<ActionCrypto toggleModal={toggleModal} />
</div>
<div>
<ActionGitHub githubLink={githubLink} />
</div>
{showModal && (
<ModalThanks isOpen={showModal} handleCloseModal={toggleModal} />
)}
</aside>
)
}

View File

@ -1,22 +0,0 @@
import React from 'react'
import Image from '../atoms/Image'
import styles from './PostImage.module.scss'
import { FluidObject, FixedObject } from 'gatsby-image'
interface PostImageProps {
title?: string
fluid?: FluidObject
fixed?: FixedObject
alt: string
}
const PostImage = ({ title, fluid, fixed, alt }: PostImageProps) => (
<figure className={styles.postImage}>
<Image fluid={fluid} fixed={fixed} alt={alt} />
{title && (
<figcaption className={styles.postImageTitle}>{title}</figcaption>
)}
</figure>
)
export default PostImage

View File

@ -1,9 +1,9 @@
import React from 'react'
import ExifMap from './ExifMap'
import styles from './Exif.module.scss'
import { PostMetadataImageExif } from '../../@types/PostMetadata'
import { Exif as ExifMeta } from '../../@types/Image'
export default function Exif({ exif }: { exif: PostMetadataImageExif }) {
export default function Exif({ exif }: { exif: ExifMeta }) {
const { iso, model, fstop, shutterspeed, focalLength, exposure, gps } = exif
return (

View File

@ -10,7 +10,7 @@
@media (min-width: 940px) {
max-width: 940px;
border-radius: 0.25rem;
border-radius: $border-radius;
overflow: hidden;
}

View File

@ -1,30 +1,24 @@
import React from 'react'
import { graphql } from 'gatsby'
import Img, { FixedObject, FluidObject } from 'gatsby-image'
import Img from 'gatsby-image'
import styles from './Image.module.scss'
import { ImageProps } from '../../@types/Image'
export default function Image({
fluid,
fixed,
alt
}: {
fluid?: FluidObject
fixed?: FixedObject
alt: string
}) {
return (
<Img
className={styles.imageWrap}
backgroundColor="transparent"
fluid={fluid}
fixed={fixed}
alt={alt}
/>
)
}
export const Image = ({ fluid, fixed, alt }: ImageProps) => (
<Img
className={styles.imageWrap}
backgroundColor="transparent"
fluid={fluid}
fixed={fixed}
alt={alt}
/>
)
export const imageSizeDefault = graphql`
fragment ImageFluid on ImageSharp {
original {
src
}
fluid(maxWidth: 940, quality: 85) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
@ -33,7 +27,10 @@ export const imageSizeDefault = graphql`
export const imageSizeThumb = graphql`
fragment ImageFluidThumb on ImageSharp {
fluid(maxWidth: 200, maxHeight: 85, quality: 85, cropFocus: CENTER) {
original {
src
}
fluid(maxWidth: 400, maxHeight: 170, quality: 85, cropFocus: CENTER) {
...GatsbyImageSharpFluid_withWebp_noBase64
}
}

View File

@ -2,7 +2,7 @@ import React from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import { Helmet } from 'react-helmet'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../@types/Post'
const query = graphql`
query {
@ -117,7 +117,7 @@ export default function SEO({
slug,
postSEO
}: {
post?: PostMetadata
post?: Post
slug?: string
postSEO?: boolean
}) {

View File

@ -2,26 +2,16 @@
@import 'mixins';
.featured {
@include breakoutviewport;
@include divider;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding-left: $spacer;
padding-right: $spacer;
margin-bottom: -($spacer);
padding-bottom: $spacer * $line-height;
@media (min-width: $screen-xs) {
padding-bottom: $spacer * 3;
margin-top: $spacer * $line-height;
}
display: grid;
gap: $spacer;
grid-template-columns: 1fr 1fr;
padding-bottom: $spacer * 3;
margin-bottom: -($spacer / 2);
@media (min-width: $screen-md) {
padding-left: 0;
padding-right: 0;
justify-content: initial;
@include breakoutviewport;
}
}
@ -44,28 +34,6 @@
.featuredItem {
position: relative;
max-width: 12rem;
flex: 0 0 48%;
margin-bottom: $spacer / 2;
&:last-child {
margin-bottom: 0;
display: none;
}
@media (min-width: $screen-xs) {
flex: 1;
max-width: none;
margin-left: $spacer / 2;
&:last-child {
display: block;
}
&:first-child {
margin-left: 0;
}
}
a {
display: block;

View File

@ -1,17 +1,17 @@
import React from 'react'
import { Link, graphql, useStaticQuery } from 'gatsby'
import Image from '../atoms/Image'
import { Image } from '../atoms/Image'
import styles from './Featured.module.scss'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../@types/Post'
function FeaturedPure({
data
}: {
data: { allMarkdownRemark: { edges: [{ node: PostMetadata }] } }
data: { allMarkdownRemark: { edges: [{ node: Post }] } }
}) {
return (
<div className={styles.featured}>
{data.allMarkdownRemark.edges.map(({ node }: { node: PostMetadata }) => {
{data.allMarkdownRemark.edges.map(({ node }: { node: Post }) => {
const { title, image } = node.frontmatter
const { slug } = node.fields

View File

@ -4,7 +4,7 @@ import { Link } from 'gatsby'
import Hamburger from '../atoms/Hamburger'
import styles from './Menu.module.scss'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import { MenuItem } from '../../@types/SiteMetadata'
import { MenuItem } from '../../@types/Site'
export default function Menu() {
const [menuOpen, setMenuOpen] = useState(false)

View File

@ -52,8 +52,6 @@
}
.title {
@include heading-band;
font-size: $font-size-h3;
}

View File

@ -1,11 +1,8 @@
import React, { useState } from 'react'
import { graphql, useStaticQuery } from 'gatsby'
import PostTeaser from '../Post/PostTeaser'
import PostTeaser from '../../templates/Post/PostTeaser'
import styles from './RelatedPosts.module.scss'
import {
PostMetadata,
PostMetadataFrontmatter
} from '../../@types/PostMetadata'
import { Post, Frontmatter } from '../../@types/Post'
const query = graphql`
query {
@ -36,11 +33,11 @@ const query = graphql`
`
function postsWithDataFilter(
posts: [{ node: PostMetadata }],
key: keyof PostMetadataFrontmatter,
posts: [{ node: Post }],
key: keyof Frontmatter,
valuesToFind: string[]
) {
const newArray = posts.filter(({ node }: { node: PostMetadata }) => {
const newArray = posts.filter(({ node }: { node: Post }) => {
const frontmatterKey = node.frontmatter[key] as []
if (
@ -54,7 +51,7 @@ function postsWithDataFilter(
}
function photosWithDataFilter(posts: []) {
const newArray = posts.filter((post: { node: PostMetadata }) => {
const newArray = posts.filter((post: { node: Post }) => {
const { fileAbsolutePath } = post.node
if (fileAbsolutePath.includes('content/photos')) {
@ -93,7 +90,7 @@ export default function RelatedPosts({
{filteredPosts
.sort(() => 0.5 - Math.random())
.slice(0, 6)
.map(({ node }: { node: PostMetadata }) => (
.map(({ node }: { node: Post }) => (
<PostTeaser key={node.id} post={node} />
))}
</ul>

View File

@ -1,5 +1,5 @@
import React from 'react'
import { ReactComponent as SearchIcon } from '../../images/magnifying-glass.svg'
import { ReactComponent as SearchIcon } from '../../../images/magnifying-glass.svg'
import styles from './SearchButton.module.scss'
const SearchButton = (props: any) => (

View File

@ -1,7 +1,7 @@
@import 'variables';
.searchInput {
composes: input from '../atoms/Input.module.scss';
composes: input from '../../atoms/Input.module.scss';
&::-webkit-search-cancel-button {
display: none;

View File

@ -1,5 +1,5 @@
import React from 'react'
import Input from '../atoms/Input'
import Input from '../../atoms/Input'
import styles from './SearchInput.module.scss'
export default function SearchInput({

View File

@ -1,11 +1,11 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { graphql, useStaticQuery } from 'gatsby'
import Container from '../atoms/Container'
import PostTeaser from '../Post/PostTeaser'
import Container from '../../atoms/Container'
import PostTeaser from '../../../templates/Post/PostTeaser'
import SearchResultsEmpty from './SearchResultsEmpty'
import styles from './SearchResults.module.scss'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../../@types/Post'
export interface Results {
slug: string
@ -40,7 +40,7 @@ function SearchResultsPure({
toggleSearch,
posts
}: {
posts: [{ node: PostMetadata }]
posts: [{ node: Post }]
searchQuery: string
results: Results[]
toggleSearch(): void
@ -53,10 +53,9 @@ function SearchResultsPure({
{results.map((page: { slug: string }) =>
posts
.filter(
({ node }: { node: PostMetadata }) =>
node.fields.slug === page.slug
({ node }: { node: Post }) => node.fields.slug === page.slug
)
.map(({ node }: { node: PostMetadata }) => (
.map(({ node }: { node: Post }) => (
<PostTeaser
key={page.slug}
post={node}

View File

@ -16,44 +16,34 @@ export const alertMessages = (
success: 'Confirmed. You are awesome, thanks!'
})
export default function Alerts({
transactionHash,
message
}: {
transactionHash: string | null
message: { text?: string; status?: string } | null
}) {
const constructMessage = () => {
let messageOutput
interface AlertProps {
transactionHash: string
message?: { text?: string; status?: string }
}
if (transactionHash) {
messageOutput =
message &&
message.text +
'<br />' +
alertMessages(null, transactionHash).transaction
} else {
messageOutput = message && message.text
}
const constructMessage = (
transactionHash: string,
message?: { text?: string }
) =>
transactionHash
? message &&
message.text + '<br />' + alertMessages(null, transactionHash).transaction
: message && message.text
return messageOutput
}
const classes = () => {
const { status } = message
if (status === 'success') {
return styles.success
} else if (status === 'error') {
return styles.error
}
return styles.alert
}
const classes = (status: string) =>
status === 'success'
? styles.success
: status === 'error'
? styles.error
: styles.alert
export default function Alerts({ transactionHash, message }: AlertProps) {
return (
<div
className={classes()}
dangerouslySetInnerHTML={{ __html: `${constructMessage()}` }}
className={classes(message.status)}
dangerouslySetInnerHTML={{
__html: `${constructMessage(transactionHash, message)}`
}}
/>
)
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import Input from '../atoms/Input'
import Input from '../../atoms/Input'
import Account from './Account'
import Conversion from './Conversion'
import styles from './InputGroup.module.scss'

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react'
import Container from '../atoms/Container'
import Vcard from '../molecules/Vcard'
import ThemeSwitch from '../molecules/ThemeSwitch'
import ModalThanks from '../molecules/ModalThanks'
import ModalThanks from './ModalThanks'
import { ReactComponent as Github } from '../../images/github.svg'
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'

View File

@ -1,7 +1,7 @@
import React from 'react'
import { Link } from 'gatsby'
import Container from '../atoms/Container'
import Search from '../Search'
import Search from '../molecules/Search'
import Menu from '../molecules/Menu'
import { ReactComponent as Logo } from '../../images/logo.svg'

View File

@ -1,14 +1,26 @@
import React, { lazy, Suspense } from 'react'
import shortid from 'shortid'
import { Author } from '../../@types/Site'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import Qr from '../atoms/Qr'
import Modal from '../atoms/Modal'
import styles from './ModalThanks.module.scss'
const Web3Donation = lazy(() => import('../Web3Donation'))
const Qr = lazy(() => import('../atoms/Qr'))
const Web3Donation = lazy(() => import('../molecules/Web3Donation'))
const Coin = ({ address, author }: { address: string; author: Author }) => (
<div className={styles.coin}>
<Suspense fallback={<div>Loading...</div>}>
<Qr title={address} address={(author as any)[address]} />
</Suspense>
</div>
)
export default function ModalThanks(props: any) {
const { author } = useSiteMetadata()
const coins = Object.keys(author).filter(
key => key === 'bitcoin' || key === 'ether'
)
return (
<Modal
@ -26,15 +38,9 @@ export default function ModalThanks(props: any) {
<p>Send Bitcoin or Ether from any wallet.</p>
</header>
{Object.keys(author)
.filter(key => key === 'bitcoin' || key === 'ether')
.map((address: string, i: number) => (
<div key={i} className={styles.coin}>
<Suspense fallback={<div>Loading...</div>}>
<Qr title={address} address={(author as any)[address]} />
</Suspense>
</div>
))}
{coins.map((address: string) => (
<Coin key={shortid.generate()} address={address} author={author} />
))}
</div>
</Modal>
)

View File

@ -1,7 +1,7 @@
import { useStaticQuery, graphql } from 'gatsby'
import { SiteMetadata } from '../@types/SiteMetadata'
import { Site } from '../@types/Site'
export function useSiteMetadata(): SiteMetadata {
export function useSiteMetadata(): Site {
const query = graphql`
query {
site {

View File

@ -1,9 +1,9 @@
import React from 'react'
import { graphql, Link } from 'gatsby'
import PostImage from '../components/Post/PostImage'
import PostImage from '../templates/Post/PostImage'
import Page from '../templates/Page'
import styles from './goodies.module.scss'
import { PostMetadata } from '../@types/PostMetadata'
import { Post } from '../@types/Post'
const page = {
frontmatter: {
@ -13,7 +13,7 @@ const page = {
}
}
const GoodiesThumb = ({ post }: { post: PostMetadata }) => {
const GoodiesThumb = ({ post }: { post: Post }) => {
const { title, image } = post.frontmatter
const { slug } = post.fields
@ -33,7 +33,7 @@ export default function Goodies({
data,
location
}: {
data: { goodies: { edges: [{ node: PostMetadata }] } }
data: { goodies: { edges: [{ node: Post }] } }
location: Location
}) {
return (

View File

@ -1,9 +1,9 @@
import React from 'react'
import { graphql, Link } from 'gatsby'
import Page from '../templates/Page'
import PostImage from '../components/Post/PostImage'
import PostImage from '../templates/Post/PostImage'
import styles from './photos.module.scss'
import { PostMetadata } from '../@types/PostMetadata'
import { Post } from '../@types/Post'
const page = {
frontmatter: {
@ -12,7 +12,7 @@ const page = {
}
}
const PhotoThumb = ({ photo }: { photo: PostMetadata }) => {
const PhotoThumb = ({ photo }: { photo: Post }) => {
const { title, image } = photo.frontmatter
const { slug } = photo.fields
const { fluid } = image.childImageSharp
@ -32,7 +32,7 @@ export default function Photos({
data,
location
}: {
data: { photos: { edges: [{ node: PostMetadata }] } }
data: { photos: { edges: [{ node: Post }] } }
location: Location
}) {
return (

View File

@ -1,32 +0,0 @@
.gatsby-resp-image-figure,
.gatsby-resp-image-wrapper {
margin-bottom: $spacer;
}
.anchor {
margin-top: $spacer / 3;
font-size: $font-size-large;
color: $brand-grey-light;
font-weight: 700;
span {
transition: opacity 0.2s ease-out;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
.anchor span {
opacity: 0;
}
&:hover {
.anchor span {
opacity: 1;
}
}
}

View File

@ -1,57 +1,5 @@
@import 'variables';
// Centering Blocks
/////////////////////////////////////
@mixin aligncenter() {
display: block;
margin-left: auto;
margin-right: auto;
}
// Toggling content
/////////////////////////////////////
// Hide from both screenreaders and browsers: h5bp.com/u
@mixin hide() {
display: none !important;
visibility: hidden;
}
@mixin show() {
display: block;
visibility: visible;
}
// Hide only visually, but have it available for screenreaders: h5bp.com/v
@mixin visuallyhidden() {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
// Extends the .visuallyhidden class to allow the
// element to be focusable when navigated to via the keyboard: h5bp.com/p
&.focusable:active,
&.focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto;
}
}
// Hide visually and from screenreaders, but maintain layout
@mixin invisible() {
visibility: hidden;
}
// CSS image replacement
/////////////////////////////////////
@ -132,20 +80,6 @@
}
}
// Heading band
/////////////////////////////////////
@mixin heading-band() {
display: inline-block;
background: rgba(255, 255, 255, 0.5);
padding: ($spacer/2) $spacer ($spacer/2) 100%;
margin-left: -100%;
:global(.dark) & {
background: darken($body-background-color--dark, 2%);
}
}
// Layout breakout
/////////////////////////////////////

View File

@ -68,7 +68,7 @@ $font-family-monospace: 'Fira Code', 'Fira Mono', Menlo, Monaco, Consolas,
$font-family-headings: 'brandon-grotesque', 'Avenir Next', 'Helvetica Neue',
Helvetica, Arial, sans-serif;
$font-weight-headings: 500;
$font-weight-headings: 700;
$line-height-headings: 1.1;
$color-headings: $brand-main;
@ -81,7 +81,7 @@ $spacer: ($font-size-base * $line-height);
$padding-base-vertical: 0.75rem;
$padding-base-horizontal: 1.25rem;
$border-radius: 3px;
$border-radius: 0.25rem;
// Code
/////////////////////////////////////

View File

@ -26,10 +26,7 @@ body {
line-height: $line-height;
color: $text-color;
text-rendering: optimizeLegibility;
letter-spacing: -0.01em;
font-feature-settings: 'liga', 'kern';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
transition: background 0.4s $easing;
background: $body-background-color;
@ -105,20 +102,6 @@ a {
// Headings
/////////////////////////////////////
h1,
h2 {
margin-top: $spacer * $line-height;
margin-bottom: $spacer * 2;
}
h3,
h4,
h5,
h6 {
margin-top: $spacer * $line-height;
margin-bottom: $spacer;
}
h1 {
font-size: $font-size-h2;
@ -128,8 +111,6 @@ h1 {
}
h2 {
@include heading-band();
font-size: $font-size-h3;
@media (min-width: $screen-xs) {
@ -174,7 +155,11 @@ h6 {
font-family: $font-family-headings;
line-height: $line-height-headings;
font-weight: $font-weight-headings;
letter-spacing: -0.02em;
letter-spacing: -0.01em;
margin-top: $spacer * $line-height;
margin-bottom: $spacer;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
// stylelint-disable no-descending-specificity
&,
@ -376,4 +361,3 @@ blockquote {
@import 'code';
@import 'buttons';
@import 'alerts';
@import 'content';

View File

@ -3,7 +3,7 @@ import { Helmet } from 'react-helmet'
import SEO from '../components/atoms/SEO'
import Layout from '../components/Layout'
import styles from './Page.module.scss'
import { PostMetadata } from '../@types/PostMetadata'
import { Post } from '../@types/Post'
export default function Page({
title,
@ -16,7 +16,7 @@ export default function Page({
children: any
section?: string
location?: Location
post?: PostMetadata
post?: Post
}) {
return (
<>

View File

@ -1,26 +0,0 @@
@import 'variables';
@import 'mixins';
.hentry {
width: 100%;
padding-top: $spacer;
padding-bottom: $spacer * 3;
> a {
display: block;
}
&:only-child {
padding-bottom: $spacer;
}
}
.postImageWrap {
@include breakoutviewport();
figure {
max-width: none;
margin-top: $spacer * 1.5;
margin-bottom: 0;
}
}

View File

@ -9,32 +9,16 @@
padding-top: $spacer;
padding-bottom: $spacer;
border-radius: $border-radius;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
display: grid;
gap: $spacer / 2;
@media (min-width: $screen-sm) {
grid-template-columns: repeat(3, 1fr);
}
:global(.dark) & {
background: darken($body-background-color--dark, 2%);
}
> div {
flex: 1 1 100%;
border-bottom: 1px dashed rgba($brand-grey-light, 0.3);
&:last-child {
border-bottom: 0;
}
@media (min-width: $screen-sm) {
flex: 0 0 33%;
border-bottom: 0;
border-left: 1px dashed rgba($brand-grey-light, 0.3);
&:first-child {
border-left: 0;
}
}
}
}
.link {
@ -66,9 +50,25 @@
padding-right: $spacer;
position: relative;
text-align: left;
border-bottom: 1px dashed rgba($brand-grey-light, 0.3);
&:last-child {
border-bottom: 0;
}
@media (min-width: $screen-sm) {
border-bottom: 0;
border-left: 1px dashed rgba($brand-grey-light, 0.3);
&:first-child {
border-left: 0;
}
}
&:hover,
&:focus {
cursor: pointer;
.link,
.actionTitle,
.actionText {

View File

@ -0,0 +1,74 @@
import React, { useState } from 'react'
import ModalThanks from '../../components/organisms/ModalThanks'
import styles from './PostActions.module.scss'
import { ReactComponent as Twitter } from '../../images/twitter.svg'
import { ReactComponent as Bitcoin } from '../../images/bitcoin.svg'
import { ReactComponent as GitHub } from '../../images/github.svg'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
interface ActionProps {
title: string
text: string
url?: string
onClick?(): void
}
const Icon = ({ text }: { text: string }) =>
text.includes('GitHub') ? (
<GitHub />
) : text.includes('Bitcoin') ? (
<Bitcoin />
) : (
<Twitter />
)
const Action = ({ title, text, url, onClick }: ActionProps) => {
return (
<a className={styles.action} href={url} onClick={onClick}>
<Icon text={text} />
<h1 className={styles.actionTitle}>{title}</h1>
<p className={styles.actionText}>{text}</p>
</a>
)
}
export default function PostActions({
slug,
githubLink
}: {
slug: string
githubLink: string
}) {
const { siteUrl } = useSiteMetadata()
const [showModal, setShowModal] = useState(false)
const urlTwitter = `https://twitter.com/intent/tweet?text=@kremalicious&url=${siteUrl}${slug}`
const toggleModal = () => {
setShowModal(!showModal)
}
return (
<aside className={styles.actions}>
<Action
title="Have a comment?"
text="Hit me up @kremalicious"
url={urlTwitter}
/>
<Action
title="Found something useful?"
text="Say thanks with Bitcoins or Ether"
onClick={toggleModal}
/>
<Action
title="Edit on GitHub"
text="Contribute to this post on GitHub"
url={githubLink}
/>
{showModal && (
<ModalThanks isOpen={showModal} handleCloseModal={toggleModal} />
)}
</aside>
)
}

View File

@ -1,9 +1,9 @@
import React from 'react'
import Changelog from '../atoms/Changelog'
import { PostMetadata } from '../../@types/PostMetadata'
import Changelog from '../../components/atoms/Changelog'
import { Post } from '../../@types/Post'
// Remove lead paragraph from content
const PostContent = ({ post }: { post: PostMetadata }) => {
const PostContent = ({ post }: { post: Post }) => {
const separator = '<!-- more -->'
const changelog = post.frontmatter.changelog

View File

@ -1,7 +1,7 @@
@import 'variables';
@import 'mixins';
.postImageTitle {
.imageTitle {
transition: 0.1s ease-out;
font-size: $font-size-h3;
font-family: $font-family-headings;
@ -9,7 +9,6 @@
font-weight: $font-weight-headings;
font-style: normal;
text-align: left;
letter-spacing: -0.02em;
margin: 0;
position: absolute;
top: 10%;
@ -22,7 +21,7 @@
transform: translate3d(0, -20px, 0);
}
.postImage {
.image {
display: block;
a & {
@ -31,7 +30,7 @@
}
a:hover & {
.postImageTitle {
.imageTitle {
opacity: 1;
transform: translate3d(0, 0, 0);
}

View File

@ -0,0 +1,13 @@
import React from 'react'
import { Image } from '../../components/atoms/Image'
import styles from './PostImage.module.scss'
import { ImageProps } from '../../@types/Image'
const PostImage = ({ title, fluid, fixed, alt, original }: ImageProps) => (
<figure className={styles.image} data-original={original && original.src}>
<Image fluid={fluid} fixed={fixed} alt={alt} />
{title && <figcaption className={styles.imageTitle}>{title}</figcaption>}
</figure>
)
export default PostImage

View File

@ -1,10 +1,10 @@
import React from 'react'
import styles from './PostLead.module.scss'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../@types/Post'
// Extract lead paragraph from content
// Grab everything before more tag, or just first paragraph
const PostLead = ({ post, index }: { post: PostMetadata; index?: boolean }) => {
const PostLead = ({ post, index }: { post: Post; index?: boolean }) => {
let lead
const content = post.html
const separator = '<!-- more -->'

View File

@ -1,12 +1,12 @@
import React from 'react'
import { Link } from 'gatsby'
import slugify from 'slugify'
import Time from '../atoms/Time'
import Time from '../../components/atoms/Time'
import { useSiteMetadata } from '../../hooks/use-site-metadata'
import styles from './PostMeta.module.scss'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../@types/Post'
export default function PostMeta({ post }: { post: PostMetadata }) {
export default function PostMeta({ post }: { post: Post }) {
const siteMeta = useSiteMetadata()
const { author, updated, tags, type } = post.frontmatter
const { date } = post.fields

View File

@ -1,14 +1,14 @@
import React from 'react'
import { Link } from 'gatsby'
import Image from '../atoms/Image'
import { Image } from '../../components/atoms/Image'
import styles from './PostTeaser.module.scss'
import { PostMetadata } from '../../@types/PostMetadata'
import { Post } from '../../@types/Post'
export default function PostTeaser({
post,
toggleSearch
}: {
post: PostMetadata
post: Post
toggleSearch?: () => void
}) {
const { image, title } = post.frontmatter

View File

@ -0,0 +1,63 @@
@import 'variables';
@import 'mixins';
.hentry {
width: 100%;
padding-top: $spacer;
padding-bottom: $spacer * 3;
> a {
display: block;
}
&:only-child {
padding-bottom: $spacer;
}
.gatsby-resp-image-figure,
.gatsby-resp-image-wrapper {
margin-bottom: $spacer;
}
:global(.anchor) {
margin-top: $spacer / 3;
font-size: $font-size-large;
color: $brand-grey-light;
&:hover,
&:focus {
color: $link-color;
}
span {
transition: opacity 0.2s ease-out;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
:global(.anchor) span {
opacity: 0;
}
&:hover {
:global(.anchor) span {
opacity: 1;
}
}
}
}
.imageWrap {
@include breakoutviewport();
figure {
max-width: none;
margin-top: $spacer * 1.5;
margin-bottom: 0;
}
}

View File

@ -1,19 +1,19 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { graphql } from 'gatsby'
import Layout from '../components/Layout'
import PostImage from '../components/Post/PostImage'
import PostTitle from '../components/Post/PostTitle'
import PostLead from '../components/Post/PostLead'
import PostContent from '../components/Post/PostContent'
import PostActions from '../components/Post/PostActions'
import PostLinkActions from '../components/Post/PostLinkActions'
import SEO from '../components/atoms/SEO'
import PostMeta from '../components/Post/PostMeta'
import Exif from '../components/atoms/Exif'
import RelatedPosts from '../components/molecules/RelatedPosts'
import styles from './Post.module.scss'
import { PostMetadata } from '../@types/PostMetadata'
import Layout from '../../components/Layout'
import PostImage from './PostImage'
import PostTitle from './PostTitle'
import PostLead from './PostLead'
import PostContent from './PostContent'
import PostActions from './PostActions'
import PostLinkActions from './PostLinkActions'
import SEO from '../../components/atoms/SEO'
import PostMeta from './PostMeta'
import Exif from '../../components/atoms/Exif'
import RelatedPosts from '../../components/molecules/RelatedPosts'
import styles from './index.module.scss'
import { Post as PostMetadata } from '../../@types/Post'
export default function Post({
data,
@ -40,8 +40,12 @@ export default function Post({
{type === 'post' && <PostLead post={post} />}
{type === 'photo' && <PostContent post={post} />}
{image && (
<div className={styles.postImageWrap}>
<PostImage fluid={image.childImageSharp.fluid} alt={title} />
<div className={styles.imageWrap}>
<PostImage
fluid={image.childImageSharp.fluid}
alt={title}
original={image.childImageSharp.original}
/>
</div>
)}
{image && image.fields && <Exif exif={image.fields.exif} />}

View File

@ -9,28 +9,13 @@
padding-top: $spacer * 2;
padding-bottom: $spacer * 2;
@media (min-width: $screen-sm) {
padding-top: $spacer * 3;
padding-bottom: $spacer * 3;
&:first-of-type {
padding-top: $spacer * 4;
}
}
:global(.gatsby-image-wrapper) {
max-height: 100vh;
}
}
.archiveTitle {
@include heading-band();
font-size: $font-size-h3;
margin-top: 0;
margin-bottom: 0;
@media (min-width: $screen-md) {
margin-left: -117%;
}
}

View File

@ -1,25 +1,25 @@
import React from 'react'
import { Link, graphql } from 'gatsby'
import Layout from '../components/Layout'
import PostImage from '../components/Post/PostImage'
import PostTitle from '../components/Post/PostTitle'
import PostLead from '../components/Post/PostLead'
import PostContent from '../components/Post/PostContent'
import PostMore from '../components/Post/PostMore'
import PostLinkActions from '../components/Post/PostLinkActions'
import PostImage from './Post/PostImage'
import PostTitle from './Post/PostTitle'
import PostLead from './Post/PostLead'
import PostContent from './Post/PostContent'
import PostMore from './Post/PostMore'
import PostLinkActions from './Post/PostLinkActions'
import SEO from '../components/atoms/SEO'
import Pagination from '../components/molecules/Pagination'
import Featured from '../components/molecules/Featured'
import styles from './Posts.module.scss'
import stylesPost from './Post.module.scss'
import { PostMetadata } from '../@types/PostMetadata'
import stylesPost from './Post/index.module.scss'
import { Post } from '../@types/Post'
export default function Posts({
data,
location,
pageContext
}: {
data: { allMarkdownRemark: { edges: [{ node: PostMetadata }] } }
data: { allMarkdownRemark: { edges: [{ node: Post }] } }
location: Location
pageContext: {
tag: string
@ -31,7 +31,7 @@ export default function Posts({
const edges = data.allMarkdownRemark.edges
const { tag, currentPageNumber, numPages, nextPage } = pageContext
const PostsList = edges.map(({ node }: { node: PostMetadata }) => {
const PostsList = edges.map(({ node }: { node: Post }) => {
const { type, linkurl, title, image } = node.frontmatter
const { slug } = node.fields
@ -43,11 +43,12 @@ export default function Posts({
{image && (
<Link to={slug} title={title}>
<div className={stylesPost.postImageWrap}>
<div className={stylesPost.imageWrap}>
<PostImage
title={type === 'photo' ? title : null}
fluid={image.childImageSharp.fluid}
alt={title}
original={image.childImageSharp.original}
/>
</div>
</Link>