8276768: Snippet copy feature should use button instead of link

Reviewed-by: prappo
This commit is contained in:
Hannes Wallnöfer 2021-11-08 11:35:26 +00:00
parent d8b0dee65e
commit 0395e4ef8c
5 changed files with 70 additions and 70 deletions

View File

@ -447,13 +447,14 @@ public class TagletWriterImpl extends TagletWriter {
String copyText = resources.getText("doclet.Copy_snippet_to_clipboard"); String copyText = resources.getText("doclet.Copy_snippet_to_clipboard");
String copiedText = resources.getText("doclet.Copied_snippet_to_clipboard"); String copiedText = resources.getText("doclet.Copied_snippet_to_clipboard");
HtmlTree snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer, HtmlTree snippetContainer = HtmlTree.DIV(HtmlStyle.snippetContainer,
HtmlTree.A("#", new HtmlTree(TagName.IMG) new HtmlTree(TagName.BUTTON)
.add(HtmlTree.SPAN(Text.of(copyText))
.put(HtmlAttr.DATA_COPIED, copiedText))
.add(new HtmlTree(TagName.IMG)
.put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath()) .put(HtmlAttr.SRC, htmlWriter.pathToRoot.resolve(DocPaths.CLIPBOARD_SVG).getPath())
.put(HtmlAttr.ALT, copyText)) .put(HtmlAttr.ALT, copyText))
.addStyle(HtmlStyle.snippetCopy) .addStyle(HtmlStyle.snippetCopy)
.put(HtmlAttr.ONCLICK, "copySnippet(this)") .put(HtmlAttr.ONCLICK, "copySnippet(this)"));
.put(HtmlAttr.ARIA_LABEL, copyText)
.put(HtmlAttr.DATA_COPIED, copiedText));
return snippetContainer.add(pre.add(code)); return snippetContainer.add(pre.add(code));
} }

View File

@ -106,19 +106,24 @@ function indexFilesLoaded() {
&& tagSearchIndex; && tagSearchIndex;
} }
function copySnippet(link) { function copySnippet(button) {
var textarea = document.createElement("textarea"); var textarea = document.createElement("textarea");
textarea.style.height = 0; textarea.style.height = 0;
document.body.appendChild(textarea); document.body.appendChild(textarea);
textarea.value = link.nextElementSibling.innerText; textarea.value = button.nextElementSibling.innerText;
textarea.select(); textarea.select();
document.execCommand("copy"); document.execCommand("copy");
document.body.removeChild(textarea); document.body.removeChild(textarea);
link.classList.add("copied"); var span = button.firstElementChild;
var parent = link.parentElement; var copied = span.getAttribute("data-copied");
parent.onmouseleave = parent.ontouchend = function() { if (span.innerHTML !== copied) {
link.classList.remove("copied"); var initialLabel = span.innerHTML;
}; span.innerHTML = copied;
var parent = button.parentElement;
parent.onmouseleave = parent.ontouchend = function() {
span.innerHTML = initialLabel;
};
}
} }
// Workaround for scroll position not being included in browser history (8249133) // Workaround for scroll position not being included in browser history (8249133)

View File

