From add0522095c1ff37e5fc8c557842aca51b6e7455 Mon Sep 17 00:00:00 2001 From: DrMint <thomas@barillot.net> Date: Sat, 23 Apr 2022 20:16:23 +0200 Subject: [PATCH] Fused all the preview cards to one all-encompassing component --- .../{ContentTOCLine.tsx => ContentLine.tsx} | 2 +- .../Library/LibraryContentPreview.tsx | 61 ------ .../Library/LibraryItemsPreview.tsx | 98 --------- src/components/News/PostsPreview.tsx | 62 ------ .../{Content => }/ThumbnailHeader.tsx | 0 src/components/ThumbnailPreview.tsx | 193 ++++++++++++++++++ src/components/Videos/VideoPreview.tsx | 80 -------- .../Chronology/ChronologyItemComponent.tsx | 0 .../Chronology/ChronologyYearComponent.tsx | 2 +- 9 files changed, 195 insertions(+), 303 deletions(-) rename src/components/Library/{ContentTOCLine.tsx => ContentLine.tsx} (95%) delete mode 100644 src/components/Library/LibraryContentPreview.tsx delete mode 100644 src/components/Library/LibraryItemsPreview.tsx delete mode 100644 src/components/News/PostsPreview.tsx rename src/components/{Content => }/ThumbnailHeader.tsx (100%) create mode 100644 src/components/ThumbnailPreview.tsx delete mode 100644 src/components/Videos/VideoPreview.tsx rename src/components/{ => Wiki}/Chronology/ChronologyItemComponent.tsx (100%) rename src/components/{ => Wiki}/Chronology/ChronologyYearComponent.tsx (86%) diff --git a/src/components/Library/ContentTOCLine.tsx b/src/components/Library/ContentLine.tsx similarity index 95% rename from src/components/Library/ContentTOCLine.tsx rename to src/components/Library/ContentLine.tsx index 782c047..0419d53 100644 --- a/src/components/Library/ContentTOCLine.tsx +++ b/src/components/Library/ContentLine.tsx @@ -20,7 +20,7 @@ interface Props { langui: AppStaticProps["langui"]; } -export default function ContentTOCLine(props: Props): JSX.Element { +export default function ContentLine(props: Props): JSX.Element { const { content, langui, parentSlug } = props; const [opened, setOpened] = useState(false); diff --git a/src/components/Library/LibraryContentPreview.tsx b/src/components/Library/LibraryContentPreview.tsx deleted file mode 100644 index 0f44a4f..0000000 --- a/src/components/Library/LibraryContentPreview.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import Chip from "components/Chip"; -import Img, { ImageQuality } from "components/Img"; -import { GetContentsQuery } from "graphql/generated"; -import Link from "next/link"; -import { prettySlug } from "queries/helpers"; - -interface Props { - item: Exclude< - GetContentsQuery["contents"], - null | undefined - >["data"][number]["attributes"]; -} - -export default function LibraryContentPreview(props: Props): JSX.Element { - const { item } = props; - - return ( - <Link href={`/contents/${item?.slug}`} passHref> - <div className="drop-shadow-shade-xl cursor-pointer grid items-end fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform"> - {item?.thumbnail?.data?.attributes ? ( - <Img - className="rounded-md coarse:rounded-b-none" - image={item.thumbnail.data.attributes} - quality={ImageQuality.Medium} - /> - ) : ( - <div className="w-full aspect-[3/2] bg-light rounded-lg"></div> - )} - <div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute coarse:rounded-b-md bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2"> - <div className="grid grid-flow-col gap-1 overflow-hidden place-content-start"> - {item?.type?.data?.attributes && ( - <Chip> - {item.type.data.attributes.titles?.[0] - ? item.type.data.attributes.titles[0]?.title - : prettySlug(item.type.data.attributes.slug)} - </Chip> - )} - </div> - <div> - {item?.titles?.[0] ? ( - <> - <p>{item.titles[0].pre_title}</p> - <h1 className="text-lg">{item.titles[0].title}</h1> - <h2>{item.titles[0].subtitle}</h2> - </> - ) : ( - <h1 className="text-lg">{prettySlug(item?.slug)}</h1> - )} - </div> - <div className="grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start"> - {item?.categories?.data.map((category) => ( - <Chip key={category.id} className="text-sm"> - {category.attributes?.short} - </Chip> - ))} - </div> - </div> - </div> - </Link> - ); -} diff --git a/src/components/Library/LibraryItemsPreview.tsx b/src/components/Library/LibraryItemsPreview.tsx deleted file mode 100644 index 4f06ece..0000000 --- a/src/components/Library/LibraryItemsPreview.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import Chip from "components/Chip"; -import Img, { ImageQuality } from "components/Img"; -import { useAppLayout } from "contexts/AppLayoutContext"; -import { - GetLibraryItemQuery, - GetLibraryItemsPreviewQuery, -} from "graphql/generated"; -import Link from "next/link"; -import { AppStaticProps } from "queries/getAppStaticProps"; -import { prettyDate, prettyItemSubType, prettyPrice } from "queries/helpers"; - -interface Props { - className?: string; - item: - | Exclude< - Exclude< - Exclude< - GetLibraryItemQuery["libraryItems"], - null | undefined - >["data"][number]["attributes"], - null | undefined - >["subitems"], - null | undefined - >["data"][number]["attributes"] - | Exclude< - GetLibraryItemsPreviewQuery["libraryItems"], - null | undefined - >["data"][number]["attributes"]; - currencies: AppStaticProps["currencies"]; -} - -export default function LibraryItemsPreview(props: Props): JSX.Element { - const { item } = props; - const appLayout = useAppLayout(); - - return ( - <Link href={`/library/${item?.slug}`} passHref> - <div - className={`drop-shadow-shade-xl cursor-pointer grid items-end hover:rounded-3xl fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform ${props.className}`} - > - {item?.thumbnail?.data?.attributes ? ( - <Img - image={item.thumbnail.data.attributes} - quality={ImageQuality.Small} - /> - ) : ( - <div className="w-full aspect-[21/29.7] bg-light rounded-lg"></div> - )} - - <div className="linearbg-obi fine:drop-shadow-shade-lg fine:absolute place-items-start bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-2"> - {item?.metadata && item.metadata.length > 0 && item.metadata[0] && ( - <div className="flex flex-row gap-1"> - <Chip>{prettyItemSubType(item.metadata[0])}</Chip> - </div> - )} - - <div> - <h2 className="mobile:text-sm text-lg leading-5">{item?.title}</h2> - <h3 className="mobile:text-xs leading-3">{item?.subtitle}</h3> - </div> - - <div className="w-full grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:h-0 [scrollbar-width:none] place-content-start"> - {item?.categories?.data.map((category) => ( - <Chip key={category.id} className="text-sm"> - {category.attributes?.short} - </Chip> - ))} - </div> - - {(item?.release_date || item?.price) && ( - <div className="grid grid-flow-col w-full"> - {item.release_date && ( - <p className="mobile:text-xs text-sm"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - event - </span> - {prettyDate(item.release_date)} - </p> - )} - {item.price && ( - <p className="mobile:text-xs text-sm justify-self-end"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - shopping_cart - </span> - {prettyPrice( - item.price, - props.currencies, - appLayout.currency - )} - </p> - )} - </div> - )} - </div> - </div> - </Link> - ); -} diff --git a/src/components/News/PostsPreview.tsx b/src/components/News/PostsPreview.tsx deleted file mode 100644 index b1cd585..0000000 --- a/src/components/News/PostsPreview.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Chip from "components/Chip"; -import Img, { ImageQuality } from "components/Img"; -import { GetPostsPreviewQuery } from "graphql/generated"; -import Link from "next/link"; -import { prettyDate, prettySlug } from "queries/helpers"; - -interface Props { - post: Exclude< - GetPostsPreviewQuery["posts"], - null | undefined - >["data"][number]["attributes"]; -} - -export default function PostPreview(props: Props): JSX.Element { - const { post } = props; - - return ( - <Link href={`/news/${post?.slug}`} passHref> - <div className="drop-shadow-shade-xl cursor-pointer grid items-end hover:scale-[1.02] transition-transform"> - {post?.thumbnail?.data?.attributes ? ( - <Img - className="rounded-md rounded-b-none" - image={post.thumbnail.data.attributes} - quality={ImageQuality.Medium} - /> - ) : ( - <div className="w-full aspect-[3/2] bg-light rounded-lg"></div> - )} - <div className="linearbg-obi fine:drop-shadow-shade-lg rounded-b-md top-full transition-opacity z-20 grid p-4 gap-2"> - {post?.date && ( - <div className="grid grid-flow-col w-full"> - <p className="mobile:text-xs text-sm"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - event - </span> - {prettyDate(post.date)} - </p> - </div> - )} - - <div> - {post?.translations?.[0] ? ( - <> - <h1 className="text-xl">{post.translations[0].title}</h1> - <p>{post.translations[0].excerpt}</p> - </> - ) : ( - <h1 className="text-lg">{prettySlug(post?.slug)}</h1> - )} - </div> - <div className="grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start"> - {post?.categories?.data.map((category) => ( - <Chip key={category.id} className="text-sm"> - {category.attributes?.short} - </Chip> - ))} - </div> - </div> - </div> - </Link> - ); -} diff --git a/src/components/Content/ThumbnailHeader.tsx b/src/components/ThumbnailHeader.tsx similarity index 100% rename from src/components/Content/ThumbnailHeader.tsx rename to src/components/ThumbnailHeader.tsx diff --git a/src/components/ThumbnailPreview.tsx b/src/components/ThumbnailPreview.tsx new file mode 100644 index 0000000..94e8c2e --- /dev/null +++ b/src/components/ThumbnailPreview.tsx @@ -0,0 +1,193 @@ +import { useAppLayout } from "contexts/AppLayoutContext"; +import { + DatePickerFragment, + PricePickerFragment, + UploadImageFragment, +} from "graphql/generated"; +import Link from "next/link"; +import { AppStaticProps } from "queries/getAppStaticProps"; +import { + prettyDate, + prettyDuration, + prettyPrice, + prettyShortenNumber, +} from "queries/helpers"; +import Chip from "./Chip"; +import Img, { ImageQuality } from "./Img"; + +interface Props { + thumbnail?: UploadImageFragment | string | null | undefined; + thumbnailAspectRatio?: string; + href: string; + pre_title?: string | null | undefined; + title: string | null | undefined; + subtitle?: string | null | undefined; + description?: string | null | undefined; + topChips?: string[]; + bottomChips?: string[]; + keepInfoVisible?: boolean; + metadata?: { + currencies?: AppStaticProps["currencies"]; + release_date?: DatePickerFragment | null; + price?: PricePickerFragment | null; + views?: number; + author?: string; + position: "Bottom" | "Top"; + }; + hoverlay?: { + __typename: "Video"; + duration: number; + }; +} + +export default function ThumbnailPreview(props: Props): JSX.Element { + const { + href, + thumbnail, + pre_title, + title, + subtitle, + description, + topChips, + bottomChips, + keepInfoVisible, + thumbnailAspectRatio, + metadata, + hoverlay, + } = props; + + const appLayout = useAppLayout(); + + const metadataJSX = + metadata && (metadata.release_date || metadata.price) ? ( + <div className="flex flex-row flex-wrap gap-x-3 w-full"> + {metadata.release_date && ( + <p className="mobile:text-xs text-sm"> + <span className="material-icons !text-base translate-y-[.15em] mr-1"> + event + </span> + {prettyDate(metadata.release_date)} + </p> + )} + {metadata.price && metadata.currencies && ( + <p className="mobile:text-xs text-sm justify-self-end"> + <span className="material-icons !text-base translate-y-[.15em] mr-1"> + shopping_cart + </span> + {prettyPrice( + metadata.price, + metadata.currencies, + appLayout.currency + )} + </p> + )} + {metadata.views && ( + <p className="mobile:text-xs text-sm"> + <span className="material-icons !text-base translate-y-[.15em] mr-1"> + visibility + </span> + {prettyShortenNumber(metadata.views)} + </p> + )} + {metadata.author && ( + <p className="mobile:text-xs text-sm"> + <span className="material-icons !text-base translate-y-[.15em] mr-1"> + person + </span> + {metadata.author} + </p> + )} + </div> + ) : ( + <></> + ); + + return ( + <Link href={href} passHref> + <div + className="drop-shadow-shade-xl cursor-pointer grid items-end + fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] + [--bg-opacity:0] hover:[--bg-opacity:0.5] [--play-opacity:0] + hover:[--play-opacity:100] transition-transform" + > + {thumbnail ? ( + <div className="relative"> + <Img + className={ + keepInfoVisible + ? "rounded-t-md" + : "rounded-md coarse:rounded-b-none" + } + image={thumbnail} + quality={ImageQuality.Medium} + /> + {hoverlay && hoverlay.__typename === "Video" && ( + <> + <div + className="absolute inset-0 text-light grid + place-content-center drop-shadow-shade-lg bg-shade + bg-opacity-[var(--bg-opacity)] transition-colors" + > + <span + className="material-icons text-6xl + opacity-[var(--play-opacity)] transition-opacity" + > + play_circle_outline + </span> + </div> + <div + className="absolute right-2 bottom-2 text-light bg-black + bg-opacity-60 px-2 rounded-full" + > + {prettyDuration(hoverlay.duration)} + </div> + </> + )} + </div> + ) : ( + <div + className={`w-full aspect-[${thumbnailAspectRatio}] bg-light ${ + keepInfoVisible + ? "rounded-t-md" + : "rounded-md coarse:rounded-b-none" + }`} + ></div> + )} + <div + className={`linearbg-obi ${ + keepInfoVisible + ? "-mt-[0.3333em]" + : `fine:drop-shadow-shade-lg fine:absolute coarse:rounded-b-md + bottom-2 -inset-x-0.5 opacity-[var(--cover-opacity)]` + } transition-opacity z-20 grid p-4 gap-2`} + > + {metadata?.position === "Top" && metadataJSX} + {topChips && topChips.length > 0 && ( + <div className="grid grid-flow-col gap-1 overflow-hidden place-content-start"> + {topChips.map((text, index) => ( + <Chip key={index}>{text}</Chip> + ))} + </div> + )} + <div> + {pre_title && <p>{pre_title}</p>} + {title && <h1 className="text-lg">{title}</h1>} + {subtitle && <h2>{subtitle}</h2>} + </div> + {description && <p>{description}</p>} + {bottomChips && bottomChips.length > 0 && ( + <div className="grid grid-flow-col gap-1 overflow-hidden place-content-start"> + {bottomChips.map((text, index) => ( + <Chip key={index} className="text-sm"> + {text} + </Chip> + ))} + </div> + )} + + {metadata?.position === "Bottom" && metadataJSX} + </div> + </div> + </Link> + ); +} diff --git a/src/components/Videos/VideoPreview.tsx b/src/components/Videos/VideoPreview.tsx deleted file mode 100644 index 7fec600..0000000 --- a/src/components/Videos/VideoPreview.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Chip from "components/Chip"; -import { GetVideosPreviewQuery } from "graphql/generated"; -import Link from "next/link"; -import { - getVideoThumbnailURL, - prettyDate, - prettyDuration, - prettyShortenNumber, -} from "queries/helpers"; - -interface Props { - video: Exclude< - Exclude< - GetVideosPreviewQuery["videos"], - null | undefined - >["data"][number]["attributes"], - null | undefined - >; -} - -export default function PostPreview(props: Props): JSX.Element { - const { video } = props; - - return ( - <Link href={`/archives/videos/v/${video.uid}`} passHref> - <div className="drop-shadow-shade-xl cursor-pointer grid items-end hover:scale-[1.02] [--bg-opacity:0] hover:[--bg-opacity:0.5] [--play-opacity:0] hover:[--play-opacity:100] transition-transform"> - <div className="relative"> - <img - className="aspect-video rounded-t-lg" - src={getVideoThumbnailURL(video.uid)} - alt={video.title} - /> - <div className="absolute inset-0 text-light grid place-content-center drop-shadow-shade-lg bg-shade bg-opacity-[var(--bg-opacity)] transition-colors"> - <span className="material-icons text-6xl opacity-[var(--play-opacity)] transition-opacity"> - play_circle_outline - </span> - </div> - <div className="absolute right-2 bottom-2 text-light bg-black bg-opacity-60 px-2 rounded-full"> - {prettyDuration(video.duration)} - </div> - </div> - <div className="linearbg-obi fine:drop-shadow-shade-lg rounded-b-md top-full transition-opacity z-20 grid p-4 gap-2"> - <div className="flex flex-row flex-wrap gap-x-3 w-full"> - <p className="mobile:text-xs text-sm"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - event - </span> - {prettyDate(video.published_date)} - </p> - <p className="mobile:text-xs text-sm"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - visibility - </span> - {prettyShortenNumber(video.views)} - </p> - {video.channel?.data?.attributes && ( - <p className="mobile:text-xs text-sm"> - <span className="material-icons !text-base translate-y-[.15em] mr-1"> - person - </span> - {video.channel.data.attributes.title} - </p> - )} - </div> - - <div> - <h1 className="text-xl">{video.title}</h1> - </div> - <div className="grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start"> - {video.categories?.data.map((category) => ( - <Chip key={category.id} className="text-sm"> - {category.attributes?.short} - </Chip> - ))} - </div> - </div> - </div> - </Link> - ); -} diff --git a/src/components/Chronology/ChronologyItemComponent.tsx b/src/components/Wiki/Chronology/ChronologyItemComponent.tsx similarity index 100% rename from src/components/Chronology/ChronologyItemComponent.tsx rename to src/components/Wiki/Chronology/ChronologyItemComponent.tsx diff --git a/src/components/Chronology/ChronologyYearComponent.tsx b/src/components/Wiki/Chronology/ChronologyYearComponent.tsx similarity index 86% rename from src/components/Chronology/ChronologyYearComponent.tsx rename to src/components/Wiki/Chronology/ChronologyYearComponent.tsx index 53c6c54..69ba5b6 100644 --- a/src/components/Chronology/ChronologyYearComponent.tsx +++ b/src/components/Wiki/Chronology/ChronologyYearComponent.tsx @@ -1,4 +1,4 @@ -import ChronologyItemComponent from "components/Chronology/ChronologyItemComponent"; +import ChronologyItemComponent from "components/Wiki/Chronology/ChronologyItemComponent"; import { GetChronologyItemsQuery } from "graphql/generated"; import { AppStaticProps } from "queries/getAppStaticProps";