From c70ef99f70a552dee3a272378830a6708bd34edf Mon Sep 17 00:00:00 2001 From: lorenzodianni Date: Sat, 7 Jan 2023 16:14:58 +0100 Subject: [PATCH] feat: toggle sidebar in responsive mode | #15 #19 --- demo/demo.css | 10 ---- src/sidebar.core.ts | 1 - src/sidebar.element.ts | 41 ++++++++------ src/sidebarjs.scss | 126 ++++++++++++++++++++++++----------------- 4 files changed, 97 insertions(+), 81 deletions(-) diff --git a/demo/demo.css b/demo/demo.css index 53b6437..a4c5554 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -256,13 +256,3 @@ input[type="radio"]:checked + label:before { h2 { margin: 12px 0; } - -@media (min-width: 1025px) { - [sidebarjs-toggle="rightSidebarName"] { - margin-left: auto; - } - - [sidebarjs-toggle="leftSidebarName"], [sidebarjs-toggle=""] { - display: none; - } -} diff --git a/src/sidebar.core.ts b/src/sidebar.core.ts index 136c403..1049120 100644 --- a/src/sidebar.core.ts +++ b/src/sidebar.core.ts @@ -3,7 +3,6 @@ export const SIDEBARJS_FALLBACK_NAME = ''; export const SIDEBARJS_CONTENT = 'sidebarjs-content'; export const SIDEBARJS_TRANSITION_START = 'sidebarjs--transition-start'; export const SIDEBARJS_TRANSITION_END = 'sidebarjs--transition-end'; -export const SIDEBARJS_RESPONSIVE = 'sidebarjs--responsive'; export const IS_VISIBLE = `${SIDEBARJS}--is-visible`; export const IS_MOVING = `${SIDEBARJS}--is-moving`; export const POSITIONS: SidebarPosition[] = [SidebarPosition.Left, SidebarPosition.Right]; diff --git a/src/sidebar.element.ts b/src/sidebar.element.ts index 6c4c82a..8492103 100644 --- a/src/sidebar.element.ts +++ b/src/sidebar.element.ts @@ -17,7 +17,6 @@ import { SidebarConfig, SIDEBARJS, SIDEBARJS_CONTENT, - SIDEBARJS_RESPONSIVE, SIDEBARJS_TRANSITION_END, SIDEBARJS_TRANSITION_START, SidebarPosition, @@ -131,15 +130,15 @@ export class SidebarElement implements SidebarBase { } public setPosition(position: SidebarPosition): void { - this.addComponentClass(IS_MOVING); - this.position = POSITIONS.indexOf(position) >= 0 ? position : SidebarPosition.Left; - const resetMainContent = (document.querySelectorAll(`[${SIDEBARJS}]`) || []).length === 1; - this.removeComponentClassPosition(resetMainContent); - this.addComponentClass(`${SIDEBARJS}--${this.position}`); - if (this.responsive && this.mainContent) { - this.mainContent.classList.add(`${SIDEBARJS_CONTENT}--${this.position}`); - } - setTimeout(() => this.component && this.removeComponentClass(IS_MOVING), 200); + this.invokeWithoutComponentTransition(() => { + this.position = POSITIONS.indexOf(position) >= 0 ? position : SidebarPosition.Left; + const resetMainContent = (document.querySelectorAll(`[${SIDEBARJS}]`) || []).length === 1; + this.removeComponentClassPosition(resetMainContent); + this.addComponentClass(`${SIDEBARJS}--${this.position}`); + if (this.responsive && this.mainContent) { + this.mainContent.classList.add(`${SIDEBARJS_CONTENT}--${this.position}`); + } + }, true); } public addAttrsEventsListeners(sidebarName: string): void { @@ -354,12 +353,8 @@ export class SidebarElement implements SidebarBase { if (this.responsive && !this.mainContent) { throw new Error(`You have set {responsive: true} without provide a [${SIDEBARJS_CONTENT}] element`); } - this.addComponentClass(SIDEBARJS_RESPONSIVE); const destroyMediaQuery = this.setMediaQuery(breakpoint); - return () => { - destroyMediaQuery(); - this.removeComponentClass(SIDEBARJS_RESPONSIVE); - }; + return () => destroyMediaQuery(); } private setMediaQuery(breakpoint?: SidebarConfig['responsive']) { @@ -371,11 +366,23 @@ export class SidebarElement implements SidebarBase { return () => { mediaQuery.removeEventListener('change', toggleMediaQueryClass); this.toggleDesktopBreakpointClass(false); - } + }; + } + + private removeIsMovingComponentClass = () => { + this.component && this.removeComponentClass(IS_MOVING); + }; + + private invokeWithoutComponentTransition(fn: () => void, isAsync?: boolean) { + this.addComponentClass(IS_MOVING); + fn(); + isAsync ? setTimeout(this.removeIsMovingComponentClass, 200) : this.removeIsMovingComponentClass(); } private toggleDesktopBreakpointClass(isDesktop: boolean) { - document.body.classList.toggle('sidebarjs--desktop-breakpoint', isDesktop); + this.invokeWithoutComponentTransition(() => { + document.body.classList.toggle('sidebarjs--desktop-breakpoint', isDesktop); + }, true); } private applyStyle(el: HTMLElement, prop: CSSStyleProperties, val: CSSStyleValues, vendorify?: boolean): void { diff --git a/src/sidebarjs.scss b/src/sidebarjs.scss index 29cfdb4..fc671f6 100644 --- a/src/sidebarjs.scss +++ b/src/sidebarjs.scss @@ -1,6 +1,8 @@ -$timing: ease; -$duration: .3s; -$width: 300px; +:root { + --sidebarjs-width: 300px; + --sidebarjs-transition-duration: .3s; + --sidebarjs-transition-timing-function: ease; +} @mixin shadow($position) { $x: 0; @@ -19,14 +21,6 @@ $width: 300px; height: 100%; } -@mixin component--is-visible($important: null) { - transform: translate(0, 0) $important; - transition: transform 0s $timing 0s; - [sidebarjs-container] { - transform: translate(0, 0) $important; - } -} - %component--left { transform: translate(-100%, 0); } @@ -35,31 +29,12 @@ $width: 300px; transform: translate(100%, 0); } -[sidebarjs].sidebarjs--left { - @extend %component--left; - - [sidebarjs-container] { - @extend %component--left; - @include shadow('left'); - } -} - -[sidebarjs].sidebarjs--right { - @extend %component--right; - - [sidebarjs-container] { - @extend %component--right; - @include shadow('right'); - margin-left: auto; - } -} - [sidebarjs-backdrop] { @extend %component--full-screen; position: absolute; background: #000; opacity: 0; - transition: opacity $duration $timing; + transition: opacity var(--sidebarjs-transition-duration) var(--sidebarjs-transition-timing-function); will-change: opacity; } @@ -69,10 +44,10 @@ $width: 300px; display: flex; flex-direction: column; width: 90%; - max-width: $width; + max-width: var(--sidebarjs-width); height: 100%; background: #fff; - transition: transform $timing $duration; + transition: transform var(--sidebarjs-transition-timing-function) var(--sidebarjs-transition-duration); will-change: transform; } @@ -80,18 +55,43 @@ $width: 300px; @extend %component--full-screen; position: fixed; z-index: 9999; - transition: transform 0s $timing $duration; + transition: transform 0s var(--sidebarjs-transition-timing-function) var(--sidebarjs-transition-duration); &.sidebarjs--is-visible { - @include component--is-visible; + transform: translate(0, 0); + transition: transform 0s var(--sidebarjs-transition-timing-function) 0s; + + [sidebarjs-container] { + transform: translate(0, 0); + } } &.sidebarjs--is-moving { - transition: none; transform: translate(0, 0); - [sidebarjs-container], [sidebarjs-backdrop] { - transition: none; + &, [sidebarjs-container], [sidebarjs-backdrop] { + transition: none !important; + } + } + + &.sidebarjs--left { + &, [sidebarjs-container] { + @extend %component--left; + } + + [sidebarjs-container] { + @include shadow('left'); + } + } + + &.sidebarjs--right { + &, [sidebarjs-container] { + @extend %component--right; + } + + [sidebarjs-container] { + @include shadow('right'); + margin-left: auto; } } } @@ -100,7 +100,7 @@ $width: 300px; position: relative; width: 100%; min-height: 100%; - transition: width $duration $timing; + transition: width var(--sidebarjs-transition-duration) var(--sidebarjs-transition-timing-function); &.sidebarjs-content--left { margin-left: auto; @@ -114,13 +114,29 @@ $width: 300px; } .sidebarjs--desktop-breakpoint { - [sidebarjs].sidebarjs--responsive { - @include component--is-visible(!important); - width: $width; + display: flex; + + [sidebarjs-backdrop] { + display: none; + } + + [sidebarjs] { + position: relative; + flex-shrink: 0; + height: 100vh; + width: 0; + transition: width var(--sidebarjs-transition-duration) var(--sidebarjs-transition-timing-function); + + &.sidebarjs--transition-start { + will-change: width; + } + + &.sidebarjs--is-visible { + width: var(--sidebarjs-width); + } &.sidebarjs--left { - left: 0; - right: auto; + order: 0; [sidebarjs-container] { box-shadow: 1px 0 0 rgba(black, .1); @@ -128,27 +144,31 @@ $width: 300px; } &.sidebarjs--right { - right: 0; - left: auto; + order: 1000; [sidebarjs-container] { box-shadow: -1px 0 0 rgba(black, .1); } } + &, [sidebarjs-container] { + transform: none; + } + [sidebarjs-container] { max-width: none; width: 100%; box-shadow: none; } - } - [sidebarjs-content] { - width: calc(100% - #{$width}); - - &.sidebarjs-content--left.sidebarjs-content--right { - width: calc(100% - #{$width*2}); - margin: 0 auto; + &:not(.sidebarjs--is-visible) { + [sidebarjs-container] { + overflow-x: hidden; + } } } + + [sidebarjs-content].sidebarjs-content--left.sidebarjs-content--right { + margin: 0 auto; + } }