Skip to content

Commit 5ce85cc

Browse files
authored
Merge pull request #21 from grega/install-block
Add tabbed install block
2 parents 143ac4b + ae48089 commit 5ce85cc

3 files changed

Lines changed: 355 additions & 130 deletions

File tree

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<div class="install">
2+
<div class="tabs">
3+
<div role="tablist" aria-labelledby="tablist-1" class="automatic">
4+
<button
5+
id="tab-1"
6+
type="button"
7+
role="tab"
8+
aria-selected="true"
9+
aria-controls="tabpanel-1"
10+
>
11+
<span class="focus">brew</span>
12+
</button>
13+
<button
14+
id="tab-2"
15+
type="button"
16+
role="tab"
17+
aria-selected="false"
18+
aria-controls="tabpanel-2"
19+
tabindex="-1"
20+
>
21+
<span class="focus">curl</span>
22+
</button>
23+
</div>
24+
25+
<div id="tabpanel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
26+
<div class="code-block">
27+
<pre><code>brew install grega/tap/hdi</code></pre>
28+
<button
29+
class="code-block-btn"
30+
data-copy="brew install grega/tap/hdi"
31+
title="Copy to clipboard"
32+
>
33+
Copy
34+
</button>
35+
</div>
36+
</div>
37+
<div
38+
id="tabpanel-2"
39+
role="tabpanel"
40+
tabindex="0"
41+
aria-labelledby="tab-2"
42+
class="is-hidden"
43+
>
44+
<div class="code-block">
45+
<pre><code>curl -fsSL https://raw.githubusercontent.com/grega/hdi/main/hdi \
46+
-o ~/.local/bin/hdi &amp;&amp; chmod +x ~/.local/bin/hdi</code></pre>
47+
<button
48+
class="code-block-btn"
49+
data-copy="curl -fsSL https://raw.githubusercontent.com/grega/hdi/main/hdi -o ~/.local/bin/hdi && chmod +x ~/.local/bin/hdi"
50+
title="Copy to clipboard"
51+
>
52+
Copy
53+
</button>
54+
</div>
55+
</div>
56+
</div>
57+
</div>
58+
59+
<style>
60+
.tabs {
61+
border: 1px solid var(--surface0);
62+
padding: 1rem;
63+
padding-block-start: 0.5rem;
64+
border-radius: 0.5rem;
65+
}
66+
67+
[role="tablist"] {
68+
min-width: 100%;
69+
display: flex;
70+
flex-wrap: wrap;
71+
gap: 0rem 1rem;
72+
margin-block-end: 1rem;
73+
}
74+
75+
[role="tab"],
76+
[role="tab"]:focus,
77+
[role="tab"]:hover {
78+
background-color: unset;
79+
color: var(--text);
80+
padding-inline: 0;
81+
text-underline-offset: 0.5rem;
82+
text-decoration: underline 4px solid;
83+
}
84+
85+
[role="tab"][aria-selected="true"] {
86+
text-decoration-color: var(--blue);
87+
}
88+
89+
[role="tab"][aria-selected="false"] {
90+
text-decoration-color: var(--surface2);
91+
}
92+
93+
[role="tabpanel"] {
94+
padding: 1rem;
95+
background-color: var(--mantle);
96+
width: 100%;
97+
overflow-y: auto;
98+
overflow-x: hidden;
99+
}
100+
101+
[role="tabpanel"].is-hidden {
102+
display: none;
103+
}
104+
105+
.code-block {
106+
width: 100%;
107+
display: flex;
108+
align-items: center;
109+
gap: 1rem;
110+
pre {
111+
flex: 1;
112+
overflow: auto;
113+
justify-self: stretch;
114+
display: flex;
115+
align-items: center;
116+
}
117+
}
118+
119+
.code-block-btn {
120+
align-self: start;
121+
}
122+
</style>
123+
124+
<script>
125+
/*
126+
* Follows W3C Accessible Tabs Pattern
127+
* https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
128+
* https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-automatic/
129+
*/
130+
131+
// @ts-nocheck
132+
133+
class TabsAutomatic {
134+
constructor(groupNode) {
135+
this.tablistNode = groupNode;
136+
137+
this.tabs = [];
138+
139+
this.firstTab = null;
140+
this.lastTab = null;
141+
142+
this.tabs = Array.from(this.tablistNode.querySelectorAll("[role=tab]"));
143+
this.tabpanels = [];
144+
145+
for (var i = 0; i < this.tabs.length; i += 1) {
146+
var tab = this.tabs[i];
147+
var tabpanel = document.getElementById(
148+
tab.getAttribute("aria-controls"),
149+
);
150+
151+
tab.tabIndex = -1;
152+
tab.setAttribute("aria-selected", "false");
153+
this.tabpanels.push(tabpanel);
154+
155+
tab.addEventListener("keydown", this.onKeydown.bind(this));
156+
tab.addEventListener("click", this.onClick.bind(this));
157+
158+
if (!this.firstTab) {
159+
this.firstTab = tab;
160+
}
161+
this.lastTab = tab;
162+
}
163+
164+
this.setSelectedTab(this.firstTab, false);
165+
}
166+
167+
setSelectedTab(currentTab, setFocus) {
168+
if (typeof setFocus !== "boolean") {
169+
setFocus = true;
170+
}
171+
for (var i = 0; i < this.tabs.length; i += 1) {
172+
var tab = this.tabs[i];
173+
if (currentTab === tab) {
174+
tab.setAttribute("aria-selected", "true");
175+
tab.removeAttribute("tabindex");
176+
this.tabpanels[i].classList.remove("is-hidden");
177+
if (setFocus) {
178+
tab.focus();
179+
}
180+
} else {
181+
tab.setAttribute("aria-selected", "false");
182+
tab.tabIndex = -1;
183+
this.tabpanels[i].classList.add("is-hidden");
184+
}
185+
}
186+
}
187+
188+
setSelectedToPreviousTab(currentTab) {
189+
var index;
190+
191+
if (currentTab === this.firstTab) {
192+
this.setSelectedTab(this.lastTab);
193+
} else {
194+
index = this.tabs.indexOf(currentTab);
195+
this.setSelectedTab(this.tabs[index - 1]);
196+
}
197+
}
198+
199+
setSelectedToNextTab(currentTab) {
200+
var index;
201+
202+
if (currentTab === this.lastTab) {
203+
this.setSelectedTab(this.firstTab);
204+
} else {
205+
index = this.tabs.indexOf(currentTab);
206+
this.setSelectedTab(this.tabs[index + 1]);
207+
}
208+
}
209+
210+
/* EVENT HANDLERS */
211+
212+
onKeydown(event) {
213+
var tgt = event.currentTarget,
214+
flag = false;
215+
216+
switch (event.key) {
217+
case "ArrowLeft":
218+
this.setSelectedToPreviousTab(tgt);
219+
flag = true;
220+
break;
221+
222+
case "ArrowRight":
223+
this.setSelectedToNextTab(tgt);
224+
flag = true;
225+
break;
226+
227+
case "Home":
228+
this.setSelectedTab(this.firstTab);
229+
flag = true;
230+
break;
231+
232+
case "End":
233+
this.setSelectedTab(this.lastTab);
234+
flag = true;
235+
break;
236+
237+
default:
238+
break;
239+
}
240+
241+
if (flag) {
242+
event.stopPropagation();
243+
event.preventDefault();
244+
}
245+
}
246+
247+
onClick(event) {
248+
this.setSelectedTab(event.currentTarget);
249+
}
250+
}
251+
252+
// Initialize tablist
253+
254+
window.addEventListener("load", function () {
255+
var tablists = document.querySelectorAll("[role=tablist].automatic");
256+
for (var i = 0; i < tablists.length; i++) {
257+
new TabsAutomatic(tablists[i]);
258+
}
259+
});
260+
</script>
261+
262+
<script>
263+
// Copy buttons
264+
document.querySelectorAll(".code-block-btn").forEach(function (btn) {
265+
btn.addEventListener("click", function () {
266+
const htmlBtn = btn as HTMLElement;
267+
navigator.clipboard
268+
.writeText(htmlBtn.dataset.copy || "")
269+
.then(function () {
270+
var original = htmlBtn.textContent;
271+
htmlBtn.textContent = "Copied!";
272+
htmlBtn.classList.add("copied");
273+
setTimeout(function () {
274+
htmlBtn.textContent = original;
275+
htmlBtn.classList.remove("copied");
276+
}, 1500);
277+
});
278+
});
279+
});
280+
</script>

0 commit comments

Comments
 (0)