index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. exports.__esModule = true;
  4. exports.default = void 0;
  5. var _babelHelperVueJsxMergeProps = _interopRequireDefault(require("@vue/babel-helper-vue-jsx-merge-props"));
  6. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  7. var _resetScroll = require("../utils/dom/reset-scroll");
  8. var _number = require("../utils/format/number");
  9. var _event = require("../utils/dom/event");
  10. var _scroll = require("../utils/dom/scroll");
  11. var _utils = require("../utils");
  12. var _icon = _interopRequireDefault(require("../icon"));
  13. var _cell = _interopRequireDefault(require("../cell"));
  14. var _shared = require("../cell/shared");
  15. // Utils
  16. // Components
  17. var _createNamespace = (0, _utils.createNamespace)('field'),
  18. createComponent = _createNamespace[0],
  19. bem = _createNamespace[1];
  20. var _default = createComponent({
  21. inheritAttrs: false,
  22. provide: function provide() {
  23. return {
  24. vanField: this
  25. };
  26. },
  27. inject: {
  28. vanForm: {
  29. default: null
  30. }
  31. },
  32. props: (0, _extends2.default)({}, _shared.cellProps, {
  33. name: String,
  34. rules: Array,
  35. disabled: {
  36. type: Boolean,
  37. default: null
  38. },
  39. readonly: {
  40. type: Boolean,
  41. default: null
  42. },
  43. autosize: [Boolean, Object],
  44. leftIcon: String,
  45. rightIcon: String,
  46. clearable: Boolean,
  47. formatter: Function,
  48. maxlength: [Number, String],
  49. labelWidth: [Number, String],
  50. labelClass: null,
  51. labelAlign: String,
  52. inputAlign: String,
  53. placeholder: String,
  54. errorMessage: String,
  55. errorMessageAlign: String,
  56. showWordLimit: Boolean,
  57. value: {
  58. type: [Number, String],
  59. default: ''
  60. },
  61. type: {
  62. type: String,
  63. default: 'text'
  64. },
  65. error: {
  66. type: Boolean,
  67. default: null
  68. },
  69. colon: {
  70. type: Boolean,
  71. default: null
  72. },
  73. clearTrigger: {
  74. type: String,
  75. default: 'focus'
  76. },
  77. formatTrigger: {
  78. type: String,
  79. default: 'onChange'
  80. }
  81. }),
  82. data: function data() {
  83. return {
  84. focused: false,
  85. validateFailed: false,
  86. validateMessage: ''
  87. };
  88. },
  89. watch: {
  90. value: function value() {
  91. this.updateValue(this.value);
  92. this.resetValidation();
  93. this.validateWithTrigger('onChange');
  94. this.$nextTick(this.adjustSize);
  95. }
  96. },
  97. mounted: function mounted() {
  98. this.updateValue(this.value, this.formatTrigger);
  99. this.$nextTick(this.adjustSize);
  100. if (this.vanForm) {
  101. this.vanForm.addField(this);
  102. }
  103. },
  104. beforeDestroy: function beforeDestroy() {
  105. if (this.vanForm) {
  106. this.vanForm.removeField(this);
  107. }
  108. },
  109. computed: {
  110. showClear: function showClear() {
  111. var readonly = this.getProp('readonly');
  112. if (this.clearable && !readonly) {
  113. var hasValue = (0, _utils.isDef)(this.value) && this.value !== '';
  114. var trigger = this.clearTrigger === 'always' || this.clearTrigger === 'focus' && this.focused;
  115. return hasValue && trigger;
  116. }
  117. },
  118. showError: function showError() {
  119. if (this.error !== null) {
  120. return this.error;
  121. }
  122. if (this.vanForm && this.vanForm.showError && this.validateFailed) {
  123. return true;
  124. }
  125. },
  126. listeners: function listeners() {
  127. return (0, _extends2.default)({}, this.$listeners, {
  128. blur: this.onBlur,
  129. focus: this.onFocus,
  130. input: this.onInput,
  131. click: this.onClickInput,
  132. keypress: this.onKeypress
  133. });
  134. },
  135. labelStyle: function labelStyle() {
  136. var labelWidth = this.getProp('labelWidth');
  137. if (labelWidth) {
  138. return {
  139. width: (0, _utils.addUnit)(labelWidth)
  140. };
  141. }
  142. },
  143. formValue: function formValue() {
  144. if (this.children && (this.$scopedSlots.input || this.$slots.input)) {
  145. return this.children.value;
  146. }
  147. return this.value;
  148. }
  149. },
  150. methods: {
  151. // @exposed-api
  152. focus: function focus() {
  153. if (this.$refs.input) {
  154. this.$refs.input.focus();
  155. }
  156. },
  157. // @exposed-api
  158. blur: function blur() {
  159. if (this.$refs.input) {
  160. this.$refs.input.blur();
  161. }
  162. },
  163. runValidator: function runValidator(value, rule) {
  164. return new Promise(function (resolve) {
  165. var returnVal = rule.validator(value, rule);
  166. if ((0, _utils.isPromise)(returnVal)) {
  167. return returnVal.then(resolve);
  168. }
  169. resolve(returnVal);
  170. });
  171. },
  172. isEmptyValue: function isEmptyValue(value) {
  173. if (Array.isArray(value)) {
  174. return !value.length;
  175. }
  176. if (value === 0) {
  177. return false;
  178. }
  179. return !value;
  180. },
  181. runSyncRule: function runSyncRule(value, rule) {
  182. if (rule.required && this.isEmptyValue(value)) {
  183. return false;
  184. }
  185. if (rule.pattern && !rule.pattern.test(value)) {
  186. return false;
  187. }
  188. return true;
  189. },
  190. getRuleMessage: function getRuleMessage(value, rule) {
  191. var message = rule.message;
  192. if ((0, _utils.isFunction)(message)) {
  193. return message(value, rule);
  194. }
  195. return message;
  196. },
  197. runRules: function runRules(rules) {
  198. var _this = this;
  199. return rules.reduce(function (promise, rule) {
  200. return promise.then(function () {
  201. if (_this.validateFailed) {
  202. return;
  203. }
  204. var value = _this.formValue;
  205. if (rule.formatter) {
  206. value = rule.formatter(value, rule);
  207. }
  208. if (!_this.runSyncRule(value, rule)) {
  209. _this.validateFailed = true;
  210. _this.validateMessage = _this.getRuleMessage(value, rule);
  211. return;
  212. }
  213. if (rule.validator) {
  214. return _this.runValidator(value, rule).then(function (result) {
  215. if (result === false) {
  216. _this.validateFailed = true;
  217. _this.validateMessage = _this.getRuleMessage(value, rule);
  218. }
  219. });
  220. }
  221. });
  222. }, Promise.resolve());
  223. },
  224. validate: function validate(rules) {
  225. var _this2 = this;
  226. if (rules === void 0) {
  227. rules = this.rules;
  228. }
  229. return new Promise(function (resolve) {
  230. if (!rules) {
  231. resolve();
  232. }
  233. _this2.resetValidation();
  234. _this2.runRules(rules).then(function () {
  235. if (_this2.validateFailed) {
  236. resolve({
  237. name: _this2.name,
  238. message: _this2.validateMessage
  239. });
  240. } else {
  241. resolve();
  242. }
  243. });
  244. });
  245. },
  246. validateWithTrigger: function validateWithTrigger(trigger) {
  247. if (this.vanForm && this.rules) {
  248. var defaultTrigger = this.vanForm.validateTrigger === trigger;
  249. var rules = this.rules.filter(function (rule) {
  250. if (rule.trigger) {
  251. return rule.trigger === trigger;
  252. }
  253. return defaultTrigger;
  254. });
  255. if (rules.length) {
  256. this.validate(rules);
  257. }
  258. }
  259. },
  260. resetValidation: function resetValidation() {
  261. if (this.validateFailed) {
  262. this.validateFailed = false;
  263. this.validateMessage = '';
  264. }
  265. },
  266. updateValue: function updateValue(value, trigger) {
  267. if (trigger === void 0) {
  268. trigger = 'onChange';
  269. }
  270. value = (0, _utils.isDef)(value) ? String(value) : ''; // native maxlength have incorrect line-break counting
  271. // see: https://github.com/vant-ui/vant/issues/5033
  272. var maxlength = this.maxlength;
  273. if ((0, _utils.isDef)(maxlength) && value.length > maxlength) {
  274. if (this.value && this.value.length === +maxlength) {
  275. value = this.value;
  276. } else {
  277. value = value.slice(0, maxlength);
  278. }
  279. }
  280. if (this.type === 'number' || this.type === 'digit') {
  281. var isNumber = this.type === 'number';
  282. value = (0, _number.formatNumber)(value, isNumber, isNumber);
  283. }
  284. if (this.formatter && trigger === this.formatTrigger) {
  285. value = this.formatter(value);
  286. }
  287. var input = this.$refs.input;
  288. if (input && value !== input.value) {
  289. input.value = value;
  290. }
  291. if (value !== this.value) {
  292. this.$emit('input', value);
  293. }
  294. },
  295. onInput: function onInput(event) {
  296. // not update v-model when composing
  297. if (event.target.composing) {
  298. return;
  299. }
  300. this.updateValue(event.target.value);
  301. },
  302. onFocus: function onFocus(event) {
  303. this.focused = true;
  304. this.$emit('focus', event); // https://github.com/vant-ui/vant/issues/9715
  305. this.$nextTick(this.adjustSize); // readonly not work in legacy mobile safari
  306. /* istanbul ignore if */
  307. if (this.getProp('readonly')) {
  308. this.blur();
  309. }
  310. },
  311. onBlur: function onBlur(event) {
  312. if (this.getProp('readonly')) {
  313. return;
  314. }
  315. this.focused = false;
  316. this.updateValue(this.value, 'onBlur');
  317. this.$emit('blur', event);
  318. this.validateWithTrigger('onBlur');
  319. this.$nextTick(this.adjustSize);
  320. (0, _resetScroll.resetScroll)();
  321. },
  322. onClick: function onClick(event) {
  323. this.$emit('click', event);
  324. },
  325. onClickInput: function onClickInput(event) {
  326. this.$emit('click-input', event);
  327. },
  328. onClickLeftIcon: function onClickLeftIcon(event) {
  329. this.$emit('click-left-icon', event);
  330. },
  331. onClickRightIcon: function onClickRightIcon(event) {
  332. this.$emit('click-right-icon', event);
  333. },
  334. onClear: function onClear(event) {
  335. (0, _event.preventDefault)(event);
  336. this.$emit('input', '');
  337. this.$emit('clear', event);
  338. },
  339. onKeypress: function onKeypress(event) {
  340. var ENTER_CODE = 13;
  341. if (event.keyCode === ENTER_CODE) {
  342. var submitOnEnter = this.getProp('submitOnEnter');
  343. if (!submitOnEnter && this.type !== 'textarea') {
  344. (0, _event.preventDefault)(event);
  345. } // trigger blur after click keyboard search button
  346. if (this.type === 'search') {
  347. this.blur();
  348. }
  349. }
  350. this.$emit('keypress', event);
  351. },
  352. adjustSize: function adjustSize() {
  353. var input = this.$refs.input;
  354. if (!(this.type === 'textarea' && this.autosize) || !input) {
  355. return;
  356. }
  357. var scrollTop = (0, _scroll.getRootScrollTop)();
  358. input.style.height = 'auto';
  359. var height = input.scrollHeight;
  360. if ((0, _utils.isObject)(this.autosize)) {
  361. var _this$autosize = this.autosize,
  362. maxHeight = _this$autosize.maxHeight,
  363. minHeight = _this$autosize.minHeight;
  364. if (maxHeight) {
  365. height = Math.min(height, maxHeight);
  366. }
  367. if (minHeight) {
  368. height = Math.max(height, minHeight);
  369. }
  370. }
  371. if (height) {
  372. input.style.height = height + 'px'; // https://github.com/vant-ui/vant/issues/9178
  373. (0, _scroll.setRootScrollTop)(scrollTop);
  374. }
  375. },
  376. genInput: function genInput() {
  377. var h = this.$createElement;
  378. var type = this.type;
  379. var disabled = this.getProp('disabled');
  380. var readonly = this.getProp('readonly');
  381. var inputSlot = this.slots('input');
  382. var inputAlign = this.getProp('inputAlign');
  383. if (inputSlot) {
  384. return h("div", {
  385. "class": bem('control', [inputAlign, 'custom']),
  386. "on": {
  387. "click": this.onClickInput
  388. }
  389. }, [inputSlot]);
  390. }
  391. var inputProps = {
  392. ref: 'input',
  393. class: bem('control', inputAlign),
  394. domProps: {
  395. value: this.value
  396. },
  397. attrs: (0, _extends2.default)({}, this.$attrs, {
  398. name: this.name,
  399. disabled: disabled,
  400. readonly: readonly,
  401. placeholder: this.placeholder
  402. }),
  403. on: this.listeners,
  404. // add model directive to skip IME composition
  405. directives: [{
  406. name: 'model',
  407. value: this.value
  408. }]
  409. };
  410. if (type === 'textarea') {
  411. return h("textarea", (0, _babelHelperVueJsxMergeProps.default)([{}, inputProps]));
  412. }
  413. var inputType = type;
  414. var inputMode; // type="number" is weird in iOS, and can't prevent dot in Android
  415. // so use inputmode to set keyboard in modern browsers
  416. if (type === 'number') {
  417. inputType = 'text';
  418. inputMode = 'decimal';
  419. }
  420. if (type === 'digit') {
  421. inputType = 'tel';
  422. inputMode = 'numeric';
  423. }
  424. return h("input", (0, _babelHelperVueJsxMergeProps.default)([{
  425. "attrs": {
  426. "type": inputType,
  427. "inputmode": inputMode
  428. }
  429. }, inputProps]));
  430. },
  431. genLeftIcon: function genLeftIcon() {
  432. var h = this.$createElement;
  433. var showLeftIcon = this.slots('left-icon') || this.leftIcon;
  434. if (showLeftIcon) {
  435. return h("div", {
  436. "class": bem('left-icon'),
  437. "on": {
  438. "click": this.onClickLeftIcon
  439. }
  440. }, [this.slots('left-icon') || h(_icon.default, {
  441. "attrs": {
  442. "name": this.leftIcon,
  443. "classPrefix": this.iconPrefix
  444. }
  445. })]);
  446. }
  447. },
  448. genRightIcon: function genRightIcon() {
  449. var h = this.$createElement;
  450. var slots = this.slots;
  451. var showRightIcon = slots('right-icon') || this.rightIcon;
  452. if (showRightIcon) {
  453. return h("div", {
  454. "class": bem('right-icon'),
  455. "on": {
  456. "click": this.onClickRightIcon
  457. }
  458. }, [slots('right-icon') || h(_icon.default, {
  459. "attrs": {
  460. "name": this.rightIcon,
  461. "classPrefix": this.iconPrefix
  462. }
  463. })]);
  464. }
  465. },
  466. genWordLimit: function genWordLimit() {
  467. var h = this.$createElement;
  468. if (this.showWordLimit && this.maxlength) {
  469. var count = (this.value || '').length;
  470. return h("div", {
  471. "class": bem('word-limit')
  472. }, [h("span", {
  473. "class": bem('word-num')
  474. }, [count]), "/", this.maxlength]);
  475. }
  476. },
  477. genMessage: function genMessage() {
  478. var h = this.$createElement;
  479. if (this.vanForm && this.vanForm.showErrorMessage === false) {
  480. return;
  481. }
  482. var message = this.errorMessage || this.validateMessage;
  483. if (message) {
  484. var errorMessageAlign = this.getProp('errorMessageAlign');
  485. return h("div", {
  486. "class": bem('error-message', errorMessageAlign)
  487. }, [message]);
  488. }
  489. },
  490. getProp: function getProp(key) {
  491. if ((0, _utils.isDef)(this[key])) {
  492. return this[key];
  493. }
  494. if (this.vanForm && (0, _utils.isDef)(this.vanForm[key])) {
  495. return this.vanForm[key];
  496. }
  497. },
  498. genLabel: function genLabel() {
  499. var h = this.$createElement;
  500. var colon = this.getProp('colon') ? ':' : '';
  501. if (this.slots('label')) {
  502. return [this.slots('label'), colon];
  503. }
  504. if (this.label) {
  505. return h("span", [this.label + colon]);
  506. }
  507. }
  508. },
  509. render: function render() {
  510. var _bem;
  511. var h = arguments[0];
  512. var slots = this.slots;
  513. var disabled = this.getProp('disabled');
  514. var labelAlign = this.getProp('labelAlign');
  515. var scopedSlots = {
  516. icon: this.genLeftIcon
  517. };
  518. var Label = this.genLabel();
  519. if (Label) {
  520. scopedSlots.title = function () {
  521. return Label;
  522. };
  523. }
  524. var extra = this.slots('extra');
  525. if (extra) {
  526. scopedSlots.extra = function () {
  527. return extra;
  528. };
  529. }
  530. return h(_cell.default, {
  531. "attrs": {
  532. "icon": this.leftIcon,
  533. "size": this.size,
  534. "center": this.center,
  535. "border": this.border,
  536. "isLink": this.isLink,
  537. "required": this.required,
  538. "clickable": this.clickable,
  539. "titleStyle": this.labelStyle,
  540. "valueClass": bem('value'),
  541. "titleClass": [bem('label', labelAlign), this.labelClass],
  542. "arrowDirection": this.arrowDirection
  543. },
  544. "scopedSlots": scopedSlots,
  545. "class": bem((_bem = {
  546. error: this.showError,
  547. disabled: disabled
  548. }, _bem["label-" + labelAlign] = labelAlign, _bem['min-height'] = this.type === 'textarea' && !this.autosize, _bem)),
  549. "on": {
  550. "click": this.onClick
  551. }
  552. }, [h("div", {
  553. "class": bem('body')
  554. }, [this.genInput(), this.showClear && h(_icon.default, {
  555. "attrs": {
  556. "name": "clear"
  557. },
  558. "class": bem('clear'),
  559. "on": {
  560. "touchstart": this.onClear
  561. }
  562. }), this.genRightIcon(), slots('button') && h("div", {
  563. "class": bem('button')
  564. }, [slots('button')])]), this.genWordLimit(), this.genMessage()]);
  565. }
  566. });
  567. exports.default = _default;