Merge branch 'backdrop-blur' into experimental-typography-focus-hero

This commit is contained in:
Chuck Dries 2021-07-16 16:16:49 -07:00
commit 9db0296b04
35 changed files with 931 additions and 775 deletions

View File

@ -2,29 +2,24 @@ module.exports = {
globals: { globals: {
__PATH_PREFIX__: true, __PATH_PREFIX__: true,
}, },
'parser': 'babel-eslint', // uses babel-eslint transforms parser: "babel-eslint", // uses babel-eslint transforms
'settings': { settings: {
'react': { react: {
'version': 'detect', // detect react version version: "detect", // detect react version
}, },
}, },
'env': { env: {
'node': true, // defines things like process.env when generating through node node: true, // defines things like process.env when generating through node
'browser': true browser: true,
}, },
'extends': [ extends: [
'eslint:recommended', // use recommended configs "eslint:recommended", // use recommended configs
'plugin:react/recommended', "plugin:react/recommended",
'plugin:react-hooks/recommended', "plugin:react-hooks/recommended",
], ],
'rules': { rules: {
'react/prop-types': 0, "react/prop-types": 0,
'quotes': ['warn', 'single'], "no-unused-vars": 1,
'semi': 1, "react/jsx-sort-props": 1,
'indent': ['warn', 2],
'comma-dangle': ['warn', 'always-multiline'],
'no-unused-vars': 1,
'jsx-quotes': 1,
'react/jsx-sort-props': 1,
}, },
}; };

5
.prettierignore Normal file
View File

@ -0,0 +1,5 @@
.cache
node_modules
old\ website
public
gatsby

1
.prettierrc.json Normal file
View File

@ -0,0 +1 @@
{}

11
.vscode/launch.json vendored
View File

@ -1,6 +1,7 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [{ "configurations": [
{
"name": "Launch Chrome", "name": "Launch Chrome",
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",
@ -18,9 +19,7 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": null, "preLaunchTask": null,
"runtimeExecutable": null, "runtimeExecutable": null,
"runtimeArgs": [ "runtimeArgs": ["--nolazy"],
"--nolazy"
],
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"
}, },
@ -38,9 +37,7 @@
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"preLaunchTask": null, "preLaunchTask": null,
"runtimeExecutable": null, "runtimeExecutable": null,
"runtimeArgs": [ "runtimeArgs": ["--nolazy"],
"--nolazy"
],
"env": { "env": {
"NODE_ENV": "development" "NODE_ENV": "development"
}, },

View File

@ -1,10 +1,12 @@
- Gallery - Gallery
- [x] Custom image metadata - [x] Custom image metadata
- [ ] add metadata to image files - [ ] add metadata to image files
- [ ] image details page (modal route?) - [ ] image details page (modal route?)
- [ ] gallery page - [ ] gallery page
- Homepage - Homepage
- [ ] homepage basic layout - [ ] homepage basic layout
- [ ] homepage aesthetics - [ ] homepage aesthetics
- [ ] homepage to gallery page transition - [ ] homepage to gallery page transition
@ -14,6 +16,7 @@
- [ ] About page? - [ ] About page?
- Portfolio/Projects page - Portfolio/Projects page
- basic layout - basic layout
- source data - source data
- aesthetics - aesthetics

View File

@ -1,17 +1,17 @@
import './src/styles/global.css'; import "./src/styles/global.css";
import posthog from 'posthog-js'; import posthog from "posthog-js";
const env = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development'; const env =
if (env === 'production') { process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
posthog.init('HR8Gte105aCHNx2BqhL1XkbvH9kzKGptxjkbhuTj6Ek', { api_host: 'https://posthog.chuckdries.com' }); if (env === "production") {
posthog.init("HR8Gte105aCHNx2BqhL1XkbvH9kzKGptxjkbhuTj6Ek", {
api_host: "https://posthog.chuckdries.com",
});
} }
export const onRouteUpdate = function () { export const onRouteUpdate = function () {
if ( if (env === "production" && typeof window.plausible === "object") {
env === 'production' && window.plausible("pageview");
typeof window.plausible === 'object' posthog.capture("$pageview");
) {
window.plausible('pageview');
posthog.capture('$pageview');
} }
}; };
// import * as React from 'react'; // import * as React from 'react';

View File

@ -1,56 +1,56 @@
module.exports = { module.exports = {
siteMetadata: { siteMetadata: {
title: 'Chuck Dries', title: "Chuck Dries",
siteUrl: 'https://chuckdries.com', siteUrl: "https://chuckdries.com",
}, },
plugins: [ plugins: [
'gatsby-plugin-sass', "gatsby-plugin-sass",
'gatsby-plugin-image', "gatsby-plugin-image",
'gatsby-plugin-react-helmet', "gatsby-plugin-react-helmet",
{ {
resolve: 'gatsby-plugin-manifest', resolve: "gatsby-plugin-manifest",
options: { options: {
icon: 'src/images/glasses-outline.svg', icon: "src/images/glasses-outline.svg",
}, },
}, },
'gatsby-plugin-mdx', "gatsby-plugin-mdx",
'gatsby-plugin-sharp', "gatsby-plugin-sharp",
'gatsby-transformer-sharp', "gatsby-transformer-sharp",
'gatsby-plugin-postcss', "gatsby-plugin-postcss",
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'images', name: "images",
path: './src/images/', path: "./src/images/",
}, },
__key: 'images', __key: "images",
}, },
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'gallery', name: "gallery",
path: './data/gallery/', path: "./data/gallery/",
}, },
__key: 'gallery', __key: "gallery",
}, },
{ {
resolve: 'gatsby-source-filesystem', resolve: "gatsby-source-filesystem",
options: { options: {
name: 'pages', name: "pages",
path: './src/pages/', path: "./src/pages/",
}, },
__key: 'pages', __key: "pages",
}, },
{ {
resolve: 'gatsby-plugin-eslint', resolve: "gatsby-plugin-eslint",
options: { options: {
stages: ['develop'], stages: ["develop"],
extensions: ['js', 'jsx'], extensions: ["js", "jsx"],
exclude: ['node_modules', '.cache', 'public'], exclude: ["node_modules", ".cache", "public"],
// Any eslint-webpack-plugin options below // Any eslint-webpack-plugin options below
}, },
}, },
'gatsby-plugin-preval', "gatsby-plugin-preval",
'gatsby-plugin-robots-txt', "gatsby-plugin-robots-txt",
], ],
}; };

View File

