/* eslint-disable react-hooks/rules-of-hooks */ import { getClassNames } from '@websolutespa/bom-core'; import React, { useEffect, useState } from 'react'; import styled, { css } from 'styled-components'; import { tupleNumber } from '../../components/tooltip/tooltip-props'; import { UIStyledComponentProps } from '../../components/types'; import { getCssResponsive } from '../../components/utils'; import { SizeVariant } from '../../variants'; import { RatingIcon } from './rating-icon'; const ratingCountTuple = tupleNumber(2, 3, 4, 5, 6, 7, 8, 9, 10); const ratingValueTuple = tupleNumber(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); export type RatingValue = typeof ratingValueTuple[number]; export type RatingCount = typeof ratingCountTuple[number]; type Props = { size?: SizeVariant; className?: string; count?: RatingCount | number; icon?: React.JSX.Element; initialValue?: RatingValue; locked?: boolean; value?: RatingValue | number; onValueChange?: (value: number) => void; onLockedChange?: (locked: boolean) => void; }; export type RatingProps = UIStyledComponentProps; const StyledRating = styled.div` display: inline-flex; align-items: center; .icon { width: 1.2em; height: 1.2em; margin-right: 0.1em; color: var(--form-border-color-disabled); cursor: pointer; & :global(svg) { width: 100%; height: 100%; transform: scale(1); transition: transform, color, fill 30ms linear; } &.hovered { color: var(--form-color-hover); :global(svg) { transform: scale(0.9); } } } &.disabled { .icon { cursor: default; } } ${props => (css`font-size: var(--button-size-${props.size || 'md'});`)} ${props => getCssResponsive(props)} `; export const Rating: React.FC = ({ className = '', count = 5 as RatingCount, icon = () as React.JSX.Element, initialValue = 1 as RatingValue, locked: lockedProp = false, value: customValue, onValueChange, onLockedChange, ...props }: React.PropsWithChildren) => { const [value, setValue] = useState(initialValue); const [locked, setLocked] = useState(lockedProp); const onSetLocked = (next: boolean) => { setLocked(next); onLockedChange && onLockedChange(next); }; const onSetValue = (next: number) => { setValue(next); const emitValue = next > count ? count : next; onValueChange && onValueChange(emitValue); }; const onClick = (index: number) => { if (locked) { return onSetLocked(false); } onSetValue(index); onSetLocked(true); }; const onMouseEnter = (index: number) => { if (locked) { return; } onSetValue(index); }; useEffect(() => { if (typeof customValue === 'undefined') { return; } setValue(customValue < 0 ? 0 : customValue); }, [customValue]); const ratingClassNames = getClassNames('rating', className); const getIconClassNames = (index: number) => getClassNames('icon', { hovered: index + 1 <= value }); return ( {[...Array(count)].map((_, index) => (
onMouseEnter(index + 1)} onClick={() => onClick(index + 1)}> {icon}
))}
); }; Rating.displayName = 'Rating';