Created
September 22, 2023 20:01
-
-
Save ryanjafari/6c9f973f1d4a5caab978e96dcae05054 to your computer and use it in GitHub Desktop.
Loading images from static imports or from filesystem for use with Next.js Image element
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Head from 'next/head' | |
import Image from 'next/image' | |
import { useCallback, useEffect, useState } from 'react' | |
import { Button } from '@/components/Button' | |
import { Modal } from '@/components/Modal' | |
import { SimpleLayout } from '@/components/SimpleLayout' | |
import screenEdivv1 from '@/images/screens/edivv/1-edivv-home-page.png' | |
import screenEdivv2 from '@/images/screens/edivv/2-edivv-featured-items.png' | |
import screenEdivv3 from '@/images/screens/edivv/3-edivv-facebook-sign-in.png' | |
import screenEdivv4 from '@/images/screens/edivv/4-edivv-item-detail.png' | |
const staticScreenModules = { | |
edivv: { | |
[extractFilename(screenEdivv1)]: screenEdivv1, | |
[extractFilename(screenEdivv2)]: screenEdivv2, | |
[extractFilename(screenEdivv3)]: screenEdivv3, | |
[extractFilename(screenEdivv4)]: screenEdivv4, | |
}, | |
} | |
export default function Project({ projectSlug, screenFilenames }) { | |
const [isModalOpen, setModalOpen] = useState(false) | |
const [selectedScreen, setSelectedScreen] = useState({}) | |
const [screenModules, setScreenModules] = useState({}) | |
useEffect(() => { | |
// If the projectSlug exists in staticScreenModules, use the static imports | |
if (staticScreenModules[projectSlug] && !screenFilenames) { | |
setScreenModules(staticScreenModules[projectSlug]) | |
} | |
// Otherwise, proceed with dynamic imports | |
else if (!staticScreenModules[projectSlug] && screenFilenames) { | |
Promise.all( | |
screenFilenames.map((screenFilename) => | |
import(`@/images/screens/${projectSlug}/${screenFilename}`).then( | |
(mod) => { | |
return { [screenFilename]: mod.default } | |
} | |
) | |
) | |
) | |
.then((modules) => { | |
const newScreenModules = Object.assign({}, ...modules) | |
setScreenModules(newScreenModules) | |
}) | |
.catch((error) => { | |
console.error('Failed to load module:', error) | |
}) | |
} | |
}, [projectSlug, screenFilenames]) | |
const handleScreenClick = useCallback((module, alt) => { | |
setSelectedScreen({ | |
screenModule: module, | |
screenAlt: alt, | |
}) | |
setModalOpen(true) | |
}, []) | |
const projectDisplayName = capitalizeFirstLetter(projectSlug) | |
const introMessage = `Screenshots from ${projectDisplayName}` | |
return ( | |
<> | |
<Head> | |
<title>{`${projectDisplayName} - Ryan Jafari`}</title> | |
<meta name="description" content={introMessage} /> | |
</Head> | |
<SimpleLayout title={projectDisplayName} intro={introMessage}> | |
<Button href="/projects" variant="secondary"> | |
Back to Projects | |
</Button> | |
<ul | |
role="list" | |
className="mt-6 grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8" | |
> | |
{Object.entries(screenModules).map( | |
([screenFilename, screenModule]) => { | |
//const screenModule = screenModules[screenFilename] | |
if (!screenModule) return null // Don't render until the module is loaded | |
const screenDisplayName = transformFilename(screenFilename) | |
const screenAlt = `Screenshot from ${projectDisplayName}: ${screenDisplayName}` | |
return ( | |
<li key={screenFilename} className="relative"> | |
<div className="group aspect-h-7 aspect-w-10 block w-full overflow-hidden rounded-lg bg-gray-100 focus-within:ring-2 focus-within:ring-indigo-500 focus-within:ring-offset-2 focus-within:ring-offset-gray-100"> | |
<Image | |
src={screenModule} | |
alt={screenAlt} | |
className="pointer-events-none object-cover group-hover:opacity-75" | |
priority | |
/> | |
<button | |
type="button" | |
className="absolute inset-0 focus:outline-none" | |
onClick={() => handleScreenClick(screenModule, screenAlt)} | |
> | |
<span className="sr-only"> | |
{`View details for ${screenDisplayName}`} | |
</span> | |
</button> | |
</div> | |
<p className="pointer-events-none mt-2 block truncate text-sm font-medium text-gray-900"> | |
{screenDisplayName} | |
</p> | |
<p className="pointer-events-none block text-sm font-medium text-gray-500"> | |
{screenFilename} | |
</p> | |
</li> | |
) | |
} | |
)} | |
</ul> | |
{selectedScreen && ( | |
<Modal | |
screen={selectedScreen} | |
open={isModalOpen} | |
onClose={() => setModalOpen(false)} | |
/> | |
)} | |
</SimpleLayout> | |
</> | |
) | |
} | |
const capitalizeFirstLetter = (word) => | |
word.charAt(0).toUpperCase() + word.slice(1) | |
const removeFileExtension = (filename) => filename.replace(/\.[^\.]+$/, '') | |
const removeLeadingNumber = (filename) => filename.replace(/^\d+-/, '') | |
const transformFilename = (filename) => { | |
const withoutExtension = removeFileExtension(filename) | |
const withoutNumber = removeLeadingNumber(withoutExtension) | |
const words = withoutNumber.split('-').map(capitalizeFirstLetter) | |
return words.join(' ') | |
} | |
function extractFilename(module) { | |
const path = module.src | |
const filename = path.split('/').pop() | |
return filename.replace(/\.[a-f0-9]{8}\.png$/, '.png') | |
} | |
export async function getStaticPaths() { | |
// Here you would fetch or define the list of slugs you have | |
// Can read from the file system the folders in @/images/screens/* | |
const projectSlugs = ['edivv'] | |
const paths = projectSlugs.map((projectSlug) => ({ | |
params: { projectSlug }, | |
})) | |
return { | |
paths, | |
fallback: false, // See the "fallback" section below | |
} | |
} | |
export async function getStaticProps({ params }) { | |
const { projectSlug } = params | |
// If the projectSlug exists in staticScreenModules, skip reading from the filesystem | |
if (staticScreenModules[projectSlug]) { | |
return { | |
props: { | |
projectSlug, | |
}, | |
} | |
} | |
const path = require('path') | |
const fs = require('fs') | |
const screensDirectory = path.join( | |
process.cwd(), | |
`src/images/screens/${projectSlug}` | |
) | |
const screenFilenames = fs.readdirSync(screensDirectory) | |
return { | |
props: { | |
projectSlug, | |
screenFilenames, | |
}, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment