// This file is part of InvenioRDM // Copyright (C) 2022 CERN. // // Invenio RDM Records is free software; you can redistribute it and/or modify it // under the terms of the MIT License; see LICENSE file for more details. import PropTypes from "prop-types"; import React, { Component } from "react"; import { Image as SUIImage, Ref } from "semantic-ui-react"; import axios from "axios"; /** * Primary UI Image component providing a fallback url if src one is not * able to be resolved. This is a thin layer on top of the <img> element. */ export class Image extends Component { async componentDidMount() { const { fallbackSrc, loadFallbackFirst, src } = this.props; if (loadFallbackFirst) { try { await axios.get(src); this.setSrc(this.myRef.current, src); } catch (error) { // Fallback image is already loaded console.warn(` '${src}' couldn't be resolved. '${fallbackSrc}' will be used instead.`); } } } myRef = React.createRef(); setSrc = (currentTarget, src, isFallback = false) => { if (isFallback) { if (!currentTarget.classList.contains("fallback_image")) { currentTarget.className += " fallback_image"; } } else { if (currentTarget.classList.contains("fallback_image")) { currentTarget.classList.remove("fallback_image"); } } if (currentTarget.nodeName !== "IMG") { // Item.Image is wrapping the <img> in a div element const img = currentTarget.querySelector("img"); if (!img) { throw Error("No img tag found"); } currentTarget = img; } currentTarget.src = src; }; render() { const { alt, className, src, fallbackSrc, loadFallbackFirst, ...UIprops } = this.props; const loadingClass = !loadFallbackFirst ? `${className} placeholder` : `${className} fallback_image`; const url = loadFallbackFirst ? fallbackSrc : src; return ( <Ref innerRef={this.myRef}> <SUIImage className={loadingClass} alt={alt} src={url} {...(!loadFallbackFirst && { onError: ({ currentTarget }) => { currentTarget.onerror = null; // prevents looping this.setSrc(currentTarget, fallbackSrc, true); }, onLoad: () => { // Control the loader via ref to make it immediately invisible if (!loadFallbackFirst) { this.myRef.current.classList.remove("placeholder"); } }, })} {...UIprops} /> </Ref> ); } } Image.propTypes = { src: PropTypes.string.isRequired, fallbackSrc: PropTypes.string, className: PropTypes.string, alt: PropTypes.string, loadFallbackFirst: PropTypes.bool, }; Image.defaultProps = { className: "", alt: "No image found", fallbackSrc: "/static/images/square-placeholder.png", loadFallbackFirst: false, };