Rate.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. var __defProp = Object.defineProperty;
  2. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  3. var __getOwnPropNames = Object.getOwnPropertyNames;
  4. var __hasOwnProp = Object.prototype.hasOwnProperty;
  5. var __export = (target, all) => {
  6. for (var name2 in all)
  7. __defProp(target, name2, { get: all[name2], enumerable: true });
  8. };
  9. var __copyProps = (to, from, except, desc) => {
  10. if (from && typeof from === "object" || typeof from === "function") {
  11. for (let key of __getOwnPropNames(from))
  12. if (!__hasOwnProp.call(to, key) && key !== except)
  13. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  14. }
  15. return to;
  16. };
  17. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  18. var stdin_exports = {};
  19. __export(stdin_exports, {
  20. default: () => stdin_default,
  21. rateProps: () => rateProps
  22. });
  23. module.exports = __toCommonJS(stdin_exports);
  24. var import_vue = require("vue");
  25. var import_utils = require("../utils");
  26. var import_use = require("@vant/use");
  27. var import_use_refs = require("../composables/use-refs");
  28. var import_use_touch = require("../composables/use-touch");
  29. var import_icon = require("../icon");
  30. const [name, bem] = (0, import_utils.createNamespace)("rate");
  31. function getRateStatus(value, index, allowHalf, readonly) {
  32. if (value >= index) {
  33. return {
  34. status: "full",
  35. value: 1
  36. };
  37. }
  38. if (value + 0.5 >= index && allowHalf && !readonly) {
  39. return {
  40. status: "half",
  41. value: 0.5
  42. };
  43. }
  44. if (value + 1 >= index && allowHalf && readonly) {
  45. const cardinal = 10 ** 10;
  46. return {
  47. status: "half",
  48. value: Math.round((value - index + 1) * cardinal) / cardinal
  49. };
  50. }
  51. return {
  52. status: "void",
  53. value: 0
  54. };
  55. }
  56. const rateProps = {
  57. size: import_utils.numericProp,
  58. icon: (0, import_utils.makeStringProp)("star"),
  59. color: String,
  60. count: (0, import_utils.makeNumericProp)(5),
  61. gutter: import_utils.numericProp,
  62. clearable: Boolean,
  63. readonly: Boolean,
  64. disabled: Boolean,
  65. voidIcon: (0, import_utils.makeStringProp)("star-o"),
  66. allowHalf: Boolean,
  67. voidColor: String,
  68. touchable: import_utils.truthProp,
  69. iconPrefix: String,
  70. modelValue: (0, import_utils.makeNumberProp)(0),
  71. disabledColor: String
  72. };
  73. var stdin_default = (0, import_vue.defineComponent)({
  74. name,
  75. props: rateProps,
  76. emits: ["change", "update:modelValue"],
  77. setup(props, {
  78. emit
  79. }) {
  80. const touch = (0, import_use_touch.useTouch)();
  81. const [itemRefs, setItemRefs] = (0, import_use_refs.useRefs)();
  82. const groupRef = (0, import_vue.ref)();
  83. const unselectable = (0, import_vue.computed)(() => props.readonly || props.disabled);
  84. const untouchable = (0, import_vue.computed)(() => unselectable.value || !props.touchable);
  85. const list = (0, import_vue.computed)(() => Array(+props.count).fill("").map((_, i) => getRateStatus(props.modelValue, i + 1, props.allowHalf, props.readonly)));
  86. let ranges;
  87. let groupRefRect;
  88. let minRectTop = Number.MAX_SAFE_INTEGER;
  89. let maxRectTop = Number.MIN_SAFE_INTEGER;
  90. const updateRanges = () => {
  91. groupRefRect = (0, import_use.useRect)(groupRef);
  92. const rects = itemRefs.value.map(import_use.useRect);
  93. ranges = [];
  94. rects.forEach((rect, index) => {
  95. minRectTop = Math.min(rect.top, minRectTop);
  96. maxRectTop = Math.max(rect.top, maxRectTop);
  97. if (props.allowHalf) {
  98. ranges.push({
  99. score: index + 0.5,
  100. left: rect.left,
  101. top: rect.top,
  102. height: rect.height
  103. }, {
  104. score: index + 1,
  105. left: rect.left + rect.width / 2,
  106. top: rect.top,
  107. height: rect.height
  108. });
  109. } else {
  110. ranges.push({
  111. score: index + 1,
  112. left: rect.left,
  113. top: rect.top,
  114. height: rect.height
  115. });
  116. }
  117. });
  118. };
  119. const getScoreByPosition = (x, y) => {
  120. for (let i = ranges.length - 1; i > 0; i--) {
  121. if (y >= groupRefRect.top && y <= groupRefRect.bottom) {
  122. if (x > ranges[i].left && y >= ranges[i].top && y <= ranges[i].top + ranges[i].height) {
  123. return ranges[i].score;
  124. }
  125. } else {
  126. const curTop = y < groupRefRect.top ? minRectTop : maxRectTop;
  127. if (x > ranges[i].left && ranges[i].top === curTop) {
  128. return ranges[i].score;
  129. }
  130. }
  131. }
  132. return props.allowHalf ? 0.5 : 1;
  133. };
  134. const select = (value) => {
  135. if (unselectable.value || value === props.modelValue) return;
  136. emit("update:modelValue", value);
  137. emit("change", value);
  138. };
  139. const onTouchStart = (event) => {
  140. if (untouchable.value) {
  141. return;
  142. }
  143. touch.start(event);
  144. updateRanges();
  145. };
  146. const onTouchMove = (event) => {
  147. if (untouchable.value) {
  148. return;
  149. }
  150. touch.move(event);
  151. if (touch.isHorizontal() && !touch.isTap.value) {
  152. const {
  153. clientX,
  154. clientY
  155. } = event.touches[0];
  156. (0, import_utils.preventDefault)(event);
  157. select(getScoreByPosition(clientX, clientY));
  158. }
  159. };
  160. const renderStar = (item, index) => {
  161. const {
  162. icon,
  163. size,
  164. color,
  165. count,
  166. gutter,
  167. voidIcon,
  168. disabled,
  169. voidColor,
  170. allowHalf,
  171. iconPrefix,
  172. disabledColor
  173. } = props;
  174. const score = index + 1;
  175. const isFull = item.status === "full";
  176. const isVoid = item.status === "void";
  177. const renderHalf = allowHalf && item.value > 0 && item.value < 1;
  178. let style;
  179. if (gutter && score !== +count) {
  180. style = {
  181. paddingRight: (0, import_utils.addUnit)(gutter)
  182. };
  183. }
  184. const onClickItem = (event) => {
  185. updateRanges();
  186. let value = allowHalf ? getScoreByPosition(event.clientX, event.clientY) : score;
  187. if (props.clearable && touch.isTap.value && value === props.modelValue) {
  188. value = 0;
  189. }
  190. select(value);
  191. };
  192. return (0, import_vue.createVNode)("div", {
  193. "key": index,
  194. "ref": setItemRefs(index),
  195. "role": "radio",
  196. "style": style,
  197. "class": bem("item"),
  198. "tabindex": disabled ? void 0 : 0,
  199. "aria-setsize": count,
  200. "aria-posinset": score,
  201. "aria-checked": !isVoid,
  202. "onClick": onClickItem
  203. }, [(0, import_vue.createVNode)(import_icon.Icon, {
  204. "size": size,
  205. "name": isFull ? icon : voidIcon,
  206. "class": bem("icon", {
  207. disabled,
  208. full: isFull
  209. }),
  210. "color": disabled ? disabledColor : isFull ? color : voidColor,
  211. "classPrefix": iconPrefix
  212. }, null), renderHalf && (0, import_vue.createVNode)(import_icon.Icon, {
  213. "size": size,
  214. "style": {
  215. width: item.value + "em"
  216. },
  217. "name": isVoid ? voidIcon : icon,
  218. "class": bem("icon", ["half", {
  219. disabled,
  220. full: !isVoid
  221. }]),
  222. "color": disabled ? disabledColor : isVoid ? voidColor : color,
  223. "classPrefix": iconPrefix
  224. }, null)]);
  225. };
  226. (0, import_use.useCustomFieldValue)(() => props.modelValue);
  227. (0, import_use.useEventListener)("touchmove", onTouchMove, {
  228. target: groupRef
  229. });
  230. return () => (0, import_vue.createVNode)("div", {
  231. "ref": groupRef,
  232. "role": "radiogroup",
  233. "class": bem({
  234. readonly: props.readonly,
  235. disabled: props.disabled
  236. }),
  237. "tabindex": props.disabled ? void 0 : 0,
  238. "aria-disabled": props.disabled,
  239. "aria-readonly": props.readonly,
  240. "onTouchstartPassive": onTouchStart
  241. }, [list.value.map(renderStar)]);
  242. }
  243. });