add rows and autosize property

pull/225/head
baiyaaaaa 2016-10-04 12:17:19 +08:00
parent 3c10d063e8
commit 70410a2b1f
5 changed files with 157 additions and 19 deletions

View File

@ -8,6 +8,7 @@
- 修复 TimePicker 错误的隐藏面板 - 修复 TimePicker 错误的隐藏面板
- 修复 Table Cell 的样式, #204 - 修复 Table Cell 的样式, #204
- 为 Message Box 和 Dialog 添加 lockScroll 属性,用于定义是否在弹框出现时将 body 滚动锁定 - 为 Message Box 和 Dialog 添加 lockScroll 属性,用于定义是否在弹框出现时将 body 滚动锁定
- 新增 Input textarea 类型的 rows, autosize 属性
### 1.0.0-rc.5 ### 1.0.0-rc.5

View File

@ -240,6 +240,8 @@
```html ```html
<el-input <el-input
type="textarea" type="textarea"
placeholder="请输入内容"
:autosize="{minRows: 2, maxRows: 5}"
v-model="textarea"> v-model="textarea">
</el-input> </el-input>
``` ```
@ -625,15 +627,17 @@
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
|------------- |---------------- |---------------- |---------------------- |-------- | |------------- |---------------- |---------------- |---------------------- |-------- |
| type | 同原生的 input 的 type 属性,如果为 textarea 则显示为 textarea | string | — | — | | type | 同原生的 input 的 type 属性,另外提供 type="textarea" | string | — | — |
| value | 绑定值 | string, number | — | — | | value | 绑定值 | string, number | — | — |
| maxlength | 最大输入长度 | number | — | — | | maxlength | 最大输入长度 | number | — | — |
| minlength | 最小输入长度 | number | — | — | | minlength | 最小输入长度 | number | — | — |
| placeholder | 输入框占位文本 | string | — | — | | placeholder | 输入框占位文本 | string | — | — |
| disabled | 禁用 | boolean | — | false | | disabled | 禁用 | boolean | — | false |
| size | 输入框尺寸 | string | large, small, mini | — | | size | 输入框尺寸,只在 `type!="textarea"` 时有效 | string | large, small, mini | — |
| icon | 输入框尾部图标 | string | — | — | | icon | 输入框尾部图标 | string | — | — |
| number | 指定model值为number类型 | boolean | — | false | | number | 指定 model 值为 number 类型 | boolean | — | false |
| rows | 输入框行数,只对 `type="textarea"` 有效 | number | — | 2 |
| autosize | 自适应内容高度,只对 `type="textarea"` 有效,可传入对象,如,{ minRows: 2, maxRows: 6 } | boolean/object | — | false |
### Autocomplete API ### Autocomplete API

View File

@ -0,0 +1,100 @@
let hiddenTextarea;
const HIDDEN_STYLE = `
height:0 !important;
visibility:hidden !important;
overflow:hidden !important;
position:absolute !important;
z-index:-1000 !important;
top:0 !important;
right:0 !important
`;
const CONTEXT_STYLE = [
'letter-spacing',
'line-height',
'padding-top',
'padding-bottom',
'font-family',
'font-weight',
'font-size',
'text-rendering',
'text-transform',
'width',
'text-indent',
'padding-left',
'padding-right',
'border-width',
'box-sizing'
];
function calculateNodeStyling(node) {
const style = window.getComputedStyle(node);
const boxSizing = style.getPropertyValue('box-sizing');
const paddingSize = (
parseFloat(style.getPropertyValue('padding-bottom')) +
parseFloat(style.getPropertyValue('padding-top'))
);
const borderSize = (
parseFloat(style.getPropertyValue('border-bottom-width')) +
parseFloat(style.getPropertyValue('border-top-width'))
);
const contextStyle = CONTEXT_STYLE
.map(name => `${name}:${style.getPropertyValue(name)}`)
.join(';');
return { contextStyle, paddingSize, borderSize, boxSizing };
}
export default function calcTextareaHeight(
targetNode,
minRows = null,
maxRows = null
) {
if (!hiddenTextarea) {
hiddenTextarea = document.createElement('textarea');
document.body.appendChild(hiddenTextarea);
}
let {
paddingSize,
borderSize,
boxSizing,
contextStyle
} = calculateNodeStyling(targetNode);
hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);
hiddenTextarea.value = targetNode.value || targetNode.placeholder || '';
let height = hiddenTextarea.scrollHeight;
if (boxSizing === 'border-box') {
height = height + borderSize;
} else if (boxSizing === 'content-box') {
height = height - paddingSize;
}
hiddenTextarea.value = '';
let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;
if (minRows !== null) {
let minHeight = singleRowHeight * minRows;
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
height = Math.max(minHeight, height);
}
if (maxRows !== null) {
let maxHeight = singleRowHeight * maxRows;
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
height = Math.min(maxHeight, height);
}
return { height: height + 'px'};
};

