input2.mjs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import { defineComponent, useAttrs, useSlots, computed, shallowRef, ref, nextTick, watch, onMounted, toRef, withDirectives, openBlock, createElementBlock, mergeProps, unref, createCommentVNode, Fragment, normalizeClass, renderSlot, createElementVNode, createBlock, withCtx, resolveDynamicComponent, withModifiers, createVNode, toDisplayString, normalizeStyle, vShow } from 'vue';
  2. import { useResizeObserver, isClient } from '@vueuse/core';
  3. import { isNil } from 'lodash-unified';
  4. import { ElIcon } from '../../icon/index.mjs';
  5. import { View, Hide, CircleClose } from '@element-plus/icons-vue';
  6. import '../../../utils/index.mjs';
  7. import '../../../hooks/index.mjs';
  8. import '../../../constants/index.mjs';
  9. import { calcTextareaHeight } from './utils.mjs';
  10. import { inputProps, inputEmits } from './input.mjs';
  11. import _export_sfc from '../../../_virtual/plugin-vue_export-helper.mjs';
  12. import { useAttrs as useAttrs$1 } from '../../../hooks/use-attrs/index.mjs';
  13. import { useFormItem, useFormItemInputId } from '../../../hooks/use-form-item/index.mjs';
  14. import { useSize, useDisabled } from '../../../hooks/use-common-props/index.mjs';
  15. import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
  16. import { ValidateComponentsMap } from '../../../utils/vue/icon.mjs';
  17. import { useCursor } from '../../../hooks/use-cursor/index.mjs';
  18. import { isObject, NOOP } from '@vue/shared';
  19. import { UPDATE_MODEL_EVENT } from '../../../constants/event.mjs';
  20. import { isKorean } from '../../../utils/i18n.mjs';
  21. import { debugWarn } from '../../../utils/error.mjs';
  22. const _hoisted_1 = ["role"];
  23. const _hoisted_2 = ["id", "type", "disabled", "formatter", "parser", "readonly", "autocomplete", "tabindex", "aria-label", "placeholder", "form"];
  24. const _hoisted_3 = ["id", "tabindex", "disabled", "readonly", "autocomplete", "aria-label", "placeholder", "form"];
  25. const __default__ = defineComponent({
  26. name: "ElInput",
  27. inheritAttrs: false
  28. });
  29. const _sfc_main = /* @__PURE__ */ defineComponent({
  30. ...__default__,
  31. props: inputProps,
  32. emits: inputEmits,
  33. setup(__props, { expose, emit }) {
  34. const props = __props;
  35. const rawAttrs = useAttrs();
  36. const slots = useSlots();
  37. const containerAttrs = computed(() => {
  38. const comboBoxAttrs = {};
  39. if (props.containerRole === "combobox") {
  40. comboBoxAttrs["aria-haspopup"] = rawAttrs["aria-haspopup"];
  41. comboBoxAttrs["aria-owns"] = rawAttrs["aria-owns"];
  42. comboBoxAttrs["aria-expanded"] = rawAttrs["aria-expanded"];
  43. }
  44. return comboBoxAttrs;
  45. });
  46. const containerKls = computed(() => [
  47. props.type === "textarea" ? nsTextarea.b() : nsInput.b(),
  48. nsInput.m(inputSize.value),
  49. nsInput.is("disabled", inputDisabled.value),
  50. nsInput.is("exceed", inputExceed.value),
  51. {
  52. [nsInput.b("group")]: slots.prepend || slots.append,
  53. [nsInput.bm("group", "append")]: slots.append,
  54. [nsInput.bm("group", "prepend")]: slots.prepend,
  55. [nsInput.m("prefix")]: slots.prefix || props.prefixIcon,
  56. [nsInput.m("suffix")]: slots.suffix || props.suffixIcon || props.clearable || props.showPassword,
  57. [nsInput.bm("suffix", "password-clear")]: showClear.value && showPwdVisible.value
  58. },
  59. rawAttrs.class
  60. ]);
  61. const wrapperKls = computed(() => [
  62. nsInput.e("wrapper"),
  63. nsInput.is("focus", focused.value)
  64. ]);
  65. const attrs = useAttrs$1({
  66. excludeKeys: computed(() => {
  67. return Object.keys(containerAttrs.value);
  68. })
  69. });
  70. const { form, formItem } = useFormItem();
  71. const { inputId } = useFormItemInputId(props, {
  72. formItemContext: formItem
  73. });
  74. const inputSize = useSize();
  75. const inputDisabled = useDisabled();
  76. const nsInput = useNamespace("input");
  77. const nsTextarea = useNamespace("textarea");
  78. const input = shallowRef();
  79. const textarea = shallowRef();
  80. const focused = ref(false);
  81. const hovering = ref(false);
  82. const isComposing = ref(false);
  83. const passwordVisible = ref(false);
  84. const countStyle = ref();
  85. const textareaCalcStyle = shallowRef(props.inputStyle);
  86. const _ref = computed(() => input.value || textarea.value);
  87. const needStatusIcon = computed(() => {
  88. var _a;
  89. return (_a = form == null ? void 0 : form.statusIcon) != null ? _a : false;
  90. });
  91. const validateState = computed(() => (formItem == null ? void 0 : formItem.validateState) || "");
  92. const validateIcon = computed(() => validateState.value && ValidateComponentsMap[validateState.value]);
  93. const passwordIcon = computed(() => passwordVisible.value ? View : Hide);
  94. const containerStyle = computed(() => [
  95. rawAttrs.style,
  96. props.inputStyle
  97. ]);
  98. const textareaStyle = computed(() => [
  99. props.inputStyle,
  100. textareaCalcStyle.value,
  101. { resize: props.resize }
  102. ]);
  103. const nativeInputValue = computed(() => isNil(props.modelValue) ? "" : String(props.modelValue));
  104. const showClear = computed(() => props.clearable && !inputDisabled.value && !props.readonly && !!nativeInputValue.value && (focused.value || hovering.value));
  105. const showPwdVisible = computed(() => props.showPassword && !inputDisabled.value && !props.readonly && !!nativeInputValue.value && (!!nativeInputValue.value || focused.value));
  106. const isWordLimitVisible = computed(() => props.showWordLimit && !!attrs.value.maxlength && (props.type === "text" || props.type === "textarea") && !inputDisabled.value && !props.readonly && !props.showPassword);
  107. const textLength = computed(() => Array.from(nativeInputValue.value).length);
  108. const inputExceed = computed(() => !!isWordLimitVisible.value && textLength.value > Number(attrs.value.maxlength));
  109. const suffixVisible = computed(() => !!slots.suffix || !!props.suffixIcon || showClear.value || props.showPassword || isWordLimitVisible.value || !!validateState.value && needStatusIcon.value);
  110. const [recordCursor, setCursor] = useCursor(input);
  111. useResizeObserver(textarea, (entries) => {
  112. if (!isWordLimitVisible.value || props.resize !== "both")
  113. return;
  114. const entry = entries[0];
  115. const { width } = entry.contentRect;
  116. countStyle.value = {
  117. right: `calc(100% - ${width + 15 + 6}px)`
  118. };
  119. });
  120. const resizeTextarea = () => {
  121. const { type, autosize } = props;
  122. if (!isClient || type !== "textarea")
  123. return;
  124. if (autosize) {
  125. const minRows = isObject(autosize) ? autosize.minRows : void 0;
  126. const maxRows = isObject(autosize) ? autosize.maxRows : void 0;
  127. textareaCalcStyle.value = {
  128. ...calcTextareaHeight(textarea.value, minRows, maxRows)
  129. };
  130. } else {
  131. textareaCalcStyle.value = {
  132. minHeight: calcTextareaHeight(textarea.value).minHeight
  133. };
  134. }
  135. };
  136. const setNativeInputValue = () => {
  137. const input2 = _ref.value;
  138. if (!input2 || input2.value === nativeInputValue.value)
  139. return;
  140. input2.value = nativeInputValue.value;
  141. };
  142. const handleInput = async (event) => {
  143. recordCursor();
  144. let { value } = event.target;
  145. if (props.formatter) {
  146. value = props.parser ? props.parser(value) : value;
  147. value = props.formatter(value);
  148. }
  149. if (isComposing.value)
  150. return;
  151. if (value === nativeInputValue.value) {
  152. setNativeInputValue();
  153. return;
  154. }
  155. emit(UPDATE_MODEL_EVENT, value);
  156. emit("input", value);
  157. await nextTick();
  158. setNativeInputValue();
  159. setCursor();
  160. };
  161. const handleChange = (event) => {
  162. emit("change", event.target.value);
  163. };
  164. const handleCompositionStart = (event) => {
  165. emit("compositionstart", event);
  166. isComposing.value = true;
  167. };
  168. const handleCompositionUpdate = (event) => {
  169. var _a;
  170. emit("compositionupdate", event);
  171. const text = (_a = event.target) == null ? void 0 : _a.value;
  172. const lastCharacter = text[text.length - 1] || "";
  173. isComposing.value = !isKorean(lastCharacter);
  174. };
  175. const handleCompositionEnd = (event) => {
  176. emit("compositionend", event);
  177. if (isComposing.value) {
  178. isComposing.value = false;
  179. handleInput(event);
  180. }
  181. };
  182. const handlePasswordVisible = () => {
  183. passwordVisible.value = !passwordVisible.value;
  184. focus();
  185. };
  186. const focus = async () => {
  187. var _a;
  188. await nextTick();
  189. (_a = _ref.value) == null ? void 0 : _a.focus();
  190. };
  191. const blur = () => {
  192. var _a;
  193. return (_a = _ref.value) == null ? void 0 : _a.blur();
  194. };
  195. const handleFocus = (event) => {
  196. focused.value = true;
  197. emit("focus", event);
  198. };
  199. const handleBlur = (event) => {
  200. var _a;
  201. focused.value = false;
  202. emit("blur", event);
  203. if (props.validateEvent) {
  204. (_a = formItem == null ? void 0 : formItem.validate) == null ? void 0 : _a.call(formItem, "blur").catch((err) => debugWarn(err));
  205. }
  206. };
  207. const handleMouseLeave = (evt) => {
  208. hovering.value = false;
  209. emit("mouseleave", evt);
  210. };
  211. const handleMouseEnter = (evt) => {
  212. hovering.value = true;
  213. emit("mouseenter", evt);
  214. };
  215. const handleKeydown = (evt) => {
  216. emit("keydown", evt);
  217. };
  218. const select = () => {
  219. var _a;
  220. (_a = _ref.value) == null ? void 0 : _a.select();
  221. };
  222. const clear = () => {
  223. emit(UPDATE_MODEL_EVENT, "");
  224. emit("change", "");
  225. emit("clear");
  226. emit("input", "");
  227. };
  228. watch(() => props.modelValue, () => {
  229. var _a;
  230. nextTick(() => resizeTextarea());
  231. if (props.validateEvent) {
  232. (_a = formItem == null ? void 0 : formItem.validate) == null ? void 0 : _a.call(formItem, "change").catch((err) => debugWarn(err));
  233. }
  234. });
  235. watch(nativeInputValue, () => setNativeInputValue());
  236. watch(() => props.type, async () => {
  237. await nextTick();
  238. setNativeInputValue();
  239. resizeTextarea();
  240. });
  241. onMounted(() => {
  242. if (!props.formatter && props.parser) {
  243. debugWarn("ElInput", "If you set the parser, you also need to set the formatter.");
  244. }
  245. setNativeInputValue();
  246. nextTick(resizeTextarea);
  247. });
  248. expose({
  249. input,
  250. textarea,
  251. ref: _ref,
  252. textareaStyle,
  253. autosize: toRef(props, "autosize"),
  254. focus,
  255. blur,
  256. select,
  257. clear,
  258. resizeTextarea
  259. });
  260. return (_ctx, _cache) => {
  261. return withDirectives((openBlock(), createElementBlock("div", mergeProps(unref(containerAttrs), {
  262. class: unref(containerKls),
  263. style: unref(containerStyle),
  264. role: _ctx.containerRole,
  265. onMouseenter: handleMouseEnter,
  266. onMouseleave: handleMouseLeave
  267. }), [
  268. createCommentVNode(" input "),
  269. _ctx.type !== "textarea" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
  270. createCommentVNode(" prepend slot "),
  271. _ctx.$slots.prepend ? (openBlock(), createElementBlock("div", {
  272. key: 0,
  273. class: normalizeClass(unref(nsInput).be("group", "prepend"))
  274. }, [
  275. renderSlot(_ctx.$slots, "prepend")
  276. ], 2)) : createCommentVNode("v-if", true),
  277. createElementVNode("div", {
  278. class: normalizeClass(unref(wrapperKls))
  279. }, [
  280. createCommentVNode(" prefix slot "),
  281. _ctx.$slots.prefix || _ctx.prefixIcon ? (openBlock(), createElementBlock("span", {
  282. key: 0,
  283. class: normalizeClass(unref(nsInput).e("prefix"))
  284. }, [
  285. createElementVNode("span", {
  286. class: normalizeClass(unref(nsInput).e("prefix-inner")),
  287. onClick: focus
  288. }, [
  289. renderSlot(_ctx.$slots, "prefix"),
  290. _ctx.prefixIcon ? (openBlock(), createBlock(unref(ElIcon), {
  291. key: 0,
  292. class: normalizeClass(unref(nsInput).e("icon"))
  293. }, {
  294. default: withCtx(() => [
  295. (openBlock(), createBlock(resolveDynamicComponent(_ctx.prefixIcon)))
  296. ]),
  297. _: 1
  298. }, 8, ["class"])) : createCommentVNode("v-if", true)
  299. ], 2)
  300. ], 2)) : createCommentVNode("v-if", true),
  301. createElementVNode("input", mergeProps({
  302. id: unref(inputId),
  303. ref_key: "input",
  304. ref: input,
  305. class: unref(nsInput).e("inner")
  306. }, unref(attrs), {
  307. type: _ctx.showPassword ? passwordVisible.value ? "text" : "password" : _ctx.type,
  308. disabled: unref(inputDisabled),
  309. formatter: _ctx.formatter,
  310. parser: _ctx.parser,
  311. readonly: _ctx.readonly,
  312. autocomplete: _ctx.autocomplete,
  313. tabindex: _ctx.tabindex,
  314. "aria-label": _ctx.label,
  315. placeholder: _ctx.placeholder,
  316. style: _ctx.inputStyle,
  317. form: props.form,
  318. onCompositionstart: handleCompositionStart,
  319. onCompositionupdate: handleCompositionUpdate,
  320. onCompositionend: handleCompositionEnd,
  321. onInput: handleInput,
  322. onFocus: handleFocus,
  323. onBlur: handleBlur,
  324. onChange: handleChange,
  325. onKeydown: handleKeydown
  326. }), null, 16, _hoisted_2),
  327. createCommentVNode(" suffix slot "),
  328. unref(suffixVisible) ? (openBlock(), createElementBlock("span", {
  329. key: 1,
  330. class: normalizeClass(unref(nsInput).e("suffix"))
  331. }, [
  332. createElementVNode("span", {
  333. class: normalizeClass(unref(nsInput).e("suffix-inner")),
  334. onClick: focus
  335. }, [
  336. !unref(showClear) || !unref(showPwdVisible) || !unref(isWordLimitVisible) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
  337. renderSlot(_ctx.$slots, "suffix"),
  338. _ctx.suffixIcon ? (openBlock(), createBlock(unref(ElIcon), {
  339. key: 0,
  340. class: normalizeClass(unref(nsInput).e("icon"))
  341. }, {
  342. default: withCtx(() => [
  343. (openBlock(), createBlock(resolveDynamicComponent(_ctx.suffixIcon)))
  344. ]),
  345. _: 1
  346. }, 8, ["class"])) : createCommentVNode("v-if", true)
  347. ], 64)) : createCommentVNode("v-if", true),
  348. unref(showClear) ? (openBlock(), createBlock(unref(ElIcon), {
  349. key: 1,
  350. class: normalizeClass([unref(nsInput).e("icon"), unref(nsInput).e("clear")]),
  351. onMousedown: withModifiers(unref(NOOP), ["prevent"]),
  352. onClick: clear
  353. }, {
  354. default: withCtx(() => [
  355. createVNode(unref(CircleClose))
  356. ]),
  357. _: 1
  358. }, 8, ["class", "onMousedown"])) : createCommentVNode("v-if", true),
  359. unref(showPwdVisible) ? (openBlock(), createBlock(unref(ElIcon), {
  360. key: 2,
  361. class: normalizeClass([unref(nsInput).e("icon"), unref(nsInput).e("password")]),
  362. onClick: handlePasswordVisible
  363. }, {
  364. default: withCtx(() => [
  365. (openBlock(), createBlock(resolveDynamicComponent(unref(passwordIcon))))
  366. ]),
  367. _: 1
  368. }, 8, ["class"])) : createCommentVNode("v-if", true),
  369. unref(isWordLimitVisible) ? (openBlock(), createElementBlock("span", {
  370. key: 3,
  371. class: normalizeClass(unref(nsInput).e("count"))
  372. }, [
  373. createElementVNode("span", {
  374. class: normalizeClass(unref(nsInput).e("count-inner"))
  375. }, toDisplayString(unref(textLength)) + " / " + toDisplayString(unref(attrs).maxlength), 3)
  376. ], 2)) : createCommentVNode("v-if", true),
  377. unref(validateState) && unref(validateIcon) && unref(needStatusIcon) ? (openBlock(), createBlock(unref(ElIcon), {
  378. key: 4,
  379. class: normalizeClass([
  380. unref(nsInput).e("icon"),
  381. unref(nsInput).e("validateIcon"),
  382. unref(nsInput).is("loading", unref(validateState) === "validating")
  383. ])
  384. }, {
  385. default: withCtx(() => [
  386. (openBlock(), createBlock(resolveDynamicComponent(unref(validateIcon))))
  387. ]),
  388. _: 1
  389. }, 8, ["class"])) : createCommentVNode("v-if", true)
  390. ], 2)
  391. ], 2)) : createCommentVNode("v-if", true)
  392. ], 2),
  393. createCommentVNode(" append slot "),
  394. _ctx.$slots.append ? (openBlock(), createElementBlock("div", {
  395. key: 1,
  396. class: normalizeClass(unref(nsInput).be("group", "append"))
  397. }, [
  398. renderSlot(_ctx.$slots, "append")
  399. ], 2)) : createCommentVNode("v-if", true)
  400. ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
  401. createCommentVNode(" textarea "),
  402. createElementVNode("textarea", mergeProps({
  403. id: unref(inputId),
  404. ref_key: "textarea",
  405. ref: textarea,
  406. class: unref(nsTextarea).e("inner")
  407. }, unref(attrs), {
  408. tabindex: _ctx.tabindex,
  409. disabled: unref(inputDisabled),
  410. readonly: _ctx.readonly,
  411. autocomplete: _ctx.autocomplete,
  412. style: unref(textareaStyle),
  413. "aria-label": _ctx.label,
  414. placeholder: _ctx.placeholder,
  415. form: props.form,
  416. onCompositionstart: handleCompositionStart,
  417. onCompositionupdate: handleCompositionUpdate,
  418. onCompositionend: handleCompositionEnd,
  419. onInput: handleInput,
  420. onFocus: handleFocus,
  421. onBlur: handleBlur,
  422. onChange: handleChange,
  423. onKeydown: handleKeydown
  424. }), null, 16, _hoisted_3),
  425. unref(isWordLimitVisible) ? (openBlock(), createElementBlock("span", {
  426. key: 0,
  427. style: normalizeStyle(countStyle.value),
  428. class: normalizeClass(unref(nsInput).e("count"))
  429. }, toDisplayString(unref(textLength)) + " / " + toDisplayString(unref(attrs).maxlength), 7)) : createCommentVNode("v-if", true)
  430. ], 64))
  431. ], 16, _hoisted_1)), [
  432. [vShow, _ctx.type !== "hidden"]
  433. ]);
  434. };
  435. }
  436. });
  437. var Input = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "/home/runner/work/element-plus/element-plus/packages/components/input/src/input.vue"]]);
  438. export { Input as default };
  439. //# sourceMappingURL=input2.mjs.map