1
0
Fork 0
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:
Tanya 2022-06-16 21:37:37 +08:00 committed by GitHub
parent 16ba86fddb
commit 30d96909d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 531 additions and 185 deletions

View 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);
}
}