View File

@ -13,20 +13,20 @@
<slot name="prepend"></slot> <slot name="prepend"></slot>
</div> </div>
<input <input
class="el-input__inner"
v-model="currentValue"
:type="type" :type="type"
:name="name" :name="name"
class="el-input__inner"
:placeholder="placeholder" :placeholder="placeholder"
v-model="currentValue"
:disabled="disabled" :disabled="disabled"
:readonly="readonly" :readonly="readonly"
@focus="$emit('onfocus', currentValue)"
@blur="handleBlur"
:number="number" :number="number"
:maxlength="maxlength" :maxlength="maxlength"
:minlength="minlength" :minlength="minlength"
:autocomplete="autoComplete" :autocomplete="autoComplete"
ref="input" ref="input"
@focus="$emit('onfocus', currentValue)"
@blur="handleBlur"
> >
<!-- input 图标 --> <!-- input 图标 -->
<i class="el-input__icon" :class="[icon ? 'el-icon-' + icon : '']" v-if="icon"></i> <i class="el-input__icon" :class="[icon ? 'el-icon-' + icon : '']" v-if="icon"></i>
@ -36,12 +36,27 @@
<slot name="append"></slot> <slot name="append"></slot>
</div> </div>
</template> </template>
<!-- 写成垂直的方式会导致 placeholder 失效, 蜜汁bug --> <textarea
<textarea v-else v-model="currentValue" class="el-textarea__inner" :name="name" :placeholder="placeholder" :disabled="disabled" :readonly="readonly" @focus="$emit('onfocus', currentValue)" @blur="handleBlur"></textarea> v-else
class="el-textarea__inner"
v-model="currentValue"
ref="textarea"
:name="name"
:placeholder="placeholder"
:disabled="disabled"
:style="textareaStyle"
:readonly="readonly"
:rows="rows"
:maxlength="maxlength"
:minlength="minlength"
@focus="$emit('onfocus', currentValue)"
@blur="handleBlur">
</textarea>
</div> </div>
</template> </template>
<script> <script>
import emitter from 'main/mixins/emitter'; import emitter from 'main/mixins/emitter';
import calcTextareaHeight from './calcTextareaHeight';
export default { export default {
name: 'ElInput', name: 'ElInput',
@ -82,6 +97,14 @@
type: Boolean, type: Boolean,
default: false default: false
}, },
autosize: {
type: [Boolean, Object],
default: false
},
rows: {
type: Number,
default: 2
},
autoComplete: { autoComplete: {
type: String, type: String,
default: 'off' default: 'off'
@ -98,12 +121,22 @@
inputSelect() { inputSelect() {
this.$refs.input.select(); this.$refs.input.select();
},
resizeTextarea() {
var { autosize, type } = this;
if (!autosize || type !== 'textarea') {
return;
}
const minRows = autosize ? autosize.minRows : null;
const maxRows = autosize ? autosize.maxRows : null;
this.textareaStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
} }
}, },
data() { data() {
return { return {
currentValue: '' currentValue: this.value,
textareaStyle: {}
}; };
}, },
@ -111,6 +144,10 @@
this.$on('inputSelect', this.inputSelect); this.$on('inputSelect', this.inputSelect);
}, },
mounted() {
this.resizeTextarea();
},
computed: { computed: {
validating() { validating() {
return this.$parent.validating; return this.$parent.validating;
@ -118,16 +155,13 @@
}, },
watch: { watch: {
'value': { 'value'(val, oldValue) {
immediate: true, this.currentValue = val;
handler(val) { this.resizeTextarea();
this.currentValue = val;
}
}, },
'currentValue'(val) { 'currentValue'(val) {
this.$emit('input', val); this.$emit('input', val);
this.$emit('onchange', val);
this.dispatch('form-item', 'el.form.change', [val]); this.dispatch('form-item', 'el.form.change', [val]);
} }
} }

View File

@ -161,11 +161,10 @@
@e inner { @e inner {
display: block; display: block;
resize: vertical; resize: vertical;
padding: 8px 5px; padding: 5px 7px;
line-height: normal; line-height: 1.5;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
min-height: 88px;
font-size: var(--font-size-base); font-size: var(--font-size-base);
color: var(--input-color); color: var(--input-color);
background-color: #fff; background-color: #fff;