add Input component

add Input component
This commit is contained in:
梁灏 2016-11-08 17:53:04 +08:00
parent 650ce7b855
commit 0f822c9b36
10 changed files with 489 additions and 25 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 160 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,22 +1,44 @@
<template>
<div :class="wrapClasses">
<input
:class="classes"
:type="type"
<template v-if="type !== 'textarea'">
<div :class="[prefixCls + '-group-prepend']" v-if="prepend" v-el:prepend><slot name="prepend"></slot></div>
<i class="ivu-icon" :class="['ivu-icon-' + icon, prefixCls + '-icon']" v-if="icon" @click="handleIconClick"></i>
<input
type="text"
:class="inputClasses"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="maxlength"
v-model="value"
@keyup.enter="handleEnter">
<div :class="[prefixCls + '-group-append']" v-if="append" v-el:append><slot name="append"></slot></div>
</template>
<textarea
v-else
v-el:textarea
:class="textareaClasses"
:style="textareaStyles"
:placeholder="placeholder"
:name="name"
v-model="value">
:disabled="disabled"
:rows="rows"
:maxlength="maxlength"
v-model="value"
@keyup.enter="handleEnter">
</textarea>
</div>
</template>
<script>
import { oneOf } from '../../utils/assist';
import calcTextareaHeight from '../../utils/calcTextareaHeight';
const prefixCls = 'ivu-input';
export default {
props: {
type: {
type: String,
validator (value) {
return oneOf(value, ['text', 'textarea']);
},
default: 'text'
},
value: {
@ -24,31 +46,105 @@
default: '',
twoWay: true
},
placeholder: String,
name: String,
size: {
validator (value) {
return oneOf(value, ['small', 'large']);
}
},
placeholder: {
type: String,
default: ''
},
maxlength: {
type: Number
},
disabled: {
type: Boolean,
default: false
},
icon: String,
autosize: {
type: [Boolean, Object],
default: false
},
rows: {
type: Number,
default: 2
}
},
data () {
return {
prefixCls: prefixCls,
prepend: true,
append: true,
textareaStyles: {}
}
},
computed: {
wrapClasses () {
return [
`${prefixCls}-wrapper`,
{
[`${prefixCls}-type`]: this.type,
[`${prefixCls}-group`]: this.prepend || this.append,
[`${prefixCls}-group-${this.size}`]: (this.prepend || this.append) && !!this.size
}
]
},
classes () {
inputClasses () {
return [
`${prefixCls}`,
{
[`${prefixCls}-${this.size}`]: !!this.size
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-disabled`]: this.disabled
}
]
},
textareaClasses () {
return [
`${prefixCls}`,
{
[`${prefixCls}-disabled`]: this.disabled
}
]
}
},
methods: {
handleEnter () {
this.$emit('on-enter');
},
handleIconClick () {
this.$emit('on-click');
},
resizeTextarea () {
const autosize = this.autosize;
if (!autosize || this.type !== 'textarea') {
return false;
}
const minRows = autosize.minRows;
const maxRows = autosize.maxRows;
this.textareaStyles = calcTextareaHeight(this.$els.textarea, minRows, maxRows);
}
},
watch: {
value (val) {
this.$nextTick(() => {
this.resizeTextarea();
});
this.$emit('on-change', val);
}
},
ready () {
if (this.type === 'text') {
this.prepend = this.$els.prepend.innerHTML !== '';
this.append = this.$els.append.innerHTML !== '';
} else {
this.prepend = false;
this.append = false;
}
this.resizeTextarea();
}
}
</script>

View file

@ -23,4 +23,5 @@
@import "select";
@import "select-dropdown";
@import "tooltip";
@import "poptip";
@import "poptip";
@import "input";

View file

@ -0,0 +1,35 @@
@input-prefix-cls: ~"@{css-prefix}input";
.@{input-prefix-cls} {
.input;
&-wrapper{
display: inline-block;
width: 100%;
position: relative;
}
&-icon {
width: 28px;
height: 100%;
font-size: 16px;
text-align: center;
color: @subsidiary-color;
position: absolute;
right: 0;
z-index: 1;
&:after{
content: '';
display: inline-block;
width: 0;
height: 100%;
vertical-align: middle;
}
}
&-icon + &{
padding-right: 28px;
}
}
.@{input-prefix-cls}-group{
.input-group(~"@{input-prefix-cls}");
}

View file

@ -74,4 +74,136 @@
&-small {
.input-small();
}
}
}
.input-group(@inputClass) {
display: table;
width: 100%;
border-collapse: separate;
position: relative;
// Undo padding and float of grid classes
&[class*="col-"] {
float: none;
padding-left: 0;
padding-right: 0;
}
> [class*="col-"] {
padding-right: 8px;
}
&-prepend,
&-append,
> .@{inputClass} {
display: table-cell;
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
}
&-prepend .@{css-prefix}btn,
&-append .@{css-prefix}btn
{
border-color: transparent;
background-color: transparent;
color: inherit;
margin: -(@input-padding-vertical-base + 1) (-@input-padding-horizontal);
}
&-prepend,
&-append
{
width: 1px; // To make addon/wrap as small as possible
white-space: nowrap;
vertical-align: middle;
}
.@{inputClass} {
width: 100%;
float: left;
margin-bottom: 0;
position: relative;
z-index: 2;
}
&-prepend,
&-append
{
padding: @input-padding-vertical-base @input-padding-horizontal;
font-size: @font-size-base;
font-weight: normal;
line-height: 1;
color: @input-color;
text-align: center;
background-color: #eee;
border: 1px solid @input-border-color;
border-radius: @border-radius-base;
// Reset Select's style in addon
.@{css-prefix}select {
margin: -(@input-padding-vertical-base + 1) (-@input-padding-horizontal); // lesshint spaceAroundOperator: false
&-selection {
background-color: inherit;
margin: -1px;
border: 1px solid transparent;
}
&-visible .@{css-prefix}select-selection{
box-shadow: none;
}
}
}
// Reset rounded corners
> span > .@{inputClass}:first-child,
> .@{inputClass}:first-child,
&-prepend
{
border-bottom-right-radius: 0 !important;
border-top-right-radius: 0 !important;
// Reset Select's style in addon
.@{css-prefix}-select .@{css-prefix}-select-selection {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
}
&-prepend {
border-right: 0;
}
&-append {
border-left: 0;
}
> .@{inputClass}:last-child,
&-append
{
border-bottom-left-radius: 0 !important;
border-top-left-radius: 0 !important;
// Reset Select's style in addon
.@{css-prefix}-select .@{css-prefix}-select-selection {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
}
// Sizing options
&-large .@{inputClass},
&-large > &-prepend,
&-large > &-append
{
.input-large();
}
&-small .@{inputClass},
&-small > &-prepend,
&-small > &-append
{
.input-small();
}
}

View file

@ -12,6 +12,7 @@
@link-active-color : shade(@link-color, 5%);
@selected-color : fade(@primary-color, 90%);
@tooltip-color : #fff;
@subsidiary-color : #9ea7b4;
// Base
@body-background : #fff;
@ -84,8 +85,8 @@
// Input
@input-height-base : 28px;
@input-height-large : 32px;
@input-height-small : 22px;
@input-height-large : 36px;
@input-height-small : 24px;
@input-padding-horizontal : 7px;
@input-padding-vertical-base : 4px;

View file

@ -0,0 +1,108 @@
// Thanks to
// https://github.com/andreypopp/react-textarea-autosize/
// https://github.com/ElemeFE/element/blob/master/packages/input/src/calcTextareaHeight.js
let hiddenTextarea;
const HIDDEN_STYLE = `
height:0 !important;
min-height:0 !important;
max-height:none !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;
let minHeight = -Infinity;
let maxHeight = Infinity;
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) {
minHeight = singleRowHeight * minRows;
if (boxSizing === 'border-box') {
minHeight = minHeight + paddingSize + borderSize;
}
height = Math.max(minHeight, height);
}
if (maxRows !== null) {
maxHeight = singleRowHeight * maxRows;
if (boxSizing === 'border-box') {
maxHeight = maxHeight + paddingSize + borderSize;
}
height = Math.min(maxHeight, height);
}
return {
height: `${height}px`,
minHeight: `${minHeight}px`,
maxHeight: `${maxHeight}px`
};
};

View file

@ -1,26 +1,117 @@
<template>
<i-input></i-input>
<i-input icon="ios-clock-outline" style="width:200px;" :value.sync="v" @on-enter="enter" @on-click="iconclick" size="large" placeholder="请输入"></i-input>
<i-input icon="ios-clock-outline" style="width:200px;" :value.sync="v" @on-enter="enter" placeholder="请输入"></i-input>
<i-input icon="ios-clock-outline" style="width:200px;" :value.sync="v" @on-enter="enter" size="small" placeholder="请输入"></i-input>
<br>
<br>
<i-input style="width:200px;" :value.sync="v" @on-enter="enter" size="large" placeholder="请输入"></i-input>
<i-input style="width:200px;" :value.sync="v" @on-enter="enter" placeholder="请输入"></i-input>
<i-input style="width:200px;" :value.sync="v" @on-enter="enter" @on-change="change" size="small" placeholder="请输入"></i-input>
{{ v }}
<br>
<br>
<i-input placeholder="this is something" style="width:200px;" :value.sync="t" type="textarea" :autosize="autosize"></i-input>
{{ t }}
<br>
<br>
<div style="width: 400px">
<i-input :value.sync="v">
<span slot="prepend">http://</span>
<span slot="append">
<i-button icon="ios-search"></i-button>
</span>
</i-input>
<br>
<i-input :value.sync="v">
<span slot="prepend">http://</span>
<span slot="append"><Icon type="ios-search"></Icon></span>
</i-input>
<br>
<i-input :value.sync="v" size="small">
<span slot="prepend">http://</span>
<span slot="append"><Icon type="ios-search"></Icon></span>
</i-input>
<br>
<i-input :value.sync="v" size="large">
<i-select :model.sync="select1" slot="prepend" style="width: 80px">
<i-option value="http">http://</i-option>
<i-option value="https">https://</i-option>
</i-select>
<i-select :model.sync="select2" slot="append" style="width: 70px">
<i-option value="com">.com</i-option>
<i-option value="cn">.cn</i-option>
<i-option value="net">.net</i-option>
<i-option value="io">.io</i-option>
</i-select>
</i-input>
<br>
<i-input :value.sync="v">
<i-select :model.sync="select1" slot="prepend" style="width: 80px">
<i-option value="http">http://</i-option>
<i-option value="https">https://</i-option>
</i-select>
<i-select :model.sync="select2" slot="append" style="width: 70px">
<i-option value="com">.com</i-option>
<i-option value="cn">.cn</i-option>
<i-option value="net">.net</i-option>
<i-option value="io">.io</i-option>
</i-select>
</i-input>
<br>
<i-input :value.sync="v" size="small">
<i-select :model.sync="select1" slot="prepend" style="width: 80px">
<i-option value="http">http://</i-option>
<i-option value="https">https://</i-option>
</i-select>
<i-select :model.sync="select2" slot="append" style="width: 70px">
<i-option value="com">.com</i-option>
<i-option value="cn">.cn</i-option>
<i-option value="net">.net</i-option>
<i-option value="io">.io</i-option>
</i-select>
</i-input>
</div>
</template>
<script>
import { Input } from 'iview';
import { iInput, Icon, iButton, iSelect, iOption } from 'iview';
export default {
components: {
iInput: Input
iInput,
Icon,
iButton,
iSelect,
iOption
},
props: {
},
data () {
return {
v: 'hello',
t: '',
autosize: {
minRows: 2,
maxRows: 5
},
select1: 'http',
select2: 'com'
}
},
computed: {
},
methods: {
enter () {
console.log(123)
},
iconclick () {
console.log('iconclicked')
},
change (val) {
console.log(val)
}
}
}
</script>