import { computed, defineComponent, ref, createVNode as _createVNode } from "vue"; import { addUnit, truthProp, numericProp, preventDefault, makeStringProp, makeNumberProp, makeNumericProp, createNamespace } from "../utils/index.mjs"; import { useRect, useCustomFieldValue, useEventListener } from "@vant/use"; import { useRefs } from "../composables/use-refs.mjs"; import { useTouch } from "../composables/use-touch.mjs"; import { Icon } from "../icon/index.mjs"; const [name, bem] = createNamespace("rate"); function getRateStatus(value, index, allowHalf, readonly) { if (value >= index) { return { status: "full", value: 1 }; } if (value + 0.5 >= index && allowHalf && !readonly) { return { status: "half", value: 0.5 }; } if (value + 1 >= index && allowHalf && readonly) { const cardinal = 10 ** 10; return { status: "half", value: Math.round((value - index + 1) * cardinal) / cardinal }; } return { status: "void", value: 0 }; } const rateProps = { size: numericProp, icon: makeStringProp("star"), color: String, count: makeNumericProp(5), gutter: numericProp, clearable: Boolean, readonly: Boolean, disabled: Boolean, voidIcon: makeStringProp("star-o"), allowHalf: Boolean, voidColor: String, touchable: truthProp, iconPrefix: String, modelValue: makeNumberProp(0), disabledColor: String }; var stdin_default = defineComponent({ name, props: rateProps, emits: ["change", "update:modelValue"], setup(props, { emit }) { const touch = useTouch(); const [itemRefs, setItemRefs] = useRefs(); const groupRef = ref(); const unselectable = computed(() => props.readonly || props.disabled); const untouchable = computed(() => unselectable.value || !props.touchable); const list = computed(() => Array(+props.count).fill("").map((_, i) => getRateStatus(props.modelValue, i + 1, props.allowHalf, props.readonly))); let ranges; let groupRefRect; let minRectTop = Number.MAX_SAFE_INTEGER; let maxRectTop = Number.MIN_SAFE_INTEGER; const updateRanges = () => { groupRefRect = useRect(groupRef); const rects = itemRefs.value.map(useRect); ranges = []; rects.forEach((rect, index) => { minRectTop = Math.min(rect.top, minRectTop); maxRectTop = Math.max(rect.top, maxRectTop); if (props.allowHalf) { ranges.push({ score: index + 0.5, left: rect.left, top: rect.top, height: rect.height }, { score: index + 1, left: rect.left + rect.width / 2, top: rect.top, height: rect.height }); } else { ranges.push({ score: index + 1, left: rect.left, top: rect.top, height: rect.height }); } }); }; const getScoreByPosition = (x, y) => { for (let i = ranges.length - 1; i > 0; i--) { if (y >= groupRefRect.top && y <= groupRefRect.bottom) { if (x > ranges[i].left && y >= ranges[i].top && y <= ranges[i].top + ranges[i].height) { return ranges[i].score; } } else { const curTop = y < groupRefRect.top ? minRectTop : maxRectTop; if (x > ranges[i].left && ranges[i].top === curTop) { return ranges[i].score; } } } return props.allowHalf ? 0.5 : 1; }; const select = (value) => { if (unselectable.value || value === props.modelValue) return; emit("update:modelValue", value); emit("change", value); }; const onTouchStart = (event) => { if (untouchable.value) { return; } touch.start(event); updateRanges(); }; const onTouchMove = (event) => { if (untouchable.value) { return; } touch.move(event); if (touch.isHorizontal() && !touch.isTap.value) { const { clientX, clientY } = event.touches[0]; preventDefault(event); select(getScoreByPosition(clientX, clientY)); } }; const renderStar = (item, index) => { const { icon, size, color, count, gutter, voidIcon, disabled, voidColor, allowHalf, iconPrefix, disabledColor } = props; const score = index + 1; const isFull = item.status === "full"; const isVoid = item.status === "void"; const renderHalf = allowHalf && item.value > 0 && item.value < 1; let style; if (gutter && score !== +count) { style = { paddingRight: addUnit(gutter) }; } const onClickItem = (event) => { updateRanges(); let value = allowHalf ? getScoreByPosition(event.clientX, event.clientY) : score; if (props.clearable && touch.isTap.value && value === props.modelValue) { value = 0; } select(value); }; return _createVNode("div", { "key": index, "ref": setItemRefs(index), "role": "radio", "style": style, "class": bem("item"), "tabindex": disabled ? void 0 : 0, "aria-setsize": count, "aria-posinset": score, "aria-checked": !isVoid, "onClick": onClickItem }, [_createVNode(Icon, { "size": size, "name": isFull ? icon : voidIcon, "class": bem("icon", { disabled, full: isFull }), "color": disabled ? disabledColor : isFull ? color : voidColor, "classPrefix": iconPrefix }, null), renderHalf && _createVNode(Icon, { "size": size, "style": { width: item.value + "em" }, "name": isVoid ? voidIcon : icon, "class": bem("icon", ["half", { disabled, full: !isVoid }]), "color": disabled ? disabledColor : isVoid ? voidColor : color, "classPrefix": iconPrefix }, null)]); }; useCustomFieldValue(() => props.modelValue); useEventListener("touchmove", onTouchMove, { target: groupRef }); return () => _createVNode("div", { "ref": groupRef, "role": "radiogroup", "class": bem({ readonly: props.readonly, disabled: props.disabled }), "tabindex": props.disabled ? void 0 : 0, "aria-disabled": props.disabled, "aria-readonly": props.readonly, "onTouchstartPassive": onTouchStart }, [list.value.map(renderStar)]); } }); export { stdin_default as default, rateProps };