238 lines
4.6 KiB
JavaScript
238 lines
4.6 KiB
JavaScript
|
/**
|
||
|
* {通知组件}
|
||
|
* @author yutent<yutent.io@gmail.com>
|
||
|
* @date 2023/04/10 17:17:10
|
||
|
*/
|
||
|
|
||
|
import { css, html, Component, bind, styleMap } from 'wkit'
|
||
|
import '../base/icon.js'
|
||
|
|
||
|
const ANIMATION = [
|
||
|
{ transform: 'translateX(60%)', opacity: 0 },
|
||
|
{ transform: 'translateX(0)', opacity: 1 }
|
||
|
]
|
||
|
|
||
|
const DEFAULT_OPT = {
|
||
|
icon: '',
|
||
|
image: '',
|
||
|
body: '',
|
||
|
progress: 0,
|
||
|
duration: 4500
|
||
|
}
|
||
|
|
||
|
let notifyIns = null
|
||
|
|
||
|
function merge(opt = {}) {
|
||
|
let output = { ...DEFAULT_OPT }
|
||
|
|
||
|
for (let k in opt) {
|
||
|
if (opt[k] !== void 0) {
|
||
|
output[k] = opt[k]
|
||
|
}
|
||
|
}
|
||
|
return output
|
||
|
}
|
||
|
|
||
|
class NotifyGroup extends Component {
|
||
|
static styles = [
|
||
|
css`
|
||
|
:host {
|
||
|
position: fixed;
|
||
|
z-index: 65535;
|
||
|
top: 0;
|
||
|
right: 0;
|
||
|
}
|
||
|
`
|
||
|
]
|
||
|
|
||
|
mounted() {
|
||
|
this.$on('removed', ev => {
|
||
|
if (this.children.length < 1) {
|
||
|
notifyIns = null
|
||
|
this.remove()
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Notify extends Component {
|
||
|
static props = {
|
||
|
title: '',
|
||
|
opt: { ...DEFAULT_OPT }
|
||
|
}
|
||
|
|
||
|
static styles = [
|
||
|
css`
|
||
|
:host {
|
||
|
display: block;
|
||
|
margin-top: 16px !important;
|
||
|
}
|
||
|
.noselect {
|
||
|
-webkit-user-select: none;
|
||
|
user-select: none;
|
||
|
|
||
|
img,
|
||
|
a {
|
||
|
-webkit-user-drag: none;
|
||
|
}
|
||
|
}
|
||
|
cite {
|
||
|
font-style: normal;
|
||
|
}
|
||
|
.notification {
|
||
|
width: 300px;
|
||
|
padding: 12px;
|
||
|
border-radius: 3px;
|
||
|
font-size: 14px;
|
||
|
background: rgba(255, 255, 255, 0.9);
|
||
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
|
||
|
}
|
||
|
|
||
|
.icon {
|
||
|
flex-shrink: 0;
|
||
|
width: 48px;
|
||
|
height: 48px;
|
||
|
margin-right: 8px;
|
||
|
}
|
||
|
.main,
|
||
|
.content {
|
||
|
display: flex;
|
||
|
}
|
||
|
.content {
|
||
|
flex: 1;
|
||
|
flex-direction: column;
|
||
|
}
|
||
|
.title {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
justify-content: space-between;
|
||
|
line-height: 2;
|
||
|
|
||
|
wc-icon {
|
||
|
--wc-icon-size: 14px;
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
}
|
||
|
`,
|
||
|
// 额外功能
|
||
|
css`
|
||
|
.image {
|
||
|
width: 284px;
|
||
|
height: 86px;
|
||
|
margin-top: 8px;
|
||
|
object-fit: cover;
|
||
|
}
|
||
|
.progress {
|
||
|
width: 100%;
|
||
|
margin-top: 8px;
|
||
|
|
||
|
.bar {
|
||
|
display: flex;
|
||
|
width: 100%;
|
||
|
height: 8px;
|
||
|
border-radius: 4px;
|
||
|
background: var(--color-plain-1);
|
||
|
}
|
||
|
|
||
|
.thumb {
|
||
|
width: 10%;
|
||
|
height: 8px;
|
||
|
border-radius: 4px;
|
||
|
background: var(--color-teal-1);
|
||
|
}
|
||
|
}
|
||
|
`
|
||
|
]
|
||
|
|
||
|
#up = null
|
||
|
|
||
|
close() {
|
||
|
clearTimeout(this.timer)
|
||
|
this.$refs.box.$animate(true).then(_ => {
|
||
|
this.remove()
|
||
|
this.#up.$emit('removed')
|
||
|
})
|
||
|
}
|
||
|
|
||
|
setProgress(val) {
|
||
|
let n = +val || 0
|
||
|
if (n < 0) {
|
||
|
n = 0
|
||
|
} else if (n > 100) {
|
||
|
n = 100
|
||
|
}
|
||
|
this.opt.progress = n
|
||
|
this.$requestUpdate()
|
||
|
}
|
||
|
|
||
|
mounted() {
|
||
|
this.$refs.box.$animate()
|
||
|
|
||
|
this.#up = this.parentNode
|
||
|
|
||
|
if (this.opt.duration > 0) {
|
||
|
this.timer = setTimeout(() => {
|
||
|
this.close()
|
||
|
}, this.opt.duration)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
let progress = styleMap({ width: this.opt.progress + '%' })
|
||
|
let iconShow = styleMap({ display: this.opt.icon ? '' : 'none' })
|
||
|
let imageShow = styleMap({ display: this.opt.image ? '' : 'none' })
|
||
|
let progressShow = styleMap({
|
||
|
display: this.opt.progress > 0 ? '' : 'none'
|
||
|
})
|
||
|
|
||
|
return html`<div
|
||
|
class="notification noselect"
|
||
|
ref="box"
|
||
|
#animation=${{ custom: ANIMATION }}
|
||
|
>
|
||
|
<main class="main">
|
||
|
<img class="icon" src=${this.opt.icon} style=${iconShow} />
|
||
|
<section class="content">
|
||
|
<strong class="title">
|
||
|
${this.title}
|
||
|
<wc-icon name="close" @click=${this.close}></wc-icon>
|
||
|
</strong>
|
||
|
<cite class="body">${this.opt.body}</cite>
|
||
|
</section>
|
||
|
</main>
|
||
|
<img src=${this.opt.image} class="image" style=${imageShow} />
|
||
|
|
||
|
<div class="progress" style=${progressShow}>
|
||
|
<div class="bar"><span class="thumb" style=${progress}></span></div>
|
||
|
</div>
|
||
|
</div>`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NotifyGroup.reg('notify-group')
|
||
|
Notify.reg('notify')
|
||
|
|
||
|
export default function notify(
|
||
|
title = '通知',
|
||
|
{ icon, body, image, progress, duration }
|
||
|
) {
|
||
|
//
|
||
|
let container = notifyIns || document.createElement('wc-notify-group')
|
||
|
let elem = document.createElement('wc-notify')
|
||
|
|
||
|
elem.title = title
|
||
|
|
||
|
elem.opt = merge({ icon, body, image, progress, duration })
|
||
|
|
||
|
container.append(elem)
|
||
|
|
||
|
if (!notifyIns) {
|
||
|
notifyIns = container
|
||
|
document.body.append(notifyIns)
|
||
|
}
|
||
|
|
||
|
return elem
|
||
|
}
|
||
|
|
||
|
window.notify = notify
|