Skip to content

Commit 48aa1c4

Browse files
authored
feat(helper): add Message class to handle message (#417)
1 parent 73b05ea commit 48aa1c4

File tree

5 files changed

+150
-0
lines changed

5 files changed

+150
-0
lines changed

eslint.config.js

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ export default vuepress(
9999
],
100100
'@typescript-eslint/no-dynamic-delete': 'off',
101101
'@typescript-eslint/no-non-null-assertion': 'off',
102+
'@typescript-eslint/no-empty-function': [
103+
'error',
104+
{ allow: ['protected-constructors'] },
105+
],
102106
'@typescript-eslint/no-floating-promises': [
103107
'error',
104108
{

tools/helper/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"./noopComponent": "./lib/client/noopComponent.js",
3232
"./noopModule": "./lib/client/noopModule.js",
3333
"./colors.css": "./lib/client/styles/colors.css",
34+
"./message.css": "./lib/client/styles/message.css",
3435
"./normalize.css": "./lib/client/styles/normalize.css",
3536
"./sr-only.css": "./lib/client/styles/sr-only.css",
3637
"./transition/*.css": "./lib/client/styles/transition/*.css",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
:root {
2+
--message-offset: calc(var(--vp-header-offset, 3.6rem) + 1rem);
3+
--message-timing-duration: 0.3s;
4+
--message-timing-function: ease-in-out;
5+
--message-gap: 0.5rem;
6+
}
7+
8+
@keyframes message-move-in {
9+
0% {
10+
opacity: 0;
11+
transform: translateY(-10px);
12+
}
13+
14+
100% {
15+
opacity: 1;
16+
transform: translateY(0);
17+
}
18+
}
19+
20+
@keyframes message-move-out {
21+
0% {
22+
opacity: 1;
23+
transform: translateY(0);
24+
}
25+
26+
100% {
27+
opacity: 0;
28+
transform: translateY(-100%);
29+
}
30+
}
31+
32+
#message-container {
33+
position: fixed;
34+
inset: var(--message-offset) 0 auto;
35+
z-index: 75;
36+
37+
display: flex;
38+
flex-flow: column;
39+
gap: var(--message-gap);
40+
align-items: center;
41+
42+
text-align: center;
43+
}
44+
45+
.message-item {
46+
display: inline-block;
47+
48+
padding: 8px 10px;
49+
border-radius: 3px;
50+
51+
background: var(--vp-c-bg);
52+
color: var(--vp-c-text);
53+
box-shadow: 0 0 10px 0 var(--vp-c-shadow);
54+
55+
font-size: 14px;
56+
57+
&.move-in {
58+
animation: message-move-in var(--message-timing-duration)
59+
var(--message-timing-function);
60+
}
61+
62+
&.move-out {
63+
animation: message-move-out var(--message-timing-duration)
64+
var(--message-timing-function);
65+
animation-fill-mode: forwards;
66+
}
67+
68+
svg {
69+
position: relative;
70+
bottom: -0.125em;
71+
margin-inline-end: 5px;
72+
}
73+
}

tools/helper/src/client/utils/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export * from './getHeaders.js'
55
export * from './isFocusingTextControl.js'
66
export * from './isKeyMatched.js'
77
export * from './hasGlobalComponent.js'
8+
export * from './message.js'
89
export * from './wait.js'
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { keys } from '../../shared/index.js'
2+
3+
const containerId = 'message-container'
4+
5+
export class Message {
6+
private elements: Record<number, HTMLDivElement>
7+
8+
public constructor() {
9+
this.elements = {}
10+
}
11+
12+
public static get containerElement(): HTMLElement {
13+
let containerElement = document.getElementById(containerId)
14+
15+
if (containerElement) return containerElement
16+
17+
containerElement = document.createElement('div')
18+
containerElement.id = containerId
19+
document.body.appendChild(containerElement)
20+
21+
return containerElement
22+
}
23+
24+
public getElement(messageId: number): HTMLDivElement {
25+
return this.elements[messageId]
26+
}
27+
28+
public pop(html: string, duration = 2000, clickToClose = true): number {
29+
const messageId = Date.now()
30+
const messageElement = document.createElement('div')
31+
messageElement.className = 'message-item move-in'
32+
messageElement.innerHTML = html
33+
Message.containerElement.appendChild(messageElement)
34+
this.elements[messageId] = messageElement
35+
36+
if (clickToClose)
37+
messageElement.addEventListener('click', () => {
38+
this.close(messageId)
39+
})
40+
41+
if (duration > 0)
42+
setTimeout(() => {
43+
this.close(messageId)
44+
}, duration)
45+
46+
return messageId
47+
}
48+
49+
public close(messageId?: number): void {
50+
if (messageId) {
51+
const messageElement = this.elements[messageId]
52+
53+
messageElement.classList.remove('move-in')
54+
messageElement.classList.add('move-out')
55+
messageElement.addEventListener('animationend', () => {
56+
messageElement.remove()
57+
delete this.elements[messageId]
58+
})
59+
} else {
60+
keys(this.elements).forEach((id) => {
61+
this.close(Number(id))
62+
})
63+
}
64+
}
65+
66+
public destroy(): void {
67+
const containerElement = document.getElementById(containerId)
68+
if (containerElement) document.body.removeChild(containerElement)
69+
this.elements = {}
70+
}
71+
}

0 commit comments

Comments
 (0)