Skip to content

Instantly share code, notes, and snippets.

@ryanjafari
Created September 22, 2023 20:01
Show Gist options
  • Save ryanjafari/6c9f973f1d4a5caab978e96dcae05054 to your computer and use it in GitHub Desktop.
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
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