From a99de82cb996d60e972433fde01df2598f64f8db Mon Sep 17 00:00:00 2001 From: Sebastian Tilsch Date: Thu, 4 Apr 2024 00:09:01 +0200 Subject: [PATCH] play with some markdown content --- .../components/form/show/EntityDetailCard.tsx | 95 ++++---- .../form/show/EntityDetailCardProps.ts | 17 ++ .../form/show/EntityDetailElement.tsx | 18 +- .../components/form/show/MarkdownContent.tsx | 18 ++ .../form/show/MarkdownContentNoSSR.tsx | 13 ++ .../form/show/StylizedDetailCard.tsx | 214 ++++++++++++++++++ 6 files changed, 324 insertions(+), 51 deletions(-) create mode 100644 apps/exhibition-live/components/form/show/EntityDetailCardProps.ts create mode 100644 apps/exhibition-live/components/form/show/MarkdownContent.tsx create mode 100644 apps/exhibition-live/components/form/show/MarkdownContentNoSSR.tsx create mode 100644 apps/exhibition-live/components/form/show/StylizedDetailCard.tsx diff --git a/apps/exhibition-live/components/form/show/EntityDetailCard.tsx b/apps/exhibition-live/components/form/show/EntityDetailCard.tsx index dbcd309d..9a274b56 100644 --- a/apps/exhibition-live/components/form/show/EntityDetailCard.tsx +++ b/apps/exhibition-live/components/form/show/EntityDetailCard.tsx @@ -20,20 +20,10 @@ import { useSettings } from "../../state/useLocalSettings"; import NiceModal from "@ebay/nice-modal-react"; import { EditEntityModal } from "../edit/EditEntityModal"; import { useModalRegistry } from "../../state"; +import { EntityDetailCardProps } from "./EntityDetailCardProps"; +import { StylizedDetailCard } from "./StylizedDetailCard"; -interface OwnProps { - typeIRI: string; - entityIRI: string; - cardInfo: PrimaryFieldResults; - cardActionChildren?: React.ReactNode; - data: any; - readonly?: boolean; - inlineEditing?: boolean; - onEditClicked?: () => void; -} - -type Props = OwnProps; -export const EntityDetailCard: FunctionComponent = ({ +export const EntityDetailCard: FunctionComponent = ({ typeIRI, entityIRI, cardInfo, @@ -42,6 +32,7 @@ export const EntityDetailCard: FunctionComponent = ({ readonly, inlineEditing, onEditClicked, + tableProps = {}, }) => { const { t } = useTranslation(); @@ -78,45 +69,55 @@ export const EntityDetailCard: FunctionComponent = ({ return ( <> - - - {cardInfo.image && ( - - )} - - - {cardInfo.label} - - - {cardInfo.description} - - - - {cardActionChildren !== null && ( - - {typeof cardActionChildren !== "undefined" ? ( - cardActionChildren - ) : ( - <> - {!readonly && ( - - )} - + {cardInfo.image && cardInfo.description ? ( + + ) : ( + + + {cardInfo.image && ( + )} - - )} - + + + {cardInfo.label} + + + {cardInfo.description} + + + + {cardActionChildren !== null && ( + + {typeof cardActionChildren !== "undefined" ? ( + cardActionChildren + ) : ( + <> + {!readonly && ( + + )} + + )} + + )} + + )} {enableDebug && ( <> diff --git a/apps/exhibition-live/components/form/show/EntityDetailCardProps.ts b/apps/exhibition-live/components/form/show/EntityDetailCardProps.ts new file mode 100644 index 00000000..f5d05d8d --- /dev/null +++ b/apps/exhibition-live/components/form/show/EntityDetailCardProps.ts @@ -0,0 +1,17 @@ +import { PrimaryFieldResults } from "../../utils/types"; +import React from "react"; +import { AllPropTableProps } from "../lobid/LobidAllPropTable"; + +type OwnProps = { + typeIRI: string; + entityIRI: string; + cardInfo: PrimaryFieldResults; + cardActionChildren?: React.ReactNode; + data: any; + readonly?: boolean; + inlineEditing?: boolean; + onEditClicked?: () => void; + tableProps?: Partial; +}; + +export type EntityDetailCardProps = OwnProps; diff --git a/apps/exhibition-live/components/form/show/EntityDetailElement.tsx b/apps/exhibition-live/components/form/show/EntityDetailElement.tsx index ec336a9e..1c4a643b 100644 --- a/apps/exhibition-live/components/form/show/EntityDetailElement.tsx +++ b/apps/exhibition-live/components/form/show/EntityDetailElement.tsx @@ -12,6 +12,7 @@ import { EntityDetailCard } from "./EntityDetailCard"; import { useTypeIRIFromEntity } from "../../state"; import { useTranslation } from "next-i18next"; import { PrimaryField } from "@slub/edb-core-types"; +import { filterUndefOrNull } from "@slub/edb-core-utils"; type EntityDetailElementProps = { typeIRI: string | undefined; @@ -47,16 +48,24 @@ export const EntityDetailElement = ({ ); const { t } = useTranslation(); const data = initialData || rawData?.document; + const fieldDeclaration = useMemo( + () => primaryFields[typeName] as PrimaryField, + [typeName], + ); const cardInfo = useMemo>(() => { - const fieldDecl = primaryFields[typeName] as PrimaryField | undefined; - if (data && fieldDecl) - return applyToEachField(data, fieldDecl, extractFieldIfString); + if (data && fieldDeclaration) + return applyToEachField(data, fieldDeclaration, extractFieldIfString); return { label: null, description: null, image: null, }; - }, [typeName, data]); + }, [fieldDeclaration, data]); + + const disabledProperties = useMemo( + () => filterUndefOrNull(Object.values(fieldDeclaration)), + [fieldDeclaration], + ); return ( @@ -68,6 +77,7 @@ export const EntityDetailElement = ({ cardActionChildren={cardActionChildren} inlineEditing={inlineEditing} readonly={readonly} + tableProps={{ disabledProperties }} /> ); diff --git a/apps/exhibition-live/components/form/show/MarkdownContent.tsx b/apps/exhibition-live/components/form/show/MarkdownContent.tsx new file mode 100644 index 00000000..5b4e50a4 --- /dev/null +++ b/apps/exhibition-live/components/form/show/MarkdownContent.tsx @@ -0,0 +1,18 @@ +import React, { useEffect } from "react"; +import { useRemark } from "react-remark"; +import { Container } from "@mui/material"; + +export type MarkdownContentProps = { + mdDocument: string; +}; +const MarkdownContent = ({ mdDocument }: MarkdownContentProps) => { + const [reactContent, setMarkdownSource] = useRemark(); + + useEffect(() => { + setMarkdownSource(mdDocument); + }, [mdDocument]); + + return {reactContent}; +}; + +export default MarkdownContent; diff --git a/apps/exhibition-live/components/form/show/MarkdownContentNoSSR.tsx b/apps/exhibition-live/components/form/show/MarkdownContentNoSSR.tsx new file mode 100644 index 00000000..b2afa254 --- /dev/null +++ b/apps/exhibition-live/components/form/show/MarkdownContentNoSSR.tsx @@ -0,0 +1,13 @@ +import dynamic from "next/dynamic"; + +const DynamicComponentWithNoSSR = dynamic(() => import("./MarkdownContent"), { + ssr: false, +}); + +export type MarkdownContentProps = { + mdDocument: string; +}; + +export default (props: MarkdownContentProps) => ( + +); diff --git a/apps/exhibition-live/components/form/show/StylizedDetailCard.tsx b/apps/exhibition-live/components/form/show/StylizedDetailCard.tsx new file mode 100644 index 00000000..865ca349 --- /dev/null +++ b/apps/exhibition-live/components/form/show/StylizedDetailCard.tsx @@ -0,0 +1,214 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { Box, styled, Typography } from "@mui/material"; +import { EntityDetailCardProps } from "./EntityDetailCardProps"; +import ColorThief from "color-thief-ts"; +import MarkdownContent from "./MarkdownContentNoSSR"; + +type ColorArray = [number, number, number]; + +type StyledCardProps = { + palette: ColorArray[]; + children: React.ReactNode; +}; + +const StyledAnimatedCard = styled(Box)( + ({ theme, palette }) => ({ + "& h1.heading": { fontSize: "2em", textAlign: "center", color: "white" }, + "& h1": { fontSize: "2em", color: "black" }, + "& p": { + fontSize: "max(10pt, 2.5vmin)", + lineHeight: 1.4, + color: theme.palette.primary.dark, + marginBottom: "1.5rem", + }, + "& .wrap": { + display: "flex", + flexWrap: "nowrap", + justifyContent: "space-between", + width: "100%", + height: "65vmin", + margin: "2rem auto", + border: "none", + transition: "0.3s ease-in-out", + position: "relative", + overflow: "hidden", + }, + "& .overlay": { + position: "relative", + display: "flex", + width: "100%", + height: "100%", + padding: "1rem 0.75rem", + background: theme.palette.secondary.dark, + transition: "0.4s ease-in-out", + zIndex: 1, + }, + "& .overlay-content": { + display: "flex", + flexDirection: "column", + justifyContent: "space-between", + width: "60%", + height: "100%", + padding: "0.5rem 0 0 0.5rem", + transition: "0.3s ease-in-out 0.2s", + textAlign: "left", + zIndex: 1, + }, + "& .image-content": { + position: "absolute", + top: "0", + right: "0", + width: "40%", + height: "100%", + backgroundSize: "cover", + transition: "0.3s ease-in-out", + }, + "& .inset": { + maxWidth: "50%", + margin: "0.25em 1em 1em 0", + borderRadius: "0.25em", + cssFloat: "left", + }, + "& .dots": { + position: "absolute", + bottom: "1rem", + right: "2rem", + display: "flex", + flexDirection: "row", + justifyContent: "space-around", + alignItems: "center", + width: "55px", + height: "4vmin", + transition: "0.3s ease-in-out 0.3s", + }, + "& .dot": { + width: "14px", + height: "14px", + background: theme.palette.background.default, + border: "1px solid indigo", + borderRadius: "50%", + transition: "0.3s ease-in-out 0.3s", + }, + "& .text": { + position: "absolute", + top: "0", + right: "0", + width: "60%", + height: "100%", + padding: "3vmin 4vmin", + background: theme.palette.primary.light, + overflowY: "auto", + }, + "& .wrap:hover .overlay": { width: "50%" }, + "& .wrap:hover .image-content": { width: "40vmin" }, + "& .wrap:hover .overlay-content": { + border: "none", + transitionDelay: "0.2s", + width: "40%", + }, + "& .wrap:hover .dots": { transform: "translateX(1rem)" }, + "& .wrap:hover .dots .dot": { background: "white" }, + "& .animate": { + animationDuration: "0.7s", + animationTimingFunction: "cubic-bezier(0.26, 0.53, 0.74, 1.48)", + animationFillMode: "backwards", + }, + "& .pop": { animationName: "pop" }, + "& @keyframes pop": { + "& 0%": { opacity: 0, transform: "scale(0.5, 0.5)" }, + "& 100%": { opacity: 1, transform: "scale(1, 1)" }, + }, + "& .slide": { animationName: "slide" }, + "& @keyframes slide": { + "& 0%": { opacity: 0, transform: "translate(4em, 0)" }, + "& 100%": { opacity: 1, transform: "translate(0, 0)" }, + }, + "& .slide-left": { animationName: "slide-left" }, + "& @keyframes slide-left": { + "& 0%": { opacity: 0, transform: "translate(-40px, 0)" }, + "& 100%": { opacity: 1, transform: "translate(0, 0)" }, + }, + "& .slide-up": { animationName: "slide-up" }, + "& @keyframes slide-up": { + "& 0%": { opacity: 0, transform: "translateY(3em)" }, + "& 100%": { opacity: 1, transform: "translateY(0)" }, + }, + "& .delay-1": { animationDelay: "0.3s" }, + "& .delay-2": { animationDelay: "0.6s" }, + "& .delay-3": { animationDelay: "0.9s" }, + "& .delay-4": { animationDelay: "1.2s" }, + "& .delay-5": { animationDelay: "1.5s" }, + "& .delay-6": { animationDelay: "1.8s" }, + "& .delay-7": { animationDelay: "2.1s" }, + "& .delay-8": { animationDelay: "2.4s" }, + }), +); + +export const StylizedDetailCard = ({ cardInfo }: EntityDetailCardProps) => { + const imgRef = useRef(); + const [colorPalette, setColorPalette] = useState([]); + useEffect(() => { + const ct = new ColorThief({ crossOrigin: true }); + if (!imgRef.current) { + return; + } + if (imgRef.current.complete) { + console.log("will try to get palette from image"); + try { + setColorPalette(ct.getPalette(imgRef.current, 3)); + } catch (e) { + console.log("cannot get color palette"); + console.error(e); + } + } else { + imgRef.current.addEventListener("load", () => { + console.log("will try to get palette from image"); + try { + setColorPalette(ct.getPalette(imgRef.current, 3)); + } catch (e) { + console.log("cannot get color palette"); + console.error(e); + } + }); + } + }, [cardInfo.image, setColorPalette]); + return ( + +
+
+
+

+ {cardInfo.label} +

+ + {cardInfo.label} + +
+
+ +
+
+
+
+
+
+
+
+
{JSON.stringify(colorPalette, null, 2)}
+ {} +
+
+
+ ); +};