pull/9/head
wangxueliang 2018-01-15 10:57:10 +08:00
commit a40f741f95
75 changed files with 1839 additions and 620 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

@ -1,8 +0,0 @@
export default (node, nodeProps) => {
const { props, style, class: cls, attrs, key } = nodeProps
if (node.componentOptions) {
const propsData = node.componentOptions.propsData
Object.assign(propsData, nodeProps)
}
return node
}

View File

@ -0,0 +1,30 @@
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)
}
const getComponentFromProp = (instance, h, prop) => {
const temp = instance[prop]
if (temp !== undefined) {
return typeof temp === 'function' ? temp(h) : temp
}
return instance.$slots[prop]
}
export { hasProp, filterProps, getOptionProps, getComponentFromProp }
export default hasProp

View File

@ -78,11 +78,30 @@ export function cloneElement (n, nodeProps, clone) {
const { style = data.style, const { style = data.style,
class: cls = data.class, class: cls = data.class,
attrs = data.attrs, attrs = data.attrs,
ref,
} = nodeProps } = nodeProps
node.data = Object.assign(data, { style, attrs, class: cls, on: { ...(data.on || {}), ...on }}) node.data = Object.assign(data, { style, attrs, class: cls, on: { ...(data.on || {}), ...on }})
if (key !== undefined) { if (key !== undefined) {
node.key = key node.key = key
node.data.key = key node.data.key = key
} }
if (typeof ref === 'string') {
node.data.ref = ref
}
return node return node
} }
export function getComponentName (opts) {
return opts && (opts.Ctor.options.name || opts.tag)
}
export function isValidElement (ele) {
return !!ele.tag
}
export function getClass (ele) {
return ele.data && (ele.data.class || ele.data.staticClass)
}
export function getStyle (ele) {
return ele.data && (ele.data.style || ele.data.staticStyle)
}

View File

