@johnrom/react-countup
Version:
A React component wrapper around CountUp.js
370 lines (320 loc) • 10.5 kB
JavaScript
;
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;