Compare commits

..

No commits in common. "main" and "5ddaf606c1ba8afc81516f72c7a5170489c691ff" have entirely different histories.

48 changed files with 326 additions and 1265 deletions

View File

@ -1,2 +0,0 @@
[lfs]
url = "https://git.chuckdries.com/chuckdries/Personal-Website.git/info/lfs/"

BIN
data/gallery/DSC00003.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC00015.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC00159-2.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC00512.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC01832.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC02538-2.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC03049.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/gallery/DSC03401.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC04122.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC05702.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC06124.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC06515.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC06719.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC06803.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC08086.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC08103.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC08222.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC08263.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC08588.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC09447.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC09453.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/DSC09454.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/_DSC6060.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/_DSC6062.jpg (Stored with Git LFS)

Binary file not shown.

BIN
data/gallery/_DSC6066.jpg (Stored with Git LFS)

Binary file not shown.

View File

@ -14,25 +14,6 @@ export const onRouteUpdate = function () {
window.plausible("pageview");
}
};
// docs say you can return a scroll position from this fn, but that's a bold-faced lie
export const shouldUpdateScroll = ({
prevRouterProps,
routerProps: { location },
pathname,
}) => {
if (pathname.startsWith('/photogallery/') && pathname !== '/photogallery/' ) {
return false;
}
if (prevRouterProps?.location.pathname === pathname) {
return false;
}
if (pathname === "/photogallery/" && location.hash.length) {
return false;
}
return true;
}
// import * as React from 'react';
// import { MDXProvider } from '@mdx-js/react';

View File

