From 95969ce276c774dca37007c5f908b274cf761e7e Mon Sep 17 00:00:00 2001 From: Yeokyung Yoon Date: Tue, 19 Jul 2022 02:05:12 +0900 Subject: [PATCH] feat: add sidebar slider --- package.json | 1 + src/backend/views/components/sidebar.twig | 4 ++ src/frontend/js/modules/sidebar.js | 51 +++++++++++++++++++ src/frontend/styles/components/sidebar.pcss | 56 +++++++++++++++++++-- src/frontend/svg/arrow-left.svg | 3 ++ yarn.lock | 5 ++ 6 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 src/frontend/svg/arrow-left.svg diff --git a/package.json b/package.json index d3d10df..59d6a45 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest" }, "dependencies": { + "@codexteam/shortcuts": "^1.1.1", "config": "^3.3.6", "cookie-parser": "^1.4.5", "csurf": "^1.11.0", diff --git a/src/backend/views/components/sidebar.twig b/src/backend/views/components/sidebar.twig index a735b89..28d6131 100644 --- a/src/backend/views/components/sidebar.twig +++ b/src/backend/views/components/sidebar.twig @@ -52,4 +52,8 @@ + +
+ {{ svg('arrow-left') }} +
diff --git a/src/frontend/js/modules/sidebar.js b/src/frontend/js/modules/sidebar.js index dcb6d60..f1194b8 100644 --- a/src/frontend/js/modules/sidebar.js +++ b/src/frontend/js/modules/sidebar.js @@ -1,9 +1,11 @@ import { Storage } from '../utils/storage'; +import Shortcut from '@codexteam/shortcuts'; /** * Local storage key */ const LOCAL_STORAGE_KEY = 'docs_sidebar_state'; +const SIDEBAR_VISIBILITY_KEY = 'docs_sidebar_visibility'; /** @@ -31,6 +33,9 @@ export default class Sidebar { sectionList: 'docs-sidebar__section-list', sectionListItemActive: 'docs-sidebar__section-list-item--active', sidebarToggler: 'docs-sidebar__toggler', + sidebarSlider: 'docs-sidebar__slider', + sidebarCollapsed: 'docs-sidebar--collapsed', + sidebarAnimated: 'docs-sidebar--animated', sidebarContent: 'docs-sidebar__content', sidebarContentHidden: 'docs-sidebar__content--hidden', sidebarContentInvisible: 'docs-sidebar__content--invisible', @@ -45,14 +50,21 @@ export default class Sidebar { * Stores refs to HTML elements needed for correct sidebar work */ this.nodes = { + sidebar: null, sections: [], sidebarContent: null, toggler: null, + slider: null, }; this.sidebarStorage = new Storage(LOCAL_STORAGE_KEY); const storedState = this.sidebarStorage.get(); this.sectionsState = storedState ? JSON.parse(storedState) : {}; + + this.sidebarVisibilityStorage = new Storage(SIDEBAR_VISIBILITY_KEY); + const storedVisibility = this.sidebarVisibilityStorage.get(); + + this.visibility = storedVisibility !== 'false'; } /** @@ -62,11 +74,14 @@ export default class Sidebar { * @param {HTMLElement} moduleEl - module element */ init(settings, moduleEl) { + this.nodes.sidebar = 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.nodes.slider = moduleEl.querySelector('.' + Sidebar.CSS.sidebarSlider); + this.nodes.slider.addEventListener('click', () => this.handleSliderClick()); this.ready(); } @@ -168,12 +183,48 @@ export default class Sidebar { this.nodes.sidebarContent.classList.toggle(Sidebar.CSS.sidebarContentHidden); } + /** + * Initializes sidebar + * + * @returns {void} + */ + initSidebar() { + if (!this.visibility) { + this.nodes.slider.classList.add(Sidebar.CSS.sidebarSliderHidden); + this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarCollapsed); + } + + // prevent sidebar animation on page load + setTimeout(() => { + this.nodes.sidebar.classList.add(Sidebar.CSS.sidebarAnimated); + }, 200); + + // add shortcut to slide sidebar + const shortcutForSlider = new Shortcut({ + name: 'CMD+SHIFT+S', + on: document.body, + callback: () => this.handleSliderClick(), + }); + } + + /** + * Slides sidebar + * + * @returns {void} + */ + handleSliderClick() { + this.visibility = !this.visibility; + this.sidebarVisibilityStorage.set(this.visibility); + this.nodes.sidebar.classList.toggle(Sidebar.CSS.sidebarCollapsed); + } + /** * Displays sidebar when ready * * @returns {void} */ ready() { + this.initSidebar(); this.nodes.sidebarContent.classList.remove(Sidebar.CSS.sidebarContentInvisible); } } diff --git a/src/frontend/styles/components/sidebar.pcss b/src/frontend/styles/components/sidebar.pcss index 2a455a4..77e04cf 100644 --- a/src/frontend/styles/components/sidebar.pcss +++ b/src/frontend/styles/components/sidebar.pcss @@ -1,6 +1,34 @@ .docs-sidebar { width: 100vw; - + + &--animated { + .docs-sidebar__content { + transition: transform 200ms ease-in-out; + } + + .docs-sidebar__slider { + transition: left 200ms ease-in-out; + + svg { + transition: transform 200ms ease-in-out; + } + } + } + + &--collapsed { + .docs-sidebar__content { + transform: translateX(-100%); + } + + .docs-sidebar__slider { + left: 20px; + + svg { + transform: rotate(180deg); + } + } + } + @media (--desktop) { width: var(--layout-sidebar-width); } @@ -14,7 +42,7 @@ display: flex; flex-direction: column; overflow: auto; - + @media (--desktop) { height: calc(100vh - var(--layout-height-header)); border-right: 1px solid var(--color-line-gray); @@ -157,7 +185,7 @@ height: 24px; transition-property: background-color; transition-duration: 0.1s; - + @apply --squircle; @media (--can-hover) { @@ -178,7 +206,7 @@ color: var(--color-text-second); padding: 20px 15px; border-bottom: 1px solid var(--color-line-gray); - + @media (--desktop) { display: none; } @@ -188,6 +216,24 @@ } } + &__slider { + display: none; + position: fixed; + left: calc(var(--layout-sidebar-width) + 20px); + bottom: 20px; + width: 32px; + height: 32px; + border-radius: 8px; + cursor: pointer; + background-color: var(--color-link-hover); + + @media (--desktop) { + display: flex; + justify-content: center; + align-items: center; + } + } + &__logo { display: none; margin-top: auto; @@ -217,4 +263,4 @@ } } -} \ No newline at end of file +} diff --git a/src/frontend/svg/arrow-left.svg b/src/frontend/svg/arrow-left.svg new file mode 100644 index 0000000..281b737 --- /dev/null +++ b/src/frontend/svg/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/yarn.lock b/yarn.lock index c07636c..3be5259 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,6 +907,11 @@ resolved "https://registry.yarnpkg.com/@codexteam/misprints/-/misprints-1.0.0.tgz#e5a7dec7389fe0f176cd51a040d6dc9bdc252086" integrity sha512-R2IO1JmcaWCuWNPFVEAyar2HqQFuJwkeQUyVF0ovY4ip7z+VnVTYWxeYhCx7eZYEQCyXmcJooICQDihtn16lOA== +"@codexteam/shortcuts@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@codexteam/shortcuts/-/shortcuts-1.1.1.tgz#6aa19ed0476da78045847ddc6d5b311f2f015094" + integrity sha512-wtpYocFlFQSOiea3KAySn9ONno/yKL4JukokV0vJUq1BOUmVEx71sdTW7qgQhG1wcfIO2R/XJ/y4K9EZQyBzng== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"