mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-18 20:59:42 +02:00
Add search to sidebar (#215)
* remove package json * twig file modified * search bar style added * the background content added * add the switching b/w the shortcut logo * shortcut for search added * add the arrowup and arrowdown short cut * sidebar search added * keyup and keydown replace with input * the sidebar search selected added * unusal things * the enter evenlister added with search refactring * comments added * the scroll added if element is not visble * metakey added * event listner using shortcut added * the integration for input box completed * nodemon config updated * replace the shortcuts with event listener * bugfix: up height of header added * feat:integrate sidebar toggle with search shortcut * syntax improved * event listener updated * border adjusted * search adjusted * sidebar search navigation adjusted * new search module added * new module integrated * boxshadow added as border * sidebar search class added * sidebar search=>filter * comments added * filter for section added * the expand feature added during navigation * remove the space * header height variable added * shortcut logic updated * enum for direction added * common search function added * expand every match * updated styles * updated styles * margin remove in mobile view with bold removed * clean css added Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
parent
5a7f1c843b
commit
698c09c489
7 changed files with 463 additions and 13 deletions
|
@ -8,5 +8,5 @@
|
||||||
"watch": [
|
"watch": [
|
||||||
"**/*"
|
"**/*"
|
||||||
],
|
],
|
||||||
"ext": "js,twig"
|
"ext": "ts,js,twig"
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,7 @@ class Pages {
|
||||||
await alias.destroy();
|
await alias.destroy();
|
||||||
}
|
}
|
||||||
const removedPage = page.destroy();
|
const removedPage = page.destroy();
|
||||||
|
|
||||||
await PagesFlatArray.regenerate();
|
await PagesFlatArray.regenerate();
|
||||||
|
|
||||||
return removedPage;
|
return removedPage;
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="docs-sidebar__content docs-sidebar__content--invisible">
|
<aside class="docs-sidebar__content docs-sidebar__content--invisible">
|
||||||
|
<span class="docs-sidebar__search-wrapper">
|
||||||
|
<input class="docs-sidebar__search" type="text" placeholder="Search" />
|
||||||
|
</span>
|
||||||
{% for firstLevelPage in menu %}
|
{% for firstLevelPage in menu %}
|
||||||
<section class="docs-sidebar__section" data-id="{{firstLevelPage._id}}">
|
<section class="docs-sidebar__section" data-id="{{firstLevelPage._id}}">
|
||||||
<a class="docs-sidebar__section-title-wrapper"
|
<a class="docs-sidebar__section-title-wrapper"
|
||||||
|
|
364
src/frontend/js/classes/sidebar-filter.js
Normal file
364
src/frontend/js/classes/sidebar-filter.js
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
/**
|
||||||
|
* HEIGHT of the header in px
|
||||||
|
*/
|
||||||
|
const HEADER_HEIGHT = parseInt(window.getComputedStyle(
|
||||||
|
document.documentElement).getPropertyValue('--layout-height-header'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum for the direction of the navigation during the filtering.
|
||||||
|
*/
|
||||||
|
const Direction = {
|
||||||
|
Next: 1,
|
||||||
|
Previous: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sidebar Search module.
|
||||||
|
*/
|
||||||
|
export default class SidebarFilter {
|
||||||
|
/**
|
||||||
|
* CSS classes
|
||||||
|
*
|
||||||
|
* @returns {Record<string, string>}
|
||||||
|
*/
|
||||||
|
static get CSS() {
|
||||||
|
return {
|
||||||
|
sectionHidden: 'docs-sidebar__section--hidden',
|
||||||
|
sectionTitle: 'docs-sidebar__section-title',
|
||||||
|
sectionTitleSelected: 'docs-sidebar__section-title--selected',
|
||||||
|
sectionTitleActive: 'docs-sidebar__section-title--active',
|
||||||
|
sectionList: 'docs-sidebar__section-list',
|
||||||
|
sectionListItem: 'docs-sidebar__section-list-item',
|
||||||
|
sectionListItemWrapperHidden: 'docs-sidebar__section-list-item-wrapper--hidden',
|
||||||
|
sectionListItemSlelected: 'docs-sidebar__section-list-item--selected',
|
||||||
|
sidebarSearchWrapper: 'docs-sidebar__search-wrapper',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates base properties
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
* Stores refs to HTML elements needed for sidebar filter to work.
|
||||||
|
*/
|
||||||
|
this.sidebar = null;
|
||||||
|
this.sections = [];
|
||||||
|
this.sidebarContent = null;
|
||||||
|
this.search = null;
|
||||||
|
this.searchResults = [];
|
||||||
|
this.selectedSearchResultIndex = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize sidebar filter.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement[]} sections - Array of sections.
|
||||||
|
* @param {HTMLElement} sidebarContent - Sidebar content.
|
||||||
|
* @param {HTMLElement} search - Search input.
|
||||||
|
* @param {Function} setSectionCollapsed - Function to set section collapsed.
|
||||||
|
*/
|
||||||
|
init(sections, sidebarContent, search, setSectionCollapsed) {
|
||||||
|
// Store refs to HTML elements.
|
||||||
|
this.sections = sections;
|
||||||
|
this.sidebarContent = sidebarContent;
|
||||||
|
this.search = search;
|
||||||
|
this.setSectionCollapsed = setSectionCollapsed;
|
||||||
|
let shortcutText = 'Ctrl P';
|
||||||
|
|
||||||
|
// Search initialize with platform specific shortcut.
|
||||||
|
if (window.navigator.userAgent.indexOf('Mac') !== -1) {
|
||||||
|
shortcutText = '⌘ P';
|
||||||
|
}
|
||||||
|
this.search.parentElement.setAttribute('data-shortcut', shortcutText);
|
||||||
|
|
||||||
|
// Initialize search input.
|
||||||
|
this.search.value = '';
|
||||||
|
|
||||||
|
// Add event listener for search input.
|
||||||
|
this.search.addEventListener('input', e => {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
this.filter(e.target.value);
|
||||||
|
});
|
||||||
|
// Add event listener for keyboard events.
|
||||||
|
this.search.addEventListener('keydown', e => this.handleKeyboardEvent(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle keyboard events while search input is focused.
|
||||||
|
*
|
||||||
|
* @param {Event} e - Event Object.
|
||||||
|
*/
|
||||||
|
handleKeyboardEvent(e) {
|
||||||
|
// Return if search is not focused.
|
||||||
|
if (this.search !== document.activeElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle enter key when item is focused.
|
||||||
|
if (e.code === 'Enter' && this.selectedSearchResultIndex !== null) {
|
||||||
|
// navigate to focused item.
|
||||||
|
this.searchResults[this.selectedSearchResultIndex].element.click();
|
||||||
|
// prevent default action.
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle up and down navigation.
|
||||||
|
if (e.code === 'ArrowUp' || e.code === 'ArrowDown') {
|
||||||
|
// check for search results.
|
||||||
|
if (this.searchResults.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current focused item.
|
||||||
|
const prevSelectedSearchResultIndex = this.selectedSearchResultIndex;
|
||||||
|
|
||||||
|
// get next item to be focus.
|
||||||
|
if (e.code === 'ArrowUp') {
|
||||||
|
this.selectedSearchResultIndex = this.getNextIndex(
|
||||||
|
Direction.Previous,
|
||||||
|
this.selectedSearchResultIndex,
|
||||||
|
this.searchResults.length - 1);
|
||||||
|
} else if (e.code === 'ArrowDown') {
|
||||||
|
this.selectedSearchResultIndex = this.getNextIndex(
|
||||||
|
Direction.Next,
|
||||||
|
this.selectedSearchResultIndex,
|
||||||
|
this.searchResults.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// blur previous focused item.
|
||||||
|
this.blurTitleOrItem(prevSelectedSearchResultIndex);
|
||||||
|
// focus next item.
|
||||||
|
this.focusTitleOrItem(this.selectedSearchResultIndex);
|
||||||
|
|
||||||
|
// prevent default action.
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get index of next item to be focused.
|
||||||
|
*
|
||||||
|
* @param {number} direction - direction for navigation.
|
||||||
|
* @param {number} titleOrItemIndex - Current title or item index.
|
||||||
|
* @param {number} maxNumberOfTitlesOrItems - Max number of titles or items.
|
||||||
|
* @returns {number} - Next section or item index.
|
||||||
|
*/
|
||||||
|
getNextIndex(direction, titleOrItemIndex, maxNumberOfTitlesOrItems) {
|
||||||
|
let nextTitleOrItemIndex = titleOrItemIndex;
|
||||||
|
|
||||||
|
if (direction === Direction.Previous) {
|
||||||
|
// if no item is focused, focus last item.
|
||||||
|
if (titleOrItemIndex === null) {
|
||||||
|
return maxNumberOfTitlesOrItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// focus previous item.
|
||||||
|
nextTitleOrItemIndex--;
|
||||||
|
|
||||||
|
// circular navigation.
|
||||||
|
if (nextTitleOrItemIndex < 0) {
|
||||||
|
nextTitleOrItemIndex = maxNumberOfTitlesOrItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextTitleOrItemIndex;
|
||||||
|
} else if (direction === Direction.Next) {
|
||||||
|
// if no item is focused, focus first item.
|
||||||
|
if (titleOrItemIndex === null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// focus next item.
|
||||||
|
nextTitleOrItemIndex++;
|
||||||
|
|
||||||
|
// circular navigation.
|
||||||
|
if (nextTitleOrItemIndex > maxNumberOfTitlesOrItems) {
|
||||||
|
nextTitleOrItemIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextTitleOrItemIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focus title or item at given index.
|
||||||
|
*
|
||||||
|
* @param {number} titleOrItemIndex - Title or item index.
|
||||||
|
*/
|
||||||
|
focusTitleOrItem(titleOrItemIndex) {
|
||||||
|
// check for valid index.
|
||||||
|
if (titleOrItemIndex === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { element, type } = this.searchResults[titleOrItemIndex];
|
||||||
|
|
||||||
|
if (!element || !type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// focus title or item.
|
||||||
|
if (type === 'title') {
|
||||||
|
element.classList.add(SidebarFilter.CSS.sectionTitleSelected);
|
||||||
|
} else if (type === 'item') {
|
||||||
|
element.classList.add(SidebarFilter.CSS.sectionListItemSlelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll to focused title or item.
|
||||||
|
this.scrollToTitleOrItem(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blur title or item at given index.
|
||||||
|
*
|
||||||
|
* @param {number} titleOrItemIndex - Title or item index.
|
||||||
|
*/
|
||||||
|
blurTitleOrItem(titleOrItemIndex) {
|
||||||
|
// check for valid index.
|
||||||
|
if (titleOrItemIndex === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { element, type } = this.searchResults[titleOrItemIndex];
|
||||||
|
|
||||||
|
if (!element || !type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blur title or item.
|
||||||
|
if (type === 'title') {
|
||||||
|
element.classList.remove(SidebarFilter.CSS.sectionTitleSelected);
|
||||||
|
} else if (type === 'item') {
|
||||||
|
element.classList.remove(SidebarFilter.CSS.sectionListItemSlelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to title or item.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} titleOrItem - Title or item element.
|
||||||
|
*/
|
||||||
|
scrollToTitleOrItem(titleOrItem) {
|
||||||
|
// check if it's visible.
|
||||||
|
const rect = titleOrItem.getBoundingClientRect();
|
||||||
|
let elemTop = rect.top;
|
||||||
|
let elemBottom = rect.bottom;
|
||||||
|
const halfOfViewport = window.innerHeight / 2;
|
||||||
|
const scrollTop = this.sidebarContent.scrollTop;
|
||||||
|
|
||||||
|
// scroll top if item is not visible.
|
||||||
|
if (elemTop < HEADER_HEIGHT) {
|
||||||
|
// scroll half viewport up.
|
||||||
|
const nextTop = scrollTop - halfOfViewport;
|
||||||
|
|
||||||
|
// check if element visible after scroll.
|
||||||
|
elemTop = (elemTop + nextTop) < HEADER_HEIGHT ? elemTop : nextTop;
|
||||||
|
this.sidebarContent.scroll({
|
||||||
|
top: elemTop,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
} else if (elemBottom > window.innerHeight) {
|
||||||
|
// scroll bottom if item is not visible.
|
||||||
|
// scroll half viewport down.
|
||||||
|
const nextDown = halfOfViewport + scrollTop;
|
||||||
|
|
||||||
|
// check if element visible after scroll.
|
||||||
|
elemBottom = (elemBottom - nextDown) > window.innerHeight ? elemBottom : nextDown;
|
||||||
|
this.sidebarContent.scroll({
|
||||||
|
top: elemBottom,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if content contains search text.
|
||||||
|
*
|
||||||
|
* @param {string} content - content to be searched.
|
||||||
|
* @param {string} searchValue - Search value.
|
||||||
|
* @returns {boolean} - true if content contains search value.
|
||||||
|
*/
|
||||||
|
isValueMatched(content, searchValue) {
|
||||||
|
return content.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter sidebar items.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} section - Section element.
|
||||||
|
* @param {string} searchValue - Search value.
|
||||||
|
*/
|
||||||
|
filterSection(section, searchValue) {
|
||||||
|
// match with section title.
|
||||||
|
const sectionTitle = section.querySelector('.' + SidebarFilter.CSS.sectionTitle);
|
||||||
|
const sectionList = section.querySelector('.' + SidebarFilter.CSS.sectionList);
|
||||||
|
|
||||||
|
// check if section title matches.
|
||||||
|
const isTitleMatch = this.isValueMatched(sectionTitle.textContent, searchValue);
|
||||||
|
|
||||||
|
const matchResults = [];
|
||||||
|
// match with section items.
|
||||||
|
let isSingleItemMatch = false;
|
||||||
|
|
||||||
|
if (sectionList) {
|
||||||
|
const sectionListItems = sectionList.querySelectorAll('.' + SidebarFilter.CSS.sectionListItem);
|
||||||
|
|
||||||
|
sectionListItems.forEach(item => {
|
||||||
|
if (this.isValueMatched(item.textContent, searchValue)) {
|
||||||
|
// remove hiden class from item.
|
||||||
|
item.parentElement.classList.remove(SidebarFilter.CSS.sectionListItemWrapperHidden);
|
||||||
|
// add item to search results.
|
||||||
|
matchResults.push({
|
||||||
|
element: item,
|
||||||
|
type: 'item',
|
||||||
|
});
|
||||||
|
isSingleItemMatch = true;
|
||||||
|
} else {
|
||||||
|
// hide item if it is not a match.
|
||||||
|
item.parentElement.classList.add(SidebarFilter.CSS.sectionListItemWrapperHidden);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!isTitleMatch && !isSingleItemMatch) {
|
||||||
|
// hide section if it's items are not a match.
|
||||||
|
section.classList.add(SidebarFilter.CSS.sectionHidden);
|
||||||
|
} else {
|
||||||
|
const parentSection = sectionTitle.closest('section');
|
||||||
|
|
||||||
|
// if item is in collapsed section, expand it.
|
||||||
|
if (!parentSection.classList.contains(SidebarFilter.CSS.sectionTitleActive)) {
|
||||||
|
this.setSectionCollapsed(parentSection, false);
|
||||||
|
}
|
||||||
|
// show section if it's items are a match.
|
||||||
|
section.classList.remove(SidebarFilter.CSS.sectionHidden);
|
||||||
|
// add section title to search results.
|
||||||
|
this.searchResults.push({
|
||||||
|
element: sectionTitle,
|
||||||
|
type: 'title',
|
||||||
|
}, ...matchResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter sidebar sections.
|
||||||
|
*
|
||||||
|
* @param {string} searchValue - Search value.
|
||||||
|
*/
|
||||||
|
filter(searchValue) {
|
||||||
|
// remove selection from previous search results.
|
||||||
|
this.blurTitleOrItem(this.selectedSearchResultIndex);
|
||||||
|
// empty selected index.
|
||||||
|
this.selectedSearchResultIndex = null;
|
||||||
|
// empty search results.
|
||||||
|
this.searchResults = [];
|
||||||
|
// match search value with sidebar sections.
|
||||||
|
this.sections.forEach(section => {
|
||||||
|
this.filterSection(section, searchValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { Storage } from '../utils/storage';
|
import { Storage } from '../utils/storage';
|
||||||
import Shortcut from '@codexteam/shortcuts';
|
import Shortcut from '@codexteam/shortcuts';
|
||||||
|
import SidebarFilter from '../classes/sidebar-filter';
|
||||||
/**
|
/**
|
||||||
* Local storage key
|
* Local storage key
|
||||||
*/
|
*/
|
||||||
|
@ -38,6 +38,7 @@ export default class Sidebar {
|
||||||
sidebarContent: 'docs-sidebar__content',
|
sidebarContent: 'docs-sidebar__content',
|
||||||
sidebarContentVisible: 'docs-sidebar__content--visible',
|
sidebarContentVisible: 'docs-sidebar__content--visible',
|
||||||
sidebarContentInvisible: 'docs-sidebar__content--invisible',
|
sidebarContentInvisible: 'docs-sidebar__content--invisible',
|
||||||
|
sidebarSearch: 'docs-sidebar__search',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ export default class Sidebar {
|
||||||
sidebarContent: null,
|
sidebarContent: null,
|
||||||
toggler: null,
|
toggler: null,
|
||||||
slider: null,
|
slider: null,
|
||||||
|
search: null,
|
||||||
};
|
};
|
||||||
this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY);
|
this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY);
|
||||||
const storedState = this.sidebarStorage.get();
|
const storedState = this.sidebarStorage.get();
|
||||||
|
@ -67,6 +69,8 @@ export default class Sidebar {
|
||||||
|
|
||||||
// Sidebar visibility
|
// Sidebar visibility
|
||||||
this.isVisible = storedVisibility !== 'false';
|
this.isVisible = storedVisibility !== 'false';
|
||||||
|
// Sidebar filter module
|
||||||
|
this.filter = new SidebarFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +88,11 @@ export default class Sidebar {
|
||||||
this.nodes.toggler.addEventListener('click', () => this.toggleSidebar());
|
this.nodes.toggler.addEventListener('click', () => this.toggleSidebar());
|
||||||
this.nodes.slider = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSlider);
|
this.nodes.slider = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSlider);
|
||||||
this.nodes.slider.addEventListener('click', () => this.handleSliderClick());
|
this.nodes.slider.addEventListener('click', () => this.handleSliderClick());
|
||||||
|
|
||||||
|
this.nodes.search = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSearch);
|
||||||
|
this.filter.init(this.nodes.sections, this.nodes.sidebarContent,
|
||||||
|
this.nodes.search, this.setSectionCollapsed);
|
||||||
|
|
||||||
this.ready();
|
this.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +220,25 @@ export default class Sidebar {
|
||||||
on: document.body,
|
on: document.body,
|
||||||
callback: () => this.handleSliderClick(),
|
callback: () => this.handleSliderClick(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add event listener to focus search input on Ctrl+P or ⌘+P is pressed.
|
||||||
|
// eslint-disable-next-line no-new
|
||||||
|
new Shortcut({
|
||||||
|
name: 'CMD+P',
|
||||||
|
on: document.body,
|
||||||
|
callback: (e) => {
|
||||||
|
// If sidebar is not visible.
|
||||||
|
if (!this.isVisible) {
|
||||||
|
// make sidebar visible.
|
||||||
|
this.handleSliderClick();
|
||||||
|
}
|
||||||
|
// focus search input.
|
||||||
|
this.nodes.search.focus();
|
||||||
|
// Stop propagation of event.
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -77,10 +77,51 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__search {
|
||||||
|
@apply --input;
|
||||||
|
|
||||||
|
appearance: none;
|
||||||
|
background: url("../svg/search.svg") left 10px center no-repeat;
|
||||||
|
background-color: var(--color-input-primary);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 7.5px 35px;
|
||||||
|
padding-right: 42px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&-wrapper::after {
|
||||||
|
color: var(--color-button-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
content: attr(data-shortcut);
|
||||||
|
margin-left: -45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--mobile) {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&-wrapper::after {
|
||||||
|
content: "";
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__search::placeholder {
|
||||||
|
color: var(--color-button-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
&__section {
|
&__section {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
&--hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&--animated {
|
&--animated {
|
||||||
.docs-sidebar__section-list {
|
.docs-sidebar__section-list {
|
||||||
|
@ -107,8 +148,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section:not(:first-child) {
|
@media (--mobile) {
|
||||||
margin-top: 19px;
|
&__section:nth-child(2) {
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section-title {
|
&__section-title {
|
||||||
|
@ -141,6 +184,12 @@
|
||||||
transition-duration: 0.1s;
|
transition-duration: 0.1s;
|
||||||
|
|
||||||
@apply --squircle;
|
@apply --squircle;
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
border-radius: 8px;
|
||||||
|
/* border using box-shadow which doesn't increase the height */
|
||||||
|
box-shadow: 0 0 0 2px rgba(147, 166, 233, 0.5) inset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__section-title > span,
|
&__section-title > span,
|
||||||
|
@ -157,10 +206,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&__section-list-item-wrapper {
|
&__section-list-item-wrapper {
|
||||||
padding: 1px 0;
|
padding: 1px 0;
|
||||||
display: block;
|
display: block;
|
||||||
|
&--hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li:last-child {
|
li:last-child {
|
||||||
|
@ -169,7 +220,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&__section-title:not(&__section-title--active),
|
&__section-title:not(&__section-title--active),
|
||||||
&__section-list-item:not(&__section-list-item--active) {
|
&__section-list-item:not(&__section-list-item--active) {
|
||||||
@media (--can-hover) {
|
@media (--can-hover) {
|
||||||
|
@ -181,7 +231,7 @@
|
||||||
|
|
||||||
&__section-title--active,
|
&__section-title--active,
|
||||||
&__section-list-item--active {
|
&__section-list-item--active {
|
||||||
background: linear-gradient(270deg, #129BFF 0%, #8A53FF 100%);
|
background: linear-gradient(270deg, #129bff 0%, #8a53ff 100%);
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
@media (--can-hover) {
|
@media (--can-hover) {
|
||||||
|
@ -223,7 +273,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&__toggler {
|
&__toggler {
|
||||||
color: var(--color-text-second);
|
color: var(--color-text-second);
|
||||||
padding: 20px 15px;
|
padding: 20px 15px;
|
||||||
|
@ -241,7 +290,9 @@
|
||||||
&__slider {
|
&__slider {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
transform: translateX(calc(var(--layout-sidebar-width) + var(--hide-sidebar-toggler-offset)));
|
transform: translateX(
|
||||||
|
calc(var(--layout-sidebar-width) + var(--hide-sidebar-toggler-offset))
|
||||||
|
);
|
||||||
bottom: var(--hide-sidebar-toggler-offset);
|
bottom: var(--hide-sidebar-toggler-offset);
|
||||||
width: var(--hide-sidebar-toggler-size);
|
width: var(--hide-sidebar-toggler-size);
|
||||||
height: var(--hide-sidebar-toggler-size);
|
height: var(--hide-sidebar-toggler-size);
|
||||||
|
@ -287,5 +338,4 @@
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
4
src/frontend/svg/search.svg
Normal file
4
src/frontend/svg/search.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="6.5" cy="6.5" r="5.5" stroke="#717682" stroke-width="2"/>
|
||||||
|
<rect x="10.4143" y="10" width="5" height="2" rx="1" transform="rotate(45 10.4143 10)" fill="#717682"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 277 B |
Loading…
Add table
Add a link
Reference in a new issue