Added support for touch screens where hovering is not possible

This commit is contained in:
DrMint 2022-02-17 02:49:32 +01:00
parent c6b6ec1479
commit 0e35f5aed9
10 changed files with 367 additions and 151 deletions

View File

@ -22,12 +22,15 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
const subPanelClass =
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:w-[20rem]";
let contentPanelClass = "";
let turnSubIntoContent = false;
if (props.subPanel && props.contentPanel) {
contentPanelClass =
"fixed desktop:left-[40rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
} else if (props.contentPanel) {
contentPanelClass =
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
} else if (props.subPanel) {
turnSubIntoContent = true;
}
return (
@ -52,7 +55,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => setsubPanelOpen(true)}
>
{props.subPanel
{props.subPanel && !turnSubIntoContent
? props.subPanelIcon
? props.subPanelIcon
: "tune"
@ -68,12 +71,21 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
{props.contentPanel}
</div>
) : (
""
<div className="top-0 left-0 right-0 bottom-20 overflow-y-scroll fixed desktop:left-[40rem] desktop:top-0 desktop:bottom-0 desktop:right-0 opacity-40">
<div className="grid place-content-center h-full">
<div className="text-dark border-dark border-2 border-dotted rounded-2xl p-8 grid grid-flow-col place-items-center gap-9">
<p className="text-4xl"></p>
<p className="text-2xl w-64">Select one of the options in the sidebar</p>
</div>
</div>
</div>
)}
{/* Background when navbar is opened */}
<div
className={`fixed bg-dark inset-0 transition-opacity duration-500 ${
className={`fixed bg-dark inset-0 transition-opacity duration-500
${turnSubIntoContent ? "z-10" : ""}
${
mainPanelOpen || subPanelOpen
? " opacity-50"
: "opacity-0 translate-x-full"
@ -84,25 +96,30 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
}}
></div>
{/* Main panel */}
<div
className={`${mainPanelClass} border-r-[1px] border-black top-0 bottom-0 left-0 right-12 bg-light overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-500
${mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
>
<MainPanel langui={props.langui} />
</div>
{/* Sub panel */}
{props.subPanel ? (
<div
className={`${subPanelClass} border-r-[1px] mobile:border-r-0 mobile:border-l-[1px] border-black top-0 bottom-0 right-0 left-12 bg-light overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-500
${subPanelOpen ? "" : "mobile:translate-x-full"}`}
${
turnSubIntoContent
? "mobile:mobile:translate-x-0 mobile:bottom-20 mobile:left-0 mobile:border-l-0"
: ""
}
${subPanelOpen ? "" : "mobile:translate-x-full"}`}
>
{props.subPanel}
</div>
) : (
""
)}
{/* Main panel */}
<div
className={`${mainPanelClass} border-r-[1px] border-black top-0 bottom-0 left-0 right-12 bg-light overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-500 z-20
${mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
>
<MainPanel langui={props.langui} />
</div>
</>
);
}

View File

@ -21,10 +21,10 @@ export default function LibraryContentPreview(
return (
<Link href={"/library/content/" + item.slug} passHref>
<div className="drop-shadow-dark-xl cursor-pointer grid items-end hover:rounded-3xl [--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform">
<div className="drop-shadow-dark-xl cursor-pointer grid items-end fine:[--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform">
{item.thumbnail.data ? (
<Image
className=" rounded-md"
className="rounded-md coarse:rounded-b-none"
src={getAssetURL(item.thumbnail.data.attributes.url)}
alt={item.thumbnail.data.attributes.alternativeText}
height={item.thumbnail.data.attributes.height}
@ -33,7 +33,18 @@ export default function LibraryContentPreview(
) : (
<div className="w-full aspect-[3/2] bg-light rounded-lg"></div>
)}
<div className="linearbg-1 drop-shadow-dark-lg absolute bottom-2 inset-x-[-0.15rem] opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-1 text-left">
<div className="linearbg-1 fine:drop-shadow-dark-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 text-left">
<div className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
{item.type ? (
<Chip>
{item.type.data.attributes.titles.length > 0
? item.type.data.attributes.titles[0].title
: prettySlug(item.type.data.attributes.slug)}
</Chip>
) : (
""
)}
</div>
<div>
{item.titles.length > 0 ? (
<>
@ -45,20 +56,13 @@ export default function LibraryContentPreview(
<h1 className="text-lg">{prettySlug(item.slug)}</h1>
)}
</div>
<div className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
<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 className="grid grid-flow-col gap-1 overflow-hidden place-content-start">
{item.type ? (
<Chip>{item.type.data.attributes.titles.length > 0 ? item.type.data.attributes.titles[0].title : prettySlug(item.type.data.attributes.slug)}</Chip>
) : (
""
)}
</div>
</div>
</div>
</Link>

View File

@ -1,9 +1,20 @@
import Link from "next/link";
import { GetLibraryItemsPreviewQuery } from "graphql/operations-types";
import { getAssetURL, prettyDate, prettyPrice } from "queries/helpers";
import {
GetLibraryItemsPreviewQuery,
GetWebsiteInterfaceQuery,
} from "graphql/operations-types";
import {
getAssetURL,
prettyDate,
prettyPrice,
prettyItemSubType,
} from "queries/helpers";
import Image from "next/image";
import Chip from "components/Chip";
export type LibraryItemsPreviewProps = {
className?: string;
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
item: {
slug: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["slug"];
thumbnail: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["thumbnail"];
@ -11,6 +22,7 @@ export type LibraryItemsPreviewProps = {
subtitle: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["subtitle"];
price?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["price"];
release_date?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["release_date"];
metadata?: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"];
};
};
@ -18,10 +30,13 @@ export default function LibraryItemsPreview(
props: LibraryItemsPreviewProps
): JSX.Element {
const item = props.item;
const langui = props.langui;
return (
<Link href={"/library/items/" + item.slug} passHref>
<div className="drop-shadow-dark-xl cursor-pointer grid items-end hover:rounded-3xl [--cover-opacity:0] hover:[--cover-opacity:1] hover:scale-[1.02] transition-transform">
<div
className={`drop-shadow-dark-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 ? (
<Image
src={getAssetURL(item.thumbnail.data.attributes.url)}
@ -32,36 +47,48 @@ export default function LibraryItemsPreview(
) : (
<div className="w-full aspect-[21/29.7] bg-light rounded-lg"></div>
)}
<div className="linearbg-1 drop-shadow-dark-lg absolute bottom-2 inset-x-[-0.15rem] opacity-[var(--cover-opacity)] transition-opacity z-20 grid p-4 gap-4 text-left">
<div className="linearbg-1 fine:drop-shadow-dark-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 text-left">
{item.metadata && item.metadata.length > 0 ? (
<div className="flex flex-row gap-1">
<Chip>{prettyItemSubType(item.metadata[0])}</Chip>
</div>
) : (
""
)}
<div>
<h2 className="text-lg leading-7">{item.title}</h2>
<h3 className="leading-3">{item.subtitle}</h3>
</div>
<div className="grid grid-flow-col">
{item.release_date ? (
<p className="text-sm">
<span className="material-icons !text-base translate-y-[.15em] mr-1">
event
</span>
{prettyDate(item.release_date)}
</p>
) : (
""
)}
{item.price ? (
<p className="text-sm justify-self-end">
<span className="material-icons !text-base translate-y-[.15em] mr-1">
shopping_cart
</span>
{prettyPrice(item.price)}
</p>
) : (
""
)}
<h2 className="mobile:text-sm text-lg leading-5">{item.title}</h2>
<h3 className="mobile:text-xs leading-3">{item.subtitle}</h3>
</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)}
</p>
) : (
""
)}
</div>
) : (
""
)}
</div>
</div>
</Link>
);
}