@ -102,7 +102,8 @@ export default {
const props = this.$props const props = this.$props
if (!props.disabled) { if (!props.disabled) {
const source = this.$el 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> <script>
import Align from '../index' import Align from '../index'
import StateMixin from '../../_util/StateMixin' import BaseMixin from '../../_util/BaseMixin'
export default { export default {
mixins: [StateMixin], mixins: [BaseMixin],
data () { data () {
return { return {
monitor: true, monitor: true,

View File

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

View File

@ -2,39 +2,13 @@
import Icon from '../icon' import Icon from '../icon'
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/ const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar) const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar)
import buttonTypes from './buttonTypes'
export default { export default {
name: 'Button', name: 'Button',
__ANT_BUTTON: true,
components: { Icon }, components: { Icon },
props: { props: {
prefixCls: { ...buttonTypes,
default: 'ant-btn',
type: String,
},
type: {
validator (value) {
return ['primary', 'danger', 'dashed', 'ghost', 'default'].includes(value)
},
},
htmlType: {
default: 'button',
validator (value) {
return ['button', 'submit', 'reset'].includes(value)
},
},
icon: String,
shape: {
validator (value) {
return ['circle', 'circle-outline'].includes(value)
},
},
size: {
validator (value) {
return ['small', 'large', 'default'].includes(value)
},
},
loading: [Boolean, Object],
disabled: Boolean,
ghost: Boolean,
}, },
data () { data () {
return { return {

View File

@ -0,0 +1,12 @@
import PropTypes from '../_util/vue-types'
export default {
prefixCls: PropTypes.string.def('ant-btn'),
type: PropTypes.oneOf(['primary', 'danger', 'dashed', 'ghost', 'default']).def('default'),
htmlType: PropTypes.oneOf(['button', 'submit', 'reset']).def('button'),
icon: PropTypes.string,
shape: PropTypes.oneOf(['circle', 'circle-outline']),
size: PropTypes.oneOf(['small', 'large', 'default']).def('default'),
loading: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
disabled: PropTypes.bool,
ghost: PropTypes.bool,
}

View File

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

View File

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

View File

@ -10,7 +10,7 @@ export { default as Grid } from './grid'
export { default as Rate } from './rate' export { default as Rate } from './rate'
export { default as ToolTip } from './tooltip' export { default as Tooltip } from './tooltip'
export { default as Pagination } from './pagination' export { default as Pagination } from './pagination'
@ -29,3 +29,7 @@ export { default as Tabs } from './tabs'
export { default as Input } from './input' export { default as Input } from './input'
export { default as Breadcrumb } from './breadcrumb' export { default as Breadcrumb } from './breadcrumb'
export { default as Popover } from './popover'
export { default as Popconfirm } from './popconfirm'

View File

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

View File

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

View File

@ -1,8 +1,6 @@
<script> <script>
import { cloneElement } from '../../_util/vnode'
import Clone from '../../_util/Clone' import Clone from '../../_util/Clone'
import Menu, { SubMenu, Item as MenuItem, Divider } from '../src/index' import Menu, { SubMenu, Item as MenuItem, Divider } from '../src/index'
import { Icon } from 'antd'
import animate from 'css-animation' import animate from 'css-animation'
function handleSelect (info) { function handleSelect (info) {
@ -74,17 +72,16 @@ export default {
</SubMenu> </SubMenu>
</SubMenu> </SubMenu>
</SubMenu>) </SubMenu>)
const onOpenChange = (value) => {
function onOpenChange (value) { console.log('onOpenChange', value, this.$refs)
console.log('onOpenChange', value)
} }
const commonMenu = () => ( const commonMenu = () => (
<Menu onSelect={handleSelect} onOpenChange={onOpenChange}> <Menu onSelect={handleSelect} onOpenChange={onOpenChange}>
<SubMenu key='1' title={<span>sub menu</span>}> <SubMenu ref='test' key='1' title={<span>sub menu</span>}>
<MenuItem key='1-1'> <MenuItem key='1-1'>
0-1 0-1
</MenuItem> </MenuItem>
<MenuItem key='1-2'>0-2</MenuItem> <MenuItem key='1-2' disabled>0-2</MenuItem>
</SubMenu> </SubMenu>
{nestSubMenu()} {nestSubMenu()}
<MenuItem key='2'>1</MenuItem> <MenuItem key='2'>1</MenuItem>
@ -130,6 +127,7 @@ export default {
<div style={{ margin: '20px', width: '400px' }}><Clone childProps={{ <div style={{ margin: '20px', width: '400px' }}><Clone childProps={{
mode: 'inline', mode: 'inline',
defaultOpenKeys: ['1'], defaultOpenKeys: ['1'],
defaultSelectedKeys: ['1-2', '4-1'],
openAnimation: animation, openAnimation: animation,
}} > }} >
{commonMenu()} {commonMenu()}

View File

@ -33,6 +33,7 @@ export default {
const Tag = this.$props.tag const Tag = this.$props.tag
const tagProps = { const tagProps = {
attr: { ...otherProps, ...this.$attrs }, attr: { ...otherProps, ...this.$attrs },
on: this.$listeners,
} }
return <Tag {...tagProps} class={this.class}>{this.$slots.default}</Tag> return <Tag {...tagProps} class={this.class}>{this.$slots.default}</Tag>
}, },

View File

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

View File

@ -2,7 +2,7 @@
import PropTypes from '../../_util/vue-types' import PropTypes from '../../_util/vue-types'
import KeyCode from '../../_util/KeyCode' import KeyCode from '../../_util/KeyCode'
import { noop } from './util' import { noop } from './util'
import StateMixin from '../../_util/StateMixin' import BaseMixin from '../../_util/BaseMixin'
const MenuItem = { const MenuItem = {
name: 'MenuItem', name: 'MenuItem',
@ -14,44 +14,44 @@ const MenuItem = {
selectedKeys: PropTypes.array, selectedKeys: PropTypes.array,
disabled: PropTypes.bool, disabled: PropTypes.bool,
title: PropTypes.string, title: PropTypes.string,
index: PropTypes.number,
inlineIndent: PropTypes.number.def(24), inlineIndent: PropTypes.number.def(24),
level: PropTypes.number.def(1), level: PropTypes.number.def(1),
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']).def('vertical'), 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, parentMenu: PropTypes.object,
// onDestroy: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
clearSubMenuTimers: PropTypes.func.def(noop), clearSubMenuTimers: PropTypes.func.def(noop),
}, },
inject: { inject: {
parentMenuContext: { default: undefined }, parentMenuContext: { default: undefined },
}, },
mixins: [StateMixin], mixins: [BaseMixin],
isMenuItem: true, isMenuItem: true,
beforeDestroy () { beforeDestroy () {
const props = this.$props const props = this.$props
this.$emit('destroy', props.eventKey) this.__emit('destroy', props.eventKey)
}, },
methods: { 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) { onKeyDown (e) {
const keyCode = e.keyCode const keyCode = e.keyCode
if (keyCode === KeyCode.ENTER) { if (keyCode === KeyCode.ENTER) {
this.$emit('click', e) this.__emit('click', e)
return true return true
} }
}, },
onMouseLeave (e) { onMouseLeave (e) {
const { eventKey } = this.$props const { eventKey } = this.$props
this.$emit('itemHover', { this.__emit('itemHover', {
key: eventKey, key: eventKey,
hover: false, hover: false,
}) })
this.$emit('mouseLeave', { this.__emit('mouseleave', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
}) })
@ -62,11 +62,11 @@ const MenuItem = {
if (parentMenuContext && parentMenuContext.subMenuInstance) { if (parentMenuContext && parentMenuContext.subMenuInstance) {
parentMenuContext.subMenuInstance.clearSubMenuTimers() parentMenuContext.subMenuInstance.clearSubMenuTimers()
} }
this.$emit('itemHover', { this.__emit('itemHover', {
key: eventKey, key: eventKey,
hover: true, hover: true,
}) })
this.$emit('mouseEnter', { this.__emit('mouseenter', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
}) })
@ -81,15 +81,15 @@ const MenuItem = {
item: this, item: this,
domEvent: e, domEvent: e,
} }
this.$emit('click', info) this.__emit('click', info)
if (multiple) { if (multiple) {
if (selected) { if (selected) {
this.$emit('deselect', info) this.__emit('deselect', info)
} else { } else {
this.$emit('select', info) this.__emit('select', info)
} }
} else if (!selected) { } 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 KeyCode from '../../_util/KeyCode'
import scrollIntoView from 'dom-scroll-into-view' import scrollIntoView from 'dom-scroll-into-view'
import { getKeyFromChildrenIndex, loopMenuItem } from './util' import { getKeyFromChildrenIndex, loopMenuItem } from './util'
@ -9,10 +9,8 @@ function allDisabled (arr) {
if (!arr.length) { if (!arr.length) {
return true return true
} }
return arr.every(c => { return arr.every(c => {
const propsData = c.componentOptions.propsData || {} return !!c.disabled
return !!propsData.disabled
}) })
} }
@ -53,10 +51,6 @@ export default {
deep: true, deep: true,
}, },
}, },
created () {
this.instanceArray = []
},
methods: { methods: {
getActiveKey (originalActiveKey) { getActiveKey (originalActiveKey) {
let activeKey = originalActiveKey let activeKey = originalActiveKey
@ -86,23 +80,13 @@ export default {
} }
return activeKey return activeKey
}, },
saveRef (index, subIndex, c) {
if (c) {
if (subIndex !== undefined) {
this.instanceArray[index] = this.instanceArray[index] || []
this.instanceArray[index][subIndex] = c
} else {
this.instanceArray[index] = c
}
}
},
// all keyboard events callbacks run from here at first // all keyboard events callbacks run from here at first
onKeyDown (e, callback) { onKeyDown (e) {
const keyCode = e.keyCode const keyCode = e.keyCode
let handled let handled
this.getFlatInstanceArray().forEach((obj) => { this.getFlatInstanceArray().forEach((obj) => {
if (obj && obj.$props.active) { if (obj && obj.active) {
handled = this.$emit('keydown', e) handled = obj.onKeyDown(e)
} }
}) })
if (handled) { if (handled) {
@ -115,15 +99,11 @@ export default {
if (activeItem) { if (activeItem) {
e.preventDefault() e.preventDefault()
this.setState({ this.setState({
sActiveKey: activeItem.$props.eventKey, sActiveKey: activeItem.eventKey,
}, () => { }, () => {
scrollIntoView(activeItem.$el, this.$el, { scrollIntoView(activeItem.$el, this.$el, {
onlyScrollIfNeeded: true, onlyScrollIfNeeded: true,
}) })
// https://github.com/react-component/menu/commit/9899a9672f6f028ec3cdf773f1ecea5badd2d33e
if (typeof callback === 'function') {
callback(activeItem)
}
}) })
return 1 return 1
} else if (activeItem === undefined) { } else if (activeItem === undefined) {
@ -143,22 +123,13 @@ export default {
}, },
getFlatInstanceArray () { getFlatInstanceArray () {
let instanceArray = this.instanceArray let instance = []
const hasInnerArray = instanceArray.some((a) => { try {
return Array.isArray(a) instance = this.$children[0].$children || []
}) } catch (error) {
if (hasInnerArray) {
instanceArray = []
this.instanceArray.forEach((a) => {
if (Array.isArray(a)) {
instanceArray.push.apply(instanceArray, a)
} else {
instanceArray.push(a)
} }
}) return instance
this.instanceArray = instanceArray
}
return instanceArray
}, },
renderCommonMenuItem (child, i, subIndex, extraProps) { renderCommonMenuItem (child, i, subIndex, extraProps) {
@ -175,7 +146,6 @@ export default {
renderMenuItem: this.renderMenuItem, renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls, rootPrefixCls: props.prefixCls,
index: i, index: i,
// parentMenu: this,
eventKey: key, eventKey: key,
active: !childProps.disabled && isActive, active: !childProps.disabled && isActive,
multiple: props.multiple, multiple: props.multiple,
@ -195,11 +165,7 @@ export default {
destroy: this.onDestroy, destroy: this.onDestroy,
select: this.onSelect, select: this.onSelect,
}, },
// ref: childProps.disabled ? undefined : child.ref,
// ref: childProps.disabled ? undefined
// : createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)),
} }
// !childProps.disabled && this.saveRef(i, subIndex, child.ref)
if (props.mode === 'inline') { if (props.mode === 'inline') {
newChildProps.props.triggerSubMenuAction = 'click' newChildProps.props.triggerSubMenuAction = 'click'
} }
@ -210,7 +176,6 @@ export default {
}, },
renderRoot (props, children = []) { renderRoot (props, children = []) {
this.instanceArray = []
const className = { const className = {
[props.prefixCls]: true, [props.prefixCls]: true,
[props.class]: true, [props.class]: true,
@ -236,7 +201,7 @@ export default {
domProps.attrs.tabIndex = '0' domProps.attrs.tabIndex = '0'
domProps.on.keydown = this.onKeyDown domProps.on.keydown = this.onKeyDown
} }
const newChildren = children.map(this.renderMenuItem) const newChildren = children.map((c, i) => this.renderMenuItem(c, i))
return ( return (
<DOMWrap <DOMWrap
{...domProps} {...domProps}
@ -259,8 +224,7 @@ export default {
// find current activeIndex // find current activeIndex
let activeIndex = -1 let activeIndex = -1
children.every((c, ci) => { children.every((c, ci) => {
const propsData = c.componentOptions.propsData || {} if (c && c.eventKey === sActiveKey) {
if (c && propsData.eventKey === sActiveKey) {
activeIndex = ci activeIndex = ci
return false return false
} }
@ -275,8 +239,7 @@ export default {
let i = start let i = start
for (; ;) { for (; ;) {
const child = children[i] const child = children[i]
const propsData = child.componentOptions.propsData || {} if (!child || child.disabled) {
if (!child || propsData.disabled) {
i = (i + 1 + len) % len i = (i + 1 + len) % len
// complete a loop // complete a loop
if (i === start) { if (i === start) {

View File

@ -5,7 +5,7 @@ import KeyCode from '../../_util/KeyCode'
import SubPopupMenu from './SubPopupMenu' import SubPopupMenu from './SubPopupMenu'
import placements from './placements' import placements from './placements'
import { loopMenuItemRecusively, noop } from './util' import { loopMenuItemRecusively, noop } from './util'
import StateMixin from '../../_util/StateMixin' import BaseMixin from '../../_util/BaseMixin'
let guid = 0 let guid = 0
@ -31,6 +31,7 @@ export default {
multiple: PropTypes.bool, multiple: PropTypes.bool,
active: PropTypes.bool, // TODO: remove active: PropTypes.bool, // TODO: remove
isRootMenu: PropTypes.bool, isRootMenu: PropTypes.bool,
index: PropTypes.number,
// onItemHover: PropTypes.func, // onItemHover: PropTypes.func,
// onSelect: PropTypes.func, // onSelect: PropTypes.func,
triggerSubMenuAction: PropTypes.string, triggerSubMenuAction: PropTypes.string,
@ -44,18 +45,11 @@ export default {
subMenuCloseDelay: PropTypes.number.def(0.1), subMenuCloseDelay: PropTypes.number.def(0.1),
level: PropTypes.number.def(1), level: PropTypes.number.def(1),
inlineIndent: PropTypes.number.def(24), 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: { inject: {
parentMenuContext: { default: undefined }, parentMenuContext: { default: undefined },
}, },
mixins: [StateMixin], mixins: [BaseMixin],
isSubMenu: true, isSubMenu: true,
data () { data () {
return { return {
@ -72,7 +66,7 @@ export default {
beforeDestroy () { beforeDestroy () {
const { eventKey, parentMenuContext } = this const { eventKey, parentMenuContext } = this
this.$emit('destroy', eventKey) this.__emit('destroy', eventKey)
if (parentMenuContext.subMenuInstance === this) { if (parentMenuContext.subMenuInstance === this) {
this.clearSubMenuTimers() this.clearSubMenuTimers()
} }
@ -94,13 +88,10 @@ export default {
popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px` popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px`
}, 0) }, 0)
}, },
onDestroy (key) {
this.$emit('destroy', key)
},
onKeyDown (e) { onKeyDown (e) {
const keyCode = e.keyCode const keyCode = e.keyCode
const menu = this.menuInstance const menu = this.$refs.menuInstance
const isOpen = this.isOpen() const isOpen = this.isOpen()
if (keyCode === KeyCode.ENTER) { if (keyCode === KeyCode.ENTER) {
@ -141,10 +132,6 @@ export default {
} }
}, },
onOpenChange (e) {
this.$emit('openChange', e)
},
onPopupVisibleChange (visible) { onPopupVisibleChange (visible) {
this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave') this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave')
}, },
@ -155,7 +142,7 @@ export default {
this.setState({ this.setState({
defaultActiveFirst: false, defaultActiveFirst: false,
}) })
this.$emit('mouseenter', { this.__emit('mouseenter', {
key, key,
domEvent: e, domEvent: e,
}) })
@ -169,7 +156,7 @@ export default {
parentMenuContext.subMenuInstance = this parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuLeaveFn = () => { parentMenuContext.subMenuLeaveFn = () => {
// trigger mouseleave // trigger mouseleave
this.$emit('mouseleave', { this.__emit('mouseleave', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
}) })
@ -181,11 +168,11 @@ export default {
onTitleMouseEnter (domEvent) { onTitleMouseEnter (domEvent) {
const { eventKey: key } = this.$props const { eventKey: key } = this.$props
this.clearSubMenuTitleLeaveTimer() this.clearSubMenuTitleLeaveTimer()
this.$emit('itemHover', { this.__emit('itemHover', {
key, key,
hover: true, hover: true,
}) })
this.$emit('titleMouseenter', { this.__emit('titleMouseenter', {
key, key,
domEvent, domEvent,
}) })
@ -195,11 +182,11 @@ export default {
const { eventKey, parentMenuContext } = this const { eventKey, parentMenuContext } = this
parentMenuContext.subMenuInstance = this parentMenuContext.subMenuInstance = this
parentMenuContext.subMenuTitleLeaveFn = () => { parentMenuContext.subMenuTitleLeaveFn = () => {
this.$emit('itemHover', { this.__emit('itemHover', {
key: eventKey, key: eventKey,
hover: false, hover: false,
}) })
this.$emit('titleMouseleave', { this.__emit('titleMouseleave', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
}) })
@ -210,7 +197,7 @@ export default {
onTitleClick (e) { onTitleClick (e) {
const { triggerSubMenuAction, eventKey } = this.$props const { triggerSubMenuAction, eventKey } = this.$props
this.$emit('itemClick', { this.__emit('itemClick', {
key: eventKey, key: eventKey,
domEvent: e, domEvent: e,
test: 111, test: 111,
@ -225,15 +212,7 @@ export default {
}, },
onSubMenuClick (info) { onSubMenuClick (info) {
this.$emit('click', this.addKeyPath(info)) this.__emit('click', this.addKeyPath(info))
},
onSelect (info) {
this.$emit('select', info)
},
onDeselect (info) {
this.$emit('deselect', info)
}, },
getPrefixCls () { getPrefixCls () {
@ -265,7 +244,7 @@ export default {
triggerOpenChange (open, type) { triggerOpenChange (open, type) {
const key = this.$props.eventKey const key = this.$props.eventKey
this.onOpenChange({ this.__emit('openChange', {
key, key,
item: this, item: this,
trigger: type, trigger: type,
@ -308,6 +287,7 @@ export default {
renderChildren (children, vShow) { renderChildren (children, vShow) {
const props = this.$props const props = this.$props
const isOpen = this.isOpen() const isOpen = this.isOpen()
const { select, deselect, destroy, openChange } = this.$listeners
const subPopupMenuProps = { const subPopupMenuProps = {
props: { props: {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode, mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
@ -331,10 +311,7 @@ export default {
}, },
on: { on: {
click: this.onSubMenuClick, click: this.onSubMenuClick,
select: this.onSelect, select, deselect, destroy, openChange,
deselect: this.onDeselect,
destroy: this.onDestroy,
openChange: this.onOpenChange,
}, },
id: this._menuId, id: this._menuId,
ref: 'menuInstance', ref: 'menuInstance',

View File

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

View File

@ -0,0 +1,29 @@
<template>
<div>
<md>
## 基本
最简单的用法
</md>
<Popconfirm title="Are you sure delete this task?" @confirm="confirm" @cancel="cancel" okText="Yes" cancelText="No">
<a href="#">Delete</a>
</Popconfirm>
</div>
</template>
<script>
import { Popconfirm, Button } from 'antd'
export default {
methods: {
confirm (e) {
console.log(e)
},
cancel (e) {
console.log(e)
},
},
components: {
Popconfirm,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,118 @@
<script>
import omit from 'omit.js'
import Tooltip from '../tooltip'
import abstractTooltipProps from '../tooltip/abstractTooltipProps'
import PropTypes from '../_util/vue-types'
import { getOptionProps, hasProp, getComponentFromProp } from '../_util/props-util'
import BaseMixin from '../_util/BaseMixin'
import buttonTypes from '../button/buttonTypes'
import Icon from '../icon'
import Button from '../button'
export default {
name: 'popconfirm',
props: {
...abstractTooltipProps,
prefixCls: PropTypes.string.def('ant-popover'),
transitionName: PropTypes.string.def('zoom-big'),
content: PropTypes.any,
title: PropTypes.any,
trigger: abstractTooltipProps.trigger.def('click'),
okType: buttonTypes.type.def('primary'),
okText: PropTypes.any,
cancelText: PropTypes.any,
},
mixins: [BaseMixin],
model: {
prop: 'visible',
event: 'change',
},
data () {
return {
sVisible: this.$props.visible,
}
},
methods: {
onConfirm (e) {
this.setVisible(false)
this.$emit('confirm', e)
},
onCancel (e) {
this.setVisible(false)
this.$emit('cancel', e)
},
onVisibleChange (sVisible) {
this.setVisible(sVisible)
},
setVisible (sVisible) {
const props = this.$props
if (!hasProp(this, 'visible')) {
this.setState({ sVisible })
}
const { onVisibleChange } = props
if (onVisibleChange) {
onVisibleChange(sVisible)
}
this.$emit('change', sVisible)
},
getPopupDomNode () {
return this.$refs.tooltip.getPopupDomNode()
},
},
render (h) {
const { prefixCls, okType } = this.$props
const props = getOptionProps(this)
const otherProps = omit(props, [
'title',
'content',
'cancelText',
'okText',
])
const tooltipProps = {
props: {
...otherProps,
visible: this.sVisible,
},
ref: 'tooltip',
on: {
change: this.onVisibleChange,
},
}
const overlay = (
<div>
<div class={`${prefixCls}-inner-content`}>
<div class={`${prefixCls}-message`}>
<Icon type='exclamation-circle' />
<div class={`${prefixCls}-message-title`}>
{getComponentFromProp(this, h, 'title')}
</div>
</div>
<div class={`${prefixCls}-buttons`}>
<Button onClick={this.onCancel} size='small'>
{getComponentFromProp(this, h, 'cancelText')}
</Button>
<Button onClick={this.onConfirm} type={okType} size='small'>
{getComponentFromProp(this, h, 'okText')}
</Button>
</div>
</div>
</div>
)
return (
<Tooltip
{...tooltipProps}
>
<template slot='title'>
{overlay}
</template>
{this.$slots.default}
</Tooltip>
)
},
}
</script>

View File

@ -0,0 +1,34 @@
---
category: Components
subtitle: 气泡确认框
type: Feedback
title: Popconfirm
---
点击元素,弹出气泡式的确认框。
## 何时使用
目标元素的操作需要用户进一步的确认时,在目标元素附近弹出浮层提示,询问用户。
`confirm` 弹出的全屏居中模态对话框相比,交互形式更轻量。
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| cancelText | 取消按钮文字 | string\|function\|slot | 取消 |
| okText | 确认按钮文字 | string\|function\|slot | 确定 |
| 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,60 @@
<script>
import Tooltip from '../tooltip'
import abstractTooltipProps from '../tooltip/abstractTooltipProps'
import PropTypes from '../_util/vue-types'
import { getOptionProps, getComponentFromProp } 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()
},
},
render (h) {
const { title, prefixCls, content, $slots } = this
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'>
<div>
{(title || $slots.title) &&
<div class={`${prefixCls}-title`}>
{getComponentFromProp(this, h, 'title')}
</div>
}
<div class={`${prefixCls}-inner-content`}>
{getComponentFromProp(this, h, 'content')}
</div>
</div>
</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> </label>
</template> </template>
<script> <script>
import hasProp from '../_util/hasProp' import hasProp from '../_util/props-util'
export default { export default {
name: 'Radio', name: 'Radio',
props: { props: {

View File

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

View File

@ -10,5 +10,8 @@ import './avatar/style'
import './badge/style' import './badge/style'
import './tabs/style' import './tabs/style'
import './input/style' import './input/style'
import './tooltip/style'
import './popover/style'
import './popconfirm/style'
import './menu/style' import './menu/style'

View File

@ -1,6 +1,6 @@
import Tabs from './index.vue' import Tabs from './index.vue'
import TabPane from './TabPane' import TabPane from './src/TabPane'
import TabContent from './TabContent' import TabContent from './src/TabContent'
Tabs.TabPane = TabPane Tabs.TabPane = TabPane
export default Tabs export default Tabs
export { TabPane, TabContent } export { TabPane, TabContent }

View File

@ -1,7 +1,7 @@
<script> <script>
import Tabs from './Tabs' import Tabs from './src/Tabs'
import isFlexSupported from '../_util/isFlexSupported' import isFlexSupported from '../_util/isFlexSupported'
import hasProp from '../_util/hasProp' import hasProp from '../_util/props-util'
export default { export default {
props: { props: {
prefixCls: { type: String, default: 'ant-tabs' }, prefixCls: { type: String, default: 'ant-tabs' },
@ -75,7 +75,6 @@ export default {
tabPosition, tabPosition,
tabBarStyle, tabBarStyle,
hideAdd, hideAdd,
onTabClick,
onPrevClick, onPrevClick,
onNextClick, onNextClick,
animated, animated,
@ -114,36 +113,38 @@ export default {
} }
}) })
const tabBarProps = { const tabBarProps = {
inkBarAnimated, props: {
onTabClick,
onPrevClick,
onNextClick,
style: tabBarStyle,
hideAdd, hideAdd,
removeTab: this.removeTab, removeTab: this.removeTab,
createNewTab: this.createNewTab, createNewTab: this.createNewTab,
inkBarAnimated,
},
on: {
// tabClick: onTabClick,
prevClick: onPrevClick,
nextClick: onNextClick,
},
style: tabBarStyle,
} }
const tabContentProps = { const tabContentProps = {
props: {
animated: tabPaneAnimated, animated: tabPaneAnimated,
animatedWithMargin: true, animatedWithMargin: true,
},
} }
const self = this
const tabsProps = { const tabsProps = {
props: { props: {
prefixCls, prefixCls,
tabBarPosition: tabPosition, tabBarPosition: tabPosition,
onChange: this.handleChange,
tabBarProps: tabBarProps, tabBarProps: tabBarProps,
tabContentProps: tabContentProps, tabContentProps: tabContentProps,
destroyInactiveTabPane, destroyInactiveTabPane,
defaultActiveKey, defaultActiveKey,
type, type,
onTabClick: this.onTabClick,
}, },
on: { on: {
change (val) { change: this.handleChange,
self.handleChange(val) tabClick: this.onTabClick,
},
}, },
} }
if (hasProp(this, 'activeKey')) { if (hasProp(this, 'activeKey')) {

View File

@ -2,10 +2,11 @@
import InkTabBarMixin from './InkTabBarMixin' import InkTabBarMixin from './InkTabBarMixin'
import ScrollableTabBarMixin from './ScrollableTabBarMixin' import ScrollableTabBarMixin from './ScrollableTabBarMixin'
import TabBarMixin from './TabBarMixin' import TabBarMixin from './TabBarMixin'
import BaseMixin from '../../_util/BaseMixin'
export default { export default {
name: 'ScrollableInkTabBar', name: 'ScrollableInkTabBar',
mixins: [TabBarMixin, InkTabBarMixin, ScrollableTabBarMixin], mixins: [TabBarMixin, InkTabBarMixin, ScrollableTabBarMixin, BaseMixin],
render (h) { render (h) {
const inkBarNode = this.getInkBarNode() const inkBarNode = this.getInkBarNode()
const tabs = this.getTabs(h) const tabs = this.getTabs(h)

View File

@ -1,10 +1,11 @@
<script> <script>
import ScrollableTabBarMixin from './ScrollableTabBarMixin' import ScrollableTabBarMixin from './ScrollableTabBarMixin'
import TabBarMixin from './TabBarMixin' import TabBarMixin from './TabBarMixin'
import BaseMixin from '../../_util/BaseMixin'
export default { export default {
name: 'ScrollableTabBar', name: 'ScrollableTabBar',
mixins: [TabBarMixin, ScrollableTabBarMixin], mixins: [TabBarMixin, ScrollableTabBarMixin, BaseMixin],
render (h) { render (h) {
const inkBarNode = this.getInkBarNode() const inkBarNode = this.getInkBarNode()
const tabs = this.getTabs(h) const tabs = this.getTabs(h)

View File

@ -6,8 +6,6 @@ function noop () {
export default { export default {
props: { props: {
scrollAnimated: { type: Boolean, default: true }, scrollAnimated: { type: Boolean, default: true },
onPrevClick: { type: Function, default: noop },
onNextClick: { type: Function, default: noop },
}, },
data () { data () {
@ -201,7 +199,7 @@ export default {
}, },
prevClick (e) { prevClick (e) {
this.$props.onPrevClick(e) this.__emit('prevClick', e)
const navWrapNode = this.$refs.navWrap const navWrapNode = this.$refs.navWrap
const navWrapNodeWH = this.getOffsetWH(navWrapNode) const navWrapNodeWH = this.getOffsetWH(navWrapNode)
const { offset } = this const { offset } = this
@ -209,7 +207,7 @@ export default {
}, },
nextClick (e) { nextClick (e) {
this.$props.onNextClick(e) this.__emit('nextClick', e)
const navWrapNode = this.$refs.navWrap const navWrapNode = this.$refs.navWrap
const navWrapNodeWH = this.getOffsetWH(navWrapNode) const navWrapNodeWH = this.getOffsetWH(navWrapNode)
const { offset } = this const { offset } = this

View File

@ -1,9 +1,8 @@
<script> <script>
import TabBarMixin from './TabBarMixin' import TabBarMixin from './TabBarMixin'
function noop () { import BaseMixin from '../../_util/BaseMixin'
}
export default { export default {
mixins: [TabBarMixin], mixins: [TabBarMixin, BaseMixin],
name: 'TabBar', name: 'TabBar',
props: { props: {
prefixCls: { prefixCls: {
@ -15,14 +14,6 @@ export default {
type: String, type: String,
}, },
disabled: Boolean, disabled: Boolean,
onKeyDown: {
default: noop,
type: Function,
},
onTabClick: {
default: noop,
type: Function,
},
activeKey: String, activeKey: String,
panels: Array, panels: Array,
}, },

View File

@ -1,4 +1,4 @@
import Icon from '../icon' import Icon from '../../icon'
function noop () { function noop () {
} }
export default { export default {
@ -12,14 +12,6 @@ export default {
type: String, type: String,
}, },
disabled: Boolean, disabled: Boolean,
onKeyDown: {
default: noop,
type: Function,
},
onTabClick: {
default: noop,
type: Function,
},
activeKey: String, activeKey: String,
panels: Array, panels: Array,
hideAdd: Boolean, hideAdd: Boolean,
@ -51,7 +43,7 @@ export default {
} else { } else {
} }
const onClick = () => { const onClick = () => {
!disabled && this.onTabClick(tabKey) !disabled && this.__emit('tabClick', tabKey)
} }
let tabC = typeof tab === 'function' ? child.tab(h, tabKey) : tab let tabC = typeof tab === 'function' ? child.tab(h, tabKey) : tab
@ -86,6 +78,9 @@ export default {
return rst return rst
}, },
onKeyDown (e) {
this.__emit('keydown', e)
},
getRootNode (contents, createElement) { getRootNode (contents, createElement) {
const { const {
prefixCls, onKeyDown, tabBarPosition, hideAdd, prefixCls, onKeyDown, tabBarPosition, hideAdd,

View File

@ -1,9 +1,10 @@
<script> <script>
import Icon from '../icon' import Icon from '../../icon'
import KeyCode from './KeyCode' import KeyCode from './KeyCode'
import TabContent from './TabContent' import TabContent from './TabContent'
import ScrollableInkTabBar from './ScrollableInkTabBar' import ScrollableInkTabBar from './ScrollableInkTabBar'
import hasProp from '../_util/hasProp' import hasProp from '../../_util/props-util'
import BaseMixin from '../../_util/BaseMixin'
function getDefaultActiveKey (t) { function getDefaultActiveKey (t) {
let activeKey let activeKey
t.$slots.default && t.$slots.default.forEach(({ componentOptions = {}, key: tabKey }) => { t.$slots.default && t.$slots.default.forEach(({ componentOptions = {}, key: tabKey }) => {
@ -23,8 +24,7 @@ function activeKeyIsValid (t, key) {
}) })
return key !== undefined && keys.indexOf(key) >= 0 return key !== undefined && keys.indexOf(key) >= 0
} }
function noop () {
}
export default { export default {
name: 'Tabs', name: 'Tabs',
components: { Icon }, components: { Icon },
@ -32,6 +32,7 @@ export default {
prop: 'activeKey', prop: 'activeKey',
event: 'change', event: 'change',
}, },
mixins: [BaseMixin],
props: { props: {
prefixCls: { prefixCls: {
default: 'ant-tabs', default: 'ant-tabs',
@ -53,8 +54,6 @@ export default {
return ['line', 'card', 'editable-card'].includes(value) return ['line', 'card', 'editable-card'].includes(value)
}, },
}, },
onChange: { type: Function, default: noop },
onTabClick: { type: Function, default: noop },
}, },
data () { data () {
return { return {
@ -90,7 +89,7 @@ export default {
return activeKey return activeKey
}, },
handleTabClick (activeKey) { handleTabClick (activeKey) {
this.onTabClick(activeKey) this.__emit('tabClick', activeKey)
this.setActiveKey(activeKey) this.setActiveKey(activeKey)
}, },
@ -112,7 +111,7 @@ export default {
if (!hasProp(this, 'activeKey')) { if (!hasProp(this, 'activeKey')) {
this.stateActiveKey = activeKey this.stateActiveKey = activeKey
} }
this.onChange(activeKey) this.__emit('change', activeKey)
} }
}, },
@ -172,25 +171,31 @@ export default {
}) })
const tabContentProps = { const tabContentProps = {
props: { props: {
...this.tabContentProps, ...this.tabContentProps.props,
prefixCls, prefixCls,
tabBarPosition, tabBarPosition,
activeKey: stateActiveKey, activeKey: stateActiveKey,
destroyInactiveTabPane, destroyInactiveTabPane,
onChange: setActiveKey, // onChange: setActiveKey,
},
on: {
change: setActiveKey,
}, },
} }
const tabBarProps = { const tabBarProps = {
props: { props: {
...this.tabBarProps, ...this.tabBarProps.props,
panels: panels, panels: panels,
prefixCls: prefixCls, prefixCls: prefixCls,
onKeyDown: onNavKeyDown,
tabBarPosition: tabBarPosition, tabBarPosition: tabBarPosition,
onTabClick: handleTabClick,
activeKey: stateActiveKey, activeKey: stateActiveKey,
}, },
style: this.tabBarProps.style || {}, style: this.tabBarProps.style || {},
on: {
...this.tabBarProps.on,
keydown: onNavKeyDown,
tabClick: handleTabClick,
},
} }
const contents = [ const contents = [
<ScrollableInkTabBar <ScrollableInkTabBar

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,24 @@
<template>
<div>
<md>
## 箭头指向
设置了 `arrowPointAtCenter` 箭头将指向目标元素的中心
</md>
<Tooltip placement="topLeft" title="Prompt Text">
<AntButton>Align edge / 边缘对齐</AntButton>
</Tooltip>
<Tooltip placement="topLeft" title="Prompt Text" arrowPointAtCenter>
<AntButton>Arrow points to center / 箭头指向中心</AntButton>
</Tooltip>
</div>
</template>
<script>
import { Tooltip, Button } from 'antd'
export default {
components: {
Tooltip,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,43 @@
<template>
<div>
<md>
## 自动调整位置
气泡框不可见时自动调整位置
</md>
<div :style="wrapStyles">
<Tooltip placement="left" title="Prompt Text" :getPopupContainer="getPopupContainer">
<AntButton>Adjust automatically / 自动调整</AntButton>
</Tooltip>
<br />
<Tooltip placement="left" title="Prompt Text" :getPopupContainer="getPopupContainer" :autoAdjustOverflow="false">
<AntButton>Ingore / 不处理</AntButton>
</Tooltip>
</div>
</div>
</template>
<script>
import { Tooltip, Button } from 'antd'
const wrapStyles = {
overflow: 'hidden',
position: 'relative',
padding: '24px',
border: '1px solid #e9e9e9',
}
export default {
data () {
return {
wrapStyles,
}
},
methods: {
getPopupContainer (trigger) {
return trigger.parentElement
},
},
components: {
Tooltip,
AntButton: Button,
},
}
</script>

View File

@ -0,0 +1,23 @@
<template>
<div>
<md>
## 基本
最简单的用法
</md>
<Tooltip>
<template slot='title'>
prompt text
</template>
Tooltip will show when mouse enter.
</Tooltip>
</div>
</template>
<script>
import { Tooltip } from 'antd'
export default {
components: {
Tooltip,
},
}
</script>

View File

@ -1,97 +1,26 @@
<template> <template>
<div> <div>
<tool-tip <h1>Basic</h1>
placement="top" <Basic />
:title="showText" <h1>ArrowCenter</h1>
:autoAdjustOverflow="autoAdjustOverflow" <ArrowCenter />
> <h1>AutoAdjust</h1>
<h1 @click="boom" class="test">撞到边缘翻转位置 & 点击更新</h1> <AutoAdjust />
</tool-tip> <h1>Placement</h1>
<ant-button @click="reverse" type="primary">{{autoAdjustOverflow ? '启用' : '关闭'}}自动调整中</ant-button> <Placement />
<div class="box">
<h2>切换arrowPointAtCenter模式</h2>
<ant-button @click="change">{{arrowPointAtCenter}}</ant-button>
<table>
<tr v-for="(tr, index) in table" :key="index">
<td v-for="(td, i) in tr" :key="i">
<tool-tip
v-if="td"
:placement="td"
:title="td"
:arrowPointAtCenter="arrowPointAtCenter"
>
<AntButton type="primary">{{td}}</AntButton>
</tool-tip>
</td>
</tr>
</table>
</div>
<div>
<p>
<tool-tip :arrowPointAtCenter="true" title="Consider using the NamedModulesPlugin for module names." placement="topLeft">
<ant-button>arrowPointAtCenter arrowPointAtCenter arrowPointAtCenter</ant-button>
</tool-tip>
</p>
</div>
</div> </div>
</template> </template>
<script> <script>
import { ToolTip, Button } from 'antd' import Basic from './basic'
import 'antd/button/style' import ArrowCenter from './arrow-point-at-center'
export default { import AutoAdjust from './auto-adjust-overflow'
name: 'tooltip-basic', import Placement from './placement'
data() { export default {
return {
show: true,
showText: '你好啊233',
table: [
['', 'topLeft', 'top', 'topRight', ''],
['leftTop', '', '', '', 'rightTop'],
['left', '', '', '', 'right'],
['leftBottom', '', '', '', 'rightBottom'],
['', 'bottomLeft', 'bottom', 'bottomRight', ''],
],
arrowPointAtCenter: false,
autoAdjustOverflow: true,
}
},
methods: {
boom() {
if (this.showText.length % 20) {
this.showText += '3'
} else {
this.showText += ' '
}
},
change() {
this.arrowPointAtCenter = !this.arrowPointAtCenter
},
reverse() {
this.autoAdjustOverflow = !this.autoAdjustOverflow
}
},
components: { components: {
ToolTip, Basic,
AntButton: Button, ArrowCenter,
} AutoAdjust,
} Placement,
},
}
</script> </script>
<style scoped lang="less">
.test {
margin: 20px;
display: inline-block;
}
.box {
margin: 100px;
}
table {
td {
padding: 20px;
}
p {
text-align: center;
vertical-align: middle;
}
}
</style>

View File

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

View File

@ -1,4 +1,3 @@
import ToolTip from './tooltip.vue' import ToolTip from './tooltip.vue'
import './style'
export default ToolTip export default ToolTip

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

@ -0,0 +1,88 @@
import { placements as rcPlacements } from './src/placements'
const autoAdjustOverflowEnabled = {
adjustX: 1,
adjustY: 1,
}
const autoAdjustOverflowDisabled = {
adjustX: 0,
adjustY: 0,
}
const targetOffset = [0, 0]
export function getOverflowOptions (autoAdjustOverflow) {
if (typeof autoAdjustOverflow === 'boolean') {
return autoAdjustOverflow ? autoAdjustOverflowEnabled : autoAdjustOverflowDisabled
}
return {
...autoAdjustOverflowDisabled,
...autoAdjustOverflow,
}
}
export default function getPlacements (config) {
const { arrowWidth = 5, horizontalArrowShift = 16, verticalArrowShift = 12, autoAdjustOverflow = true } = config
const placementMap = {
left: {
points: ['cr', 'cl'],
offset: [-4, 0],
},
right: {
points: ['cl', 'cr'],
offset: [4, 0],
},
top: {
points: ['bc', 'tc'],
offset: [0, -4],
},
bottom: {
points: ['tc', 'bc'],
offset: [0, 4],
},
topLeft: {
points: ['bl', 'tc'],
offset: [-(horizontalArrowShift + arrowWidth), -4],
},
leftTop: {
points: ['tr', 'cl'],
offset: [-4, -(verticalArrowShift + arrowWidth)],
},
topRight: {
points: ['br', 'tc'],
offset: [horizontalArrowShift + arrowWidth, -4],
},
rightTop: {
points: ['tl', 'cr'],
offset: [4, -(verticalArrowShift + arrowWidth)],
},
bottomRight: {
points: ['tr', 'bc'],
offset: [horizontalArrowShift + arrowWidth, 4],
},
rightBottom: {
points: ['bl', 'cr'],
offset: [4, verticalArrowShift + arrowWidth],
},
bottomLeft: {
points: ['tl', 'bc'],
offset: [-(horizontalArrowShift + arrowWidth), 4],
},
leftBottom: {
points: ['br', 'cl'],
offset: [-4, verticalArrowShift + arrowWidth],
},
}
Object.keys(placementMap).forEach(key => {
placementMap[key] = config.arrowPointAtCenter ? {
...placementMap[key],
overflow: getOverflowOptions(autoAdjustOverflow),
targetOffset,
} : {
...rcPlacements[key],
overflow: getOverflowOptions(autoAdjustOverflow),
}
})
return placementMap
}

View File

@ -0,0 +1,101 @@
<script>
import PropTypes from '../../_util/vue-types'
import Trigger from '../../trigger'
import { placements } from './placements'
import hasProp from '../../_util/props-util'
function noop () {}
export default {
props: {
trigger: PropTypes.any.def('hover'),
defaultVisible: PropTypes.bool,
visible: PropTypes.bool,
placement: PropTypes.string.def('right'),
transitionName: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
animation: PropTypes.any,
afterVisibleChange: PropTypes.func.def(() => {}),
overlay: PropTypes.any,
overlayStyle: PropTypes.object,
overlayClassName: PropTypes.string,
prefixCls: PropTypes.string.def('rc-tooltip'),
mouseEnterDelay: PropTypes.number.def(0),
mouseLeaveDelay: PropTypes.number.def(0.1),
getTooltipContainer: PropTypes.func,
destroyTooltipOnHide: PropTypes.bool.def(false),
align: PropTypes.object.def({}),
arrowContent: PropTypes.any.def(null),
tipId: PropTypes.string,
builtinPlacements: PropTypes.object,
},
methods: {
getPopupElement (h) {
const { arrowContent, overlay, prefixCls, tipId } = this.$props
return ([
<div class={`${prefixCls}-arrow`} key='arrow'>
{this.$slots.arrowContent}
{typeof arrowContent === 'function' ? arrowContent(h) : arrowContent}
</div>,
<div class={`${prefixCls}-inner`} key='content' id={tipId}>
{typeof overlay === 'function' ? overlay(h) : overlay}
{this.$slots.overlay}
</div>,
])
},
getPopupDomNode () {
return this.$refs.trigger.getPopupDomNode()
},
},
render (h) {
const {
overlayClassName, trigger,
mouseEnterDelay, mouseLeaveDelay,
overlayStyle, prefixCls,
afterVisibleChange,
transitionName, animation,
placement, align,
destroyTooltipOnHide,
defaultVisible, getTooltipContainer,
...restProps
} = this.$props
const extraProps = { ...restProps }
if (hasProp(this, 'visible')) {
extraProps.popupVisible = this.$props.visible
}
const triggerProps = {
props: {
popupClassName: overlayClassName,
prefixCls: prefixCls,
action: trigger,
builtinPlacements: placements,
popupPlacement: placement,
popupAlign: align,
getPopupContainer: getTooltipContainer,
afterPopupVisibleChange: afterVisibleChange,
popupTransitionName: transitionName,
popupAnimation: animation,
defaultPopupVisible: defaultVisible,
destroyPopupOnHide: destroyTooltipOnHide,
mouseLeaveDelay: mouseLeaveDelay,
popupStyle: overlayStyle,
mouseEnterDelay: mouseEnterDelay,
...extraProps,
},
on: {
popupVisibleChange: this.$listeners.visibleChange || noop,
popupAlign: this.$listeners.popupAlign || noop,
},
ref: 'trigger',
}
return (<Trigger {...triggerProps}>
<template slot='popup'>
{this.getPopupElement(h)}
</template>
{this.$slots.default}
</Trigger>)
},
}
</script>

View File

@ -0,0 +1,3 @@
import Tooltip from './Tooltip'
export default Tooltip

View File

@ -0,0 +1,83 @@
const autoAdjustOverflow = {
adjustX: 1,
adjustY: 1,
}
const targetOffset = [0, 0]
export const placements = {
left: {
points: ['cr', 'cl'],
overflow: autoAdjustOverflow,
offset: [-4, 0],
targetOffset,
},
right: {
points: ['cl', 'cr'],
overflow: autoAdjustOverflow,
offset: [4, 0],
targetOffset,
},
top: {
points: ['bc', 'tc'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset,
},
bottom: {
points: ['tc', 'bc'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset,
},
topLeft: {
points: ['bl', 'tl'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset,
},
leftTop: {
points: ['tr', 'tl'],
overflow: autoAdjustOverflow,
offset: [-4, 0],
targetOffset,
},
topRight: {
points: ['br', 'tr'],
overflow: autoAdjustOverflow,
offset: [0, -4],
targetOffset,
},
rightTop: {
points: ['tl', 'tr'],
overflow: autoAdjustOverflow,
offset: [4, 0],
targetOffset,
},
bottomRight: {
points: ['tr', 'br'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset,
},
rightBottom: {
points: ['bl', 'br'],
overflow: autoAdjustOverflow,
offset: [4, 0],
targetOffset,
},
bottomLeft: {
points: ['tl', 'bl'],
overflow: autoAdjustOverflow,
offset: [0, 4],
targetOffset,
},
leftBottom: {
points: ['br', 'bl'],
overflow: autoAdjustOverflow,
offset: [-4, 0],
targetOffset,
},
}
export default placements

View File

@ -1,121 +1,127 @@
<script> <script>
import Vue from 'vue' 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/props-util'
import abstractTooltipProps from './abstractTooltipProps'
const splitObject = (obj, keys) => {
const picked = {}
const omited = { ...obj }
keys.forEach(key => {
if (obj && key in obj) {
picked[key] = obj[key]
delete omited[key]
}
})
return { picked, omited }
}
export default { export default {
name: 'ToolTip', name: 'Tooltip',
props: { props: {
title: String, ...abstractTooltipProps,
prefixCls: { title: PropTypes.any,
default: 'ant-tooltip',
},
placement: {
default: 'top',
validator: val => ['top', 'left', 'right', 'bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom'].includes(val),
},
transitionName: {
default: 'zoom-big-fast',
},
mouseEnterDelay: {
default: 0.1,
},
mouseLeaveDelay: {
default: 0.1,
},
arrowPointAtCenter: {
default: false,
},
autoAdjustOverflow: {
default: true,
}, },
model: {
prop: 'visible',
event: 'change',
}, },
data () { data () {
return { return {
vnode: null, sVisible: !!this.$props.visible,
visible: false,
left: 0,
top: 0,
realPlacement: this.placement,
t1: null,
t2: null,
} }
}, },
computed: { watch: {
classes () { visible (val) {
const { prefixCls } = this this.sVisible = val
return {
[`${prefixCls}`]: true,
}
}, },
}, },
methods: { methods: {
checkPosition (popup, text, placement) { onVisibleChange (visible) {
const { top, left, bottom, right } = text if (!hasProp(this, 'visible')) {
const reg = /(top|bottom|left|right)(.*)/ this.sVisible = this.isNoTitle() ? false : visible
const [, abstractPos, suffix] = placement.match(reg)
let ret = placement
// we can change the position many times
if (abstractPos === 'left' && left < popup.width) ret = 'right' + suffix
if (abstractPos === 'right' && document.documentElement.clientWidth - right < popup.width) ret = 'left' + suffix
if (abstractPos === 'top' && top < popup.height) ret = 'bottom' + suffix
if (abstractPos === 'bottom' && document.documentElement.clientHeight - bottom < popup.height) ret = 'left' + suffix
return ret
},
mountNode (callback) {
if (this.vnode) {
callback()
return
} }
const div = document.createElement('div') if (!this.isNoTitle()) {
document.body.appendChild(div) this.$emit('change', visible)
const that = this
const vnode = new Vue({
data () {
return {
left: 0,
top: 0,
} }
}, },
methods: {
hideSelf (e) { getPopupDomNode () {
if (that.t1) { return this.$refs.tooltip.getPopupDomNode()
clearTimeout(that.t1)
that.t1 = null
}
if (that.mouseLeaveDelay) {
that.t2 = window.setTimeout(() => {
if (e.relatedTarget === that.$el) {
return
}
that.visible = false
}, +that.mouseLeaveDelay * 1e3)
}
}, },
},
render (h) { getPlacements () {
return ( const { builtinPlacements, arrowPointAtCenter, autoAdjustOverflow } = this.$props
<transition name={that.transitionName}> return builtinPlacements || getPlacements({
<div arrowPointAtCenter,
v-show={that.visible} verticalArrowShift: 8,
class={`ant-tooltip ant-tooltip-placement-${that.realPlacement}`} autoAdjustOverflow,
style={{ left: this.left + 'px', top: this.top + 'px' }}
onMouseleave={this.hideSelf}
>
<div class='ant-tooltip-content'>
<div class='ant-tooltip-arrow'/>
<div class='ant-tooltip-inner'>
<span>{that.title}</span>
</div>
</div>
</div>
</transition>
)
},
}).$mount(div)
this.$nextTick(() => {
this.vnode = vnode
callback()
}) })
}, },
onPopupAlign: (placement, domNode, target, align) => {
isHoverTrigger () {
const { trigger } = this.$props
if (!trigger || trigger === 'hover') {
return true
}
if (Array.isArray(trigger)) {
return trigger.indexOf('hover') >= 0
}
return false
},
// Fix Tooltip won't hide at disabled button
// mouse events don't trigger at disabled button in Chrome
// https://github.com/react-component/tooltip/issues/18
getDisabledCompatibleChildren (ele) {
const isAntBtn = ele.componentOptions && ele.componentOptions.Ctor.options.__ANT_BUTTON
if (((isAntBtn && ele.componentOptions.propsData.disabled) || (ele.tag === 'button' && ele.data && ele.data.attrs.disabled !== false)) && this.isHoverTrigger()) {
// Pick some layout related style properties up to span
// Prevent layout bugs like https://github.com/ant-design/ant-design/issues/5254
const { picked, omited } = splitObject(
getStyle(ele),
['position', 'left', 'right', 'top', 'bottom', 'float', 'display', 'zIndex'],
)
const spanStyle = {
display: 'inline-block', // default inline-block is important
...picked,
cursor: 'not-allowed',
}
const buttonStyle = {
...omited,
pointerEvents: 'none',
}
const spanCls = getClass(ele)
const child = cloneElement(ele, {
style: buttonStyle,
class: null,
})
return (
<span style={spanStyle} class={spanCls}>
{child}
</span>
)
}
return ele
},
isNoTitle () {
const { $slots, title } = this
return !$slots.title && !title
},
//
onPopupAlign (domNode, align) {
const placements = this.getPlacements()
//
const placement = Object.keys(placements).filter(
key => (
placements[key].points[0] === align.points[0] &&
placements[key].points[1] === align.points[1]
),
)[0]
if (!placement) { if (!placement) {
return return
} }
@ -135,118 +141,48 @@ export default {
} else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) { } else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) {
transformOrigin.left = `${-align.offset[0]}px` transformOrigin.left = `${-align.offset[0]}px`
} }
target.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}` domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`
}, },
addEventHandle (old, fn) {
if (!old) {
return fn
} else if (Array.isArray(old)) {
return old.indexOf(fn) > -1 ? old : old.concat(fn)
} else {
return old === fn ? old : [old, fn]
}
}, },
computeOffset (popup, text, placement, scale) {
let { width, height, top, left } = text
// you cant change the properties of DOMRect
top += window.scrollY
left += window.scrollX
// FIXME: we can get the numbers from scale, but that's not what we really want
const p = { width: popup.width / scale, height: popup.height / scale }
const ret = { left, top }
if (/top/.test(placement)) ret.top -= p.height
if (/bottom/.test(placement)) ret.top += height
if (/left/.test(placement)) ret.left -= p.width
if (/right/.test(placement)) ret.left += width
// FIXME: magic number 20 & 14 comes from the offset of triangle
if (/Left/.test(placement)) {
if (this.arrowPointAtCenter) ret.left += width / 2 - 20
} else if (/Right/.test(placement)) {
ret.left += (width - p.width)
if (this.arrowPointAtCenter) ret.left -= width / 2 - 20
} else if (/(top)|(bottom)/.test(placement)) {
ret.left += (width - p.width) / 2
}
if (/Top/.test(placement)) {
if (this.arrowPointAtCenter) ret.top += height / 2 - 14
} else if (/Bottom/.test(placement)) {
ret.top += (height - p.height)
if (this.arrowPointAtCenter) ret.top -= height / 2 - 14
} else if (/(left)|(right)/.test(placement)) {
ret.top += (height - p.height) / 2
}
return ret
},
showNode () {
this.mountNode(() => {
this.visible = true
this.$nextTick(() => {
const popup = this.vnode.$el.getBoundingClientRect()
const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || []
const content = this.$el.getBoundingClientRect()
const place = this.autoAdjustOverflow ? this.checkPosition(popup, content, this.placement, scale) : this.placement
this.realPlacement = place
const { left, top } = this.computeOffset(popup, content, place, scale)
this.vnode.left = left
this.vnode.top = top
})
this.onPopupAlign(this.realPlacement, this.$el, this.vnode.$el, { offset: [0, 0] })
})
},
hideNode (e) {
if (!this.vnode) return
if (e.relatedTarget === this.vnode.$el) {
return
}
this.visible = false
},
checkShow (e) {
if (this.t2) {
clearTimeout(this.t2)
this.t2 = null
}
if (this.mouseEnterDelay) {
this.t1 = window.setTimeout(() => {
this.showNode(e)
}, +this.mouseEnterDelay * 1e3)
}
},
checkHide (e) {
if (this.t1) {
clearTimeout(this.t1)
this.t1 = null
}
if (this.mouseLeaveDelay) {
this.t2 = window.setTimeout(() => {
this.hideNode(e)
}, +this.mouseLeaveDelay * 1e3)
}
},
},
render (h) { render (h) {
const inner = this.$slots.default[0] const { $props, $data, $slots } = this
inner.data = inner.data || {} const { title, prefixCls, openClassName, getPopupContainer, getTooltipContainer } = $props
inner.data.on = inner.data.on || {} const children = ($slots.default || []).filter(c => c.tag || c.text.trim() !== '')[0]
inner.data.on.mouseenter = this.addEventHandle(inner.data.on.mouseenter, this.checkShow) let sVisible = $data.sVisible
inner.data.on.mouseleave = this.addEventHandle(inner.data.on.mouseleave, this.checkHide) // Hide tooltip when there is no title
if (!hasProp(this, 'visible') && this.isNoTitle()) {
return this.$slots.default[0] sVisible = false
}
if (!children) {
return null
}
const child = this.getDisabledCompatibleChildren(isValidElement(children) ? children : <span>{children}</span>)
const childCls = {
[openClassName || `${prefixCls}-open`]: true,
}
const tooltipProps = {
props: {
...$props,
getTooltipContainer: getPopupContainer || getTooltipContainer,
builtinPlacements: this.getPlacements(),
visible: sVisible,
}, },
updated () { ref: 'tooltip',
if (!this.vnode) return on: {
const popup = this.vnode.$el.getBoundingClientRect() visibleChange: this.onVisibleChange,
const [, scale = 1] = window.getComputedStyle(this.vnode.$el).transform.match(/matrix\((.*?),/) || [] popupAlign: this.onPopupAlign,
const content = this.$el.getBoundingClientRect()
const { left, top } = this.computeOffset(popup, content, this.realPlacement, scale)
this.vnode.left = left
this.vnode.top = top
}, },
beforeDestroy () { }
if (!this.vnode) return return (
this.vnode.$el.remove() <RcTooltip {...tooltipProps}>
this.vnode.$destroy() <template slot='overlay'>
{typeof title === 'function' ? title(h) : title}
{$slots.title}
</template>
{sVisible ? cloneElement(child, { class: childCls }) : child}
</RcTooltip>
)
}, },
} }
</script> </script>

View File

@ -3,6 +3,7 @@ import PropTypes from '../_util/vue-types'
import Align from '../align' import Align from '../align'
import PopupInner from './PopupInner' import PopupInner from './PopupInner'
import LazyRenderBox from './LazyRenderBox' import LazyRenderBox from './LazyRenderBox'
import { noop } from './utils'
export default { export default {
props: { props: {
@ -56,13 +57,11 @@ export default {
onAlign (popupDomNode, align) { onAlign (popupDomNode, align) {
const props = this.$props const props = this.$props
const currentAlignClassName = props.getClassNameFromAlign(align) const currentAlignClassName = props.getClassNameFromAlign(align)
// FIX: https://github.com/react-component/trigger/issues/56
// FIX: https://github.com/react-component/tooltip/issues/79
if (this.currentAlignClassName !== currentAlignClassName) { if (this.currentAlignClassName !== currentAlignClassName) {
popupDomNode.className = popupDomNode.className.replace(this.currentAlignClassName, currentAlignClassName)
this.currentAlignClassName = currentAlignClassName this.currentAlignClassName = currentAlignClassName
popupDomNode.className = this.getClassName(currentAlignClassName)
} }
this.$emit('align', popupDomNode, align) this.$listeners.align && this.$listeners.align(popupDomNode, align)
}, },
getPopupDomNode () { getPopupDomNode () {
@ -95,30 +94,17 @@ export default {
getClassName (currentAlignClassName) { getClassName (currentAlignClassName) {
return `${this.$props.prefixCls} ${this.$props.popupClassName} ${currentAlignClassName}` return `${this.$props.prefixCls} ${this.$props.popupClassName} ${currentAlignClassName}`
}, },
onMouseEnter (e) {
this.$emit('mouseenter', e)
},
onMouseLeave (e) {
this.$emit('mouseleave', e)
},
beforeEnter (el) {
try {
// this.$refs.alignInstance && this.$refs.alignInstance.forceAlign()
} catch (error) {
}
this.$refs.alignInstance && this.$refs.alignInstance.forceAlign()
},
afterLeave (el) { afterLeave (el) {
if (this.destroyPopupOnHide) { if (this.destroyPopupOnHide) {
this.destroyPopup = true this.destroyPopup = true
} }
}, },
getPopupElement () { getPopupElement () {
const { $props: props, onMouseEnter, onMouseLeave, $slots } = this const { $props: props, $slots, $listeners } = this
const { align, visible, prefixCls, animation } = props const { align, visible, prefixCls, animation } = props
const className = this.getClassName(this.currentAlignClassName || const { mouseenter, mouseleave } = $listeners
props.getClassNameFromAlign(align)) this.currentAlignClassName = this.currentAlignClassName || props.getClassNameFromAlign(align)
const className = this.getClassName(this.currentAlignClassName)
// const hiddenClassName = `${prefixCls}-hidden` // const hiddenClassName = `${prefixCls}-hidden`
if (!visible) { if (!visible) {
this.currentAlignClassName = null this.currentAlignClassName = null
@ -131,8 +117,8 @@ export default {
}, },
class: `${className}`, class: `${className}`,
on: { on: {
mouseenter: onMouseEnter, mouseenter: mouseenter || noop,
mouseleave: onMouseLeave, mouseleave: mouseleave || noop,
}, },
ref: 'popupInstance', ref: 'popupInstance',
style: { ...this.getZIndexStyle() }, style: { ...this.getZIndexStyle() },
@ -144,7 +130,6 @@ export default {
} }
return (<transition return (<transition
{...transitionProps} {...transitionProps}
onBeforeEnter={this.beforeEnter}
onAfterLeave={this.afterLeave} onAfterLeave={this.afterLeave}
> >
<Align <Align

View File

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

View File

@ -140,13 +140,13 @@
</tr> </tr>
<tr> <tr>
<td>popupVisibleChange</td> <td>popupVisibleChange</td>
<td>$emit(visible)</td> <td>$emit</td>
<td></td> <td></td>
<td>call when popup visible is changed</td> <td>call when popup visible is changed</td>
</tr> </tr>
<tr> <tr>
<td>popupAlign</td> <td>popupAlign</td>
<td>$emit(popupDomNode, align)</td> <td>$emit</td>
<td></td> <td></td>
<td>callback when popup node is aligned</td> <td>callback when popup node is aligned</td>
</tr> </tr>

View File

@ -1,12 +1,12 @@
<script> <script>
import PropTypes from '../_util/vue-types' import PropTypes from '../_util/vue-types'
import contains from '../_util/Dom/contains' import contains from '../_util/Dom/contains'
import hasProp from '../_util/hasProp' import hasProp from '../_util/props-util'
import addEventListener from '../_util/Dom/addEventListener' import addEventListener from '../_util/Dom/addEventListener'
import warning from '../_util/warning' import warning from '../_util/warning'
import Popup from './Popup' import Popup from './Popup'
import { getAlignFromPlacement, getPopupClassNameFromAlign } from './utils' import { getAlignFromPlacement, getPopupClassNameFromAlign, noop } from './utils'
import StateMixin from '../_util/StateMixin' import BaseMixin from '../_util/BaseMixin'
import { cloneElement, cloneVNode } from '../_util/vnode' import { cloneElement, cloneVNode } from '../_util/vnode'
function returnEmptyString () { function returnEmptyString () {
@ -27,8 +27,8 @@ export default {
showAction: PropTypes.any.def([]), showAction: PropTypes.any.def([]),
hideAction: PropTypes.any.def([]), hideAction: PropTypes.any.def([]),
getPopupClassNameFromAlign: PropTypes.any.def(returnEmptyString), getPopupClassNameFromAlign: PropTypes.any.def(returnEmptyString),
// onPopupVisibleChange: PropTypes.func, // onPopupVisibleChange: PropTypes.func.def(noop),
// afterPopupVisibleChange: PropTypes.func, afterPopupVisibleChange: PropTypes.func.def(noop),
popup: PropTypes.any, popup: PropTypes.any,
popupStyle: PropTypes.object.def({}), popupStyle: PropTypes.object.def({}),
prefixCls: PropTypes.string.def('rc-trigger-popup'), prefixCls: PropTypes.string.def('rc-trigger-popup'),
@ -51,7 +51,7 @@ export default {
destroyPopupOnHide: PropTypes.bool.def(false), destroyPopupOnHide: PropTypes.bool.def(false),
mask: PropTypes.bool.def(false), mask: PropTypes.bool.def(false),
maskClosable: PropTypes.bool.def(true), maskClosable: PropTypes.bool.def(true),
// onPopupAlign: PropTypes.func, // onPopupAlign: PropTypes.func.def(noop),
popupAlign: PropTypes.object.def({}), popupAlign: PropTypes.object.def({}),
popupVisible: PropTypes.bool, popupVisible: PropTypes.bool,
defaultPopupVisible: PropTypes.bool.def(false), defaultPopupVisible: PropTypes.bool.def(false),
@ -62,7 +62,7 @@ export default {
maskAnimation: PropTypes.string, maskAnimation: PropTypes.string,
}, },
mixins: [StateMixin], mixins: [BaseMixin],
data () { data () {
const props = this.$props const props = this.$props
let popupVisible let popupVisible
@ -96,7 +96,7 @@ export default {
}, },
sPopupVisible (val) { sPopupVisible (val) {
this.$nextTick(() => { this.$nextTick(() => {
this.$emit('afterPopupVisibleChange', val) this.afterPopupVisibleChange(val)
}) })
}, },
}, },
@ -258,11 +258,10 @@ export default {
}, },
getRootDomNode () { getRootDomNode () {
console.log('this.$el.children', this.$el.children)
return this.$el.children ? this.$el.children[0] : this.$el return this.$el.children ? this.$el.children[0] : this.$el
}, },
getPopupClassFromAlign (align) { handleGetPopupClassFromAlign (align) {
const className = [] const className = []
const props = this.$props const props = this.$props
const { popupPlacement, builtinPlacements, prefixCls } = props const { popupPlacement, builtinPlacements, prefixCls } = props
@ -283,10 +282,7 @@ export default {
} }
return popupAlign return popupAlign
}, },
onPopupAlign () { getComponent (h) {
this.$emit('popupAlign', ...arguments)
},
getComponent () {
const mouseProps = {} const mouseProps = {}
if (this.isMouseEnterToShow()) { if (this.isMouseEnterToShow()) {
mouseProps.mouseenter = this.onPopupMouseenter mouseProps.mouseenter = this.onPopupMouseenter
@ -295,8 +291,8 @@ export default {
mouseProps.mouseleave = this.onPopupMouseleave mouseProps.mouseleave = this.onPopupMouseleave
} }
const { prefixCls, destroyPopupOnHide, sPopupVisible, const { prefixCls, destroyPopupOnHide, sPopupVisible,
popupStyle, popupClassName, action, onPopupAlign, popupStyle, popupClassName, action,
popupAnimation, getPopupClassFromAlign, getRootDomNode, popupAnimation, handleGetPopupClassFromAlign, getRootDomNode,
mask, zIndex, popupTransitionName, getPopupAlign, mask, zIndex, popupTransitionName, getPopupAlign,
maskAnimation, maskTransitionName, popup, $slots, getContainer } = this maskAnimation, maskTransitionName, popup, $slots, getContainer } = this
const popupProps = { const popupProps = {
@ -307,7 +303,7 @@ export default {
action, action,
align: getPopupAlign(), align: getPopupAlign(),
animation: popupAnimation, animation: popupAnimation,
getClassNameFromAlign: getPopupClassFromAlign, getClassNameFromAlign: handleGetPopupClassFromAlign,
getRootDomNode, getRootDomNode,
mask, mask,
zIndex, zIndex,
@ -318,7 +314,7 @@ export default {
popupClassName, popupClassName,
}, },
on: { on: {
align: onPopupAlign, align: this.$listeners.popupAlign || noop,
...mouseProps, ...mouseProps,
}, },
ref: 'popup', ref: 'popup',
@ -329,7 +325,7 @@ export default {
{...popupProps} {...popupProps}
ref='popup' ref='popup'
> >
{typeof popup === 'function' ? popup() : popup} {typeof popup === 'function' ? popup(h) : popup}
{popup === undefined ? $slots.popup : null} {popup === undefined ? $slots.popup : null}
</Popup> </Popup>
) )
@ -359,7 +355,7 @@ export default {
}) })
this.$forceUpdate() this.$forceUpdate()
} }
this.$emit('popupVisibleChange', sPopupVisible) this.$listeners.popupVisibleChange && this.$listeners.popupVisibleChange(sPopupVisible)
} }
}, },
@ -473,7 +469,7 @@ export default {
this.setPopupVisible(false) this.setPopupVisible(false)
}, },
}, },
render () { render (h) {
const children = this.$slots.default const children = this.$slots.default
if (children.length > 1) { if (children.length > 1) {
warning(false, 'Trigger $slots.default.length > 1, just support only one default', true) warning(false, 'Trigger $slots.default.length > 1, just support only one default', true)
@ -522,7 +518,7 @@ export default {
const trigger = cloneElement(cloneVNode(child), newChildProps) const trigger = cloneElement(cloneVNode(child), newChildProps)
const { sPopupVisible, forceRender } = this const { sPopupVisible, forceRender } = this
if (sPopupVisible || forceRender || this._component) { if (sPopupVisible || forceRender || this._component) {
this._component = this.getComponent() this._component = this.getComponent(h)
} else { } else {
this._component = null this._component = null
} }

View File

@ -21,7 +21,5 @@ export function getPopupClassNameFromAlign (builtinPlacements, prefixCls, align)
} }
return '' return ''
} }
export function noop () {
export function saveRef (name, component) {
this[name] = component
} }

View File

@ -7,6 +7,10 @@ Checkbox | done
Radio | done Radio | done
Tabs | done Tabs | done
Tag | done Tag | done
ToolTip | done
Popconfirm
Popover | done
Menu
Carousel Carousel
Mention Mention
Input | done |select完成后补全demo Input | done |select完成后补全demo
@ -22,18 +26,14 @@ TimePicker
##万 ##万
Grid Grid
Col Col
ToolTip
Affix Affix
Alert Alert
BackTop BackTop
Dropdown Dropdown
Layout Layout
message message
Menu
Modal Modal
notification notification
Popconfirm
Popover
Anchor Anchor
Tree Tree
TreeSelect TreeSelect

View File

@ -1,3 +1,3 @@
.icon-test{ #app {
font-size: 35px; padding: 50px;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div v-html="marked($slots.default[0].text.trim() || '')" /> <div style="padding: 10px 0;" v-html="marked($slots.default[0].text.trim() || '')" />
</template> </template>
<script> <script>
import marked from 'marked' import marked from 'marked'
@ -16,7 +16,6 @@ marked.setOptions({
export default { export default {
name: 'md', name: 'md',
data () { data () {
console.log(this.$slots.default)
return { return {
marked, marked,
} }

View File

@ -67,10 +67,9 @@
"style-loader": "^0.18.2", "style-loader": "^0.18.2",
"stylelint": "^8.1.1", "stylelint": "^8.1.1",
"stylelint-config-standard": "^17.0.0", "stylelint-config-standard": "^17.0.0",
"vue": "^2.4.4",
"vue-loader": "^13.0.5", "vue-loader": "^13.0.5",
"vue-router": "^3.0.1", "vue-router": "^3.0.1",
"vue-template-compiler": "^2.4.4", "vue-template-compiler": "^2.5.13",
"webpack": "^3.6.0", "webpack": "^3.6.0",
"webpack-dev-server": "^2.8.2" "webpack-dev-server": "^2.8.2"
}, },