UNPKG

@johnrom/react-countup

Version:

A React component wrapper around CountUp.js

370 lines (320 loc) 10.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var React = require('react'); var warning = require('warning'); var countup_js = require('countup.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var warning__default = /*#__PURE__*/_interopDefaultLegacy(warning); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } /** * Silence SSR Warnings. * Borrowed from Formik v2.1.1, Licensed MIT. * * https://github.com/formium/formik/blob/9316a864478f8fcd4fa99a0735b1d37afdf507dc/LICENSE */ var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? React.useLayoutEffect : React.useEffect; /** * Create a stable reference to a callback which is updated after each render is committed. * Typed version borrowed from Formik v2.2.1. Licensed MIT. * * https://github.com/formium/formik/blob/9316a864478f8fcd4fa99a0735b1d37afdf507dc/LICENSE */ function useEventCallback(fn) { var ref = React.useRef(fn); // we copy a ref to the callback scoped to the current state/props on each render useIsomorphicLayoutEffect(function () { ref.current = fn; }); return React.useCallback(function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return ref.current.apply(void 0, args); }, []); } var createCountUpInstance = function createCountUpInstance(el, props) { var decimal = props.decimal, decimals = props.decimals, duration = props.duration, easingFn = props.easingFn, end = props.end, formattingFn = props.formattingFn, numerals = props.numerals, prefix = props.prefix, separator = props.separator, start = props.start, suffix = props.suffix, useEasing = props.useEasing; return new countup_js.CountUp(el, end, { startVal: start, duration: duration, decimal: decimal, decimalPlaces: decimals, easingFn: easingFn, formattingFn: formattingFn, numerals: numerals, separator: separator, prefix: prefix, suffix: suffix, useEasing: useEasing, useGrouping: !!separator }); }; var defaults = { decimal: '.', decimals: 0, onEnd: function onEnd() {}, onPauseResume: function onPauseResume() {}, onReset: function onReset() {}, onStart: function onStart() {}, onUpdate: function onUpdate() {}, prefix: '', separator: '', start: 0, startOnMount: true, suffix: '', useEasing: true, enableReinitialize: true }; var useCountUp = function useCountUp(props) { var config = React.useMemo(function () { return _objectSpread2(_objectSpread2({}, defaults), props); }, [props]); var ref = config.ref; var countUpRef = React.useRef(); var timerRef = React.useRef(); var isInitializedRef = React.useRef(false); var createInstance = useEventCallback(function () { return createCountUpInstance(typeof ref === 'string' ? ref : ref.current, config); }); var getCountUp = useEventCallback(function (recreate) { var countUp = countUpRef.current; if (countUp && !recreate) { return countUp; } var newCountUp = createInstance(); countUpRef.current = newCountUp; return newCountUp; }); var start = useEventCallback(function () { var delay = config.delay, onStart = config.onStart, onEnd = config.onEnd; var run = function run() { return getCountUp(true).start(function () { onEnd === null || onEnd === void 0 ? void 0 : onEnd({ pauseResume: pauseResume, reset: reset, start: restart, update: update }); }); }; if (delay && delay > 0) { timerRef.current = setTimeout(run, delay * 1000); } else { run(); } onStart === null || onStart === void 0 ? void 0 : onStart({ pauseResume: pauseResume, reset: reset, update: update }); }); var pauseResume = useEventCallback(function () { var onPauseResume = config.onPauseResume; getCountUp().pauseResume(); onPauseResume === null || onPauseResume === void 0 ? void 0 : onPauseResume({ reset: reset, start: restart, update: update }); }); var reset = useEventCallback(function () { timerRef.current && clearTimeout(timerRef.current); var onReset = config.onReset; getCountUp().reset(); onReset === null || onReset === void 0 ? void 0 : onReset({ pauseResume: pauseResume, start: restart, update: update }); }); var update = useEventCallback(function (newEnd) { var onUpdate = config.onUpdate; getCountUp().update(newEnd); onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({ pauseResume: pauseResume, reset: reset, start: restart }); }); var restart = useEventCallback(function () { reset(); start(); }); var maybeInitialize = useEventCallback(function () { if (config.startOnMount) { start(); } }); var maybeReinitialize = function maybeReinitialize() { if (config.startOnMount) { reset(); start(); } }; React.useEffect(function () { if (!isInitializedRef.current) { isInitializedRef.current = true; maybeInitialize(); } else if (config.enableReinitialize) { maybeReinitialize(); } }, [maybeInitialize, reset, config]); React.useEffect(function () { return function () { reset(); }; }, []); return { start: restart, pauseResume: pauseResume, reset: reset, update: update, getCountUp: getCountUp }; }; var _excluded = ["className", "redraw", "children", "style"]; var CountUp = function CountUp(props) { var className = props.className; props.redraw; var children = props.children, style = props.style, useCountUpProps = _objectWithoutProperties(props, _excluded); var containerRef = React__default['default'].useRef(null); var isInitializedRef = React__default['default'].useRef(false); var countUp = useCountUp(_objectSpread2(_objectSpread2({}, useCountUpProps), {}, { ref: containerRef, startOnMount: typeof children !== 'function' || props.delay === 0, // component manually restarts enableReinitialize: false })); var restart = useEventCallback(function () { countUp.start(); }); var update = useEventCallback(function (end) { if (!props.preserveValue) { countUp.reset(); } countUp.update(end); }); React.useEffect(function () { // unlike the hook, the CountUp component initializes on mount countUp.getCountUp(); if (typeof props.children === 'function') { // Warn when user didn't use containerRef at all warning__default['default'](containerRef.current instanceof Element, "Couldn't find attached element to hook the CountUp instance into! Try to attach \"containerRef\" from the render prop to a an HTMLElement, eg. <span ref={containerRef} />."); } }, []); React.useEffect(function () { if (isInitializedRef.current) { update(props.end); } }, [props.end]); // if props.redraw, call this effect on every props change React.useEffect(function () { if (props.redraw && isInitializedRef.current) { restart(); } }, [props.redraw && props]); // if not props.redraw, call this effect only when certain props are changed React.useEffect(function () { if (!props.redraw && isInitializedRef.current) { restart(); } }, [props.redraw, props.start, props.suffix, props.prefix, props.duration, props.separator, props.decimals, props.decimal, props.className, props.formattingFn]); React.useEffect(function () { isInitializedRef.current = true; }, []); if (typeof children === 'function') { // TypeScript forces functional components to return JSX.Element | null. return children(_objectSpread2({ countUpRef: containerRef }, countUp)); } return /*#__PURE__*/React__default['default'].createElement("span", { className: className, ref: containerRef, style: style }); }; exports.default = CountUp; exports.useCountUp = useCountUp;