textarea.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import XEUtils from 'xe-utils/ctor'
  2. import GlobalConfig from '../../conf'
  3. import { UtilTools } from '../../tools'
  4. let autoTxtElem
  5. export default {
  6. name: 'VxeTextarea',
  7. props: {
  8. value: [String, Number],
  9. name: String,
  10. readonly: Boolean,
  11. disabled: Boolean,
  12. placeholder: String,
  13. maxlength: [String, Number],
  14. rows: { type: [String, Number], default: 2 },
  15. showWordCount: Boolean,
  16. autosize: [Boolean, Object],
  17. form: String,
  18. resize: { type: String, default: () => GlobalConfig.textarea.resize },
  19. size: { type: String, default: () => GlobalConfig.textarea.size || GlobalConfig.size }
  20. },
  21. computed: {
  22. vSize () {
  23. return this.size || this.$parent.size || this.$parent.vSize
  24. },
  25. inputCount () {
  26. return XEUtils.getSize(this.value)
  27. },
  28. isCountError () {
  29. return this.maxlength && this.inputCount > XEUtils.toNumber(this.maxlength)
  30. },
  31. defaultEvents () {
  32. const evnts = {}
  33. XEUtils.each(this.$listeners, (cb, name) => {
  34. if (['change'].indexOf(name) === -1) {
  35. evnts[name] = this.triggerEvent
  36. }
  37. })
  38. evnts.input = this.inputEvent
  39. return evnts
  40. },
  41. sizeOpts () {
  42. return Object.assign({ minRows: 1, maxRows: 10 }, GlobalConfig.textarea.autosize, this.autosize)
  43. }
  44. },
  45. watch: {
  46. value () {
  47. this.updateAutoTxt()
  48. }
  49. },
  50. created () {
  51. if (!autoTxtElem) {
  52. autoTxtElem = document.createElement('div')
  53. document.body.appendChild(autoTxtElem)
  54. }
  55. },
  56. mounted () {
  57. this.updateAutoTxt()
  58. this.handleResize()
  59. },
  60. render (h) {
  61. const { defaultEvents, value, vSize, name, form, resize, placeholder, readonly, disabled, maxlength, autosize, showWordCount } = this
  62. const attrs = {
  63. name,
  64. form,
  65. placeholder,
  66. maxlength,
  67. readonly,
  68. disabled
  69. }
  70. if (placeholder) {
  71. attrs.placeholder = UtilTools.getFuncText(placeholder)
  72. }
  73. return h('div', {
  74. class: ['vxe-textarea', {
  75. [`size--${vSize}`]: vSize,
  76. 'is--autosize': autosize,
  77. 'is--disabled': disabled
  78. }]
  79. }, [
  80. h('textarea', {
  81. ref: 'textarea',
  82. class: 'vxe-textarea--inner',
  83. domProps: {
  84. value
  85. },
  86. attrs,
  87. style: resize ? {
  88. resize
  89. } : null,
  90. on: defaultEvents
  91. }),
  92. showWordCount ? h('span', {
  93. class: ['vxe-textarea--count', {
  94. 'is--error': this.isCountError
  95. }]
  96. }, `${this.inputCount}${maxlength ? `/${maxlength}` : ''}`) : null
  97. ])
  98. },
  99. methods: {
  100. focus () {
  101. this.$refs.textarea.focus()
  102. return this.$nextTick()
  103. },
  104. blur () {
  105. this.$refs.textarea.blur()
  106. return this.$nextTick()
  107. },
  108. triggerEvent (evnt) {
  109. const { value } = this
  110. this.$emit(evnt.type, { value, $event: evnt }, evnt)
  111. },
  112. emitUpdate (value, evnt) {
  113. if (this.value !== value) {
  114. this.$emit('input', value)
  115. this.$emit('change', { value, $event: evnt })
  116. }
  117. },
  118. inputEvent (evnt) {
  119. this.emitUpdate(evnt.target.value, evnt)
  120. this.handleResize()
  121. },
  122. updateAutoTxt () {
  123. const { $refs, value, size, autosize } = this
  124. if (autosize) {
  125. const textElem = $refs.textarea
  126. const textStyle = getComputedStyle(textElem)
  127. autoTxtElem.className = ['vxe-textarea--autosize', size ? `size--${size}` : ''].join(' ')
  128. autoTxtElem.style.width = `${textElem.clientWidth}px`
  129. autoTxtElem.style.padding = textStyle.padding
  130. autoTxtElem.innerHTML = ('' + (value || ' ')).replace(/\n$/, '\n ')
  131. }
  132. },
  133. handleResize () {
  134. if (this.autosize) {
  135. this.$nextTick(() => {
  136. const { $refs, sizeOpts } = this
  137. const { minRows, maxRows } = sizeOpts
  138. const textElem = $refs.textarea
  139. const sizeHeight = autoTxtElem.clientHeight
  140. const textStyle = getComputedStyle(textElem)
  141. const lineHeight = XEUtils.toNumber(textStyle.lineHeight)
  142. const paddingTop = XEUtils.toNumber(textStyle.paddingTop)
  143. const paddingBottom = XEUtils.toNumber(textStyle.paddingBottom)
  144. const borderTopWidth = XEUtils.toNumber(textStyle.borderTopWidth)
  145. const borderBottomWidth = XEUtils.toNumber(textStyle.borderBottomWidth)
  146. const intervalHeight = paddingTop + paddingBottom + borderTopWidth + borderBottomWidth
  147. const rowNum = (sizeHeight - intervalHeight) / lineHeight
  148. const textRows = rowNum && /[0-9]/.test(rowNum) ? rowNum : Math.floor(rowNum) + 1
  149. let vaildRows = textRows
  150. if (textRows < minRows) {
  151. vaildRows = minRows
  152. } else if (textRows > maxRows) {
  153. vaildRows = maxRows
  154. }
  155. textElem.style.height = `${(vaildRows * lineHeight) + intervalHeight}px`
  156. })
  157. }
  158. }
  159. }
  160. }