@ -10,10 +10,6 @@ import sharp from "sharp";
import { Palette } from "node-vibrant/lib/color";
import { performance } from "perf_hooks";
import util from "node:util";
import { exec as _exec } from "child_process";
const exec = util.promisify(_exec);
// const path = require("path");
// const Vibrant = require("node-vibrant");
// const chroma = require("chroma-js");
@ -118,8 +114,7 @@ function transformMetaToNodeData(
metaData: Record<string, unknown>,
vibrantData: Palette,
imagePath: string,
{ r, g, b }: { r: number; b: number; g: number },
datePublished: string
{ r, g, b }: { r: number; b: number; g: number }
) {
const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;
const vibrantHue = vibrantData.Vibrant!.getHsl()[0] * 360;
@ -136,7 +131,6 @@ function transformMetaToNodeData(
}
return {
dateTaken: metaData.DateTimeOriginal,
datePublished,
meta: {
Make: metaData.Make,
Model: metaData.Model,
@ -145,7 +139,6 @@ function transformMetaToNodeData(
ISO: metaData.ISO,
DateTimeOriginal: metaData.DateTimeOriginal,
CreateDate: metaData.CreateDate,
ModifyDate: metaData.ModifyDate,
ShutterSpeedValue: metaData.ShutterSpeedValue,
ApertureValue: metaData.ApertureValue,
FocalLength: metaData.FocalLength,
@ -180,34 +173,13 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({
const { createNodeField } = actions;
if (node.internal.type === "File" && node.sourceInstanceName === "gallery") {
const { stdout: datePublished, stderr } = await exec(
`git log --diff-filter=A --follow --format=%aI -1 -- ${node.absolutePath}`
);
const metaData = await exifr.parse(node.absolutePath as string, {
iptc: true,
xmp: true,
// icc: true
});
if (stderr.length) {
console.error("something went wrong checking publish date: ", stderr);
}
let metaData;
try {
metaData = await exifr.parse(node.absolutePath as string, {
iptc: true,
xmp: true,
// icc: true
});
} catch (e) {
console.error(`something wen wrong with exifr on image ${node.base}`, e);
throw e;
}
let sharpImage: sharp.Sharp;
try {
sharpImage = sharp(node.absolutePath as string);
} catch (e) {
console.error(`something wen wrong with sharp on image ${node.base}`, e);
throw e;
}
const sharpImage = sharp(node.absolutePath as string);
const { dominant } = await sharpImage.stats();
const resizedImage = await sharpImage
.resize({
@ -228,9 +200,7 @@ export const onCreateNode: GatsbyNode["onCreateNode"] = async function ({
metaData,
vibrantData,
node.absolutePath as string,
dominant,
// if datePublished is empty, image has not been committed to git yet and is thus brand new
datePublished.length ? datePublished.replace("\n", "") : new Date().toISOString()
dominant
),
});
}

View File

@ -41,7 +41,6 @@
"gatsby-source-filesystem": "^5.0.0",
"gatsby-transformer-sharp": "^5.0.0",
"kebab-case": "^1.0.1",
"lodash.debounce": "^4.0.8",
"node-iptc": "^1.0.5",
"node-vibrant": "3.1.6",
"postcss": "^8.4.19",
@ -61,7 +60,6 @@
},
"devDependencies": {
"@types/chroma-js": "^2.1.4",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.8.3",
"@types/ramda": "^0.28.15",
"@types/react": "^18.0.21",

View File

@ -1,193 +0,0 @@
a = {
ApplicationRecordVersion: '\x00\x04',
DateCreated: '2015-10-18T17:59:38',
TimeCreated: '175938',
DigitalCreationDate: '20151018',
DigitalCreationTime: '175938',
format: 'image/jpeg',
CreatorTool: 'Adobe Lightroom 5.4 (Macintosh)',
ModifyDate: '2022-07-09T21:16:39.000Z',
CreateDate: '2015-10-19T00:59:38.000Z',
MetadataDate: '2022-07-09T14:16:39-07:00',
Rating: 5,
LensInfo: [ 50, 50, 1.8, 1.8 ],
Lens: 'DT 50mm F1.8 SAM',
DocumentID: 'xmp.did:27795bf1-8bb2-49a0-807a-cf6150bd505b',
OriginalDocumentID: 'CE2B8F240393ADFDF9F092CF395C6632',
InstanceID: 'xmp.iid:27795bf1-8bb2-49a0-807a-cf6150bd505b',
History: [
{ action: 'derived', parameters: 'saved to new location' },
{
action: 'saved',
instanceID: 'xmp.iid:27795bf1-8bb2-49a0-807a-cf6150bd505b',
when: '2022-07-09T14:16:39-07:00',
softwareAgent: 'Adobe Lightroom 5.4 (Macintosh)',
changed: '/'
}
],
DerivedFrom: {
documentID: 'CE2B8F240393ADFDF9F092CF395C6632',
originalDocumentID: 'CE2B8F240393ADFDF9F092CF395C6632'
},
good: true,
Version: 14.4,
ProcessVersion: 6.7,
WhiteBalance: 'Manual',
IncrementalTemperature: -11,
IncrementalTint: 23,
Exposure2012: 0,
Contrast2012: 6,
Highlights2012: 0,
Shadows2012: 0,
Whites2012: 35,
Blacks2012: -22,
Texture: 0,
Clarity2012: 0,
Dehaze: 0,
Vibrance: 0,
Saturation: 'Normal',
ParametricShadows: 0,
ParametricDarks: 0,
ParametricLights: 0,
ParametricHighlights: 0,
ParametricShadowSplit: 25,
ParametricMidtoneSplit: 50,
ParametricHighlightSplit: 75,
Sharpness: 'Normal',
LuminanceSmoothing: 0,
ColorNoiseReduction: 0,
HueAdjustmentRed: 0,
HueAdjustmentOrange: 0,
HueAdjustmentYellow: 0,
HueAdjustmentGreen: 0,
HueAdjustmentAqua: 0,
HueAdjustmentBlue: 0,
HueAdjustmentPurple: 0,
HueAdjustmentMagenta: 0,
SaturationAdjustmentRed: 0,
SaturationAdjustmentOrange: 0,
SaturationAdjustmentYellow: 0,
SaturationAdjustmentGreen: 0,
SaturationAdjustmentAqua: 0,
SaturationAdjustmentBlue: 0,
SaturationAdjustmentPurple: 0,
SaturationAdjustmentMagenta: 0,
LuminanceAdjustmentRed: 0,
LuminanceAdjustmentOrange: 0,
LuminanceAdjustmentYellow: 0,
LuminanceAdjustmentGreen: 0,
LuminanceAdjustmentAqua: 0,
LuminanceAdjustmentBlue: 0,
LuminanceAdjustmentPurple: 0,
LuminanceAdjustmentMagenta: 0,
SplitToningShadowHue: 0,
SplitToningShadowSaturation: 0,
SplitToningHighlightHue: 0,
SplitToningHighlightSaturation: 0,
SplitToningBalance: 0,
ColorGradeMidtoneHue: 0,
ColorGradeMidtoneSat: 0,
ColorGradeShadowLum: 0,
ColorGradeMidtoneLum: 0,
ColorGradeHighlightLum: 0,
ColorGradeBlending: 50,
ColorGradeGlobalHue: 0,
ColorGradeGlobalSat: 0,
ColorGradeGlobalLum: 0,
AutoLateralCA: 0,
LensProfileEnable: 0,
LensManualDistortionAmount: 0,
VignetteAmount: 0,
DefringePurpleAmount: 0,
DefringePurpleHueLo: 30,
DefringePurpleHueHi: 70,
DefringeGreenAmount: 0,
DefringeGreenHueLo: 40,
DefringeGreenHueHi: 60,
PerspectiveUpright: 0,
PerspectiveVertical: 0,
PerspectiveHorizontal: 0,
PerspectiveRotate: 0,
PerspectiveAspect: 0,
PerspectiveScale: 100,
PerspectiveX: 0,
PerspectiveY: 0,
GrainAmount: 0,
PostCropVignetteAmount: 0,
ShadowTint: 0,
RedHue: 0,
RedSaturation: 0,
GreenHue: 0,
GreenSaturation: 0,
BlueHue: 0,
BlueSaturation: 0,
ConvertToGrayscale: false,
OverrideLookVignette: false,
ToneCurveName2012: 'Linear',
CameraProfile: 'Embedded',
CameraProfileDigest: '54650A341B5B5CCAE8442D0B43A92BCE',
HasSettings: true,
CropTop: 0,
CropLeft: 0,
CropBottom: 1,
CropRight: 1,
CropAngle: 0,
CropConstrainToWarp: 0,
HasCrop: false,
AlreadyApplied: true,
ToneCurvePV2012: [ '0, 0', '255, 255' ],
ToneCurvePV2012Red: [ '0, 0', '255, 255' ],
ToneCurvePV2012Green: [ '0, 0', '255, 255' ],
ToneCurvePV2012Blue: [ '0, 0', '255, 255' ],
Make: 'SONY',
Model: 'SLT-A55V',
XResolution: 240,
YResolution: 240,
ResolutionUnit: 'inches',
Software: 'Adobe Lightroom 5.4 (Macintosh)',
ExposureTime: 0.01,
FNumber: 1.8,
ExposureProgram: 'Manual',
ISO: 200,
SensitivityType: 2,
RecommendedExposureIndex: 200,
ExifVersion: '2.3.1',
DateTimeOriginal: '2015-10-19T00:59:38.000Z',
OffsetTime: '-07:00',
ShutterSpeedValue: 6.643856,
ApertureValue: 1.695994,
BrightnessValue: 3,
ExposureCompensation: 0,
MaxApertureValue: 1.7,
MeteringMode: 'Pattern',
LightSource: 'Cloudy weather',
Flash: 'Flash did not fire, compulsory flash mode',
FocalLength: 50,
ColorSpace: 1,
FileSource: 'Digital Camera',
SceneType: 'Directly photographed',
CustomRendered: 'Normal',
ExposureMode: 'Manual',
FocalLengthIn35mmFormat: 75,
SceneCaptureType: 'Standard',
Contrast: 'Normal',
LensModel: 'DT 50mm F1.8 SAM',
GPSVersionID: '2.2.0.0',
GPSLatitudeRef: 'N',
GPSLatitude: [ 33, 25.7121, 0 ],
GPSLongitudeRef: 'W',
GPSLongitude: [ 111, 55.57122, 0 ],
GPSAltitude: 354.7,
GPSTimeStamp: '19:2:7',
GPSStatus: 'V',
GPSMeasureMode: '3',
GPSSpeedRef: 'K',
GPSSpeed: 33.8,
GPSTrackRef: 'T',
GPSTrack: 0.36,
GPSMapDatum: 'WGS-84',
GPSDateStamp: '2015:10:09',
GPSDifferential: 0,
latitude: 33.428535,
longitude: -111.926187
}

View File

@ -63,15 +63,6 @@ const GalleryImage = ({ data, location: { state } }) => {
setIsClient(true);
}, []);
useEffect(() => {
setTimeout(() => {
window.scrollTo({
top: 180,
behavior: 'smooth'
});
}, 50);
}, [image.base]);
const nextIndex =
sortedImageList && currentIndex < sortedImageList.length
? currentIndex + 1
@ -144,7 +135,7 @@ const GalleryImage = ({ data, location: { state } }) => {
locationString = location.join(", ");
}
const vibrant = getVibrant(image, true);
const BLEND = "hsl";
const BLEND = 'hsl'
const darkAccent = chroma
.mix(vibrant.Vibrant, "hsla(216, 0%, 90%, 1)", 0.6, BLEND)
.hex();
@ -154,7 +145,7 @@ const GalleryImage = ({ data, location: { state } }) => {
ar > 1
? "flex-col"
: "portrait:mx-auto landscape:mx-5 landscape:flex-row-reverse portrait:flex-col";
const verticalPad = ar > 1 ? "180px" : "20px";
const verticalPad = ar > 1 ? "250px" : "100px";
const shutterSpeed = React.useMemo(
() =>

View File

@ -1,37 +1,44 @@
import * as React from "react";
import classNames from "classnames";
import { Link } from "gatsby";
import Checkmark from "@spectrum-icons/workflow/Checkmark";
interface KeywordsPickerProps {
keywords: string[];
value: string | null;
getHref: (value: string | null, selected: boolean) => string;
onPick: (value: string | null) => void;
onChange: (val: string | null) => void;
}
const KeywordsPicker = ({ keywords, value, getHref, onPick }: KeywordsPickerProps) => {
const KeywordsPicker = ({ keywords, value, onChange }: KeywordsPickerProps) => {
return (
<div className="mx-2 mt-2">
<span className="text-xs text-black">Collections</span>
<span className="text-xs text-black">
Collections
</span>
<ul className="flex gap-1 flex-wrap mt-1 mb-2">
{keywords.map((keyword) => {
const selected = value === keyword;
return (
<li key={keyword}>
<Link
<button
className={classNames(
`py-[5px] px-3 rounded-full text-sm block`,
`text-black border border-gray-400`,
`py-[5px] px-3 rounded-full text-sm`,
`text-black border border-black`,
selected
? "bg-black/10 font-bold"
? "bg-transparentblack font-bold"
: `bg-white
hover:bg-black/10`
hover:bg-transparentblack`
)}
onClick={() => onPick(keyword)}
replace={false}
to={getHref(keyword, selected)}
onClick={() => (selected ? onChange(null) : onChange(keyword))}
type="button"
>
{keyword}{" "}
</Link>
{/* {selected && (
<Checkmark
UNSAFE_className="mx-1"
UNSAFE_style={{ width: "15px" }}
aria-hidden="true"
/>
)} */}
</button>
</li>
);
})}

View File

@ -91,8 +91,8 @@ function Option({ item, state }: OptionProps) {
return (
<li
{...optionProps}
className={`p-2 outline-none cursor-default flex items-center justify-between ${text} text-sm ${
isFocused ? "bg-black/10" : ""
className={`p-3 outline-none cursor-default flex items-center justify-between ${text} text-sm ${
isFocused ? "bg-transparentblack" : ""
} ${isSelected ? "font-bold" : ""}`}
ref={ref}
>

View File

@ -18,25 +18,23 @@ interface Row {
}
interface MasonryGalleryProps {
images: readonly GalleryImage[];
images: GalleryImage[];
aspectsByBreakpoint: {
[breakpoint: string]: number;
};
debugHue?: boolean;
dataFn?: (image: GalleryImage) => string[] | null;
debugRating?: boolean;
linkState?: object;
showPalette?: boolean;
singleRow?: boolean;
}
const MasonryGallery = ({
images: _images,
images,
aspectsByBreakpoint: aspectTargetsByBreakpoint,
debugHue,
dataFn,
debugRating,
linkState,
showPalette,
singleRow,
}: MasonryGalleryProps) => {
const [isClient, setIsClient] = React.useState(false);
React.useEffect(() => {
@ -47,57 +45,65 @@ const MasonryGallery = ({
[aspectTargetsByBreakpoint]
);
// const { observe, currentBreakpoint } = useDimensions({
// breakpoints,
// });
const { breakpoint } = useBreakpoint(breakpoints, "xs");
// const breakpoint = currentBreakpoint.length ? currentBreakpoint : "xs";
const galleryWidth = `calc(100vw - ${
breakpoint === "xs" || breakpoint === "sm" ? "32" : "160"
}px)`;
const aspectRatios = React.useMemo(
() => R.map(getAspectRatio, _images).filter(Boolean),
[_images]
() => R.map(getAspectRatio, images).filter(Boolean),
[images]
) as number[];
const targetAspect = aspectTargetsByBreakpoint[breakpoint];
const rows = React.useMemo(() => {
const _rows: Row[] = [{ aspect: 0, startIndex: 0, images: 0 }];
for (const currentAspect of aspectRatios) {
const currentRow = _rows[_rows.length - 1];
const currentDiff = Math.abs(targetAspect - currentRow.aspect);
const diffIfImageIsAddedToCurrentRow = Math.abs(
targetAspect - (currentRow.aspect + currentAspect)
);
// does adding current image to our row get us closer to our target aspect ratio?
if (currentDiff > diffIfImageIsAddedToCurrentRow) {
currentRow.aspect += currentAspect;
currentRow.images += 1;
// _rows.push(currentRow);
continue;
}
if (singleRow) {
break;
}
// start a new row
_rows.push({
aspect: currentAspect,
images: 1,
startIndex: currentRow.startIndex + currentRow.images,
});
}
return R.indexBy(R.prop("startIndex"), _rows);
}, [aspectRatios, targetAspect, singleRow]);
const sortedImageList = React.useMemo(
() => _images.map((image) => image.base),
[_images]
const rows = React.useMemo(
() =>
R.pipe(
R.reduce(
(acc, currentAspect: number): Row[] => {
const currentRow = acc.pop()!;
const currentDiff = Math.abs(targetAspect - currentRow.aspect);
const diffIfImageIsAddedToCurrentRow = Math.abs(
targetAspect - (currentRow.aspect + currentAspect)
);
// add image to current row if it gets us closer to our target aspect ratio
if (currentDiff > diffIfImageIsAddedToCurrentRow) {
return [
...acc,
{
aspect: currentRow.aspect + currentAspect,
images: currentRow.images + 1,
startIndex: currentRow.startIndex,
},
];
}
return [
...acc,
currentRow,
{
aspect: currentAspect,
images: 1,
startIndex: currentRow.startIndex + currentRow.images,
} as Row,
];
},
[{ aspect: 0, startIndex: 0, images: 0 }] as Row[]
),
R.indexBy(R.prop("startIndex"))
)(aspectRatios),
[aspectRatios, targetAspect]
);
const images = singleRow ? _images.slice(0, rows[0].images) : _images;
const sortedImageList = React.useMemo(
() => images.map((image) => image.base),
[images]
);
let cursor = 0;
return (
@ -119,11 +125,9 @@ const MasonryGallery = ({
}
const rowAspectRatioSum = currentRow.aspect;
const ar = getAspectRatio(image);
let width: string;
let height = `calc(${galleryWidth} / ${rowAspectRatioSum} ${
showPalette ? "+ 10px" : "- 10px"
})`;
if (rowAspectRatioSum < targetAspect * 0.66 && !singleRow) {
let width;
let height = `calc(${galleryWidth} / ${rowAspectRatioSum} ${showPalette ? "+ 10px" : "- 10px"})`;
if (rowAspectRatioSum < targetAspect * 0.66) {
// incomplete row, render stuff at "ideal" sizes instead of filling width
width = `calc(calc(100vw - 160px) / ${targetAspect / ar})`;
height = "unset";
@ -134,12 +138,10 @@ const MasonryGallery = ({
const vibrant = getVibrant(image);
// @ts-ignore
const img = getImage(image);
const data = dataFn ? dataFn(image) : null;
return (
<Link
className="border-8 border-white overflow-hidden relative"
id={singleRow ? undefined : image.base}
className={classNames("border-8 border-white overflow-hidden")}
id={image.base}
key={`${image.base}`}
state={{
...linkState,
@ -159,68 +161,47 @@ const MasonryGallery = ({
}}
to={`/photogallery/${image.base}/`}
>
{data && (
<div className="text-white z-20 absolute flex flex-col items-start">
{data.map((dataString, i) => (
<span
className="bg-black/30 backdrop-blur shadow p-[2px] m-[2px] max-w-full"
key={i}
>
{dataString}
</span>
))}
</div>
{debugRating && (
<span className="text-white z-20 absolute bg-black">
rating: {image.fields?.imageMeta?.meta?.Rating}
</span>
)}
{img && (
<div
className={`h-full ${
showPalette && "grid grid-rows-[1fr_20px]"
}`}
>
<div className={`h-full ${showPalette && "grid grid-rows-[1fr_20px]"}`}>
<GatsbyImage
alt={
image.fields?.imageMeta?.meta?.Keywords?.length
? `image of ${image.fields?.imageMeta?.meta?.Keywords.join(
" and "
)}. ${getName(image)}`
: getName(image)
}
alt={getName(image)}
className="w-full"
image={img}
objectFit="cover"
/>
{showPalette && vibrant && (
<div className="grid grid-cols-6 flex-shrink-0 h-[20px] w-full">
<div
style={{
background: `rgba(${vibrant.Vibrant?.join(",")})`,
}}
></div>
<div
style={{
background: `rgb(${vibrant.LightVibrant?.join(",")})`,
}}
></div>
<div
style={{
background: `rgb(${vibrant.DarkVibrant?.join(",")})`,
}}
></div>
<div
style={{ background: `rgb(${vibrant.Muted?.join(",")})` }}
></div>
<div
style={{
background: `rgb(${vibrant.LightMuted?.join(",")})`,
}}
></div>
<div
style={{
background: `rgb(${vibrant.DarkMuted?.join(",")})`,
}}
></div>
</div>
)}
{ showPalette && vibrant && <div className="grid grid-cols-6 flex-shrink-0 h-[20px] w-full">
<div
style={{ background: `rgba(${vibrant.Vibrant?.join(",")})` }}
></div>
<div
style={{
background: `rgb(${vibrant.LightVibrant?.join(",")})`,
}}
></div>
<div
style={{
background: `rgb(${vibrant.DarkVibrant?.join(",")})`,
}}
></div>
<div
style={{ background: `rgb(${vibrant.Muted?.join(",")})` }}
></div>
<div
style={{
background: `rgb(${vibrant.LightMuted?.join(",")})`,
}}
></div>
<div
style={{
background: `rgb(${vibrant.DarkMuted?.join(",")})`,
}}
></div>
</div>}
</div>
)}
</Link>

View File

@ -1,17 +1,16 @@
import React, { useRef, useState } from "react";
import React, { useState } from "react";
import classnames from "classnames";
import { Link, navigate } from "gatsby";
import { Link } from "gatsby";
import { Popover } from "react-tiny-popover";
import { StaticImage } from "gatsby-plugin-image";
const navClasses =
"hover:underline hover:bg-black/10 block p-3 text-black flex-shrink-0 whitespace-nowrap";
"hover:underline hover:bg-transparentblack block p-3 text-black";
const ExternalLinks = () => (
<ul
className={classnames(
"z-30 overflow-hidden bg-vibrant-dark",
"rounded shadow-lg border border-gray-400"
"rounded shadow border border-vibrant-light"
)}
>
<li>
@ -88,56 +87,17 @@ interface NavProps {
const Nav = ({ internalLinks, className }: NavProps) => {
const [linksMenu, setLinksMenu] = useState(false);
const faceClicks = useRef(0);
const faceLastClicked = useRef(0);
return (
<nav
className={classnames(
"my-4 flex flex-col-reverse md:flex-row",
"justify-between",
"items-center w-full font-sans px-4 md:px-8",
"my-4 flex flex-col-reverse md:flex-row items-center w-full font-sans px-4 md:px-8",
className
)}
>
<div className="flex flex-auto items-center">
<div
className={classnames(
"h-[120px] w-[120px] mr-4 my-5 flex-shrink-0"
)}
onClick={() => {
const prevClick = faceLastClicked.current;
faceLastClicked.current = Date.now();
if (prevClick > 0 && faceLastClicked.current - prevClick > 500) {
console.log('too slow!')
faceClicks.current = 1;
return;
}
if (faceClicks.current === 4) {
navigate("/photogallery/?debug=true");
return;
}
faceClicks.current += 1;
}}
>
<StaticImage
alt="A picture of me"
className="relative"
placeholder="tracedSVG"
src="../images/circle-profile.png"
style={
{
// top: "-70%",
// left: "-50%",
// width: "200%",
}
}
/>
</div>
<div className="items-baseline">
<h1 className="font-bold mr-2">Chuck Dries</h1>
<h2 className="text-md">Software Engineer & Photographer</h2>
</div>
<div className="md:flex items-baseline flex-auto">
<h1 className="font-bold mr-2">Chuck Dries</h1>
<h2 className="text-md">Software Engineer & Photographer</h2>
</div>
<div className="flex">

View File

@ -48,8 +48,8 @@ export function Select<T extends object>(props: AriaSelectProps<T>) {
/>
<button
{...mergeProps(buttonProps, focusProps)}
className={`py-[5px] px-3 w-[150px] flex flex-row items-center justify-between overflow-hidden cursor-default rounded border hover:bg-black/10 ${
isFocusVisible ? "border-green-700" : "border-gray-400"
className={`py-[5px] px-3 w-[150px] flex flex-row items-center justify-between overflow-hidden cursor-default rounded border hover:bg-transparentblack ${
isFocusVisible ? "border-green-500" : "border-black"
} ${state.isOpen ? "bg-gray-100" : "bg-white"}`}
ref={ref}
>

View File

@ -1,33 +0,0 @@
import * as React from 'react';
import {useToggleState} from 'react-stately';
import {AriaToggleButtonProps, useToggleButton} from 'react-aria';
import {useRef} from 'react';
import classNames from 'classnames';
export function ToggleButton(props: AriaToggleButtonProps) {
let ref = useRef(null);
let state = useToggleState(props);
let { buttonProps, isPressed } = useToggleButton(props, state, ref);
return (
<button
{...buttonProps}
className={classNames(buttonProps.className, "py-[3px] px-2 mx-1 rounded")}
ref={ref}
style={{
background: isPressed
? state.isSelected ? 'darkgreen' : 'gray'
: state.isSelected
? 'green'
: 'lightgray',
color: state.isSelected ? 'white' : 'black',
userSelect: 'none',
WebkitUserSelect: 'none',
border: 'none'
}}
>
{props.children}
</button>
);
}

384
src/gatsby-types.d.ts vendored
View File

@ -571,7 +571,6 @@ type FileFieldsFilterInput = {
};
type FileFieldsImageMeta = {
readonly datePublished: Maybe<Scalars['Date']>;
readonly dateTaken: Maybe<Scalars['Date']>;
readonly dominantHue: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
readonly meta: Maybe<FileFieldsImageMetaMeta>;
@ -580,14 +579,6 @@ type FileFieldsImageMeta = {
};
type FileFieldsImageMeta_datePublishedArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type FileFieldsImageMeta_dateTakenArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
@ -596,7 +587,6 @@ type FileFieldsImageMeta_dateTakenArgs = {
};
type FileFieldsImageMetaFieldSelector = {
readonly datePublished: InputMaybe<FieldSelectorEnum>;
readonly dateTaken: InputMaybe<FieldSelectorEnum>;
readonly dominantHue: InputMaybe<FieldSelectorEnum>;
readonly meta: InputMaybe<FileFieldsImageMetaMetaFieldSelector>;
@ -605,7 +595,6 @@ type FileFieldsImageMetaFieldSelector = {
};
type FileFieldsImageMetaFilterInput = {
readonly datePublished: InputMaybe<DateQueryOperatorInput>;
readonly dateTaken: InputMaybe<DateQueryOperatorInput>;
readonly dominantHue: InputMaybe<FloatQueryOperatorInput>;
readonly meta: InputMaybe<FileFieldsImageMetaMetaFilterInput>;
@ -628,7 +617,6 @@ type FileFieldsImageMetaMeta = {
readonly Location: Maybe<Scalars['String']>;
readonly Make: Maybe<Scalars['String']>;
readonly Model: Maybe<Scalars['String']>;
readonly ModifyDate: Maybe<Scalars['Date']>;
readonly ObjectName: Maybe<Scalars['String']>;
readonly Rating: Maybe<Scalars['Int']>;
readonly ShutterSpeedValue: Maybe<Scalars['Float']>;
@ -651,14 +639,6 @@ type FileFieldsImageMetaMeta_DateTimeOriginalArgs = {
locale: InputMaybe<Scalars['String']>;
};
type FileFieldsImageMetaMeta_ModifyDateArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type FileFieldsImageMetaMetaFieldSelector = {
readonly ApertureValue: InputMaybe<FieldSelectorEnum>;
readonly Caption: InputMaybe<FieldSelectorEnum>;
@ -674,7 +654,6 @@ type FileFieldsImageMetaMetaFieldSelector = {
readonly Location: InputMaybe<FieldSelectorEnum>;
readonly Make: InputMaybe<FieldSelectorEnum>;
readonly Model: InputMaybe<FieldSelectorEnum>;
readonly ModifyDate: InputMaybe<FieldSelectorEnum>;
readonly ObjectName: InputMaybe<FieldSelectorEnum>;
readonly Rating: InputMaybe<FieldSelectorEnum>;
readonly ShutterSpeedValue: InputMaybe<FieldSelectorEnum>;
@ -696,7 +675,6 @@ type FileFieldsImageMetaMetaFilterInput = {
readonly Location: InputMaybe<StringQueryOperatorInput>;
readonly Make: InputMaybe<StringQueryOperatorInput>;
readonly Model: InputMaybe<StringQueryOperatorInput>;
readonly ModifyDate: InputMaybe<DateQueryOperatorInput>;
readonly ObjectName: InputMaybe<StringQueryOperatorInput>;
readonly Rating: InputMaybe<IntQueryOperatorInput>;
readonly ShutterSpeedValue: InputMaybe<FloatQueryOperatorInput>;
@ -718,7 +696,6 @@ type FileFieldsImageMetaMetaSortInput = {
readonly Location: InputMaybe<SortOrderEnum>;
readonly Make: InputMaybe<SortOrderEnum>;
readonly Model: InputMaybe<SortOrderEnum>;
readonly ModifyDate: InputMaybe<SortOrderEnum>;
readonly ObjectName: InputMaybe<SortOrderEnum>;
readonly Rating: InputMaybe<SortOrderEnum>;
readonly ShutterSpeedValue: InputMaybe<SortOrderEnum>;
@ -726,7 +703,6 @@ type FileFieldsImageMetaMetaSortInput = {
};
type FileFieldsImageMetaSortInput = {
readonly datePublished: InputMaybe<SortOrderEnum>;
readonly dateTaken: InputMaybe<SortOrderEnum>;
readonly dominantHue: InputMaybe<SortOrderEnum>;
readonly meta: InputMaybe<FileFieldsImageMetaMetaSortInput>;
@ -1532,7 +1508,6 @@ type Query = {
readonly allSiteFunction: SiteFunctionConnection;
readonly allSitePage: SitePageConnection;
readonly allSitePlugin: SitePluginConnection;
readonly allStaticImage: StaticImageConnection;
readonly directory: Maybe<Directory>;
readonly file: Maybe<File>;
readonly imageSharp: Maybe<ImageSharp>;
@ -1541,7 +1516,6 @@ type Query = {
readonly siteFunction: Maybe<SiteFunction>;
readonly sitePage: Maybe<SitePage>;
readonly sitePlugin: Maybe<SitePlugin>;
readonly staticImage: Maybe<StaticImage>;
};
@ -1609,14 +1583,6 @@ type Query_allSitePluginArgs = {
};
type Query_allStaticImageArgs = {
filter: InputMaybe<StaticImageFilterInput>;
limit: InputMaybe<Scalars['Int']>;
skip: InputMaybe<Scalars['Int']>;
sort: InputMaybe<ReadonlyArray<InputMaybe<StaticImageSortInput>>>;
};
type Query_directoryArgs = {
absolutePath: InputMaybe<StringQueryOperatorInput>;
accessTime: InputMaybe<DateQueryOperatorInput>;
@ -1786,46 +1752,6 @@ type Query_sitePluginArgs = {
version: InputMaybe<StringQueryOperatorInput>;
};
type Query_staticImageArgs = {
absolutePath: InputMaybe<StringQueryOperatorInput>;
accessTime: InputMaybe<DateQueryOperatorInput>;
atime: InputMaybe<DateQueryOperatorInput>;
atimeMs: InputMaybe<FloatQueryOperatorInput>;
base: InputMaybe<StringQueryOperatorInput>;
birthTime: InputMaybe<DateQueryOperatorInput>;
birthtime: InputMaybe<DateQueryOperatorInput>;
birthtimeMs: InputMaybe<FloatQueryOperatorInput>;
blksize: InputMaybe<IntQueryOperatorInput>;
blocks: InputMaybe<IntQueryOperatorInput>;
changeTime: InputMaybe<DateQueryOperatorInput>;
children: InputMaybe<NodeFilterListInput>;
ctime: InputMaybe<DateQueryOperatorInput>;
ctimeMs: InputMaybe<FloatQueryOperatorInput>;
dev: InputMaybe<IntQueryOperatorInput>;
dir: InputMaybe<StringQueryOperatorInput>;
ext: InputMaybe<StringQueryOperatorInput>;
extension: InputMaybe<StringQueryOperatorInput>;
id: InputMaybe<StringQueryOperatorInput>;
ino: InputMaybe<IntQueryOperatorInput>;
internal: InputMaybe<InternalFilterInput>;
mode: InputMaybe<IntQueryOperatorInput>;
modifiedTime: InputMaybe<DateQueryOperatorInput>;
mtime: InputMaybe<DateQueryOperatorInput>;
mtimeMs: InputMaybe<FloatQueryOperatorInput>;
name: InputMaybe<StringQueryOperatorInput>;
nlink: InputMaybe<IntQueryOperatorInput>;
parent: InputMaybe<NodeFilterInput>;
prettySize: InputMaybe<StringQueryOperatorInput>;
rdev: InputMaybe<IntQueryOperatorInput>;
relativeDirectory: InputMaybe<StringQueryOperatorInput>;
relativePath: InputMaybe<StringQueryOperatorInput>;
root: InputMaybe<StringQueryOperatorInput>;
size: InputMaybe<IntQueryOperatorInput>;
sourceInstanceName: InputMaybe<StringQueryOperatorInput>;
uid: InputMaybe<IntQueryOperatorInput>;
};
type Site = Node & {
readonly buildTime: Maybe<Scalars['Date']>;
readonly children: ReadonlyArray<Node>;
@ -2592,312 +2518,6 @@ type SortOrderEnum =
| 'ASC'
| 'DESC';
type StaticImage = Node & {
readonly absolutePath: Maybe<Scalars['String']>;
readonly accessTime: Maybe<Scalars['Date']>;
readonly atime: Maybe<Scalars['Date']>;
readonly atimeMs: Maybe<Scalars['Float']>;
readonly base: Maybe<Scalars['String']>;
readonly birthTime: Maybe<Scalars['Date']>;
readonly birthtime: Maybe<Scalars['Date']>;
readonly birthtimeMs: Maybe<Scalars['Float']>;
readonly blksize: Maybe<Scalars['Int']>;
readonly blocks: Maybe<Scalars['Int']>;
readonly changeTime: Maybe<Scalars['Date']>;
readonly children: ReadonlyArray<Node>;
readonly ctime: Maybe<Scalars['Date']>;
readonly ctimeMs: Maybe<Scalars['Float']>;
readonly dev: Maybe<Scalars['Int']>;
readonly dir: Maybe<Scalars['String']>;
readonly ext: Maybe<Scalars['String']>;
readonly extension: Maybe<Scalars['String']>;
readonly id: Scalars['ID'];
readonly ino: Maybe<Scalars['Int']>;
readonly internal: Internal;
readonly mode: Maybe<Scalars['Int']>;
readonly modifiedTime: Maybe<Scalars['Date']>;
readonly mtime: Maybe<Scalars['Date']>;
readonly mtimeMs: Maybe<Scalars['Float']>;
readonly name: Maybe<Scalars['String']>;
readonly nlink: Maybe<Scalars['Int']>;
readonly parent: Maybe<Node>;
readonly prettySize: Maybe<Scalars['String']>;
readonly rdev: Maybe<Scalars['Int']>;
readonly relativeDirectory: Maybe<Scalars['String']>;
readonly relativePath: Maybe<Scalars['String']>;
readonly root: Maybe<Scalars['String']>;
readonly size: Maybe<Scalars['Int']>;
readonly sourceInstanceName: Maybe<Scalars['String']>;
readonly uid: Maybe<Scalars['Int']>;
};
type StaticImage_accessTimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_atimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_birthTimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_birthtimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_changeTimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_ctimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_modifiedTimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImage_mtimeArgs = {
difference: InputMaybe<Scalars['String']>;
formatString: InputMaybe<Scalars['String']>;
fromNow: InputMaybe<Scalars['Boolean']>;
locale: InputMaybe<Scalars['String']>;
};
type StaticImageConnection = {
readonly distinct: ReadonlyArray<Scalars['String']>;
readonly edges: ReadonlyArray<StaticImageEdge>;
readonly group: ReadonlyArray<StaticImageGroupConnection>;
readonly max: Maybe<Scalars['Float']>;
readonly min: Maybe<Scalars['Float']>;
readonly nodes: ReadonlyArray<StaticImage>;
readonly pageInfo: PageInfo;
readonly sum: Maybe<Scalars['Float']>;
readonly totalCount: Scalars['Int'];
};
type StaticImageConnection_distinctArgs = {
field: StaticImageFieldSelector;
};
type StaticImageConnection_groupArgs = {
field: StaticImageFieldSelector;
limit: InputMaybe<Scalars['Int']>;
skip: InputMaybe<Scalars['Int']>;
};
type StaticImageConnection_maxArgs = {
field: StaticImageFieldSelector;
};
type StaticImageConnection_minArgs = {
field: StaticImageFieldSelector;
};
type StaticImageConnection_sumArgs = {
field: StaticImageFieldSelector;
};
type StaticImageEdge = {
readonly next: Maybe<StaticImage>;
readonly node: StaticImage;
readonly previous: Maybe<StaticImage>;
};
type StaticImageFieldSelector = {
readonly absolutePath: InputMaybe<FieldSelectorEnum>;
readonly accessTime: InputMaybe<FieldSelectorEnum>;
readonly atime: InputMaybe<FieldSelectorEnum>;
readonly atimeMs: InputMaybe<FieldSelectorEnum>;
readonly base: InputMaybe<FieldSelectorEnum>;
readonly birthTime: InputMaybe<FieldSelectorEnum>;
readonly birthtime: InputMaybe<FieldSelectorEnum>;
readonly birthtimeMs: InputMaybe<FieldSelectorEnum>;
readonly blksize: InputMaybe<FieldSelectorEnum>;
readonly blocks: InputMaybe<FieldSelectorEnum>;
readonly changeTime: InputMaybe<FieldSelectorEnum>;
readonly children: InputMaybe<NodeFieldSelector>;
readonly ctime: InputMaybe<FieldSelectorEnum>;
readonly ctimeMs: InputMaybe<FieldSelectorEnum>;
readonly dev: InputMaybe<FieldSelectorEnum>;
readonly dir: InputMaybe<FieldSelectorEnum>;
readonly ext: InputMaybe<FieldSelectorEnum>;
readonly extension: InputMaybe<FieldSelectorEnum>;
readonly id: InputMaybe<FieldSelectorEnum>;
readonly ino: InputMaybe<FieldSelectorEnum>;
readonly internal: InputMaybe<InternalFieldSelector>;
readonly mode: InputMaybe<FieldSelectorEnum>;
readonly modifiedTime: InputMaybe<FieldSelectorEnum>;
readonly mtime: InputMaybe<FieldSelectorEnum>;
readonly mtimeMs: InputMaybe<FieldSelectorEnum>;
readonly name: InputMaybe<FieldSelectorEnum>;
readonly nlink: InputMaybe<FieldSelectorEnum>;
readonly parent: InputMaybe<NodeFieldSelector>;
readonly prettySize: InputMaybe<FieldSelectorEnum>;
readonly rdev: InputMaybe<FieldSelectorEnum>;
readonly relativeDirectory: InputMaybe<FieldSelectorEnum>;
readonly relativePath: InputMaybe<FieldSelectorEnum>;
readonly root: InputMaybe<FieldSelectorEnum>;
readonly size: InputMaybe<FieldSelectorEnum>;
readonly sourceInstanceName: InputMaybe<FieldSelectorEnum>;
readonly uid: InputMaybe<FieldSelectorEnum>;
};
type StaticImageFilterInput = {
readonly absolutePath: InputMaybe<StringQueryOperatorInput>;
readonly accessTime: InputMaybe<DateQueryOperatorInput>;
readonly atime: InputMaybe<DateQueryOperatorInput>;
readonly atimeMs: InputMaybe<FloatQueryOperatorInput>;
readonly base: InputMaybe<StringQueryOperatorInput>;
readonly birthTime: InputMaybe<DateQueryOperatorInput>;
readonly birthtime: InputMaybe<DateQueryOperatorInput>;
readonly birthtimeMs: InputMaybe<FloatQueryOperatorInput>;
readonly blksize: InputMaybe<IntQueryOperatorInput>;
readonly blocks: InputMaybe<IntQueryOperatorInput>;
readonly changeTime: InputMaybe<DateQueryOperatorInput>;
readonly children: InputMaybe<NodeFilterListInput>;
readonly ctime: InputMaybe<DateQueryOperatorInput>;
readonly ctimeMs: InputMaybe<FloatQueryOperatorInput>;
readonly dev: InputMaybe<IntQueryOperatorInput>;
readonly dir: InputMaybe<StringQueryOperatorInput>;
readonly ext: InputMaybe<StringQueryOperatorInput>;
readonly extension: InputMaybe<StringQueryOperatorInput>;
readonly id: InputMaybe<StringQueryOperatorInput>;
readonly ino: InputMaybe<IntQueryOperatorInput>;
readonly internal: InputMaybe<InternalFilterInput>;
readonly mode: InputMaybe<IntQueryOperatorInput>;
readonly modifiedTime: InputMaybe<DateQueryOperatorInput>;
readonly mtime: InputMaybe<DateQueryOperatorInput>;
readonly mtimeMs: InputMaybe<FloatQueryOperatorInput>;
readonly name: InputMaybe<StringQueryOperatorInput>;
readonly nlink: InputMaybe<IntQueryOperatorInput>;
readonly parent: InputMaybe<NodeFilterInput>;
readonly prettySize: InputMaybe<StringQueryOperatorInput>;
readonly rdev: InputMaybe<IntQueryOperatorInput>;
readonly relativeDirectory: InputMaybe<StringQueryOperatorInput>;
readonly relativePath: InputMaybe<StringQueryOperatorInput>;
readonly root: InputMaybe<StringQueryOperatorInput>;
readonly size: InputMaybe<IntQueryOperatorInput>;
readonly sourceInstanceName: InputMaybe<StringQueryOperatorInput>;
readonly uid: InputMaybe<IntQueryOperatorInput>;
};
type StaticImageGroupConnection = {
readonly distinct: ReadonlyArray<Scalars['String']>;
readonly edges: ReadonlyArray<StaticImageEdge>;
readonly field: Scalars['String'];
readonly fieldValue: Maybe<Scalars['String']>;
readonly group: ReadonlyArray<StaticImageGroupConnection>;
readonly max: Maybe<Scalars['Float']>;
readonly min: Maybe<Scalars['Float']>;
readonly nodes: ReadonlyArray<StaticImage>;
readonly pageInfo: PageInfo;
readonly sum: Maybe<Scalars['Float']>;
readonly totalCount: Scalars['Int'];
};
type StaticImageGroupConnection_distinctArgs = {
field: StaticImageFieldSelector;
};
type StaticImageGroupConnection_groupArgs = {
field: StaticImageFieldSelector;
limit: InputMaybe<Scalars['Int']>;
skip: InputMaybe<Scalars['Int']>;
};
type StaticImageGroupConnection_maxArgs = {
field: StaticImageFieldSelector;
};
type StaticImageGroupConnection_minArgs = {
field: StaticImageFieldSelector;
};
type StaticImageGroupConnection_sumArgs = {
field: StaticImageFieldSelector;
};
type StaticImageSortInput = {
readonly absolutePath: InputMaybe<SortOrderEnum>;
readonly accessTime: InputMaybe<SortOrderEnum>;
readonly atime: InputMaybe<SortOrderEnum>;
readonly atimeMs: InputMaybe<SortOrderEnum>;
readonly base: InputMaybe<SortOrderEnum>;
readonly birthTime: InputMaybe<SortOrderEnum>;
readonly birthtime: InputMaybe<SortOrderEnum>;
readonly birthtimeMs: InputMaybe<SortOrderEnum>;
readonly blksize: InputMaybe<SortOrderEnum>;
readonly blocks: InputMaybe<SortOrderEnum>;
readonly changeTime: InputMaybe<SortOrderEnum>;
readonly children: InputMaybe<NodeSortInput>;
readonly ctime: InputMaybe<SortOrderEnum>;
readonly ctimeMs: InputMaybe<SortOrderEnum>;
readonly dev: InputMaybe<SortOrderEnum>;
readonly dir: InputMaybe<SortOrderEnum>;
readonly ext: InputMaybe<SortOrderEnum>;
readonly extension: InputMaybe<SortOrderEnum>;
readonly id: InputMaybe<SortOrderEnum>;
readonly ino: InputMaybe<SortOrderEnum>;
readonly internal: InputMaybe<InternalSortInput>;
readonly mode: InputMaybe<SortOrderEnum>;
readonly modifiedTime: InputMaybe<SortOrderEnum>;
readonly mtime: InputMaybe<SortOrderEnum>;
readonly mtimeMs: InputMaybe<SortOrderEnum>;
readonly name: InputMaybe<SortOrderEnum>;
readonly nlink: InputMaybe<SortOrderEnum>;
readonly parent: InputMaybe<NodeSortInput>;
readonly prettySize: InputMaybe<SortOrderEnum>;
readonly rdev: InputMaybe<SortOrderEnum>;
readonly relativeDirectory: InputMaybe<SortOrderEnum>;
readonly relativePath: InputMaybe<SortOrderEnum>;
readonly root: InputMaybe<SortOrderEnum>;
readonly size: InputMaybe<SortOrderEnum>;
readonly sourceInstanceName: InputMaybe<SortOrderEnum>;
readonly uid: InputMaybe<SortOrderEnum>;
};
type StringQueryOperatorInput = {
readonly eq: InputMaybe<Scalars['String']>;
readonly glob: InputMaybe<Scalars['String']>;
@ -2927,12 +2547,10 @@ type GalleryImageQueryVariables = Exact<{
type GalleryImageQuery = { readonly file: { readonly base: string, readonly publicURL: string | null, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly dateTaken: string | null, readonly meta: { readonly Make: string | null, readonly Model: string | null, readonly ExposureTime: number | null, readonly FNumber: number | null, readonly ISO: number | null, readonly DateTimeOriginal: string | null, readonly CreateDate: string | null, readonly ShutterSpeedValue: number | null, readonly ApertureValue: number | null, readonly FocalLength: number | null, readonly LensModel: string | null, readonly ObjectName: string | null, readonly Caption: string | null, readonly Location: string | null, readonly City: string | null, readonly State: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null } | null };
type GalleryImageFileFragment = { readonly nodes: ReadonlyArray<{ readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly datePublished: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> };
type GalleryPageQueryQueryVariables = Exact<{ [key: string]: never; }>;
type GalleryPageQueryQuery = { readonly recents: { readonly nodes: ReadonlyArray<{ readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly datePublished: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> }, readonly all: { readonly nodes: ReadonlyArray<{ readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly datePublished: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null, readonly CreateDate: string | null, readonly ModifyDate: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> } };
type GalleryPageQueryQuery = { readonly allFile: { readonly nodes: ReadonlyArray<{ readonly relativePath: string, readonly base: string, readonly childImageSharp: { readonly gatsbyImageData: import('gatsby-plugin-image').IGatsbyImageData, readonly fluid: { readonly aspectRatio: number } | null } | null, readonly fields: { readonly imageMeta: { readonly vibrantHue: number | null, readonly dominantHue: ReadonlyArray<number | null> | null, readonly dateTaken: string | null, readonly meta: { readonly Keywords: ReadonlyArray<string | null> | null, readonly Rating: number | null, readonly ObjectName: string | null } | null, readonly vibrant: { readonly DarkMuted: ReadonlyArray<number | null> | null, readonly DarkVibrant: ReadonlyArray<number | null> | null, readonly LightMuted: ReadonlyArray<number | null> | null, readonly LightVibrant: ReadonlyArray<number | null> | null, readonly Vibrant: ReadonlyArray<number | null> | null, readonly Muted: ReadonlyArray<number | null> | null } | null } | null } | null }> } };
type GatsbyImageSharpFixedFragment = { readonly base64: string | null, readonly width: number, readonly height: number, readonly src: string, readonly srcSet: string };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

View File

@ -102,8 +102,8 @@ const IndexPage = ({
objectFit={browserIsLandscape ? "cover" : "contain"}
style={{
height: screenHeight
? `${screenHeight - 268}px`
: "calc(100vh-268px)",
? `${screenHeight - 160}px`
: "calc(100vh-160px)",
}}
/>
</Link>

View File

@ -1,12 +1,12 @@
import * as React from "react";
import * as R from "ramda";
import { graphql, Link, navigate, PageProps } from "gatsby";
import { graphql, PageProps } from "gatsby";
import { Helmet } from "react-helmet";
// import { Picker, Item } from "@adobe/react-spectrum";
import MasonryGallery from "../components/MasonryGallery";
import KeywordsPicker from "../components/KeywordsPicker";
import {
compareDates,
getGalleryPageUrl,
getHelmetSafeBodyStyle,
getVibrantStyle,
@ -15,56 +15,50 @@ import Nav from "../components/Nav";
import { Item, Select } from "../components/Select";
import { Switch } from "../components/Switch";
import ColorPalette from "@spectrum-icons/workflow/ColorPalette";
import { ToggleButton } from "../components/ToggleButton";
const SORT_KEYS = {
hue: ["fields", "imageMeta", "vibrantHue"],
rating: ["fields", "imageMeta", "meta", "Rating"],
// hue_debug: ["fields", "imageMeta", "dominantHue", 0],
hue_debug: ["fields", "imageMeta", "dominantHue", "0"],
date: ["fields", "imageMeta", "dateTaken"],
datePublished: ["fields", "imageMeta", "datePublished"],
hue_debug: ["fields", "imageMeta", "dominantHue", 0],
date: [],
} as const;
export type GalleryImage =
Queries.GalleryPageQueryQuery["all"]["nodes"][number];
Queries.GalleryPageQueryQuery["allFile"]["nodes"][number];
function smartCompareDates(
key: keyof typeof SORT_KEYS,
left: GalleryImage,
right: GalleryImage
) {
let diff = compareDates(SORT_KEYS[key], left, right);
if (diff !== 0) {
return diff;
}
return compareDates(SORT_KEYS.date, left, right);
}
const GalleryPage = ({ data }: PageProps<Queries.GalleryPageQueryQuery>) => {
const hash =
typeof window !== "undefined" ? window.location.hash.replace("#", "") : "";
const GalleryPage = ({
data,
location,
}: PageProps<Queries.GalleryPageQueryQuery>) => {
const hash = location.hash ? location.hash.replace("#", "") : "";
const params = new URLSearchParams(location.search);
const filterKeyword = params.get("filter");
const sortKey = params.get("sort") ?? "rating";
const showDebug = Boolean(params.get("debug")?.length);
const [hashCleared, setHashCleared] = React.useState(false); // eslint-disable-line no-unused-vars
// ^ used just to force a re-render with the cleared hash value (I know, it's a smell for sure)
const [filterKeyword, _setKeyword] = React.useState(null as string | null);
const [sortKey, _setSortKey] = React.useState("rating" as string);
const showDebug =
typeof window !== "undefined" &&
window.location.search.includes("debug=true");
const [showPalette, setShowPalette] = React.useState(false);
const onKeywordPick = React.useCallback((newKeyword: string | null) => {
if (newKeyword) {
try {
window.plausible("Filter Keyword", {
props: { keyword: newKeyword },
});
} catch (e) {
// do nothing
const setKeyword = React.useCallback(
(newKeyword: string | null) => {
if (newKeyword) {
try {
window.plausible("Filter Keyword", {
props: { keyword: newKeyword },
});
} catch (e) {
// do nothing
}
}
}
}, []);
_setKeyword(newKeyword);
window.history.replaceState(
null,
"",
getGalleryPageUrl({ keyword: newKeyword, sortKey }, hash)
);
},
[_setKeyword, sortKey, hash]
);
const setSortKey = React.useCallback(
(newSortKey: string) => {
@ -75,65 +69,75 @@ const GalleryPage = ({
} catch (e) {
// do nothing
}
navigate(
getGalleryPageUrl(
{ sortKey: newSortKey, keyword: filterKeyword, showDebug },
hash
)
// { replace: true }
_setSortKey(newSortKey);
window.history.replaceState(
null,
"",
getGalleryPageUrl({ sortKey: newSortKey, keyword: filterKeyword }, hash)
);
},
[filterKeyword, hash, showDebug]
[_setSortKey, filterKeyword, hash]
);
const removeHash = React.useCallback(() => {
if (!hash.length) {
const url = new URL(
typeof window !== "undefined"
? window.location.href.toString()
: "https://chuckdries.com/photogallery/"
);
url.hash = "";
window.history.replaceState(null, "", url.href.toString());
window.removeEventListener("wheel", removeHash);
setHashCleared(true);
}, []);
const scrollIntoView = React.useCallback(() => {
if (!hash) {
return;
}
console.log('remove hash')
navigate(
getGalleryPageUrl({ sortKey, keyword: filterKeyword, showDebug }, ""),
{ replace: true }
);
window.removeEventListener("scroll", removeHash);
}, [hash, sortKey, filterKeyword, showDebug]);
React.useEffect(() => {
// window.addEventListener("scroll", removeHash);
return () => {
window.removeEventListener("scroll", removeHash);
};
}, [removeHash]);
React.useEffect(() => {
// hacky but it works for now
requestAnimationFrame(() => {
// don't scroll into view if user got here with back button or if we just cleared it
if (!hash.length) {
return;
}
const el = document.getElementById(hash);
if (!el) {
console.log("⚠failed to find hash");
return;
}
console.log("scrolling into view manually", el.offsetTop);
el.scrollIntoView({
block: hash.startsWith("all") ? "start" : "center",
behavior: hash.startsWith("all") ? "smooth" : "auto",
});
setTimeout(() => {
window.addEventListener("scroll", removeHash);
}, 100);
const el = document.getElementById(hash);
if (!el) {
return;
}
el.scrollIntoView({
block: "center",
});
window.addEventListener("wheel", removeHash);
}, [hash, removeHash]);
React.useEffect(() => {
const url = new URL(window.location.toString());
const sortKeyFromUrl = url.searchParams.get("sort");
if (sortKeyFromUrl) {
_setSortKey(sortKeyFromUrl);
}
const filterKeyFromUrl = url.searchParams.get("filter");
if (filterKeyFromUrl) {
_setKeyword(filterKeyFromUrl);
}
// hacky but it works for now
setTimeout(() => {
// don't scroll into view if user got here with back button
scrollIntoView();
}, 100);
}, [setSortKey, setKeyword, scrollIntoView]);
const images: GalleryImage[] = React.useMemo(() => {
const sort =
sortKey === "date" || sortKey === "datePublished"
? R.sort((node1: typeof data["all"]["nodes"][number], node2) =>
smartCompareDates(sortKey, node1, node2)
)
sortKey === "date"
? R.sort((node1: typeof data["allFile"]["nodes"][number], node2) => {
const date1 = new Date(
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node1)
);
const date2 = new Date(
R.pathOr("", ["fields", "imageMeta", "dateTaken"], node2)
);
return -1 * (date1.getTime() - date2.getTime());
})
: R.sort(
// @ts-ignore
R.descend(R.path<GalleryImage>(SORT_KEYS[sortKey]))
@ -153,7 +157,7 @@ const GalleryPage = ({
// @ts-ignore
sort,
filter
)(data.all.nodes) as any;
)(data.allFile.nodes) as any;
return ret;
} catch (e) {
console.log("caught images!", e);
@ -161,52 +165,6 @@ const GalleryPage = ({
}
}, [data, sortKey, filterKeyword]);
const recents = React.useMemo(() => {
return R.sort(
(left, right) => smartCompareDates("datePublished", left, right),
data.recents.nodes
);
}, [data]);
const [dbgTags, setDbgTags] = React.useState(false);
const [dbgSortKey, setDbgSortKey] = React.useState(false);
const [dbgName, setDbgName] = React.useState(false);
const dataFn = React.useCallback(
(image: GalleryImage): string[] | null => {
if (!showDebug) {
return null;
}
let data: string[] = [];
if (dbgName) {
data.push(image.base);
}
if (dbgSortKey) {
switch (sortKey) {
case "hue":
case "rating": {
data.push(R.pathOr("x", SORT_KEYS[sortKey], image));
break;
}
case "date":
case "datePublished": {
const date = R.pathOr(null, SORT_KEYS[sortKey], image);
if (date) {
data.push(new Date(date).toLocaleString());
} else {
data.push("x");
}
break;
}
}
}
if (dbgTags) {
data.push(image.fields?.imageMeta?.meta?.Keywords?.join(",") ?? "x");
}
return data;
},
[showDebug, sortKey, dbgName, dbgSortKey, dbgTags]
);
return (
<>
{/* @ts-ignore */}
@ -216,7 +174,6 @@ const GalleryPage = ({
className="bg-white transition-color"
// @ts-ignore
style={getHelmetSafeBodyStyle(
// @ts-ignore shrug
getVibrantStyle({
Muted: [0, 0, 0],
LightMuted: [0, 0, 0],
@ -238,50 +195,8 @@ const GalleryPage = ({
]}
/>
</div>
<div className="gradient pb-6">
<div className="px-4 md:px-8 flex items-baseline">
<h3 className="mx-2 font-bold" id="recently">
Recently published
</h3>
{sortKey !== "datePublished" && (
<Link
className="underline cursor-pointer text-gray-500"
to="?sort=datePublished#all"
>
show more
</Link>
)}
</div>
<MasonryGallery
aspectsByBreakpoint={{
xs: 3,
sm: 3,
md: 4,
lg: 4,
xl: 5,
"2xl": 6,
"3xl": 8,
}}
images={recents}
linkState={{
sortKey: 'datePublished',
filterKeyword,
}}
singleRow
/>
</div>
<div className="px-4 md:px-8 mt-2 pt-2">
<h3 className="mx-2 font-bold" id="all">
All images
</h3>
</div>
<div className="flex flex-col lg:flex-row lg:items-end justify-between px-4 md:px-8 sm:mx-auto">
<KeywordsPicker
getHref={(val, selected) =>
selected
? getGalleryPageUrl({ keyword: null, sortKey, showDebug }, hash)
: getGalleryPageUrl({ keyword: val, sortKey, showDebug }, hash)
}
keywords={[
"Boyce Thompson Arboretum",
"winter",
@ -291,52 +206,38 @@ const GalleryPage = ({
"landscape",
"flowers",
"product",
// "waterfall",
// "fireworks",
// "panoramic",
"waterfall",
"fireworks",
"panoramic",
"Portland Japanese Garden",
// "shoot the light",
// "sunset",
]}
onPick={onKeywordPick}
onChange={setKeyword}
value={filterKeyword}
/>
<div className="my-2 mx-2 flex flex-row items-end">
{showDebug && (
<div className="mr-2">
<ToggleButton isSelected={dbgName} onChange={setDbgName}>
name
</ToggleButton>
<ToggleButton isSelected={dbgSortKey} onChange={setDbgSortKey}>
sort key
</ToggleButton>
<ToggleButton isSelected={dbgTags} onChange={setDbgTags}>
tags
</ToggleButton>
</div>
)}
<div className="border border-gray-400 rounded mr-2">
<Switch
isSelected={showPalette}
onChange={(val) => setShowPalette(val)}
>
<ColorPalette
UNSAFE_style={{
width: "24px",
margin: "0 4px",
}}
/>
</Switch>
<div className="m-2 flex flex-row items-end">
<div className="border border-black rounded mr-2">
<Switch
isSelected={showPalette}
onChange={(val) => setShowPalette(val)}
>
<ColorPalette
UNSAFE_style={{
width: "24px",
margin: "0 4px",
}}
/>
</Switch>
</div>
<Select
label="Sort by..."
// @ts-expect-error React.key, but string is more convenient for the state
// @ts-ignore
onSelectionChange={setSortKey}
selectedKey={sortKey}
>
<Item key="rating">Curated</Item>
<Item key="datePublished">Date published</Item>
<Item key="date">Date taken</Item>
<Item key="date">Date</Item>
<Item key="hue">Hue</Item>
</Select>
</div>
@ -352,8 +253,8 @@ const GalleryPage = ({
"2xl": 6.1,
"3xl": 8,
}}
dataFn={dataFn}
debugHue={sortKey === "hue_debug"}
debugRating={sortKey === "rating" && showDebug}
images={images}
linkState={{
sortKey,
@ -367,49 +268,37 @@ const GalleryPage = ({
export const query = graphql`
query GalleryPageQuery {
recents: allFile(
filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: { imageMeta: { datePublished: DESC } } }
limit: 10
) {
...GalleryImageFile
}
all: allFile(
allFile(
filter: { sourceInstanceName: { eq: "gallery" } }
sort: { fields: { imageMeta: { dateTaken: DESC } } }
) {
...GalleryImageFile
}
}
fragment GalleryImageFile on FileConnection {
nodes {
base
childImageSharp {
fluid {
aspectRatio
}
gatsbyImageData(
layout: CONSTRAINED
height: 550
placeholder: DOMINANT_COLOR
)
}
fields {
imageMeta {
vibrantHue
dominantHue
dateTaken
datePublished
meta {
Keywords
Rating
ObjectName
CreateDate
ModifyDate
nodes {
relativePath
base
childImageSharp {
fluid {
aspectRatio
}
vibrant {
...VibrantColors
gatsbyImageData(
layout: CONSTRAINED
height: 550
placeholder: DOMINANT_COLOR
)
}
fields {
imageMeta {
vibrantHue
dominantHue
dateTaken
meta {
Keywords
Rating
ObjectName
}
vibrant {
# Vibrant
...VibrantColors
}
}
}
}

View File

@ -9,9 +9,6 @@
* {
box-sizing: border-box;
}
:root {
/* scroll-behavior: smooth; */
}
/* .hero * {
transition: color .2s, background-color .2s;
} */
@ -86,15 +83,27 @@
.gradient {
background-image: linear-gradient(
180deg,
hsla(0deg, 0%, 0%, 0%) 95%,
hsla(0deg, 0%, 0%, 10%) 100%
hsl(31deg 24% 44%) 0%,
hsl(27deg 25% 42%) 7%,
hsl(22deg 26% 40%) 14%,
hsl(18deg 27% 37%) 21%,
hsl(12deg 28% 35%) 29%,
hsl(9deg 29% 32%) 36%,
hsl(4deg 30% 30%) 43%,
hsl(0deg 31% 27%) 50%,
hsl(0deg 31% 23%) 57%,
hsl(0deg 31% 19%) 64%,
hsl(0deg 32% 15%) 71%,
hsl(0deg 30% 12%) 79%,
hsl(0deg 30% 8%) 86%,
hsl(0deg 30% 4%) 93%,
hsl(0deg 0% 0%) 100%
);
}
}
body {
@apply bg-gray-100;
overflow: auto;
/* @apply bg-black; */
/* @apply text-white; */
}

View File

@ -1,23 +1,18 @@
import React, { useCallback, useRef } from "react";
import { pathOr } from "ramda";
// import kebabCase from 'lodash/kebabCase';
import React from "react";
import { HomepageImage } from "./pages";
import { GalleryImage } from "./pages/photogallery";
export const getMeta = <T extends GalleryImage | HomepageImage>(image: T) =>
image.fields?.imageMeta;
export const getMeta = <T extends GalleryImage | HomepageImage>(image: T) => image.fields?.imageMeta;
export const getName = (image: GalleryImage) =>
image.fields?.imageMeta?.meta?.ObjectName || image.base;
image.fields?.imageMeta?.meta?.ObjectName || image.base;
// some pleasing default colors for SSR and initial hydration
export const getVibrant = (image: GalleryImage | HomepageImage) =>
getMeta(image)?.vibrant;
export const getVibrant = (image: GalleryImage | HomepageImage) => getMeta(image)?.vibrant;
export const hasName = (image: GalleryImage) =>
Boolean(image.fields?.imageMeta?.meta?.ObjectName);
export const hasName = (image: GalleryImage) => Boolean(image.fields?.imageMeta?.meta?.ObjectName);
export const getAspectRatio = (image: GalleryImage | HomepageImage): number =>
image.childImageSharp?.fluid?.aspectRatio ?? 1;
@ -30,10 +25,7 @@ export const getCanonicalSize = (image: GalleryImage) => ({
export const getRgba = (palette: string[], alpha: number) =>
`rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, ${alpha || 1})`;
export const getVibrantStyle = (
vibrant: Queries.FileFieldsImageMetaVibrant,
screenHeight?: number
) => ({
export const getVibrantStyle = (vibrant: Queries.FileFieldsImageMetaVibrant, screenHeight?: number) => ({
"--muted": vibrant.Muted,
"--dark-muted": vibrant.DarkMuted,
"--light-muted": vibrant.LightMuted,
@ -48,12 +40,10 @@ export const getHelmetSafeBodyStyle = (style: React.CSSProperties) => {
if (typeof window === "undefined") {
return style;
}
return (
Object.keys(style)
// @ts-ignore
.map((key) => `${key}: ${style[key]};`)
.join("")
);
return Object.keys(style)
// @ts-ignore
.map((key) => `${key}: ${style[key]};`)
.join("");
};
const gcd = (a: number, b: number): number => {
@ -65,9 +55,6 @@ const gcd = (a: number, b: number): number => {
};
export const getShutterFractionFromExposureTime = (exposureTime: number) => {
if (exposureTime === 0.3333333333333333) {
return "1/3";
}
if (exposureTime === 0.03333333333333333) {
return "1/30";
}
@ -101,14 +88,10 @@ export const getShutterFractionFromExposureTime = (exposureTime: number) => {
interface galleryPageUrlProps {
keyword: string | null;
sortKey: string | null;
showDebug: boolean;
sortKey: string;
}
export const getGalleryPageUrl = (
{ keyword, sortKey, showDebug }: galleryPageUrlProps,
hash: string
) => {
export const getGalleryPageUrl = ({ keyword, sortKey }: galleryPageUrlProps, hash: string) => {
const url = new URL(
`${
typeof window !== "undefined"
@ -124,55 +107,14 @@ export const getGalleryPageUrl = (
}
}
if (sortKey !== undefined) {
if (sortKey === "rating" || sortKey === null) {
if (sortKey === "rating") {
url.searchParams.delete("sort");
} else {
url.searchParams.set("sort", sortKey);
}
}
if (showDebug) {
url.searchParams.set("debug", "true");
}
if (hash) {
url.hash = hash;
}
return url.href.toString().replace(url.origin, "");
};
export function compareDates<T>(
date_path: readonly string[],
left: T,
right: T
): number {
// why tf do my dates have newlines in them?!?!
const date1 = new Date(pathOr("", date_path, left).replace(/\s/g, ""));
const date2 = new Date(pathOr("", date_path, right).replace(/\s/g, ""));
const diff = -1 * (date1.getTime() - date2.getTime());
return diff;
}
/**
* Returns a memoized function that will only call the passed function when it hasn't been called for the wait period
* @param func The function to be called
* @param wait Wait period after function hasn't been called for
* @returns A memoized function that is debounced
*/
export const useDebouncedCallback = (func: Function, wait: number) => {
// Use a ref to store the timeout between renders
// and prevent changes to it from causing re-renders
const timeout = useRef<ReturnType<typeof setTimeout>>();
return useCallback(
(...args: any) => {
const later = () => {
clearTimeout(timeout.current!);
func(...args);
};
clearTimeout(timeout.current ?? undefined);
timeout.current = setTimeout(later, wait);
},
[func, wait]
);
};

View File

@ -4560,22 +4560,6 @@ __metadata:
languageName: node
linkType: hard
"@types/lodash.debounce@npm:^4.0.7":
version: 4.0.7
resolution: "@types/lodash.debounce@npm:4.0.7"
dependencies:
"@types/lodash": "*"
checksum: e873b2d77f89010876baba3437ef826b17221b98948e00b5590828334a481dea1c8f9d28543210e564adc53199584f42c3cb171f8b6c3614fefc0b4e0888679c
languageName: node
linkType: hard
"@types/lodash@npm:*":
version: 4.14.191
resolution: "@types/lodash@npm:4.14.191"
checksum: ba0d5434e10690869f32d5ea49095250157cae502f10d57de0a723fd72229ce6c6a4979576f0f13e0aa9fbe3ce2457bfb9fa7d4ec3d6daba56730a51906d1491
languageName: node
linkType: hard
"@types/lodash@npm:^4.14.53":
version: 4.14.170
resolution: "@types/lodash@npm:4.14.170"
@ -6480,7 +6464,6 @@ __metadata:
"@react-spectrum/provider": ^3.6.0
"@spectrum-icons/workflow": ^4.0.4
"@types/chroma-js": ^2.1.4
"@types/lodash.debounce": ^4.0.7
"@types/node": ^18.8.3
"@types/ramda": ^0.28.15
"@types/react": ^18.0.21
@ -6512,7 +6495,6 @@ __metadata:
gatsby-source-filesystem: ^5.0.0
gatsby-transformer-sharp: ^5.0.0
kebab-case: ^1.0.1
lodash.debounce: ^4.0.8
node-iptc: ^1.0.5
node-vibrant: 3.1.6
postcss: ^8.4.19