-
-
Notifications
You must be signed in to change notification settings - Fork 229
/
Copy pathLightbox.tsx
110 lines (105 loc) · 4.23 KB
/
Lightbox.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { useCallback, useRef, useState } from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import {
faSearchPlus,
faSearchMinus,
faCompress,
faDownload,
faPlus,
} from "@fortawesome/free-solid-svg-icons"
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
import { LoadingIndicator } from "@ourworldindata/grapher"
import { triggerDownloadFromBlob } from "@ourworldindata/utils"
import { unmountComponentAtNode } from "react-dom"
import { useTriggerOnEscape } from "./hooks.js"
export const Lightbox = ({
children,
containerNode,
imgSrc,
imgFilename,
}: {
children: any
containerNode: Element | null
imgSrc: string
// With CF Images, the filename is not the last part of the URL
// so we need to pass it separately
imgFilename?: string
}) => {
const [isLoaded, setIsLoaded] = useState(false)
const contentRef = useRef<HTMLDivElement>(null)
const close = useCallback(() => {
if (containerNode) {
unmountComponentAtNode(containerNode)
}
}, [containerNode])
const handleDownload = useCallback(async () => {
const response = await fetch(imgSrc)
const blob = await response.blob()
const filename = imgFilename || imgSrc.split("/").pop() || "image"
triggerDownloadFromBlob(filename, blob)
}, [imgFilename, imgSrc])
useTriggerOnEscape(close)
return (
<div className="container">
{!isLoaded && (
<LoadingIndicator backgroundColor="#000" color="#ccc" />
)}
<TransformWrapper doubleClick={{ mode: "reset" }}>
{({ zoomIn, zoomOut, resetTransform }) => (
<>
<div
className="content"
ref={contentRef}
onClick={(e) => {
if (e.target === contentRef.current) {
close()
}
}}
>
<TransformComponent>
{children(isLoaded, setIsLoaded)}
</TransformComponent>
</div>
<div className="tools">
{isLoaded && (
<>
<button
aria-label="Zoom in"
onClick={() => zoomIn()}
>
<FontAwesomeIcon icon={faSearchPlus} />
</button>
<button
aria-label="Zoom out"
onClick={() => zoomOut()}
>
<FontAwesomeIcon icon={faSearchMinus} />
</button>
<button
aria-label="Reset zoom"
onClick={() => resetTransform()}
>
<FontAwesomeIcon icon={faCompress} />
</button>
<button
onClick={handleDownload}
aria-label="Download high resolution image"
>
<FontAwesomeIcon icon={faDownload} />
</button>
</>
)}
<button
aria-label="Close"
onClick={close}
className="close"
>
<FontAwesomeIcon icon={faPlus} />
</button>
</div>
</>
)}
</TransformWrapper>
</div>
)
}