[Archived] Archived WIP of vielle.dev

Move <code-heading /> definition to seperate component

vielle.dev 99a60361 098959cc

verified
+152 -130
+150
src/components/blog/CodeHeading.astro
··· 1 + --- 2 + import Copy from "@/assets/copy.svg"; 3 + 4 + interface Props { 5 + colours: { text: string; border: string }; 6 + } 7 + 8 + const { colours } = Astro.props; 9 + --- 10 + 11 + <template id="code-heading"> 12 + <!-- slots need to be set using set:html 13 + else it is treated as astro slot --> 14 + <span class="lang"><Fragment set:html={"<slot>.txt</slot>"} /></span> 15 + <span id="copied" style="visibility:hidden" role="alert">Copied!</span> 16 + <button id="copy"><Copy /></button> 17 + 18 + <!-- define:vars didnt work :( --> 19 + <style 20 + set:html={` 21 + :host { 22 + --text: ${colours.text}; 23 + --border: ${colours.border}; 24 + } 25 + `} 26 + ></style> 27 + 28 + <style> 29 + @keyframes teeter { 30 + from, 31 + to { 32 + rotate: 0deg; 33 + } 34 + 25% { 35 + rotate: 15deg; 36 + } 37 + 75% { 38 + rotate: -15deg; 39 + } 40 + } 41 + 42 + .lang { 43 + color: var(--text); 44 + } 45 + 46 + button { 47 + border: none; 48 + background: none; 49 + aspect-ratio: 1; 50 + border-radius: 100%; 51 + 52 + display: flex; 53 + align-items: center; 54 + justify-content: center; 55 + 56 + &:hover, 57 + &:focus { 58 + scale: 1.2; 59 + outline: none; 60 + background-color: #ffffff20; 61 + } 62 + 63 + &:active { 64 + scale: 1.4; 65 + animation: teeter 0.2s; 66 + } 67 + 68 + & svg { 69 + stroke: var(--text); 70 + margin: 0.2rem; 71 + } 72 + } 73 + 74 + :host { 75 + display: flex block; 76 + justify-content: space-between; 77 + align-items: center; 78 + /* gets overridden by * because why not ig */ 79 + padding: 1rem !important; 80 + position: sticky; 81 + top: 0; 82 + left: 0; 83 + border-bottom: 0.4rem solid var(--border); 84 + user-select: none; 85 + } 86 + </style> 87 + </template> 88 + 89 + <script> 90 + class CodeHeading extends HTMLElement { 91 + contents = ""; 92 + static observedAttributes = ["contents"]; 93 + 94 + template: HTMLTemplateElement; 95 + content: DocumentFragment; 96 + shadowRoot: ShadowRoot; 97 + 98 + constructor() { 99 + super(); 100 + const template = document.getElementById("code-heading"); 101 + if (!template || !(template instanceof HTMLTemplateElement)) 102 + throw new Error("Could not get #code-heading"); 103 + this.template = template; 104 + this.content = template.content; 105 + 106 + this.shadowRoot = this.attachShadow({ mode: "open" }); 107 + this.shadowRoot.appendChild(this.content.cloneNode(true)); 108 + 109 + const copy = this.shadowRoot.getElementById("copy"); 110 + if (!copy) throw new Error("No #copy in #code-heading"); 111 + 112 + const copied = this.shadowRoot.getElementById("copied"); 113 + if (!copied) throw new Error("No #copied in #code-heading"); 114 + 115 + const copied_animation = { 116 + opacity: [0, 1], 117 + visibility: ["hidden", "visible"], 118 + }; 119 + 120 + copy.addEventListener("click", () => { 121 + navigator.clipboard 122 + .writeText(this.contents) 123 + .catch((e) => { 124 + console.error("Encountered error copying to clipboard;", e); 125 + }) 126 + .then(async () => { 127 + await copied.animate(copied_animation, { 128 + duration: 200, 129 + fill: "forwards", 130 + }).finished; 131 + 132 + copied.animate(copied_animation, { 133 + duration: 200, 134 + delay: 2000, 135 + fill: "forwards", 136 + direction: "reverse", 137 + }); 138 + }); 139 + }); 140 + } 141 + 142 + attributeChangedCallback(name: string, _: any, newV?: string) { 143 + if (name == "contents") { 144 + this.contents = newV ?? ""; 145 + } 146 + } 147 + } 148 + 149 + customElements.define("code-heading", CodeHeading); 150 + </script>
+2 -130
src/pages/blog/[id].astro
··· 3 3 import Nav from "@/components/generic/Nav.astro"; 4 4 import LightDarkToggle from "@/components/generic/LightDarkToggle.astro"; 5 5 6 - import Copy from "@/assets/copy.svg"; 7 - 8 6 import { blog } from "@/config"; 9 7 const { 10 8 post: { light, dark, rainbow }, ··· 15 13 import { parse } from "node:path"; 16 14 17 15 import "@/components/blog/post.css"; 16 + import CodeHeading from "@/components/blog/CodeHeading.astro"; 18 17 19 18 const { id } = Astro.params; 20 19 if (!id) return Astro.redirect("/404"); ··· 54 53 </main> 55 54 </Base> 56 55 57 - <template id="code-heading"> 58 - <!-- slots need to be set using set:html 59 - else it is treated as astro slot --> 60 - <span class="lang"><Fragment set:html={"<slot>.txt</slot>"} /></span> 61 - <span id="copied" style="visibility:hidden" role="alert">Copied!</span> 62 - <button id="copy"><Copy /></button> 63 - 64 - <style> 65 - @keyframes teeter { 66 - from, 67 - to { 68 - rotate: 0deg; 69 - } 70 - 25% { 71 - rotate: 15deg; 72 - } 73 - 75% { 74 - rotate: -15deg; 75 - } 76 - } 77 - 78 - .lang { 79 - color: var(--typo-body); 80 - } 81 - 82 - button { 83 - border: none; 84 - background: none; 85 - aspect-ratio: 1; 86 - border-radius: 100%; 87 - 88 - display: flex; 89 - align-items: center; 90 - justify-content: center; 91 - 92 - &:hover, 93 - &:focus { 94 - scale: 1.2; 95 - outline: none; 96 - background-color: #ffffff20; 97 - } 98 - 99 - &:active { 100 - scale: 1.4; 101 - animation: teeter 0.2s; 102 - } 103 - 104 - & svg { 105 - stroke: var(--typo-body); 106 - margin: 0.2rem; 107 - } 108 - } 109 - 110 - :host { 111 - display: flex block; 112 - justify-content: space-between; 113 - align-items: center; 114 - /* gets overridden by * because why not ig */ 115 - padding: 1rem !important; 116 - position: sticky; 117 - top: 0; 118 - left: 0; 119 - border-bottom: 0.4rem solid var(--bg-main); 120 - user-select: none; 121 - } 122 - </style> 123 - </template> 56 + <CodeHeading colours={{ text: "var(--typo-body)", border: "var(--bg-main)" }} /> 124 57 125 58 <script> 126 - class CodeHeading extends HTMLElement { 127 - contents = ""; 128 - static observedAttributes = ["contents"]; 129 - 130 - template: HTMLTemplateElement; 131 - content: DocumentFragment; 132 - shadowRoot: ShadowRoot; 133 - 134 - constructor() { 135 - super(); 136 - const template = document.getElementById("code-heading"); 137 - if (!template || !(template instanceof HTMLTemplateElement)) 138 - throw new Error("Could not get #code-heading"); 139 - this.template = template; 140 - this.content = template.content; 141 - 142 - this.shadowRoot = this.attachShadow({ mode: "open" }); 143 - this.shadowRoot.appendChild(this.content.cloneNode(true)); 144 - 145 - const copy = this.shadowRoot.getElementById("copy"); 146 - if (!copy) throw new Error("No #copy in #code-heading"); 147 - 148 - const copied = this.shadowRoot.getElementById("copied"); 149 - if (!copied) throw new Error("No #copied in #code-heading"); 150 - 151 - const copied_animation = { 152 - opacity: [0, 1], 153 - visibility: ["hidden", "visible"], 154 - }; 155 - 156 - copy.addEventListener("click", () => { 157 - navigator.clipboard 158 - .writeText(this.contents) 159 - .catch((e) => { 160 - console.error("Encountered error copying to clipboard;", e); 161 - }) 162 - .then(async () => { 163 - await copied.animate(copied_animation, { 164 - duration: 200, 165 - fill: "forwards", 166 - }).finished; 167 - 168 - copied.animate(copied_animation, { 169 - duration: 200, 170 - delay: 2000, 171 - fill: "forwards", 172 - direction: "reverse", 173 - }); 174 - }); 175 - }); 176 - } 177 - 178 - attributeChangedCallback(name: string, _: any, newV?: string) { 179 - if (name == "contents") { 180 - this.contents = newV ?? ""; 181 - } 182 - } 183 - } 184 - 185 - customElements.define("code-heading", CodeHeading); 186 - 187 59 document.querySelectorAll(".astro-code").forEach((code) => { 188 60 if (!(code instanceof HTMLElement)) return; 189 61