@ -1,12 +1,12 @@
const fs = require('fs'); const fs = require("fs");
const util = require('util'); const util = require("util");
const path = require('path'); const path = require("path");
const { read } = require('fast-exif'); const { read } = require("fast-exif");
const iptc = require('node-iptc'); const iptc = require("node-iptc");
const Vibrant = require('node-vibrant'); const Vibrant = require("node-vibrant");
const chroma = require('chroma-js'); const chroma = require("chroma-js");
const chalk = require('chalk'); const chalk = require("chalk");
const R = require('ramda'); const R = require("ramda");
const readFile = util.promisify(fs.readFile); const readFile = util.promisify(fs.readFile);
@ -15,9 +15,11 @@ const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;
const logColorsWithContrast = (color1, color2, text) => { const logColorsWithContrast = (color1, color2, text) => {
const c1hex = color1.hex(); const c1hex = color1.hex();
const c2hex = color2.hex(); const c2hex = color2.hex();
console.log(chalk.hex(c1hex).bgHex(c2hex)( console.log(
chalk.hex(c1hex).bgHex(c2hex)(
`${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}` `${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}`
)); )
);
}; };
function processColors(vibrantData, imagePath) { function processColors(vibrantData, imagePath) {
@ -29,7 +31,10 @@ function processColors(vibrantData, imagePath) {
let LightMuted = chroma(vibrantData.LightMuted.getRgb()); let LightMuted = chroma(vibrantData.LightMuted.getRgb());
// first pass - darken bg and lighten relevant fg colors // first pass - darken bg and lighten relevant fg colors
if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) { if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
DarkVibrant = DarkVibrant.darken(); DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten(); Vibrant = Vibrant.brighten();
@ -39,7 +44,10 @@ function processColors(vibrantData, imagePath) {
} }
} }
// second pass - first doesn't always do enough // second pass - first doesn't always do enough
if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) { if (
badContrast(DarkVibrant, Vibrant) ||
badContrast(DarkVibrant, LightMuted)
) {
// DarkVibrant = DarkVibrant.darken(); // DarkVibrant = DarkVibrant.darken();
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
Vibrant = Vibrant.brighten(2); Vibrant = Vibrant.brighten(2);
@ -50,15 +58,14 @@ function processColors(vibrantData, imagePath) {
} }
if (badContrast(DarkVibrant, Vibrant)) { if (badContrast(DarkVibrant, Vibrant)) {
console.warn('contrast still too low', imagePath); console.warn("contrast still too low", imagePath);
logColorsWithContrast(Vibrant, DarkVibrant, 'V-DV'); logColorsWithContrast(Vibrant, DarkVibrant, "V-DV");
} }
if (badContrast(DarkVibrant, LightMuted)) { if (badContrast(DarkVibrant, LightMuted)) {
console.warn('contrast still too low', imagePath); console.warn("contrast still too low", imagePath);
logColorsWithContrast(LightMuted, DarkVibrant, 'LM-DV'); logColorsWithContrast(LightMuted, DarkVibrant, "LM-DV");
} }
return { return {
Vibrant: Vibrant.rgb(), Vibrant: Vibrant.rgb(),
DarkVibrant: DarkVibrant.rgb(), DarkVibrant: DarkVibrant.rgb(),
@ -82,25 +89,20 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
const gps = { longitude: null, latitude: null }; const gps = { longitude: null, latitude: null };
if (exifData) { if (exifData) {
if ( if (exifData.gps && exifData.gps.GPSLongitude && exifData.gps.GPSLatitude) {
exifData.gps &&
exifData.gps.GPSLongitude &&
exifData.gps.GPSLatitude
) {
gps.longitude = convertDMSToDD( gps.longitude = convertDMSToDD(
exifData.gps.GPSLongitude, exifData.gps.GPSLongitude,
exifData.gps.GPSLongitudeRef === 'E' exifData.gps.GPSLongitudeRef === "E"
); );
gps.latitude = convertDMSToDD( gps.latitude = convertDMSToDD(
exifData.gps.GPSLatitude, exifData.gps.GPSLatitude,
exifData.gps.GPSLatitudeRef === 'N' exifData.gps.GPSLatitudeRef === "N"
); );
} }
} }
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null; const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
return { return {
exif: exifData?.exif, exif: exifData?.exif,
gps, gps,
@ -110,10 +112,9 @@ function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
}; };
} }
exports.onCreateNode = async function ({ node, actions }) { exports.onCreateNode = async function ({ node, actions }) {
const { createNodeField } = actions; const { createNodeField } = actions;
if (node.internal.type === 'File' && node.sourceInstanceName === 'gallery') { if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
const file = await readFile(node.absolutePath); const file = await readFile(node.absolutePath);
const iptcData = iptc(file); const iptcData = iptc(file);
const exifData = await read(node.absolutePath); const exifData = await read(node.absolutePath);
@ -123,8 +124,13 @@ exports.onCreateNode = async function ({ node, actions }) {
createNodeField({ createNodeField({
node, node,
name: 'imageMeta', name: "imageMeta",
value: transformMetaToNodeData(exifData, iptcData, vibrantData, node.absolutePath), value: transformMetaToNodeData(
exifData,
iptcData,
vibrantData,
node.absolutePath
),
}); });
} }
}; };
@ -136,13 +142,11 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
// get all images // get all images
const galleryImages = await graphql(` const galleryImages = await graphql(`
{ {
allFile(filter: { allFile(filter: { sourceInstanceName: { eq: "gallery" } }) {
sourceInstanceName: { eq: "gallery" }}
) {
edges { edges {
node { node {
relativePath, relativePath
base, base
fields { fields {
imageMeta { imageMeta {
dateTaken dateTaken
@ -155,25 +159,28 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
`); `);
// Handle errors // Handle errors
if (galleryImages.errors) { if (galleryImages.errors) {
reporter.panicOnBuild('Error while running GraphQL query.'); reporter.panicOnBuild("Error while running GraphQL query.");
return; return;
} }
// Create pages for each markdown file. // Create pages for each markdown file.
const galleryImageTemplate = path.resolve('src/components/GalleryImage/GalleryImage.js'); const galleryImageTemplate = path.resolve(
"src/components/GalleryImage/GalleryImage.js"
);
// const diffDate = (a, b) => // const diffDate = (a, b) =>
// new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime(); // new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime();
const edges = R.sort(R.descend((edge) => const edges = R.sort(
new Date(R.path(['node', 'fields', 'imageMeta', 'dateTaken'], edge))), R.descend(
galleryImages.data.allFile.edges); (edge) =>
new Date(R.path(["node", "fields", "imageMeta", "dateTaken"], edge))
),
galleryImages.data.allFile.edges
);
edges.forEach(({ node }, index) => { edges.forEach(({ node }, index) => {
const nextImage = index === edges.length - 1 const nextImage =
? null index === edges.length - 1 ? null : edges[index + 1].node.base;
: edges[index + 1].node.base; const prevImage = index === 0 ? null : edges[index - 1].node.base;
const prevImage = index === 0
? null
: edges[index - 1].node.base;
const page = { const page = {
path: `photogallery/${node.base}`, path: `photogallery/${node.base}`,
component: galleryImageTemplate, component: galleryImageTemplate,

View File

@ -1 +1 @@
import './src/styles/global.css'; import "./src/styles/global.css";

View File

@ -17,7 +17,8 @@
"lint": "eslint --ext .jsx,.js src", "lint": "eslint --ext .jsx,.js src",
"upload": "rsync -rz --delete public/ ci@droplet.chuckdries.com:www/personal-website", "upload": "rsync -rz --delete public/ ci@droplet.chuckdries.com:www/personal-website",
"upload-staging": "rsync -rz --delete public/ ci@droplet.chuckdries.com:www/personal-website-staging", "upload-staging": "rsync -rz --delete public/ ci@droplet.chuckdries.com:www/personal-website-staging",
"deploy": "yarn build && yarn upload" "deploy": "yarn build && yarn upload",
"pretty": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@mdx-js/mdx": "^1.6.22", "@mdx-js/mdx": "^1.6.22",
@ -64,5 +65,7 @@
"tailwindcss": "^2.1.2", "tailwindcss": "^2.1.2",
"use-breakpoint": "^2.0.1" "use-breakpoint": "^2.0.1"
}, },
"devDependencies": {} "devDependencies": {
"prettier": "2.3.2"
}
} }

View File

@ -1,7 +1,7 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
'postcss-nested': {}, "postcss-nested": {},
autoprefixer: {}, autoprefixer: {},
}, },
}; };

View File

@ -1,4 +1,4 @@
import preval from 'babel-plugin-preval/macro'; import preval from "babel-plugin-preval/macro";
const themeBreakpoints = preval` const themeBreakpoints = preval`
const R = require('ramda') const R = require('ramda')
const resolveConfig = require('tailwindcss/resolveConfig'); const resolveConfig = require('tailwindcss/resolveConfig');

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from "react";
import { graphql, navigate, Link } from 'gatsby'; import { graphql, navigate, Link } from "gatsby";
import { import {
getAspectRatio, getAspectRatio,
getMeta, getMeta,
@ -8,19 +8,21 @@ import {
getVibrant, getVibrant,
getVibrantToHelmetSafeBodyStyle, getVibrantToHelmetSafeBodyStyle,
hasName, hasName,
} from '../../utils'; } from "../../utils";
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from "gatsby-plugin-image";
import { Helmet } from 'react-helmet'; import { Helmet } from "react-helmet";
import classnames from 'classnames'; import classnames from "classnames";
import posthog from 'posthog-js'; import posthog from "posthog-js";
import MetadataItem from './MetadataItem'; import MetadataItem from "./MetadataItem";
const logKeyShortcut = (keyCode) => { const logKeyShortcut = (keyCode) => {
try { try {
// eslint-disable-next-line // eslint-disable-next-line
posthog.capture('[key shortcut]', { keyCode }); posthog.capture("[key shortcut]", { keyCode });
window.plausible('KeyShortcut', {props: { keyCode }}); window.plausible("KeyShortcut", { props: { keyCode } });
} catch (e) {/* do nothing */} } catch (e) {
/* do nothing */
}
}; };
const GalleryImage = ({ data, pageContext }) => { const GalleryImage = ({ data, pageContext }) => {
@ -29,32 +31,31 @@ const GalleryImage = ({ data, pageContext }) => {
React.useEffect(() => { React.useEffect(() => {
const keyListener = (e) => { const keyListener = (e) => {
switch (e.code) { switch (e.code) {
case 'ArrowRight': { case "ArrowRight": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
if (pageContext.nextImage) { if (pageContext.nextImage) {
navigate(`/photogallery/${pageContext.nextImage}/`); navigate(`/photogallery/${pageContext.nextImage}/`);
} }
return; return;
} }
case 'ArrowLeft': { case "ArrowLeft": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
if (pageContext.prevImage) { if (pageContext.prevImage) {
navigate(`/photogallery/${pageContext.prevImage}/`); navigate(`/photogallery/${pageContext.prevImage}/`);
} }
return; return;
} }
case 'Escape': case "Escape":
case 'KeyG': { case "KeyG": {
logKeyShortcut(e.code); logKeyShortcut(e.code);
navigate('/photogallery/'); navigate("/photogallery/");
} }
} }
}; };
document.addEventListener('keydown', keyListener); document.addEventListener("keydown", keyListener);
return () => { return () => {
document.removeEventListener('keydown', keyListener); document.removeEventListener("keydown", keyListener);
}; };
}, [pageContext]); }, [pageContext]);
@ -62,15 +63,24 @@ const GalleryImage = ({ data, pageContext }) => {
const meta = getMeta(image); const meta = getMeta(image);
let locationString; let locationString;
if (meta.iptc.city || meta.iptc.province_or_state) { if (meta.iptc.city || meta.iptc.province_or_state) {
const location = [meta.iptc.city, meta.iptc.province_or_state].filter(Boolean); const location = [meta.iptc.city, meta.iptc.province_or_state].filter(
locationString = location.join(', '); Boolean
);
locationString = location.join(", ");
} }
const vibrant = getVibrant(image, true); const vibrant = getVibrant(image, true);
const orientationClasses = ar > 1 ? 'flex-col mx-auto' : 'portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col'; const orientationClasses =
const shutterSpeed = React.useMemo(() => getShutterFractionFromExposureTime(meta.exif.ExposureTime || 0), [meta]); ar > 1
? "flex-col mx-auto"
: "portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col";
const shutterSpeed = React.useMemo(
() => getShutterFractionFromExposureTime(meta.exif.ExposureTime || 0),
[meta]
);
const dateTaken = React.useMemo(() => new Date(meta.dateTaken), [meta]); const dateTaken = React.useMemo(() => new Date(meta.dateTaken), [meta]);
return (<> return (
<>
<Helmet> <Helmet>
<title>{name} - Gallery | Chuck Dries</title> <title>{name} - Gallery | Chuck Dries</title>
<body <body
@ -84,25 +94,39 @@ const GalleryImage = ({ data, pageContext }) => {
className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1" className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
type="button" type="button"
>back</button> >
back
</button>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/" to="/"
>home</Link> >
home
</Link>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/photogallery/" to="/photogallery/"
>gallery <span className="bg-gray-300 text-black">esc</span></Link> >
{pageContext.prevImage && <Link gallery <span className="bg-gray-300 text-black">esc</span>
</Link>
{pageContext.prevImage && (
<Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to={`/photogallery/${pageContext.prevImage}/`} to={`/photogallery/${pageContext.prevImage}/`}
>previous <span className="bg-gray-300 text-black">&#11104;</span></Link>} >
{pageContext.nextImage && <Link previous <span className="bg-gray-300 text-black">&#11104;</span>
</Link>
)}
{pageContext.nextImage && (
<Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to={`/photogallery/${pageContext.nextImage}/`} to={`/photogallery/${pageContext.nextImage}/`}
>next <span className="bg-gray-300 text-black">&#11106;</span></Link>} >
next <span className="bg-gray-300 text-black">&#11106;</span>
</Link>
)}
</nav> </nav>
<div className={classnames('flex', orientationClasses)}> <div className={classnames("flex", orientationClasses)}>
<div className="flex-grow-0"> <div className="flex-grow-0">
<GatsbyImage <GatsbyImage
alt={name} alt={name}
@ -113,36 +137,80 @@ const GalleryImage = ({ data, pageContext }) => {
objectFit="contain" objectFit="contain"
style={{ style={{
maxWidth: `calc(max(90vh, 500px) * ${ar})`, maxWidth: `calc(max(90vh, 500px) * ${ar})`,
maxHeight: '90vh', maxHeight: "90vh",
// minHeight: '500px', // minHeight: '500px',
}} /> }}
/>
</div> </div>
<div className={classnames( <div
'flex-shrink-0 mx-2 flex flex-row portrait:items-end', ar <= 1 className={classnames(
? 'pt-5 flex-col flex-auto text-right' "flex-shrink-0 mx-2 flex flex-row portrait:items-end",
: 'portrait:pt-5 portrait:flex-col portrait:text-right' ar <= 1
)}> ? "pt-5 flex-col flex-auto text-right"
: "portrait:pt-5 portrait:flex-col portrait:text-right"
)}
>
<div className="flex-auto mr-2"> <div className="flex-auto mr-2">
<p className="text-muted-light font-mono text-sm m-0 mt-1">{image.base}</p> <p className="text-muted-light font-mono text-sm m-0 mt-1">
{hasName(image) && <h1 className="text-4xl mt-0 font-serif">{name}</h1>} {image.base}
</p>
{hasName(image) && (
<h1 className="text-4xl mt-0 font-serif">{name}</h1>
)}
<p className="landscape:mr-2">{meta.iptc.caption}</p> <p className="landscape:mr-2">{meta.iptc.caption}</p>
</div> </div>
{<div className="portrait:border-t-2 border-muted-light portrait:mt-2 mr-2 portrait:mb-1" style={{width: 30}}></div>} {
<MetadataItem aspectRatio={ar} data={dateTaken.toLocaleDateString()} icon="calendar-sharp" title="date taken"/> <div
<MetadataItem aspectRatio={ar} data={locationString} icon="location-sharp" title="location"/> className="portrait:border-t-2 border-muted-light portrait:mt-2 mr-2 portrait:mb-1"
<MetadataItem aspectRatio={ar} data={shutterSpeed} icon="stopwatch-sharp" title="shutter speed" /> style={{ width: 30 }}
<MetadataItem aspectRatio={ar} data={`f/${meta.exif.FNumber}`} icon="aperture-sharp" title="aperture" /> ></div>
<MetadataItem aspectRatio={ar} data={meta.exif.ISO} icon="film-outline" title="ISO" /> }
<MetadataItem
aspectRatio={ar}
data={dateTaken.toLocaleDateString()}
icon="calendar-sharp"
title="date taken"
/>
<MetadataItem
aspectRatio={ar}
data={locationString}
icon="location-sharp"
title="location"
/>
<MetadataItem
aspectRatio={ar}
data={shutterSpeed}
icon="stopwatch-sharp"
title="shutter speed"
/>
<MetadataItem
aspectRatio={ar}
data={`f/${meta.exif.FNumber}`}
icon="aperture-sharp"
title="aperture"
/>
<MetadataItem
aspectRatio={ar}
data={meta.exif.ISO}
icon="film-outline"
title="ISO"
/>
</div> </div>
</div> </div>
<div></div> <div></div>
</div> </div>
</>); </>
);
}; };
export const query = graphql` export const query = graphql`
query GalleryImage($imageFilename: String) { query GalleryImage($imageFilename: String) {
allFile(filter: {sourceInstanceName: {eq: "gallery"}, base: {eq: $imageFilename}}) { allFile(
filter: {
sourceInstanceName: { eq: "gallery" }
base: { eq: $imageFilename }
}
) {
edges { edges {
node { node {
base base
@ -182,8 +250,6 @@ export const query = graphql`
} }
} }
} }
`; `;
export default GalleryImage; export default GalleryImage;

View File

@ -1,14 +1,13 @@
import classNames from 'classnames'; import classNames from "classnames";
import React from 'react'; import React from "react";
const MetadataItem = ({ const MetadataItem = ({ aspectRatio, icon, data, title }) =>
aspectRatio, data ? (
icon, <div
data, className={classNames(
title, "flex items-baseline ml-2 text-lg",
}) => data ? ( aspectRatio <= 1 ? "flex-row-reverse" : "portrait:flex-row-reverse"
<div className={classNames('flex items-baseline ml-2 text-lg', )}
aspectRatio <= 1 ? 'flex-row-reverse' : 'portrait:flex-row-reverse')}
title={title} title={title}
> >
<span className="icon-offset mr-1"> <span className="icon-offset mr-1">

View File

@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from "react";
import classnames from 'classnames'; import classnames from "classnames";
export const HeroA = ({ export const HeroA = ({
href, href,
@ -9,8 +9,14 @@ export const HeroA = ({
...linkProps ...linkProps
}) => ( }) => (
<a <a
className={classnames('mx-2 underline', isClient && 'text-muted-light hover:text-vibrant-light', className)} className={classnames(
"mx-2 underline",
isClient && "text-muted-light hover:text-vibrant-light",
className
)}
href={href} href={href}
{...linkProps} {...linkProps}
>{children}</a> >
{children}
</a>
); );

View File

@ -1,33 +1,41 @@
import * as React from 'react'; import * as React from "react";
import { Link } from 'gatsby'; import { Link } from "gatsby";
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from "gatsby-plugin-image";
import * as R from 'ramda'; import * as R from "ramda";
import { getAspectRatio, getName } from '../utils'; import { getAspectRatio, getName } from "../utils";
import useBreakpoint from 'use-breakpoint'; import useBreakpoint from "use-breakpoint";
import themeBreakpoints from '../breakpoints'; import themeBreakpoints from "../breakpoints";
const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => { const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
const breakpoints = React.useMemo(() => const breakpoints = React.useMemo(
R.pick(R.keys(itemsPerRowByBreakpoint), themeBreakpoints) () => R.pick(R.keys(itemsPerRowByBreakpoint), themeBreakpoints),
, [itemsPerRowByBreakpoint]); [itemsPerRowByBreakpoint]
);
const { breakpoint } = useBreakpoint(breakpoints, 'sm'); const { breakpoint } = useBreakpoint(breakpoints, "sm");
const aspectRatios = React.useMemo(() => R.map(getAspectRatio, images), [images]); const aspectRatios = React.useMemo(
const rowAspectRatioSumsByBreakpoint = React.useMemo(() => R.map(R.pipe( () => R.map(getAspectRatio, images),
R.splitEvery(R.__, aspectRatios), [images]
R.map(R.sum) );
))(itemsPerRowByBreakpoint), [aspectRatios, itemsPerRowByBreakpoint]); const rowAspectRatioSumsByBreakpoint = React.useMemo(
() =>
R.map(R.pipe(R.splitEvery(R.__, aspectRatios), R.map(R.sum)))(
itemsPerRowByBreakpoint
),
[aspectRatios, itemsPerRowByBreakpoint]
);
const itemsPerRow = itemsPerRowByBreakpoint[breakpoint]; const itemsPerRow = itemsPerRowByBreakpoint[breakpoint];
const rowAspectRatioSumsForCurrentBP = rowAspectRatioSumsByBreakpoint[breakpoint]; const rowAspectRatioSumsForCurrentBP =
rowAspectRatioSumsByBreakpoint[breakpoint];
return ( return (
<div <div
className="w-full" className="w-full"
style={{ style={{
position: 'relative', position: "relative",
}} }}
// style={{ maxWidth: minWidth }} // style={{ maxWidth: minWidth }}
> >
@ -36,11 +44,12 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
const rowAspectRatioSum = rowAspectRatioSumsForCurrentBP[rowIndex]; const rowAspectRatioSum = rowAspectRatioSumsForCurrentBP[rowIndex];
// const width = ((getAspectRatio(image) / rowAspectRatioSum) * 100).toFixed(10); // const width = ((getAspectRatio(image) / rowAspectRatioSum) * 100).toFixed(10);
const ar = getAspectRatio(image); const ar = getAspectRatio(image);
const widthNumber = rowAspectRatioSum === ar const widthNumber =
// image is only one in row rowAspectRatioSum === ar
? 100 / itemsPerRow ? // image is only one in row
// image is one of several in row 100 / itemsPerRow
: ((ar / rowAspectRatioSum) * 100).toFixed(5); : // image is one of several in row
((ar / rowAspectRatioSum) * 100).toFixed(5);
const width = `${widthNumber}%`; const width = `${widthNumber}%`;
return ( return (
@ -51,7 +60,8 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
style={{ style={{
width, width,
// marginLeft: '8px', // marginLeft: '8px',
}} to={`/photogallery/${image.base}`} }}
to={`/photogallery/${image.base}`}
> >
<GatsbyImage <GatsbyImage
alt={getName(image)} alt={getName(image)}
@ -63,7 +73,8 @@ const MasonryGallery = ({ images, itemsPerRow: itemsPerRowByBreakpoint }) => {
</Link> </Link>
); );
})} })}
</div>); </div>
);
// return null; // return null;
}; };

View File

@ -1,10 +1,10 @@
import * as React from 'react'; import * as React from "react";
import classnames from 'classnames'; import classnames from "classnames";
import { MDXProvider } from '@mdx-js/react'; import { MDXProvider } from "@mdx-js/react";
import '../../styles/resume.css'; import "../../styles/resume.css";
const MyH1 = props => <h1 style={{ color: 'tomato' }} {...props} />; const MyH1 = (props) => <h1 style={{ color: "tomato" }} {...props} />;
// const MyParagraph = props => ( // const MyParagraph = props => (
// <p style={{ fontSize: '18px', lineHeight: 1.6 }} {...props} /> // <p style={{ fontSize: '18px', lineHeight: 1.6 }} {...props} />
// ); // );
@ -14,12 +14,13 @@ const components = {
// p: MyParagraph, // p: MyParagraph,
}; };
const ResumeLayout = ({ pageContext, children }) => { const ResumeLayout = ({ pageContext, children }) => {
console.log('pc', pageContext); console.log("pc", pageContext);
return ( return (
<MDXProvider components={components}> <MDXProvider components={components}>
<div className={classnames('font-serif container mx-auto resume')}>{children}</div> <div className={classnames("font-serif container mx-auto resume")}>
{children}
</div>
</MDXProvider> </MDXProvider>
); );
}; };

View File

@ -1,4 +1,4 @@
import { graphql } from 'gatsby'; import { graphql } from "gatsby";
export const VibrantColorsFragment = graphql` export const VibrantColorsFragment = graphql`
fragment VibrantColors on FileFieldsImageMetaVibrant { fragment VibrantColors on FileFieldsImageMetaVibrant {

View File

@ -1,7 +1,8 @@
import React from 'react'; import React from "react";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
const env = process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || 'development'; const env =
process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development";
export default function HTML(props) { export default function HTML(props) {
return ( return (
@ -9,25 +10,40 @@ export default function HTML(props) {
<head> <head>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<meta content="ie=edge" httpEquiv="x-ua-compatible" /> <meta content="ie=edge" httpEquiv="x-ua-compatible" />
<meta content="Chuck Dries: Full Stack Software Engineer and Photographer" name="description" /> <meta
content="Chuck Dries: Full Stack Software Engineer and Photographer"
name="description"
/>
<meta <meta
content="width=device-width, initial-scale=1, shrink-to-fit=no" content="width=device-width, initial-scale=1, shrink-to-fit=no"
name="viewport" name="viewport"
/> />
{props.headComponents} {props.headComponents}
{env === 'production' && <script async data-domain="chuckdries.com" defer src="https://analytics.chuckdries.com/js/plausible.js"></script>} {env === "production" && (
<script
async
data-domain="chuckdries.com"
defer
src="https://analytics.chuckdries.com/js/plausible.js"
></script>
)}
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
{env === 'production' && <script>{`window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }`}</script>} {env === "production" && (
<script>{`window.plausible = window.plausible || function() { (window.plausible.q = window.plausible.q || []).push(arguments) }`}</script>
)}
</head> </head>
<body {...props.bodyAttributes}> <body {...props.bodyAttributes}>
{props.preBodyComponents} {props.preBodyComponents}
<div <div
dangerouslySetInnerHTML={{ __html: props.body }} dangerouslySetInnerHTML={{ __html: props.body }}
id="___gatsby" id="___gatsby"
key={'body'} key={"body"}
/> />
{props.postBodyComponents} {props.postBodyComponents}
<script src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js" type="module"></script> <script
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"
type="module"
></script>
</body> </body>
</html> </html>
); );

View File

@ -1,11 +1,11 @@
import * as React from 'react'; import * as React from "react";
import { Link } from 'gatsby'; import { Link } from "gatsby";
// styles // styles
const pageStyles = { const pageStyles = {
color: '#232129', color: "#232129",
padding: '96px', padding: "96px",
fontFamily: '-apple-system, Roboto, sans-serif, serif', fontFamily: "-apple-system, Roboto, sans-serif, serif",
}; };
const headingStyles = { const headingStyles = {
marginTop: 0, marginTop: 0,
@ -17,10 +17,10 @@ const paragraphStyles = {
marginBottom: 48, marginBottom: 48,
}; };
const codeStyles = { const codeStyles = {
color: '#8A6534', color: "#8A6534",
padding: 4, padding: 4,
backgroundColor: '#FFF4DB', backgroundColor: "#FFF4DB",
fontSize: '1.25rem', fontSize: "1.25rem",
borderRadius: 4, borderRadius: 4,
}; };
@ -31,13 +31,13 @@ const NotFoundPage = () => {
<title>Not found</title> <title>Not found</title>
<h1 style={headingStyles}>Page not found</h1> <h1 style={headingStyles}>Page not found</h1>
<p style={paragraphStyles}> <p style={paragraphStyles}>
Sorry{' '} Sorry{" "}
<span aria-label="Pensive emoji" role="img"> <span aria-label="Pensive emoji" role="img">
😔 😔
</span>{' '} </span>{" "}
we couldnt find what you were looking for. we couldnt find what you were looking for.
<br /> <br />
{process.env.NODE_ENV === 'development' ? ( {process.env.NODE_ENV === "development" ? (
<> <>
<br /> <br />
Try creating a page in <code style={codeStyles}>src/pages/</code>. Try creating a page in <code style={codeStyles}>src/pages/</code>.

View File

@ -2,8 +2,8 @@
title: Charles Dries Resume title: Charles Dries Resume
--- ---
import ResumeLayout from '../components/resume/ResumeLayout' import ResumeLayout from "../components/resume/ResumeLayout";
export default ResumeLayout export default ResumeLayout;
# Hello, World! # Hello, World!

View File

@ -175,7 +175,7 @@ const IndexPage = ({
title="view image details" title="view image details"
to={`/photogallery/${image.base}/`} to={`/photogallery/${image.base}/`}
> >
<span className="icon-offset"><ion-icon name="image"></ion-icon></span> Photography Gallery
</Link> </Link>
<button <button
className={classnames( className={classnames(
@ -191,7 +191,7 @@ const IndexPage = ({
<span className="icon-offset"><ion-icon name="shuffle"></ion-icon></span> <span className="icon-offset"><ion-icon name="shuffle"></ion-icon></span>
</button> </button>
</div> </div>
<Link <section
className={classnames( className={classnames(
'hover:underline p-3 px-5 py-4 my-3 text-md sm:text-lg rounded-md border-2 arrow-right-after font-bold font-serif', 'hover:underline p-3 px-5 py-4 my-3 text-md sm:text-lg rounded-md border-2 arrow-right-after font-bold font-serif',
isClient && 'text-muted-dark bg-muted-light bg-opacity-70 border-muted-dark hover:bg-muted')} isClient && 'text-muted-dark bg-muted-light bg-opacity-70 border-muted-dark hover:bg-muted')}

View File

@ -1,20 +1,21 @@
import * as React from 'react'; import * as React from "react";
import { graphql, Link } from 'gatsby'; import { graphql, Link } from "gatsby";
import { navigate } from 'gatsby'; import { navigate } from "gatsby";
import { Helmet } from 'react-helmet'; import { Helmet } from "react-helmet";
import MasonryGallery from '../components/MasonryGallery'; import MasonryGallery from "../components/MasonryGallery";
// TODO: caption and title more images // TODO: caption and title more images
// TODO: more images // TODO: more images
const GalleryPage = ({ data }) => { const GalleryPage = ({ data }) => {
const images = React.useMemo(() => const images = React.useMemo(
data.allFile.edges () => data.allFile.edges.map((edge) => edge.node, [data]),
.map(edge => edge.node, [data]) [data]
, [data]); );
return (<> return (
<>
<Helmet> <Helmet>
<title>Photo Gallery | Chuck Dries</title> <title>Photo Gallery | Chuck Dries</title>
<body className="bg-black text-white" /> <body className="bg-black text-white" />
@ -24,18 +25,26 @@ const GalleryPage = ({ data }) => {
className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1" className="hover:underline text-vibrant-light hover:text-muted-light arrow-left-before mr-1"
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
type="button" type="button"
>back</button> >
back
</button>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/" to="/"
>home</Link> >
home
</Link>
<Link <Link
className="hover:underline text-vibrant-light hover:text-muted-light mx-1" className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
to="/photogallery/" to="/photogallery/"
>gallery</Link> >
gallery
</Link>
</nav> </nav>
<div className="bg-black min-h-screen mx-auto 2xl:container"> <div className="bg-black min-h-screen mx-auto 2xl:container">
<h1 className="text-5xl mt-0 ml-5 font-serif font-black z-10 relative">Photo Gallery</h1> <h1 className="text-5xl mt-0 ml-5 font-serif font-black z-10 relative">
Photo Gallery
</h1>
<div className="mx-auto"> <div className="mx-auto">
<MasonryGallery <MasonryGallery
images={images} images={images}
@ -44,12 +53,13 @@ const GalleryPage = ({ data }) => {
md: 2, md: 2,
lg: 3, lg: 3,
xl: 3, xl: 3,
'2xl': 4, "2xl": 4,
}} }}
/> />
</div> </div>
</div> </div>
</>); </>
);
}; };
export const query = graphql` export const query = graphql`
@ -66,10 +76,7 @@ query GalleryPageQuery {
fluid { fluid {
aspectRatio aspectRatio
} }
gatsbyImageData( gatsbyImageData(layout: CONSTRAINED, height: 550)
layout: CONSTRAINED
height: 550
)
} }
fields { fields {
imageMeta { imageMeta {
@ -82,6 +89,7 @@ query GalleryPageQuery {
} }
} }
} }
}`; }
`;
export default GalleryPage; export default GalleryPage;

View File

@ -1,6 +1,6 @@
/* @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap'); */ /* @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital@0;1&display=swap'); */
/* black, bold, regular */ /* black, bold, regular */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap");
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@ -30,7 +30,7 @@
scroll-snap-align: start; scroll-snap-align: start;
} }
.scroll-padding-6 { .scroll-padding-6 {
scroll-padding: theme('spacing.6'); scroll-padding: theme("spacing.6");
} }
.blurred-or-opaque-bg-1 { .blurred-or-opaque-bg-1 {
@apply bg-opacity-80; @apply bg-opacity-80;
@ -77,7 +77,7 @@ a {
margin-left: 3px; margin-left: 3px;
transform: translate(0px); transform: translate(0px);
display: inline-block; display: inline-block;
transition: all .2s; transition: all 0.2s;
} }
.arrow-left-before:before { .arrow-left-before:before {

View File

@ -3,4 +3,3 @@
@apply text-3xl font-bold; @apply text-3xl font-bold;
} }
} }

View File

@ -2,31 +2,36 @@
export const getMeta = (image) => image.fields.imageMeta; export const getMeta = (image) => image.fields.imageMeta;
export const getName = (image) => getMeta(image)?.iptc.object_name || image.base; export const getName = (image) =>
getMeta(image)?.iptc.object_name || image.base;
// some pleasing default colors for SSR and initial hydration // some pleasing default colors for SSR and initial hydration
export const getVibrant = (image) => getMeta(image)?.vibrant; export const getVibrant = (image) => getMeta(image)?.vibrant;
export const hasName = (image) => Boolean(getMeta(image)?.iptc.object_name); export const hasName = (image) => Boolean(getMeta(image)?.iptc.object_name);
export const getAspectRatio = (image) => image.childImageSharp.fluid.aspectRatio; export const getAspectRatio = (image) =>
image.childImageSharp.fluid.aspectRatio;
export const getRgba = (palette, alpha) => `rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`; export const getRgba = (palette, alpha) =>
`rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`;
// work around SSR bug in react-helmet // work around SSR bug in react-helmet
export const getVibrantToHelmetSafeBodyStyle = (vibrant) => { export const getVibrantToHelmetSafeBodyStyle = (vibrant) => {
const style = { const style = {
'--muted': vibrant.Muted, "--muted": vibrant.Muted,
'--dark-muted': vibrant.DarkMuted, "--dark-muted": vibrant.DarkMuted,
'--light-muted': vibrant.LightMuted, "--light-muted": vibrant.LightMuted,
'--vibrant': vibrant.Vibrant, "--vibrant": vibrant.Vibrant,
'--dark-vibrant': vibrant.DarkVibrant, "--dark-vibrant": vibrant.DarkVibrant,
'--light-vibrant': vibrant.LightVibrant, "--light-vibrant": vibrant.LightVibrant,
}; };
if (typeof window === 'undefined') { if (typeof window === "undefined") {
return style; return style;
} }
return Object.keys(style).map(key => `${(key)}: ${style[key]}`).join(';'); return Object.keys(style)
.map((key) => `${key}: ${style[key]}`)
.join(";");
}; };
const gcd = (a, b) => { const gcd = (a, b) => {

View File

@ -6,7 +6,8 @@
<body> <body>
<h1>contenteditable can be applied to visible style tags</h1> <h1>contenteditable can be applied to visible style tags</h1>
<p contenteditable="true"> <p contenteditable="true">
This paragraph is editable, as is the style tag below. Edit it to see the layout change in real time. This paragraph is editable, as is the style tag below. Edit it to see the
layout change in real time.
</p> </p>
<pre> <pre>
<style contenteditable="true"> <style contenteditable="true">

View File

@ -1,10 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Test Canvas Game</title> <title>Test Canvas Game</title>
<style> <style>
body { body {
@ -15,15 +14,14 @@
</head> </head>
<body> <body>
<canvas id="gc"> <canvas id="gc"> </canvas>
</canvas>
<script> <script>
//setup //setup
c = document.getElementById("gc"); c = document.getElementById("gc");
cc = c.getContext('2d'); cc = c.getContext("2d");
c.height = window.innerHeight; c.height = window.innerHeight;
c.width = window.innerWidth; c.width = window.innerWidth;
c.addEventListener('mousemove', function(e) { c.addEventListener("mousemove", function (e) {
p1y = e.clientY - ph / 2; p1y = e.clientY - ph / 2;
}); });
@ -40,8 +38,6 @@
score1 = score2 = 0; score1 = score2 = 0;
ais = 5; ais = 5;
function reset() { function reset() {
bx = c.width / 2; bx = c.width / 2;
by = c.height / 2; by = c.height / 2;
@ -81,7 +77,7 @@
dy = by - (p1y + ph / 2); dy = by - (p1y + ph / 2);
yv = dy * 10; //.3 yv = dy * 10; //.3
} else { } else {
score2++ score2++;
reset(); reset();
} }
} }
@ -99,16 +95,16 @@
// cc.fillStyle="white"; // cc.fillStyle="white";
cc.fillRect(0, 0, c.width, c.height); cc.fillRect(0, 0, c.width, c.height);
//draw paddles //draw paddles
cc.fillStyle = 'cyan'; cc.fillStyle = "cyan";
cc.fillRect(0, p1y, pt, ph); cc.fillRect(0, p1y, pt, ph);
cc.fillStyle = 'red'; cc.fillStyle = "red";
cc.fillRect(c.width - pt, p2y, pt, ph); cc.fillRect(c.width - pt, p2y, pt, ph);
//draw ball //draw ball
cc.fillStyle = 'lightgreen'; cc.fillStyle = "lightgreen";
cc.fillRect(bx - bd / 2, by - bd / 2, bd, bd); cc.fillRect(bx - bd / 2, by - bd / 2, bd, bd);
//draw scores //draw scores
cc.fillStyle = 'white'; cc.fillStyle = "white";
cc.font = '20px Times' cc.font = "20px Times";
cc.fillText(score1, 100, 100); cc.fillText(score1, 100, 100);
cc.fillText(score2, c.width - 100, 100); cc.fillText(score2, c.width - 100, 100);
//draw framerate //draw framerate
@ -116,9 +112,8 @@
window.requestAnimationFrame(update); //Keep the game running window.requestAnimationFrame(update); //Keep the game running
} }
reset() //prepare the game reset(); //prepare the game
update() //start the game update(); //start the game
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,19 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Document</title> <title>Document</title>
<link rel="stylesheet" href="link/to/css"> <link rel="stylesheet" href="link/to/css" />
</head> </head>
<body> <body>
<a href="">a tags are for links</a> <a href="">a tags are for links</a>
<p>p tags are for paragraphs</p> <p>p tags are for paragraphs</p>
<h1>h1 through h6 are for headers</h1> <h1>h1 through h6 are for headers</h1>
<img src="path/to/image.jpg" alt="imgs are for images"> <img src="path/to/image.jpg" alt="imgs are for images" />
<div>divs are just boxes</div> <div>divs are just boxes</div>
</body> </body>
</html> </html>

View File

@ -1,10 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Robotic Chuck</title> <title>Robotic Chuck</title>
<style> <style>
body { body {
@ -35,9 +34,9 @@
#genbutton { #genbutton {
color: white; color: white;
background: #01C106; background: #01c106;
padding: 1em; padding: 1em;
border: 1px solid #1E7931; border: 1px solid #1e7931;
font-size: 1em; font-size: 1em;
border-radius: 3px; border-radius: 3px;
} }
@ -55,23 +54,48 @@
var outBox = document.getElementById("output"); var outBox = document.getElementById("output");
var titleBox = document.getElementById("titleIn"); var titleBox = document.getElementById("titleIn");
var descBox = document.getElementById("description"); var descBox = document.getElementById("description");
var tstring = "<style>.here,.invmenu a{font-style:italic}.here{font-size:14px;font-weight:400;color:grey}.invmenu{padding:5px 20px;margin:10px 0}</style>\n"; var tstring =
tstring = tstring + "<" + "div class=\"invmenu\" style=\"max-width:100%;background: #f2f2f2;\">\n<h3 style=\"text-align:left;\">" + titleBox.value + "</h3>\n<p><em>"; "<style>.here,.invmenu a{font-style:italic}.here{font-size:14px;font-weight:400;color:grey}.invmenu{padding:5px 20px;margin:10px 0}</style>\n";
tstring = tstring + descBox.value + "</em></p>\n<hr style=\"border: 1px solid #cfcfcf\">\n" tstring =
formatter = links.map((object) => "<h4><a href=\"" + object.url + "\">" + object.headline + "</a></h4>\n"); tstring +
"<" +
'div class="invmenu" style="max-width:100%;background: #f2f2f2;">\n<h3 style="text-align:left;">' +
titleBox.value +
"</h3>\n<p><em>";
tstring =
tstring +
descBox.value +
'</em></p>\n<hr style="border: 1px solid #cfcfcf">\n';
formatter = links.map(
(object) =>
'<h4><a href="' +
object.url +
'">' +
object.headline +
"</a></h4>\n"
);
tstring = tstring + formatter.join(""); tstring = tstring + formatter.join("");
tstring = tstring + "</div>" tstring = tstring + "</div>";
console.log(tstring); console.log(tstring);
outBox.innerText = tstring; outBox.innerText = tstring;
outBox.style.minHeight = (formatter.length + 9) + "em"; outBox.style.minHeight = formatter.length + 9 + "em";
document.getElementById("preview").innerHTML = tstring; document.getElementById("preview").innerHTML = tstring;
} }
</script> </script>
<p></p> <p></p>
<p>Title of box:</p> <input type="text" id="titleIn" placeholder="Investigating Hope: The Series"> <p>Title of box:</p>
<input
type="text"
id="titleIn"
placeholder="Investigating Hope: The Series"
/>
<p>Description text:</p> <p>Description text:</p>
<input type="text" id="description" placeholder="This article is one in a series of investigative pieces about a complaint filed with ASU regarding accusations against on-campus ministry Hope Church."> <input
type="text"
id="description"
placeholder="This article is one in a series of investigative pieces about a complaint filed with ASU regarding accusations against on-campus ministry Hope Church."
/>
<p>Enter the links to generate according to this format:</p> <p>Enter the links to generate according to this format:</p>
<pre><code class="json">[ <pre><code class="json">[
{ {
@ -87,20 +111,37 @@
"url": "yet-another-url" "url": "yet-another-url"
} }
]</code></pre> ]</code></pre>
<p>This format is known as JSON, it's an internet standard that's supposed to be easy for humans and computers to read. <p>
Note how commas are used to separate multiple items but are never used after the last item in a set. This is important, This format is known as JSON, it's an internet standard that's supposed to
it will not work if you include a trailing comma where there should not be one.</p> be easy for humans and computers to read. Note how commas are used to
<textarea name="input" id="in" cols="100" rows="30" placeholder="Please be careful, this will not work if you format the JSON incorrectly"></textarea> separate multiple items but are never used after the last item in a set.
<p><a id="genbutton" href="#output" onclick="generate()">Generate Code</a></p> This is important, it will not work if you include a trailing comma where
there should not be one.
</p>
<textarea
name="input"
id="in"
cols="100"
rows="30"
placeholder="Please be careful, this will not work if you format the JSON incorrectly"
></textarea>
<p>
<a id="genbutton" href="#output" onclick="generate()">Generate Code</a>
</p>
<p>Paste the following code into a safeembed</p> <p>Paste the following code into a safeembed</p>
<textarea id="output" cols="100" readonly></textarea> <textarea id="output" cols="100" readonly></textarea>
<p><em> Don't forget:</em> For each page you embed on, add the following bit of code just before the <code>&lt;/h4&gt;</code> at the end of the line on the line that corresponds to the current page.</p> <p>
<em> Don't forget:</em> For each page you embed on, add the following bit
of code just before the <code>&lt;/h4&gt;</code> at the end of the line on
the line that corresponds to the current page.
</p>
<pre><code>&lt;span class="here"&gt;(You are here)&lt;/span&gt;</code></pre> <pre><code>&lt;span class="here"&gt;(You are here)&lt;/span&gt;</code></pre>
<p>The code generated will look roughly like this:</P> <p>The code generated will look roughly like this:</p>
<div id="preview" style="max-width: 800px;"></div> <div id="preview" style="max-width: 800px"></div>
<p>Gryphon overrides certain styles by default, so make sure to test on your article.</p> <p>
Gryphon overrides certain styles by default, so make sure to test on your
article.
</p>
</body> </body>
</html> </html>

View File

@ -1,34 +1,34 @@
const defaultTheme = require('tailwindcss/defaultTheme'); const defaultTheme = require("tailwindcss/defaultTheme");
module.exports = { module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}'], purge: ["./src/**/*.{js,jsx,ts,tsx}"],
// darkMode: 'media', // or 'media' or 'class' // darkMode: 'media', // or 'media' or 'class'
theme: { theme: {
screens: { screens: {
'sm': '640px', sm: "640px",
'md': '768px', md: "768px",
'lg': '1024px', lg: "1024px",
'xl': '1280px', xl: "1280px",
'2xl': '1536px', "2xl": "1536px",
'portrait': {'raw': '(orientation: portrait)'}, portrait: { raw: "(orientation: portrait)" },
'landscape': {'raw': '(orientation: landscape)'}, landscape: { raw: "(orientation: landscape)" },
}, },
spacing: { spacing: {
'0': '0px', 0: "0px",
'1': '4px', 1: "4px",
'2': '8px', 2: "8px",
'3': '12px', 3: "12px",
'4': '16px', 4: "16px",
'5': '24px', 5: "24px",
'6': '32px', 6: "32px",
'7': '48px', 7: "48px",
'8': '80px', 8: "80px",
'9': '800px', 9: "800px",
}, },
fontFamily: { fontFamily: {
...defaultTheme.fontFamily, ...defaultTheme.fontFamily,
// serif: ['Didot', 'Didot LT', 'STD', 'Hoefler Text' , 'Garamond', 'Times New Roman', 'serif'] // serif: ['Didot', 'Didot LT', 'STD', 'Hoefler Text' , 'Garamond', 'Times New Roman', 'serif']
serif: ['Playfair Display', 'serif'], serif: ["Playfair Display", "serif"],
}, },
extend: { extend: {
dropShadow: { dropShadow: {
@ -43,7 +43,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--vibrant))'; return "rgb(var(--vibrant))";
}, },
light: ({ opacityVariable, opacityValue }) => { light: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -52,7 +52,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--light-vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--light-vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--light-vibrant))'; return "rgb(var(--light-vibrant))";
}, },
dark: ({ opacityVariable, opacityValue }) => { dark: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -61,7 +61,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--dark-vibrant), var(${opacityVariable}, 1))`; return `rgba(var(--dark-vibrant), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--dark-vibrant))'; return "rgb(var(--dark-vibrant))";
}, },
}, },
muted: { muted: {
@ -72,7 +72,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--muted), var(${opacityVariable}, 1))`; return `rgba(var(--muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--muted))'; return "rgb(var(--muted))";
}, },
light: ({ opacityVariable, opacityValue }) => { light: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -81,7 +81,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--light-muted), var(${opacityVariable}, 1))`; return `rgba(var(--light-muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--light-muted))'; return "rgb(var(--light-muted))";
}, },
dark: ({ opacityVariable, opacityValue }) => { dark: ({ opacityVariable, opacityValue }) => {
if (opacityValue !== undefined) { if (opacityValue !== undefined) {
@ -90,7 +90,7 @@ module.exports = {
if (opacityVariable !== undefined) { if (opacityVariable !== undefined) {
return `rgba(var(--dark-muted), var(${opacityVariable}, 1))`; return `rgba(var(--dark-muted), var(${opacityVariable}, 1))`;
} }
return 'rgb(var(--dark-muted))'; return "rgb(var(--dark-muted))";
}, },
}, },
}, },

View File

@ -11144,7 +11144,7 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@^2.0.5: prettier@2.3.2, prettier@^2.0.5:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d"
integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==