1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00

F #3951: Add image component (#878)

This commit is contained in:
Sergio Betanzos 2021-02-25 13:01:19 +01:00 committed by GitHub
parent 532c4b238d
commit bd821b397e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 66 deletions

View File

@ -7,14 +7,14 @@ import ProvisionIcon from '@material-ui/icons/Cloud'
import SelectCard from 'client/components/Cards/SelectCard'
import Action from 'client/components/Cards/SelectCard/Action'
import { StatusBadge } from 'client/components/Status'
import Image from 'client/components/Image'
import { isExternalURL } from 'client/utils'
import * as Types from 'client/types/provision'
import {
PROVISIONS_STATES,
PROVIDER_IMAGES_URL,
PROVISION_IMAGES_URL,
DEFAULT_IMAGE,
IMAGE_FORMATS
DEFAULT_IMAGE
} from 'client/constants'
const ProvisionCard = memo(
@ -27,34 +27,12 @@ const ProvisionCard = memo(
const stateInfo = PROVISIONS_STATES[bodyData?.state]
const image = bodyData?.image ?? DEFAULT_IMAGE
const isExternalImage = isExternalURL(image)
const isExternalImage = useMemo(() => isExternalURL(image), [image])
const mediaProps = useMemo(() => {
const src = isExternalImage ? image : `${IMAGES_URL}/${image}`
const onError = evt => { evt.target.src = DEFAULT_IMAGE }
return {
component: 'picture',
children: (
<>
{(image && !isExternalImage) && IMAGE_FORMATS.map(format => (
<source
key={format}
srcSet={`${src}.${format}`}
type={`image/${format}`}
/>
))}
<img
decoding='async'
draggable={false}
loading='lazy'
src={src}
onError={onError}
/>
</>
)
}
}, [image, isExternalImage])
const imageUrl = useMemo(
() => isExternalImage ? image : `${IMAGES_URL}/${image}`,
[isExternalImage]
)
return (
<SelectCard
@ -73,7 +51,10 @@ const ProvisionCard = memo(
)
}
isSelected={isSelected}
mediaProps={mediaProps}
mediaProps={{
component: 'div',
children: <Image src={imageUrl} withSources={image && !isExternalImage} />
}}
subheader={`#${ID}`}
title={NAME}
disableFilterImage={isExternalImage}

View File

@ -1,4 +1,4 @@
import * as React from 'react'
import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import * as Types from 'client/types/provision'
@ -6,48 +6,23 @@ import * as Types from 'client/types/provision'
import ProvidersIcon from '@material-ui/icons/Public'
import SelectCard from 'client/components/Cards/SelectCard'
import Image from 'client/components/Image'
import { isExternalURL } from 'client/utils'
import {
PROVIDER_IMAGES_URL,
PROVISION_IMAGES_URL,
DEFAULT_IMAGE,
IMAGE_FORMATS
} from 'client/constants'
import { PROVIDER_IMAGES_URL, PROVISION_IMAGES_URL } from 'client/constants'
const ProvisionTemplateCard = React.memo(
const ProvisionTemplateCard = memo(
({ value, isProvider, isSelected, isValid, handleClick }) => {
const { description, name, plain = {} } = value
const { image = '' } = isProvider ? plain : value
const isExternalImage = isExternalURL(image)
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
const mediaProps = React.useMemo(() => {
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
const src = isExternalImage ? image : `${IMAGES_URL}/${image}`
const onError = evt => { evt.target.src = DEFAULT_IMAGE }
const isExternalImage = useMemo(() => isExternalURL(image), [image])
return {
component: 'picture',
children: (
<>
{(image && !isExternalImage) && IMAGE_FORMATS.map(format => (
<source
key={format}
srcSet={`${IMAGES_URL}/${image}.${format}`}
type={`image/${format}`}
/>
))}
<img
decoding='async'
draggable={false}
loading='lazy'
src={src}
onError={onError}
/>
</>
)
}
}, [image, isProvider])
const imageUrl = useMemo(
() => isExternalImage ? image : `${IMAGES_URL}/${image}`,
[isExternalImage]
)
return (
<SelectCard
@ -57,7 +32,10 @@ const ProvisionTemplateCard = React.memo(
icon={<ProvidersIcon />}
cardActionAreaProps={{ disabled: !isValid }}
isSelected={isSelected}
mediaProps={mediaProps}
mediaProps={{
component: 'div',
children: <Image src={imageUrl} withSources={image && !isExternalImage} />
}}
subheader={description}
title={name}
/>

View File

@ -0,0 +1,63 @@
import React, { useState, memo } from 'react'
import PropTypes from 'prop-types'
import { DEFAULT_IMAGE, IMAGE_FORMATS } from 'client/constants'
const MAX_RETRIES = 2
const INITIAL_STATE = { fail: false, retries: 0 }
const Image = memo(({ src = DEFAULT_IMAGE, withSources, imgProps }) => {
const [error, setError] = useState(INITIAL_STATE)
const addRetry = () => {
setError(prev => ({ ...prev, retries: prev.retries + 1 }))
}
const onImageFail = () => {
setError(prev => ({ fail: true, retries: prev.retries + 1 }))
}
if (error.retries >= MAX_RETRIES) {
return null
}
if (error.fail) {
return <img src={DEFAULT_IMAGE} draggable={false} onError={addRetry} />
}
return (
<picture>
{withSources && IMAGE_FORMATS.map(format => (
<source key={format}
srcSet={`${src}.${format}`}
type={`image/${format}`}
/>
))}
<img {...imgProps} src={src} onError={onImageFail} />
</picture>
)
}, (prev, next) => prev.src === next.src)
Image.propTypes = {
src: PropTypes.string,
withSources: PropTypes.bool,
imgProps: PropTypes.shape({
decoding: PropTypes.oneOf(['sync', 'async', 'auto']),
draggable: PropTypes.bool,
loading: PropTypes.oneOf(['eager', 'lazy'])
})
}
Image.defaultProps = {
src: undefined,
withSources: false,
imgProps: {
decoding: 'async',
draggable: false,
loading: 'lazy'
}
}
Image.displayName = 'Image'
export default Image