Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Link to search modal #20

Closed
wants to merge 5 commits into from
Closed
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
10 changes: 10 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ to support all browsers.

The CSS is also autoprefixed to extend the support to most of the browsers.

Link To The Search UI
~~~~~~~~~~~~~~~~~~~~~

If you want to share your search, you can do so by passing a URL param -- ``rtd_search``.
The search UI will opens on page loads and search for the query specified in the URL.

Example::

https://readthedocs-sphinx-search.readthedocs.io/en/latest?rtd_search=get involved


.. toctree::
:maxdepth: 1
Expand Down
19 changes: 19 additions & 0 deletions sphinx_search/_static/css/rtd_sphinx_search.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
height: 30px;
font-size: 19px;
outline: none;
padding-right: 25px;

/* Other */
background-color: #fcfcfc;
Expand Down Expand Up @@ -215,6 +216,24 @@
border-bottom: 1px solid black;
}

.search__link__button {
margin-left: -25px;
background-color: #fcfcfc;
border: none;
color: black;
opacity: 0.125;
outline: none;
}

.search__link__button:hover {
cursor: pointer;
opacity: 1;
}

.search__link__button:focus {
outline: none;
}

@keyframes fade-in {
from {
opacity: 0;
Expand Down
2 changes: 1 addition & 1 deletion sphinx_search/_static/css/rtd_sphinx_search.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

147 changes: 134 additions & 13 deletions sphinx_search/_static/js/rtd_sphinx_search.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,18 +345,27 @@ const generateAndReturnInitialHtml = () => {
placeholder: "Search ..."
});

let linkToSearchButton = createDomNode("button", {
class: "search__link__button",
title: "Copy link to search"
});
linkToSearchButton.innerHTML =
'<?xml version="1.0" encoding="UTF-8"?><svg width="15px" height="15px" enable-background="new 0 0 465.951 465.951" version="1.1" viewBox="0 0 465.951 465.951" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m441.96 284.36l-59.389-59.383c-15.984-15.985-35.396-23.982-58.238-23.982-23.223 0-43.013 8.375-59.385 25.125l-25.125-25.125c16.751-16.368 25.125-36.256 25.125-59.671 0-22.841-7.898-42.157-23.698-57.958l-58.815-59.097c-15.798-16.178-35.212-24.27-58.242-24.27-22.841 0-42.16 7.902-57.958 23.7l-41.97 41.683c-16.179 15.802-24.267 35.118-24.267 57.957 0 22.841 7.996 42.258 23.982 58.245l59.385 59.383c15.99 15.988 35.404 23.982 58.245 23.982 23.219 0 43.015-8.374 59.383-25.126l25.125 25.126c-16.75 16.371-25.125 36.258-25.125 59.672 0 22.843 7.898 42.154 23.697 57.958l58.82 59.094c15.801 16.177 35.208 24.27 58.238 24.27 22.844 0 42.154-7.897 57.958-23.698l41.973-41.682c16.177-15.804 24.271-35.118 24.271-57.958-5e-3 -22.838-7.999-42.25-23.99-58.245zm-240.96-122.19c-0.571-0.571-2.334-2.378-5.28-5.424-2.948-3.046-4.995-5.092-6.136-6.14-1.143-1.047-2.952-2.474-5.426-4.286-2.478-1.809-4.902-3.044-7.28-3.711-2.38-0.666-4.998-0.998-7.854-0.998-7.611 0-14.084 2.666-19.414 7.993s-7.992 11.799-7.992 19.414c0 2.853 0.332 5.471 0.998 7.851 0.666 2.382 1.903 4.808 3.711 7.281 1.809 2.474 3.237 4.283 4.283 5.426 1.044 1.141 3.09 3.188 6.136 6.139 3.046 2.95 4.853 4.709 5.424 5.281-5.711 5.898-12.563 8.848-20.555 8.848-7.804 0-14.277-2.568-19.414-7.705l-59.39-59.386c-5.327-5.33-7.992-11.802-7.992-19.417 0-7.421 2.662-13.796 7.992-19.126l41.971-41.687c5.523-5.14 11.991-7.705 19.417-7.705 7.611 0 14.083 2.663 19.414 7.993l58.813 59.097c5.33 5.33 7.992 11.801 7.992 19.414 1e-3 7.991-3.139 14.94-9.418 20.848zm202.15 199.55l-41.973 41.686c-5.332 4.945-11.8 7.423-19.418 7.423-7.809 0-14.27-2.566-19.41-7.707l-58.813-59.101c-5.331-5.332-7.99-11.8-7.99-19.41 0-7.994 3.138-14.941 9.421-20.841 0.575 0.567 2.334 2.381 5.284 5.42 2.95 3.046 4.996 5.093 6.14 6.14 1.143 1.051 2.949 2.478 5.42 4.288 2.478 1.811 4.9 3.049 7.282 3.713 2.382 0.667 4.997 0.999 7.851 0.999 7.618 0 14.086-2.665 19.418-7.994 5.324-5.328 7.994-11.8 7.994-19.41 0-2.854-0.339-5.472-1-7.851-0.67-2.382-1.902-4.809-3.72-7.282-1.811-2.471-3.23-4.284-4.281-5.428-1.047-1.136-3.094-3.183-6.139-6.14-3.046-2.949-4.853-4.709-5.428-5.276 5.715-6.092 12.566-9.138 20.554-9.138 7.617 0 14.085 2.663 19.41 7.994l59.388 59.382c5.332 5.332 7.995 11.807 7.995 19.417 0 7.416-2.663 13.799-7.985 19.116z"/></svg>';

// for material ui design input field
let horizontal_bar = createDomNode("span", { class: "bar" });

search_outer.appendChild(search_outer_input);
search_outer.appendChild(linkToSearchButton);
search_outer.appendChild(horizontal_bar);
search_outer_wrapper.appendChild(search_outer);

return {
search_outer_wrapper,
search_outer_input,
search_outer,
cross_icon
cross_icon,
linkToSearchButton
};
};

