mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-23 15:19:41 +02:00
Sidebar redesign (#200)
* New sidebar * Save state to local storage * Make sidebar sticky * Text overflow * Fix add page button on mobile * Mobile layout * Display sidebar when ready * Add logo * Remove files * Fix margin * Update logo padding-bottom * Hovers * Decrease logo's font size * Make logo not sticky * Cleanup classnames * Simplify css * Update sidebar module * Fix animation * Fix cursor issue * Fix vars and logo paddings
This commit is contained in:
parent
16ba86fddb
commit
30d96909d3
15 changed files with 531 additions and 185 deletions
178
src/frontend/js/modules/sidebar.js
Normal file
178
src/frontend/js/modules/sidebar.js
Normal file
|
@ -0,0 +1,178 @@
|
|||
import { Storage } from '../utils/storage';
|
||||
|
||||
/**
|
||||
* Local storage key
|
||||
*/
|
||||
const LOCAL_STORAGE_KEY = 'docs_sidebar_state';
|
||||
|
||||
|
||||
/**
|
||||
* Section list item height in px
|
||||
*/
|
||||
const ITEM_HEIGHT = 31;
|
||||
|
||||
/**
|
||||
* Sidebar module
|
||||
*/
|
||||
export default class Sidebar {
|
||||
/**
|
||||
* CSS classes
|
||||
*
|
||||
* @returns {Record<string, string>}
|
||||
*/
|
||||
static get CSS() {
|
||||
return {
|
||||
toggler: 'docs-sidebar__section-toggler',
|
||||
section: 'docs-sidebar__section',
|
||||
sectionCollapsed: 'docs-sidebar__section--collapsed',
|
||||
sectionAnimated: 'docs-sidebar__section--animated',
|
||||
sectionTitle: 'docs-sidebar__section-title',
|
||||
sectionTitleActive: 'docs-sidebar__section-title--active',
|
||||
sectionList: 'docs-sidebar__section-list',
|
||||
sectionListItemActive: 'docs-sidebar__section-list-item--active',
|
||||
sidebarToggler: 'docs-sidebar__toggler',
|
||||
sidebarContent: 'docs-sidebar__content',
|
||||
sidebarContentHidden: 'docs-sidebar__content--hidden',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates base properties
|
||||
*/
|
||||
constructor() {
|
||||
/**
|
||||
* Stores refs to HTML elements needed for correct sidebar work
|
||||
*/
|
||||
this.nodes = {
|
||||
sections: [],
|
||||
sidebarContent: null,
|
||||
toggler: null,
|
||||
};
|
||||
this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY);
|
||||
const storedState = this.sidebarStorage.get();
|
||||
|
||||
this.sectionsState = storedState ? JSON.parse(storedState) : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by ModuleDispatcher to initialize module from DOM
|
||||
*
|
||||
* @param {writingSettings} settings - module settings
|
||||
* @param {HTMLElement} moduleEl - module element
|
||||
*/
|
||||
init(settings, moduleEl) {
|
||||
this.nodes.sections = Array.from(moduleEl.querySelectorAll('.' + Sidebar.CSS.section));
|
||||
this.nodes.sections.forEach(section => this.initSection(section));
|
||||
this.nodes.sidebarContent = moduleEl.querySelector('.' + Sidebar.CSS.sidebarContent);
|
||||
this.nodes.toggler = moduleEl.querySelector('.' + Sidebar.CSS.sidebarToggler);
|
||||
this.nodes.toggler.addEventListener('click', () => this.toggleSidebar());
|
||||
this.ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes sidebar sections: applies stored state and adds event listeners
|
||||
*
|
||||
* @param {HTMLElement} section
|
||||
* @returns {void}
|
||||
*/
|
||||
initSection(section) {
|
||||
const id = section.dataset.id;
|
||||
const togglerEl = section.querySelector('.' + Sidebar.CSS.toggler);
|
||||
|
||||
if (!togglerEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
togglerEl.addEventListener('click', e => this.handleSectionTogglerClick(id, section, e));
|
||||
|
||||
if (typeof this.sectionsState[id] === 'undefined') {
|
||||
this.sectionsState[id] = false;
|
||||
}
|
||||
if (this.sectionsState[id]) {
|
||||
this.setSectionCollapsed(section, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and set sections list max height for smooth animation
|
||||
*/
|
||||
const sectionList = section.querySelector('.' + Sidebar.CSS.sectionList);
|
||||
|
||||
if (!sectionList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsCount = sectionList.children.length;
|
||||
|
||||
sectionList.style.maxHeight = `${ itemsCount * ITEM_HEIGHT }px`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles section expansion
|
||||
*
|
||||
* @param {number} sectionId - id of the section to toggle
|
||||
* @param {HTMLElement} sectionEl - section html element
|
||||
* @param {MouseEvent} event - click event
|
||||
* @returns {void}
|
||||
*/
|
||||
handleSectionTogglerClick(sectionId, sectionEl, event) {
|
||||
event.preventDefault();
|
||||
this.sectionsState[sectionId] = !this.sectionsState[sectionId];
|
||||
this.sidebarStorage.set(JSON.stringify(this.sectionsState));
|
||||
this.setSectionCollapsed(sectionEl, this.sectionsState[sectionId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates section's collapsed state
|
||||
*
|
||||
* @param {HTMLElement} sectionEl - element of the section to toggle
|
||||
* @param {boolean} collapsed - new collapsed state
|
||||
* @param {boolean} [animated] - true if state should change with animation
|
||||
*/
|
||||
setSectionCollapsed(sectionEl, collapsed, animated = true) {
|
||||
const sectionList = sectionEl.querySelector('.' + Sidebar.CSS.sectionList);
|
||||
|
||||
if (!sectionList) {
|
||||
return;
|
||||
}
|
||||
sectionEl.classList.toggle(Sidebar.CSS.sectionAnimated, animated);
|
||||
sectionEl.classList.toggle(Sidebar.CSS.sectionCollapsed, collapsed);
|
||||
|
||||
/**
|
||||
* Highlight section item as active if active child item is collapsed.
|
||||
*/
|
||||
const activeSectionListItem = sectionList.querySelector('.' + Sidebar.CSS.sectionListItemActive);
|
||||
const sectionTitle = sectionEl.querySelector('.' + Sidebar.CSS.sectionTitle);
|
||||
|
||||
if (!activeSectionListItem) {
|
||||
return;
|
||||
}
|
||||
if (collapsed && animated) {
|
||||
/**
|
||||
* Highlights section title as active with a delay to let section collapse animation finish first
|
||||
*/
|
||||
setTimeout(() => {
|
||||
sectionTitle.classList.toggle(Sidebar.CSS.sectionTitleActive, collapsed);
|
||||
}, 200);
|
||||
} else {
|
||||
sectionTitle.classList.toggle(Sidebar.CSS.sectionTitleActive, collapsed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles sidebar visibility
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
toggleSidebar() {
|
||||
this.nodes.sidebarContent.classList.toggle(Sidebar.CSS.sidebarContentHidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays sidebar when ready
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
ready() {
|
||||
this.nodes.sidebarContent.classList.remove(Sidebar.CSS.sidebarContentHidden);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue