253 lines
7.2 KiB
JavaScript
253 lines
7.2 KiB
JavaScript
const smNotifications = document.createElement('template')
|
|
smNotifications.innerHTML = `
|
|
<style>
|
|
*{
|
|
padding: 0;
|
|
margin: 0;
|
|
-webkit-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
}
|
|
:host{
|
|
display: -webkit-box;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
--accent-color: #4d2588;
|
|
--text-color: 17, 17, 17;
|
|
--background-color: 255, 255, 255;
|
|
--danger-color: red;
|
|
--icon-height: 1.5rem;
|
|
--icon-width: 1.5rem;
|
|
}
|
|
.hide{
|
|
opacity: 0 !important;
|
|
pointer-events: none !important;
|
|
}
|
|
.notification-panel{
|
|
display: grid;
|
|
width: 100%;
|
|
gap: 0.5rem;
|
|
position: fixed;
|
|
left: 0;
|
|
bottom: 0;
|
|
z-index: 100;
|
|
max-height: 100%;
|
|
padding: 1rem;
|
|
overflow: hidden auto;
|
|
-ms-scroll-chaining: none;
|
|
overscroll-behavior: contain;
|
|
}
|
|
.notification-panel:empty{
|
|
display:none;
|
|
}
|
|
.notification{
|
|
display: -webkit-box;
|
|
display: -ms-flexbox;
|
|
display: flex;
|
|
position: relative;
|
|
border-radius: 0.3rem;
|
|
background: rgba(var(--background-color), 1);
|
|
overflow: hidden;
|
|
overflow-wrap: break-word;
|
|
word-wrap: break-word;
|
|
-ms-word-break: break-all;
|
|
word-break: break-all;
|
|
word-break: break-word;
|
|
-ms-hyphens: auto;
|
|
-webkit-hyphens: auto;
|
|
hyphens: auto;
|
|
max-width: 100%;
|
|
padding: 1rem;
|
|
align-items: center;
|
|
}
|
|
.icon-container:not(:empty){
|
|
margin-right: 0.5rem;
|
|
height: var(--icon-height);
|
|
width: var(--icon-width);
|
|
}
|
|
h4:first-letter,
|
|
p:first-letter{
|
|
text-transform: uppercase;
|
|
}
|
|
h4{
|
|
font-weight: 400;
|
|
}
|
|
p{
|
|
line-height: 1.6;
|
|
-webkit-box-flex: 1;
|
|
-ms-flex: 1;
|
|
flex: 1;
|
|
color: rgba(var(--text-color), 0.9);
|
|
overflow-wrap: break-word;
|
|
overflow-wrap: break-word;
|
|
word-wrap: break-word;
|
|
-ms-word-break: break-all;
|
|
word-break: break-all;
|
|
word-break: break-word;
|
|
-ms-hyphens: auto;
|
|
-webkit-hyphens: auto;
|
|
hyphens: auto;
|
|
max-width: 100%;
|
|
}
|
|
.notification:last-of-type{
|
|
margin-bottom: 0;
|
|
}
|
|
.icon {
|
|
height: 100%;
|
|
width: 100%;
|
|
fill: rgba(var(--text-color), 0.7);
|
|
}
|
|
.close{
|
|
height: 2rem;
|
|
width: 2rem;
|
|
border: none;
|
|
cursor: pointer;
|
|
margin-left: 1rem;
|
|
border-radius: 50%;
|
|
padding: 0.3rem;
|
|
transition: background-color 0.3s, transform 0.3s;
|
|
background-color: transparent;
|
|
}
|
|
.close:active{
|
|
transform: scale(0.9);
|
|
}
|
|
@media screen and (min-width: 640px){
|
|
.notification-panel{
|
|
max-width: 28rem;
|
|
width: max-content;
|
|
}
|
|
.notification{
|
|
width: auto;
|
|
border: solid 1px rgba(var(--text-color), 0.2);
|
|
}
|
|
}
|
|
@media (any-hover: hover){
|
|
::-webkit-scrollbar{
|
|
width: 0.5rem;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb{
|
|
background: rgba(var(--text-color), 0.3);
|
|
border-radius: 1rem;
|
|
&:hover{
|
|
background: rgba(var(--text-color), 0.5);
|
|
}
|
|
}
|
|
.close:hover{
|
|
background-color: rgba(var(--text-color), 0.1);
|
|
}
|
|
}
|
|
</style>
|
|
<div class="notification-panel"></div>
|
|
`
|
|
|
|
customElements.define('sm-notifications', class extends HTMLElement {
|
|
constructor() {
|
|
super()
|
|
this.shadow = this.attachShadow({
|
|
mode: 'open'
|
|
}).append(smNotifications.content.cloneNode(true))
|
|
|
|
this.notificationPanel = this.shadowRoot.querySelector('.notification-panel')
|
|
this.animationOptions = {
|
|
duration: 300,
|
|
fill: "forwards",
|
|
easing: "cubic-bezier(0.175, 0.885, 0.32, 1.275)"
|
|
}
|
|
|
|
this.push = this.push.bind(this)
|
|
this.createNotification = this.createNotification.bind(this)
|
|
this.removeNotification = this.removeNotification.bind(this)
|
|
this.clearAll = this.clearAll.bind(this)
|
|
|
|
}
|
|
|
|
randString(length) {
|
|
let result = '';
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
for (let i = 0; i < length; i++)
|
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
return result;
|
|
}
|
|
|
|
createNotification(message, options) {
|
|
const { pinned = false, icon = '' } = options
|
|
const notification = document.createElement('div')
|
|
notification.id = this.randString(8)
|
|
notification.classList.add('notification')
|
|
let composition = ``
|
|
composition += `
|
|
<div class="icon-container">${icon}</div>
|
|
<p>${message}</p>
|
|
`
|
|
if (pinned) {
|
|
notification.classList.add('pinned')
|
|
composition += `
|
|
<button class="close">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></svg>
|
|
</button>
|
|
`
|
|
}
|
|
notification.innerHTML = composition
|
|
return notification
|
|
}
|
|
|
|
push(message, options = {}) {
|
|
const notification = this.createNotification(message, options)
|
|
this.notificationPanel.append(notification)
|
|
notification.animate([
|
|
{
|
|
transform: `translateY(1rem)`,
|
|
opacity: '0'
|
|
},
|
|
{
|
|
transform: `none`,
|
|
opacity: '1'
|
|
},
|
|
], this.animationOptions)
|
|
return notification.id
|
|
}
|
|
|
|
removeNotification(notification) {
|
|
notification.animate([
|
|
{
|
|
transform: `none`,
|
|
opacity: '1'
|
|
},
|
|
{
|
|
transform: `translateY(0.5rem)`,
|
|
opacity: '0'
|
|
}
|
|
], this.animationOptions).onfinish = () => {
|
|
notification.remove()
|
|
}
|
|
}
|
|
|
|
clearAll() {
|
|
Array.from(this.notificationPanel.children).forEach(child => {
|
|
this.removeNotification(child)
|
|
})
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.notificationPanel.addEventListener('click', e => {
|
|
if (e.target.closest('.close')) (
|
|
this.removeNotification(e.target.closest('.notification'))
|
|
)
|
|
})
|
|
|
|
const observer = new MutationObserver(mutationList => {
|
|
mutationList.forEach(mutation => {
|
|
if (mutation.type === 'childList') {
|
|
if (mutation.addedNodes.length && !mutation.addedNodes[0].classList.contains('pinned')) {
|
|
setTimeout(() => {
|
|
this.removeNotification(mutation.addedNodes[0])
|
|
}, 5000);
|
|
}
|
|
}
|
|
})
|
|
})
|
|
observer.observe(this.notificationPanel, {
|
|
childList: true,
|
|
})
|
|
}
|
|
}) |