Expand All @@ -367,6 +376,9 @@ const showSearchModal = () => {
// removes previous results (if there are any).
removeResults();

// remove previous search query (if any)
SEARCH_QUERY = "";

// removes the focus from the initial input field
// which as already present in the docs.
let search_bar = getInputField();
Expand Down Expand Up @@ -406,19 +418,74 @@ const removeSearchModal = () => {
search_outer_wrapper.classList.remove("display-block");
};

/**
* Returns the search URL
*
* @param {Object} searchParams params for search
*/
const getSearchUrl = searchParams => {
const api_host = getProjectInfo().api_host;
const url =
api_host + "/api/v2/docsearch/?" + convertObjToUrlParams(searchParams);
return url;
};

/**
* Returns project info
*/
const getProjectInfo = () => {
return {
project: READTHEDOCS_DATA.project,
version: READTHEDOCS_DATA.version,
language: READTHEDOCS_DATA.language || "en",
api_host: READTHEDOCS_DATA.api_host
};
};

/**
* Returns the URL parameters in the form of Object
*
* @return {Object} url params
*/
const getUrlVars = () => {
let vars = {};
let params = window.location.href.replace(
/[?&]+([^=&]+)=([^&]*)/gi,
(m, key, value) => {
vars[key] = value;
}
);
return vars;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really not in jQuery already? I feel like we're reinventing a lot of wheels.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.
I checked on the internet. There is no direct utility from the jquery or javascript to get the url parameters.

};

/**
* Copy the text passes as argument.
*
* @param {String} str string to be copied to clipboard
*/
const copyToClipBoard = str => {
let textarea = createDomNode("textarea", {
readonly: "",
style: "position: absolute; left: -9999px;"
});
textarea.value = str;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, is this really not a standard thing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.
here also -- no utility of jquery or javascript is there which dirctly copies the text.


window.addEventListener("DOMContentLoaded", evt => {
// only add event listeners if READTHEDOCS_DATA global
// variable is found.
if (READTHEDOCS_DATA !== undefined) {
const project = READTHEDOCS_DATA.project;
const version = READTHEDOCS_DATA.version;
const language = READTHEDOCS_DATA.language || "en";
const api_host = READTHEDOCS_DATA.api_host;
const project_info = getProjectInfo();

const initialHtml = generateAndReturnInitialHtml();
let search_outer_wrapper = initialHtml.search_outer_wrapper;
let search_outer_input = initialHtml.search_outer_input;
let cross_icon = initialHtml.cross_icon;
let linkToSearchButton = initialHtml.linkToSearchButton;

document.body.appendChild(search_outer_wrapper);

Expand All @@ -438,22 +505,22 @@ window.addEventListener("DOMContentLoaded", evt => {
SEARCH_QUERY = e.target.value;
let search_params = {
q: encodeURIComponent(SEARCH_QUERY),
project: project,
version: version,
language: language
project: project_info.project,
version: project_info.version,
language: project_info.language
};

const search_url =
api_host +
"/api/v2/docsearch/?" +
convertObjToUrlParams(search_params);
const search_url = getSearchUrl(search_params);

if (typeof SEARCH_QUERY === "string" && SEARCH_QUERY.length > 0) {
if (current_request !== null) {
// cancel previous ajax request.
current_request.cancel();
}
current_request = fetchAndGenerateResults(search_url, project);
current_request = fetchAndGenerateResults(
search_url,
project_info.project
);
current_request();
} else {
removeResults();
Expand Down Expand Up @@ -521,12 +588,66 @@ window.addEventListener("DOMContentLoaded", evt => {
removeSearchModal();
});

// copy the url, to clipboard, to the search
// UI when the button is clicked.
linkToSearchButton.addEventListener("click", e => {
e.preventDefault();

const host = window.location.origin;
const path = window.location.pathname;
let urlToBeCopied = host + path;

if (
SEARCH_QUERY !== undefined &&
SEARCH_QUERY !== null &&
SEARCH_QUERY !== ""
) {
let urlParam = convertObjToUrlParams({
rtd_search: encodeURIComponent(SEARCH_QUERY)
});
urlToBeCopied += "?" + urlParam;
}

copyToClipBoard(urlToBeCopied);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow what this is doing. We should just update the browser's URL when the user types with the proper data, we don't need a custom way to copy a link.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generating the url to the search ui and then copying it to the clipboard.

});

// close the search modal if the user pressed
// Escape button
document.addEventListener("keydown", e => {
if (e.keyCode === 27) {
removeSearchModal();
}
});

// if "rtd_search" is present in URL parameters,
// then open the search modal and show the results
// for the value of "rtd_search"
const url_params = getUrlVars();
if (
url_params["rtd_search"] !== undefined &&
typeof url_params["rtd_search"] === "string" &&
url_params["rtd_search"] !== ""
) {
let query = decodeURIComponent(url_params["rtd_search"]);
let search_outer_input = document.querySelector(
".search__outer__input"
);
const projectInfo = getProjectInfo();
const searchParams = {
q: encodeURIComponent(query),
project: projectInfo.project,
version: projectInfo.version,
language: projectInfo.language
};
const search_url = getSearchUrl(searchParams);
showSearchModal();
SEARCH_QUERY = query;
search_outer_input.value = SEARCH_QUERY;
let ajax_call = fetchAndGenerateResults(
search_url,
projectInfo.project
);
ajax_call();
}
}
});
Loading