rate2.mjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import { defineComponent, inject, ref, computed, markRaw, watch, openBlock, createElementBlock, unref, normalizeClass, normalizeStyle, Fragment, renderList, createVNode, withCtx, withDirectives, createBlock, resolveDynamicComponent, vShow, createCommentVNode, toDisplayString } from 'vue';
  2. import { ElIcon } from '../../icon/index.mjs';
  3. import { rateProps, rateEmits } from './rate.mjs';
  4. import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
  5. import { formContextKey, formItemContextKey } from '../../form/src/constants.mjs';
  6. import { useFormSize } from '../../form/src/hooks/use-form-common-props.mjs';
  7. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  8. import { useFormItemInputId } from '../../form/src/hooks/use-form-item.mjs';
  9. import { isArray, isObject, isString } from '@vue/shared';
  10. import { UPDATE_MODEL_EVENT, CHANGE_EVENT } from '../../../constants/event.mjs';
  11. import { EVENT_CODE } from '../../../constants/aria.mjs';
  12. import { hasClass } from '../../../utils/dom/style.mjs';
  13. const __default__ = defineComponent({
  14. name: "ElRate"
  15. });
  16. const _sfc_main = /* @__PURE__ */ defineComponent({
  17. ...__default__,
  18. props: rateProps,
  19. emits: rateEmits,
  20. setup(__props, { expose, emit }) {
  21. const props = __props;
  22. function getValueFromMap(value, map) {
  23. const isExcludedObject = (val) => isObject(val);
  24. const matchedKeys = Object.keys(map).map((key) => +key).filter((key) => {
  25. const val = map[key];
  26. const excluded = isExcludedObject(val) ? val.excluded : false;
  27. return excluded ? value < key : value <= key;
  28. }).sort((a, b) => a - b);
  29. const matchedValue = map[matchedKeys[0]];
  30. return isExcludedObject(matchedValue) && matchedValue.value || matchedValue;
  31. }
  32. const formContext = inject(formContextKey, void 0);
  33. const formItemContext = inject(formItemContextKey, void 0);
  34. const rateSize = useFormSize();
  35. const ns = useNamespace("rate");
  36. const { inputId, isLabeledByFormItem } = useFormItemInputId(props, {
  37. formItemContext
  38. });
  39. const currentValue = ref(props.modelValue);
  40. const hoverIndex = ref(-1);
  41. const pointerAtLeftHalf = ref(true);
  42. const rateClasses = computed(() => [ns.b(), ns.m(rateSize.value)]);
  43. const rateDisabled = computed(() => props.disabled || (formContext == null ? void 0 : formContext.disabled));
  44. const rateStyles = computed(() => {
  45. return ns.cssVarBlock({
  46. "void-color": props.voidColor,
  47. "disabled-void-color": props.disabledVoidColor,
  48. "fill-color": activeColor.value
  49. });
  50. });
  51. const text = computed(() => {
  52. let result = "";
  53. if (props.showScore) {
  54. result = props.scoreTemplate.replace(/\{\s*value\s*\}/, rateDisabled.value ? `${props.modelValue}` : `${currentValue.value}`);
  55. } else if (props.showText) {
  56. result = props.texts[Math.ceil(currentValue.value) - 1];
  57. }
  58. return result;
  59. });
  60. const valueDecimal = computed(() => props.modelValue * 100 - Math.floor(props.modelValue) * 100);
  61. const colorMap = computed(() => isArray(props.colors) ? {
  62. [props.lowThreshold]: props.colors[0],
  63. [props.highThreshold]: { value: props.colors[1], excluded: true },
  64. [props.max]: props.colors[2]
  65. } : props.colors);
  66. const activeColor = computed(() => {
  67. const color = getValueFromMap(currentValue.value, colorMap.value);
  68. return isObject(color) ? "" : color;
  69. });
  70. const decimalStyle = computed(() => {
  71. let width = "";
  72. if (rateDisabled.value) {
  73. width = `${valueDecimal.value}%`;
  74. } else if (props.allowHalf) {
  75. width = "50%";
  76. }
  77. return {
  78. color: activeColor.value,
  79. width
  80. };
  81. });
  82. const componentMap = computed(() => {
  83. let icons = isArray(props.icons) ? [...props.icons] : { ...props.icons };
  84. icons = markRaw(icons);
  85. return isArray(icons) ? {
  86. [props.lowThreshold]: icons[0],
  87. [props.highThreshold]: {
  88. value: icons[1],
  89. excluded: true
  90. },
  91. [props.max]: icons[2]
  92. } : icons;
  93. });
  94. const decimalIconComponent = computed(() => getValueFromMap(props.modelValue, componentMap.value));
  95. const voidComponent = computed(() => rateDisabled.value ? isString(props.disabledVoidIcon) ? props.disabledVoidIcon : markRaw(props.disabledVoidIcon) : isString(props.voidIcon) ? props.voidIcon : markRaw(props.voidIcon));
  96. const activeComponent = computed(() => getValueFromMap(currentValue.value, componentMap.value));
  97. function showDecimalIcon(item) {
  98. const showWhenDisabled = rateDisabled.value && valueDecimal.value > 0 && item - 1 < props.modelValue && item > props.modelValue;
  99. const showWhenAllowHalf = props.allowHalf && pointerAtLeftHalf.value && item - 0.5 <= currentValue.value && item > currentValue.value;
  100. return showWhenDisabled || showWhenAllowHalf;
  101. }
  102. function emitValue(value) {
  103. if (props.clearable && value === props.modelValue) {
  104. value = 0;
  105. }
  106. emit(UPDATE_MODEL_EVENT, value);
  107. if (props.modelValue !== value) {
  108. emit(CHANGE_EVENT, value);
  109. }
  110. }
  111. function selectValue(value) {
  112. if (rateDisabled.value) {
  113. return;
  114. }
  115. if (props.allowHalf && pointerAtLeftHalf.value) {
  116. emitValue(currentValue.value);
  117. } else {
  118. emitValue(value);
  119. }
  120. }
  121. function handleKey(e) {
  122. if (rateDisabled.value) {
  123. return;
  124. }
  125. let _currentValue = currentValue.value;
  126. const code = e.code;
  127. if (code === EVENT_CODE.up || code === EVENT_CODE.right) {
  128. if (props.allowHalf) {
  129. _currentValue += 0.5;
  130. } else {
  131. _currentValue += 1;
  132. }
  133. e.stopPropagation();
  134. e.preventDefault();
  135. } else if (code === EVENT_CODE.left || code === EVENT_CODE.down) {
  136. if (props.allowHalf) {
  137. _currentValue -= 0.5;
  138. } else {
  139. _currentValue -= 1;
  140. }
  141. e.stopPropagation();
  142. e.preventDefault();
  143. }
  144. _currentValue = _currentValue < 0 ? 0 : _currentValue;
  145. _currentValue = _currentValue > props.max ? props.max : _currentValue;
  146. emit(UPDATE_MODEL_EVENT, _currentValue);
  147. emit(CHANGE_EVENT, _currentValue);
  148. return _currentValue;
  149. }
  150. function setCurrentValue(value, event) {
  151. if (rateDisabled.value) {
  152. return;
  153. }
  154. if (props.allowHalf && event) {
  155. let target = event.target;
  156. if (hasClass(target, ns.e("item"))) {
  157. target = target.querySelector(`.${ns.e("icon")}`);
  158. }
  159. if (target.clientWidth === 0 || hasClass(target, ns.e("decimal"))) {
  160. target = target.parentNode;
  161. }
  162. pointerAtLeftHalf.value = event.offsetX * 2 <= target.clientWidth;
  163. currentValue.value = pointerAtLeftHalf.value ? value - 0.5 : value;
  164. } else {
  165. currentValue.value = value;
  166. }
  167. hoverIndex.value = value;
  168. }
  169. function resetCurrentValue() {
  170. if (rateDisabled.value) {
  171. return;
  172. }
  173. if (props.allowHalf) {
  174. pointerAtLeftHalf.value = props.modelValue !== Math.floor(props.modelValue);
  175. }
  176. currentValue.value = props.modelValue;
  177. hoverIndex.value = -1;
  178. }
  179. watch(() => props.modelValue, (val) => {
  180. currentValue.value = val;
  181. pointerAtLeftHalf.value = props.modelValue !== Math.floor(props.modelValue);
  182. });
  183. if (!props.modelValue) {
  184. emit(UPDATE_MODEL_EVENT, 0);
  185. }
  186. expose({
  187. setCurrentValue,
  188. resetCurrentValue
  189. });
  190. return (_ctx, _cache) => {
  191. var _a;
  192. return openBlock(), createElementBlock("div", {
  193. id: unref(inputId),
  194. class: normalizeClass([unref(rateClasses), unref(ns).is("disabled", unref(rateDisabled))]),
  195. role: "slider",
  196. "aria-label": !unref(isLabeledByFormItem) ? _ctx.ariaLabel || "rating" : void 0,
  197. "aria-labelledby": unref(isLabeledByFormItem) ? (_a = unref(formItemContext)) == null ? void 0 : _a.labelId : void 0,
  198. "aria-valuenow": currentValue.value,
  199. "aria-valuetext": unref(text) || void 0,
  200. "aria-valuemin": "0",
  201. "aria-valuemax": _ctx.max,
  202. tabindex: "0",
  203. style: normalizeStyle(unref(rateStyles)),
  204. onKeydown: handleKey
  205. }, [
  206. (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.max, (item, key) => {
  207. return openBlock(), createElementBlock("span", {
  208. key,
  209. class: normalizeClass(unref(ns).e("item")),
  210. onMousemove: ($event) => setCurrentValue(item, $event),
  211. onMouseleave: resetCurrentValue,
  212. onClick: ($event) => selectValue(item)
  213. }, [
  214. createVNode(unref(ElIcon), {
  215. class: normalizeClass([
  216. unref(ns).e("icon"),
  217. { hover: hoverIndex.value === item },
  218. unref(ns).is("active", item <= currentValue.value)
  219. ])
  220. }, {
  221. default: withCtx(() => [
  222. !showDecimalIcon(item) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
  223. withDirectives((openBlock(), createBlock(resolveDynamicComponent(unref(activeComponent)), null, null, 512)), [
  224. [vShow, item <= currentValue.value]
  225. ]),
  226. withDirectives((openBlock(), createBlock(resolveDynamicComponent(unref(voidComponent)), null, null, 512)), [
  227. [vShow, !(item <= currentValue.value)]
  228. ])
  229. ], 64)) : createCommentVNode("v-if", true),
  230. showDecimalIcon(item) ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
  231. (openBlock(), createBlock(resolveDynamicComponent(unref(voidComponent)), {
  232. class: normalizeClass([unref(ns).em("decimal", "box")])
  233. }, null, 8, ["class"])),
  234. createVNode(unref(ElIcon), {
  235. style: normalizeStyle(unref(decimalStyle)),
  236. class: normalizeClass([unref(ns).e("icon"), unref(ns).e("decimal")])
  237. }, {
  238. default: withCtx(() => [
  239. (openBlock(), createBlock(resolveDynamicComponent(unref(decimalIconComponent))))
  240. ]),
  241. _: 1
  242. }, 8, ["style", "class"])
  243. ], 64)) : createCommentVNode("v-if", true)
  244. ]),
  245. _: 2
  246. }, 1032, ["class"])
  247. ], 42, ["onMousemove", "onClick"]);
  248. }), 128)),
  249. _ctx.showText || _ctx.showScore ? (openBlock(), createElementBlock("span", {
  250. key: 0,
  251. class: normalizeClass(unref(ns).e("text")),
  252. style: normalizeStyle({ color: _ctx.textColor })
  253. }, toDisplayString(unref(text)), 7)) : createCommentVNode("v-if", true)
  254. ], 46, ["id", "aria-label", "aria-labelledby", "aria-valuenow", "aria-valuetext", "aria-valuemax"]);
  255. };
  256. }
  257. });
  258. var Rate = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "rate.vue"]]);
  259. export { Rate as default };
  260. //# sourceMappingURL=rate2.mjs.map