import classNames from "classnames"; import React, { useState } from "react"; import { ControlledProps, useDefaultValue } from "../form/controlled"; import { Icon } from "../icon"; import { callBoth, useConfig } from "../util"; import { StyledProps } from "../_type"; import { injectValue } from "../_util/inject-value"; import { KeyMap } from "../_util/key-map"; export interface RateCharacterProps { /** * 序号 */ index: number; /** * 是否被全选 */ full: boolean; /** * 是否被半选 */ half: boolean; } export interface RateProps extends ControlledProps, StyledProps { /** * 字符数 * @default 5 */ count?: number; /** * 是否启用半选 * @default false */ allowHalf?: boolean; /** * 是否启用再次点击当前值后清空 * @default true */ allowClear?: boolean; /** * 是否为只读形式 * @default false */ readonly?: boolean; /** * 主题 * @default "default" */ theme?: "default" | "primary"; /** * 尺寸 * @default "m" */ size?: "m" | "l"; /** * 自定义字符 * * `[未点亮字符, 点亮字符]` */ character?: | [React.ReactNode, React.ReactNode] | ((props: RateCharacterProps) => [React.ReactNode, React.ReactNode]); } const defaultCharacter = size => [ , , ]; const primaryCharacter = size => [ , , ]; export const Rate = React.forwardRef(function Rate( props: RateProps, ref: React.Ref ) { const { classPrefix } = useConfig(); const { value, onChange, count = 5, allowClear = true, allowHalf, readonly, theme, size, character = theme === "primary" ? primaryCharacter(size) : defaultCharacter(size), className, ...restProps } = useDefaultValue(props, 0); // 内部 const innerValue = Math.round(value * 2); const [hoverValue, setHoverValue] = useState(-1); const current = hoverValue >= 0 ? hoverValue : innerValue; function handleClick(event: React.MouseEvent, value: number) { if (readonly) { return; } setHoverValue(value); if (allowClear && innerValue === value) { onChange(0, { event }); setHoverValue(-1); return; } onChange(Number((value / 2).toFixed(1)), { event, }); } function handleMouseEnter(value: number) { if (readonly) { return; } setHoverValue(value); } function handleMouseLeave() { setHoverValue(-1); } function handleKeyDown(event: React.KeyboardEvent) { if (readonly) { return; } switch (event.key) { case KeyMap.Left: setHoverValue(-1); if (innerValue >= 1) { onChange( Number(((innerValue - (allowHalf ? 1 : 2)) / 2).toFixed(1)), { event } ); } break; case KeyMap.Right: setHoverValue(-1); if (innerValue < count * 2) { onChange( Number(((innerValue + (allowHalf ? 1 : 2)) / 2).toFixed(1)), { event } ); } break; case KeyMap.Backspace: setHoverValue(-1); onChange(0, { event }); break; } } return (
{Array(count) .fill(null) .map((_, index) => { const itemValue = index * 2 + 2; const half = itemValue === current + 1 && current % 2 > 0; const full = itemValue <= current; const [inactive, active] = injectValue(character)({ index, half, full, }); return (
{allowHalf && (
handleClick(event, itemValue - 1)} onMouseEnter={() => handleMouseEnter(itemValue - 1)} > {!(full || half) ? inactive : active}
)}
handleClick(event, itemValue)} onMouseEnter={() => handleMouseEnter(itemValue)} > {!full ? inactive : active}
); })}
); });