import * as React from "react" ; import { useMemo } from "react" ; import clsx from "clsx" ; import { darken , IconButton , lighten , Paper , Skeleton , Theme , Tooltip , Typography } from "@mui/material" ; import createStyles from "@mui/styles/createStyles" ; import makeStyles from "@mui/styles/makeStyles" ; import { AnyProperty , EntityReference } from "../../models" ; import KeyboardTabIcon from "@mui/icons-material/KeyboardTab" ; import { PreviewComponent , PreviewComponentProps , PreviewSize } from "../internal" ; import { SkeletonComponent } from "./SkeletonComponent" ; import { ErrorView } from "../../core" ; import { useEntityFetch , useNavigation , useSideEntityController } from "../../hooks" ; export type ReferencePreviewProps = PreviewComponentProps < EntityReference > & { onHover ? : boolean ; } ; const useReferenceStyles = makeStyles < Theme , { size : PreviewSize ; onHover ? : boolean ; } > ( ( theme ) => createStyles ( { paper : { display : "flex" , color : "#838383" , backgroundColor : darken ( theme . palette . background . default , 0.1 ) , borderRadius : "2px" , overflow : "hidden" , fontWeight : theme . typography . fontWeightMedium } , root : { display : "flex" , flexDirection : "column" , flexGrow : 1 , maxWidth : "calc(100% - 60px)" , margin : theme . spacing ( 1 ) } , regular : { padding : theme . spacing ( 1 ) , width : "100%" } , small : { width : "100%" } , tiny : { width : "100%" , itemsAlign : "center" } , clamp : { lineClamp : 1 } , marginAuto : { margin : "auto" } , inner : { display : ( { size } ) => size !== "regular" ? "block" : undefined , whiteSpace : ( { size } ) => size !== "regular" ? "nowrap" : undefined , overflow : ( { size } ) => size !== "regular" ? "hidden" : undefined , textOverflow : ( { size } ) => size !== "regular" ? "ellipsis" : undefined , margin : ( { size } ) => size !== "tiny" ? theme . spacing ( 0.2 ) : theme . spacing ( 0 ) } , clickable : { tabindex : 0 , backgroundColor : ( { onHover } ) => onHover ? ( theme . palette . mode === "dark" ? lighten ( theme . palette . background . default , 0.1 ) : darken ( theme . palette . background . default , 0.15 ) ) : darken ( theme . palette . background . default , 0.1 ) , transition : "background-color 300ms ease, box-shadow 300ms ease" , boxShadow : ( { onHover } ) => onHover ? "0 0 0 2px rgba(128,128,128,0.05)" : undefined , cursor : ( { onHover } ) => onHover ? "pointer" : undefined } } ) ) ; category export const ReferencePreview = React . memo < ReferencePreviewProps > ( ReferencePreviewComponent , areEqual ) as React . FunctionComponent < ReferencePreviewProps > ; function areEqual ( prevProps , nextProps ) { return prevProps . name === nextProps . name && prevProps . size === nextProps . size && prevProps . height === nextProps . height && prevProps . width === nextProps . width && prevProps . onHover === nextProps . onHover && prevProps . value ?. id === nextProps . value ?. id && prevProps . value ?. path === nextProps . value ?. path ; } function ReferencePreviewComponent < M extends { [ Key ] : any ; } > ( { value , property , onClick , size , onHover } ) { const referenceClasses = useReferenceStyles ( { size , onHover } ) ; const reference = value ; const previewProperties = property . previewProperties ; const navigationContext = useNavigation ( ) ; const sideEntityController = useSideEntityController ( ) ; const collectionResolver = navigationContext . getCollectionResolver < M > ( property . path ) ; if ( ! collectionResolver ) { throw Error ( `Couldn't find the corresponding collection view for the path: ${ property . path }` ) ; } const schema = collectionResolver . schema ; const { entity , dataLoading , dataLoadingError } = useEntityFetch ( { path : reference . path , entityId : reference . id , schema : collectionResolver . schemaResolver , useCache : true } ) ; const listProperties = useMemo ( ( ) => { let res = previewProperties ; if ( ! res || ! res . length ) { res = Object . keys ( schema . properties ) ; } if ( size === "small" || size === "regular" ) res = res . slice ( 0 , 3 ) ; else if ( size === "tiny" ) res = res . slice ( 0 , 1 ) ; return res ; } , [ previewProperties , schema . properties , size ] ) ; let body ; function buildError ( error , tooltip ? ) { return < ErrorView error = { error } tooltip = { tooltip } / > ; } if ( ! value ) { body = buildError ( "Reference not set" ) ; } else if ( ! ( value instanceof EntityReference ) ) { body = buildError ( "Unexpected value" , JSON . stringify ( value ) ) ; } else if ( entity && ! entity . values ) { body = buildError ( "Reference does not exist" , reference . path ) ; } else { body = ( < > < div className = { referenceClasses . root } > { size !== "tiny" && ( value ? < div className = { referenceClasses . inner } > < Typography variant = { "caption" } className = { "mono" } > { value . id } < /Typography> < /div> : < Skeleton variant = "text" / > ) } { listProperties && listProperties . map ( ( key ) => { const childProperty = schema . properties [ key as string ] ; return ( < div key = { "ref_prev_" + ( key as string ) } className = { referenceClasses . inner } > { entity ? < PreviewComponent name = { key as string } value = { entity . values [ key as string ] } property = { childProperty as AnyProperty } size = { "tiny" } / > : < SkeletonComponent property = { childProperty as AnyProperty } size = { "tiny" } / > } < /div>); } ) } < /div> < div className = { referenceClasses . marginAuto } > { entity && < Tooltip title = { `See details for ${ entity . id }` } > < IconButton size = { size === "tiny" ? "small" : "medium" } onClick = { ( e ) => { e . stopPropagation ( ) ; sideEntityController . open ( { entityId : entity . id , path : entity . path , schema : schema , overrideSchemaRegistry : false } ) ; } } > < KeyboardTabIcon fontSize = { "small" } /> < /IconButton> < /Tooltip>} < /div> < />); } return ( < Paper elevation = { 0 } className = { clsx ( referenceClasses . paper , { [ referenceClasses . regular ] : size === "regular" , [ referenceClasses . small ] : size === "small" , [ referenceClasses . tiny ] : size === "tiny" , [ referenceClasses . clickable ] : ! ! onClick } ) } onClick = { onClick } > { body } < /Paper>); }