A web component that is the representation of a Bluesky comment. (Doesn't work)
BlueskyCommentArticle.astro
186 lines 4.1 kB view raw
1<script> 2 type Attributes = 'display-name' | 'handle' | 'likes' | 'replies' | 'reposts'; 3 4 export class BlueskyCommentArticle extends HTMLElement { 5 static get observedAttributes() { 6 return [ 7 'display-name', 8 'handle', 9 'likes', 10 'replies', 11 'reposts', 12 ] as Array<Attributes>; 13 } 14 15 constructor() { 16 super(); 17 18 const template = document.querySelector<HTMLTemplateElement>( 19 'template#bluesky-comment' 20 )?.content; 21 22 if (!template) throw new Error("Couldn't find bluesky comment template!"); 23 24 this.appendChild(template.cloneNode(true)); 25 } 26 27 attributeChangedCallback( 28 name: Attributes, 29 _oldValue: string, 30 newValue: string 31 ) { 32 if (name === 'display-name') { 33 this.displayName = newValue; 34 return; 35 } 36 37 if (name === 'handle') { 38 this.handle = newValue; 39 return; 40 } 41 42 if (name === 'likes') { 43 this.likes = newValue; 44 return; 45 } 46 47 if (name === 'replies') { 48 this.replies = newValue; 49 return; 50 } 51 52 if (name === 'reposts') { 53 this.reposts = newValue; 54 return; 55 } 56 } 57 58 get displayName() { 59 return this.getAttribute('display-name') ?? 'Unknown'; 60 } 61 62 set displayName(value: string) { 63 this?.querySelector('span.display-name')?.setHTMLUnsafe(value); 64 } 65 66 get handle() { 67 return this.getAttribute('handle') ?? 'unknown-handle'; 68 } 69 70 set handle(value: string) { 71 this?.querySelector('span.handle')?.setHTMLUnsafe(`@(${value})`); 72 } 73 74 get likes() { 75 return String(this.getAttribute('likes') ?? 0); 76 } 77 78 set likes(value: string) { 79 this?.querySelector('span.likes')?.setHTMLUnsafe(`${value} Likes`); 80 } 81 82 get replies() { 83 return String(this.getAttribute('replies') ?? 0); 84 } 85 86 set replies(value: string) { 87 this?.querySelector('span.replies')?.setHTMLUnsafe(`${value} Replies`); 88 } 89 90 get reposts() { 91 return String(this.getAttribute('reposts') ?? 0); 92 } 93 94 set reposts(value: string) { 95 this?.querySelector('span.reposts')?.setHTMLUnsafe(`${value} Reposts`); 96 } 97 } 98 99 customElements.define('bluesky-comment', BlueskyCommentArticle, { 100 extends: 'article', 101 }); 102</script> 103 104<template id={'bluesky-comment'}> 105 <style> 106 /* :host { 107 display: flex; 108 flex-direction: column; 109 gap: 1rem; 110 background-color: var(--surface0); 111 padding: 1rem; 112 border-radius: 0.5rem; 113 } */ 114 115 .author { 116 display: flex; 117 flex-direction: row; 118 font-size: medium; 119 margin: 0; 120 } 121 122 .author > a { 123 flex-grow: 1; 124 text-decoration: none; 125 color: var(--text); 126 } 127 128 .handle { 129 color: var(--subtext0); 130 } 131 132 .time { 133 color: var(--subtext0); 134 font-size: x-small; 135 } 136 137 footer > a { 138 display: flex; 139 flex-direction: row; 140 gap: 2rem; 141 text-decoration: none; 142 color: var(--text); 143 } 144 145 footer > a > p { 146 display: flex; 147 flex-direction: row; 148 align-items: center; 149 gap: 0.5rem; 150 margin-bottom: 0; 151 } 152 </style> 153 154 <header> 155 <h2 class="author"> 156 <a 157 rel="noopener noreferrer" 158 target="_blank" 159 aria-label="{comment.author.displayName}'s profile on Bluesky" 160 > 161 <span class="display-name"></span> 162 <span class="handle"></span> 163 </a> 164 <time class="timestamp"></time> 165 </h2> 166 </header> 167 <p> 168 <slot name="default">a</slot> 169 </p> 170 <footer> 171 <a rel="noopener noreferrer" target="_blank"> 172 <p> 173 <iconify-icon icon="fa6-solid:heart" height="1rem"></iconify-icon> 174 <span class="likes"></span> 175 </p> 176 <p> 177 <iconify-icon icon="fa6-solid:comment" height="1rem"></iconify-icon> 178 <span class="replies"></span> 179 </p> 180 <p> 181 <iconify-icon icon="fa6-solid:repeat" height="1rem"></iconify-icon> 182 <span class="reposts"></span> 183 </p> 184 </a> 185 </footer> 186</template>