mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-11-27 04:09:51 +08:00
refactor(notification): support vue3
This commit is contained in:
parent
50580f5cfc
commit
89a819e344
@ -6,17 +6,19 @@
|
||||
:language="lang"
|
||||
>
|
||||
<n-message-provider>
|
||||
<n-layout position="absolute" class="root-layout">
|
||||
<doc-header
|
||||
:lang="lang"
|
||||
:items="flattenedItems"
|
||||
:env="env"
|
||||
@lang-change="handleLangChange"
|
||||
/>
|
||||
<n-layout class="home-layout" style="top: 64px; overflow: hidden;" position="absolute">
|
||||
<router-view />
|
||||
<n-notification-provider>
|
||||
<n-layout position="absolute" class="root-layout">
|
||||
<doc-header
|
||||
:lang="lang"
|
||||
:items="flattenedItems"
|
||||
:env="env"
|
||||
@lang-change="handleLangChange"
|
||||
/>
|
||||
<n-layout class="home-layout" style="top: 64px; overflow: hidden;" position="absolute">
|
||||
<router-view />
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</n-notification-provider>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
@ -1,17 +1,20 @@
|
||||
# 基础用法
|
||||
```html
|
||||
<n-button @click="notify1">
|
||||
<n-button @click="handleClick1">
|
||||
Wouldn't it be Nice
|
||||
</n-button>
|
||||
<n-button @click="notify2">
|
||||
<n-button @click="handleClick2">
|
||||
Satisfaction
|
||||
</n-button>
|
||||
```
|
||||
```js
|
||||
import { h, resolveComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
inject: ['notification', 'message'],
|
||||
methods: {
|
||||
notify1 () {
|
||||
this.$NNotification.open({
|
||||
handleClick1 () {
|
||||
this.notification.create({
|
||||
title: `Wouldn't it be Nice`,
|
||||
description: 'From the Beach Boys',
|
||||
content: `Wouldn't it be nice if we were older
|
||||
@ -25,47 +28,41 @@ In the morning when the day is new
|
||||
And after having spent the day together
|
||||
Hold each other close the whole night through`,
|
||||
meta: '2019-5-27 15:11',
|
||||
avatar: h =>
|
||||
h('n-avatar', {
|
||||
props: {
|
||||
size: 'small',
|
||||
round: true,
|
||||
src:'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
|
||||
}
|
||||
avatar: () =>
|
||||
h(resolveComponent('n-avatar'), {
|
||||
size: 'small',
|
||||
round: true,
|
||||
src:'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
|
||||
}),
|
||||
onAfterHide: () => {
|
||||
this.$NMessage.success(`Wouldn't it be Nice`)
|
||||
onAfterLeave: () => {
|
||||
this.message.success(`Wouldn't it be Nice`)
|
||||
},
|
||||
})
|
||||
},
|
||||
notify2 () {
|
||||
handleClick2 () {
|
||||
let markAsRead = false
|
||||
const notification = this.$NNotification.open({
|
||||
const notification = this.notification.create({
|
||||
title: 'Satisfaction',
|
||||
content: `I cant get no satisfaction
|
||||
I cant get no satisfaction
|
||||
Cause I try and I try and I try and I try
|
||||
I cant get no, I cant get no`,
|
||||
meta: '2019-5-27 15:11',
|
||||
action: h => h(
|
||||
'n-button',
|
||||
action: () => h(
|
||||
resolveComponent('n-button'),
|
||||
{
|
||||
props: {
|
||||
text: true,
|
||||
type: 'primary'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
markAsRead = true
|
||||
notification.hide()
|
||||
}
|
||||
text: true,
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
markAsRead = true
|
||||
notification.destroy()
|
||||
}
|
||||
},
|
||||
['已读']
|
||||
),
|
||||
onClose: () => {
|
||||
if (!markAsRead) {
|
||||
this.$NMessage.warning('请设为已读')
|
||||
this.message.warning('请设为已读')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,10 @@
|
||||
</n-button>
|
||||
```
|
||||
```js
|
||||
import { h, resolveComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
inject: ['notification'],
|
||||
data () {
|
||||
return {
|
||||
notification: null
|
||||
@ -17,7 +20,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
open () {
|
||||
this.notification = this.$NNotification.open({
|
||||
this.notification = this.notification.create({
|
||||
title: `Wouldn't it be Nice`,
|
||||
description: 'From the Beach Boys',
|
||||
content: `Wouldn't it be nice if we were older
|
||||
@ -31,13 +34,11 @@ In the morning when the day is new
|
||||
And after having spent the day together
|
||||
Hold each other close the whole night through`,
|
||||
meta: '2019-5-27 15:11',
|
||||
avatar: h =>
|
||||
h('n-avatar', {
|
||||
props: {
|
||||
size: 'small',
|
||||
round: true,
|
||||
src:'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
|
||||
}
|
||||
avatar: () =>
|
||||
h(resolveComponent('n-avatar'), {
|
||||
size: 'small',
|
||||
round: true,
|
||||
src:'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
|
||||
}),
|
||||
onClose: () => {
|
||||
this.notification = null
|
||||
@ -46,13 +47,9 @@ Hold each other close the whole night through`,
|
||||
},
|
||||
change () {
|
||||
if (this.notification) {
|
||||
this.notification.content = h => h('img', {
|
||||
attrs: {
|
||||
src: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
|
||||
},
|
||||
style: {
|
||||
width: '100%'
|
||||
}
|
||||
this.notification.content = () => h('img', {
|
||||
src: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
|
||||
style: 'width: 100%;'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,30 @@
|
||||
# 不可关闭
|
||||
通知可以不能被关闭
|
||||
```html
|
||||
<n-button @click="notify('info')">
|
||||
<n-button @click="handleClick">
|
||||
不能关闭
|
||||
</n-button>
|
||||
```
|
||||
```js
|
||||
export default {
|
||||
inject: ['notification'],
|
||||
methods: {
|
||||
notify (type) {
|
||||
this.$NNotification.open({
|
||||
title: `你能关掉我吗?`,
|
||||
handleClick () {
|
||||
const notification = this.notification
|
||||
notification.create({
|
||||
title: '你能关掉我吗?',
|
||||
duration: 2000,
|
||||
closable: false,
|
||||
onAfterHide: () => {
|
||||
this.$NNotification.open({
|
||||
onAfterLeave: () => {
|
||||
notification.create({
|
||||
title: `哈哈哈哈!`,
|
||||
duration: 2000,
|
||||
closable: false,
|
||||
onAfterHide: () => {
|
||||
this.$NNotification.open({
|
||||
onAfterLeave: () => {
|
||||
notification.create({
|
||||
title: `你不能`,
|
||||
duration: 2000,
|
||||
closable: false,
|
||||
closable: false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -1,21 +1,22 @@
|
||||
# 持续时间
|
||||
自动关闭。
|
||||
```html
|
||||
<n-button @click="notify('info')">
|
||||
<n-button @click="handleClick">
|
||||
持续时间 10000ms
|
||||
</n-button>
|
||||
```
|
||||
```js
|
||||
export default {
|
||||
inject: ['notification'],
|
||||
methods: {
|
||||
notify (type) {
|
||||
handleClick () {
|
||||
let count = 10
|
||||
const notification = this.$NNotification.open({
|
||||
const notification = this.notification.create({
|
||||
title: `平山道 + 雨 = 什么?`,
|
||||
content: `你有 ${count} 秒来回答这个问题`,
|
||||
duration: 10000,
|
||||
closable: false,
|
||||
onAfterShow: () => {
|
||||
onAfterEnter: () => {
|
||||
const minusCount = () => {
|
||||
count--
|
||||
notification.content = `你有 ${count} 秒来回答这个问题`
|
||||
@ -25,8 +26,8 @@ export default {
|
||||
}
|
||||
window.setTimeout(minusCount, 1000)
|
||||
},
|
||||
onAfterHide: () => {
|
||||
this.$NNotification.open({
|
||||
onAfterLeave: () => {
|
||||
this.notification.create({
|
||||
title: `答案是平山河`,
|
||||
content: '这其实连个冷笑话都算不上',
|
||||
duration: 10000
|
||||
|
@ -12,60 +12,60 @@ closable
|
||||
duration
|
||||
```
|
||||
## API
|
||||
#### $Notification API
|
||||
#### $NNotification Methods
|
||||
|名称|类型|说明|
|
||||
|-|-|-|
|
||||
|open|`(option: NotificationOption, type: string = 'default') => NotificationEnvironment`|`type` 可以是 `'default'`, `'warning'`, `'info'`, `'success'` 或 `'error'`|
|
||||
|success|`(option: NofiticationOption) => NotificationEnvironment`||
|
||||
|info|`(option: NofiticationOption) => NotificationEnvironment`||
|
||||
|warning|`(option: NofiticationOption) => NotificationEnvironment`||
|
||||
|error|`(option: NofiticationOption) => NotificationEnvironment`||
|
||||
|
||||
#### $Notification Properties
|
||||
|
||||
### NotificationProvider Props
|
||||
|名称|类型|默认值|说明|
|
||||
|-|-|-|-|
|
||||
|scrollable|`boolean`|`false`||
|
||||
|scrollable|`boolean`|`true`||
|
||||
|to|`string \| HTMLElement`|`'body'`||
|
||||
|
||||
### NotificationProvider Injection API
|
||||
#### NotificationProvider Injection Methods
|
||||
|名称|类型|说明|
|
||||
|-|-|-|
|
||||
|create|`(option: NotificationOption) => NotificationReactive`||
|
||||
|success|`(option: NotificationOption) => NotificationReactive`||
|
||||
|info|`(option: NotificationOption) => NotificationReactive`||
|
||||
|warning|`(option: NotificationOption) => NotificationReactive`||
|
||||
|error|`(option: NotificationOption) => NotificationReactive`||
|
||||
|
||||
### NotificationOption API
|
||||
#### NotificationOption Properties
|
||||
|
||||
|名称|类型|默认值|说明|
|
||||
|-|-|-|-|
|
||||
|theme|`'light' \| 'dark'`|`null`|如果设定会将该通知的主题设为该主题,如果没有设定则全局主题则取决于调用位置(它工作起来和 <n-a to="n-message#about-theme">$NMessage 的主题</n-a>比较像,在大多数情况下你不用为此而操心)|
|
||||
|avatar|`() => VNode \| Array<VNode>`|`null`|可以是 render 函数|
|
||||
|title|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|description|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|content|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|meta|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|action|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|avatar|`() => VNode \| Array<VNode>`|`null`|可以是 render 函数|
|
||||
|closable|`boolean`|`true`||
|
||||
|onClose|`() => boolean \| Promise<boolean> \| any`|`() => {}`|关闭通知的回调。返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭|
|
||||
|onAfterHide|`Function`|`null`||
|
||||
|onAfterShow|`Function`|`null`||
|
||||
|content|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|description|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|duration|`number`|`null`|如果没有设定则不会自动关闭,单位毫秒|
|
||||
|meta|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|theme|`'light' \| 'dark' \| null \| string`|`null`||
|
||||
|title|`string \| (() => VNode \| Array<VNode>)`|`null`|可以是 render 函数|
|
||||
|onAfterEnter|`Function`|`null`||
|
||||
|onAfterLeave|`Function`|`null`||
|
||||
|onClose|`() => boolean \| Promise<boolean>`|`() => {}`|关闭通知的回调。返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭|
|
||||
|onLeave|`Function`||
|
||||
|
||||
### NotificationEnvironment API
|
||||
#### NotificationEnvironment Properties
|
||||
### NotificationReactive API
|
||||
#### NotificationReactive Properties
|
||||
NofiticationEnvironment 实例的属性可以被动态改变。
|
||||
|
||||
|名称|类型|说明|
|
||||
|-|-|-|
|
||||
|theme|`'light' \| 'dark'`|如果设定会将该通知的主题设为该主题,如果没有设定则全局主题则取决于调用位置(它工作起来和 <n-a to="n-message#about-theme">$NMessage 的主题</n-a>比较像,在大多数情况下你不用为此而操心)|
|
||||
|avatar|`() => VNode \| Array<VNode>`|可以是 render 函数|
|
||||
|title|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|description|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|content|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|meta|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|action|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|avatar|`() => VNode \| Array<VNode>`|可以是 render 函数|
|
||||
|closable|`boolean`||
|
||||
|onClose|`(next: function) => any`|点击了关闭按钮的回调。只有调用了 next 通知才会被关闭|
|
||||
|onHide|`Function`||
|
||||
|onAfterHide|`Function`||
|
||||
|onAfterShow|`Function`||
|
||||
|content|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|description|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|meta|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|theme|`'light' \| 'dark' \| null \| string`||
|
||||
|title|`string \| (() => VNode \| Array<VNode>)`|可以是 render 函数|
|
||||
|onAfterEnter|`Function`||
|
||||
|onAfterLeave|`Function`||
|
||||
|onClose|`() => boolean \| Promise<boolean>`|`() => {}`|关闭通知的回调。返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭|
|
||||
|onLeave|`Function`||
|
||||
|
||||
#### NotificationEnvironment Methods
|
||||
#### NotificationReactive Methods
|
||||
|名称|类型|说明|
|
||||
|-|-|-|
|
||||
|hide|`()`||
|
||||
|destroy|`()`|销毁该通知|
|
@ -1,16 +1,27 @@
|
||||
# 可滚动
|
||||
如果有太多信息,你可以通过设定 `$NNotification.scrollable = true` 让他们变得可以滚动。但是在那种情况下,通知会比他们看起来的多占据一点点空间,会挡住一些通知外面离通知很近的鼠标操作。如果你不想要这个特性,什么都不做就好。
|
||||
如果有太多信息,通知的容器是可以滚动的。但是在那种情况下,通知会比他们看起来的多占据一点点空间,会挡住一些通知外面离通知很近的鼠标操作。如果你不想要这个特性,你可以通过设定 `<n-notification-provider :scrollable="false" />` 来使通知不可滚动。
|
||||
|
||||
改变这个属性会导致已经存在全部通知被清空,确保你在合适的时机修改了这个属性。
|
||||
```html
|
||||
<n-button @click="handleClick(true)">可以滚动(点完多开几个通知)</n-button>
|
||||
<n-button @click="handleClick(false)">不可以滚动</n-button>
|
||||
<n-button @click="handleClick">看看这个东西怎么滚动</n-button>
|
||||
```
|
||||
```js
|
||||
export default {
|
||||
inject: ['notification'],
|
||||
methods: {
|
||||
handleClick (scrollable) {
|
||||
this.$NNotification.scrollable = scrollable
|
||||
Array.apply(null, { length: 5 }).forEach(
|
||||
notification => this.notification.create({
|
||||
title: '很多个通知',
|
||||
content: `试着滚起来
|
||||
试着滚起来
|
||||
试着滚起来
|
||||
试着滚起来
|
||||
试着滚起来
|
||||
试着滚起来
|
||||
试着滚起来`
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,11 @@
|
||||
```
|
||||
```js
|
||||
export default {
|
||||
inject: ['notification'],
|
||||
methods: {
|
||||
notify (type) {
|
||||
this.$NNotification[type]({
|
||||
content: `说点啥呢`,
|
||||
this.notification[type]({
|
||||
content: '说点啥呢',
|
||||
meta: '想不出来'
|
||||
})
|
||||
}
|
||||
|
@ -44,4 +44,3 @@ export function useIsMounted () {
|
||||
}
|
||||
|
||||
export { default as useLastClickPosition } from './use-last-click-position'
|
||||
export { default as useContainer } from './use-container'
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { computed } from 'vue'
|
||||
|
||||
export default function useContainer (
|
||||
toRef,
|
||||
containerClassNameRef
|
||||
) {
|
||||
const getContainerTarget = computed(
|
||||
() => typeof toRef.value === 'string' ? () => document.querySelector(toRef.value) : () => toRef.value
|
||||
)
|
||||
const getContainer = computed(
|
||||
() => () => getContainerTarget.value().querySelector('.' + containerClassNameRef.value)
|
||||
)
|
||||
const mountIfNotExist = () => {
|
||||
const targetEl = getContainerTarget.value()
|
||||
if (!targetEl.querySelector('.' + containerClassNameRef.value)) {
|
||||
const containerEl = document.createElement('div')
|
||||
containerEl.className = containerClassNameRef.value
|
||||
targetEl.appendChild(containerEl)
|
||||
}
|
||||
}
|
||||
const unmountIfEmpty = () => {
|
||||
const container = getContainer.value()
|
||||
if (!container.childElementCount) {
|
||||
container.parentNode.removeChild(container)
|
||||
}
|
||||
}
|
||||
return [
|
||||
mountIfNotExist,
|
||||
unmountIfEmpty
|
||||
]
|
||||
}
|
@ -41,7 +41,7 @@ import Log from './log'
|
||||
import Menu from './menu'
|
||||
import Message from './message'
|
||||
import Modal from './modal'
|
||||
// import Notification from './notification'
|
||||
import Notification from './notification'
|
||||
import Pagination from './pagination'
|
||||
import Popconfirm from './popconfirm'
|
||||
import Popselect from './popselect'
|
||||
@ -232,7 +232,7 @@ export default create({
|
||||
Modal,
|
||||
Input,
|
||||
Message,
|
||||
// Notification,
|
||||
Notification,
|
||||
Pagination,
|
||||
Tooltip,
|
||||
Popup,
|
||||
|
@ -1,9 +1,7 @@
|
||||
import Notification from './src/NotificationPlugin'
|
||||
import { install } from '../_utils/naive/installThemeAwarableProperty'
|
||||
import NotificationProvider from './src/NotificationProvider'
|
||||
|
||||
Notification.install = function (app, naive) {
|
||||
Notification.Vue = app
|
||||
install(app, Notification, `$${naive.componentPrefix}Notification`)
|
||||
app.component(naive.componentPrefix + NotificationProvider.name, NotificationProvider)
|
||||
}
|
||||
|
||||
export default Notification
|
||||
|
@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
[`n-${theme}-theme`]: theme,
|
||||
'n-notification--no-avatar': noAvatar,
|
||||
[`n-${syntheticTheme}-theme`]: syntheticTheme,
|
||||
'n-notification--closable': closable,
|
||||
'n-notification--show-avatar': showAvatar,
|
||||
[`n-notification--${type}-type`]: type
|
||||
}"
|
||||
class="n-notification"
|
||||
>
|
||||
<div
|
||||
v-if="!noAvatar"
|
||||
v-if="showAvatar"
|
||||
class="n-notification__avatar"
|
||||
>
|
||||
<render v-if="avatar" :render="avatar" />
|
||||
<n-icon v-else>
|
||||
<md-information-circle v-if="type === 'info'" />
|
||||
<md-alert v-else-if="type === 'warning'" />
|
||||
<md-close-circle v-else-if="type === 'error'" />
|
||||
<md-checkmark-circle v-else-if="type === 'success'" />
|
||||
<info-icon v-if="type === 'info'" />
|
||||
<warning-icon v-else-if="type === 'warning'" />
|
||||
<error-icon v-else-if="type === 'error'" />
|
||||
<success-icon v-else-if="type === 'success'" />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div
|
||||
@ -26,7 +26,7 @@
|
||||
@click="handleCloseClick"
|
||||
>
|
||||
<n-icon>
|
||||
<md-close />
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</div>
|
||||
<div
|
||||
@ -56,32 +56,32 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import withapp from '../../_mixins/withapp'
|
||||
import themeable from '../../_mixins/themeable'
|
||||
import NIcon from '../../icon'
|
||||
import mdClose from '../../_icons/md-close'
|
||||
import mdCheckmarkCircle from '../../_icons/md-checkmark-circle'
|
||||
import mdAlert from '../../_icons/md-alert'
|
||||
import mdInformationCircle from '../../_icons/md-information-circle'
|
||||
import mdCloseCircle from '../../_icons/md-close-circle'
|
||||
import render from '../../_utils/vue/render'
|
||||
import asthemecontext from '../../_mixins/asthemecontext'
|
||||
import usecssr from '../../_mixins/usecssr'
|
||||
import render from '../../_utils/vue/render'
|
||||
import styles from './styles'
|
||||
import NIcon from '../../icon'
|
||||
import CloseIcon from '../../_icons/md-close'
|
||||
import SuccessIcon from '../../_icons/md-checkmark-circle'
|
||||
import WarningIcon from '../../_icons/md-alert'
|
||||
import InfoIcon from '../../_icons/md-information-circle'
|
||||
import ErrorIcon from '../../_icons/md-close-circle'
|
||||
|
||||
export default {
|
||||
name: 'Notification',
|
||||
components: {
|
||||
NIcon,
|
||||
mdClose,
|
||||
render,
|
||||
mdCheckmarkCircle,
|
||||
mdAlert,
|
||||
mdInformationCircle,
|
||||
mdCloseCircle
|
||||
CloseIcon,
|
||||
SuccessIcon,
|
||||
WarningIcon,
|
||||
InfoIcon,
|
||||
ErrorIcon
|
||||
},
|
||||
cssrName: 'Notification',
|
||||
mixins: [
|
||||
withapp,
|
||||
themeable,
|
||||
asthemecontext,
|
||||
usecssr(styles)
|
||||
],
|
||||
props: {
|
||||
@ -96,7 +96,7 @@ export default {
|
||||
default: 'default'
|
||||
},
|
||||
avatar: {
|
||||
type: [Function],
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
title: {
|
||||
@ -116,18 +116,22 @@ export default {
|
||||
default: null
|
||||
},
|
||||
action: {
|
||||
type: [Object, Function],
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
onClose: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
noAvatar () {
|
||||
return !(this.avatar || this.type !== 'default')
|
||||
showAvatar () {
|
||||
return this.avatar || this.type !== 'default'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCloseClick () {
|
||||
this.$emit('close')
|
||||
this.onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,12 @@
|
||||
'n-notification-container--scrollable': scrollable
|
||||
}"
|
||||
>
|
||||
<n-scrollbar v-if="scrollable" ref="scrollbar" :theme="theme" />
|
||||
<n-scrollbar
|
||||
v-if="scrollable"
|
||||
>
|
||||
<slot />
|
||||
</n-scrollbar>
|
||||
<slot v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -13,18 +18,14 @@
|
||||
import NScrollbar from '../../scrollbar'
|
||||
|
||||
export default {
|
||||
name: 'NotificationContainer',
|
||||
components: {
|
||||
NScrollbar
|
||||
},
|
||||
props: {
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
theme: null
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
178
src/notification/src/NotificationEnvironment.js
Normal file
178
src/notification/src/NotificationEnvironment.js
Normal file
@ -0,0 +1,178 @@
|
||||
import { nextTick, Transition, h } from 'vue'
|
||||
import NNotification from './Notification'
|
||||
|
||||
export default {
|
||||
name: 'NotificationEnvironment',
|
||||
props: {
|
||||
duration: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
avatar: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
title: {
|
||||
type: [String, Function],
|
||||
default: undefined
|
||||
},
|
||||
description: {
|
||||
type: [String, Function],
|
||||
default: undefined
|
||||
},
|
||||
content: {
|
||||
type: [String, Function],
|
||||
default: undefined
|
||||
},
|
||||
meta: {
|
||||
type: [String, Function],
|
||||
default: undefined
|
||||
},
|
||||
action: {
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
onClose: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
onHide: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
onAfterEnter: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
onAfterLeave: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
// private
|
||||
onInternalAfterLeave: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
// deprecated
|
||||
onAfterShow: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
onAfterHide: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
show: true,
|
||||
timerId: null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.duration) {
|
||||
this.timerId = window.setTimeout(this.hide, this.duration)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hide () {
|
||||
const {
|
||||
timerId
|
||||
} = this
|
||||
this.show = false
|
||||
if (timerId) {
|
||||
window.clearTimeout(timerId)
|
||||
}
|
||||
},
|
||||
handleBeforeEnter (el) {
|
||||
nextTick(() => {
|
||||
el.style.height = el.offsetHeight + 'px'
|
||||
el.style.maxHeight = 0
|
||||
el.style.transition = 'none'
|
||||
void el.offsetHeight
|
||||
el.style.transition = null
|
||||
el.style.maxHeight = el.style.height
|
||||
})
|
||||
},
|
||||
handleAfterEnter (el) {
|
||||
el.style.height = null
|
||||
el.style.maxHeight = null
|
||||
this.onAfterEnter(this)
|
||||
// deprecated
|
||||
this.onAfterShow(this)
|
||||
},
|
||||
handleBeforeLeave (el) {
|
||||
el.style.maxHeight = el.offsetHeight + 'px'
|
||||
el.style.height = el.offsetHeight + 'px'
|
||||
void el.offsetHeight
|
||||
},
|
||||
handleLeave (el) {
|
||||
this.onHide()
|
||||
el.style.maxHeight = 0
|
||||
void el.offsetHeight
|
||||
},
|
||||
handleAfterLeave () {
|
||||
const {
|
||||
onAfterLeave,
|
||||
onInternalAfterLeave,
|
||||
onAfterHide
|
||||
} = this
|
||||
onAfterLeave()
|
||||
onInternalAfterLeave(this._.vnode.key)
|
||||
// deprecated
|
||||
onAfterHide()
|
||||
},
|
||||
handleClose () {
|
||||
Promise
|
||||
.resolve(
|
||||
this.onClose()
|
||||
)
|
||||
.then(feedback => {
|
||||
if (feedback === false) return
|
||||
this.hide()
|
||||
})
|
||||
},
|
||||
// deprecated
|
||||
deactivate () {
|
||||
this.hide()
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return h(Transition, {
|
||||
name: 'n-notification-transition',
|
||||
appear: true,
|
||||
onBeforeEnter: this.handleBeforeEnter,
|
||||
onAfterEnter: this.handleAfterEnter,
|
||||
onBeforeLeave: this.handleBeforeLeave,
|
||||
onLeave: this.handleLeave,
|
||||
onAfterLeave: this.handleAfterLeave
|
||||
}, {
|
||||
default: () => [
|
||||
this.show ? h(NNotification, {
|
||||
type: this.type,
|
||||
theme: this.theme,
|
||||
avatar: this.avatar,
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
content: this.content,
|
||||
meta: this.meta,
|
||||
action: this.action,
|
||||
closable: this.closable,
|
||||
onClose: this.handleClose
|
||||
}) : null
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
<script>
|
||||
import NNotification from './Notification'
|
||||
|
||||
export default {
|
||||
name: 'NNotificationEnvironment',
|
||||
props: {
|
||||
onDestroy: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
type: 'default',
|
||||
active: true,
|
||||
theme: null,
|
||||
inheritedTheme: null,
|
||||
avatar: null,
|
||||
title: null,
|
||||
description: null,
|
||||
content: null,
|
||||
meta: null,
|
||||
action: null,
|
||||
closable: true,
|
||||
onClose: () => {},
|
||||
onHide: () => {},
|
||||
onAfterShow: () => {},
|
||||
onAfterHide: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
syntheticTheme () {
|
||||
return this.theme || this.inheritedTheme
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.duration !== null) {
|
||||
window.setTimeout(this.hide, this.duration)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deactivate () {
|
||||
this.hide()
|
||||
},
|
||||
hide () {
|
||||
this.active = false
|
||||
},
|
||||
handleEnter () {
|
||||
this.$nextTick().then(() => {
|
||||
this.$el.style.height = this.$el.offsetHeight + 'px'
|
||||
this.$el.style.maxHeight = 0
|
||||
this.$el.style.transition = 'none'
|
||||
this.$el.getBoundingClientRect()
|
||||
this.$el.style.transition = null
|
||||
this.$el.style.maxHeight = this.$el.style.height
|
||||
})
|
||||
},
|
||||
handleAfterEnter () {
|
||||
this.$el.style.height = null
|
||||
this.$el.style.maxHeight = null
|
||||
this.onAfterShow(this)
|
||||
},
|
||||
handleBeforeLeave () {
|
||||
this.$el.style.maxHeight = this.$el.offsetHeight + 'px'
|
||||
this.$el.style.height = this.$el.offsetHeight + 'px'
|
||||
this.$el.getBoundingClientRect()
|
||||
},
|
||||
handleLeave () {
|
||||
this.onHide()
|
||||
this.$el.style.maxHeight = 0
|
||||
this.$el.getBoundingClientRect()
|
||||
},
|
||||
handleAfterLeave () {
|
||||
this.onDestroy(this)
|
||||
this.onAfterHide()
|
||||
},
|
||||
handleClose () {
|
||||
Promise
|
||||
.resolve(
|
||||
this.onClose()
|
||||
)
|
||||
.then(feedback => {
|
||||
if (feedback === false) return
|
||||
this.hide()
|
||||
})
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
return h('transition', {
|
||||
props: {
|
||||
name: 'n-notification-transition',
|
||||
appear: true
|
||||
},
|
||||
on: {
|
||||
'enter': this.handleEnter,
|
||||
'after-enter': this.handleAfterEnter,
|
||||
'before-leave': this.handleBeforeLeave,
|
||||
'leave': this.handleLeave,
|
||||
'after-leave': this.handleAfterLeave
|
||||
}
|
||||
}, [
|
||||
this.active ? h(NNotification, {
|
||||
props: {
|
||||
type: this.type,
|
||||
theme: this.syntheticTheme,
|
||||
avatar: this.avatar,
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
content: this.content,
|
||||
meta: this.meta,
|
||||
action: this.action,
|
||||
closable: this.closable
|
||||
},
|
||||
on: {
|
||||
close: () => {
|
||||
this.handleClose()
|
||||
}
|
||||
}
|
||||
}) : null
|
||||
])
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,136 +0,0 @@
|
||||
import NNotificationEnvironment from './NotificationEnvironment'
|
||||
import NNotificationContainer from './NotificationContainer'
|
||||
|
||||
function mountNotificationContainer () {
|
||||
let container = Notification.container
|
||||
if (!container) {
|
||||
container = new Notification.Vue(Object.assign(NNotificationContainer, {
|
||||
propsData: {
|
||||
scrollable: Notification.scrollable
|
||||
}
|
||||
}))
|
||||
container.$mount()
|
||||
Notification.container = container
|
||||
document.body.appendChild(container.$el)
|
||||
}
|
||||
return container
|
||||
}
|
||||
|
||||
function unmountNotificationContainer () {
|
||||
const container = Notification.container
|
||||
if (Notification.instances.size) {
|
||||
const instances = Array.from(Notification.instances)
|
||||
instances.forEach(unmountNotification)
|
||||
}
|
||||
if (container) {
|
||||
const el = container.$el
|
||||
if (el && el.parentElement) {
|
||||
el.parentElement.removeChild(el)
|
||||
}
|
||||
container.$destroy()
|
||||
Notification.container = null
|
||||
}
|
||||
}
|
||||
|
||||
function createNotification (option) {
|
||||
const instance = new Notification.Vue(Object.assign(
|
||||
NNotificationEnvironment,
|
||||
{
|
||||
propsData: {
|
||||
onDestroy: unmountNotification,
|
||||
duration: option.duration
|
||||
}
|
||||
}
|
||||
))
|
||||
updateNotification(instance, option)
|
||||
return instance
|
||||
}
|
||||
|
||||
function mountNotification (instance) {
|
||||
if (!Notification.container) {
|
||||
throw new Error('[naive-ui/notification]: container not exist when try to mount notification')
|
||||
}
|
||||
Notification.instances.add(instance)
|
||||
instance.$mount()
|
||||
const el = instance.$el
|
||||
if (Notification.scrollable) {
|
||||
const slot = Notification.container.$refs.scrollbar.$refs.scrollContent
|
||||
slot.appendChild(el)
|
||||
} else {
|
||||
const slot = Notification.container.$el
|
||||
slot.appendChild(el)
|
||||
}
|
||||
}
|
||||
|
||||
function unmountNotification (instance) {
|
||||
Notification.instances.delete(instance)
|
||||
const el = instance.$el
|
||||
if (el && el.parentElement) {
|
||||
el.parentElement.removeChild(el)
|
||||
}
|
||||
instance.$destroy()
|
||||
if (!Notification.instances.size) {
|
||||
unmountNotificationContainer()
|
||||
}
|
||||
}
|
||||
|
||||
function updateNotification (instance, option) {
|
||||
Object.keys(option).forEach(key => {
|
||||
if (instance.hasOwnProperty(key)) {
|
||||
instance[key] = option[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const Notification = {
|
||||
theme: null,
|
||||
inheritedTheme: null,
|
||||
instances: new Set(),
|
||||
container: null,
|
||||
_scrollable: false,
|
||||
get scrollable () {
|
||||
return Notification._scrollable
|
||||
},
|
||||
set scrollable (value) {
|
||||
if (value !== Notification._scrollable) {
|
||||
Notification._scrollable = value
|
||||
unmountNotificationContainer()
|
||||
}
|
||||
},
|
||||
handleThemeChange (theme) {
|
||||
const container = Notification.container
|
||||
Notification.inheritedTheme = theme
|
||||
const syntheticTheme = Notification.theme || Notification.inheritedTheme
|
||||
if (container) {
|
||||
container.theme = syntheticTheme
|
||||
}
|
||||
Notification.instances.forEach(instance => {
|
||||
instance.inheritedTheme = syntheticTheme
|
||||
})
|
||||
},
|
||||
open (options, type = 'default') {
|
||||
mountNotificationContainer()
|
||||
const syntheticTheme = Notification.theme || Notification.inheritedTheme
|
||||
if (Notification.container && syntheticTheme) {
|
||||
Notification.container.theme = syntheticTheme
|
||||
}
|
||||
const notificationOptions = { type, ...options, inheritedTheme: syntheticTheme }
|
||||
const instance = createNotification(notificationOptions)
|
||||
mountNotification(instance)
|
||||
return instance
|
||||
},
|
||||
success (option) {
|
||||
return this.open(option, 'success')
|
||||
},
|
||||
info (option) {
|
||||
return this.open(option, 'info')
|
||||
},
|
||||
warning (option) {
|
||||
return this.open(option, 'warning')
|
||||
},
|
||||
error (option) {
|
||||
return this.open(option, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
export default Notification
|
92
src/notification/src/NotificationProvider.js
Normal file
92
src/notification/src/NotificationProvider.js
Normal file
@ -0,0 +1,92 @@
|
||||
import { Fragment, h, Teleport, reactive, ref } from 'vue'
|
||||
import createId from '../../_utils/vue/createId'
|
||||
import NotificationContainer from './NotificationContainer'
|
||||
import NotificationEnvironment from './NotificationEnvironment'
|
||||
import omit from '../../_utils/vue/omit'
|
||||
|
||||
export default {
|
||||
name: 'NotificationProvider',
|
||||
provide () {
|
||||
return {
|
||||
notification: {
|
||||
create: this.create,
|
||||
info: this.info,
|
||||
success: this.success,
|
||||
warning: this.warning,
|
||||
error: this.error
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
to: {
|
||||
type: [String, Object],
|
||||
default: 'body'
|
||||
},
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
setup () {
|
||||
const notificationListRef = ref([])
|
||||
return {
|
||||
notificationList: notificationListRef
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
create (options) {
|
||||
const key = createId()
|
||||
const notificationReactive = reactive({
|
||||
...options,
|
||||
key,
|
||||
destroy: () => this.$refs[`n-notification-${key}`].hide()
|
||||
})
|
||||
this.notificationList.push(notificationReactive)
|
||||
return notificationReactive
|
||||
},
|
||||
...[
|
||||
'info',
|
||||
'success',
|
||||
'warning',
|
||||
'error'
|
||||
].reduce((api, type) => {
|
||||
api[type] = function (options) {
|
||||
return this.create({ ...options, type })
|
||||
}
|
||||
return api
|
||||
}, {}),
|
||||
handleAfterLeave (key) {
|
||||
const { notificationList } = this
|
||||
notificationList.splice(
|
||||
notificationList.findIndex(notification => notification.key === key),
|
||||
1
|
||||
)
|
||||
},
|
||||
// deprecated
|
||||
open (...args) {
|
||||
return this.create(...args)
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return h(Fragment, null, [
|
||||
h(Teleport, {
|
||||
to: this.to
|
||||
}, [
|
||||
this.notificationList.length ? h(NotificationContainer, {
|
||||
scrollable: this.scrollable
|
||||
}, {
|
||||
default: () => {
|
||||
return this.notificationList.map(
|
||||
notification => h(NotificationEnvironment, {
|
||||
ref: `n-notification-${notification.key}`,
|
||||
...omit(notification, ['destroy']),
|
||||
onInternalAfterLeave: this.handleAfterLeave
|
||||
})
|
||||
)
|
||||
}
|
||||
}) : null
|
||||
]),
|
||||
this.$slots.default()
|
||||
])
|
||||
}
|
||||
}
|
@ -109,24 +109,24 @@ export default c([
|
||||
// TODO: refactor type styles & transition
|
||||
['success', 'info', 'warning', 'error', 'default']
|
||||
.map(type => typeStyle(type, props.$local[createKey('iconColor', type)])),
|
||||
c('&-transition-enter, &-transition-leave-to', {
|
||||
c('&-transition-enter-from, &-transition-leave-to', {
|
||||
raw: `
|
||||
opacity: 0;
|
||||
margin-bottom: 0;
|
||||
transform: translateX(calc(100% + 16px));
|
||||
`
|
||||
}),
|
||||
c('&-transition-leave, &-transition-enter-to', {
|
||||
c('&-transition-leave-from, &-transition-enter-to', {
|
||||
raw: `
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
`
|
||||
}),
|
||||
cM('no-avatar', [
|
||||
cM('show-avatar', [
|
||||
cB('notification-main', {
|
||||
raw: `
|
||||
margin-left: 8px;
|
||||
width: calc(100% - 8px);
|
||||
margin-left: 40px;
|
||||
width: calc(100% - 40px);
|
||||
`
|
||||
})
|
||||
]),
|
||||
@ -188,8 +188,8 @@ export default c([
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 40px;
|
||||
width: calc(100% - 40px);
|
||||
margin-left: 8px;
|
||||
width: calc(100% - 8px);
|
||||
`
|
||||
}, [
|
||||
cB('notification-main-footer', {
|
||||
|
6
vue3.md
6
vue3.md
@ -52,7 +52,7 @@ placeable 进行了大调整
|
||||
- deprecate `onAfterHide`, new `onAfterLeave`
|
||||
- remove `hide` on message instance, new `destroy`
|
||||
- TODO: update enUS docs
|
||||
- [ ] modal
|
||||
- [x] modal
|
||||
- rewrite with teleport
|
||||
- deprecate v-model show hide
|
||||
- add prop `display-directive`
|
||||
@ -60,6 +60,10 @@ placeable 进行了大调整
|
||||
- deprecate `overlay-style`, use `body-style`
|
||||
- TODO: update docs, scrollbar mouseup
|
||||
- [ ] notification
|
||||
- deprecate `open`, use `create`
|
||||
- deprecate `onHide`, use `onLeave`
|
||||
- deprecate `onAfterShow`, use `onAfterEnter`
|
||||
- deprecate `onAfterHide`, use `onAfterHide`
|
||||
- [ ] pagination
|
||||
- [ ] popconfirm
|
||||
- [ ] popover
|
||||
|
Loading…
Reference in New Issue
Block a user