index.vue 7.8 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
<template>
2
  <uni-textarea v-on="$listeners">
3 4 5 6
    <div
      ref="wrapper"
      class="uni-textarea-wrapper"
    >
fxy060608's avatar
fxy060608 已提交
7
      <div
8
        v-show="!(composing || valueSync.length)"
fxy060608's avatar
fxy060608 已提交
9 10 11
        ref="placeholder"
        :style="placeholderStyle"
        :class="placeholderClass"
12
        class="uni-textarea-placeholder"
13 14
        v-text="placeholder"
      />
15 16
      <div
        ref="line"
fxy060608's avatar
fxy060608 已提交
17
        class="uni-textarea-line"
18 19
        v-text="' '"
      />
20 21
      <div class="uni-textarea-compute">
        <div
22
          v-for="(item, index) in valueCompute"
fxy060608's avatar
fxy060608 已提交
23
          :key="index"
24 25
          v-text="item.trim() ? item : '.'"
        />
26 27
        <v-uni-resize-sensor
          ref="sensor"
fxy060608's avatar
fxy060608 已提交
28 29
          @resize="_resize"
        />
30
      </div>
fxy060608's avatar
fxy060608 已提交
31
      <textarea
32
        v-if="!disabled || !fixColor"
fxy060608's avatar
fxy060608 已提交
33 34
        ref="textarea"
        v-model="valueSync"
35
        v-keyboard
36
        v-field
fxy060608's avatar
fxy060608 已提交
37 38
        :disabled="disabled"
        :maxlength="maxlengthNumber"
39 40
        :class="{ 'uni-textarea-textarea-fix-margin': fixMargin }"
        :style="{ 'overflow-y': autoHeight ? 'hidden' : 'auto' }"
41
        :enterkeyhint="confirmType"
fxy060608's avatar
fxy060608 已提交
42
        class="uni-textarea-textarea"
43
        @change.stop
D
DCloud_LXH 已提交
44 45 46
        @compositionstart.stop="_onComposition"
        @compositionend.stop="_onComposition"
        @compositionupdate.stop="_onComposition"
47 48 49 50
        @input.stop="_onInput"
        @focus="_onFocus"
        @blur="_onBlur"
        @touchstart.passive="_onTouchstart"
51 52
        @keyup.enter="_onKeyUpEnter"
        @keydown.enter="_onKeyDownEnter"
53
      />
54
      <!-- fix: 禁止 readonly 状态获取焦点 -->
55 56 57 58 59 60 61 62 63 64
      <textarea
        v-if="disabled && fixColor"
        ref="textarea"
        :value="valueSync"
        tabindex="-1"
        :readonly="disabled"
        :maxlength="maxlengthNumber"
        :class="{ 'uni-textarea-textarea-fix-margin': fixMargin }"
        :style="{ 'overflow-y': autoHeight ? 'hidden' : 'auto' }"
        class="uni-textarea-textarea"
65
        @focus="($event) => $event.target.blur()"
66
      />
fxy060608's avatar
fxy060608 已提交
67 68 69 70 71
    </div>
  </uni-textarea>
