File size: 4,175 Bytes
1e22bf6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { Carousel } from "@mantine/carousel";
import { Button, Group, Stack, Text, Transition, rem } from "@mantine/core";
import { useEffect, useState } from "react";
import type { ImageSearchResult } from "../../../../modules/types";
import "@mantine/carousel/styles.css";
import Lightbox from "yet-another-react-lightbox";
import Captions from "yet-another-react-lightbox/plugins/captions";
import "yet-another-react-lightbox/styles.css";
import "yet-another-react-lightbox/plugins/captions.css";
import { addLogEntry } from "../../../../modules/logEntries";
import { getHostname } from "../../../../modules/stringFormatters";

interface ImageResultsState {
  isLightboxOpen: boolean;
  lightboxIndex: number;
  canStartTransition: boolean;
}

export default function ImageResultsList({
  imageResults,
}: {
  imageResults: ImageSearchResult[];
}) {
  const [state, setState] = useState<ImageResultsState>({
    isLightboxOpen: false,
    lightboxIndex: 0,
    canStartTransition: false,
  });

  useEffect(() => {
    setState((prev) => ({ ...prev, canStartTransition: true }));
  }, []);

  const handleImageClick = (index: number) => {
    setState((prev) => ({
      ...prev,
      lightboxIndex: index,
      isLightboxOpen: true,
    }));
  };

  const imageStyle = {
    objectFit: "cover",
    height: rem(180),
    width: rem(240),
    borderRadius: rem(4),
    border: `${rem(2)} solid var(--mantine-color-default-border)`,
    cursor: "zoom-in",
  } as const;

  return (
    <>
      <Carousel slideSize="0" slideGap="xs" align="start" dragFree loop>
        {imageResults.map(([title, url, thumbnailUrl], index) => (
          <Transition
            key={url}
            mounted={state.canStartTransition}
            transition="fade"
            timingFunction="ease"
            enterDelay={index * 250}
            duration={1500}
          >
            {(styles) => (
              <Carousel.Slide style={styles}>
                <img
                  alt={title}
                  src={thumbnailUrl}
                  loading="lazy"
                  onClick={() => handleImageClick(index)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") {
                      handleImageClick(index);
                    }
                  }}
                  style={imageStyle}
                />
              </Carousel.Slide>
            )}
          </Transition>
        ))}
      </Carousel>
      <Lightbox
        open={state.isLightboxOpen}
        close={() => setState((prev) => ({ ...prev, isLightboxOpen: false }))}
        plugins={[Captions]}
        index={state.lightboxIndex}
        slides={imageResults.map(([title, url, thumbnailUrl, sourceUrl]) => ({
          src: thumbnailUrl,
          alt: title,
          description: (
            <Stack align="center" gap="md">
              {title && (
                <Text component="cite" ta="center">
                  {title}
                </Text>
              )}
              <Group align="center" justify="center" gap="xs">
                <Button
                  variant="subtle"
                  component="a"
                  size="xs"
                  href={sourceUrl}
                  target="_blank"
                  title="Click to see the image in full size"
                  rel="noopener noreferrer"
                  onClick={() => {
                    addLogEntry("User visited an image result in full size");
                  }}
                >
                  View in full resolution
                </Button>
                <Button
                  variant="subtle"
                  component="a"
                  href={url}
                  target="_blank"
                  size="xs"
                  title="Click to visit the page where the image was found"
                  rel="noopener noreferrer"
                  onClick={() => {
                    addLogEntry("User visited an image result source");
                  }}
                >
                  Visit {getHostname(url)}
                </Button>
              </Group>
            </Stack>
          ),
        }))}
      />
    </>
  );
}