Skip to content
Draft
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
3 changes: 3 additions & 0 deletions assets/js/bioconductor.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ var handleCitations = function () {

data = data.replace(" (????)", "");
jQuery("#bioc-citation").html(data);
if (window.initializeCopyButtons) {
window.initializeCopyButtons();
}
jQuery("#bioc-citation-outer").show();
},
error: function (data, textStatus, jqXHR) {
Expand Down
139 changes: 138 additions & 1 deletion assets/js/code_blocks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,144 @@
document.addEventListener("DOMContentLoaded", () => {
const copyButtonIcon = `
<svg aria-hidden="true" viewBox="0 0 24 24" focusable="false">
<path d="M16 1H6C4.9 1 4 1.9 4 3V17H6V3H16V1ZM19 5H10C8.9 5 8 5.9 8 7V21C8 22.1 8.9 23 10 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H10V7H19V21Z"></path>
</svg>
`;
const defaultCopyLabel = "Copy";
let copyFallbackWarningShown = false;

const trimCodeBlocks = () => {
Array.from(document.getElementsByTagName("code")).forEach((block) => {
block.innerHTML = block.innerHTML.trim();
});
};

const getCopyText = (button) => {
const copyTarget = button.dataset.copyTarget;

if (copyTarget) {
const target = document.querySelector(copyTarget);

if (target) {
return (target.innerText || target.textContent || "").trim();
}
}

const codeBlock = button.closest("pre")?.querySelector("code");

if (codeBlock) {
return (codeBlock.innerText || codeBlock.textContent || "").trim();
}

return "";
};

const setCopyButtonState = (button, state = "default") => {
const defaultLabel = button.dataset.copyLabel || defaultCopyLabel;
const successLabel = button.dataset.copySuccessLabel || "Copied";
const errorLabel = button.dataset.copyErrorLabel || "Copy failed";
let label = defaultLabel;

if (state === "copied") {
label = successLabel;
} else if (state === "error") {
label = errorLabel;
}

button.classList.toggle("copied", state === "copied");
button.classList.toggle("copy-error", state === "error");
button.setAttribute("aria-label", label);
button.setAttribute("title", label);

const text = button.querySelector(".copy-button-label");

if (text) {
text.textContent = label;
}
};

const fallbackCopyText = (text) => {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.setAttribute("readonly", "");
textArea.style.position = "fixed";
textArea.style.left = "-9999px";
textArea.style.top = "0";
document.body.appendChild(textArea);
textArea.select();
if (!copyFallbackWarningShown) {
console.warn("Falling back to document.execCommand('copy').");
copyFallbackWarningShown = true;
}
const copied = document.execCommand("copy");
document.body.removeChild(textArea);

if (!copied) {
throw new Error("Copy command failed");
}
};

const handleCopyClick = async (event) => {
const button = event.currentTarget;
const text = getCopyText(button);

if (!text) {
return;
}

try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(text);
} else {
fallbackCopyText(text);
}

setCopyButtonState(button, "copied");
window.setTimeout(() => setCopyButtonState(button), 2000);
} catch {
setCopyButtonState(button, "error");
window.setTimeout(() => setCopyButtonState(button), 2000);
}
};

const initializeCopyButton = (button) => {
if (button.dataset.copyButtonReady === "true") {
return;
}

button.type = "button";
button.classList.add("copy-button");

if (!button.querySelector(".copy-button-label")) {
button.innerHTML = `${copyButtonIcon}<span class="copy-button-label">${button.dataset.copyLabel || defaultCopyLabel}</span>`;
}

setCopyButtonState(button);
button.addEventListener("click", handleCopyClick);
button.dataset.copyButtonReady = "true";
};

const addCodeCopyButton = (pre) => {
if (pre.querySelector(".copy-button")) {
return;
}

const button = document.createElement("button");
button.className = "copy-button code-copy-button";
button.dataset.copyLabel = defaultCopyLabel;
button.dataset.copySuccessLabel = "Copied";
pre.appendChild(button);
initializeCopyButton(button);
};

const initializeCopyButtons = () => {
document.querySelectorAll("pre[data-copyable]").forEach(addCodeCopyButton);
document.querySelectorAll(".copy-button[data-copy-target]").forEach(initializeCopyButton);
};

document.addEventListener("DOMContentLoaded", () => {
trimCodeBlocks();
initializeCopyButtons();
window.hljs.highlightAll();
});

window.initializeCopyButtons = initializeCopyButtons;
77 changes: 77 additions & 0 deletions assets/style/components/code.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pre:has(code) {
background: var(--gradient-brandreverse);
white-space: normal;
margin-bottom: 2rem;
position: relative;
}

code * {
Expand All @@ -22,6 +23,11 @@ pre code.hljs {
font-family: Courier, monospace;
}

pre[data-copyable] code,
pre[data-copyable] code.hljs {
padding-top: 3rem;
}

pre code,
pre code.hljs,
.hljs-punctuation {
Expand Down Expand Up @@ -114,3 +120,74 @@ pre code.light .hljs-title {
overflow-x:auto;
white-space:pre;
}

.copy-button {
align-items: center;
background: rgb(255 255 255 / 12%);
border: 1px solid rgb(255 255 255 / 30%);
border-radius: 999px;
color: white;
cursor: pointer;
display: inline-flex;
gap: 0.35rem;
line-height: 1;
padding: 0.4rem 0.7rem;
transition: background 0.2s ease;
}

.copy-button:hover,
.copy-button:focus {
background: rgb(255 255 255 / 20%);
}

.copy-button.copied {
background: var(--primary-p400);
border-color: var(--primary-p400);
}

.copy-button.copy-error {
background: var(--error-e500);
border-color: var(--error-e500);
}

.copy-button svg {
fill: currentcolor;
height: 0.9rem;
width: 0.9rem;
}

.code-copy-button {
position: absolute;
right: 0.75rem;
top: 0.75rem;
z-index: 1;
}

.copy-button-label {
font-size: 0.85rem;
font-family: inherit;
}

.bioc-citation-header {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 0.5rem;
}

.bioc-citation-header .copy-button {
background: var(--primary-p400);
border-color: var(--primary-p400);
}

.bioc-citation-header .copy-button:hover,
.bioc-citation-header .copy-button:focus {
background: var(--primary-p500);
}

@media (prefers-reduced-motion: reduce) {
.copy-button {
transition: none;
}
}
22 changes: 11 additions & 11 deletions content/install.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h3 id="install-bioconductor-packages">Install Bioconductor Packages</h3>
<p>To install core packages, type the following in an R command window:</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
Expand All @@ -12,7 +12,7 @@ <h3 id="install-bioconductor-packages">Install Bioconductor Packages</h3>

<p>To install core packages, type the following in an R command window:</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
BiocManager::install(c("GenomicFeatures", "AnnotationDbi"))
</code>
Expand All @@ -38,7 +38,7 @@ <h3 id="find-bioconductor-packages">Find Bioconductor Packages</h3>
To search through available packages programmatically, use the following:
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
BiocManager::available()
</code>
Expand All @@ -59,7 +59,7 @@ <h3 id="update-bioconductor-packages">
version of Bioconductor, start a new session of R and enter:
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
BiocManager::install()
</code>
Expand All @@ -78,7 +78,7 @@ <h5>Updgrading Installed Bioconductor Packages</h5>
for your version of R, enter:
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
Expand Down Expand Up @@ -113,7 +113,7 @@ <h5>Recompiling Installed Bioconductor Packages</h5>
to address this might be to start a new R session and enter:
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
Expand All @@ -136,7 +136,7 @@ <h3 id="troubleshoot-bioconductor-packages">

<p>Use the commands:</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
BiocManager::valid() ## R version 3.5 or later
</code>
Expand Down Expand Up @@ -212,7 +212,7 @@ <h3 id="why-biocmanagerinstall">Why use BiocManager::install()?</h3>
R in use regardless of the R and Bioconductor release cycles.
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
> library(BiocManager)
Bioconductor version 3.9 (BiocManager 1.30.4), ?BiocManager::install
Expand All @@ -233,7 +233,7 @@ <h3 id="why-biocmanagerinstall">Why use BiocManager::install()?</h3>
would like to update.
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
> BiocManager::install()
Bioconductor version 3.9 (BiocManager 1.30.4), R 3.6.0 Patched
Expand All @@ -250,7 +250,7 @@ <h3 id="why-biocmanagerinstall">Why use BiocManager::install()?</h3>
version of Bioconductor.
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
> BiocManager::install(version = "devel")
Upgrade 89 packages to Bioconductor version '3.10'? [y/n]: y
Expand Down Expand Up @@ -286,7 +286,7 @@ <h3 id="why-biocmanagerinstall">Why use BiocManager::install()?</h3>
a repository; regular users would seldom have these).
</p>

<pre>
<pre data-copyable>
<code class="dark nohighlight">
> BiocManager::valid()

Expand Down
13 changes: 11 additions & 2 deletions layouts/_bioc_views_package_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ <h2><%= @package[:Title]%></h2>
<strong>Maintainer:</strong> <%= filter_emails(@package[:Maintainer])%>
</p>
<div id="bioc-citation-outer">
<strong>Citation (from within R, enter <code>citation("<%=@package[:Package]%>")</code>):</strong>
<div class="bioc-citation-header">
<strong>Citation (from within R, enter <code>citation("<%=@package[:Package]%>")</code>):</strong>
<button
type="button"
class="copy-button"
data-copy-target="#bioc-citation"
data-copy-label="Copy citation"
data-copy-success-label="Citation copied">
Copy citation
</button>
</div>
<div id="bioc-citation" class="bioc-citation"></div>
</div>
</div>
Expand All @@ -47,4 +57,3 @@ <h2><%= @package[:Title]%></h2>
<%= render("/components/packages/details/", :package => @package) %>
<%= render("/components/packages/archives/", :package => @package) %>
</div>

2 changes: 1 addition & 1 deletion layouts/components/packages/installation.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ <h3>Installation</h3>
To install this package, start R (version "<%= r_ver_for_bioc_ver(@package[:bioc_version_num]) %>") and enter:
</p>

<pre><code>
<pre data-copyable><code>
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
<% if (@package[:bioc_version_str] == "Development") %>
Expand Down