add notification and fix tag bug
parent
1906809049
commit
1e6f60d8d2
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
setState (state, callback) {
|
setState (state, callback) {
|
||||||
Object.assign(this.$data, state)
|
Object.assign(this.$data, typeof state === 'function' ? state(this.$data) : state)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
callback && callback()
|
callback && callback()
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Safe chained function
|
||||||
|
*
|
||||||
|
* Will only create a new function if needed,
|
||||||
|
* otherwise will pass back existing functions or null.
|
||||||
|
*
|
||||||
|
* @returns {function|null}
|
||||||
|
*/
|
||||||
|
export default function createChainedFunction () {
|
||||||
|
const args = [].slice.call(arguments, 0)
|
||||||
|
if (args.length === 1) {
|
||||||
|
return args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return function chainedFunction () {
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (args[i] && args[i].apply) {
|
||||||
|
args[i].apply(this, arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import animate from './css-animation'
|
||||||
|
const noop = () => {}
|
||||||
|
const getTransitionProps = (transitionName, opt = {}) => {
|
||||||
|
const { beforeEnter, enter, leave, afterLeave, appear = true, tag } = opt
|
||||||
|
const transitionProps = {
|
||||||
|
props: {
|
||||||
|
appear,
|
||||||
|
css: false,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
beforeEnter: beforeEnter || noop,
|
||||||
|
enter: enter || ((el, done) => {
|
||||||
|
animate(el, `${transitionName}-enter`, done)
|
||||||
|
}),
|
||||||
|
leave: leave || ((el, done) => {
|
||||||
|
animate(el, `${transitionName}-leave`, done)
|
||||||
|
}),
|
||||||
|
afterLeave: afterLeave || noop,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// transition-group
|
||||||
|
if (tag) {
|
||||||
|
transitionProps.tag = tag
|
||||||
|
}
|
||||||
|
return transitionProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getTransitionProps
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { getStyle } from '../../_util/vnode'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: {
|
||||||
|
duration: PropTypes.number.def(1.5),
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted () {
|
||||||
|
this.startCloseTimer()
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestory () {
|
||||||
|
this.clearCloseTimer()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
close () {
|
||||||
|
this.clearCloseTimer()
|
||||||
|
this._emit('close')
|
||||||
|
},
|
||||||
|
|
||||||
|
startCloseTimer () {
|
||||||
|
if (this.duration) {
|
||||||
|
this.closeTimer = setTimeout(() => {
|
||||||
|
this.close()
|
||||||
|
}, this.duration * 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearCloseTimer () {
|
||||||
|
if (this.closeTimer) {
|
||||||
|
clearTimeout(this.closeTimer)
|
||||||
|
this.closeTimer = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { prefixCls, closable, clearCloseTimer, startCloseTimer, $slots, close } = this
|
||||||
|
const componentClass = `${prefixCls}-notice`
|
||||||
|
const className = {
|
||||||
|
[`${componentClass}`]: 1,
|
||||||
|
[`${componentClass}-closable`]: closable,
|
||||||
|
}
|
||||||
|
const style = getStyle(this)
|
||||||
|
return (
|
||||||
|
<div class={className} style={style || { right: '50%' } } onMouseenter={clearCloseTimer}
|
||||||
|
onMouseleave={startCloseTimer}
|
||||||
|
>
|
||||||
|
<div class={`${componentClass}-content`}>{$slots.default}</div>
|
||||||
|
{closable
|
||||||
|
? <a tabIndex='0' onClick={close} class={`${componentClass}-close`}>
|
||||||
|
<span class={`${componentClass}-close-x`}></span>
|
||||||
|
</a> : null
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,145 @@
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import PropTypes from '../../_util/vue-types'
|
||||||
|
import { getStyle } from '../../_util/vnode'
|
||||||
|
import BaseMixin from '../../_util/BaseMixin'
|
||||||
|
import createChainedFunction from '../../_util/createChainedFunction'
|
||||||
|
import getTransitionProps from '../../_util/getTransitionProps'
|
||||||
|
import Notice from './Notice'
|
||||||
|
|
||||||
|
let seed = 0
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
function getUuid () {
|
||||||
|
return `rcNotification_${now}_${seed++}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const Notification = {
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: {
|
||||||
|
prefixCls: PropTypes.string.def('rc-notification'),
|
||||||
|
transitionName: PropTypes.string,
|
||||||
|
animation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).def('fade'),
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
notices: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTransitionName () {
|
||||||
|
const props = this.$props
|
||||||
|
let transitionName = props.transitionName
|
||||||
|
if (!transitionName && props.animation) {
|
||||||
|
transitionName = `${props.prefixCls}-${props.animation}`
|
||||||
|
}
|
||||||
|
return transitionName
|
||||||
|
},
|
||||||
|
|
||||||
|
add (notice) {
|
||||||
|
const key = notice.key = notice.key || getUuid()
|
||||||
|
this.setState(previousState => {
|
||||||
|
const notices = previousState.notices
|
||||||
|
if (!notices.filter(v => v.key === key).length) {
|
||||||
|
return {
|
||||||
|
notices: notices.concat(notice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
remove (key) {
|
||||||
|
this.setState(previousState => {
|
||||||
|
return {
|
||||||
|
notices: previousState.notices.filter(notice => notice.key !== key),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { prefixCls, notices, remove, getTransitionName } = this
|
||||||
|
const noticeNodes = notices.map((notice) => {
|
||||||
|
const onClose = createChainedFunction(remove.bind(this, notice.key), notice.on.close)
|
||||||
|
const noticeProps = {
|
||||||
|
...notice,
|
||||||
|
props: {
|
||||||
|
prefixCls,
|
||||||
|
...notice.props,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
...notice.on,
|
||||||
|
close: onClose,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (<Notice
|
||||||
|
{...noticeProps}
|
||||||
|
>
|
||||||
|
{notice.content}
|
||||||
|
</Notice>)
|
||||||
|
})
|
||||||
|
const className = {
|
||||||
|
[prefixCls]: 1,
|
||||||
|
}
|
||||||
|
const style = getStyle(this)
|
||||||
|
return (
|
||||||
|
<div class={className} style={style || {
|
||||||
|
top: '65px',
|
||||||
|
left: '50%',
|
||||||
|
}}>
|
||||||
|
<transition-group {...getTransitionProps(getTransitionName(), { tag: 'div' })}>{noticeNodes}</transition-group>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification.newInstance = function newNotificationInstance (properties, callback) {
|
||||||
|
const { getContainer, style, class: className, on = {}, ...props } = properties || {}
|
||||||
|
const div = document.createElement('div')
|
||||||
|
if (getContainer) {
|
||||||
|
const root = getContainer()
|
||||||
|
root.appendChild(div)
|
||||||
|
} else {
|
||||||
|
document.body.appendChild(div)
|
||||||
|
}
|
||||||
|
const notificationInstance = new Vue({
|
||||||
|
el: div,
|
||||||
|
mounted () {
|
||||||
|
const self = this
|
||||||
|
this.$nextTick(() => {
|
||||||
|
callback({
|
||||||
|
notice (noticeProps) {
|
||||||
|
self.$refs.notification.add(noticeProps)
|
||||||
|
},
|
||||||
|
removeNotice (key) {
|
||||||
|
self.$refs.notification.remove(key)
|
||||||
|
},
|
||||||
|
component: self,
|
||||||
|
destroy () {
|
||||||
|
// self.$destroy()
|
||||||
|
notificationInstance.$destroy()
|
||||||
|
div.parentNode.removeChild(div)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render () {
|
||||||
|
const p = {
|
||||||
|
props,
|
||||||
|
on,
|
||||||
|
ref: 'notification',
|
||||||
|
style,
|
||||||
|
class: className,
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Notification
|
||||||
|
{...p}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Notification
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,2 @@
|
||||||
|
import Notification from './Notification'
|
||||||
|
export default Notification
|
|
@ -1,25 +1,10 @@
|
||||||
<template>
|
|
||||||
<transition
|
|
||||||
:name="`${prefixCls}-zoom`"
|
|
||||||
appear
|
|
||||||
@after-leave="animationEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="!closed"
|
|
||||||
:class="classes"
|
|
||||||
:style="tagStyle"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
<Icon v-if="closable" type="cross" @click="close" />
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
|
||||||
<script>
|
<script>
|
||||||
import Icon from '../icon'
|
import Icon from '../icon'
|
||||||
|
import getTransitionProps from '../_util/getTransitionProps'
|
||||||
|
import omit from 'omit.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Tag',
|
name: 'Tag',
|
||||||
components: { Icon },
|
|
||||||
props: {
|
props: {
|
||||||
prefixCls: {
|
prefixCls: {
|
||||||
default: 'ant-tag',
|
default: 'ant-tag',
|
||||||
|
@ -27,22 +12,20 @@ export default {
|
||||||
},
|
},
|
||||||
color: String,
|
color: String,
|
||||||
closable: Boolean,
|
closable: Boolean,
|
||||||
styles: {
|
|
||||||
default: () => ({}),
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
const isPresetColor = (color) => {
|
|
||||||
if (!color) { return false }
|
|
||||||
return /^(pink|red|yellow|orange|cyan|green|blue|purple)(-inverse)?$/.test(color)
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
closed: false,
|
closed: false,
|
||||||
isPresetColor: isPresetColor(this.color),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isPresetColor () {
|
||||||
|
const isPresetColor = (color) => {
|
||||||
|
if (!color) { return false }
|
||||||
|
return /^(pink|red|yellow|orange|cyan|green|blue|purple)(-inverse)?$/.test(color)
|
||||||
|
}
|
||||||
|
return isPresetColor(this.color)
|
||||||
|
},
|
||||||
classes () {
|
classes () {
|
||||||
const { prefixCls, color, isPresetColor } = this
|
const { prefixCls, color, isPresetColor } = this
|
||||||
return {
|
return {
|
||||||
|
@ -52,16 +35,16 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tagStyle () {
|
tagStyle () {
|
||||||
const { color, styles, isPresetColor } = this
|
const { color, isPresetColor } = this
|
||||||
|
console.log(color, isPresetColor)
|
||||||
return {
|
return {
|
||||||
backgroundColor: (color && !isPresetColor) ? color : null,
|
backgroundColor: (color && !isPresetColor) ? color : null,
|
||||||
...styles,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
animationEnd () {
|
animationEnd () {
|
||||||
this.$emit('after-close')
|
this.$emit('afterClose')
|
||||||
},
|
},
|
||||||
close (e) {
|
close (e) {
|
||||||
this.$emit('close', e)
|
this.$emit('close', e)
|
||||||
|
@ -71,5 +54,31 @@ export default {
|
||||||
this.closed = true
|
this.closed = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
render () {
|
||||||
|
const { prefixCls, animationEnd, classes, tagStyle, closable, close, closed, $slots, $listeners } = this
|
||||||
|
const transitionProps = getTransitionProps(`${prefixCls}-zoom`, {
|
||||||
|
afterLeave: animationEnd,
|
||||||
|
})
|
||||||
|
// const tagProps = {
|
||||||
|
// on
|
||||||
|
// }
|
||||||
|
return (
|
||||||
|
<transition
|
||||||
|
{...transitionProps}
|
||||||
|
>
|
||||||
|
{!closed
|
||||||
|
? <div
|
||||||
|
|
||||||
|
class={classes}
|
||||||
|
style={tagStyle}
|
||||||
|
{...{ on: omit($listeners, ['close', 'afterClose']) }}
|
||||||
|
>
|
||||||
|
{$slots.default}
|
||||||
|
{closable ? <Icon type='cross' onClick={close} /> : null}
|
||||||
|
</div> : null
|
||||||
|
}
|
||||||
|
</transition>
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<div>
|
<div>
|
||||||
<template v-for="(tag, index) in tags">
|
<template v-for="(tag, index) in tags">
|
||||||
<Tooltip v-if="tag.length > 20" :key="tag" :title="tag">
|
<Tooltip v-if="tag.length > 20" :key="tag" :title="tag">
|
||||||
<Tag :key="tag" :closable="index !== 0" @after-close="() => handleClose(tag)">
|
<Tag :key="tag" :closable="index !== 0" @afterClose="() => handleClose(tag)">
|
||||||
{{`${tag.slice(0, 20)}...`}}
|
{{`${tag.slice(0, 20)}...`}}
|
||||||
</Tag>
|
</Tag>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tag v-else :key="tag" :closable="index !== 0" @after-close="() => handleClose(tag)">
|
<Tag v-else :key="tag" :closable="index !== 0" @afterClose="() => handleClose(tag)">
|
||||||
{{tag}}
|
{{tag}}
|
||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in New Issue