Skip to content

Commit 67dab0f

Browse files
committed
Extract <typing-effect> from GitHub
1 parent 5911d60 commit 67dab0f

File tree

1 file changed

+89
-8
lines changed

1 file changed

+89
-8
lines changed

src/index.ts

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,99 @@
1-
class CustomElementElement extends HTMLElement {
2-
connectedCallback(): void {
3-
this.textContent = ':wave:'
1+
const loaded: Promise<unknown> = (function () {
2+
if (document.readyState === 'complete') {
3+
return Promise.resolve()
4+
} else {
5+
return new Promise(resolve => {
6+
window.addEventListener('load', resolve)
7+
})
8+
}
9+
})()
10+
11+
class TypingEffectElement extends HTMLElement {
12+
async connectedCallback() {
13+
await loaded
14+
if (this.content) await typeLines(this.lines, this.content, this.characterDelay, this.lineDelay)
15+
if (this.cursor) this.cursor.hidden = true
16+
this.dispatchEvent(
17+
new CustomEvent('typing:complete', {
18+
bubbles: true,
19+
cancelable: true
20+
})
21+
)
22+
}
23+
24+
get content(): HTMLElement | null {
25+
return this.querySelector('[data-target="typing-effect.content"]')
26+
}
27+
28+
get cursor(): HTMLElement | null {
29+
return this.querySelector('[data-target="typing-effect.cursor"]')
30+
}
31+
32+
get lines(): string[] {
33+
const linesAttr = this.getAttribute('data-lines')
34+
35+
try {
36+
return linesAttr ? (JSON.parse(linesAttr) as string[]) : []
37+
} catch {
38+
return []
39+
}
40+
}
41+
42+
get characterDelay(): number {
43+
return Math.max(Math.min(0, Math.floor(Number(this.getAttribute('data-character-delay'))), 2_147_483_647)) || 40
44+
}
45+
46+
set characterDelay(value: number) {
47+
if (value > 2_147_483_647 || value < 0) {
48+
throw new DOMException('Value is negative or greater than the allowed amount')
49+
}
50+
this.setAttribute('data-character-delay', String(value))
51+
}
52+
53+
get lineDelay(): number {
54+
return Math.max(Math.min(0, Math.floor(Number(this.getAttribute('data-line-delay'))), 2_147_483_647)) || 40
55+
}
56+
57+
set lineDelay(value: number) {
58+
if (value > 2_147_483_647 || value < 0) {
59+
throw new DOMException('Value is negative or greater than the allowed amount')
60+
}
61+
this.setAttribute('data-line-delay', String(value))
462
}
563
}
664

765
declare global {
866
interface Window {
9-
CustomElementElement: typeof CustomElementElement
67+
TypingEffectElement: typeof TypingEffectElement
1068
}
1169
}
1270

13-
export default CustomElementElement
71+
export default TypingEffectElement
72+
73+
if (!window.customElements.get('typing-effect')) {
74+
window.TypingEffectElement = TypingEffectElement
75+
window.customElements.define('typing-effect', TypingEffectElement)
76+
}
77+
78+
async function typeLines(
79+
lines: string[],
80+
contentElement: HTMLElement,
81+
characterDelay: number,
82+
lineDelay: number
83+
): Promise<void> {
84+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
85+
for (const character of lines[lineIndex].split('')) {
86+
await wait(characterDelay)
87+
contentElement.innerHTML += character
88+
}
89+
90+
await wait(lineDelay)
91+
if (lineIndex < lines.length - 1) contentElement.append(document.createElement('br'))
92+
}
93+
}
1494

15-
if (!window.customElements.get('custom-element')) {
16-
window.CustomElementElement = CustomElementElement
17-
window.customElements.define('custom-element', CustomElementElement)
95+
async function wait(ms: number): Promise<void> {
96+
return new Promise<void>(resolve => {
97+
setTimeout(resolve, ms)
98+
})
1899
}

0 commit comments

Comments
 (0)