diff --git a/libs/components/src/app-shell/app-shell.scss b/libs/components/src/app-shell/app-shell.scss index 82f686212..aa7fc4374 100644 --- a/libs/components/src/app-shell/app-shell.scss +++ b/libs/components/src/app-shell/app-shell.scss @@ -68,7 +68,6 @@ height: 100vh; grid-area: main; overflow: auto; - transition: margin 200ms ease-in-out; .main-wrapper { margin: 0 auto; @@ -100,10 +99,6 @@ .cov-drawer--open & { margin-left: 256px; } - - .cov-help--open & { - margin-right: 320px; - } } @media only screen and (max-width: 1600px) { @@ -117,11 +112,37 @@ grid-area: help; position: fixed; right: 0; - width: 320px; + width: var(--help-width, 320px); height: 100vh; overflow-y: auto; - transition-property: width; - transition-duration: 200ms; + + .resize-handle { + position: absolute; + left: 0; + width: 10px; + height: 100vh; + cursor: ew-resize; + z-index: 1100; + user-select: none; + + &::after { + content: ''; + position: absolute; + left: 50%; + top: 0; + width: 2px; + height: 100%; + background-color: var(--mdc-theme-primary); + transform: translateX(-50%); + transition: opacity 0.3s ease; + opacity: 0; + } + + &:hover::after, + &.resizing::after { + opacity: 1; + } + } .cov-help--closed & { width: 0; @@ -152,7 +173,7 @@ .current-section-name { opacity: 0; - transition: opacity 150ms; + transition: opacity 200ms; :host([open]) & { opacity: 1; diff --git a/libs/components/src/app-shell/app-shell.ts b/libs/components/src/app-shell/app-shell.ts index c57812a42..d2717f0b7 100644 --- a/libs/components/src/app-shell/app-shell.ts +++ b/libs/components/src/app-shell/app-shell.ts @@ -31,6 +31,11 @@ export class CovalentAppShell extends DrawerBase { `, ]; + element = document.querySelector('.help'); + helpWidth = 0; + private _startX!: number; + private _startWidth!: number; + @queryAssignedElements({ slot: 'navigation' }) navigationListElements!: HTMLElement[]; @@ -77,8 +82,120 @@ export class CovalentAppShell extends DrawerBase { constructor() { super(); + this._resize = this._resize.bind(this); + this._stopResize = this._stopResize.bind(this); + this._startResizing = this._startResizing.bind(this); + this._setupEventListeners(); + window.addEventListener('DOMContentLoaded', () => { + this.setupHelpPanelListeners(); + this.resizeEvent(); + const storedWidth = localStorage.getItem('helpWidth'); + if (storedWidth) { + this.helpWidth = parseInt(storedWidth, 10); + this.updateHelpPanelWidth(); + } + }); + } + + setupHelpPanelListeners() { + const helpToggle = document.querySelector('.help-item'); + const helpClose = document.querySelector('.help-close'); + + helpToggle?.addEventListener('click', () => { + this.toggleHelpPanel(); + }); + + helpClose?.addEventListener('click', () => { + this.toggleHelpPanel(false); + }); + } + + toggleHelpPanel(open?: boolean) { + this.helpOpen = open !== undefined ? open : !this.helpOpen; + if (this.helpOpen) { + const storedWidth = localStorage.getItem('helpWidth'); + this.helpWidth = storedWidth ? parseInt(storedWidth, 10) : 320; + } else { + this.helpWidth = 0; + } + this.updateHelpPanelWidth(); + this.requestUpdate(); + } - this.resizeEvent(); + private _setupEventListeners() { + window.addEventListener('DOMContentLoaded', () => { + const helpToggle = document.querySelector('.help-item'); + const helpClose = document.querySelector('.help-close'); + + helpToggle?.addEventListener('click', () => { + this.helpOpen = !this.helpOpen; + this.helpWidth = this.helpOpen ? 320 : 0; + this.requestUpdate(); + }); + + helpClose?.addEventListener('click', () => { + this.helpOpen = false; + this.helpWidth = 0; + this.requestUpdate(); + }); + }); + } + + override firstUpdated() { + const resizeHandle = this.shadowRoot?.querySelector('.resize-handle'); + if (resizeHandle) { + resizeHandle.addEventListener('mousedown', (event) => { + if (event instanceof MouseEvent) { + this._startResizing(event); + } + }); + + resizeHandle.addEventListener('dblclick', () => { + if (this.helpWidth > 320 || this.helpWidth !== 320) { + this.helpWidth = 320; + localStorage.setItem('helpWidth', '320'); + this.updateHelpPanelWidth(); + this.requestUpdate(); + } + }); + } + } + + private _startResizing(event: MouseEvent) { + const resizeHandle = this.shadowRoot?.querySelector('.resize-handle'); + if (event.target === resizeHandle) { + this._startX = event.clientX; + this._startWidth = this.helpWidth; + document.addEventListener('mousemove', this._resize); + document.addEventListener('mouseup', this._stopResize); + (event.target as HTMLElement).classList.add('resizing'); + } + } + + private _resize(event: MouseEvent) { + const diff = event.clientX - this._startX; + const windowWidth = window.innerWidth; + const mainMinWidth = 600; + const maxWidthForHelp = Math.max(320, windowWidth - mainMinWidth); + const newWidth = Math.max( + 320, + Math.min(maxWidthForHelp, this._startWidth - diff) + ); + if (this.helpWidth !== newWidth) { + this.helpWidth = newWidth; + localStorage.setItem('helpWidth', this.helpWidth.toString()); + this.updateHelpPanelWidth(); + } + event.preventDefault(); + } + + private _stopResize() { + document.removeEventListener('mousemove', this._resize); + document.removeEventListener('mouseup', this._stopResize); + const resizeHandle = this.shadowRoot?.querySelector('.resize-handle'); + if (resizeHandle) { + resizeHandle.classList.remove('resizing'); + } } private _toggleOpen(forcedOpen = false) { @@ -95,6 +212,16 @@ export class CovalentAppShell extends DrawerBase { this.requestUpdate(); } + private updateHelpPanelWidth() { + const helpPanel = this.shadowRoot?.querySelector('.help') as HTMLElement; + const mainPanel = this.shadowRoot?.querySelector('.main') as HTMLElement; + + if (helpPanel && mainPanel) { + helpPanel.style.setProperty('--help-width', `${this.helpWidth}px`); + mainPanel.style.marginRight = `${this.helpWidth}px`; + } + } + private _handleMenuClick() { this.mdcRoot.dispatchEvent( new Event('transitionend', { bubbles: true, composed: true }) @@ -229,7 +356,8 @@ export class CovalentAppShell extends DrawerBase { ${this.renderMain()} -
+
+