</template>
<script>
import {
72
  field
fxy060608's avatar
fxy060608 已提交
73
} from 'uni-mixins'
74
const DARK_TEST_STRING = '(prefers-color-scheme: dark)'
75
const ConfirmTypes = ['done', 'go', 'next', 'search', 'send'] // 'return'
fxy060608's avatar
fxy060608 已提交
76 77
export default {
  name: 'Textarea',
78
  mixins: [field],
fxy060608's avatar
fxy060608 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  props: {
    name: {
      type: String,
      default: ''
    },
    maxlength: {
      type: [Number, String],
      default: 140
    },
    placeholder: {
      type: String,
      default: ''
    },
    disabled: {
      type: [Boolean, String],
      default: false
    },
    placeholderClass: {
      type: String,
98
      default: 'textarea-placeholder'
fxy060608's avatar
fxy060608 已提交
99 100 101 102 103 104 105 106 107
    },
    placeholderStyle: {
      type: String,
      default: ''
    },
    autoHeight: {
      type: [Boolean, String],
      default: false
    },
108 109
    confirmType: {
      type: String,
110 111 112 113
      default: 'return',
      validator (val) {
        return ConfirmTypes.concat('return').includes(val)
      }
fxy060608's avatar
fxy060608 已提交
114 115 116 117
    }
  },
  data () {
    return {
118
      valueComposition: '',
fxy060608's avatar
fxy060608 已提交
119
      height: 0,
120
      focusChangeSource: '',
121
      // iOS 13 以下版本需要修正边距
D
fix:  
DCloud_LXH 已提交
122
      fixMargin: String(navigator.platform).indexOf('iP') === 0 && String(navigator.vendor).indexOf('Apple') === 0 && window.matchMedia(DARK_TEST_STRING).media !== DARK_TEST_STRING
fxy060608's avatar
fxy060608 已提交
123 124 125 126 127 128 129
    }
  },
  computed: {
    maxlengthNumber () {
      var maxlength = Number(this.maxlength)
      return isNaN(maxlength) ? 140 : maxlength
    },
130
    valueCompute () {
131
      return (this.composing ? this.valueComposition : this.valueSync).split('\n')
132 133
    },
    isDone () {
134
      return ConfirmTypes.includes(this.confirmType)
fxy060608's avatar
fxy060608 已提交
135 136 137 138 139 140 141 142 143
    }
  },
  watch: {
    focus (val) {
      if (val) {
        this.focusChangeSource = 'focus'
      }
    },
    height (height) {
144 145 146 147
      let lineHeight = parseFloat(getComputedStyle(this.$el).lineHeight)
      if (isNaN(lineHeight)) {
        lineHeight = this.$refs.line.offsetHeight
      }
fxy060608's avatar
fxy060608 已提交
148 149 150 151 152 153 154
      var lineCount = Math.round(height / lineHeight)
      this.$trigger('linechange', {}, {
        height,
        heightRpx: 750 / window.innerWidth * height,
        lineCount
      })
      if (this.autoHeight) {
155 156
        this.$el.style.height = 'auto'
        this.$refs.wrapper.style.height = this.height + 'px'
fxy060608's avatar
fxy060608 已提交
157 158 159 160 161 162 163 164 165 166
      }
    }
  },
  created () {
    this.$dispatch('Form', 'uni-form-group-update', {
      type: 'add',
      vm: this
    })
  },
  mounted () {
167 168 169
    this._resize({
      height: this.$refs.sensor.$el.offsetHeight
    })
170 171 172 173 174 175 176 177 178

    let $vm = this
    while ($vm) {
      const scopeId = $vm.$options._scopeId
      if (scopeId) {
        this.$refs.placeholder.setAttribute(scopeId, '')
      }
      $vm = $vm.$parent
    }
fxy060608's avatar
fxy060608 已提交
179 180 181 182 183 184 185 186
  },
  beforeDestroy () {
    this.$dispatch('Form', 'uni-form-group-update', {
      type: 'remove',
      vm: this
    })
  },
  methods: {
187 188 189 190 191 192 193 194
    _onKeyDownEnter: function ($event) {
      if (this.isDone) {
        $event.preventDefault()
      }
    },
    _onKeyUpEnter: function ($event) {
      if (this.isDone) {
        this._confirm($event)
195
        !this.confirmHold && this.$refs.textarea.blur()
196 197
      }
    },
D
DCloud_LXH 已提交
198 199 200 201 202 203 204 205 206 207 208 209
    _onComposition ($event) {
      switch ($event.type) {
        case 'compositionstart':
          this.composing = true
          break
        case 'compositionend':
          if (this.composing) {
            this.composing = false
            // 部分输入法 compositionend 事件可能晚于 input
            this._onInput($event)
          }
          break
210
      }
D
DCloud_LXH 已提交
211 212 213

      !this.ignoreCompositionEvent &&
        this.$trigger($event.type, $event, { data: $event.data })
fxy060608's avatar
fxy060608 已提交
214 215 216 217 218 219 220 221 222 223 224
    },
    _confirm ($event) {
      this.$trigger('confirm', $event, {
        value: this.valueSync
      })
    },
    _linechange ($event) {
      this.$trigger('linechange', $event, {
        value: this.valueSync
      })
    },
225
    _onTouchstart () {
fxy060608's avatar
fxy060608 已提交
226 227
      this.focusChangeSource = 'touch'
    },
228 229 230
    _resize ({ height }) {
      this.height = height
    },
231
    _onInput ($event, force) {
D
DCloud_LXH 已提交
232
      if (this.composing && this.ignoreCompositionEvent) {
233
        this.valueComposition = $event.target.value
234
        return
235
      }
D
DCloud_LXH 已提交
236 237 238

      if (!this.ignoreCompositionEvent) this.valueSync = this.$refs.textarea.value

239 240 241
      this.$triggerInput($event, {
        value: this.valueSync,
        cursor: this.$refs.textarea.selectionEnd
242
      }, force)
fxy060608's avatar
fxy060608 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
    },
    _getFormData () {
      return {
        value: this.valueSync,
        key: this.name
      }
    },
    _resetFormData () {
      this.valueSync = ''
    }
  }
}
</script>

