add popover

pull/9/head
tangjinzhou 2018-01-12 16:10:41 +08:00
parent 21d84837b5
commit 728e26548f
41 changed files with 923 additions and 149 deletions

View File

@ -0,0 +1,16 @@
export default {
methods: {
setState (state, callback) {
Object.assign(this.$data, state)
this.$nextTick(() => {
callback && callback()
})
},
__emit () { // 直接调用listeners底层组件不需要vueTool记录events
const args = [].slice.call(arguments, 0)
if (args.length && this.$listeners[args[0]]) {
this.$listeners[args[0]](...args.slice(1))
}
},
},
}

View File

@ -0,0 +1,22 @@
const hasProp = (instance, prop) => {
const $options = instance.$options || {}
const propsData = $options.propsData || {}
return prop in propsData
}
const filterProps = (props, propsData = {}) => {
const res = {}
Object.keys(props).forEach((k) => {
if (k in propsData || props[k] !== undefined) {
res[k] = props[k]
}
})
return res
}
const getOptionProps = (instance) => {
const { $options = {}, $props = {}} = instance
return filterProps($props, $options.propsData)
}
export { hasProp, filterProps, getOptionProps }
export default hasProp

View File

@ -102,7 +102,8 @@ export default {
const props = this.$props
if (!props.disabled) {
const source = this.$el
this.$emit('align', source, align(source, props.target(), props.align))
// this.$emit('align', source, align(source, props.target(), props.align))
this.$listeners.align && this.$listeners.align(source, align(source, props.target(), props.align))
}
},
},

View File

