Skip to content

Commit

Permalink
Add image lightbox
Browse files Browse the repository at this point in the history
  • Loading branch information
HudsonGraeme committed Jun 12, 2023
1 parent aeb2739 commit f939745
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 114 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="/manifest.json" />
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon.png" />
<link rel="icon" type="image/ico" href="/favicon.ico" />
<link rel="icon" type="image/ico" href="https://carspotter.ca/favicon.ico" />
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@solidjs/router": "^0.8.2",
"@stitches/core": "^1.2.8",
"date-fns": "^2.29.3",
"exifr": "^7.1.3",
"ky": "^0.33.3",
"lodash": "^4.17.21",
"solid-icons": "^1.0.4",
Expand Down
139 changes: 134 additions & 5 deletions src/Components/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,152 @@
import { Image as HopeImage, ImageProps, Text, useColorModeValue, VStack } from '@hope-ui/solid'
import { createSignal, Show } from 'solid-js'
import {
Button,
Flex,
Heading,
HStack,
Image as HopeImage,
ImageProps,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Skeleton,
Table,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
useColorModeValue,
VStack,
} from '@hope-ui/solid'
import { format } from 'date-fns'
import exifr from 'exifr'
import { get, isEmpty, isNil, omitBy } from 'lodash'
import { FiDownload } from 'solid-icons/fi'
import { createEffect, createSignal, For, Show } from 'solid-js'
import { toDataURL } from '../utilities'
import Crash from './Asset/crash'
import Loading from './Asset/Loading'

const Image = ({ src, ...otherProps }: ImageProps) => {
const Image = ({ src, alt, ...otherProps }: ImageProps) => {
const [loaded, setLoaded] = createSignal(false)
const [error, setError] = createSignal(false)
const [modalOpen, setModalOpen] = createSignal(false)
const [exifData, setExifData] = createSignal(null)
const [dataURL, setDataURL] = createSignal(null)
const [fullSizeDataURL, setFullSizeDataURL] = createSignal(null)
const [largeImageLoaded, setLargeImageLoaded] = createSignal(false)
const errorTextColor = useColorModeValue('$blackAlpha10', '$whiteAlpha10')
const loadingFillColor = useColorModeValue('$blackAlpha7', '$whiteAlpha7')
const noImageBackgroundColor = useColorModeValue('$whiteAlpha10', '$blackAlpha10')

createEffect(() => {
if (src && !dataURL()) {
toDataURL(src).then((dataUrl) => setDataURL(dataUrl))
}
})

createEffect(() => {
if (src && !fullSizeDataURL() && modalOpen()) {
toDataURL(src.replace('/optimized', '').replace('avif', 'jpg')).then((url) => setFullSizeDataURL(url))
}
})

createEffect(() => {
if (dataURL()) {
exifr.parse(dataURL()).then((exif) => {
const exifData = {
Camera: get(exif, 'Model'),
Orientation: get(exif, 'Orientation'),
'Exposure Time': get(exif, 'ExposureTime'),
'F-Stop': get(exif, 'FNumber'),
ISO: get(exif, 'ISO'),
'Date Taken': format(new Date(get(exif, 'CreateDate')), `MMM do',' yyyy 'at' h:m aaaa`),
'Max Aperture': get(exif, 'MaxApertureValue'),
'Flash Mode': get(exif, 'Flash'),
'Focal Length': get(exif, 'FocalLength'),
Width: get(exif, 'ExifImageWidth'),
Height: get(exif, 'ExifImageHeight'),
}
setExifData(omitBy(exifData, isNil))
})
}
})

return (
<>
<Modal opened={modalOpen()} onClose={() => setModalOpen(false)} size="full">
<ModalOverlay />
<ModalContent>
<ModalHeader as={HStack} justifyContent="space-between">
<Heading>{alt}</Heading>
<ModalCloseButton bg="#131313" pos="relative" />
</ModalHeader>
<ModalBody>
<Show when={!largeImageLoaded()}>
<Skeleton w="6000px" h="4000px" maxW="$full" maxH="80vh" />
</Show>
<Flex w="$full" h="$full">
<HopeImage
src={fullSizeDataURL()}
onLoad={() => setLargeImageLoaded(true)}
w="$full"
h="$full"
objectFit="contain"
maxH="80vh"
flex={1}
/>
</Flex>
<VStack p={6} pt={16} alignItems="start">
<HStack w="$full" justifyContent="space-between">
<Heading fontWeight="bold" fontSize="$2xl">
Image Details
</Heading>
<Button
as="a"
bg="$accent10"
_hover={{ bg: '$accent10' }}
rightIcon={<FiDownload />}
href={fullSizeDataURL()}
download="test"
>
Download
</Button>
</HStack>
<Show when={!isEmpty(exifData())}>
<Table>
<Thead>
<Tr bg="$accent10">
<Th>Name</Th>
<Th numeric>Value</Th>
</Tr>
</Thead>
<Tbody>
<For each={Object.entries(exifData())}>
{([key, value]: [string, string], index) => (
<Tr bg="#131313">
<Td>{key}</Td>
<Td numeric>{value}</Td>
</Tr>
)}
</For>
</Tbody>
</Table>
</Show>
</VStack>
</ModalBody>
</ModalContent>
</Modal>
<Show when={!error()}>
<HopeImage
src={src}
src={dataURL()}
onLoad={() => setLoaded(true)}
onError={() => setError(true)}
transition="opacity 0.5s ease-out"
opacity={loaded() ? 1 : 0}
onClick={() => setModalOpen(true)}
{...otherProps}
/>
</Show>
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/BlogPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const BlogPost = () => {
const posts = usePosts()
const post = () => posts().find((postItem) => postItem.slug === slug)
return (
<VStack alignItems="start" my={12}>
<VStack alignItems="start" spacing="$6" my={12}>
<Button as={Link} href="/blog" variant="subtle" colorScheme="neutral" leftIcon={<FiArrowLeft />}>
Back
</Button>
Expand Down
Loading

0 comments on commit f939745

Please sign in to comment.