[Archived] Archived WIP of vielle.dev
1---
2import Copy from "@/assets/copy.svg";
3
4interface Props {
5 colours: { text: string; border: string };
6}
7
8const { colours } = Astro.props;
9---
10
11<template id="code-heading">
12 <span class="lang"><slot is:inline>.txt</slot></span>
13 <span id="copied" style="visibility:hidden" role="alert">Copied!</span>
14 <button id="copy"><Copy /></button>
15
16 <!-- define:vars didnt work :( -->
17 <style
18 set:html={`
19 :host {
20 --text: ${colours.text};
21 --border: ${colours.border};
22 }
23 `}
24 ></style>
25
26 <style>
27 @keyframes teeter {
28 from,
29 to {
30 rotate: 0deg;
31 }
32 25% {
33 rotate: 15deg;
34 }
35 75% {
36 rotate: -15deg;
37 }
38 }
39
40 .lang {
41 color: var(--text);
42 }
43
44 button {
45 border: none;
46 background: none;
47 aspect-ratio: 1;
48 border-radius: 100%;
49
50 display: flex;
51 align-items: center;
52 justify-content: center;
53
54 &:hover,
55 &:focus {
56 scale: 1.2;
57 outline: none;
58 background-color: #ffffff20;
59 }
60
61 &:active {
62 scale: 1.4;
63 animation: teeter 0.2s;
64 }
65
66 & svg {
67 stroke: var(--text);
68 margin: 2px;
69 }
70 }
71
72 :host {
73 display: flex block;
74 justify-content: space-between;
75 align-items: center;
76 /* gets overridden by * because why not ig */
77 padding: 5px 15px !important;
78 position: sticky;
79 top: 0;
80 left: 0;
81 border-bottom: 4px solid var(--border);
82 user-select: none;
83 }
84 </style>
85</template>
86
87<script>
88 class CodeHeading extends HTMLElement {
89 contents = "";
90 static observedAttributes = ["contents"];
91
92 template: HTMLTemplateElement;
93 content: DocumentFragment;
94 shadowRoot: ShadowRoot;
95
96 constructor() {
97 super();
98 const template = document.getElementById("code-heading");
99 if (!template || !(template instanceof HTMLTemplateElement))
100 throw new Error("Could not get #code-heading");
101 this.template = template;
102 this.content = template.content;
103
104 this.shadowRoot = this.attachShadow({ mode: "open" });
105 this.shadowRoot.appendChild(this.content.cloneNode(true));
106
107 const copy = this.shadowRoot.getElementById("copy");
108 if (!copy) throw new Error("No #copy in #code-heading");
109
110 const copied = this.shadowRoot.getElementById("copied");
111 if (!copied) throw new Error("No #copied in #code-heading");
112
113 const copied_animation = {
114 opacity: [0, 1],
115 visibility: ["hidden", "visible"],
116 };
117
118 copy.addEventListener("click", () => {
119 navigator.clipboard
120 .writeText(this.contents)
121 .catch((e) => {
122 console.error("Encountered error copying to clipboard;", e);
123 })
124 .then(async () => {
125 await copied.animate(copied_animation, {
126 duration: 200,
127 fill: "forwards",
128 }).finished;
129
130 copied.animate(copied_animation, {
131 duration: 200,
132 delay: 2000,
133 fill: "forwards",
134 direction: "reverse",
135 });
136 });
137 });
138 }
139
140 attributeChangedCallback(name: string, _: any, newV?: string) {
141 if (name == "contents") {
142 this.contents = newV ?? "";
143 }
144 }
145 }
146
147 customElements.define("code-heading", CodeHeading);
148</script>