Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<title>toot-embed demo</title>
</head>
<body>
<toot-embed src="https://indieweb.social/api/v1/statuses/109524390152251545">
</toot-embed>
<toot-embed src="https://indieweb.social/@keithamus/109524390152251545"></toot-embed>
<toot-embed src="https://sunny.garden/@knowler/109487032491438808"></toot-embed>

<script type="module">
// import 'https://unpkg.com/@github/toot-embed-boilerplate@latest/dist/toot-embed.js'
Expand Down
104 changes: 73 additions & 31 deletions src/toot-embed-element.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
const html = String.raw
const styles = new CSSStyleSheet()
styles.replaceSync(`
:host {
:host(:not(:--loading)) {
display: grid;
border: 1px solid grey;
max-inline-size: 36em;
padding: 0.5em;
gap: 0.5em;
border: 0.0625em solid grey;
border-radius: 0.5em;
grid-template:
"avatar author_name author_name" 25px
"avatar handle handle" 25px
"content content content" max-content
"backlink backlink backlink" 25px
/ 60px auto auto;
"avatar author-link author-link" max-content
"content content content" max-content
"backlink backlink backlink" max-content
/ min-content auto auto;
}

[part="avatar"] {
max-inline-size: 3.125em;
aspect-ratio: 1;
grid-area: avatar;
border-radius: 4px;
border-radius: 0.25em;
}
[part="author_name"] {
grid-area: author_name
[part="author-link"] {
display: grid;
align-self: center;
grid-area: author-link
}
[part="handle"] {
grid-area: handle
}
[part="content"] {
grid-area: content
grid-area: content;
line-height: 1.5;
}
[part="content"] > * {
margin-block: 0;
}
[part="content"] > * + * {
margin-block-start: 0.5em;
}
[part="backlink"] {
grid-area: backlink
Expand All @@ -41,6 +56,7 @@ styles.replaceSync(`
*/
class TootEmbedElement extends HTMLElement {
static observeAttributes = ['src']
#internals!: ElementInternals
#renderRoot!: ShadowRoot

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

get #authorLinkPart() {
return this.#renderRoot.querySelector('[part=author-link]')
}

get #authorNamePart() {
return this.#renderRoot.querySelector('[part=author_name]')
return this.#renderRoot.querySelector('[part=author-name]')
}

get #authorHandlePart() {
return this.#renderRoot.querySelector('[part=author_handle]')
return this.#renderRoot.querySelector('[part=author-handle]')
}

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


connectedCallback(): void {
this.#renderRoot = this.attachShadow({mode: 'open'})
this.#internals = this.attachInternals()
this.#internals.role = 'article'
this.#renderRoot = this.attachShadow({ mode: 'open' })
this.#renderRoot.adoptedStyleSheets.push(styles)
this.#renderRoot.innerHTML = `
<img part="avatar" width="50">
<span part="author_name"></span>
<span part="author_handle"></span>
<div part="content"></div>
<a part="backlink" href="">Original Toot</a>
`
if (this.querySelector('script[type="application/json"]')) {
return this.#render(JSON.parse(this.querySelector('script[type="application/json"]').textContent))
}
Expand All @@ -96,21 +110,49 @@ class TootEmbedElement extends HTMLElement {
}

async load() {
const src = new URL(this.src, window.location.origin)
const response = await fetch(src)
this.#internals.states.add('--loading')
const { tootId } = this.#useParams();
const apiURL = new URL(`/api/v1/statuses/${tootId}`, this.src)
const response = await fetch(apiURL)
console.log(response)

this.#render(await response.json())
this.#internals.states.delete('--loading')
}

#render(json: unknown) {
const {account, url, content} = json
#render(json) {
const { account, url, content } = json
console.log(json)
this.#authorNamePart.innerHTML = account.display_name
const handleURL = new URL(account.url)
this.#authorHandlePart.innerHTML = `${handleURL.pathname.slice(1)}@${handleURL.hostname}`
this.#avatarPart.src = account.avatar
this.#contentPart.innerHTML = content
this.#backlinkPart.href = url
const {handle} = this.#useParams()
this.#renderRoot.innerHTML = html`
<img part="avatar" src="${account.avatar}" alt="">
<a part="author-link" href="${handleURL.href}">
<span part="author-name">${account.display_name}</span>
<span part="author-handle">@${handle}@${handleURL.hostname}</span>
</a>
<div part="content">
${content}
</div>
<a part="backlink" href="${url}" rel="bookmark">Original Toot</a>
`
this.#internals.states.add('--ready')
this.#internals.ariaLabel = `${this.#authorLinkPart.textContent} ${this.#contentPart.textContent}`
}

// URLPattern only works in Chromium right now.
// Could refactor this to use RegExp for compatibility
/* @ts-ignore */
#shortPattern = new URLPattern({ pathname: '/@:handle/:tootId(\\d+)' })
/* @ts-ignore */
#longPattern = new URLPattern({ pathname: '/users/:handle/statuses/:tootId(\\d+)' })
// Toot URLs can have two different formats:
// 1. https://indieweb.social/@keithamus/109524390152251545
// 2. https://indieweb.social/users/keithamus/statuses/109524390152251545
#useParams(): { [key: string]: string } {
const groups = (this.#shortPattern.exec(this.src) ?? this.#longPattern.exec(this.src))?.pathname.groups;
if (groups) return groups;
throw `This doesn’t seem to be a toot URL: ${this.src}`;
}
}

Expand Down