@ -1,9 +1,9 @@
<script>
import Align from '../index'
import StateMixin from '../../_util/StateMixin'
import BaseMixin from '../../_util/BaseMixin'
export default {
mixins: [StateMixin],
mixins: [BaseMixin],
data () {
return {
monitor: true,

View File

@ -1,5 +1,5 @@
<script>
import StateMixin from '../_util/StateMixin'
import BaseMixin from '../_util/BaseMixin'
function getNumberArray (num) {
return num
@ -20,7 +20,7 @@ export default {
default: () => ({}),
},
},
mixins: [StateMixin],
mixins: [BaseMixin],
data () {
const { count } = this
return {

View File

@ -13,7 +13,7 @@
</label>
</template>
<script>
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
export default {
name: 'Checkbox',
props: {

View File

@ -8,7 +8,7 @@
</template>
<script>
import Checkbox from './Checkbox.vue'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
export default {
name: 'CheckboxGroup',
props: {

View File

@ -27,3 +27,5 @@ export { default as Badge } from './badge'
export { default as Tabs } from './tabs'
export { default as Input } from './input'
export { default as Popover } from './popover'

View File

@ -2,7 +2,7 @@
import TextArea from './TextArea'
import omit from 'omit.js'
import inputProps from './inputProps'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
function fixControlledValue (value) {
if (typeof value === 'undefined' || value === null) {

View File

@ -2,7 +2,7 @@
import omit from 'omit.js'
import inputProps from './inputProps'
import calculateNodeHeight from './calculateNodeHeight'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
function onNextFrame (cb) {
if (window.requestAnimationFrame) {

View File

@ -1,8 +1,8 @@
<script>
import PropTypes from '../../_util/vue-types'
import MenuMixin from './MenuMixin'
import StateMixin from '../../_util/StateMixin'
import hasProp from '../../_util/hasProp'
import BaseMixin from '../../_util/BaseMixin'
import hasProp from '../../_util/props-util'
import commonPropsType from './commonPropsType'
const Menu = {
@ -14,7 +14,7 @@ const Menu = {
selectable: PropTypes.bool.def(true),
...commonPropsType,
},
mixins: [StateMixin, MenuMixin],
mixins: [BaseMixin, MenuMixin],
data () {
const props = this.$props
@ -79,7 +79,7 @@ const Menu = {
sSelectedKeys,
})
}
this.$emit('select', {
this.__emit('select', {
...selectInfo,
sSelectedKeys,
})
@ -87,7 +87,7 @@ const Menu = {
},
onClick (e) {
this.$emit('click', e)
this.__emit('click', e)
},
onOpenChange (e_) {
@ -119,7 +119,7 @@ const Menu = {
if (!hasProp(this, 'openKeys')) {
this.setState({ sOpenKeys })
}
this.$emit('openChange', sOpenKeys)
this.__emit('openChange', sOpenKeys)
}
},
@ -137,7 +137,7 @@ const Menu = {
sSelectedKeys,
})
}
this.$emit('deselect', {
this.__emit('deselect', {
...selectInfo,
sSelectedKeys,
})

View File

@ -2,7 +2,7 @@
import PropTypes from '../../_util/vue-types'
import KeyCode from '../../_util/KeyCode'
import { noop } from './util'
import StateMixin from '../../_util/StateMixin'
import BaseMixin from '../../_util/BaseMixin'
const MenuItem = {
name: 'MenuItem',
@ -18,41 +18,40 @@ const MenuItem = {
inlineIndent: PropTypes.number.def(24),
level: PropTypes.number.def(1),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'),
// onItemHover: PropTypes.func,
// onSelect: PropTypes.func,
// onClick: PropTypes.func,
// onDeselect: PropTypes.func,
parentMenu: PropTypes.object,
// onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
clearSubMenuTimers: PropTypes.func.def(noop),
},
inject: {
parentMenuContext: { default: undefined },
},
mixins: [StateMixin],
mixins: [BaseMixin],
isMenuItem: true,
beforeDestroy () {
const props = this.$props
this.$emit('destroy', props.eventKey)
this.__emit('destroy', props.eventKey)
},
methods: {
__emit () { // listenersvueToolevents
const args = [].slice.call(arguments, 0)
if (args.length && this.$listeners[args[0]]) {
this.$listeners[args[0]](...args.slice(1))
}
},
onKeyDown (e) {
const keyCode = e.keyCode
if (keyCode === KeyCode.ENTER) {
this.$emit('click', e)
this.__emit('click', e)
return true
}
},
onMouseLeave (e) {
const { eventKey } = this.$props
this.$emit('itemHover', {
this.__emit('itemHover', {
key: eventKey,
hover: false,
})
this.$emit('mouseLeave', {
this.__emit('mouseleave', {
key: eventKey,
domEvent: e,
})
@ -63,11 +62,11 @@ const MenuItem = {
if (parentMenuContext && parentMenuContext.subMenuInstance) {
parentMenuContext.subMenuInstance.clearSubMenuTimers()
}
this.$emit('itemHover', {
this.__emit('itemHover', {
key: eventKey,
hover: true,
})
this.$emit('mouseEnter', {
this.__emit('mouseenter', {
key: eventKey,
domEvent: e,
})
@ -82,15 +81,15 @@ const MenuItem = {
item: this,
domEvent: e,
}
this.$emit('click', info)
this.__emit('click', info)
if (multiple) {
if (selected) {
this.$emit('deselect', info)
this.__emit('deselect', info)
} else {
this.$emit('select', info)
this.__emit('select', info)
}
} else if (!selected) {
this.$emit('select', info)
this.__emit('select', info)
}
},

View File

@ -1,4 +1,4 @@
import hasProp from '../../_util/hasProp'
import hasProp from '../../_util/props-util'
import KeyCode from '../../_util/KeyCode'
import scrollIntoView from 'dom-scroll-into-view'
import { getKeyFromChildrenIndex, loopMenuItem } from './util'

View File

@ -5,7 +5,7 @@ import KeyCode from '../../_util/KeyCode'
import SubPopupMenu from './SubPopupMenu'
import placements from './placements'
import { loopMenuItemRecusively, noop } from './util'
import StateMixin from '../../_util/StateMixin'
import BaseMixin from '../../_util/BaseMixin'
let guid = 0
@ -45,18 +45,11 @@ export default {
subMenuCloseDelay: PropTypes.number.def(0.1),
level: PropTypes.number.def(1),
inlineIndent: PropTypes.number.def(24),
// onDeselect: PropTypes.func,
// onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
// onTitleMouseEnter: PropTypes.func,
// onTitleMouseLeave: PropTypes.func,
// onTitleClick: PropTypes.func,
},
inject: {
parentMenuContext: { default: undefined },
},
mixins: [StateMixin],
mixins: [BaseMixin],
isSubMenu: true,
data () {
return {
@ -73,7 +66,7 @@ export default {
beforeDestroy () {
const { eventKey, parentMenuContext } = this
this.$emit('destroy', eventKey)
this.__emit('destroy', eventKey)
if (parentMenuContext.subMenuInstance === this) {
this.clearSubMenuTimers()
}
@ -95,9 +88,6 @@ export default {
popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px`
}, 0)
},
onDestroy (key) {
this.$emit('destroy', key)
},
onKeyDown (e) {
const keyCode = e.keyCode
@ -142,10 +132,6 @@ export default {
}
},
onOpenChange (e) {
this.$emit('openChange', e)
},
onPopupVisibleChange (visible) {
this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave')
},
@ -156,7 +142,7 @@ export default {
this.setState({
defaultActiveFirst: false,
})
this.$emit('mouseenter', {
this.__emit('mouseenter', {
key,
domEvent: e,
})
@ -170,7 +156,7 @@ export default {
parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuLeaveFn = () => {
// trigger mouseleave
this.$emit('mouseleave', {
this.__emit('mouseleave', {
key: eventKey,
domEvent: e,
})
@ -182,11 +168,11 @@ export default {
onTitleMouseEnter (domEvent) {
const { eventKey: key } = this.$props
this.clearSubMenuTitleLeaveTimer()
this.$emit('itemHover', {
this.__emit('itemHover', {
key,
hover: true,
})
this.$emit('titleMouseenter', {
this.__emit('titleMouseenter', {
key,
domEvent,
})
@ -196,11 +182,11 @@ export default {
const { eventKey, parentMenuContext } = this
parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuTitleLeaveFn = () => {
this.$emit('itemHover', {
this.__emit('itemHover', {
key: eventKey,
hover: false,
})
this.$emit('titleMouseleave', {
this.__emit('titleMouseleave', {
key: eventKey,
domEvent: e,
})
@ -211,7 +197,7 @@ export default {
onTitleClick (e) {
const { triggerSubMenuAction, eventKey } = this.$props
this.$emit('itemClick', {
this.__emit('itemClick', {
key: eventKey,
domEvent: e,
test: 111,
@ -226,15 +212,7 @@ export default {
},
onSubMenuClick (info) {
this.$emit('click', this.addKeyPath(info))
},
onSelect (info) {
this.$emit('select', info)
},
onDeselect (info) {
this.$emit('deselect', info)
this.__emit('click', this.addKeyPath(info))
},
getPrefixCls () {
@ -266,7 +244,7 @@ export default {
triggerOpenChange (open, type) {
const key = this.$props.eventKey
this.onOpenChange({
this.__emit('openChange', {
key,
item: this,
trigger: type,
@ -309,6 +287,7 @@ export default {
renderChildren (children, vShow) {
const props = this.$props
const isOpen = this.isOpen()
const { select, deselect, destroy, openChange } = this.$listeners
const subPopupMenuProps = {
props: {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
@ -332,10 +311,7 @@ export default {
},
on: {
click: this.onSubMenuClick,
select: this.onSelect,
deselect: this.onDeselect,
destroy: this.onDestroy,
openChange: this.onOpenChange,
select, deselect, destroy, openChange,
},
id: this._menuId,
ref: 'menuInstance',

View File

@ -1,7 +1,7 @@
<script>
import PropTypes from '../../_util/vue-types'
import MenuMixin from './MenuMixin'
import StateMixin from '../../_util/StateMixin'
import BaseMixin from '../../_util/BaseMixin'
import commonPropsType from './commonPropsType'
import { noop } from './util'
export default {
@ -10,26 +10,26 @@ export default {
clearSubMenuTimers: PropTypes.func.def(noop),
},
mixins: [MenuMixin, StateMixin],
mixins: [MenuMixin, BaseMixin],
methods: {
onDeselect (selectInfo) {
this.$emit('deselect', selectInfo)
this.__emit('deselect', selectInfo)
},
onSelect (selectInfo) {
this.$emit('select', selectInfo)
this.__emit('select', selectInfo)
},
onClick (e) {
this.$emit('click', e)
this.__emit('click', e)
},
onOpenChange (e) {
this.$emit('openChange', e)
this.__emit('openChange', e)
},
onDestroy (key) {
this.$emit('destroy', key)
this.__emit('destroy', key)
},
getOpenTransitionName () {

View File

View File

@ -0,0 +1,34 @@
---
category: Components
subtitle: 气泡确认框
type: Feedback
title: Popconfirm
---
点击元素,弹出气泡式的确认框。
## 何时使用
目标元素的操作需要用户进一步的确认时,在目标元素附近弹出浮层提示,询问用户。
`confirm` 弹出的全屏居中模态对话框相比,交互形式更轻量。
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| cancelText | 取消按钮文字 | string | 取消 |
| okText | 确认按钮文字 | string | 确定 |
| okType | 确认按钮类型 | string | primary |
| title | 确认框的描述 | string\|function\|slot | 无 |
### 事件
| 事件名称 | 说明 | 回调参数 |
| cancel | 点击取消时触发 | (e) |
| confirm | 点击确认时触发 | (e) |
更多属性请参考 [Tooltip](/components/tooltip/#API)。
## 注意
请确保 `Popconfirm` 的子元素能接受 `mouseenter`、`mouseleave`、`focus`、`click` 事件。

View File

@ -0,0 +1,5 @@
import '../../style/index.less'
// style dependencies
import '../../popover/style'
import '../../button/style'

View File

@ -0,0 +1,42 @@
<template>
<div>
<md>
## 箭头指向
设置了 `arrowPointAtCenter` 箭头将指向目标元素的中心
</md>
<Popover placement="topLeft">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Align edge / 边缘对齐</AntButton>
</Popover>
<Popover placement="topLeft" arrowPointAtCenter>
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Arrow points to center / 箭头指向中心</AntButton>
</Popover>
</div>
</template>
<script>
import { Popover, Button } from 'antd'
export default {
components: {
Popover,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,27 @@
<template>
<div>
<md>
## 基本
最简单的用法浮层的大小由内容区域决定
</md>
<Popover title="Title">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<AntButton type="primary">Hover me</AntButton>
</Popover>
</div>
</template>
<script>
import { Popover, Button } from 'antd'
export default {
components: {
Popover,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,39 @@
<template>
<div>
<md>
## 从浮层内关闭
使用 `visible` 属性控制浮层显示
</md>
<Popover
title="Title"
trigger="click"
v-model="visible"
>
<template slot="content">
<a @click="hide">Close</a>
</template>
<AntButton type="primary">Click me</AntButton>
</Popover>
</div>
</template>
<script>
import { Popover, Button } from 'antd'
export default {
data () {
return {
visible: false,
}
},
methods: {
hide () {
console.log(111)
this.visible = false
},
},
components: {
Popover,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,30 @@
<template>
<div>
<h1>Basic</h1>
<Basic />
<h1>ArrowCenter</h1>
<ArrowCenter />
<h1>Control</h1>
<Control />
<h1>Placement</h1>
<Placement />
<h1>TriggerType</h1>
<TriggerType />
</div>
</template>
<script>
import Basic from './basic'
import ArrowCenter from './arrow-point-at-center'
import Control from './control'
import Placement from './placement'
import TriggerType from './triggerType'
export default {
components: {
Basic,
ArrowCenter,
Control,
Placement,
TriggerType,
},
}
</script>

View File

@ -0,0 +1,184 @@
<template>
<div id="components-popover-demo-placement">
<md>
## 位置
位置有 12 个方向
</md>
<div :style="{ marginLeft: `${buttonWidth}px`, whiteSpace: 'nowrap' }">
<Popover placement="topLeft">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>TL</AntButton>
</Popover>
<Popover placement="top">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Top</AntButton>
</Popover>
<Popover placement="topRight">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>TR</AntButton>
</Popover>
</div>
<div :style="{ width: `${buttonWidth}px`, float: 'left' }">
<Popover placement="leftTop">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>LT</AntButton>
</Popover>
<Popover placement="left">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Left</AntButton>
</Popover>
<Popover placement="leftBottom">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>LB</AntButton>
</Popover>
</div>
<div :style="{ width: `${buttonWidth}px`, marginLeft: `${buttonWidth * 4 + 24 }px`}">
<Popover placement="rightTop">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>RT</AntButton>
</Popover>
<Popover placement="right">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Right</AntButton>
</Popover>
<Popover placement="rightBottom">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>RB</AntButton>
</Popover>
</div>
<div :style="{ marginLeft: `${buttonWidth}px`, clear: 'both', whiteSpace: 'nowrap' }">
<Popover placement="bottomLeft">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>BL</AntButton>
</Popover>
<Popover placement="bottom">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>Bottom</AntButton>
</Popover>
<Popover placement="bottomRight">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<template slot="title">
<span>Title</span>
</template>
<AntButton>BR</AntButton>
</Popover>
</div>
</div>
</template>
<script>
import { Popover, Button } from 'antd'
export default {
data () {
return {
buttonWidth: 70,
}
},
components: {
Popover,
AntButton: Button,
},
}
</script>
<style>
#components-popover-demo-placement .ant-btn {
width: 70px;
text-align: center;
padding: 0;
margin-right: 8px;
margin-bottom: 8px;
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<div>
<md>
## 三种触发方式
鼠标移入聚集点击
</md>
<Popover title="Title" trigger="hover">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<AntButton type="primary">Hover me</AntButton>
</Popover>
<Popover title="Title" trigger="focus">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<AntButton type="primary">Focus me</AntButton>
</Popover>
<Popover title="Title" trigger="click">
<template slot="content">
<div>
<p>Content</p>
<p>Content</p>
</div>
</template>
<AntButton type="primary">Click me</AntButton>
</Popover>
</div>
</template>
<script>
import { Popover, Button } from 'antd'
export default {
components: {
Popover,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,67 @@
<script>
import Tooltip from '../tooltip'
import abstractTooltipProps from '../tooltip/abstractTooltipProps'
import PropTypes from '../_util/vue-types'
import { getOptionProps } from '../_util/props-util'
export default {
name: 'popover',
props: {
...abstractTooltipProps,
prefixCls: PropTypes.string.def('ant-popover'),
transitionName: PropTypes.string.def('zoom-big'),
content: PropTypes.any,
title: PropTypes.any,
},
model: {
prop: 'visible',
event: 'change',
},
methods: {
getPopupDomNode () {
return this.$refs.tooltip.getPopupDomNode()
},
getOverlay (h) {
const { title, prefixCls, content, $slots } = this
return (
<div>
{(title || $slots.title) &&
<div class={`${prefixCls}-title`}>
{typeof title === 'function' ? title(h) : title}
{$slots.title}
</div>
}
<div class={`${prefixCls}-inner-content`}>
{typeof content === 'function' ? content(h) : content}
{$slots.content}
</div>
</div>
)
},
},
render (h) {
const props = getOptionProps(this)
delete props.title
delete props.content
const tooltipProps = {
props: {
...props,
},
ref: 'tooltip',
on: this.$listeners,
}
return (
<Tooltip
{...tooltipProps}
>
<template slot='title'>
{this.getOverlay(h)}
</template>
{this.$slots.default}
</Tooltip>
)
},
}
</script>

View File

@ -0,0 +1,27 @@
---
category: Components
subtitle: 气泡卡片
type: Data Display
title: Popover
---
点击/鼠标移入元素,弹出气泡式的卡片浮层。
## 何时使用
当目标元素有进一步的描述和相关操作时,可以收纳到卡片中,根据用户的操作行为进行展现。
`Tooltip` 的区别是,用户可以对浮层上的元素进行操作,因此它可以承载更复杂的内容,比如链接或按钮等。
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| content | 卡片内容 | string\|function\|slot | 无 |
| title | 卡片标题 | string\|function\|slot | 无 |
更多属性请参考 [Tooltip](/components/tooltip/#API)。
## 注意
请确保 `Popover` 的子元素能接受 `mouseenter`、`mouseleave`、`focus`、`click` 事件。

View File

@ -0,0 +1,2 @@
import '../../style/index.less'
import './index.less'

View File

@ -0,0 +1,221 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@popover-prefix-cls: ~"@{ant-prefix}-popover";
.@{popover-prefix-cls} {
position: absolute;
top: 0;
left: 0;
z-index: @zindex-popover;
cursor: auto;
user-select: text;
white-space: normal;
font-size: @font-size-base;
line-height: @line-height-base;
font-weight: normal;
text-align: left;
&:after {
content: "";
position: absolute;
background: rgba(255, 255, 255, 0.01);
}
&-hidden {
display: none;
}
// Offset the popover to account for the popover arrow
&-placement-top,
&-placement-topLeft,
&-placement-topRight {
padding-bottom: @popover-distance;
}
&-placement-right,
&-placement-rightTop,
&-placement-rightBottom {
padding-left: @popover-distance;
}
&-placement-bottom,
&-placement-bottomLeft,
&-placement-bottomRight {
padding-top: @popover-distance;
}
&-placement-left,
&-placement-leftTop,
&-placement-leftBottom {
padding-right: @popover-distance;
}
&-inner {
background-color: @popover-bg;
background-clip: padding-box;
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
}
&-title {
min-width: @popover-min-width;
margin: 0; // reset heading margin
padding: 8px 16px;
min-height: 32px;
border-bottom: 1px solid @border-color-split;
color: @popover-color;
font-weight: 500;
}
&-inner-content {
padding: 8px 16px;
color: @popover-color;
}
&-message {
padding: 8px 0 16px;
font-size: @font-size-base;
color: @popover-color;
> .@{iconfont-css-prefix} {
color: @warning-color;
line-height: 17px;
position: absolute;
}
&-title {
padding-left: 20px;
}
}
&-buttons {
text-align: right;
margin-bottom: 8px;
button {
margin-left: 8px;
}
}
// Arrows
// .popover-arrow is outer, .popover-arrow:after is inner
&-arrow {
&,
&:after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
}
&-arrow {
border-width: @popover-arrow-outer-width;
}
&-arrow:after {
border-width: @popover-arrow-width;
content: "";
}
&-placement-top > &-content > &-arrow,
&-placement-topLeft > &-content > &-arrow,
&-placement-topRight > &-content > &-arrow {
border-bottom-width: 0;
border-top-color: @popover-arrow-outer-color;
bottom: @popover-distance - @popover-arrow-outer-width;
&:after {
content: " ";
bottom: 1px;
margin-left: -@popover-arrow-width;
border-bottom-width: 0;
border-top-color: @popover-arrow-color;
}
}
&-placement-top > &-content > &-arrow {
left: 50%;
margin-left: -@popover-arrow-outer-width;
}
&-placement-topLeft > &-content > &-arrow {
left: 16px;
}
&-placement-topRight > &-content > &-arrow {
right: 16px;
}
&-placement-right > &-content > &-arrow,
&-placement-rightTop > &-content > &-arrow,
&-placement-rightBottom > &-content > &-arrow {
left: @popover-distance - @popover-arrow-outer-width;
border-left-width: 0;
border-right-color: @popover-arrow-outer-color;
&:after {
content: " ";
left: 1px;
bottom: -@popover-arrow-width;
border-left-width: 0;
border-right-color: @popover-arrow-color;
}
}
&-placement-right > &-content > &-arrow {
top: 50%;
margin-top: -@popover-arrow-outer-width;
}
&-placement-rightTop > &-content > &-arrow {
top: 12px;
}
&-placement-rightBottom > &-content > &-arrow {
bottom: 12px;
}
&-placement-bottom > &-content > &-arrow,
&-placement-bottomLeft > &-content > &-arrow,
&-placement-bottomRight > &-content > &-arrow {
border-top-width: 0;
border-bottom-color: @popover-arrow-outer-color;
top: @popover-distance - @popover-arrow-outer-width;
&:after {
content: " ";
top: 1px;
margin-left: -@popover-arrow-width;
border-top-width: 0;
border-bottom-color: @popover-arrow-color;
}
}
&-placement-bottom > &-content > &-arrow {
left: 50%;
margin-left: -@popover-arrow-outer-width;
}
&-placement-bottomLeft > &-content > &-arrow {
left: 16px;
}
&-placement-bottomRight > &-content > &-arrow {
right: 16px;
}
&-placement-left > &-content > &-arrow,
&-placement-leftTop > &-content > &-arrow,
&-placement-leftBottom > &-content > &-arrow {
right: @popover-distance - @popover-arrow-outer-width;
border-right-width: 0;
border-left-color: @popover-arrow-outer-color;
&:after {
content: " ";
right: 1px;
border-right-width: 0;
border-left-color: @popover-arrow-color;
bottom: -@popover-arrow-width;
}
}
&-placement-left > &-content > &-arrow {
top: 50%;
margin-top: -@popover-arrow-outer-width;
}
&-placement-leftTop > &-content > &-arrow {
top: 12px;
}
&-placement-leftBottom > &-content > &-arrow {
bottom: 12px;
}
}

View File

@ -13,7 +13,7 @@
</label>
</template>
<script>
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
export default {
name: 'Radio',
props: {

View File

@ -3,7 +3,7 @@ import Star from './Star.vue'
import Icon from '../icon'
import { getOffsetLeft } from './util'
import { cloneVNodes } from '../_util/vnode'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
export default {
name: 'Rate',

View File

@ -11,5 +11,6 @@ import './badge/style'
import './tabs/style'
import './input/style'
import './tooltip/style'
import './popover/style'
import './menu/style'

View File

@ -3,7 +3,7 @@ import Icon from '../icon'
import KeyCode from './KeyCode'
import TabContent from './TabContent'
import ScrollableInkTabBar from './ScrollableInkTabBar'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
function getDefaultActiveKey (t) {
let activeKey
t.$slots.default && t.$slots.default.forEach(({ componentOptions = {}, key: tabKey }) => {

View File

@ -1,7 +1,7 @@
<script>
import Tabs from './Tabs'
import isFlexSupported from '../_util/isFlexSupported'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
export default {
props: {
prefixCls: { type: String, default: 'ant-tabs' },

View File

@ -0,0 +1,19 @@
import PropTypes from '../_util/vue-types'
export default {
trigger: PropTypes.oneOf(['hover', 'focus', 'click']).def('hover'),
visible: PropTypes.bool,
placement: PropTypes.oneOf(['top', 'left', 'right', 'bottom',
'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
'leftTop', 'leftBottom', 'rightTop', 'rightBottom']).def('top'),
transitionName: PropTypes.string.def('zoom-big-fast'),
// onVisibleChange: PropTypes.func,
overlayStyle: PropTypes.object.def({}),
overlayClassName: PropTypes.string,
prefixCls: PropTypes.string.def('ant-tooltip'),
mouseEnterDelay: PropTypes.number.def(0.1),
mouseLeaveDelay: PropTypes.number.def(0.1),
getTooltipContainer: PropTypes.func,
getPopupContainer: PropTypes.func,
arrowPointAtCenter: PropTypes.bool.def(false),
autoAdjustOverflow: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).def(true),
}

View File

@ -0,0 +1,45 @@
---
category: Components
subtitle: 文字提示
type: Data Display
title: Tooltip
---
简单的文字提示气泡框。
## 何时使用
鼠标移入则显示提示,移出消失,气泡浮层不承载复杂文本和操作。
可用来代替系统默认的 `title` 提示,提供一个`按钮/文字/操作`的文案解释。
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| title | 提示文字 | `string` `function` `slot` | 无 |
### 共同的 API
以下 API 为 Tooltip、Popconfirm、Popover 共享的 API。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| arrowPointAtCenter | 箭头是否指向目标元素中 | boolean | `false` |
| autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` |
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | Function(triggerNode) | () => document.body |
| mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip单位秒 | number | 0 |
| mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip单位秒 | number | 0.1 |
| overlayClassName | 卡片类名 | string | 无 |
| overlayStyle | 卡片样式 | object | 无 |
| placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | top |
| trigger | 触发行为,可选 `hover/focus/click` | string | hover |
| visible(v-model) | 用于手动控制浮层显隐 | boolean | false |
### 事件
| 事件名称 | 说明 | 回调参数 |
| visibleChange | 显示隐藏变换时触发 | (visible) |
## 注意
请确保 `Tooltip` 的子元素能接受 `mouseenter`、`mouseleave`、`focus`、`click` 事件。

View File

@ -2,10 +2,11 @@
import PropTypes from '../../_util/vue-types'
import Trigger from '../../trigger'
import { placements } from './placements'
import hasProp from '../../_util/hasProp'
import hasProp from '../../_util/props-util'
function noop () {}
export default {
props: {
trigger: PropTypes.any.def(['hover']),
trigger: PropTypes.any.def('hover'),
defaultVisible: PropTypes.bool,
visible: PropTypes.bool,
placement: PropTypes.string.def('right'),
@ -14,7 +15,6 @@ export default {
PropTypes.object,
]),
animation: PropTypes.any,
// onVisibleChange: PropTypes.func,
afterVisibleChange: PropTypes.func.def(() => {}),
overlay: PropTypes.any,
overlayStyle: PropTypes.object,
@ -47,12 +47,6 @@ export default {
getPopupDomNode () {
return this.$refs.trigger.getPopupDomNode()
},
onVisibleChange (val) {
this.$emit('visibleChange', val)
},
onPopupAlign () {
this.$emit('popupAlign', ...arguments)
},
},
render (h) {
const {
@ -79,7 +73,6 @@ export default {
popupPlacement: placement,
popupAlign: align,
getPopupContainer: getTooltipContainer,
// onPopupVisibleChange: onVisibleChange,
afterPopupVisibleChange: afterVisibleChange,
popupTransitionName: transitionName,
popupAnimation: animation,
@ -91,8 +84,8 @@ export default {
...extraProps,
},
on: {
popupVisibleChange: this.onVisibleChange,
popupAlign: this.onPopupAlign,
popupVisibleChange: this.$listeners.visibleChange || noop,
popupAlign: this.$listeners.popupAlign || noop,
},
ref: 'trigger',
}

View File

@ -3,7 +3,8 @@ import { cloneElement, isValidElement, getClass, getStyle } from '../_util/vnode
import RcTooltip from './src/tooltip'
import getPlacements from './placements'
import PropTypes from '../_util/vue-types'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
import abstractTooltipProps from './abstractTooltipProps'
const splitObject = (obj, keys) => {
const picked = {}
@ -20,23 +21,12 @@ const splitObject = (obj, keys) => {
export default {
name: 'Tooltip',
props: {
trigger: PropTypes.oneOf(['hover', 'focus', 'click']).def(['hover']),
visible: PropTypes.bool,
...abstractTooltipProps,
title: PropTypes.any,
placement: PropTypes.oneOf(['top', 'left', 'right', 'bottom',
'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
'leftTop', 'leftBottom', 'rightTop', 'rightBottom']).def('top'),
transitionName: PropTypes.string.def('zoom-big-fast'),
// onVisibleChange: PropTypes.func,
overlayStyle: PropTypes.object,
overlayClassName: PropTypes.string,
prefixCls: PropTypes.string.def('ant-tooltip'),
mouseEnterDelay: PropTypes.number.def(0.1),
mouseLeaveDelay: PropTypes.number.def(0.1),
getTooltipContainer: PropTypes.func,
getPopupContainer: PropTypes.func,
arrowPointAtCenter: PropTypes.bool.def(false),
autoAdjustOverflow: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).def(true),
},
model: {
prop: 'visible',
event: 'change',
},
data () {
return {
@ -54,7 +44,7 @@ export default {
this.sVisible = this.isNoTitle() ? false : visible
}
if (!this.isNoTitle()) {
this.$emit('visibleChange', visible)
this.$emit('change', visible)
}
},
@ -164,7 +154,9 @@ export default {
if (!hasProp(this, 'visible') && this.isNoTitle()) {
sVisible = false
}
if (!children) {
return null
}
const child = this.getDisabledCompatibleChildren(isValidElement(children) ? children : <span>{children}</span>)
const childCls = {
[openClassName || `${prefixCls}-open`]: true,

View File

@ -3,6 +3,7 @@ import PropTypes from '../_util/vue-types'
import Align from '../align'
import PopupInner from './PopupInner'
import LazyRenderBox from './LazyRenderBox'
import { noop } from './utils'
export default {
props: {
@ -60,7 +61,7 @@ export default {
popupDomNode.className = popupDomNode.className.replace(this.currentAlignClassName, currentAlignClassName)
this.currentAlignClassName = currentAlignClassName
}
this.$emit('align', popupDomNode, align)
this.$listeners.align && this.$listeners.align(popupDomNode, align)
},
getPopupDomNode () {
@ -93,20 +94,15 @@ export default {
getClassName (currentAlignClassName) {
return `${this.$props.prefixCls} ${this.$props.popupClassName} ${currentAlignClassName}`
},
onMouseEnter (e) {
this.$emit('mouseenter', e)
},
onMouseLeave (e) {
this.$emit('mouseleave', e)
},
afterLeave (el) {
if (this.destroyPopupOnHide) {
this.destroyPopup = true
}
},
getPopupElement () {
const { $props: props, onMouseEnter, onMouseLeave, $slots } = this
const { $props: props, $slots, $listeners } = this
const { align, visible, prefixCls, animation } = props
const { mouseenter, mouseleave } = $listeners
this.currentAlignClassName = this.currentAlignClassName || props.getClassNameFromAlign(align)
const className = this.getClassName(this.currentAlignClassName)
// const hiddenClassName = `${prefixCls}-hidden`
@ -121,8 +117,8 @@ export default {
},
class: `${className}`,
on: {
mouseenter: onMouseEnter,
mouseleave: onMouseLeave,
mouseenter: mouseenter || noop,
mouseleave: mouseleave || noop,
},
ref: 'popupInstance',
style: { ...this.getZIndexStyle() },

View File

@ -8,22 +8,14 @@ export default {
prefixCls: PropTypes.string,
visible: PropTypes.bool,
},
methods: {
onMouseEnter (e) {
this.$emit('mouseenter', e)
},
onMouseLeave (e) {
this.$emit('mouseleave', e)
},
},
render () {
const { prefixCls, visible } = this.$props
const { onMouseEnter, onMouseLeave } = this
const { $listeners } = this
const divProps = {
on: $listeners,
}
return (
<div
onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave}
>
<div {...divProps}>
<LazyRenderBox class={`${prefixCls}-content`} visible={visible}>
{this.$slots.default}
</LazyRenderBox>

View File

@ -1,12 +1,12 @@
<script>
import PropTypes from '../_util/vue-types'
import contains from '../_util/Dom/contains'
import hasProp from '../_util/hasProp'
import hasProp from '../_util/props-util'
import addEventListener from '../_util/Dom/addEventListener'
import warning from '../_util/warning'
import Popup from './Popup'
import { getAlignFromPlacement, getPopupClassNameFromAlign, noop } from './utils'
import StateMixin from '../_util/StateMixin'
import BaseMixin from '../_util/BaseMixin'
import { cloneElement, cloneVNode } from '../_util/vnode'
function returnEmptyString () {
@ -62,7 +62,7 @@ export default {
maskAnimation: PropTypes.string,
},
mixins: [StateMixin],
mixins: [BaseMixin],
data () {
const props = this.$props
let popupVisible
@ -282,9 +282,6 @@ export default {
}
return popupAlign
},
onPopupAlign () {
this.$emit('popupAlign', ...arguments)
},
getComponent (h) {
const mouseProps = {}
if (this.isMouseEnterToShow()) {
@ -294,7 +291,7 @@ export default {
mouseProps.mouseleave = this.onPopupMouseleave
}
const { prefixCls, destroyPopupOnHide, sPopupVisible,
popupStyle, popupClassName, action, onPopupAlign,
popupStyle, popupClassName, action,
popupAnimation, handleGetPopupClassFromAlign, getRootDomNode,
mask, zIndex, popupTransitionName, getPopupAlign,
maskAnimation, maskTransitionName, popup, $slots, getContainer } = this
@ -317,7 +314,7 @@ export default {
popupClassName,
},
on: {
align: onPopupAlign,
align: this.$listeners.popupAlign || noop,
...mouseProps,
},
ref: 'popup',
@ -358,7 +355,7 @@ export default {
})
this.$forceUpdate()
}
this.$emit('popupVisibleChange', sPopupVisible)
this.$listeners.popupVisibleChange && this.$listeners.popupVisibleChange(sPopupVisible)
}
},

View File

@ -10,6 +10,7 @@ Tag | done
ToolTip | done
Popconfirm
Popover
Menu
Carousel
Mention
Input | done |select完成后补全demo
@ -31,7 +32,6 @@ BackTop
Dropdown
Layout
message
Menu
Modal
notification
Anchor