1e32073158657f9e4bae2e654a3946df17864f55d8e784866aa4bfd432808ea6c82ca18744d9f5f746f33915bad992ca9be7408627395be27a1d39829bf8c4 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. import { defineComponent, ref, reactive, computed, watch, onMounted, onUpdated, openBlock, createElementBlock, normalizeClass, unref, withModifiers, withDirectives, withKeys, renderSlot, createVNode, withCtx, createBlock, createCommentVNode, createSlots } from 'vue';
  2. import { isNil } from 'lodash-unified';
  3. import { ElInput } from '../../input/index.mjs';
  4. import { ElIcon } from '../../icon/index.mjs';
  5. import { ArrowDown, Minus, ArrowUp, Plus } from '@element-plus/icons-vue';
  6. import { inputNumberProps, inputNumberEmits } from './input-number.mjs';
  7. import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
  8. import { vRepeatClick } from '../../../directives/repeat-click/index.mjs';
  9. import { useLocale } from '../../../hooks/use-locale/index.mjs';
  10. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  11. import { useFormItem } from '../../form/src/hooks/use-form-item.mjs';
  12. import { isNumber, isUndefined } from '../../../utils/types.mjs';
  13. import { useFormSize, useFormDisabled } from '../../form/src/hooks/use-form-common-props.mjs';
  14. import { UPDATE_MODEL_EVENT, INPUT_EVENT, CHANGE_EVENT } from '../../../constants/event.mjs';
  15. import { EVENT_CODE } from '../../../constants/aria.mjs';
  16. import { throwError, debugWarn } from '../../../utils/error.mjs';
  17. import { isString } from '@vue/shared';
  18. const __default__ = defineComponent({
  19. name: "ElInputNumber"
  20. });
  21. const _sfc_main = /* @__PURE__ */ defineComponent({
  22. ...__default__,
  23. props: inputNumberProps,
  24. emits: inputNumberEmits,
  25. setup(__props, { expose, emit }) {
  26. const props = __props;
  27. const { t } = useLocale();
  28. const ns = useNamespace("input-number");
  29. const input = ref();
  30. const data = reactive({
  31. currentValue: props.modelValue,
  32. userInput: null
  33. });
  34. const { formItem } = useFormItem();
  35. const minDisabled = computed(() => isNumber(props.modelValue) && props.modelValue <= props.min);
  36. const maxDisabled = computed(() => isNumber(props.modelValue) && props.modelValue >= props.max);
  37. const numPrecision = computed(() => {
  38. const stepPrecision = getPrecision(props.step);
  39. if (!isUndefined(props.precision)) {
  40. if (stepPrecision > props.precision) ;
  41. return props.precision;
  42. } else {
  43. return Math.max(getPrecision(props.modelValue), stepPrecision);
  44. }
  45. });
  46. const controlsAtRight = computed(() => {
  47. return props.controls && props.controlsPosition === "right";
  48. });
  49. const inputNumberSize = useFormSize();
  50. const inputNumberDisabled = useFormDisabled();
  51. const displayValue = computed(() => {
  52. if (data.userInput !== null) {
  53. return data.userInput;
  54. }
  55. let currentValue = data.currentValue;
  56. if (isNil(currentValue))
  57. return "";
  58. if (isNumber(currentValue)) {
  59. if (Number.isNaN(currentValue))
  60. return "";
  61. if (!isUndefined(props.precision)) {
  62. currentValue = currentValue.toFixed(props.precision);
  63. }
  64. }
  65. return currentValue;
  66. });
  67. const toPrecision = (num, pre) => {
  68. if (isUndefined(pre))
  69. pre = numPrecision.value;
  70. if (pre === 0)
  71. return Math.round(num);
  72. let snum = String(num);
  73. const pointPos = snum.indexOf(".");
  74. if (pointPos === -1)
  75. return num;
  76. const nums = snum.replace(".", "").split("");
  77. const datum = nums[pointPos + pre];
  78. if (!datum)
  79. return num;
  80. const length = snum.length;
  81. if (snum.charAt(length - 1) === "5") {
  82. snum = `${snum.slice(0, Math.max(0, length - 1))}6`;
  83. }
  84. return Number.parseFloat(Number(snum).toFixed(pre));
  85. };
  86. const getPrecision = (value) => {
  87. if (isNil(value))
  88. return 0;
  89. const valueString = value.toString();
  90. const dotPosition = valueString.indexOf(".");
  91. let precision = 0;
  92. if (dotPosition !== -1) {
  93. precision = valueString.length - dotPosition - 1;
  94. }
  95. return precision;
  96. };
  97. const ensurePrecision = (val, coefficient = 1) => {
  98. if (!isNumber(val))
  99. return data.currentValue;
  100. if (val >= Number.MAX_SAFE_INTEGER && coefficient === 1) {
  101. return val;
  102. } else if (val <= Number.MIN_SAFE_INTEGER && coefficient === -1) {
  103. return val;
  104. }
  105. return toPrecision(val + props.step * coefficient);
  106. };
  107. const handleKeydown = (event) => {
  108. var _a;
  109. const e = event;
  110. if (props.disabledScientific && ["e", "E"].includes(e.key)) {
  111. e.preventDefault();
  112. return;
  113. }
  114. const keyHandlers = {
  115. [EVENT_CODE.up]: () => {
  116. e.preventDefault();
  117. increase();
  118. },
  119. [EVENT_CODE.down]: () => {
  120. e.preventDefault();
  121. decrease();
  122. }
  123. };
  124. (_a = keyHandlers[e.key]) == null ? void 0 : _a.call(keyHandlers);
  125. };
  126. const increase = () => {
  127. if (props.readonly || inputNumberDisabled.value || maxDisabled.value)
  128. return;
  129. const value = Number(displayValue.value) || 0;
  130. const newVal = ensurePrecision(value);
  131. setCurrentValue(newVal);
  132. emit(INPUT_EVENT, data.currentValue);
  133. setCurrentValueToModelValue();
  134. };
  135. const decrease = () => {
  136. if (props.readonly || inputNumberDisabled.value || minDisabled.value)
  137. return;
  138. const value = Number(displayValue.value) || 0;
  139. const newVal = ensurePrecision(value, -1);
  140. setCurrentValue(newVal);
  141. emit(INPUT_EVENT, data.currentValue);
  142. setCurrentValueToModelValue();
  143. };
  144. const verifyValue = (value, update) => {
  145. const { max, min, step, precision, stepStrictly, valueOnClear } = props;
  146. if (max < min) {
  147. throwError("InputNumber", "min should not be greater than max.");
  148. }
  149. let newVal = Number(value);
  150. if (isNil(value) || Number.isNaN(newVal)) {
  151. return null;
  152. }
  153. if (value === "") {
  154. if (valueOnClear === null) {
  155. return null;
  156. }
  157. newVal = isString(valueOnClear) ? { min, max }[valueOnClear] : valueOnClear;
  158. }
  159. if (stepStrictly) {
  160. newVal = toPrecision(Math.round(newVal / step) * step, precision);
  161. if (newVal !== value) {
  162. update && emit(UPDATE_MODEL_EVENT, newVal);
  163. }
  164. }
  165. if (!isUndefined(precision)) {
  166. newVal = toPrecision(newVal, precision);
  167. }
  168. if (newVal > max || newVal < min) {
  169. newVal = newVal > max ? max : min;
  170. update && emit(UPDATE_MODEL_EVENT, newVal);
  171. }
  172. return newVal;
  173. };
  174. const setCurrentValue = (value, emitChange = true) => {
  175. var _a;
  176. const oldVal = data.currentValue;
  177. const newVal = verifyValue(value);
  178. if (!emitChange) {
  179. emit(UPDATE_MODEL_EVENT, newVal);
  180. return;
  181. }
  182. if (oldVal === newVal && value)
  183. return;
  184. data.userInput = null;
  185. emit(UPDATE_MODEL_EVENT, newVal);
  186. if (oldVal !== newVal) {
  187. emit(CHANGE_EVENT, newVal, oldVal);
  188. }
  189. if (props.validateEvent) {
  190. (_a = formItem == null ? void 0 : formItem.validate) == null ? void 0 : _a.call(formItem, "change").catch((err) => debugWarn());
  191. }
  192. data.currentValue = newVal;
  193. };
  194. const handleInput = (value) => {
  195. data.userInput = value;
  196. const newVal = value === "" ? null : Number(value);
  197. emit(INPUT_EVENT, newVal);
  198. setCurrentValue(newVal, false);
  199. };
  200. const handleInputChange = (value) => {
  201. const newVal = value !== "" ? Number(value) : "";
  202. if (isNumber(newVal) && !Number.isNaN(newVal) || value === "") {
  203. setCurrentValue(newVal);
  204. }
  205. setCurrentValueToModelValue();
  206. data.userInput = null;
  207. };
  208. const focus = () => {
  209. var _a, _b;
  210. (_b = (_a = input.value) == null ? void 0 : _a.focus) == null ? void 0 : _b.call(_a);
  211. };
  212. const blur = () => {
  213. var _a, _b;
  214. (_b = (_a = input.value) == null ? void 0 : _a.blur) == null ? void 0 : _b.call(_a);
  215. };
  216. const handleFocus = (event) => {
  217. emit("focus", event);
  218. };
  219. const handleBlur = (event) => {
  220. var _a, _b;
  221. data.userInput = null;
  222. if (data.currentValue === null && ((_a = input.value) == null ? void 0 : _a.input)) {
  223. input.value.input.value = "";
  224. }
  225. emit("blur", event);
  226. if (props.validateEvent) {
  227. (_b = formItem == null ? void 0 : formItem.validate) == null ? void 0 : _b.call(formItem, "blur").catch((err) => debugWarn());
  228. }
  229. };
  230. const setCurrentValueToModelValue = () => {
  231. if (data.currentValue !== props.modelValue) {
  232. data.currentValue = props.modelValue;
  233. }
  234. };
  235. const handleWheel = (e) => {
  236. if (document.activeElement === e.target)
  237. e.preventDefault();
  238. };
  239. watch(() => props.modelValue, (value, oldValue) => {
  240. const newValue = verifyValue(value, true);
  241. if (data.userInput === null && newValue !== oldValue) {
  242. data.currentValue = newValue;
  243. }
  244. }, { immediate: true });
  245. watch(() => props.precision, () => {
  246. data.currentValue = verifyValue(props.modelValue);
  247. });
  248. onMounted(() => {
  249. var _a;
  250. const { min, max, modelValue } = props;
  251. const innerInput = (_a = input.value) == null ? void 0 : _a.input;
  252. innerInput.setAttribute("role", "spinbutton");
  253. if (Number.isFinite(max)) {
  254. innerInput.setAttribute("aria-valuemax", String(max));
  255. } else {
  256. innerInput.removeAttribute("aria-valuemax");
  257. }
  258. if (Number.isFinite(min)) {
  259. innerInput.setAttribute("aria-valuemin", String(min));
  260. } else {
  261. innerInput.removeAttribute("aria-valuemin");
  262. }
  263. innerInput.setAttribute("aria-valuenow", data.currentValue || data.currentValue === 0 ? String(data.currentValue) : "");
  264. innerInput.setAttribute("aria-disabled", String(inputNumberDisabled.value));
  265. if (!isNumber(modelValue) && modelValue != null) {
  266. let val = Number(modelValue);
  267. if (Number.isNaN(val)) {
  268. val = null;
  269. }
  270. emit(UPDATE_MODEL_EVENT, val);
  271. }
  272. innerInput.addEventListener("wheel", handleWheel, { passive: false });
  273. });
  274. onUpdated(() => {
  275. var _a, _b;
  276. const innerInput = (_a = input.value) == null ? void 0 : _a.input;
  277. innerInput == null ? void 0 : innerInput.setAttribute("aria-valuenow", `${(_b = data.currentValue) != null ? _b : ""}`);
  278. });
  279. expose({
  280. focus,
  281. blur
  282. });
  283. return (_ctx, _cache) => {
  284. return openBlock(), createElementBlock("div", {
  285. class: normalizeClass([
  286. unref(ns).b(),
  287. unref(ns).m(unref(inputNumberSize)),
  288. unref(ns).is("disabled", unref(inputNumberDisabled)),
  289. unref(ns).is("without-controls", !_ctx.controls),
  290. unref(ns).is("controls-right", unref(controlsAtRight)),
  291. unref(ns).is(_ctx.align, !!_ctx.align)
  292. ]),
  293. onDragstart: withModifiers(() => {
  294. }, ["prevent"])
  295. }, [
  296. _ctx.controls ? withDirectives((openBlock(), createElementBlock("span", {
  297. key: 0,
  298. role: "button",
  299. "aria-label": unref(t)("el.inputNumber.decrease"),
  300. class: normalizeClass([unref(ns).e("decrease"), unref(ns).is("disabled", unref(minDisabled))]),
  301. onKeydown: withKeys(decrease, ["enter"])
  302. }, [
  303. renderSlot(_ctx.$slots, "decrease-icon", {}, () => [
  304. createVNode(unref(ElIcon), null, {
  305. default: withCtx(() => [
  306. unref(controlsAtRight) ? (openBlock(), createBlock(unref(ArrowDown), { key: 0 })) : (openBlock(), createBlock(unref(Minus), { key: 1 }))
  307. ]),
  308. _: 1
  309. })
  310. ])
  311. ], 42, ["aria-label", "onKeydown"])), [
  312. [unref(vRepeatClick), decrease]
  313. ]) : createCommentVNode("v-if", true),
  314. _ctx.controls ? withDirectives((openBlock(), createElementBlock("span", {
  315. key: 1,
  316. role: "button",
  317. "aria-label": unref(t)("el.inputNumber.increase"),
  318. class: normalizeClass([unref(ns).e("increase"), unref(ns).is("disabled", unref(maxDisabled))]),
  319. onKeydown: withKeys(increase, ["enter"])
  320. }, [
  321. renderSlot(_ctx.$slots, "increase-icon", {}, () => [
  322. createVNode(unref(ElIcon), null, {
  323. default: withCtx(() => [
  324. unref(controlsAtRight) ? (openBlock(), createBlock(unref(ArrowUp), { key: 0 })) : (openBlock(), createBlock(unref(Plus), { key: 1 }))
  325. ]),
  326. _: 1
  327. })
  328. ])
  329. ], 42, ["aria-label", "onKeydown"])), [
  330. [unref(vRepeatClick), increase]
  331. ]) : createCommentVNode("v-if", true),
  332. createVNode(unref(ElInput), {
  333. id: _ctx.id,
  334. ref_key: "input",
  335. ref: input,
  336. type: "number",
  337. step: _ctx.step,
  338. "model-value": unref(displayValue),
  339. placeholder: _ctx.placeholder,
  340. readonly: _ctx.readonly,
  341. disabled: unref(inputNumberDisabled),
  342. size: unref(inputNumberSize),
  343. max: _ctx.max,
  344. min: _ctx.min,
  345. name: _ctx.name,
  346. "aria-label": _ctx.ariaLabel,
  347. "validate-event": false,
  348. inputmode: _ctx.inputmode,
  349. onKeydown: handleKeydown,
  350. onBlur: handleBlur,
  351. onFocus: handleFocus,
  352. onInput: handleInput,
  353. onChange: handleInputChange
  354. }, createSlots({
  355. _: 2
  356. }, [
  357. _ctx.$slots.prefix ? {
  358. name: "prefix",
  359. fn: withCtx(() => [
  360. renderSlot(_ctx.$slots, "prefix")
  361. ])
  362. } : void 0,
  363. _ctx.$slots.suffix ? {
  364. name: "suffix",
  365. fn: withCtx(() => [
  366. renderSlot(_ctx.$slots, "suffix")
  367. ])
  368. } : void 0
  369. ]), 1032, ["id", "step", "model-value", "placeholder", "readonly", "disabled", "size", "max", "min", "name", "aria-label", "inputmode"])
  370. ], 42, ["onDragstart"]);
  371. };
  372. }
  373. });
  374. var InputNumber = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "input-number.vue"]]);
  375. export { InputNumber as default };
  376. //# sourceMappingURL=input-number2.mjs.map