Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "Add to Bookmarks" feature #2440

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
bikeshed = {editable = true,path = "."}
bikeshed = {editable = true, path = "."}

[packages]
aiofiles = "==22.1.0"
Expand Down
113 changes: 113 additions & 0 deletions bikeshed/boilerplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,105 @@ def addIDLSection(doc: t.SpecT) -> None:
h.addClass(doc, container, "highlight")


bookmarkCss = """
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24
}
.heading {
position: relative;
}
.bookmark-tooltip {
opacity: 0;
display: block;
position: absolute;
bottom: 100%;
left: 0;
pointer-events: auto;
border: shadow 2px gray;
padding: 0;
margin: 0;
background: rgb(200 200 200 / 50%);
color: black;
border-radius: 5px;
}
a.has-bookmark-tooltip {
display: block;
}
.bookmark-tooltip button {
padding: 0;
background: rgb(100 100 100 / 50%);
}
.has-bookmark-tooltip:hover .bookmark-tooltip {
opacity: 0.8;
}
.bookmarksSection {
margin-left: 2em;
}
"""

bookmarkScript = """
"use strict";
// Add tooltip behavior to all headers.
const headers = document.querySelectorAll('.heading');
// We need to get the createElement method now, or else it is not available!?
const createElement = document.createElement;
const makeTag = (tag) => {
return createElement.call(document, tag);
}

for (let h of headers) {
const textElement = h.querySelector('span.content');
insertTooltipAction(h, 'bookmark_add', 'Add bookmark', (event) => {
addBookmark(h, textElement.textContent); })
}

function insertTooltipAction(element, className, title, action) {
const tooltipSpan = makeTag('span');
tooltipSpan.className = 'bookmark-tooltip';
const button = makeTag('button');
button.setHTML(
`<span class="material-symbols-outlined"
title="${title}">${className}</span>`);
button.addEventListener('click', action);
tooltipSpan.insertAdjacentElement('beforeend', button);
element.insertAdjacentElement('beforeend', tooltipSpan);
element.classList.add('has-bookmark-tooltip');
}

function addBookmark(header, text) {
// console.info('add bookmark for', header);
let bookmarksSection = document.querySelector('.bookmarksSection');
if (bookmarksSection == null) {
const tocContents = document.querySelector('#toc #contents');
bookmarksSection = makeTag('div');
bookmarksSection.className = 'bookmarksSection';
bookmarksSection.setHTML('Bookmarks <ul class="bookmarks"></ul>');
tocContents.insertAdjacentElement('afterend', bookmarksSection);
}
const bookmarksList = document.querySelector('.bookmarks');
const bookmarkItem = makeTag('li');
bookmarksList.insertAdjacentElement('beforeend', bookmarkItem);

const bookmarkLink = makeTag('a');
bookmarkLink.href = `#${header.id}`;
bookmarkLink.textContent = text;
bookmarkItem.insertAdjacentElement('beforeend', bookmarkLink);

insertTooltipAction(bookmarkLink, 'bookmark_remove', 'Remove bookmark',
(event) => {
removeBookmark(bookmarkItem);
event.stopPropagation();
event.preventDefault();
});
}

function removeBookmark(bookmarkItem) {
const parentElement = bookmarkItem.parentElement;
parentElement.removeChild(bookmarkItem);
}
"""


def addTOCSection(doc: t.SpecT) -> None:
toc = getFillContainer("table-of-contents", doc=doc, default=False)
if toc is None:
Expand All @@ -787,6 +886,20 @@ def addTOCSection(doc: t.SpecT) -> None:
_("Table of Contents"),
),
)
if "bookmark" in doc.md.boilerplate:
# Load icon for "add bookmark"
h.appendChild(
toc,
h.E.link(
{
"rel": "stylesheet",
"href": "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0",
}
),
)

doc.extraScripts["script-bookmark"] = bookmarkScript
doc.extraStyles["css-bookmark"] = bookmarkCss

# containers[n] holds the current <ol> for inserting each heading's <li> into.
# containers[1] is initialized with something arbitrary
Expand Down
101 changes: 100 additions & 1 deletion tests/abstract001.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Foo</title>
<link href="http://example.com/foo" rel="canonical">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" rel="stylesheet">
<style>/* css-bookmark */

.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24
}
.heading {
position: relative;
}
.tooltip {
opacity: 0;
display: block;
position: absolute;
top: -90%;
left: 0;
pointer-events: auto;
border: shadow 2px gray;
padding: 0;
margin: 0;
background: rgb(200 200 200 / 50%);
color: black;
border-radius: 5px;
}
a.has-tooltip {
display: block;
}
.tooltip button {
padding: 0;
background: rgb(100 100 100 / 50%);
}
.has-tooltip:hover .tooltip {
opacity: 0.8;
}
.bookmarksSection {
margin-left: 2em;
}
</style>
<style>/* style-autolinks */