View File

@ -124,58 +124,119 @@ query getChronologyItems($language_code: String) {
}
query getLibraryItemsPreview($language_code: String) {
libraryItems(
filters: { root_item: { eq: true } }
pagination: { limit: -1 }
sort: ["title:asc", "subtitle:asc"]
) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
size {
width
height
thickness
}
descriptions(filters: { language: { code: { eq: $language_code } } }) {
description
}
}
}
}
libraryItems(
filters: { root_item: { eq: true } }
pagination: { limit: -1 }
sort: ["title:asc", "subtitle:asc"]
) {
data {
id
attributes {
title
subtitle
slug
thumbnail {
data {
attributes {
name
alternativeText
caption
width
height
url
}
}
}
release_date {
year
month
day
}
price {
amount
currency {
data {
attributes {
symbol
code
}
}
}
}
metadata {
__typename
... on ComponentMetadataBooks {
subtype {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
}
... on ComponentMetadataGame {
platform {
data {
attributes {
short
}
}
}
}
... on ComponentMetadataVideo {
subtype {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
}
... on ComponentMetadataAudio {
subtype {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
}
... on ComponentMetadataOther {
subtype {
data {
attributes {
slug
titles(
filters: { language: { code: { eq: $language_code } } }
) {
title
}
}
}
}
}
}
}
}
}
}
query getLibraryItemsSlugs {
libraryItems(pagination: { limit: -1 }) {
data {

View File

@ -263,16 +263,90 @@ export type GetLibraryItemsPreviewQuery = {
};
};
};
size: {
__typename: "ComponentBasicsSize";
width: number;
height: number;
thickness: number;
};
descriptions: Array<{
__typename: "ComponentTranslationsLibraryItems";
description: string;
}>;
metadata: Array<
| {
__typename: "ComponentMetadataBooks";
subtype: {
__typename: "TextualSubtypeEntityResponse";
data: {
__typename: "TextualSubtypeEntity";
attributes: {
__typename: "TextualSubtype";
slug: string;
titles: Array<{
__typename: "ComponentTranslationsSimpleTitle";
title: string;
}>;
};
};
};
}
| {
__typename: "ComponentMetadataVideo";
subtype: {
__typename: "VideoSubtypeEntityResponse";
data: {
__typename: "VideoSubtypeEntity";
attributes: {
__typename: "VideoSubtype";
slug: string;
titles: Array<{
__typename: "ComponentTranslationsSimpleTitle";
title: string;
}>;
};
};
};
}
| {
__typename: "ComponentMetadataGame";
platform: {
__typename: "GamePlatformEntityResponse";
data: {
__typename: "GamePlatformEntity";
attributes: {
__typename: "GamePlatform";
short: string;
};
};
};
}
| {
__typename: "ComponentMetadataAudio";
subtype: {
__typename: "AudioSubtypeEntityResponse";
data: {
__typename: "AudioSubtypeEntity";
attributes: {
__typename: "AudioSubtype";
slug: string;
titles: Array<{
__typename: "ComponentTranslationsSimpleTitle";
title: string;
}>;
};
};
};
}
| {
__typename: "ComponentMetadataOther";
subtype: {
__typename: "OtherSubtypeEntityResponse";
data: {
__typename: "OtherSubtypeEntity";
attributes: {
__typename: "OtherSubtype";
slug: string;
titles: Array<{
__typename: "ComponentTranslationsSimpleTitle";
title: string;
}>;
};
};
};
}
| { __typename: "Error" }
>;
};
}>;
};

