diff --git a/components/_util/props-util.js b/components/_util/props-util.js
index 81a145734..6ee8a3f30 100644
--- a/components/_util/props-util.js
+++ b/components/_util/props-util.js
@@ -149,5 +149,6 @@ export {
getKey,
getAttrs,
getValueByProp,
+ parseStyleText,
}
export default hasProp
diff --git a/components/_util/vnode.js b/components/_util/vnode.js
index b61daa0fa..59eb2b221 100644
--- a/components/_util/vnode.js
+++ b/components/_util/vnode.js
@@ -1,4 +1,4 @@
-import { filterEmpty } from './props-util'
+import { filterEmpty, parseStyleText } from './props-util'
export function cloneVNode (vnode, deep) {
const componentOptions = vnode.componentOptions
const data = vnode.data
@@ -62,18 +62,45 @@ export function cloneElement (n, nodeProps, deep) {
const node = cloneVNode(ele, deep)
const { props = {}, key, on = {}, children } = nodeProps
const data = node.data || {}
- const { style = {},
- class: cls = {},
+ let cls = {}
+ let style = {}
+ const {
attrs = {},
ref,
domProps = {},
+ style: tempStyle = {},
+ class: tempCls = {},
} = nodeProps
+
+ if (typeof data.style === 'string') {
+ style = parseStyleText(data.style)
+ } else {
+ style = { ...data.style, ...style }
+ }
+ if (typeof tempStyle === 'string') {
+ style = { ...style, ...parseStyleText(style) }
+ } else {
+ style = { ...style, ...tempStyle }
+ }
+
+ if (typeof data.class === 'string') {
+ cls[data.class] = true
+ data.class.split(' ').forEach(c => { cls[c.trim()] = true })
+ } else {
+ cls = { ...data.class, ...cls }
+ }
+ if (typeof tempCls === 'string') {
+ tempCls.split(' ').forEach(c => { cls[c.trim()] = true })
+ } else {
+ cls = { ...cls, ...tempCls }
+ }
node.data = Object.assign({}, data, {
- style: { ...data.style, ...style },
+ style,
attrs: { ...data.attrs, ...attrs },
- class: { ...data.class, ...cls },
+ class: cls,
domProps: { ...data.domProps, ...domProps },
})
+
if (node.componentOptions) {
node.componentOptions.propsData = node.componentOptions.propsData || {}
node.componentOptions.listeners = node.componentOptions.listeners || {}
diff --git a/components/cascader/demo/basic.md b/components/cascader/demo/basic.md
new file mode 100644
index 000000000..e9b8df2ad
--- /dev/null
+++ b/components/cascader/demo/basic.md
@@ -0,0 +1,53 @@
+
+
+#### 基本
+省市区级联。
+
+
+
+#### Basic
+Cascade selection box for selecting province/city/district.
+
+
+```html
+
+
+
+
+```
+
diff --git a/components/cascader/index.vue b/components/cascader/index.vue
new file mode 100644
index 000000000..a1e90a80d
--- /dev/null
+++ b/components/cascader/index.vue
@@ -0,0 +1,374 @@
+
\ No newline at end of file
diff --git a/components/cascader/style/index.js b/components/cascader/style/index.js
new file mode 100644
index 000000000..992a50794
--- /dev/null
+++ b/components/cascader/style/index.js
@@ -0,0 +1,5 @@
+import '../../style/index.less'
+import './index.less'
+
+// style dependencies
+import '../../input/style'
diff --git a/components/cascader/style/index.less b/components/cascader/style/index.less
new file mode 100644
index 000000000..f3d3b6ca3
--- /dev/null
+++ b/components/cascader/style/index.less
@@ -0,0 +1,214 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+@import "../../input/style/mixin";
+
+@cascader-prefix-cls: ~"@{ant-prefix}-cascader";
+
+.@{cascader-prefix-cls} {
+ .reset-component;
+
+ &-input.@{ant-prefix}-input {
+ // Add important to fix https://github.com/ant-design/ant-design/issues/5078
+ // because input.less will compile after cascader.less
+ background-color: transparent !important;
+ cursor: pointer;
+ width: 100%;
+ display: block;
+ }
+
+ &-picker {
+ .reset-component;
+ position: relative;
+ display: inline-block;
+ cursor: pointer;
+ background-color: @component-background;
+ border-radius: @border-radius-base;
+ outline: 0;
+
+ &-with-value &-label {
+ color: transparent;
+ }
+
+ &-disabled {
+ cursor: not-allowed;
+ background: @input-disabled-bg;
+ color: @disabled-color;
+ .@{cascader-prefix-cls}-input {
+ cursor: not-allowed;
+ }
+ }
+
+ &:focus .@{cascader-prefix-cls}-input {
+ .active;
+ }
+
+ &-label {
+ position: absolute;
+ left: 0;
+ height: 20px;
+ line-height: 20px;
+ top: 50%;
+ margin-top: -10px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ width: 100%;
+ padding: 0 @control-padding-horizontal;
+ }
+
+ &-clear {
+ opacity: 0;
+ position: absolute;
+ right: @control-padding-horizontal;
+ z-index: 2;
+ background: @component-background;
+ top: 50%;
+ font-size: @font-size-sm;
+ color: @disabled-color;
+ width: 12px;
+ height: 12px;
+ margin-top: -6px;
+ line-height: 12px;
+ cursor: pointer;
+ transition: color 0.3s ease, opacity 0.15s ease;
+ &:hover {
+ color: @text-color-secondary;
+ }
+ }
+
+ &:hover &-clear {
+ opacity: 1;
+ }
+
+ // arrow
+ &-arrow {
+ position: absolute;
+ z-index: 1;
+ top: 50%;
+ right: @control-padding-horizontal;
+ width: 12px;
+ height: 12px;
+ font-size: 12px;
+ margin-top: -6px;
+ line-height: 12px;
+ color: @disabled-color;
+ &:before {
+ transition: transform .2s;
+ }
+ &&-expand {
+ &:before {
+ transform: rotate(180deg);
+ }
+ }
+ }
+ }
+
+ &-picker-small &-picker-clear,
+ &-picker-small &-picker-arrow {
+ right: @control-padding-horizontal-sm;
+ }
+
+ &-menus {
+ font-size: @font-size-base;
+ background: @component-background;
+ position: absolute;
+ z-index: @zindex-dropdown;
+ border-radius: @border-radius-base;
+ box-shadow: @box-shadow-base;
+ white-space: nowrap;
+
+ ul,
+ ol {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ &-empty,
+ &-hidden {
+ display: none;
+ }
+ &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
+ &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft {
+ animation-name: antSlideUpIn;
+ }
+
+ &.slide-up-enter.slide-up-enter-active&-placement-topLeft,
+ &.slide-up-appear.slide-up-appear-active&-placement-topLeft {
+ animation-name: antSlideDownIn;
+ }
+
+ &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft {
+ animation-name: antSlideUpOut;
+ }
+
+ &.slide-up-leave.slide-up-leave-active&-placement-topLeft {
+ animation-name: antSlideDownOut;
+ }
+ }
+ &-menu {
+ display: inline-block;
+ vertical-align: top;
+ min-width: 111px;
+ height: 180px;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ border-right: @border-width-base @border-style-base @border-color-split;
+ overflow: auto;
+ &:first-child {
+ border-radius: @border-radius-base 0 0 @border-radius-base;
+ }
+ &:last-child {
+ border-right-color: transparent;
+ margin-right: -1px;
+ border-radius: 0 @border-radius-base @border-radius-base 0;
+ }
+ &:only-child {
+ border-radius: @border-radius-base;
+ }
+ }
+ &-menu-item {
+ padding: 5px @control-padding-horizontal;
+ line-height: 22px;
+ cursor: pointer;
+ white-space: nowrap;
+ transition: all 0.3s;
+ &:hover {
+ background: @item-hover-bg;
+ }
+ &-disabled {
+ cursor: not-allowed;
+ color: @disabled-color;
+ &:hover {
+ background: transparent;
+ }
+ }
+ &-active:not(&-disabled) {
+ &,
+ &:hover {
+ background: @background-color-base;
+ font-weight: 600;
+ }
+ }
+ &-expand {
+ position: relative;
+ padding-right: 24px;
+ &:after {
+ .iconfont-font("\e61f");
+ .iconfont-size-under-12px(8px);
+ color: @text-color-secondary;
+ position: absolute;
+ right: @control-padding-horizontal;
+ }
+ }
+ &-loading:after {
+ .iconfont-font("\e64d");
+ animation: loadingCircle 1s infinite linear;
+ }
+
+ & &-keyword {
+ color: @highlight-color;
+ }
+ }
+}
diff --git a/components/index.js b/components/index.js
index e39622cde..e295c878f 100644
--- a/components/index.js
+++ b/components/index.js
@@ -91,3 +91,5 @@ export { default as AutoComplete } from './auto-complete'
export { default as Affix } from './affix'
+export { default as Cascader } from './cascader'
+
diff --git a/components/style.js b/components/style.js
index c2076c700..17336063d 100644
--- a/components/style.js
+++ b/components/style.js
@@ -25,3 +25,4 @@ import './select/style'
import './switch/style'
import './auto-complete/style'
import './affix/style'
+import './cascader/style'
diff --git a/examples/routes.js b/examples/routes.js
index 68d799d79..401b22cbd 100644
--- a/examples/routes.js
+++ b/examples/routes.js
@@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
- component: import(`../components/vc-cascader/demo/${d}`),
+ component: import(`../components/cascader/demo/${d}`),
}
}
export default [