<style>
uni-textarea {
  width: 300px;
  height: 150px;
  display: block;
  position: relative;
  font-size: 16px;
264
  line-height: normal;
265 266
  white-space: pre-wrap;
  word-break: break-all;
267 268 269 270
}
uni-textarea[hidden] {
  display: none;
}
Q
qiang 已提交
271 272
.uni-textarea-wrapper,
.uni-textarea-placeholder,
273
.uni-textarea-line,
Q
qiang 已提交
274 275 276 277 278 279 280 281
.uni-textarea-compute,
.uni-textarea-textarea {
  outline: none;
  border: none;
  padding: 0;
  margin: 0;
  text-decoration: inherit;
}
282
.uni-textarea-wrapper {
Q
qiang 已提交
283
  display: block;
fxy060608's avatar
fxy060608 已提交
284 285 286
  position: relative;
  width: 100%;
  height: 100%;
287
  min-height: inherit;
fxy060608's avatar
fxy060608 已提交
288 289
}
.uni-textarea-placeholder,
290
.uni-textarea-line,
291
.uni-textarea-compute,
fxy060608's avatar
fxy060608 已提交
292 293 294 295 296 297
.uni-textarea-textarea {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
298 299
  white-space: inherit;
  word-break: inherit;
fxy060608's avatar
fxy060608 已提交
300 301 302
}
.uni-textarea-placeholder {
  color: grey;
Q
qiang 已提交
303
  overflow: hidden;
fxy060608's avatar
fxy060608 已提交
304
}
305
.uni-textarea-line,
306 307 308 309
.uni-textarea-compute {
  visibility: hidden;
  height: auto;
}
310 311 312
.uni-textarea-line {
  width: 1em;
}
fxy060608's avatar
fxy060608 已提交
313 314
.uni-textarea-textarea {
  resize: none;
Q
qiang 已提交
315 316
  background: none;
  color: inherit;
317
  opacity: 1;
Q
qiang 已提交
318 319 320 321 322 323 324
  font: inherit;
  line-height: inherit;
  letter-spacing: inherit;
  text-align: inherit;
  text-indent: inherit;
  text-transform: inherit;
  text-shadow: inherit;
fxy060608's avatar
fxy060608 已提交
325
}
326
/* 用于解决 iOS textarea 内部默认边距 */
327
.uni-textarea-textarea-fix-margin {
328 329 330 331
  width: auto;
  right: 0;
  margin: 0 -3px;
}
332 333 334 335
.uni-textarea-textarea:disabled {
  /* 用于重置iOS14以下禁用状态文字颜色 */
  -webkit-text-fill-color: currentcolor;
}
fxy060608's avatar
fxy060608 已提交
336
</style>