monday-ui-react-core
Version:
Official monday.com UI resources for application development in React.js
148 lines (130 loc) • 3.68 kB
JSX
// Libraries import
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { SwitchTransition, CSSTransition } from "react-transition-group";
// Constants import
import {
COUNTER_COLORS,
COUNTER_SIZES,
COUNTER_TYPES
} from "./CounterConstants";
// Hooks import
import useEventListener from "../../hooks/useEventListener";
import useAfterFirstRender from "../../hooks/useAfterFirstRender";
import "./Counter.scss";
const Counter = ({
count,
size,
kind,
color,
wrapperClassName,
maxDigits,
ariaLabeledBy,
ariaLabel,
id
}) => {
// State
const [countChangeAnimationState, setCountChangeAnimationState] = useState(
false
);
// Refs
const ref = useRef(null);
// Callbacks
const setCountChangedAnimationActive = useCallback(() => {
setCountChangeAnimationState(true);
}, [setCountChangeAnimationState]);
const setCountChangedAnimationNotActive = useCallback(() => {
setCountChangeAnimationState(false);
}, [setCountChangeAnimationState]);
// Listeners
useEventListener({
eventName: "animationend",
callback: setCountChangedAnimationNotActive,
ref
});
// Custom hooks
const isAfterFirstRender = useAfterFirstRender();
// Effects
useEffect(() => {
if (!isAfterFirstRender.current) {
setCountChangedAnimationActive();
}
}, [count, isAfterFirstRender, setCountChangedAnimationActive]);
useEffect(() => {
if (maxDigits <= 0) {
console.error("Max digits must be a positive number");
}
}, [maxDigits]);
// Memos
const classNames = useMemo(() => {
return cx(
"monday-style-counter",
`monday-style-counter--size-${size}`,
`monday-style-counter--kind-${kind}`,
`monday-style-counter--color-${color}`,
{
"monday-style-counter--with-animation": countChangeAnimationState
}
);
}, [size, kind, color, countChangeAnimationState]);
const countText =
count?.toString().length > maxDigits ? `${10 ** maxDigits - 1}+` : count;
return (
<span className={wrapperClassName} aria-label={`${ariaLabel} ${countText}`}>
<div className={classNames} aria-label={countText} ref={ref}>
<SwitchTransition mode="out-in">
<CSSTransition
classNames="monday-style-counter--fade"
addEndListener={(node, done) => {
node.addEventListener("transitionend", done, false);
}}
key={countText}
>
<span id={`counter-${id}`}>{countText}</span>
</CSSTransition>
</SwitchTransition>
</div>
</span>
);
};
Counter.sizes = COUNTER_SIZES;
Counter.colors = COUNTER_COLORS;
Counter.kinds = COUNTER_TYPES;
Counter.propTypes = {
/** id to pass to the element */
id: PropTypes.string,
wrapperClassName: PropTypes.string,
count: PropTypes.number,
/** element id to describe the counter accordingly */
ariaLabeledBy: PropTypes.string,
/** Counter Description */
ariaLabel: PropTypes.string,
size: PropTypes.oneOf([Counter.sizes.LARGE, Counter.sizes.SMALL]),
color: PropTypes.oneOf([
Counter.colors.PRIMARY,
Counter.colors.DARK,
Counter.colors.NEGATIVE
]),
kind: PropTypes.oneOf([Counter.kinds.FILL, Counter.kinds.LINE]),
/** maximum number of digits to display (see relevant story)*/
maxDigits: PropTypes.number
};
Counter.defaultProps = {
id: "",
wrapperClassName: "",
count: 0,
size: COUNTER_SIZES.LARGE,
color: COUNTER_COLORS.PRIMARY,
kind: COUNTER_TYPES.FILL,
maxDigits: 3,
ariaLabeledBy: "",
ariaLabel: ""
};
export default Counter;