Skip to content

Commit 4b58cb5

Browse files
authored
Merge pull request #2 from knowler/ideas-for-improvements
Some improvement ideas
2 parents a0e0a6f + e64b382 commit 4b58cb5

File tree

2 files changed

+75
-33
lines changed

2 files changed

+75
-33
lines changed

examples/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<title>toot-embed demo</title>
66
</head>
77
<body>
8-
<toot-embed src="https://indieweb.social/api/v1/statuses/109524390152251545">
9-
</toot-embed>
8+
<toot-embed src="https://indieweb.social/@keithamus/109524390152251545"></toot-embed>
9+
<toot-embed src="https://sunny.garden/@knowler/109487032491438808"></toot-embed>
1010

1111
<script type="module">
1212
// import 'https://unpkg.com/@github/toot-embed-boilerplate@latest/dist/toot-embed.js'

src/toot-embed-element.ts

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,43 @@
1+
const html = String.raw
12
const styles = new CSSStyleSheet()
23
styles.replaceSync(`
3-
:host {
4+
:host(:not(:--loading)) {
45
display: grid;
5-
border: 1px solid grey;
6+
max-inline-size: 36em;
7+
padding: 0.5em;
8+
gap: 0.5em;
9+
border: 0.0625em solid grey;
10+
border-radius: 0.5em;
611
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+
"avatar author-link author-link" max-content
13+
"content content content" max-content
14+
"backlink backlink backlink" max-content
15+
/ min-content auto auto;
1216
}
1317
1418
[part="avatar"] {
19+
max-inline-size: 3.125em;
20+
aspect-ratio: 1;
1521
grid-area: avatar;
16-
border-radius: 4px;
22+
border-radius: 0.25em;
1723
}
18-
[part="author_name"] {
19-
grid-area: author_name
24+
[part="author-link"] {
25+
display: grid;
26+
align-self: center;
27+
grid-area: author-link
2028
}
2129
[part="handle"] {
2230
grid-area: handle
2331
}
2432
[part="content"] {
25-
grid-area: content
33+
grid-area: content;
34+
line-height: 1.5;
35+
}
36+
[part="content"] > * {
37+
margin-block: 0;
38+
}
39+
[part="content"] > * + * {
40+
margin-block-start: 0.5em;
2641
}
2742
[part="backlink"] {
2843
grid-area: backlink
@@ -41,6 +56,7 @@ styles.replaceSync(`
4156
*/
4257
class TootEmbedElement extends HTMLElement {
4358
static observeAttributes = ['src']
59+
#internals!: ElementInternals
4460
#renderRoot!: ShadowRoot
4561

4662
get src() {
@@ -55,12 +71,16 @@ class TootEmbedElement extends HTMLElement {
5571
return this.#renderRoot.querySelector('[part=content]')
5672
}
5773

74+
get #authorLinkPart() {
75+
return this.#renderRoot.querySelector('[part=author-link]')
76+
}
77+
5878
get #authorNamePart() {
59-
return this.#renderRoot.querySelector('[part=author_name]')
79+
return this.#renderRoot.querySelector('[part=author-name]')
6080
}
6181

6282
get #authorHandlePart() {
63-
return this.#renderRoot.querySelector('[part=author_handle]')
83+
return this.#renderRoot.querySelector('[part=author-handle]')
6484
}
6585

6686
get #avatarPart() {
@@ -71,17 +91,11 @@ class TootEmbedElement extends HTMLElement {
7191
return this.#renderRoot.querySelector('[part=backlink]')
7292
}
7393

74-
7594
connectedCallback(): void {
76-
this.#renderRoot = this.attachShadow({mode: 'open'})
95+
this.#internals = this.attachInternals()
96+
this.#internals.role = 'article'
97+
this.#renderRoot = this.attachShadow({ mode: 'open' })
7798
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-
`
8599
if (this.querySelector('script[type="application/json"]')) {
86100
return this.#render(JSON.parse(this.querySelector('script[type="application/json"]').textContent))
87101
}
@@ -96,21 +110,49 @@ class TootEmbedElement extends HTMLElement {
96110
}
97111

98112
async load() {
99-
const src = new URL(this.src, window.location.origin)
100-
const response = await fetch(src)
113+
this.#internals.states.add('--loading')
114+
const { tootId } = this.#useParams();
115+
const apiURL = new URL(`/api/v1/statuses/${tootId}`, this.src)
116+
const response = await fetch(apiURL)
101117
console.log(response)
118+
102119
this.#render(await response.json())
120+
this.#internals.states.delete('--loading')
103121
}
104122

105-
#render(json: unknown) {
106-
const {account, url, content} = json
123+
#render(json) {
124+
const { account, url, content } = json
107125
console.log(json)
108-
this.#authorNamePart.innerHTML = account.display_name
109126
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
127+
const {handle} = this.#useParams()
128+
this.#renderRoot.innerHTML = html`
129+
<img part="avatar" src="${account.avatar}" alt="">
130+
<a part="author-link" href="${handleURL.href}">
131+
<span part="author-name">${account.display_name}</span>
132+
<span part="author-handle">@${handle}@${handleURL.hostname}</span>
133+
</a>
134+
<div part="content">
135+
${content}
136+
</div>
137+
<a part="backlink" href="${url}" rel="bookmark">Original Toot</a>
138+
`
139+
this.#internals.states.add('--ready')
140+
this.#internals.ariaLabel = `${this.#authorLinkPart.textContent} ${this.#contentPart.textContent}`
141+
}
142+
143+
// URLPattern only works in Chromium right now.
144+
// Could refactor this to use RegExp for compatibility
145+
/* @ts-ignore */
146+
#shortPattern = new URLPattern({ pathname: '/@:handle/:tootId(\\d+)' })
147+
/* @ts-ignore */
148+
#longPattern = new URLPattern({ pathname: '/users/:handle/statuses/:tootId(\\d+)' })
149+
// Toot URLs can have two different formats:
150+
// 1. https://indieweb.social/@keithamus/109524390152251545
151+
// 2. https://indieweb.social/users/keithamus/statuses/109524390152251545
152+
#useParams(): { [key: string]: string } {
153+
const groups = (this.#shortPattern.exec(this.src) ?? this.#longPattern.exec(this.src))?.pathname.groups;
154+
if (groups) return groups;
155+
throw `This doesn’t seem to be a toot URL: ${this.src}`;
114156
}
115157
}
116158

0 commit comments

Comments
 (0)