mirror of
https://github.com/caddyserver/website.git
synced 2025-04-21 20:46:15 -04:00
add copy to clipboard functionality
This commit is contained in:
parent
f830794fc4
commit
d25c7a8f90
2 changed files with 228 additions and 112 deletions
|
@ -80,7 +80,7 @@ header nav .button {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.breadcrumb-siblings a+a {
|
||||
.breadcrumb-siblings a + a {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ main > nav.sidebar {
|
|||
main nav li img,
|
||||
article li img,
|
||||
img.external-link {
|
||||
max-height: .9em;
|
||||
max-height: 0.9em;
|
||||
}
|
||||
|
||||
main nav ul {
|
||||
|
@ -133,26 +133,24 @@ main nav li a {
|
|||
color: #546c75;
|
||||
}
|
||||
|
||||
|
||||
|
||||
main nav li a:hover {
|
||||
color: #01324b;
|
||||
}
|
||||
|
||||
main nav li a:before {
|
||||
content: '\203A';
|
||||
content: "\203A";
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 1rem;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: 0;
|
||||
transition: left .15s, opacity .15s;
|
||||
transition: left 0.15s, opacity 0.15s;
|
||||
}
|
||||
|
||||
main nav li a:hover:before {
|
||||
opacity: 1;
|
||||
left: .75em;
|
||||
left: 0.75em;
|
||||
}
|
||||
|
||||
main nav li li a:hover:before {
|
||||
|
@ -187,7 +185,7 @@ main nav li li a {
|
|||
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 2px 4px 1px rgba(0, 0, 0, .15);
|
||||
box-shadow: 1px 2px 4px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#paper1,
|
||||
|
@ -258,8 +256,12 @@ article pre,
|
|||
article table {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
article > .fullwidth { margin-bottom: 1.5rem; }
|
||||
article > .fullwidth > * { margin-bottom: 0; }
|
||||
article > .fullwidth {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
article > .fullwidth > * {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
article > pre.chroma > code {
|
||||
background: none;
|
||||
|
@ -292,7 +294,7 @@ article li {
|
|||
article li p,
|
||||
article li ul,
|
||||
article li ol {
|
||||
margin-bottom: .5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
@ -341,10 +343,10 @@ h5 {
|
|||
|
||||
.anchor-link {
|
||||
opacity: 0;
|
||||
font-size: .6em;
|
||||
font-size: 0.6em;
|
||||
border-radius: 10px;
|
||||
padding: .3em .5em;
|
||||
margin-left: .25em;
|
||||
padding: 0.3em 0.5em;
|
||||
margin-left: 0.25em;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
}
|
||||
|
@ -356,7 +358,7 @@ h5 {
|
|||
}
|
||||
|
||||
.anchor-link:hover {
|
||||
background-color: rgba(0, 0, 0, .075);
|
||||
background-color: rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
code {
|
||||
|
@ -384,25 +386,67 @@ article > pre {
|
|||
|
||||
pre > code.cmd {
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre > code.cmd button {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
margin-block: 0;
|
||||
aspect-ratio: 1;
|
||||
width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #1a2c47;
|
||||
color: #ffffff;
|
||||
border-radius: 50%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.copy-tooltip {
|
||||
position: absolute;
|
||||
right: 59px;
|
||||
top: 13px;
|
||||
color: #ffffff;
|
||||
background: #1a2c47;
|
||||
border: 1px solid transparent;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.copy-tooltip::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: -9px;
|
||||
top: 13px;
|
||||
border-left: 10px solid #1a2c47;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-top: 8px solid transparent;
|
||||
}
|
||||
|
||||
code.cmd.bash,
|
||||
code.cmd .bash,
|
||||
code.cmd.bash-continuation,
|
||||
code.cmd .bash-continuation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code.cmd.bash::before,
|
||||
code.cmd .bash::before {
|
||||
content: '$';
|
||||
margin-right: .5rem;
|
||||
content: "$";
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
code.cmd.bash-continuation::before,
|
||||
code.cmd .bash-continuation::before {
|
||||
content: '>';
|
||||
margin-right: .5rem;
|
||||
content: ">";
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
dt:hover .inline-link {
|
||||
|
@ -419,10 +463,10 @@ dd {
|
|||
|
||||
.field-name {
|
||||
display: block;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-family: "Source Code Pro", monospace;
|
||||
margin-top: 2em;
|
||||
font-weight: bold;
|
||||
margin-bottom: .5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.inline-link {
|
||||
|
@ -430,8 +474,8 @@ dd {
|
|||
position: absolute;
|
||||
margin-left: -1.5em;
|
||||
/* margin-top: -.1em; */
|
||||
padding-right: .3em;
|
||||
padding-left: .2em;
|
||||
padding-right: 0.3em;
|
||||
padding-left: 0.2em;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
@ -499,37 +543,49 @@ iframe {
|
|||
line-height: 1em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.json {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
.json .qu { color: #5c91bf; }
|
||||
.json .key { color: #1c83dc; font-weight: bold; }
|
||||
.json .str { color: #2f8598; }
|
||||
.json .num { color: #038a3f; }
|
||||
.json .bool { color: #9b5e14; }
|
||||
.json .key a:not([href]) { color: #222; }
|
||||
.json .qu {
|
||||
color: #5c91bf;
|
||||
}
|
||||
.json .key {
|
||||
color: #1c83dc;
|
||||
font-weight: bold;
|
||||
}
|
||||
.json .str {
|
||||
color: #2f8598;
|
||||
}
|
||||
.json .num {
|
||||
color: #038a3f;
|
||||
}
|
||||
.json .bool {
|
||||
color: #9b5e14;
|
||||
}
|
||||
.json .key a:not([href]) {
|
||||
color: #222;
|
||||
}
|
||||
article .json a {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.json .has-popup { border-bottom: 1px dashed #222; }
|
||||
.json a[href].has-popup { border-bottom-color: #1c82dc; }
|
||||
.json .has-popup.module { border-bottom: none; }
|
||||
|
||||
.json .has-popup {
|
||||
border-bottom: 1px dashed #222;
|
||||
}
|
||||
.json a[href].has-popup {
|
||||
border-bottom-color: #1c82dc;
|
||||
}
|
||||
.json .has-popup.module {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#hovercard {
|
||||
max-width: 450px;
|
||||
border-radius: 10px;
|
||||
filter: drop-shadow(0 5px 5px rgba(0, 0, 0, .25));
|
||||
filter: drop-shadow(0 5px 5px rgba(0, 0, 0, 0.25));
|
||||
position: absolute;
|
||||
font-size: 16px;
|
||||
transition: transform .25s ease-in-out, opacity .25s ease-in-out;
|
||||
transition: transform 0.25s ease-in-out, opacity 0.25s ease-in-out;
|
||||
/* TODO: This would be nice, but it breaks the arrow-box... */
|
||||
/* max-height: 50%;
|
||||
overflow-y: auto; */
|
||||
|
@ -541,6 +597,14 @@ article .json a {
|
|||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.show {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.popup {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
|
@ -572,12 +636,12 @@ article .json a {
|
|||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
#hovercard p+p {
|
||||
#hovercard p + p {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#hovercard li {
|
||||
margin: .25em;
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
#hovercard pre > code {
|
||||
|
@ -599,7 +663,7 @@ article .json a {
|
|||
font-size: 18px;
|
||||
line-height: 1em;
|
||||
font-weight: bold;
|
||||
padding: .5em 1em;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
#hovercard .module-link:hover {
|
||||
|
@ -614,7 +678,7 @@ article .json a {
|
|||
#hovercard .module-link-description {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
margin-left: .5em;
|
||||
margin-left: 0.5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
@ -677,14 +741,14 @@ article aside.advice {
|
|||
}
|
||||
|
||||
article aside.tip:nth-child(even)::before {
|
||||
content: '💁♀️';
|
||||
content: "💁♀️";
|
||||
}
|
||||
article aside.tip:nth-child(odd)::before {
|
||||
content: '💁♂️';
|
||||
content: "💁♂️";
|
||||
}
|
||||
|
||||
article aside.advice::before {
|
||||
content: '🤦';
|
||||
content: "🤦";
|
||||
}
|
||||
|
||||
article aside.complete {
|
||||
|
@ -696,14 +760,14 @@ article aside.complete {
|
|||
}
|
||||
|
||||
article aside.complete::before {
|
||||
content: '✅ complete';
|
||||
content: "✅ complete";
|
||||
color: #39c849;
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
margin-right: 2em;
|
||||
margin-bottom: .5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
table {
|
||||
|
@ -716,7 +780,8 @@ article > table {
|
|||
margin: 25px auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
line-height: 1.4em;
|
||||
|
@ -734,11 +799,6 @@ td code {
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#module-docs-container,
|
||||
#module-list-container,
|
||||
#module-template {
|
||||
|
@ -763,7 +823,6 @@ td code {
|
|||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
#module-multiple-repos {
|
||||
display: none;
|
||||
margin: 25px;
|
||||
|
@ -786,9 +845,6 @@ td code {
|
|||
margin: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.tabs .tab-buttons {
|
||||
display: flex;
|
||||
font-size: 0;
|
||||
|
@ -833,19 +889,6 @@ td code {
|
|||
border-radius: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
table {
|
||||
font-size: 14px;
|
||||
|
@ -953,8 +996,6 @@ td code {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #060e17;
|
||||
|
@ -971,8 +1012,14 @@ td code {
|
|||
--docsearch-searchbox-focus-background: rgb(28, 52, 79);
|
||||
--docsearch-text-color: #bdd6f7;
|
||||
--docsearch-muted-color: #96a9c4;
|
||||
--docsearch-key-gradient: linear-gradient(-26.5deg, #060e17 0%, rgb(47, 62, 72) 100%);
|
||||
--docsearch-key-shadow: inset 0 -2px 0 0 #4c4c56, inset 0 0 1px 1px rgb(70, 70, 70), 0 1px 2px 1px rgba(77, 79, 91, 0.4);
|
||||
--docsearch-key-gradient: linear-gradient(
|
||||
-26.5deg,
|
||||
#060e17 0%,
|
||||
rgb(47, 62, 72) 100%
|
||||
);
|
||||
--docsearch-key-shadow: inset 0 -2px 0 0 #4c4c56,
|
||||
inset 0 0 1px 1px rgb(70, 70, 70),
|
||||
0 1px 2px 1px rgba(77, 79, 91, 0.4);
|
||||
}
|
||||
/* End Algolia DocSearch */
|
||||
|
||||
|
@ -1032,7 +1079,7 @@ td code {
|
|||
}
|
||||
|
||||
.json .key a:not([href]) {
|
||||
color: #bdd6f7
|
||||
color: #bdd6f7;
|
||||
}
|
||||
|
||||
#hovercard,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
$(function() {
|
||||
$(function () {
|
||||
function hasPrefix(str, prefix) {
|
||||
if (!prefix) return true;
|
||||
if (!str) return false;
|
||||
if (!str) return false;
|
||||
return str.indexOf(prefix) === 0;
|
||||
}
|
||||
|
||||
// highlight current page in left nav
|
||||
var $currentPageLink = $('main nav a[href="'+window.location.pathname+'"]');
|
||||
var $currentPageLink = $(
|
||||
'main nav a[href="' + window.location.pathname + '"]'
|
||||
);
|
||||
if (hasPrefix(window.location.pathname, "/docs/json/")) {
|
||||
// as a special case, highlight the JSON structure link anywhere within it
|
||||
$currentPageLink = $('main nav a[href="/docs/json/"]');
|
||||
|
@ -15,11 +17,17 @@ $(function() {
|
|||
// as another special case, highlight the modules link anywhere within it
|
||||
$currentPageLink = $('main nav a[href="/docs/modules/"]');
|
||||
}
|
||||
$currentPageLink.addClass('current');
|
||||
$currentPageLink.addClass("current");
|
||||
|
||||
// add anchor links, inspired by https://github.com/bryanbraun/anchorjs
|
||||
$('article > h1[id], article > h2[id], article > h3[id], article > h4[id], article > h5[id], article > h6[id]').each(function() {
|
||||
var $anchor = $('<a href="#'+this.id+'" class="anchor-link" title="Direct link">🔗</a>');
|
||||
$(
|
||||
"article > h1[id], article > h2[id], article > h3[id], article > h4[id], article > h5[id], article > h6[id]"
|
||||
).each(function () {
|
||||
var $anchor = $(
|
||||
'<a href="#' +
|
||||
this.id +
|
||||
'" class="anchor-link" title="Direct link">🔗</a>'
|
||||
);
|
||||
$(this).append($anchor);
|
||||
});
|
||||
|
||||
|
@ -29,32 +37,68 @@ $(function() {
|
|||
// the <pre>; this line finds those and adds a .chroma class
|
||||
// to the outer pre element, and our CSS file has a style to
|
||||
// ensure the inner code block does not produce extra padding
|
||||
$('article > pre:not(.chroma) > code:not(.cmd)').parent().addClass('chroma');
|
||||
$("article > pre:not(.chroma) > code:not(.cmd)")
|
||||
.parent()
|
||||
.addClass("chroma");
|
||||
|
||||
// Add links to Caddyfile directive tokens in code blocks.
|
||||
// See include/docs-head.html for the whitelist bootstrapping logic
|
||||
$('pre.chroma .k')
|
||||
.filter((k, item) =>
|
||||
window.CaddyfileDirectives.includes(item.innerText)
|
||||
|| item.innerText === '<directives...>'
|
||||
$("pre.chroma .k")
|
||||
.filter(
|
||||
(k, item) =>
|
||||
window.CaddyfileDirectives.includes(item.innerText) ||
|
||||
item.innerText === "<directives...>"
|
||||
)
|
||||
.map(function(k, item) {
|
||||
.map(function (k, item) {
|
||||
let text = item.innerText;
|
||||
let url = text === '<directives...>'
|
||||
? '/docs/caddyfile/directives'
|
||||
: '/docs/caddyfile/directives/' + text;
|
||||
text = text.replace(/</g,'<').replace(/>/g,'>');
|
||||
$(item).html('<a href="' + url + '" style="color: inherit;" title="Directive">' + text + '</a>');
|
||||
let url =
|
||||
text === "<directives...>"
|
||||
? "/docs/caddyfile/directives"
|
||||
: "/docs/caddyfile/directives/" + text;
|
||||
text = text.replace(/</g, "<").replace(/>/g, ">");
|
||||
$(item).html(
|
||||
'<a href="' +
|
||||
url +
|
||||
'" style="color: inherit;" title="Directive">' +
|
||||
text +
|
||||
"</a>"
|
||||
);
|
||||
});
|
||||
|
||||
// Add links to [<matcher>] or named matcher tokens in code blocks.
|
||||
// The matcher text includes <> characters which are parsed as HTML,
|
||||
// so we must use text() to change the link text.
|
||||
$('pre.chroma .nd')
|
||||
.map(function(k, item) {
|
||||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
$(item).html('<a href="/docs/caddyfile/matchers#syntax" style="color: inherit;" title="Matcher token">' + text + '</a>');
|
||||
});
|
||||
$("pre.chroma .nd").map(function (k, item) {
|
||||
let text = item.innerText.replace(/</g, "<").replace(/>/g, ">");
|
||||
$(item).html(
|
||||
'<a href="/docs/caddyfile/matchers#syntax" style="color: inherit;" title="Matcher token">' +
|
||||
text +
|
||||
"</a>"
|
||||
);
|
||||
});
|
||||
|
||||
// select all bash code elements and dynamically add clipboard button/svg
|
||||
$("pre > code.bash").map(function (_, block) {
|
||||
// only add button if browser supports Clipboard API
|
||||
if (navigator.clipboard) {
|
||||
let tooltipText = "Copied";
|
||||
|
||||
let icon = $(
|
||||
`<svg fill="currentColor" class="copy_icon" width="32" height="32" viewBox="0 0 32 32"><path d="M25.629 27.591v-18.051h-14.127v18.051h14.127zM25.629 7.005q1.026 0 1.811 0.755t0.785 1.781v18.051q0 1.026-0.785 1.811t-1.811 0.785h-14.127q-1.026 0-1.811-0.785t-0.785-1.811v-18.051q0-1.026 0.785-1.781t1.811-0.755h14.127zM21.766 1.813v2.596h-15.455v18.051h-2.536v-18.051q0-1.026 0.755-1.811t1.781-0.785h15.455z"></path></svg>`
|
||||
);
|
||||
let $button = $(`<button>`).append(icon);
|
||||
|
||||
let $tooltip = $(`<div class="copy-tooltip">${tooltipText}</div>`);
|
||||
|
||||
let $codeBlockElem = $(block);
|
||||
$codeBlockElem.append($button);
|
||||
$codeBlockElem.append($tooltip);
|
||||
|
||||
$button.on("click", async function () {
|
||||
await copyToClipboard(block, $button);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// addLinkaddLinksToSubdirectivessToAnchors finds all the ID anchors
|
||||
|
@ -62,26 +106,51 @@ $(function() {
|
|||
// links that have an ID on the page. This is opt-in for each page,
|
||||
// because it's not necessary to run everywhere.
|
||||
function addLinksToSubdirectives() {
|
||||
let anchors = $('article *[id]').map((i, el) => el.id).toArray();
|
||||
$('pre.chroma .k')
|
||||
let anchors = $("article *[id]")
|
||||
.map((i, el) => el.id)
|
||||
.toArray();
|
||||
$("pre.chroma .k")
|
||||
.filter((k, item) => anchors.includes(item.innerText))
|
||||
.map(function(k, item) {
|
||||
let text = item.innerText.replace(/</g,'<').replace(/>/g,'>');
|
||||
let url = '#' + item.innerText;
|
||||
$(item).html('<a href="' + url + '" style="color: inherit;" title="' + text + '">' + text + '</a>');
|
||||
.map(function (k, item) {
|
||||
let text = item.innerText
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
let url = "#" + item.innerText;
|
||||
$(item).html(
|
||||
'<a href="' +
|
||||
url +
|
||||
'" style="color: inherit;" title="' +
|
||||
text +
|
||||
'">' +
|
||||
text +
|
||||
"</a>"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function stripScheme(url) {
|
||||
return url.substring(url.indexOf("://")+3);
|
||||
return url.substring(url.indexOf("://") + 3);
|
||||
}
|
||||
|
||||
// splitTypeName splits a fully qualified type name into
|
||||
// its package path and type name components, for example:
|
||||
// "github.com/foo/bar.Type" => "github.com/foo/bar" and "Type".
|
||||
function splitTypeName(fqtn) {
|
||||
let lastDotPos = fqtn.lastIndexOf('.');
|
||||
let lastDotPos = fqtn.lastIndexOf(".");
|
||||
let pkg = fqtn.substr(0, lastDotPos);
|
||||
let typeName = fqtn.substr(lastDotPos+1);
|
||||
return {pkg: pkg, typeName: typeName};
|
||||
let typeName = fqtn.substr(lastDotPos + 1);
|
||||
return { pkg: pkg, typeName: typeName };
|
||||
}
|
||||
|
||||
async function copyToClipboard(elem, button) {
|
||||
// cache the selector to avoid unnessessary overhead
|
||||
let $codeElem = $(elem);
|
||||
let $btnElem = $(button);
|
||||
|
||||
// grab the first elements text content
|
||||
let text = $codeElem.contents().get(0).nodeValue;
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
// visual feedback that task is completed
|
||||
$btnElem.next().fadeIn(500).fadeTo(1000, 1).delay(500).fadeOut(500);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue