Skip to content

Commit 54c4b64

Browse files
committed
initial implementation
1 parent abd1b1b commit 54c4b64

File tree

3 files changed

+133
-32
lines changed

3 files changed

+133
-32
lines changed

examples/index.html

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
5-
<title>custom-element demo</title>
5+
<title>toot-embed demo</title>
66
</head>
77
<body>
8-
<custom-element></custom-element>
8+
<toot-embed src="https://indieweb.social/api/v1/statuses/109524390152251545">
9+
</toot-embed>
910

1011
<script type="module">
11-
// import 'https://unpkg.com/@github/custom-element-boilerplate@latest/dist/custom-element.js'
12-
import '../src/custom-element.ts'
12+
// import 'https://unpkg.com/@github/toot-embed-boilerplate@latest/dist/toot-embed.js'
13+
import '../src/toot-embed-element.ts'
1314
</script>
1415
</body>
1516
</html>

src/custom-element.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/toot-embed-element.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
const styles = new CSSStyleSheet()
2+
styles.replaceSync(`
3+
:host {
4+
display: grid;
5+
border: 1px solid grey;
6+
grid-template:
7+
"avatar author_name author_name" 25px
8+
"avatar handle handle" 25px
9+
"content content content" max-content
10+
"backlink backlink backlink" 25px
11+
/ 60px auto auto;
12+
}
13+
14+
[part="avatar"] {
15+
grid-area: avatar;
16+
border-radius: 4px;
17+
}
18+
[part="author_name"] {
19+
grid-area: author_name
20+
}
21+
[part="handle"] {
22+
grid-area: handle
23+
}
24+
[part="content"] {
25+
grid-area: content
26+
}
27+
[part="backlink"] {
28+
grid-area: backlink
29+
}
30+
`)
31+
32+
/**
33+
* An example Custom Element. This documentation ends up in the
34+
* README so describe how this elements works here.
35+
*
36+
* You can event add examples on the element is used with Markdown.
37+
*
38+
* ```
39+
* <toot-embed></toot-embed>
40+
* ```
41+
*/
42+
class TootEmbedElement extends HTMLElement {
43+
static observeAttributes = ['src']
44+
#renderRoot!: ShadowRoot
45+
46+
get src() {
47+
return this.getAttribute('src') || ''
48+
}
49+
50+
set src(value: string) {
51+
this.setAttribute('src', `${value}`)
52+
}
53+
54+
get #contentPart() {
55+
return this.#renderRoot.querySelector('[part=content]')
56+
}
57+
58+
get #authorNamePart() {
59+
return this.#renderRoot.querySelector('[part=author_name]')
60+
}
61+
62+
get #authorHandlePart() {
63+
return this.#renderRoot.querySelector('[part=author_handle]')
64+
}
65+
66+
get #avatarPart() {
67+
return this.#renderRoot.querySelector('[part=avatar]')
68+
}
69+
70+
get #backlinkPart() {
71+
return this.#renderRoot.querySelector('[part=backlink]')
72+
}
73+
74+
75+
connectedCallback(): void {
76+
this.#renderRoot = this.attachShadow({mode: 'open'})
77+
this.#renderRoot.adoptedStyleSheets.push(styles)
78+
this.#renderRoot.innerHTML = `
79+
<img part="avatar" width="50">
80+
<span part="author_name"></span>
81+
<span part="author_handle"></span>
82+
<div part="content"></div>
83+
<a part="backlink" href="">Original Toot</a>
84+
`
85+
if (this.querySelector('script[type="application/json"]')) {
86+
return this.#render(JSON.parse(this.querySelector('script[type="application/json"]').textContent))
87+
}
88+
if (this.src) this.load()
89+
}
90+
91+
attributeChangedCallback(name: 'src', oldValue: string | null, newValue: string | null) {
92+
if (this.isConnected) return
93+
if (this.#renderRoot) return
94+
if (oldValue === newValue) return
95+
this.load()
96+
}
97+
98+
async load() {
99+
const src = new URL(this.src, window.location.origin)
100+
const response = await fetch(src)
101+
console.log(response)
102+
this.#render(await response.json())
103+
}
104+
105+
#render(json: unknown) {
106+
const {account, url, content} = json
107+
console.log(json)
108+
this.#authorNamePart.innerHTML = account.display_name
109+
const handleURL = new URL(account.url)
110+
this.#authorHandlePart.innerHTML = `${handleURL.pathname.slice(1)}@${handleURL.hostname}`
111+
this.#avatarPart.src = account.avatar
112+
this.#contentPart.innerHTML = content
113+
this.#backlinkPart.href = url
114+
}
115+
}
116+
117+
declare global {
118+
interface Window {
119+
TootEmbedElement: typeof TootEmbedElement
120+
}
121+
}
122+
123+
export default TootEmbedElement
124+
125+
if (!window.customElements.get('toot-embed')) {
126+
window.TootEmbedElement = TootEmbedElement
127+
window.customElements.define('toot-embed', TootEmbedElement)
128+
}

0 commit comments

Comments
 (0)