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 [