@ -936,62 +936,56 @@ pre.snippet {
div.snippet-container { div.snippet-container {
position: relative; position: relative;
} }
a.snippet-copy { button.snippet-copy {
position: absolute; position: absolute;
top: 8px; top: 6px;
right: 8px; right: 6px;
height: 1.7em;
opacity: 50%;
transition: opacity 0.2s;
padding: 2px;
border: none;
cursor: pointer;
background: none;
} }
a.snippet-copy img { button.snippet-copy img {
width: 18px; width: 18px;
height: 18px; height: 18px;
padding: 0.05em 0; padding: 0.05em 0;
opacity: 50%; background: none;
transition: opacity 0.2s;
} }
div.snippet-container:hover a.snippet-copy img { div.snippet-container:hover button.snippet-copy {
opacity: 80%; opacity: 80%;
} }
div.snippet-container a.snippet-copy:hover img { div.snippet-container button.snippet-copy:hover {
opacity: 100%; opacity: 100%;
} }
a.snippet-copy:active img { button.snippet-copy span {
background: #d3d3d3;
opacity: 100%;
}
a.snippet-copy::before {
color: #3d3d3d; color: #3d3d3d;
content: attr(aria-label); content: attr(aria-label);
font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
font-size: 85%; font-size: 85%;
line-height: 1.2em; line-height: 1.2em;
padding: 0.2em; padding: 0.2em;
position: absolute; position: relative;
opacity: 80%;
transition: opacity 0.2s;
white-space: nowrap; white-space: nowrap;
top: -0.01em; top: -0.5em;
right: 1.5em;
display: none; display: none;
} }
div.snippet-container:hover a.snippet-copy::before { div.snippet-container:hover button.snippet-copy span {
display: inherit; display: inline;
} }
div.snippet-container a.snippet-copy:hover::before { button.snippet-copy:active {
background: #d3d3d3;
opacity: 100%; opacity: 100%;
} }
a.snippet-copy.copied::before {
content: attr(data-copied);
}
a.snippet-copy:active::before {
background-color: #dadada;
}
@media screen and (max-width: 800px) { @media screen and (max-width: 800px) {
pre.snippet { pre.snippet {
padding-top: 26px; padding-top: 26px;
} }
a.snippet-copy { button.snippet-copy {
top: 6px; top: 4px;
right: 6px; right: 4px;
} }
} }
pre.snippet .italic { pre.snippet .italic {

View File

@ -224,9 +224,9 @@ public class TestSnippetTag extends JavadocTester {
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block">A method. <div class="block">A method.
\s \s
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"%s><code%s> Hello, Snippet! <pre class="snippet"%s><code%s> Hello, Snippet!
</code></pre> </code></pre>
</div> </div>
@ -948,9 +948,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>%s</code></pre> <pre class="snippet"><code>%s</code></pre>
</div>""".formatted(id, t.expectedOutput())); </div>""".formatted(id, t.expectedOutput()));
}); });
@ -1044,9 +1044,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>%s</code></pre> <pre class="snippet"><code>%s</code></pre>
</div>""".formatted(index, expectedOutput)); </div>""".formatted(index, expectedOutput));
}); });
@ -1605,9 +1605,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>%s</code></pre> <pre class="snippet"><code>%s</code></pre>
</div>""".formatted(index, t.expectedOutput())); </div>""".formatted(index, t.expectedOutput()));
}); });
@ -1722,18 +1722,18 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case0</span>()</div> <span class="element-name">case0</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="copySni\ <div class="snippet-container"><button class="snippet-copy" onclick="copySnippet\
ppet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.svg" alt="\ (this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" alt="Copy"\
Copy"></a> ></button>
<pre class="snippet"><code></code></pre> <pre class="snippet"><code></code></pre>
</div>"""); </div>""");
checkOutput("pkg/A.html", true, checkOutput("pkg/A.html", true,
""" """
<span class="element-name">case1</span>()</div> <span class="element-name">case1</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="copySni\ <div class="snippet-container"><button class="snippet-copy" onclick="copySnippet\
ppet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.svg" alt="\ (this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" alt="Copy"\
Copy"></a> ></button>
<pre class="snippet"><code></code></pre> <pre class="snippet"><code></code></pre>
</div>"""); </div>""");
} }
@ -1832,9 +1832,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>2</code></pre> <pre class="snippet"><code>2</code></pre>
</div> </div>
""".formatted(j)); """.formatted(j));
@ -1916,9 +1916,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>%s</code></pre> <pre class="snippet"><code>%s</code></pre>
</div>""".formatted(index, t.expectedOutput())); </div>""".formatted(index, t.expectedOutput()));
}); });
@ -2248,9 +2248,9 @@ public class TestSnippetTag extends JavadocTester {
""" """
<span class="element-name">case%s</span>()</div> <span class="element-name">case%s</span>()</div>
<div class="block"> <div class="block">
<div class="snippet-container"><a href="#" class="snippet-copy" onclick="cop\ <div class="snippet-container"><button class="snippet-copy" onclick="copySni\
ySnippet(this)" aria-label="Copy" data-copied="Copied!"><img src="../copy.sv\ ppet(this)"><span data-copied="Copied!">Copy</span><img src="../copy.svg" al\
g" alt="Copy"></a> t="Copy"></button>
<pre class="snippet"><code>%s</code></pre> <pre class="snippet"><code>%s</code></pre>
</div>""".formatted(index, t.expectedOutput())); </div>""".formatted(index, t.expectedOutput()));
}); });

View File

@ -351,7 +351,7 @@ public class LinkChecker extends HtmlChecker {
void addReference(String name, Path from, int line) { void addReference(String name, Path from, int line) {
if (checked) { if (checked) {
if (name != null && !name.isEmpty()) { if (name != null) {
ID id = map.get(name); ID id = map.get(name);
if (id == null || !id.declared) { if (id == null || !id.declared) {
error(from, line, "id not found: " + this.name + "#" + name); error(from, line, "id not found: " + this.name + "#" + name);
@ -368,7 +368,7 @@ public class LinkChecker extends HtmlChecker {
void check() { void check() {
map.forEach((name, id) -> { map.forEach((name, id) -> {
if (name != null && !name.isEmpty() && !id.declared) { if (name != null && !id.declared) {
//log.error(currFile, 0, "id not declared: " + name); //log.error(currFile, 0, "id not declared: " + name);
for (Position ref : id.references) { for (Position ref : id.references) {
error(ref.path, ref.line, "id not found: " + this.name + "#" + name); error(ref.path, ref.line, "id not found: " + this.name + "#" + name);