react-avatar
Version:
Universal React avatar component makes it possible to generate avatars based on user information.
139 lines (135 loc) • 4.26 kB
JavaScript
;
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { Cache } from './cache';
import { withConfig, ConfigProvider } from './context';
import InternalState from './internal-state';
export { getRandomColor } from './utils';
export { ConfigProvider } from './context';
export { Cache } from './cache';
function matchSource(Source, props, cb) {
const {
cache
} = props;
const instance = new Source(props);
if (!instance.isCompatible(props)) return cb();
instance.get(state => {
const failedBefore = state && state.src && cache.hasSourceFailedBefore(state.src);
if (!failedBefore && state) {
cb(state);
} else {
cb();
}
});
}
export default function createAvatarDataProvider(_ref) {
let {
sources = []
} = _ref;
// Collect propTypes for each individual source
const sourcePropTypes = sources.reduce((r, s) => Object.assign(r, s.propTypes), {});
class AvatarDataProvider extends PureComponent {
constructor(props) {
super(props);
_defineProperty(this, "_createFetcher", internal => errEvent => {
const {
cache
} = this.props;
if (!internal.isActive(this.state)) return;
// Mark img source as failed for future reference
if (errEvent && errEvent.type === 'error') cache.sourceFailed(errEvent.target.src);
const pointer = internal.sourcePointer;
if (sources.length === pointer) return;
const source = sources[pointer];
internal.sourcePointer++;
matchSource(source, this.props, nextState => {
if (!nextState) return setTimeout(internal.fetch, 0);
if (!internal.isActive(this.state)) return;
// Reset other values to prevent them from sticking (#51)
nextState = {
src: null,
value: null,
color: null,
...nextState
};
this.setState(state => {
// Internal state has been reset => we received new props
return internal.isActive(state) ? nextState : {};
});
});
});
_defineProperty(this, "fetch", () => {
const internal = new InternalState();
internal.fetch = this._createFetcher(internal);
this.setState({
internal
}, internal.fetch);
});
this.state = {
internal: null,
src: null,
value: null,
color: props.color
};
}
componentDidMount() {
this.fetch();
}
componentDidUpdate(prevProps) {
let needsUpdate = false;
// This seems redundant
//
// Props that need to be in `state` are
// `value`, `src` and `color`
for (const prop in sourcePropTypes) needsUpdate = needsUpdate || prevProps[prop] !== this.props[prop];
if (needsUpdate) setTimeout(this.fetch, 0);
}
componentWillUnmount() {
if (this.state.internal) {
this.state.internal.active = false;
}
}
render() {
const {
children,
propertyName
} = this.props;
const {
src,
value,
color,
sourceName,
internal
} = this.state;
const avatarData = {
src,
value,
color,
sourceName,
onRenderFailed: () => internal && internal.fetch() // eslint-disable-line
};
if (typeof children === 'function') return children(avatarData);
const child = React.Children.only(children);
return /*#__PURE__*/React.cloneElement(child, {
[propertyName]: avatarData
});
}
}
_defineProperty(AvatarDataProvider, "displayName", 'AvatarDataProvider');
_defineProperty(AvatarDataProvider, "propTypes", {
// PropTypes defined on sources
...sourcePropTypes,
cache: PropTypes.object,
propertyName: PropTypes.string
});
_defineProperty(AvatarDataProvider, "defaultProps", {
propertyName: 'avatar'
});
_defineProperty(AvatarDataProvider, "Cache", Cache);
_defineProperty(AvatarDataProvider, "ConfigProvider", ConfigProvider);
return Object.assign(withConfig(AvatarDataProvider), {
ConfigProvider,
Cache
});
}