index.js 16 KB

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