Convert Icon to PureComponent and fix fallback
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
- [ ] Fix: Initial load `pointString` validation (make sure all talents are valid and their deps are met)
|
||||
- [ ] Fix: Navigating between talent links for same class does not trigger re-render
|
||||
- [ ] Fix: Icon image fallback if not locally known
|
||||
|
||||
- [ ] Styling:
|
||||
- [ ] SCSS: Normalize
|
||||
|
||||
+87
-56
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import './Icon.scss'
|
||||
import React from 'react'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
name?: string
|
||||
@@ -11,60 +11,91 @@ interface Props {
|
||||
|
||||
const NOT_FOUND_ICON = 'inv_misc_questionmark'
|
||||
|
||||
export const Icon = React.forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const {
|
||||
name: defaultName,
|
||||
size = 'medium',
|
||||
golden = false,
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
} = props
|
||||
const [hasLoadedImage, setLoadedImage] = useState(false)
|
||||
const [fadeIn, setFadeIn] = useState(false)
|
||||
const [name, setName] = useState(defaultName)
|
||||
|
||||
const bgSize = size !== 'small' ? 'large' : 'medium'
|
||||
const url = name && iconUrl(name, bgSize)
|
||||
|
||||
const start = Date.now()
|
||||
|
||||
useEffect(() => {
|
||||
if (!url) return
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
const loadTime = Date.now() - start
|
||||
if (loadTime >= 300) {
|
||||
setFadeIn(true)
|
||||
}
|
||||
setLoadedImage(true)
|
||||
}
|
||||
img.onerror = () => setName(NOT_FOUND_ICON)
|
||||
img.src = url
|
||||
}, [url, start])
|
||||
|
||||
const cn = classNames('icon', `icon--${size}`, className, {
|
||||
'icon--golden': golden,
|
||||
'icon--loaded': hasLoadedImage,
|
||||
'icon--fade-in': fadeIn,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={cn} ref={ref} {...rest}>
|
||||
{url &&
|
||||
<div className="icon__bg" style={{ backgroundImage: `url(${url})` }} />
|
||||
}
|
||||
<div className="icon__frame" />
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
// TODO: Fallback is broken due to no longer using require()
|
||||
const iconUrl = (name: string, size: string): string => {
|
||||
try {
|
||||
return `${process.env.PUBLIC_URL}/images/icons/${size}/${name}.jpg`
|
||||
} catch (e) {
|
||||
const makeUrl = (name: string, size: string, useFallback = false): string => {
|
||||
if (useFallback) {
|
||||
return `https://wow.zamimg.com/images/wow/icons/${size}/${name}.jpg`
|
||||
}
|
||||
return `${process.env.PUBLIC_URL}/images/icons/${size}/${name}.jpg`
|
||||
}
|
||||
|
||||
export class Icon extends React.PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
size: 'medium',
|
||||
golden: false
|
||||
}
|
||||
|
||||
img: HTMLImageElement = undefined
|
||||
|
||||
state = {
|
||||
fadeIn: false,
|
||||
url: ''
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadImage(this.props.name)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (prevProps.name !== this.props.name) {
|
||||
this.loadImage(this.props.name)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.img) {
|
||||
this.img.onload = null
|
||||
this.img.onerror = null
|
||||
this.img.src = ''
|
||||
}
|
||||
}
|
||||
|
||||
loadImage = (icon: string, useFallback = false): void => {
|
||||
this.setState({ url: '', fadeIn: false })
|
||||
const bgSize = this.props.size !== 'small' ? 'large' : 'medium'
|
||||
const url = icon && makeUrl(icon, bgSize, useFallback)
|
||||
if (!url) return
|
||||
|
||||
if (this.img) {
|
||||
this.img.onload = null
|
||||
this.img.onerror = null
|
||||
this.img.src = ''
|
||||
}
|
||||
|
||||
const start = Date.now()
|
||||
this.img = new Image()
|
||||
this.img.onload = () => {
|
||||
const loadTime = Date.now() - start
|
||||
this.setState({ url, fadeIn: loadTime >= 300 })
|
||||
this.img = undefined
|
||||
}
|
||||
this.img.onerror = () => {
|
||||
if (url.indexOf('wow.zamimg') === -1) {
|
||||
this.loadImage(icon, true)
|
||||
} else {
|
||||
this.loadImage(NOT_FOUND_ICON)
|
||||
}
|
||||
}
|
||||
this.img.src = url
|
||||
}
|
||||
|
||||
render() {
|
||||
const { size, golden, className, ...rest } = this.props
|
||||
const { fadeIn, url } = this.state
|
||||
|
||||
const cn = classNames('icon', `icon--${size}`, className, {
|
||||
'icon--golden': golden,
|
||||
'icon--loaded': !!url,
|
||||
'icon--fade-in': fadeIn,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={cn} {...rest}>
|
||||
{url &&
|
||||
<div className="icon__bg" style={{ backgroundImage: `url(${url})` }} />
|
||||
}
|
||||
<div className="icon__frame" />
|
||||
{this.props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user