diff --git a/components/vc-dropdown/assets/index.less b/components/vc-dropdown/assets/index.less new file mode 100644 index 000000000..531cbda9c --- /dev/null +++ b/components/vc-dropdown/assets/index.less @@ -0,0 +1,218 @@ +@dropdownPrefixCls: rc-dropdown; + +@font-face { + font-family: 'anticon'; + src: url('//at.alicdn.com/t/font_1434092639_4910953.eot'); + /* IE9*/ + src: url('//at.alicdn.com/t/font_1434092639_4910953.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('//at.alicdn.com/t/font_1434092639_4910953.woff') format('woff'), /* chrome、firefox */ url('//at.alicdn.com/t/font_1434092639_4910953.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/ url('//at.alicdn.com/t/font_1434092639_4910953.svg#iconfont') format('svg'); + /* iOS 4.1- */ +} + +.@{dropdownPrefixCls} { + position: absolute; + left: -9999px; + top: -9999px; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-weight: normal; + line-height: 1.5; + + &-hidden { + display: none; + } + + &-menu { + outline: none; + position: relative; + list-style-type: none; + padding: 0; + margin: 2px 0 0 0; + text-align: left; + background-color: #fff; + border-radius: 3px; + box-shadow: 0 1px 5px #ccc; + background-clip: padding-box; + border: 1px solid #ccc; + + > li { + margin: 0; + padding: 0; + } + + &:before { + content: ""; + position: absolute; + top: -4px; + left: 0; + width: 100%; + height: 4px; + background: rgb(255, 255, 255); + background: rgba(255, 255, 255, 0.01); + } + + & > &-item { + position: relative; + display: block; + padding: 7px 10px; + clear: both; + font-size: 12px; + font-weight: normal; + color: #666666; + white-space: nowrap; + + &:hover, &-active, &-selected { + background-color: #ebfaff; + } + + &-selected { + position: relative; + &:after { + content: '\e613'; + font-family: 'anticon'; + font-weight: bold; + position: absolute; + top: 6px; + right: 16px; + color: #3CB8F0; + } + } + + &-disabled { + color: #ccc; + cursor: not-allowed; + pointer-events: none; + + &:hover { + color: #ccc; + background-color: #fff; + cursor: not-allowed; + } + } + + &:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } + + &:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + + &-divider { + height: 1px; + margin: 1px 0; + overflow: hidden; + background-color: #e5e5e5; + line-height: 0; + } + } + } + + .effect() { + animation-duration: 0.3s; + animation-fill-mode: both; + transform-origin: 0 0; + display: block !important; + } + + &-slide-up-enter,&-slide-up-appear { + .effect(); + opacity: 0; + animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); + animation-play-state: paused; + } + + &-slide-up-leave { + .effect(); + opacity: 1; + animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); + animation-play-state: paused; + } + + &-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft, + &-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft, + &-slide-up-enter&-slide-up-enter-active&-placement-bottomCenter, + &-slide-up-appear&-slide-up-appear-active&-placement-bottomCenter, + &-slide-up-enter&-slide-up-enter-active&-placement-bottomRight, + &-slide-up-appear&-slide-up-appear-active&-placement-bottomRight { + animation-name: rcDropdownSlideUpIn; + animation-play-state: running; + } + + &-slide-up-enter&-slide-up-enter-active&-placement-topLeft, + &-slide-up-appear&-slide-up-appear-active&-placement-topLeft, + &-slide-up-enter&-slide-up-enter-active&-placement-topCenter, + &-slide-up-appear&-slide-up-appear-active&-placement-topCenter, + &-slide-up-enter&-slide-up-enter-active&-placement-topRight, + &-slide-up-appear&-slide-up-appear-active&-placement-topRight { + animation-name: rcDropdownSlideDownIn; + animation-play-state: running; + } + + &-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft, + &-slide-up-leave&-slide-up-leave-active&-placement-bottomCenter, + &-slide-up-leave&-slide-up-leave-active&-placement-bottomRight { + animation-name: rcDropdownSlideUpOut; + animation-play-state: running; + } + + &-slide-up-leave&-slide-up-leave-active&-placement-topLeft, + &-slide-up-leave&-slide-up-leave-active&-placement-topCenter, + &-slide-up-leave&-slide-up-leave-active&-placement-topRight { + animation-name: rcDropdownSlideDownOut; + animation-play-state: running; + } + + @keyframes rcDropdownSlideUpIn { + 0% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); + } + 100% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } + } + @keyframes rcDropdownSlideUpOut { + 0% { + opacity: 1; + transform-origin: 0% 0%; + transform: scaleY(1); + } + 100% { + opacity: 0; + transform-origin: 0% 0%; + transform: scaleY(0); + } + } + + @keyframes rcDropdownSlideDownIn { + 0% { + opacity: 0; + transform-origin: 0% 100%; + transform: scaleY(0); + } + 100% { + opacity: 1; + transform-origin: 0% 100%; + transform: scaleY(1); + } + } + @keyframes rcDropdownSlideDownOut { + 0% { + opacity: 1; + transform-origin: 0% 100%; + transform: scaleY(1); + } + 100% { + opacity: 0; + transform-origin: 0% 100%; + transform: scaleY(0); + } + } +} diff --git a/components/vc-dropdown/demo/simple.jsx b/components/vc-dropdown/demo/simple.jsx new file mode 100644 index 000000000..0e98e3a11 --- /dev/null +++ b/components/vc-dropdown/demo/simple.jsx @@ -0,0 +1,39 @@ +import Menu, { Item as MenuItem, Divider } from '../../vc-menu/index'; +import Dropdown from '../src/index.js'; +import '../assets/index.less'; + +export default { + data() { + return {}; + }, + methods: { + onSelect({ key }) { + console.log(`${key} selected`); + }, + onVisibleChange(visible) { + console.log(visible); + }, + }, + render() { + return ( + <div style="margin: 100px"> + <div style="height: 100px"/> + <div> + <Dropdown + trigger={['click']} + animation="slide-up" + onVisibleChange={this.onVisibleChange} + > + <Menu slot="overlay" onSelect={this.onSelect}> + <MenuItem disabled>disabled</MenuItem> + <MenuItem key="1">one</MenuItem> + <Divider /> + <MenuItem key="2">two</MenuItem> + </Menu> + <button style="width: 100px">open</button> + </Dropdown> + </div> + </div> + ); + }, +}; diff --git a/components/vc-dropdown/src/Dropdown.jsx b/components/vc-dropdown/src/Dropdown.jsx index 86e43b29d..4365c622f 100644 --- a/components/vc-dropdown/src/Dropdown.jsx +++ b/components/vc-dropdown/src/Dropdown.jsx @@ -1,3 +1,4 @@ +import classNames from 'classnames'; import PropTypes from '../../_util/vue-types'; import Trigger from '../../vc-trigger'; import placements from './placements'; @@ -12,10 +13,15 @@ export default { prefixCls: PropTypes.string.def('rc-dropdown'), transitionName: PropTypes.string, overlayClassName: PropTypes.string.def(''), + openClassName: PropTypes.string, animation: PropTypes.any, align: PropTypes.object, overlayStyle: PropTypes.object.def({}), placement: PropTypes.string.def('bottomLeft'), + overlay: PropTypes.oneOfType([ + PropTypes.any, + PropTypes.func, + ]), trigger: PropTypes.array.def(['hover']), alignPoint: PropTypes.bool, showAction: PropTypes.array.def([]), @@ -77,9 +83,21 @@ export default { return !alignPoint; }, + getOverlayElement() { + const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay; + let overlayElement; + if (typeof overlay === 'function') { + overlayElement = overlay(); + } else { + overlayElement = overlay; + } + return overlayElement; + }, + getMenuElement() { const { onClick, prefixCls, $slots } = this; this.childOriginEvents = getEvents($slots.overlay[0]); + const overlayElement = this.getOverlayElement(); const extraOverlayProps = { props: { prefixCls: `${prefixCls}-menu`, @@ -89,13 +107,32 @@ export default { click: onClick, }, }; + if (typeof overlayElement.type === 'string') { + delete extraOverlayProps.props.prefixCls; + } return cloneElement($slots.overlay[0], extraOverlayProps); }, + getMenuElementOrLambda() { + const overlay = this.overlay || this.$slots.overlay || this.$scopedSlots.overlay; + if (typeof overlay === 'function') { + return this.getMenuElement; + } + return this.getMenuElement(); + }, + getPopupDomNode() { return this.$refs.trigger.getPopupDomNode(); }, + getOpenClassName() { + const { openClassName, prefixCls } = this.$props; + if (openClassName !== undefined) { + return openClassName; + } + return `${prefixCls}-open`; + }, + afterVisibleChange(visible) { if (visible && this.getMinOverlayWidthMatchTrigger()) { const overlayNode = this.getPopupDomNode(); @@ -112,6 +149,12 @@ export default { } } }, + + renderChildren() { + const children = this.$slots.default && this.$slots.default[0]; + const { sVisible } = this; + return (sVisible && children) ? cloneElement(children, { class: this.getOpenClassName() }) : children; + }, }, render() { @@ -160,7 +203,7 @@ export default { const child = this.$slots.default && this.$slots.default[0]; return ( <Trigger {...triggerProps}> - {child && !child.tag ? <span>{child}</span> : child} + {this.renderChildren()} <template slot="popup">{this.$slots.overlay && this.getMenuElement()}</template> </Trigger> ); diff --git a/components/vc-dropdown/src/index.js b/components/vc-dropdown/src/index.js index 96887dd88..866626140 100644 --- a/components/vc-dropdown/src/index.js +++ b/components/vc-dropdown/src/index.js @@ -1,3 +1,3 @@ -// base in 2.2.1 +// base in 2.4.1 import Dropdown from './Dropdown'; export default Dropdown;