Viewing File: /home/ubuntu/todaykat-frontend-base/node_modules/react-rating-stars-component/src/react-stars.js

import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import useConfig from './hooks/useConfig';
import Star from './star';

const parentStyles = {
    overflow: "hidden",
    position: "relative",
};

function getHalfStarStyles(color, uniqueness) {
    return `
    .react-stars-${uniqueness}:before {
      position: absolute;
      overflow: hidden;
      display: block;
      z-index: 1;
      top: 0; left: 0;
      width: 50%;
      content: attr(data-forhalf);
      color: ${color};
  }`;
}

function getHalfStarStyleForIcons(color) {
    return `
          span.react-stars-half > * {
          color: ${color};
      }`;
};

function ReactStars(props) {
    const [uniqueness, setUniqueness] = useState('');
    const [currentValue, setCurrentValue] = useState(0);
    const [stars, setStars] = useState([]);
    const [isUsingIcons, setIsUsingIcons] = useState(false);
    const [config, setConfig] = useConfig(props);
    const [halfStarAt, setHalfStarAt] = useState(0);
    const [halfStarHidden, setHalfStarHidden] = useState(false);
    const [classNames, setClassNames] = useState('');

    function iconsUsed(config) {
        return (
            (!config.isHalf && config.emptyIcon && config.filledIcon)
            || (config.isHalf && config.emptyIcon && config.halfIcon && config.filledIcon)
        )
    }

    function createUniqueness() {
        setUniqueness((Math.random() + "").replace(".", ""));
    }

    useEffect(() => {
        addClassNames();
        validateInitialValue(props.value, props.count);
        setStars(getStars(props.value));
        setConfig(props);
        createUniqueness();
        setIsUsingIcons(iconsUsed(props));
        setHalfStarAt(Math.floor(props.value));
        setHalfStarHidden(props.isHalf && props.value % 1 < 0.5);
    }, []);

    function validateInitialValue(value, count) {
        if (value < 0 || value > count) {
            setCurrentValue(0);
        }
        else {
            setCurrentValue(value);
        }
    }

    function addClassNames() {
        const reactStarsClass = 'react-stars';
        setClassNames(props.classNames + ` ${reactStarsClass}`);
    }

    function isDecimal(value) {
        return value % 1 === 0;
    }

    function getRate() {
        return config.isHalf ?
            Math.floor(currentValue) :
            Math.round(currentValue);
    }

    function getStars(activeCount) {
        if (typeof activeCount === 'undefined') {
            activeCount = getRate();
        }

        let stars = [];
        for (let i = 0; i < config.count; i++) {
            stars.push({
                active: i <= activeCount - 1
            });
        }
        return stars;
    }

    function mouseOver(event) {
        if (!config.edit) return;

        let index = Number(event.currentTarget.getAttribute('data-index'));

        if (config.isHalf) {
            const isAtHalf = moreThanHalf(event);
            setHalfStarHidden(isAtHalf);
            if (isAtHalf) index += 1;
            setHalfStarAt(index);
        }
        else {
            index += 1;
        }

        updateStars(index);
    }

    function updateStars(index) {
        var currentActive = stars.filter(x => x.active);
        if (index !== currentActive.length) {
            setStars(getStars(index));
        }
    }

    function moreThanHalf(event) {
        const { target } = event;
        const boundingClientRect = target.getBoundingClientRect();
        let mouseAt = event.clientX - boundingClientRect.left;
        mouseAt = Math.round(Math.abs(mouseAt));

        return mouseAt > boundingClientRect.width / 2
    }

    function mouseLeave() {
        if (!config.edit) return;

        updateHalfStarValues(currentValue);
        setStars(getStars());
    }

    function updateHalfStarValues(value) {
        if (config.isHalf) {
            setHalfStarHidden(isDecimal(value));
            setHalfStarAt(Math.floor(value));
        }
    }

    function onClick(event) {
        if (!config.edit) return;

        let index = Number(event.currentTarget.getAttribute('data-index'));
        let value;
        if (config.isHalf) {
            const isAtHalf = moreThanHalf(event);
            setHalfStarHidden(isAtHalf);
            if (isAtHalf) index += 1;
            value = isAtHalf ? index : index + 0.5;
            setHalfStarAt(index);
        }
        else {
            value = index = index + 1;
        }

        currentValueUpdated(value);
    }

    function renderHalfStarStyleElement() {
        return <style dangerouslySetInnerHTML={{
            __html: isUsingIcons
                ? getHalfStarStyleForIcons(config.activeColor)
                : getHalfStarStyles(config.activeColor, uniqueness)
        }}>
        </style>
    }

    function currentValueUpdated(value) {
        if (value !== currentValue) {
            setStars(getStars(value));
            setCurrentValue(value);
            props.onChange(value);
        }
    }

    function handleKeyDown(event) {
        if (!config.a11y && !config.edit) return;

        const { key } = event;
        let value = currentValue;

        const keyNumber = Number(key); // e.g. "1" => 1, "ArrowUp" => NaN
        if (keyNumber) {
            if (
                Number.isInteger(keyNumber) &&
                keyNumber > 0 &&
                keyNumber <= config.count
            ) {
                value = keyNumber;
            }
        } else {
            if ((key === "ArrowUp" || key === "ArrowRight") && value < config.count) {
                event.preventDefault();

                value += (config.isHalf ? 0.5 : 1);
            } else if ((key === "ArrowDown" || key === "ArrowLeft") && value > 0.5) {
                event.preventDefault();
                value -= (config.isHalf ? 0.5 : 1);
            }
        }

        updateHalfStarValues(value);

        currentValueUpdated(value);
    }

    function renderStars() {
        return stars.map((star, i) =>
            <Star
                key={i}
                index={i}
                active={star.active}
                config={config}
                onMouseOver={mouseOver}
                onMouseLeave={mouseLeave}
                onClick={onClick}
                halfStarHidden={halfStarHidden}
                halfStarAt={halfStarAt}
                isUsingIcons={isUsingIcons}
                uniqueness={uniqueness}
            />
        );
    }

    return <div className={`react-stars-wrapper-${uniqueness}`}
        style={{ display: 'flex' }}>
        <div tabIndex={config.a11y && config.edit ? 0 : null}
            aria-label='add rating by typing an integer from 0 to 5 or pressing arrow keys'
            onKeyDown={handleKeyDown}
            className={classNames}
            style={parentStyles} >
            {config.isHalf && renderHalfStarStyleElement()}
            {renderStars()}
            <p style={{ position: 'absolute', left: '-200rem' }} role='status'>
                {currentValue}
            </p>
        </div>
    </div>
}

ReactStars.propTypes = {
    classNames: PropTypes.string,
    edit: PropTypes.bool,
    half: PropTypes.bool,
    value: PropTypes.number,
    count: PropTypes.number,
    char: PropTypes.string,
    size: PropTypes.number,
    color: PropTypes.string,
    activeColor: PropTypes.string,
    emptyIcon: PropTypes.element,
    halfIcon: PropTypes.element,
    filledIcon: PropTypes.element,
    a11y: PropTypes.bool
}

ReactStars.defaultProps = {
    edit: true,
    half: false,
    value: 0,
    count: 5,
    char: '★',
    size: 15,
    color: 'gray',
    activeColor: '#ffd700',
    a11y: true,

    onChange: () => { }
};

export default ReactStars;
Back to Directory File Manager