Added lightbox
This commit is contained in:
parent
151ae0b126
commit
f4c2e2cba5
|
@ -50,7 +50,7 @@ export function getImgSizesByQuality(
|
||||||
|
|
||||||
type ImgProps = {
|
type ImgProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
image: StrapiImage;
|
image?: StrapiImage;
|
||||||
quality?: ImageQuality;
|
quality?: ImageQuality;
|
||||||
alt?: ImageProps["alt"];
|
alt?: ImageProps["alt"];
|
||||||
layout?: ImageProps["layout"];
|
layout?: ImageProps["layout"];
|
||||||
|
@ -60,42 +60,46 @@ type ImgProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Img(props: ImgProps): JSX.Element {
|
export default function Img(props: ImgProps): JSX.Element {
|
||||||
const imgSize = getImgSizesByQuality(
|
if (props.image) {
|
||||||
props.image.width,
|
const imgSize = getImgSizesByQuality(
|
||||||
props.image.height,
|
props.image.width,
|
||||||
props.quality ? props.quality : ImageQuality.Small
|
props.image.height,
|
||||||
);
|
props.quality ? props.quality : ImageQuality.Small
|
||||||
|
);
|
||||||
|
|
||||||
if (props.rawImg) {
|
if (props.rawImg) {
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
<img
|
<img
|
||||||
className={props.className}
|
className={props.className}
|
||||||
src={getAssetURL(
|
src={getAssetURL(
|
||||||
props.image.url,
|
props.image.url,
|
||||||
props.quality ? props.quality : ImageQuality.Small
|
props.quality ? props.quality : ImageQuality.Small
|
||||||
)}
|
)}
|
||||||
alt={props.alt ? props.alt : props.image.alternativeText}
|
alt={props.alt ? props.alt : props.image.alternativeText}
|
||||||
width={imgSize.width}
|
width={imgSize.width}
|
||||||
height={imgSize.height}
|
height={imgSize.height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
className={props.className}
|
||||||
|
src={getAssetURL(
|
||||||
|
props.image.url,
|
||||||
|
props.quality ? props.quality : ImageQuality.Small
|
||||||
|
)}
|
||||||
|
alt={props.alt ? props.alt : props.image.alternativeText}
|
||||||
|
width={props.layout === "fill" ? undefined : imgSize.width}
|
||||||
|
height={props.layout === "fill" ? undefined : imgSize.height}
|
||||||
|
layout={props.layout}
|
||||||
|
objectFit={props.objectFit}
|
||||||
|
priority={props.priority}
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return (
|
return <></>;
|
||||||
<Image
|
|
||||||
className={props.className}
|
|
||||||
src={getAssetURL(
|
|
||||||
props.image.url,
|
|
||||||
props.quality ? props.quality : ImageQuality.Small
|
|
||||||
)}
|
|
||||||
alt={props.alt ? props.alt : props.image.alternativeText}
|
|
||||||
width={props.layout === "fill" ? undefined : imgSize.width}
|
|
||||||
height={props.layout === "fill" ? undefined : imgSize.height}
|
|
||||||
layout={props.layout}
|
|
||||||
objectFit={props.objectFit}
|
|
||||||
priority={props.priority}
|
|
||||||
unoptimized
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { StrapiImage } from "graphql/operations-types";
|
||||||
|
import { Dispatch, SetStateAction } from "react";
|
||||||
|
import Img, { ImageQuality } from "./Img";
|
||||||
|
import Popup from "./Popup";
|
||||||
|
|
||||||
|
export type LightBoxProps = {
|
||||||
|
setState:
|
||||||
|
| Dispatch<SetStateAction<boolean>>
|
||||||
|
| Dispatch<SetStateAction<boolean | undefined>>;
|
||||||
|
state: boolean;
|
||||||
|
image?: StrapiImage;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LightBox(props: LightBoxProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
setState={props.setState}
|
||||||
|
state={props.state}
|
||||||
|
fillViewport
|
||||||
|
hideBackground
|
||||||
|
>
|
||||||
|
<Img
|
||||||
|
className="rounded-lg"
|
||||||
|
image={props.image}
|
||||||
|
layout="fill"
|
||||||
|
objectFit="contain"
|
||||||
|
quality={ImageQuality.Large}
|
||||||
|
/>
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import HorizontalLine from "components/HorizontalLine";
|
import HorizontalLine from "components/HorizontalLine";
|
||||||
|
import Img, { ImageQuality } from "components/Img";
|
||||||
import InsetBox from "components/InsetBox";
|
import InsetBox from "components/InsetBox";
|
||||||
import ToolTip from "components/ToolTip";
|
import ToolTip from "components/ToolTip";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
|
@ -200,6 +201,43 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
img: {
|
||||||
|
component: (props: {
|
||||||
|
alt: string;
|
||||||
|
src: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
caption?: string;
|
||||||
|
name?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.src.startsWith("/uploads/") ? (
|
||||||
|
<div className="relative w-full aspect-video my-8">
|
||||||
|
<Img
|
||||||
|
image={{
|
||||||
|
__typename: "UploadFile",
|
||||||
|
alternativeText: props.alt,
|
||||||
|
url: props.src,
|
||||||
|
width: props.width || 1500,
|
||||||
|
height: props.height || 1000,
|
||||||
|
caption: props.caption || "",
|
||||||
|
name: props.name || "",
|
||||||
|
}}
|
||||||
|
layout="fill"
|
||||||
|
objectFit="contain"
|
||||||
|
quality={ImageQuality.Medium}
|
||||||
|
></Img>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid place-content-center my-8">
|
||||||
|
<img {...props} className="max-h-[50vh] " />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -2,12 +2,17 @@ import { Dispatch, SetStateAction } from "react";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
|
|
||||||
export type PopupProps = {
|
export type PopupProps = {
|
||||||
setState: Dispatch<SetStateAction<boolean | undefined>>;
|
setState:
|
||||||
|
| Dispatch<SetStateAction<boolean>>
|
||||||
|
| Dispatch<SetStateAction<boolean | undefined>>;
|
||||||
state?: boolean;
|
state?: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
fillViewport?: boolean;
|
||||||
|
hideBackground?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Popup(props: PopupProps): JSX.Element {
|
export default function Popup(props: PopupProps): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed inset-0 z-50 grid place-content-center transition-[backdrop-filter] duration-500 ${
|
className={`fixed inset-0 z-50 grid place-content-center transition-[backdrop-filter] duration-500 ${
|
||||||
|
@ -15,6 +20,10 @@ export default function Popup(props: PopupProps): JSX.Element {
|
||||||
? "[backdrop-filter:blur(2px)]"
|
? "[backdrop-filter:blur(2px)]"
|
||||||
: "pointer-events-none touch-none"
|
: "pointer-events-none touch-none"
|
||||||
}`}
|
}`}
|
||||||
|
onKeyUp={(e) => {
|
||||||
|
if (e.key.match("Escape")) props.setState(false);
|
||||||
|
}}
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`fixed bg-shade inset-0 transition-all duration-500 ${
|
className={`fixed bg-shade inset-0 transition-all duration-500 ${
|
||||||
|
@ -25,8 +34,12 @@ export default function Popup(props: PopupProps): JSX.Element {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`relative p-10 bg-light rounded-lg shadow-2xl shadow-shade grid gap-4 place-items-center transition-transform ${
|
className={`p-10 grid gap-4 place-items-center transition-transform ${
|
||||||
props.state ? "scale-100" : "scale-0"
|
props.state ? "scale-100" : "scale-0"
|
||||||
|
} ${props.fillViewport ? "absolute inset-10 top-20" : "relative"} ${
|
||||||
|
props.hideBackground
|
||||||
|
? ""
|
||||||
|
: "bg-light rounded-lg shadow-2xl shadow-shade"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
Enum_Componentmetadatabooks_Binding_Type,
|
Enum_Componentmetadatabooks_Binding_Type,
|
||||||
Enum_Componentmetadatabooks_Page_Order,
|
Enum_Componentmetadatabooks_Page_Order,
|
||||||
GetLibraryItemQuery,
|
GetLibraryItemQuery,
|
||||||
|
StrapiImage,
|
||||||
} from "graphql/operations-types";
|
} from "graphql/operations-types";
|
||||||
import {
|
import {
|
||||||
convertMmToInch,
|
convertMmToInch,
|
||||||
|
@ -34,6 +35,9 @@ import { useAppLayout } from "contexts/AppLayoutContext";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import ContentTOCLine from "components/Library/ContentTOCLine";
|
import ContentTOCLine from "components/Library/ContentTOCLine";
|
||||||
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Popup from "components/Popup";
|
||||||
|
import LightBox from "components/LightBox";
|
||||||
|
|
||||||
interface LibrarySlugProps extends AppStaticProps {
|
interface LibrarySlugProps extends AppStaticProps {
|
||||||
item: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"];
|
item: GetLibraryItemQuery["libraryItems"]["data"][number]["attributes"];
|
||||||
|
@ -52,6 +56,9 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
|
||||||
|
|
||||||
sortContent(item.contents);
|
sortContent(item.contents);
|
||||||
|
|
||||||
|
const [lightboxOpened, setLightboxOpened] = useState(false);
|
||||||
|
const [lightboxImage, setLightboxImage] = useState<StrapiImage>();
|
||||||
|
|
||||||
const subPanel = (
|
const subPanel = (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
|
@ -104,6 +111,12 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
|
||||||
|
|
||||||
const contentPanel = (
|
const contentPanel = (
|
||||||
<ContentPanel width={ContentPanelWidthSizes.large}>
|
<ContentPanel width={ContentPanelWidthSizes.large}>
|
||||||
|
<LightBox
|
||||||
|
image={lightboxImage}
|
||||||
|
setState={setLightboxOpened}
|
||||||
|
state={lightboxOpened}
|
||||||
|
/>
|
||||||
|
|
||||||
<ReturnButton
|
<ReturnButton
|
||||||
href="/library/"
|
href="/library/"
|
||||||
title={langui.library}
|
title={langui.library}
|
||||||
|
@ -112,7 +125,15 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
|
||||||
className="mb-10"
|
className="mb-10"
|
||||||
/>
|
/>
|
||||||
<div className="grid place-items-center gap-12">
|
<div className="grid place-items-center gap-12">
|
||||||
<div className="drop-shadow-shade-xl w-full h-[50vh] mobile:h-[60vh] desktop:mb-16 relative cursor-pointer">
|
<div
|
||||||
|
className="drop-shadow-shade-xl w-full h-[50vh] mobile:h-[60vh] desktop:mb-16 relative cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
if (item.thumbnail.data) {
|
||||||
|
setLightboxImage(item.thumbnail.data.attributes);
|
||||||
|
setLightboxOpened(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{item.thumbnail.data ? (
|
{item.thumbnail.data ? (
|
||||||
<Img
|
<Img
|
||||||
image={item.thumbnail.data.attributes}
|
image={item.thumbnail.data.attributes}
|
||||||
|
@ -160,6 +181,10 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
|
||||||
<div
|
<div
|
||||||
key={galleryItem.id}
|
key={galleryItem.id}
|
||||||
className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer"
|
className="relative aspect-square hover:scale-[1.02] transition-transform cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setLightboxImage(galleryItem.attributes);
|
||||||
|
setLightboxOpened(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div>
|
<div className="bg-light absolute inset-0 rounded-lg drop-shadow-shade-md"></div>
|
||||||
<Img
|
<Img
|
||||||
|
|
Loading…
Reference in New Issue