Docs: add search box
Adds an in-site search using the Google Custom Search API.
Demo: https://perfetto-docs-demo.storage.googleapis.com/docs/index.html
Change-Id: I70d7e7d0b8a27a5bdbd653d114dfa9b83407f7d9
diff --git a/infra/perfetto.dev/src/assets/script.js b/infra/perfetto.dev/src/assets/script.js
index 91c57e5..aa368bc 100644
--- a/infra/perfetto.dev/src/assets/script.js
+++ b/infra/perfetto.dev/src/assets/script.js
@@ -253,6 +253,69 @@
document.body.appendChild(script);
}
+function setupSearch() {
+ const URL =
+ 'https://www.googleapis.com/customsearch/v1?key=AIzaSyBTD2XJkQkkuvDn76LSftsgWOkdBz9Gfwo&cx=007128963598137843411:8suis14kcmy&q='
+ const searchContainer = document.getElementById('search');
+ const searchBox = document.getElementById('search-box');
+ const searchRes = document.getElementById('search-res')
+ if (!searchBox || !searchRes) return;
+
+ document.body.addEventListener('keydown', (e) => {
+ if (e.key === '/' && e.target.tagName.toLowerCase() === 'body') {
+ searchBox.setSelectionRange(0, -1);
+ searchBox.focus();
+ e.preventDefault();
+ } else if (e.key === 'Escape' && searchContainer.contains(e.target)) {
+ searchBox.blur();
+
+ // Handle the case of clicking Tab and moving down to results.
+ e.target.blur();
+ }
+ });
+
+ let timerId = -1;
+ let lastSearchId = 0;
+
+ const doSearch = async () => {
+ timerId = -1;
+ searchRes.style.width = `${searchBox.offsetWidth}px`;
+
+ // `searchId` handles the case of two subsequent requests racing. This is to
+ // prevent older results, delivered in reverse order, to replace newer ones.
+ const searchId = ++lastSearchId;
+ const f = await fetch(URL + encodeURIComponent(searchBox.value));
+ const jsonRes = await f.json();
+ const results = jsonRes['items'];
+ searchRes.innerHTML = '';
+ if (results === undefined || searchId != lastSearchId) {
+ return;
+ }
+ for (const res of results) {
+ const link = document.createElement('a');
+ link.href = res.link;
+ const title = document.createElement('div');
+ title.className = 'sr-title';
+ title.innerText = res.title.replace(' - Perfetto Tracing Docs', '');
+ link.appendChild(title);
+
+ const snippet = document.createElement('div');
+ snippet.className = 'sr-snippet';
+ snippet.innerText = res.snippet;
+ link.appendChild(snippet);
+
+ const div = document.createElement('div');
+ div.appendChild(link);
+ searchRes.appendChild(div);
+ }
+ };
+
+ searchBox.addEventListener('keyup', () => {
+ if (timerId >= 0) return;
+ timerId = setTimeout(doSearch, 200);
+ });
+}
+
window.addEventListener('DOMContentLoaded', () => {
updateNav();
updateTOC();
@@ -275,6 +338,7 @@
}
updateTOC();
+ setupSearch();
// Enable animations only after the load event. This is to prevent glitches
// when switching pages.
diff --git a/infra/perfetto.dev/src/assets/style.scss b/infra/perfetto.dev/src/assets/style.scss
index 7f23862..63d6fc1 100644
--- a/infra/perfetto.dev/src/assets/style.scss
+++ b/infra/perfetto.dev/src/assets/style.scss
@@ -147,8 +147,105 @@
}
}
+
+#search {
+ position: relative;
+ flex-grow: 0;
+ transition: flex-grow cubic-bezier(1, 0.01, 1, 1) var(--anim-time), background-color ease var(--anim-time);
+ padding: 0;
+ &::before {
+ visibility: hidden;
+ user-select: none;
+ content: '';
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: var(--site-header-height);
+ bottom: 0;
+ z-index: -100;
+ background-color: rgba(255, 255, 255, 0.8);
+ backdrop-filter: blur(3px);
+ opacity: 0;
+ transition: opacity ease var(--anim-time), visibility 0s;
+
+ }
+ &:focus-within {
+ flex-grow: 1000;
+ &::before {
+ display: block;
+ opacity: 1;
+ visibility: visible;
+ }
+ #search-res {
+ display: block;
+ }
+
+ }
+
+ @media #{$mobile} {
+ display: none;
+ }
+
+ #search-box {
+ width: 100%;
+ height: 32px;
+ font-size: 1rem;;
+ color: #333;
+ background-color: rgba(255, 255, 255, 0.9);
+ border: 1px solid #eee;
+ border-radius: 2px;
+ background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z"/></svg>');
+ background-repeat: no-repeat;
+ background-size: contain;
+ padding-left: 40px;
+ outline: none;
+ &:hover, &:focus {
+ background-color: rgba(255, 255, 255, 0.95);
+ }
+ }
+
+ #search-res {
+ display: none;
+ background-color: rgba(255, 255, 255, 1.0);
+ border: 1px solid #eee;
+ box-shadow: #aaa 0px 1px 5px;
+ color: #333;
+ line-height: initial;
+ margin-top: -4px;
+ overflow-x: auto;
+ position: fixed;
+ top: var(--site-header-height);
+ max-height: calc(100vh - var(--site-header-height));
+ z-index: 10;
+ >div {
+ padding: 10px;
+ margin: 0;
+ &:hover {
+ background-color: #f0f0f0;
+ }
+ }
+ .sr-title {
+ color: #333;
+ font-weight: bold;
+ }
+ .sr-snippet {
+ color: #444;
+ font-size: 0.9rem;
+ }
+
+ a { text-decoration: none; }
+ a:hover { color: initial };
+
+ &:empty {
+ visibility: hidden;
+ }
+ }
+
+}
+
+
// -----------------------------------------------------------------------------
-// Site header
+// Site footer
// -----------------------------------------------------------------------------
// Footer in the index page.
diff --git a/infra/perfetto.dev/src/template_header.html b/infra/perfetto.dev/src/template_header.html
index 45960d9..46fc39b 100644
--- a/infra/perfetto.dev/src/template_header.html
+++ b/infra/perfetto.dev/src/template_header.html
@@ -29,6 +29,13 @@
</div>
<a href="#toggle" class="menu"><i class="material-icons-round">menu</i></a>
+ <% if (fileName.startsWith('/docs/')) { %>
+ <div id="search">
+ <input id="search-box" type="text" placeholder="Search" autocomplete="off">
+ <div id="search-res"></div>
+ </div>
+ <% } %>
+
<a href="/docs/">Docs</a>
<a href="/docs/contributing/getting-started#community">Community</a>
<a href="https://ui.perfetto.dev/">Trace Viewer</a>