.css.css, .property.property, .descriptor.descriptor {
Expand Down Expand Up @@ -432,4 +469,66 @@ <h2 class="no-num no-toc no-ref heading settled" id="abstract"><span class="cont
<nav data-fill-with="table-of-contents" id="toc">
<h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
</nav>
<main></main>
<main></main>
<script>/* script-bookmark */

"use strict";
// Add tooltip behavior to all headers.
const headers = document.querySelectorAll('.heading');
// We need to get the createElement method now, or else it is not available!?
const createElement = document.createElement;
const makeTag = (tag) => {
return createElement.call(document, tag);
}

for (let h of headers) {
const textElement = h.querySelector('span.content');
insertTooltipAction(h, 'bookmark_add', 'Add bookmark', (event) => {
addBookmark(h, textElement.textContent); })
}

function insertTooltipAction(element, className, title, action) {
const tooltipDiv = makeTag('div');
tooltipDiv.className = 'tooltip';
const button = makeTag('button');
button.setHTML(
`<span class="material-symbols-outlined"
title="${title}">${className}</span>`);
button.addEventListener('click', action);
tooltipDiv.insertAdjacentElement('beforeend', button);
element.insertAdjacentElement('beforeend', tooltipDiv);
element.classList.add('has-tooltip');
}

function addBookmark(header, text) {
// console.info('add bookmark for', header);
let bookmarksSection = document.querySelector('.bookmarksSection');
if (bookmarksSection == null) {
const tocContents = document.querySelector('#toc #contents');
bookmarksSection = makeTag('div');
bookmarksSection.className = 'bookmarksSection';
bookmarksSection.setHTML('Bookmarks <ul class="bookmarks"></ul>');
tocContents.insertAdjacentElement('afterend', bookmarksSection);
}
const bookmarksList = document.querySelector('.bookmarks');
const bookmarkItem = makeTag('li');
bookmarksList.insertAdjacentElement('beforeend', bookmarkItem);

const bookmarkLink = makeTag('a');
bookmarkLink.href = `#${header.id}`;
bookmarkLink.textContent = text;
bookmarkItem.insertAdjacentElement('beforeend', bookmarkLink);

insertTooltipAction(bookmarkLink, 'bookmark_remove', 'Remove bookmark',
(event) => {
removeBookmark(bookmarkItem);
event.stopPropagation();
event.preventDefault();
});
}

function removeBookmark(bookmarkItem) {
const parentElement = bookmarkItem.parentElement;
parentElement.removeChild(bookmarkItem);
}
</script>
99 changes: 99 additions & 0 deletions tests/adjacent-boilerplate/adjacent-boilerplate.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Foo</title>
<link href="http://example.com/foo" rel="canonical">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" rel="stylesheet">
<style>/* css-bookmark */

.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24
}
.heading {
position: relative;
}
.tooltip {
opacity: 0;
display: block;
position: absolute;
top: -90%;
left: 0;
pointer-events: auto;
border: shadow 2px gray;
padding: 0;
margin: 0;
background: rgb(200 200 200 / 50%);
color: black;
border-radius: 5px;
}
a.has-tooltip {
display: block;
}
.tooltip button {
padding: 0;
background: rgb(100 100 100 / 50%);
}
.has-tooltip:hover .tooltip {
opacity: 0.8;
}
.bookmarksSection {
margin-left: 2em;
}
</style>
<style>/* style-autolinks */

.css.css, .property.property, .descriptor.descriptor {
Expand Down Expand Up @@ -527,6 +564,68 @@ <h3 class="no-num no-ref heading settled" id="normative"><span class="content">N
<dt id="biblio-fetch">[FETCH]
<dd>Anne van Kesteren. <a href="https://fetch.spec.whatwg.org/"><cite>Fetch Standard</cite></a>. Living Standard. URL: <a href="https://fetch.spec.whatwg.org/">https://fetch.spec.whatwg.org/</a>
</dl>
<script>/* script-bookmark */

"use strict";
// Add tooltip behavior to all headers.
const headers = document.querySelectorAll('.heading');
// We need to get the createElement method now, or else it is not available!?
const createElement = document.createElement;
const makeTag = (tag) => {
return createElement.call(document, tag);
}

for (let h of headers) {
const textElement = h.querySelector('span.content');
insertTooltipAction(h, 'bookmark_add', 'Add bookmark', (event) => {
addBookmark(h, textElement.textContent); })
}

function insertTooltipAction(element, className, title, action) {
const tooltipDiv = makeTag('div');
tooltipDiv.className = 'tooltip';
const button = makeTag('button');
button.setHTML(
`<span class="material-symbols-outlined"
title="${title}">${className}</span>`);
button.addEventListener('click', action);
tooltipDiv.insertAdjacentElement('beforeend', button);
element.insertAdjacentElement('beforeend', tooltipDiv);
element.classList.add('has-tooltip');
}

function addBookmark(header, text) {
// console.info('add bookmark for', header);
let bookmarksSection = document.querySelector('.bookmarksSection');
if (bookmarksSection == null) {
const tocContents = document.querySelector('#toc #contents');
bookmarksSection = makeTag('div');
bookmarksSection.className = 'bookmarksSection';
bookmarksSection.setHTML('Bookmarks <ul class="bookmarks"></ul>');
tocContents.insertAdjacentElement('afterend', bookmarksSection);
}
const bookmarksList = document.querySelector('.bookmarks');
const bookmarkItem = makeTag('li');
bookmarksList.insertAdjacentElement('beforeend', bookmarkItem);

const bookmarkLink = makeTag('a');
bookmarkLink.href = `#${header.id}`;
bookmarkLink.textContent = text;
bookmarkItem.insertAdjacentElement('beforeend', bookmarkLink);

insertTooltipAction(bookmarkLink, 'bookmark_remove', 'Remove bookmark',
(event) => {
removeBookmark(bookmarkItem);
event.stopPropagation();
event.preventDefault();
});
}

function removeBookmark(bookmarkItem) {
const parentElement = bookmarkItem.parentElement;
parentElement.removeChild(bookmarkItem);
}
</script>
<script>/* script-dfn-panel */

document.body.addEventListener("click", function(e) {
Expand Down
Loading