UNPKG

react-avatar

Version:

Universal React avatar component makes it possible to generate avatars based on user information.

139 lines (135 loc) 4.26 kB
'use strict'; 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 }); }