UNPKG

@arco-design/web-vue

Version:

Arco Design Vue 2.0: A Vue.js 3 UI Library

217 lines (216 loc) 7.55 kB
import { defineComponent, toRefs, toRef, ref, watch, computed, createVNode, mergeProps } from "vue"; import NP from "number-precision"; import IconStarFill from "../icon/icon-star-fill/index.js"; import IconFaceMehFill from "../icon/icon-face-meh-fill/index.js"; import IconFaceSmileFill from "../icon/icon-face-smile-fill/index.js"; import IconFaceFrownFill from "../icon/icon-face-frown-fill/index.js"; import { getPrefixCls } from "../_utils/global-config.js"; import { useFormItem } from "../_hooks/use-form-item.js"; import { isUndefined, isNull, isString, isObject } from "../_utils/is.js"; var _Rate = defineComponent({ name: "Rate", props: { count: { type: Number, default: 5 }, modelValue: { type: Number, default: void 0 }, defaultValue: { type: Number, default: 0 }, allowHalf: { type: Boolean, default: false }, allowClear: { type: Boolean, default: false }, grading: { type: Boolean, default: false }, readonly: { type: Boolean, default: false }, disabled: { type: Boolean, default: false }, color: { type: [String, Object] } }, emits: { "update:modelValue": (value) => true, "change": (value) => true, "hoverChange": (value) => true }, setup(props, { emit, slots }) { const { modelValue } = toRefs(props); const prefixCls = getPrefixCls("rate"); const { mergedDisabled: _mergedDisabled, eventHandlers } = useFormItem({ disabled: toRef(props, "disabled") }); const _value = ref(props.defaultValue); const animation = ref(false); watch(modelValue, (value) => { if (isUndefined(value) || isNull(value)) { _value.value = 0; } }); const hoverIndex = ref(0); const computedValue = computed(() => { var _a; return (_a = props.modelValue) != null ? _a : _value.value; }); const displayIndex = computed(() => { const fixedValue = props.allowHalf ? NP.times(NP.round(NP.divide(computedValue.value, 0.5), 0), 0.5) : Math.round(computedValue.value); return hoverIndex.value || fixedValue; }); const mergedDisabled = computed(() => _mergedDisabled.value || props.readonly); const indexArray = computed(() => [...Array(props.grading ? 5 : props.count)]); const customColor = computed(() => { var _a; if (isString(props.color)) { return indexArray.value.map(() => props.color); } if (isObject(props.color)) { const sortedKeys = Object.keys(props.color).map((key) => Number(key)).sort((a, b) => b - a); let threshold = (_a = sortedKeys.pop()) != null ? _a : indexArray.value.length; return indexArray.value.map((_, index) => { var _a2; if (index + 1 > threshold) { threshold = (_a2 = sortedKeys.pop()) != null ? _a2 : threshold; } return props.color[String(threshold)]; }); } return void 0; }); const resetHoverIndex = () => { if (hoverIndex.value) { hoverIndex.value = 0; emit("hoverChange", 0); } }; const handleMouseEnter = (index, isHalf) => { const newHoverIndex = isHalf && props.allowHalf ? index + 0.5 : index + 1; if (newHoverIndex !== hoverIndex.value) { hoverIndex.value = newHoverIndex; emit("hoverChange", newHoverIndex); } }; const handleClick = (index, isHalf) => { var _a, _b, _c, _d; const newValue = isHalf && props.allowHalf ? index + 0.5 : index + 1; animation.value = true; if (newValue !== computedValue.value) { _value.value = newValue; emit("update:modelValue", newValue); emit("change", newValue); (_b = (_a = eventHandlers.value) == null ? void 0 : _a.onChange) == null ? void 0 : _b.call(_a); } else if (props.allowClear) { _value.value = 0; emit("update:modelValue", 0); emit("change", 0); (_d = (_c = eventHandlers.value) == null ? void 0 : _c.onChange) == null ? void 0 : _d.call(_c); } }; const handleAnimationEnd = (index) => { if (animation.value && index + 1 >= computedValue.value - 1) { animation.value = false; } }; const renderGradingCharacter = (index, displayIndex2) => { if (index > displayIndex2) { return createVNode(IconFaceMehFill, null, null); } if (displayIndex2 <= 2) { return createVNode(IconFaceFrownFill, null, null); } if (displayIndex2 <= 3) { return createVNode(IconFaceMehFill, null, null); } return createVNode(IconFaceSmileFill, null, null); }; const getAriaProps = (index, isHalf = false) => { return { "role": "radio", "aria-checked": index + (isHalf ? 0.5 : 1) <= computedValue.value, "aria-setsize": indexArray.value.length, "aria-posinset": index + (isHalf ? 0.5 : 1) }; }; const renderElement = (index) => { if (props.grading) { return renderGradingCharacter(index, displayIndex.value); } if (slots.character) { return slots.character({ index }); } return createVNode(IconStarFill, null, null); }; const renderCharacter = (index) => { const leftProps = mergedDisabled.value ? {} : { onMouseenter: () => handleMouseEnter(index, true), onClick: () => handleClick(index, true) }; const rightProps = mergedDisabled.value ? {} : { onMouseenter: () => handleMouseEnter(index, false), onClick: () => handleClick(index, false) }; const style = animation.value ? { animationDelay: `${50 * index}ms` } : void 0; const parseDisplayIndex = Math.ceil(displayIndex.value) - 1; const leftStyle = customColor.value && props.allowHalf && index + 0.5 === displayIndex.value ? { color: customColor.value[parseDisplayIndex] } : void 0; const rightStyle = customColor.value && index + 1 <= displayIndex.value ? { color: customColor.value[parseDisplayIndex] } : void 0; const cls2 = [`${prefixCls}-character`, { [`${prefixCls}-character-half`]: props.allowHalf && index + 0.5 === displayIndex.value, [`${prefixCls}-character-full`]: index + 1 <= displayIndex.value, [`${prefixCls}-character-scale`]: animation.value && index + 1 < computedValue.value }]; return createVNode("div", mergeProps({ "class": cls2, "style": style }, !props.allowHalf ? getAriaProps(index) : void 0, { "onAnimationend": () => handleAnimationEnd(index) }), [createVNode("div", mergeProps({ "class": `${prefixCls}-character-left`, "style": leftStyle }, leftProps, props.allowHalf ? getAriaProps(index, true) : void 0), [renderElement(index)]), createVNode("div", mergeProps({ "class": `${prefixCls}-character-right`, "style": rightStyle }, rightProps, props.allowHalf ? getAriaProps(index) : void 0), [renderElement(index)])]); }; const cls = computed(() => [prefixCls, { [`${prefixCls}-readonly`]: props.readonly, [`${prefixCls}-disabled`]: _mergedDisabled.value }]); return () => createVNode("div", { "class": cls.value, "onMouseleave": resetHoverIndex }, [indexArray.value.map((_, index) => renderCharacter(index))]); } }); export { _Rate as default };