View File

@ -19,6 +19,8 @@ import {
getAssetURL,
prettyDate,
prettyinlineTitle,
prettyItemType,
prettyItemSubType,
prettyPrice,
prettySlug,
} from "queries/helpers";
@ -29,7 +31,7 @@ import Chip from "components/Chip";
import Button from "components/Button";
import HorizontalLine from "components/HorizontalLine";
import AppLayout from "components/AppLayout";
import LibraryMediaPreview from "components/Library/LibraryItemsPreview";
import LibraryItemsPreview from "components/Library/LibraryItemsPreview";
type LibrarySlugProps = {
libraryItem: GetLibraryItemQuery;
@ -194,19 +196,11 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
{item.metadata.length > 0 ? (
<div className="grid place-items-center">
<h3 className="text-xl">{langui.global_type}</h3>
<Button>
{item.metadata[0].__typename === "ComponentMetadataAudio"
? langui.library_item_type_audio
: item.metadata[0].__typename === "ComponentMetadataBooks"
? langui.library_item_type_textual
: item.metadata[0].__typename === "ComponentMetadataGame"
? langui.library_item_type_game
: item.metadata[0].__typename === "ComponentMetadataOther"
? langui.library_item_type_game
: item.metadata[0].__typename === "ComponentMetadataVideo"
? langui.library_item_type_video
: ""}
</Button>
<div className="grid grid-flow-col gap-1">
<Chip>{prettyItemType(item.metadata[0], langui)}</Chip>
{""}
<Chip>{prettyItemSubType(item.metadata[0])}</Chip>
</div>
</div>
) : (
""
@ -273,19 +267,6 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
<div className="grid grid-cols-2 w-full place-content-between">
{item.metadata[0].__typename === "ComponentMetadataBooks" ? (
<>
<div className="grid place-content-start grid-flow-col gap-4">
<p className="font-bold">{langui.global_type}:</p>
<Chip>
{item.metadata[0].subtype.data.attributes.titles
.length > 0
? item.metadata[0].subtype.data.attributes.titles[0]
.title
: prettySlug(
item.metadata[0].subtype.data.attributes.slug
)}
</Chip>
</div>
<div className="grid place-content-start grid-flow-col gap-4">
<p className="font-bold">{langui.global_pages}:</p>
<p>{item.metadata[0].page_count}</p>
@ -368,11 +349,12 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
item.metadata[0].subtype.data.attributes.slug === "variant-set" ? (
<div id="variants" className="grid place-items-center gap-8 w-full">
<h2 className="text-2xl">{langui.library_item_variants}</h2>
<div className="grid gap-8 items-end grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full">
<div className="grid gap-8 items-end mobile:grid-cols-2 grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full">
{item.subitems.data.map((variant) => (
<LibraryMediaPreview
<LibraryItemsPreview
key={variant.id}
item={variant.attributes}
langui={langui}
/>
))}
</div>
@ -380,11 +362,12 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
) : (
<div id="subitems" className="grid place-items-center gap-8 w-full">
<h2 className="text-2xl">{langui.library_item_subitems}</h2>
<div className="grid gap-8 items-end grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full">
<div className="grid gap-8 items-end mobile:grid-cols-2 grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] w-full">
{item.subitems.data.map((subitem) => (
<LibraryMediaPreview
<LibraryItemsPreview
key={subitem.id}
item={subitem.attributes}
langui={langui}
/>
))}
</div>

View File

@ -41,9 +41,13 @@ export default function Library(props: LibraryProps): JSX.Element {
);
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
<div className="grid gap-8 items-end grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]">
<div className="grid gap-8 items-end mobile:grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(13rem,1fr))]">
{props.libraryItems.libraryItems.data.map((item) => (
<LibraryItemsPreview key={item.id} item={item.attributes} />
<LibraryItemsPreview
key={item.id}
item={item.attributes}
langui={langui}
/>
))}
</div>
</ContentPanel>

View File

@ -1,4 +1,7 @@
import { GetLibraryItemsPreviewQuery } from "graphql/operations-types";
import {
GetLibraryItemsPreviewQuery,
GetWebsiteInterfaceQuery,
} from "graphql/operations-types";
export function getAssetURL(url: string): string {
return process.env.NEXT_PUBLIC_URL_CMS + url;
@ -45,6 +48,47 @@ export function prettyinlineTitle(
return result;
}
export function prettyItemType(
metadata: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number],
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]
): string {
const type = metadata.__typename;
switch (metadata.__typename) {
case "ComponentMetadataAudio":
return langui.library_item_type_audio;
case "ComponentMetadataBooks":
return langui.library_item_type_textual;
case "ComponentMetadataGame":
return langui.library_item_type_game;
case "ComponentMetadataVideo":
return langui.library_item_type_video;
case "ComponentMetadataOther":
return langui.library_item_type_other;
default:
return "";
}
}
export function prettyItemSubType(
metadata: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number],
): string {
switch (metadata.__typename) {
case "ComponentMetadataAudio":
case "ComponentMetadataBooks":
case "ComponentMetadataVideo":
case "ComponentMetadataOther": {
return metadata.subtype.data.attributes.titles.length > 0
? metadata.subtype.data.attributes.titles[0].title
: prettySlug(metadata.subtype.data.attributes.slug);
}
case "ComponentMetadataGame":
return metadata.platform.data.attributes.short;
default:
return "";
}
}
export function capitalizeString(string: string): string {
function capitalizeWord(word: string): string {
return word.charAt(0).toUpperCase() + word.substring(1);

View File

@ -5,7 +5,7 @@
@layer base {
html,
body {
@apply p-0 m-0 bg-light text-black;
@apply p-0 m-0 bg-light text-black mobile:text-[90%];
}
* {
@ -41,7 +41,7 @@
}
*::-webkit-scrollbar {
@apply w-3;
@apply w-3 mobile:w-0;
}
*::-webkit-scrollbar-track {

View File

@ -14,9 +14,11 @@ module.exports = {
headers: ["Vollkorn"],
},
screens: {
desktop: {'min': '70rem'},
mobile: {'max': '70rem'},
}
desktop: { min: "70rem" },
mobile: { max: "70rem" },
coarse: { raw: "(pointer: coarse)" },
fine: { raw: "(pointer: fine)" },
},
},
plugins: [
require("@tailwindcss/typography"),