diff --git a/components/index.js b/components/index.js
index 0f80d68f4..d430782f9 100644
--- a/components/index.js
+++ b/components/index.js
@@ -25,3 +25,5 @@ export { default as Avatar } from './avatar'
export { default as Badge } from './badge'
export { default as Tabs } from './tabs'
+
+export { default as Input } from './input'
diff --git a/components/input/Group.vue b/components/input/Group.vue
new file mode 100644
index 000000000..169feccc5
--- /dev/null
+++ b/components/input/Group.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/components/input/Input.vue b/components/input/Input.vue
new file mode 100644
index 000000000..f7b332b05
--- /dev/null
+++ b/components/input/Input.vue
@@ -0,0 +1,181 @@
+
diff --git a/components/input/Search.vue b/components/input/Search.vue
new file mode 100644
index 000000000..f3d839bac
--- /dev/null
+++ b/components/input/Search.vue
@@ -0,0 +1,61 @@
+
diff --git a/components/input/TextArea.vue b/components/input/TextArea.vue
new file mode 100644
index 000000000..e69de29bb
diff --git a/components/input/calculateNodeHeight.js b/components/input/calculateNodeHeight.js
new file mode 100644
index 000000000..8f568a19e
--- /dev/null
+++ b/components/input/calculateNodeHeight.js
@@ -0,0 +1,157 @@
+// Thanks to https://github.com/andreypopp/react-textarea-autosize/
+
+/**
+ * calculateNodeHeight(uiTextNode, useCache = false)
+ */
+
+const HIDDEN_TEXTAREA_STYLE = `
+min-height:0 !important;
+max-height:none !important;
+height:0 !important;
+visibility:hidden !important;
+overflow:hidden !important;
+position:absolute !important;
+z-index:-1000 !important;
+top:0 !important;
+right:0 !important
+`
+
+const SIZING_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',
+]
+
+const computedStyleCache = {}
+let hiddenTextarea
+
+function calculateNodeStyling (node, useCache = false) {
+ const nodeRef = (
+ node.getAttribute('id') ||
+ node.getAttribute('data-reactid') ||
+ node.getAttribute('name')
+ )
+
+ if (useCache && computedStyleCache[nodeRef]) {
+ return computedStyleCache[nodeRef]
+ }
+
+ const style = window.getComputedStyle(node)
+
+ const boxSizing = (
+ style.getPropertyValue('box-sizing') ||
+ style.getPropertyValue('-moz-box-sizing') ||
+ style.getPropertyValue('-webkit-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 sizingStyle = SIZING_STYLE
+ .map(name => `${name}:${style.getPropertyValue(name)}`)
+ .join(';')
+
+ const nodeInfo = {
+ sizingStyle,
+ paddingSize,
+ borderSize,
+ boxSizing,
+ }
+
+ if (useCache && nodeRef) {
+ computedStyleCache[nodeRef] = nodeInfo
+ }
+
+ return nodeInfo
+}
+
+export default function calculateNodeHeight (
+ uiTextNode,
+ useCache = false,
+ minRows = null,
+ maxRows = null,
+) {
+ if (!hiddenTextarea) {
+ hiddenTextarea = document.createElement('textarea')
+ document.body.appendChild(hiddenTextarea)
+ }
+
+ // Fix wrap="off" issue
+ // https://github.com/ant-design/ant-design/issues/6577
+ if (uiTextNode.getAttribute('wrap')) {
+ hiddenTextarea.setAttribute('wrap', uiTextNode.getAttribute('wrap'))
+ } else {
+ hiddenTextarea.removeAttribute('wrap')
+ }
+
+ // Copy all CSS properties that have an impact on the height of the content in
+ // the textbox
+ const {
+ paddingSize, borderSize,
+ boxSizing, sizingStyle,
+ } = calculateNodeStyling(uiTextNode, useCache)
+
+ // Need to have the overflow attribute to hide the scrollbar otherwise
+ // text-lines will not calculated properly as the shadow will technically be
+ // narrower for content
+ hiddenTextarea.setAttribute('style', `${sizingStyle};${HIDDEN_TEXTAREA_STYLE}`)
+ hiddenTextarea.value = uiTextNode.value || uiTextNode.placeholder || ''
+
+ let minHeight = -Infinity
+ let maxHeight = Infinity
+ let height = hiddenTextarea.scrollHeight
+ let overflowY
+
+ if (boxSizing === 'border-box') {
+ // border-box: add border, since height = content + padding + border
+ height = height + borderSize
+ } else if (boxSizing === 'content-box') {
+ // remove padding, since height = content
+ height = height - paddingSize
+ }
+
+ if (minRows !== null || maxRows !== null) {
+ // measure height of a textarea with a single row
+ hiddenTextarea.value = ''
+ const 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
+ }
+ overflowY = height > maxHeight ? '' : 'hidden'
+ height = Math.min(maxHeight, height)
+ }
+ }
+ // Remove scroll bar flash when autosize without maxRows
+ if (!maxRows) {
+ overflowY = 'hidden'
+ }
+ return { height, minHeight, maxHeight, overflowY }
+}
diff --git a/components/input/demo/basic.vue b/components/input/demo/basic.vue
new file mode 100644
index 000000000..43218815f
--- /dev/null
+++ b/components/input/demo/basic.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/components/input/index.js b/components/input/index.js
new file mode 100644
index 000000000..5e39b58b5
--- /dev/null
+++ b/components/input/index.js
@@ -0,0 +1,9 @@
+import Input from './Input'
+import Group from './Group'
+import Search from './Search'
+// import TextArea from './TextArea'
+
+Input.Group = Group
+Input.Search = Search
+// Input.TextArea = TextArea
+export default Input
diff --git a/components/input/inputProps.js b/components/input/inputProps.js
new file mode 100644
index 000000000..9d629cfb8
--- /dev/null
+++ b/components/input/inputProps.js
@@ -0,0 +1,39 @@
+export default {
+ prefixCls: {
+ default: 'ant-input',
+ type: String,
+ },
+ defaultValue: [String, Number],
+ value: [String, Number],
+ placeholder: [String, Number],
+ type: {
+ default: 'text',
+ type: String,
+ },
+ id: [String, Number],
+ name: String,
+ size: {
+ validator (value) {
+ return ['small', 'large', 'default'].includes(value)
+ },
+ },
+ maxLength: String,
+ disabled: {
+ default: false,
+ type: Boolean,
+ },
+ readOnly: Boolean,
+ // addonBefore: React.ReactNode,
+ // addonAfter: React.ReactNode,
+ // onPressEnter?: React.FormEventHandler;
+ // onKeyDown?: React.FormEventHandler;
+ // onChange?: React.ChangeEventHandler;
+ // onClick?: React.FormEventHandler;
+ // onFocus?: React.FormEventHandler;
+ // onBlur?: React.FormEventHandler;
+ // autoComplete: String;
+ // prefix: React.ReactNode,
+ // suffix: React.ReactNode,
+ spellCheck: Boolean,
+ autoFocus: Boolean,
+}
diff --git a/components/input/style/index.js b/components/input/style/index.js
new file mode 100644
index 000000000..cf31ed80f
--- /dev/null
+++ b/components/input/style/index.js
@@ -0,0 +1,2 @@
+import '../../style/index.less'
+import './index.less'
diff --git a/components/input/style/index.less b/components/input/style/index.less
new file mode 100644
index 000000000..6271e4e96
--- /dev/null
+++ b/components/input/style/index.less
@@ -0,0 +1,29 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+@import "./mixin";
+@import "./search-input";
+
+// Input styles
+.@{ant-prefix}-input {
+ .input;
+}
+
+//== Style for input-group: input with label, with button or dropdown...
+.@{ant-prefix}-input-group {
+ .input-group(~"@{ant-prefix}-input");
+ &-wrapper {
+ display: inline-block;
+ vertical-align: top; // https://github.com/ant-design/ant-design/issues/6403
+ width: 100%;
+ }
+}
+
+// Input with affix: prefix or suffix
+.@{ant-prefix}-input-affix-wrapper {
+ .input-affix-wrapper(~"@{ant-prefix}-input");
+
+ // https://github.com/ant-design/ant-design/issues/6144
+ .@{ant-prefix}-input {
+ min-height: 100%; // use min-height, assume that no smaller height to override
+ }
+}
diff --git a/components/input/style/mixin.less b/components/input/style/mixin.less
new file mode 100644
index 000000000..d465800d5
--- /dev/null
+++ b/components/input/style/mixin.less
@@ -0,0 +1,345 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+@input-affix-width: 17px;
+
+// size mixins for input
+.input-lg() {
+ padding: @input-padding-vertical-lg @input-padding-horizontal-lg;
+ height: @input-height-lg;
+}
+
+.input-sm() {
+ padding: @input-padding-vertical-sm @input-padding-horizontal-sm;
+ height: @input-height-sm;
+}
+
+// input status
+// == when focus or actived
+.active(@color: @outline-color) {
+ border-color: ~`colorPalette("@{color}", 5)`;
+ outline: 0;
+ box-shadow: 0 0 @outline-blur-size @outline-width fade(@color, 20%);
+}
+
+// == when hoverd
+.hover(@color: @input-hover-border-color) {
+ border-color: ~`colorPalette("@{color}", 5)`;
+}
+
+.disabled() {
+ background-color: @input-disabled-bg;
+ opacity: 1;
+ cursor: not-allowed;
+ color: @disabled-color;
+ &:hover {
+ .hover(@input-border-color);
+ }
+}
+
+// Basic style for input
+.input() {
+ position: relative;
+ display: inline-block;
+ padding: @input-padding-vertical-base @input-padding-horizontal-base;
+ width: 100%;
+ height: @input-height-base;
+ font-size: @font-size-base;
+ line-height: @line-height-base;
+ color: @input-color;
+ background-color: @input-bg;
+ background-image: none;
+ border: @border-width-base @border-style-base @input-border-color;
+ border-radius: @border-radius-base;
+ .placeholder(); // Reset placeholder
+ transition: all .3s;
+
+ &:hover {
+ .hover();
+ }
+
+ &:focus {
+ .active();
+ }
+
+ &-disabled {
+ .disabled();
+ }
+
+ // Reset height for `textarea`s
+ textarea& {
+ max-width: 100%; // prevent textearea resize from coming out of its container
+ height: auto;
+ vertical-align: bottom;
+ transition: all .3s, height 0s;
+ }
+
+ // Size
+ &-lg {
+ .input-lg();
+ }
+
+ &-sm {
+ .input-sm();
+ }
+}
+
+// label input
+.input-group(@inputClass) {
+ position: relative;
+ display: table;
+ border-collapse: separate;
+ border-spacing: 0;
+ width: 100%;
+
+ // Undo padding and float of grid classes
+ &[class*="col-"] {
+ float: none;
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ > [class*="col-"] {
+ padding-right: 8px;
+ &:last-child {
+ padding-right: 0;
+ }
+ }
+
+ &-addon,
+ &-wrap,
+ > .@{inputClass} {
+ display: table-cell;
+
+ &:not(:first-child):not(:last-child) {
+ border-radius: 0;
+ }
+ }
+
+ &-addon,
+ &-wrap {
+ width: 1px; // To make addon/wrap as small as possible
+ white-space: nowrap;
+ vertical-align: middle;
+ }
+
+ &-wrap > * {
+ display: block !important;
+ }
+
+ .@{inputClass} {
+ float: left;
+ width: 100%;
+ margin-bottom: 0;
+ &:focus {
+ z-index: 1; // Fix https://gw.alipayobjects.com/zos/rmsportal/DHNpoqfMXSfrSnlZvhsJ.png
+ }
+ }
+
+ &-addon {
+ padding: @input-padding-vertical-base @input-padding-horizontal-base;
+ font-size: @font-size-base;
+ font-weight: normal;
+ line-height: 1;
+ color: @input-color;
+ text-align: center;
+ background-color: @input-addon-bg;
+ border: @border-width-base @border-style-base @input-border-color;
+ border-radius: @border-radius-base;
+ position: relative;
+ transition: all .3s;
+
+ // Reset Select's style in addon
+ .@{ant-prefix}-select {
+ margin: -(@input-padding-vertical-base + 1px) (-@input-padding-horizontal-base); // lesshint spaceAroundOperator: false
+
+ .@{ant-prefix}-select-selection {
+ background-color: inherit;
+ margin: -1px;
+ border: @border-width-base @border-style-base transparent;
+ box-shadow: none;
+ }
+
+ &-open,
+ &-focused {
+ .@{ant-prefix}-select-selection {
+ color: @primary-color;
+ }
+ }
+ }
+
+ // Expand addon icon click area
+ // https://github.com/ant-design/ant-design/issues/3714
+ > i:only-child:after {
+ position: absolute;
+ content: '';
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+ }
+
+ // Reset rounded corners
+ > .@{inputClass}:first-child,
+ &-addon:first-child {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+
+ // Reset Select's style in addon
+ .@{ant-prefix}-select .@{ant-prefix}-select-selection {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ }
+ }
+
+ > .@{inputClass}-affix-wrapper {
+ &:not(:first-child) .@{inputClass} {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ }
+
+ &:not(:last-child) .@{inputClass} {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ }
+ }
+
+ &-addon:first-child {
+ border-right: 0;
+ }
+ &-addon:last-child {
+ border-left: 0;
+ }
+
+ > .@{inputClass}:last-child,
+ &-addon:last-child {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+
+ // Reset Select's style in addon
+ .@{ant-prefix}-select .@{ant-prefix}-select-selection {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ }
+ }
+
+ // Sizing options
+ &-lg .@{inputClass},
+ &-lg > &-addon {
+ .input-lg();
+ }
+
+ &-sm .@{inputClass},
+ &-sm > &-addon {
+ .input-sm();
+ }
+
+ // Fix https://github.com/ant-design/ant-design/issues/5754
+ &-lg .@{ant-prefix}-select-selection--single {
+ height: @input-height-lg;
+ }
+
+ &-sm .@{ant-prefix}-select-selection--single {
+ height: @input-height-sm;
+ }
+
+ .@{inputClass}-affix-wrapper {
+ display: table-cell;
+ width: 100%;
+ float: left;
+ }
+
+ &&-compact {
+ display: block;
+ .clearfix;
+
+ & > * {
+ border-radius: 0;
+ border-right-width: 0;
+ vertical-align: middle;
+ float: none;
+ display: inline-block;
+ }
+
+ // Undo float for .ant-input-group .ant-input
+ .@{inputClass} {
+ float: none;
+ z-index: auto;
+ }
+
+ // reset border for Select, DatePicker, AutoComplete, Cascader, Mention, TimePicker
+ & > .@{ant-prefix}-select > .@{ant-prefix}-select-selection,
+ & > .@{ant-prefix}-calendar-picker .@{ant-prefix}-input,
+ & > .@{ant-prefix}-select-auto-complete .@{ant-prefix}-input,
+ & > .@{ant-prefix}-cascader-picker .@{ant-prefix}-input,
+ & > .@{ant-prefix}-mention-wrapper .@{ant-prefix}-mention-editor,
+ & > .@{ant-prefix}-time-picker .@{ant-prefix}-time-picker-input {
+ border-radius: 0;
+ border-right-width: 0;
+ }
+
+ & > *:first-child,
+ & > .@{ant-prefix}-select:first-child > .@{ant-prefix}-select-selection,
+ & > .@{ant-prefix}-calendar-picker:first-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-select-auto-complete:first-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-cascader-picker:first-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-mention-wrapper:first-child .@{ant-prefix}-mention-editor,
+ & > .@{ant-prefix}-time-picker:first-child .@{ant-prefix}-time-picker-input {
+ border-top-left-radius: @border-radius-base;
+ border-bottom-left-radius: @border-radius-base;
+ }
+
+ & > *:last-child,
+ & > .@{ant-prefix}-select:last-child > .@{ant-prefix}-select-selection,
+ & > .@{ant-prefix}-calendar-picker:last-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-select-auto-complete:last-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-cascader-picker:last-child .@{ant-prefix}-input,
+ & > .@{ant-prefix}-mention-wrapper:last-child .@{ant-prefix}-mention-editor,
+ & > .@{ant-prefix}-time-picker:last-child .@{ant-prefix}-time-picker-input {
+ border-top-right-radius: @border-radius-base;
+ border-bottom-right-radius: @border-radius-base;
+ border-right-width: 1px;
+ }
+ }
+}
+
+.input-affix-wrapper(@inputClass) {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+
+ .@{inputClass} {
+ z-index: 1;
+ }
+
+ &:hover .@{inputClass}:not(.@{inputClass}-disabled) {
+ .hover();
+ }
+
+ .@{inputClass}-prefix,
+ .@{inputClass}-suffix {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 2;
+ line-height: 0;
+ color: @input-color;
+ }
+
+ .@{inputClass}-prefix {
+ left: @input-padding-horizontal-base;
+ }
+
+ .@{inputClass}-suffix {
+ right: @input-padding-horizontal-base;
+ }
+
+ .@{inputClass}:not(:first-child) {
+ padding-left: @input-padding-horizontal-base + @input-affix-width;
+ }
+
+ .@{inputClass}:not(:last-child) {
+ padding-right: @input-padding-horizontal-base + @input-affix-width;
+ }
+}
diff --git a/components/input/style/search-input.less b/components/input/style/search-input.less
new file mode 100644
index 000000000..6a7ecddb5
--- /dev/null
+++ b/components/input/style/search-input.less
@@ -0,0 +1,58 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+@import "../../button/style/mixin";
+@import "./mixin";
+
+.@{ant-prefix}-input-search-icon {
+ cursor: pointer;
+ transition: all .3s;
+ font-size: 14px;
+ &:hover {
+ color: @input-hover-border-color;
+ }
+}
+
+// code blow is keeped for compatibility
+// for this demo: http://1x.ant.design/components/select/#components-select-demo-search-box
+// do not delete until 3.x
+.@{ant-prefix}-search-input-wrapper {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.@{ant-prefix}-search-input {
+ &.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child,
+ &.@{ant-prefix}-input-group .@{ant-prefix}-select:first-child {
+ border-radius: @border-radius-base;
+ position: absolute;
+ top: -1px;
+ width: 100%;
+ }
+
+ &.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child {
+ padding-right: 36px;
+ }
+
+ .@{ant-prefix}-search-btn {
+ .btn-default;
+ border-radius: 0 @border-radius-base - 1px @border-radius-base - 1px 0;
+ left: -1px;
+ position: relative;
+ border-width: 0 0 0 1px;
+ z-index: 2;
+ padding-left: 8px;
+ padding-right: 8px;
+ &:hover {
+ border-color: @border-color-base;
+ }
+ }
+ &&-focus .@{ant-prefix}-search-btn-noempty,
+ &:hover .@{ant-prefix}-search-btn-noempty {
+ .btn-primary;
+ }
+ .@{ant-prefix}-select-combobox {
+ .@{ant-prefix}-select-selection__rendered {
+ margin-right: 29px;
+ }
+ }
+}
diff --git a/components/style.js b/components/style.js
index 8c8c5c93f..b811afd8e 100644
--- a/components/style.js
+++ b/components/style.js
@@ -9,3 +9,4 @@ import './pagination/style'
import './avatar/style'
import './badge/style'
import './tabs/style'
+import './input/style'
diff --git a/contributors.md b/contributors.md
index 631161c29..bbda32e08 100644
--- a/contributors.md
+++ b/contributors.md
@@ -5,19 +5,19 @@ Button | done
Icon | done
Checkbox | done
Radio | done
-AutoComplete
-Calendar
+Tabs | done
+Tag | done
Carousel
-DatePicker
-Form
+Mention
Input
InputNumber
+AutoComplete
Select
-Tabs
-Tag
-Mention
-TimePicker
Upload
+Form
+Calendar
+DatePicker
+TimePicker
##万
Grid
diff --git a/package.json b/package.json
index 209ef2de3..473e40b41 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
"dependencies": {
"add-dom-event-listener": "^1.0.2",
"eslint-plugin-vue": "^3.13.0",
- "lodash.debounce": "^4.0.8"
+ "lodash.debounce": "^4.0.8",
+ "omit.js": "^1.0.0"
}
}