index-41ec7e28.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. import { X as getRootScrollTop, Y as setRootScrollTop, i as isObject, Z as isPromise, R as isFunction, c as createNamespace, n as numericProp, z as makeStringProp, m as makeNumericProp, e as extend, G as unknownProp, a as useParent, d as addUnit, _ as CUSTOM_FIELD_INJECTION_KEY, D as useEventListener, W as FORM_KEY, k as isDef, $ as toArray, j as formatNumber, s as clamp, p as preventDefault, I as Icon, r as resetScroll, w as withInstall } from "./index-487cde8c.js";
  2. import { c as cellSharedProps, C as Cell } from "./index-8540448e.js";
  3. import { g as getCurrentInstance, d as defineComponent, n as reactive, r as ref, a as computed, U as provide, w as watch, z as nextTick, o as onMounted, c as createVNode, m as mergeProps, s as createTextVNode } from "./index-5e4623ce.js";
  4. import { u as useExpose } from "./use-scope-id-0b5b8615.js";
  5. let current = 0;
  6. function useId() {
  7. const vm = getCurrentInstance();
  8. const { name: name2 = "unknown" } = (vm == null ? void 0 : vm.type) || {};
  9. return `${name2}-${++current}`;
  10. }
  11. function isEmptyValue(value) {
  12. if (Array.isArray(value)) {
  13. return !value.length;
  14. }
  15. if (value === 0) {
  16. return false;
  17. }
  18. return !value;
  19. }
  20. function runSyncRule(value, rule) {
  21. if (isEmptyValue(value)) {
  22. if (rule.required) {
  23. return false;
  24. }
  25. if (rule.validateEmpty === false) {
  26. return true;
  27. }
  28. }
  29. if (rule.pattern && !rule.pattern.test(String(value))) {
  30. return false;
  31. }
  32. return true;
  33. }
  34. function runRuleValidator(value, rule) {
  35. return new Promise((resolve) => {
  36. const returnVal = rule.validator(value, rule);
  37. if (isPromise(returnVal)) {
  38. returnVal.then(resolve);
  39. return;
  40. }
  41. resolve(returnVal);
  42. });
  43. }
  44. function getRuleMessage(value, rule) {
  45. const { message } = rule;
  46. if (isFunction(message)) {
  47. return message(value, rule);
  48. }
  49. return message || "";
  50. }
  51. function startComposing({ target }) {
  52. target.composing = true;
  53. }
  54. function endComposing({ target }) {
  55. if (target.composing) {
  56. target.composing = false;
  57. target.dispatchEvent(new Event("input"));
  58. }
  59. }
  60. function resizeTextarea(input, autosize) {
  61. const scrollTop = getRootScrollTop();
  62. input.style.height = "auto";
  63. let height = input.scrollHeight;
  64. if (isObject(autosize)) {
  65. const { maxHeight, minHeight } = autosize;
  66. if (maxHeight !== void 0) {
  67. height = Math.min(height, maxHeight);
  68. }
  69. if (minHeight !== void 0) {
  70. height = Math.max(height, minHeight);
  71. }
  72. }
  73. if (height) {
  74. input.style.height = `${height}px`;
  75. setRootScrollTop(scrollTop);
  76. }
  77. }
  78. function mapInputType(type) {
  79. if (type === "number") {
  80. return {
  81. type: "text",
  82. inputmode: "decimal"
  83. };
  84. }
  85. if (type === "digit") {
  86. return {
  87. type: "tel",
  88. inputmode: "numeric"
  89. };
  90. }
  91. return { type };
  92. }
  93. function getStringLength(str) {
  94. return [...str].length;
  95. }
  96. function cutString(str, maxlength) {
  97. return [...str].slice(0, maxlength).join("");
  98. }
  99. const [name, bem] = createNamespace("field");
  100. const fieldSharedProps = {
  101. id: String,
  102. name: String,
  103. leftIcon: String,
  104. rightIcon: String,
  105. autofocus: Boolean,
  106. clearable: Boolean,
  107. maxlength: numericProp,
  108. max: Number,
  109. min: Number,
  110. formatter: Function,
  111. clearIcon: makeStringProp("clear"),
  112. modelValue: makeNumericProp(""),
  113. inputAlign: String,
  114. placeholder: String,
  115. autocomplete: String,
  116. autocapitalize: String,
  117. autocorrect: String,
  118. errorMessage: String,
  119. enterkeyhint: String,
  120. clearTrigger: makeStringProp("focus"),
  121. formatTrigger: makeStringProp("onChange"),
  122. spellcheck: {
  123. type: Boolean,
  124. default: null
  125. },
  126. error: {
  127. type: Boolean,
  128. default: null
  129. },
  130. disabled: {
  131. type: Boolean,
  132. default: null
  133. },
  134. readonly: {
  135. type: Boolean,
  136. default: null
  137. }
  138. };
  139. const fieldProps = extend({}, cellSharedProps, fieldSharedProps, {
  140. rows: numericProp,
  141. type: makeStringProp("text"),
  142. rules: Array,
  143. autosize: [Boolean, Object],
  144. labelWidth: numericProp,
  145. labelClass: unknownProp,
  146. labelAlign: String,
  147. showWordLimit: Boolean,
  148. errorMessageAlign: String,
  149. colon: {
  150. type: Boolean,
  151. default: null
  152. }
  153. });
  154. var stdin_default = defineComponent({
  155. name,
  156. props: fieldProps,
  157. emits: ["blur", "focus", "clear", "keypress", "clickInput", "endValidate", "startValidate", "clickLeftIcon", "clickRightIcon", "update:modelValue"],
  158. setup(props, {
  159. emit,
  160. slots
  161. }) {
  162. const id = useId();
  163. const state = reactive({
  164. status: "unvalidated",
  165. focused: false,
  166. validateMessage: ""
  167. });
  168. const inputRef = ref();
  169. const clearIconRef = ref();
  170. const customValue = ref();
  171. const {
  172. parent: form
  173. } = useParent(FORM_KEY);
  174. const getModelValue = () => {
  175. var _a;
  176. return String((_a = props.modelValue) != null ? _a : "");
  177. };
  178. const getProp = (key) => {
  179. if (isDef(props[key])) {
  180. return props[key];
  181. }
  182. if (form && isDef(form.props[key])) {
  183. return form.props[key];
  184. }
  185. };
  186. const showClear = computed(() => {
  187. const readonly = getProp("readonly");
  188. if (props.clearable && !readonly) {
  189. const hasValue = getModelValue() !== "";
  190. const trigger = props.clearTrigger === "always" || props.clearTrigger === "focus" && state.focused;
  191. return hasValue && trigger;
  192. }
  193. return false;
  194. });
  195. const formValue = computed(() => {
  196. if (customValue.value && slots.input) {
  197. return customValue.value();
  198. }
  199. return props.modelValue;
  200. });
  201. const showRequiredMark = computed(() => {
  202. var _a;
  203. const required = getProp("required");
  204. if (required === "auto") {
  205. return (_a = props.rules) == null ? void 0 : _a.some((rule) => rule.required);
  206. }
  207. return required;
  208. });
  209. const runRules = (rules) => rules.reduce((promise, rule) => promise.then(() => {
  210. if (state.status === "failed") {
  211. return;
  212. }
  213. let {
  214. value
  215. } = formValue;
  216. if (rule.formatter) {
  217. value = rule.formatter(value, rule);
  218. }
  219. if (!runSyncRule(value, rule)) {
  220. state.status = "failed";
  221. state.validateMessage = getRuleMessage(value, rule);
  222. return;
  223. }
  224. if (rule.validator) {
  225. if (isEmptyValue(value) && rule.validateEmpty === false) {
  226. return;
  227. }
  228. return runRuleValidator(value, rule).then((result) => {
  229. if (result && typeof result === "string") {
  230. state.status = "failed";
  231. state.validateMessage = result;
  232. } else if (result === false) {
  233. state.status = "failed";
  234. state.validateMessage = getRuleMessage(value, rule);
  235. }
  236. });
  237. }
  238. }), Promise.resolve());
  239. const resetValidation = () => {
  240. state.status = "unvalidated";
  241. state.validateMessage = "";
  242. };
  243. const endValidate = () => emit("endValidate", {
  244. status: state.status,
  245. message: state.validateMessage
  246. });
  247. const validate = (rules = props.rules) => new Promise((resolve) => {
  248. resetValidation();
  249. if (rules) {
  250. emit("startValidate");
  251. runRules(rules).then(() => {
  252. if (state.status === "failed") {
  253. resolve({
  254. name: props.name,
  255. message: state.validateMessage
  256. });
  257. endValidate();
  258. } else {
  259. state.status = "passed";
  260. resolve();
  261. endValidate();
  262. }
  263. });
  264. } else {
  265. resolve();
  266. }
  267. });
  268. const validateWithTrigger = (trigger) => {
  269. if (form && props.rules) {
  270. const {
  271. validateTrigger
  272. } = form.props;
  273. const defaultTrigger = toArray(validateTrigger).includes(trigger);
  274. const rules = props.rules.filter((rule) => {
  275. if (rule.trigger) {
  276. return toArray(rule.trigger).includes(trigger);
  277. }
  278. return defaultTrigger;
  279. });
  280. if (rules.length) {
  281. validate(rules);
  282. }
  283. }
  284. };
  285. const limitValueLength = (value) => {
  286. var _a;
  287. const {
  288. maxlength
  289. } = props;
  290. if (isDef(maxlength) && getStringLength(value) > +maxlength) {
  291. const modelValue = getModelValue();
  292. if (modelValue && getStringLength(modelValue) === +maxlength) {
  293. return modelValue;
  294. }
  295. const selectionEnd = (_a = inputRef.value) == null ? void 0 : _a.selectionEnd;
  296. if (state.focused && selectionEnd) {
  297. const valueArr = [...value];
  298. const exceededLength = valueArr.length - +maxlength;
  299. valueArr.splice(selectionEnd - exceededLength, exceededLength);
  300. return valueArr.join("");
  301. }
  302. return cutString(value, +maxlength);
  303. }
  304. return value;
  305. };
  306. const updateValue = (value, trigger = "onChange") => {
  307. var _a, _b;
  308. const originalValue = value;
  309. value = limitValueLength(value);
  310. const limitDiffLen = getStringLength(originalValue) - getStringLength(value);
  311. if (props.type === "number" || props.type === "digit") {
  312. const isNumber = props.type === "number";
  313. value = formatNumber(value, isNumber, isNumber);
  314. if (trigger === "onBlur" && value !== "") {
  315. const adjustedValue = clamp(+value, (_a = props.min) != null ? _a : -Infinity, (_b = props.max) != null ? _b : Infinity);
  316. value = adjustedValue.toString();
  317. }
  318. }
  319. let formatterDiffLen = 0;
  320. if (props.formatter && trigger === props.formatTrigger) {
  321. const {
  322. formatter,
  323. maxlength
  324. } = props;
  325. value = formatter(value);
  326. if (isDef(maxlength) && getStringLength(value) > +maxlength) {
  327. value = cutString(value, +maxlength);
  328. }
  329. if (inputRef.value && state.focused) {
  330. const {
  331. selectionEnd
  332. } = inputRef.value;
  333. const bcoVal = cutString(originalValue, selectionEnd);
  334. formatterDiffLen = getStringLength(formatter(bcoVal)) - getStringLength(bcoVal);
  335. }
  336. }
  337. if (inputRef.value && inputRef.value.value !== value) {
  338. if (state.focused) {
  339. let {
  340. selectionStart,
  341. selectionEnd
  342. } = inputRef.value;
  343. inputRef.value.value = value;
  344. if (isDef(selectionStart) && isDef(selectionEnd)) {
  345. const valueLen = getStringLength(value);
  346. if (limitDiffLen) {
  347. selectionStart -= limitDiffLen;
  348. selectionEnd -= limitDiffLen;
  349. } else if (formatterDiffLen) {
  350. selectionStart += formatterDiffLen;
  351. selectionEnd += formatterDiffLen;
  352. }
  353. inputRef.value.setSelectionRange(Math.min(selectionStart, valueLen), Math.min(selectionEnd, valueLen));
  354. }
  355. } else {
  356. inputRef.value.value = value;
  357. }
  358. }
  359. if (value !== props.modelValue) {
  360. emit("update:modelValue", value);
  361. }
  362. };
  363. const onInput = (event) => {
  364. if (!event.target.composing) {
  365. updateValue(event.target.value);
  366. }
  367. };
  368. const blur = () => {
  369. var _a;
  370. return (_a = inputRef.value) == null ? void 0 : _a.blur();
  371. };
  372. const focus = () => {
  373. var _a;
  374. return (_a = inputRef.value) == null ? void 0 : _a.focus();
  375. };
  376. const adjustTextareaSize = () => {
  377. const input = inputRef.value;
  378. if (props.type === "textarea" && props.autosize && input) {
  379. resizeTextarea(input, props.autosize);
  380. }
  381. };
  382. const onFocus = (event) => {
  383. state.focused = true;
  384. emit("focus", event);
  385. nextTick(adjustTextareaSize);
  386. if (getProp("readonly")) {
  387. blur();
  388. }
  389. };
  390. const onBlur = (event) => {
  391. state.focused = false;
  392. updateValue(getModelValue(), "onBlur");
  393. emit("blur", event);
  394. if (getProp("readonly")) {
  395. return;
  396. }
  397. validateWithTrigger("onBlur");
  398. nextTick(adjustTextareaSize);
  399. resetScroll();
  400. };
  401. const onClickInput = (event) => emit("clickInput", event);
  402. const onClickLeftIcon = (event) => emit("clickLeftIcon", event);
  403. const onClickRightIcon = (event) => emit("clickRightIcon", event);
  404. const onClear = (event) => {
  405. preventDefault(event);
  406. emit("update:modelValue", "");
  407. emit("clear", event);
  408. };
  409. const showError = computed(() => {
  410. if (typeof props.error === "boolean") {
  411. return props.error;
  412. }
  413. if (form && form.props.showError && state.status === "failed") {
  414. return true;
  415. }
  416. });
  417. const labelStyle = computed(() => {
  418. const labelWidth = getProp("labelWidth");
  419. const labelAlign = getProp("labelAlign");
  420. if (labelWidth && labelAlign !== "top") {
  421. return {
  422. width: addUnit(labelWidth)
  423. };
  424. }
  425. });
  426. const onKeypress = (event) => {
  427. const ENTER_CODE = 13;
  428. if (event.keyCode === ENTER_CODE) {
  429. const submitOnEnter = form && form.props.submitOnEnter;
  430. if (!submitOnEnter && props.type !== "textarea") {
  431. preventDefault(event);
  432. }
  433. if (props.type === "search") {
  434. blur();
  435. }
  436. }
  437. emit("keypress", event);
  438. };
  439. const getInputId = () => props.id || `${id}-input`;
  440. const getValidationStatus = () => state.status;
  441. const renderInput = () => {
  442. const controlClass = bem("control", [getProp("inputAlign"), {
  443. error: showError.value,
  444. custom: !!slots.input,
  445. "min-height": props.type === "textarea" && !props.autosize
  446. }]);
  447. if (slots.input) {
  448. return createVNode("div", {
  449. "class": controlClass,
  450. "onClick": onClickInput
  451. }, [slots.input()]);
  452. }
  453. const inputAttrs = {
  454. id: getInputId(),
  455. ref: inputRef,
  456. name: props.name,
  457. rows: props.rows !== void 0 ? +props.rows : void 0,
  458. class: controlClass,
  459. disabled: getProp("disabled"),
  460. readonly: getProp("readonly"),
  461. autofocus: props.autofocus,
  462. placeholder: props.placeholder,
  463. autocomplete: props.autocomplete,
  464. autocapitalize: props.autocapitalize,
  465. autocorrect: props.autocorrect,
  466. enterkeyhint: props.enterkeyhint,
  467. spellcheck: props.spellcheck,
  468. "aria-labelledby": props.label ? `${id}-label` : void 0,
  469. onBlur,
  470. onFocus,
  471. onInput,
  472. onClick: onClickInput,
  473. onChange: endComposing,
  474. onKeypress,
  475. onCompositionend: endComposing,
  476. onCompositionstart: startComposing
  477. };
  478. if (props.type === "textarea") {
  479. return createVNode("textarea", inputAttrs, null);
  480. }
  481. return createVNode("input", mergeProps(mapInputType(props.type), inputAttrs), null);
  482. };
  483. const renderLeftIcon = () => {
  484. const leftIconSlot = slots["left-icon"];
  485. if (props.leftIcon || leftIconSlot) {
  486. return createVNode("div", {
  487. "class": bem("left-icon"),
  488. "onClick": onClickLeftIcon
  489. }, [leftIconSlot ? leftIconSlot() : createVNode(Icon, {
  490. "name": props.leftIcon,
  491. "classPrefix": props.iconPrefix
  492. }, null)]);
  493. }
  494. };
  495. const renderRightIcon = () => {
  496. const rightIconSlot = slots["right-icon"];
  497. if (props.rightIcon || rightIconSlot) {
  498. return createVNode("div", {
  499. "class": bem("right-icon"),
  500. "onClick": onClickRightIcon
  501. }, [rightIconSlot ? rightIconSlot() : createVNode(Icon, {
  502. "name": props.rightIcon,
  503. "classPrefix": props.iconPrefix
  504. }, null)]);
  505. }
  506. };
  507. const renderWordLimit = () => {
  508. if (props.showWordLimit && props.maxlength) {
  509. const count = getStringLength(getModelValue());
  510. return createVNode("div", {
  511. "class": bem("word-limit")
  512. }, [createVNode("span", {
  513. "class": bem("word-num")
  514. }, [count]), createTextVNode("/"), props.maxlength]);
  515. }
  516. };
  517. const renderMessage = () => {
  518. if (form && form.props.showErrorMessage === false) {
  519. return;
  520. }
  521. const message = props.errorMessage || state.validateMessage;
  522. if (message) {
  523. const slot = slots["error-message"];
  524. const errorMessageAlign = getProp("errorMessageAlign");
  525. return createVNode("div", {
  526. "class": bem("error-message", errorMessageAlign)
  527. }, [slot ? slot({
  528. message
  529. }) : message]);
  530. }
  531. };
  532. const renderLabel = () => {
  533. const labelWidth = getProp("labelWidth");
  534. const labelAlign = getProp("labelAlign");
  535. const colon = getProp("colon") ? ":" : "";
  536. if (slots.label) {
  537. return [slots.label(), colon];
  538. }
  539. if (props.label) {
  540. return createVNode("label", {
  541. "id": `${id}-label`,
  542. "for": slots.input ? void 0 : getInputId(),
  543. "onClick": (event) => {
  544. preventDefault(event);
  545. focus();
  546. },
  547. "style": labelAlign === "top" && labelWidth ? {
  548. width: addUnit(labelWidth)
  549. } : void 0
  550. }, [props.label + colon]);
  551. }
  552. };
  553. const renderFieldBody = () => [createVNode("div", {
  554. "class": bem("body")
  555. }, [renderInput(), showClear.value && createVNode(Icon, {
  556. "ref": clearIconRef,
  557. "name": props.clearIcon,
  558. "class": bem("clear")
  559. }, null), renderRightIcon(), slots.button && createVNode("div", {
  560. "class": bem("button")
  561. }, [slots.button()])]), renderWordLimit(), renderMessage()];
  562. useExpose({
  563. blur,
  564. focus,
  565. validate,
  566. formValue,
  567. resetValidation,
  568. getValidationStatus
  569. });
  570. provide(CUSTOM_FIELD_INJECTION_KEY, {
  571. customValue,
  572. resetValidation,
  573. validateWithTrigger
  574. });
  575. watch(() => props.modelValue, () => {
  576. updateValue(getModelValue());
  577. resetValidation();
  578. validateWithTrigger("onChange");
  579. nextTick(adjustTextareaSize);
  580. });
  581. onMounted(() => {
  582. updateValue(getModelValue(), props.formatTrigger);
  583. nextTick(adjustTextareaSize);
  584. });
  585. useEventListener("touchstart", onClear, {
  586. target: computed(() => {
  587. var _a;
  588. return (_a = clearIconRef.value) == null ? void 0 : _a.$el;
  589. })
  590. });
  591. return () => {
  592. const disabled = getProp("disabled");
  593. const labelAlign = getProp("labelAlign");
  594. const LeftIcon = renderLeftIcon();
  595. const renderTitle = () => {
  596. const Label = renderLabel();
  597. if (labelAlign === "top") {
  598. return [LeftIcon, Label].filter(Boolean);
  599. }
  600. return Label || [];
  601. };
  602. return createVNode(Cell, {
  603. "size": props.size,
  604. "class": bem({
  605. error: showError.value,
  606. disabled,
  607. [`label-${labelAlign}`]: labelAlign
  608. }),
  609. "center": props.center,
  610. "border": props.border,
  611. "isLink": props.isLink,
  612. "clickable": props.clickable,
  613. "titleStyle": labelStyle.value,
  614. "valueClass": bem("value"),
  615. "titleClass": [bem("label", [labelAlign, {
  616. required: showRequiredMark.value
  617. }]), props.labelClass],
  618. "arrowDirection": props.arrowDirection
  619. }, {
  620. icon: LeftIcon && labelAlign !== "top" ? () => LeftIcon : null,
  621. title: renderTitle,
  622. value: renderFieldBody,
  623. extra: slots.extra
  624. });
  625. };
  626. }
  627. });
  628. const Field = withInstall(stdin_default);
  629. const index = "";
  630. export {
  631. Field as F,
  632. useId as u
  633. };