From 895bfa1933ec8cbf70c10704a5254099f8f9cde5 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Fri, 15 May 2026 01:18:39 +0530 Subject: [PATCH 01/16] feat(layouts): add resizable side panels with localStorage persistence Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 259 +++++++++++++++++++++++++++++ assets/scss/_resizable-panels.scss | 138 +++++++++++++++ assets/scss/_styles_project.scss | 1 + layouts/partials/scripts.html | 16 ++ 4 files changed, 414 insertions(+) create mode 100644 assets/js/resizable-panels.js create mode 100644 assets/scss/_resizable-panels.scss create mode 100644 layouts/partials/scripts.html diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js new file mode 100644 index 00000000000..388e12c0b7c --- /dev/null +++ b/assets/js/resizable-panels.js @@ -0,0 +1,259 @@ +/** + * Resizable Panels Feature + * Allows users to adjust the width of side panels (left sidebar and right TOC) + * Preferences are saved to localStorage and restored on page load + * Includes reset functionality to restore default widths + */ + +(function() { + 'use strict'; + + const STORAGE_KEY = 'layer5-docs-panel-widths'; + const DEFAULT_WIDTHS = { + sidebar: 2, // col-xl-2 = ~16.66% + toc: 2, // col-xl-2 = ~16.66% + main: 8 // col-xl-8 = ~66.66% + }; + + // CSS class shortcuts for Bootstrap grid columns + const COL_CLASSES = { + 'col-1': 8.33, + 'col-2': 16.66, + 'col-3': 25, + 'col-4': 33.33, + 'col-5': 41.66, + 'col-6': 50, + 'col-7': 58.33, + 'col-8': 66.66, + 'col-9': 75, + 'col-10': 83.33, + 'col-11': 91.66, + 'col-12': 100 + }; + + class ResizablePanels { + constructor() { + this.sidebar = document.querySelector('.td-sidebar'); + this.toc = document.querySelector('.td-sidebar-toc'); + this.main = document.querySelector('main[role="main"]'); + this.row = document.querySelector('.row.flex-xl-nowrap'); + + if (!this.row || !this.sidebar || !this.main) { + console.warn('Resizable panels: Required elements not found'); + return; + } + + this.isResizing = false; + this.currentResizeTarget = null; + this.startX = 0; + this.startWidth = 0; + + this.init(); + } + + init() { + // Load saved widths from localStorage + this.loadSavedWidths(); + + // Create resize handles + this.createResizeHandles(); + + // Add event listeners + this.addEventListeners(); + + // Add reset button + this.addResetButton(); + } + + loadSavedWidths() { + try { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + const widths = JSON.parse(saved); + this.applyWidths(widths); + } + } catch (error) { + console.error('Error loading saved panel widths:', error); + } + } + + createResizeHandles() { + // Create resize handle between sidebar and main content + const sidebarHandle = document.createElement('div'); + sidebarHandle.className = 'resizable-panel-handle resizable-panel-handle--right'; + sidebarHandle.setAttribute('data-resize-target', 'sidebar'); + sidebarHandle.setAttribute('title', 'Drag to resize sidebar'); + this.sidebar.appendChild(sidebarHandle); + + // Create resize handle for TOC (if it exists) + if (this.toc) { + const tocHandle = document.createElement('div'); + tocHandle.className = 'resizable-panel-handle resizable-panel-handle--left'; + tocHandle.setAttribute('data-resize-target', 'toc'); + tocHandle.setAttribute('title', 'Drag to resize table of contents'); + this.toc.appendChild(tocHandle); + } + } + + addEventListeners() { + document.addEventListener('mousedown', (e) => this.onMouseDown(e)); + document.addEventListener('mousemove', (e) => this.onMouseMove(e)); + document.addEventListener('mouseup', (e) => this.onMouseUp(e)); + } + + onMouseDown(e) { + if (!e.target.classList.contains('resizable-panel-handle')) { + return; + } + + this.isResizing = true; + this.currentResizeTarget = e.target.getAttribute('data-resize-target'); + this.startX = e.clientX; + + // Store current widths for delta calculation + this.startWidths = this.getCurrentWidths(); + + // Add active state + e.target.classList.add('resizable-panel-handle--active'); + document.body.style.userSelect = 'none'; + document.body.style.cursor = 'col-resize'; + } + + onMouseMove(e) { + if (!this.isResizing) return; + + const delta = e.clientX - this.startX; + const adjustment = delta / window.innerWidth; // Convert pixels to percentage-like ratio + + let newWidths = { ...this.startWidths }; + + if (this.currentResizeTarget === 'sidebar') { + // Resizing left sidebar + const sidebarPercent = (this.startWidths.sidebar * 100) / 12; // Convert col units to percentage + const mainPercent = (this.startWidths.main * 100) / 12; + + const newSidebarPercent = sidebarPercent + (adjustment * 100); + const newMainPercent = mainPercent - (adjustment * 100); + + // Constrain widths: min 1 col, max 5 cols for sidebar; min 4 cols for main + if (newSidebarPercent >= 8.33 && newSidebarPercent <= 41.66 && newMainPercent >= 33.33) { + newWidths.sidebar = Math.round((newSidebarPercent / 100) * 12); + newWidths.main = Math.round((newMainPercent / 100) * 12); + } + } else if (this.currentResizeTarget === 'toc') { + // Resizing right TOC panel + const tocPercent = (this.startWidths.toc * 100) / 12; + const mainPercent = (this.startWidths.main * 100) / 12; + + const newTocPercent = tocPercent - (adjustment * 100); + const newMainPercent = mainPercent + (adjustment * 100); + + // Constrain widths: min 1 col, max 5 cols for toc; min 4 cols for main + if (newTocPercent >= 8.33 && newTocPercent <= 41.66 && newMainPercent >= 33.33) { + newWidths.toc = Math.round((newTocPercent / 100) * 12); + newWidths.main = Math.round((newMainPercent / 100) * 12); + } + } + + this.applyWidths(newWidths); + } + + onMouseUp(e) { + if (!this.isResizing) return; + + this.isResizing = false; + const handle = document.querySelector('.resizable-panel-handle--active'); + if (handle) { + handle.classList.remove('resizable-panel-handle--active'); + } + + document.body.style.userSelect = ''; + document.body.style.cursor = ''; + + // Save widths to localStorage + this.savePanelWidths(); + } + + applyWidths(widths) { + const { sidebar, toc, main } = widths; + + // Update sidebar + this.removeBootstrapColClasses(this.sidebar); + this.sidebar.classList.add(`col-xl-${sidebar}`); + + // Update main + this.removeBootstrapColClasses(this.main); + this.main.classList.add(`col-xl-${main}`); + + // Update TOC if it exists + if (this.toc) { + this.removeBootstrapColClasses(this.toc); + this.toc.classList.add(`col-xl-${toc}`); + } + } + + getCurrentWidths() { + const getColNumber = (element) => { + const classes = element.className.split(' '); + const colClass = classes.find(c => c.match(/col-xl-\d+/)); + return colClass ? parseInt(colClass.split('-')[2]) : null; + }; + + return { + sidebar: getColNumber(this.sidebar) || DEFAULT_WIDTHS.sidebar, + toc: this.toc ? (getColNumber(this.toc) || DEFAULT_WIDTHS.toc) : DEFAULT_WIDTHS.toc, + main: getColNumber(this.main) || DEFAULT_WIDTHS.main + }; + } + + removeBootstrapColClasses(element) { + const classes = element.className.split(' ').filter(c => !c.match(/col-xl-\d+/)); + element.className = classes.join(' ').trim(); + } + + savePanelWidths() { + try { + const widths = this.getCurrentWidths(); + localStorage.setItem(STORAGE_KEY, JSON.stringify(widths)); + } catch (error) { + console.error('Error saving panel widths:', error); + } + } + + addResetButton() { + // Find the feature-info-container or page-header to add reset button + const pageHeader = document.querySelector('.page-header'); + if (!pageHeader) return; + + const resetButton = document.createElement('button'); + resetButton.id = 'reset-panel-widths'; + resetButton.className = 'btn btn-sm btn-outline-secondary ms-2'; + resetButton.innerHTML = ' Reset Layout'; + resetButton.setAttribute('title', 'Reset panel widths to default'); + + resetButton.addEventListener('click', () => this.resetPanelWidths()); + + // Find a good place to insert the button + const featureContainer = pageHeader.querySelector('.feature-info-container'); + if (featureContainer) { + featureContainer.insertAdjacentElement('beforeend', resetButton); + } else { + pageHeader.insertAdjacentElement('beforeend', resetButton); + } + } + + resetPanelWidths() { + this.applyWidths(DEFAULT_WIDTHS); + this.savePanelWidths(); + } + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new ResizablePanels(); + }); + } else { + new ResizablePanels(); + } +})(); diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss new file mode 100644 index 00000000000..e3248d7856d --- /dev/null +++ b/assets/scss/_resizable-panels.scss @@ -0,0 +1,138 @@ +/** + * Resizable Panels Styling + * Styles for the resize handles and drag interactions + */ + +// Resize handle styling +.resizable-panel-handle { + position: absolute; + background-color: transparent; + transition: background-color 0.2s ease; + z-index: 100; + + &--right { + // Right edge handle (between sidebar and main) + right: -3px; + top: 0; + bottom: 0; + width: 6px; + cursor: col-resize; + + &:hover, + &--active { + background-color: rgba(0, 211, 169, 0.3); + + &::after { + opacity: 1; + } + } + + &::after { + content: ''; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + width: 2px; + height: 40px; + background-color: #00d3a9; + opacity: 0; + transition: opacity 0.2s ease; + border-radius: 1px; + } + } + + &--left { + // Left edge handle (between main and TOC) + left: -3px; + top: 0; + bottom: 0; + width: 6px; + cursor: col-resize; + + &:hover, + &--active { + background-color: rgba(0, 211, 169, 0.3); + + &::after { + opacity: 1; + } + } + + &::after { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 2px; + height: 40px; + background-color: #00d3a9; + opacity: 0; + transition: opacity 0.2s ease; + border-radius: 1px; + } + } + + &--active { + background-color: rgba(0, 211, 169, 0.5); + } +} + +// Reset button styling +#reset-panel-widths { + white-space: nowrap; + font-size: 0.875rem; + + i { + margin-right: 0.25rem; + } + + &:hover { + background-color: #00d3a9; + color: #fff; + } +} + +// Make panels relatively positioned for absolute handle positioning +.td-sidebar, +.td-sidebar-toc { + position: relative; +} + +// Smooth transitions for width changes +.td-sidebar, +.td-sidebar-toc, +main[role="main"] { + transition: flex: 0.3s ease; +} + +// Ensure row is flex container for proper layout +.row.flex-xl-nowrap { + display: flex; + flex-wrap: nowrap; + + > aside, + > main { + overflow-x: hidden; + } +} + +// Mobile/Tablet adjustments - disable resize handles on smaller screens +@media (max-width: 1199.98px) { + .resizable-panel-handle { + display: none; + } + + #reset-panel-widths { + display: none; + } +} + +// Print styles - hide resize handles +@media print { + .resizable-panel-handle, + #reset-panel-widths { + display: none !important; + } +} diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 00ca1b8770b..84a003f5632 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -15,6 +15,7 @@ @import "elements_project"; @import "summary.scss"; @import "_kanvas-corner-popup.scss"; +@import "_resizable-panels.scss"; .navbar-dark { min-height: 5rem; diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html new file mode 100644 index 00000000000..409b2634f53 --- /dev/null +++ b/layouts/partials/scripts.html @@ -0,0 +1,16 @@ + + +{{ $kanvasTransition := resources.Get "js/kanvas-architectural-transition.js" -}} +{{ if $kanvasTransition }} + +{{ end -}} + +{{ $kanvasPopup := resources.Get "js/kanvas-corner-popup.js" -}} +{{ if $kanvasPopup }} + +{{ end -}} + +{{ $offlineSearch := resources.Get "js/offline-search.js" -}} +{{ if $offlineSearch }} + +{{ end -}} From 53e9296bafa5d24eef6dc912a287ca7b1cab2a7a Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Fri, 15 May 2026 01:29:23 +0530 Subject: [PATCH 02/16] fix(scss): correct transition property syntax in resizable-panels Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index e3248d7856d..ae3e6167d5b 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -104,7 +104,7 @@ .td-sidebar, .td-sidebar-toc, main[role="main"] { - transition: flex: 0.3s ease; + transition: flex-basis 0.3s ease; } // Ensure row is flex container for proper layout From 8dfa71e4ba2a389e6a0d6972bd762d4a7f3378bc Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Fri, 15 May 2026 10:10:02 +0530 Subject: [PATCH 03/16] fix(ui): add bottom padding to sidebar for better spacing Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 33 +++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index ae3e6167d5b..3311227f9a2 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -17,10 +17,11 @@ bottom: 0; width: 6px; cursor: col-resize; + background-color: rgba(0, 211, 169, 0.15); &:hover, &--active { - background-color: rgba(0, 211, 169, 0.3); + background-color: rgba(0, 211, 169, 0.4); &::after { opacity: 1; @@ -30,15 +31,16 @@ &::after { content: ''; position: absolute; - right: 0; + right: 1px; top: 50%; transform: translateY(-50%); - width: 2px; - height: 40px; + width: 3px; + height: 50px; background-color: #00d3a9; - opacity: 0; + opacity: 0.3; transition: opacity 0.2s ease; - border-radius: 1px; + border-radius: 2px; + box-shadow: 0 0 4px rgba(0, 211, 169, 0.5); } } @@ -49,10 +51,11 @@ bottom: 0; width: 6px; cursor: col-resize; + background-color: rgba(0, 211, 169, 0.15); &:hover, &--active { - background-color: rgba(0, 211, 169, 0.3); + background-color: rgba(0, 211, 169, 0.4); &::after { opacity: 1; @@ -62,15 +65,16 @@ &::after { content: ''; position: absolute; - left: 0; + left: 1px; top: 50%; transform: translateY(-50%); - width: 2px; - height: 40px; + width: 3px; + height: 50px; background-color: #00d3a9; - opacity: 0; + opacity: 0.3; transition: opacity 0.2s ease; - border-radius: 1px; + border-radius: 2px; + box-shadow: 0 0 4px rgba(0, 211, 169, 0.5); } } @@ -100,6 +104,11 @@ position: relative; } +// Ensure sidebar nav has proper scrolling with resizable panels +.td-sidebar-nav { + padding-bottom: 2rem; +} + // Smooth transitions for width changes .td-sidebar, .td-sidebar-toc, From a7040abbfd42409b0bfd43df3083d0264654b220 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Fri, 15 May 2026 23:05:51 +0530 Subject: [PATCH 04/16] fix(sidebar): adjust section title spacing Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 259 ----------------------------- assets/scss/_resizable-panels.scss | 147 ---------------- assets/scss/_styles_project.scss | 2 +- layouts/partials/scripts.html | 16 -- 4 files changed, 1 insertion(+), 423 deletions(-) delete mode 100644 assets/js/resizable-panels.js delete mode 100644 assets/scss/_resizable-panels.scss delete mode 100644 layouts/partials/scripts.html diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js deleted file mode 100644 index 388e12c0b7c..00000000000 --- a/assets/js/resizable-panels.js +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Resizable Panels Feature - * Allows users to adjust the width of side panels (left sidebar and right TOC) - * Preferences are saved to localStorage and restored on page load - * Includes reset functionality to restore default widths - */ - -(function() { - 'use strict'; - - const STORAGE_KEY = 'layer5-docs-panel-widths'; - const DEFAULT_WIDTHS = { - sidebar: 2, // col-xl-2 = ~16.66% - toc: 2, // col-xl-2 = ~16.66% - main: 8 // col-xl-8 = ~66.66% - }; - - // CSS class shortcuts for Bootstrap grid columns - const COL_CLASSES = { - 'col-1': 8.33, - 'col-2': 16.66, - 'col-3': 25, - 'col-4': 33.33, - 'col-5': 41.66, - 'col-6': 50, - 'col-7': 58.33, - 'col-8': 66.66, - 'col-9': 75, - 'col-10': 83.33, - 'col-11': 91.66, - 'col-12': 100 - }; - - class ResizablePanels { - constructor() { - this.sidebar = document.querySelector('.td-sidebar'); - this.toc = document.querySelector('.td-sidebar-toc'); - this.main = document.querySelector('main[role="main"]'); - this.row = document.querySelector('.row.flex-xl-nowrap'); - - if (!this.row || !this.sidebar || !this.main) { - console.warn('Resizable panels: Required elements not found'); - return; - } - - this.isResizing = false; - this.currentResizeTarget = null; - this.startX = 0; - this.startWidth = 0; - - this.init(); - } - - init() { - // Load saved widths from localStorage - this.loadSavedWidths(); - - // Create resize handles - this.createResizeHandles(); - - // Add event listeners - this.addEventListeners(); - - // Add reset button - this.addResetButton(); - } - - loadSavedWidths() { - try { - const saved = localStorage.getItem(STORAGE_KEY); - if (saved) { - const widths = JSON.parse(saved); - this.applyWidths(widths); - } - } catch (error) { - console.error('Error loading saved panel widths:', error); - } - } - - createResizeHandles() { - // Create resize handle between sidebar and main content - const sidebarHandle = document.createElement('div'); - sidebarHandle.className = 'resizable-panel-handle resizable-panel-handle--right'; - sidebarHandle.setAttribute('data-resize-target', 'sidebar'); - sidebarHandle.setAttribute('title', 'Drag to resize sidebar'); - this.sidebar.appendChild(sidebarHandle); - - // Create resize handle for TOC (if it exists) - if (this.toc) { - const tocHandle = document.createElement('div'); - tocHandle.className = 'resizable-panel-handle resizable-panel-handle--left'; - tocHandle.setAttribute('data-resize-target', 'toc'); - tocHandle.setAttribute('title', 'Drag to resize table of contents'); - this.toc.appendChild(tocHandle); - } - } - - addEventListeners() { - document.addEventListener('mousedown', (e) => this.onMouseDown(e)); - document.addEventListener('mousemove', (e) => this.onMouseMove(e)); - document.addEventListener('mouseup', (e) => this.onMouseUp(e)); - } - - onMouseDown(e) { - if (!e.target.classList.contains('resizable-panel-handle')) { - return; - } - - this.isResizing = true; - this.currentResizeTarget = e.target.getAttribute('data-resize-target'); - this.startX = e.clientX; - - // Store current widths for delta calculation - this.startWidths = this.getCurrentWidths(); - - // Add active state - e.target.classList.add('resizable-panel-handle--active'); - document.body.style.userSelect = 'none'; - document.body.style.cursor = 'col-resize'; - } - - onMouseMove(e) { - if (!this.isResizing) return; - - const delta = e.clientX - this.startX; - const adjustment = delta / window.innerWidth; // Convert pixels to percentage-like ratio - - let newWidths = { ...this.startWidths }; - - if (this.currentResizeTarget === 'sidebar') { - // Resizing left sidebar - const sidebarPercent = (this.startWidths.sidebar * 100) / 12; // Convert col units to percentage - const mainPercent = (this.startWidths.main * 100) / 12; - - const newSidebarPercent = sidebarPercent + (adjustment * 100); - const newMainPercent = mainPercent - (adjustment * 100); - - // Constrain widths: min 1 col, max 5 cols for sidebar; min 4 cols for main - if (newSidebarPercent >= 8.33 && newSidebarPercent <= 41.66 && newMainPercent >= 33.33) { - newWidths.sidebar = Math.round((newSidebarPercent / 100) * 12); - newWidths.main = Math.round((newMainPercent / 100) * 12); - } - } else if (this.currentResizeTarget === 'toc') { - // Resizing right TOC panel - const tocPercent = (this.startWidths.toc * 100) / 12; - const mainPercent = (this.startWidths.main * 100) / 12; - - const newTocPercent = tocPercent - (adjustment * 100); - const newMainPercent = mainPercent + (adjustment * 100); - - // Constrain widths: min 1 col, max 5 cols for toc; min 4 cols for main - if (newTocPercent >= 8.33 && newTocPercent <= 41.66 && newMainPercent >= 33.33) { - newWidths.toc = Math.round((newTocPercent / 100) * 12); - newWidths.main = Math.round((newMainPercent / 100) * 12); - } - } - - this.applyWidths(newWidths); - } - - onMouseUp(e) { - if (!this.isResizing) return; - - this.isResizing = false; - const handle = document.querySelector('.resizable-panel-handle--active'); - if (handle) { - handle.classList.remove('resizable-panel-handle--active'); - } - - document.body.style.userSelect = ''; - document.body.style.cursor = ''; - - // Save widths to localStorage - this.savePanelWidths(); - } - - applyWidths(widths) { - const { sidebar, toc, main } = widths; - - // Update sidebar - this.removeBootstrapColClasses(this.sidebar); - this.sidebar.classList.add(`col-xl-${sidebar}`); - - // Update main - this.removeBootstrapColClasses(this.main); - this.main.classList.add(`col-xl-${main}`); - - // Update TOC if it exists - if (this.toc) { - this.removeBootstrapColClasses(this.toc); - this.toc.classList.add(`col-xl-${toc}`); - } - } - - getCurrentWidths() { - const getColNumber = (element) => { - const classes = element.className.split(' '); - const colClass = classes.find(c => c.match(/col-xl-\d+/)); - return colClass ? parseInt(colClass.split('-')[2]) : null; - }; - - return { - sidebar: getColNumber(this.sidebar) || DEFAULT_WIDTHS.sidebar, - toc: this.toc ? (getColNumber(this.toc) || DEFAULT_WIDTHS.toc) : DEFAULT_WIDTHS.toc, - main: getColNumber(this.main) || DEFAULT_WIDTHS.main - }; - } - - removeBootstrapColClasses(element) { - const classes = element.className.split(' ').filter(c => !c.match(/col-xl-\d+/)); - element.className = classes.join(' ').trim(); - } - - savePanelWidths() { - try { - const widths = this.getCurrentWidths(); - localStorage.setItem(STORAGE_KEY, JSON.stringify(widths)); - } catch (error) { - console.error('Error saving panel widths:', error); - } - } - - addResetButton() { - // Find the feature-info-container or page-header to add reset button - const pageHeader = document.querySelector('.page-header'); - if (!pageHeader) return; - - const resetButton = document.createElement('button'); - resetButton.id = 'reset-panel-widths'; - resetButton.className = 'btn btn-sm btn-outline-secondary ms-2'; - resetButton.innerHTML = ' Reset Layout'; - resetButton.setAttribute('title', 'Reset panel widths to default'); - - resetButton.addEventListener('click', () => this.resetPanelWidths()); - - // Find a good place to insert the button - const featureContainer = pageHeader.querySelector('.feature-info-container'); - if (featureContainer) { - featureContainer.insertAdjacentElement('beforeend', resetButton); - } else { - pageHeader.insertAdjacentElement('beforeend', resetButton); - } - } - - resetPanelWidths() { - this.applyWidths(DEFAULT_WIDTHS); - this.savePanelWidths(); - } - } - - // Initialize when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - new ResizablePanels(); - }); - } else { - new ResizablePanels(); - } -})(); diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss deleted file mode 100644 index 3311227f9a2..00000000000 --- a/assets/scss/_resizable-panels.scss +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Resizable Panels Styling - * Styles for the resize handles and drag interactions - */ - -// Resize handle styling -.resizable-panel-handle { - position: absolute; - background-color: transparent; - transition: background-color 0.2s ease; - z-index: 100; - - &--right { - // Right edge handle (between sidebar and main) - right: -3px; - top: 0; - bottom: 0; - width: 6px; - cursor: col-resize; - background-color: rgba(0, 211, 169, 0.15); - - &:hover, - &--active { - background-color: rgba(0, 211, 169, 0.4); - - &::after { - opacity: 1; - } - } - - &::after { - content: ''; - position: absolute; - right: 1px; - top: 50%; - transform: translateY(-50%); - width: 3px; - height: 50px; - background-color: #00d3a9; - opacity: 0.3; - transition: opacity 0.2s ease; - border-radius: 2px; - box-shadow: 0 0 4px rgba(0, 211, 169, 0.5); - } - } - - &--left { - // Left edge handle (between main and TOC) - left: -3px; - top: 0; - bottom: 0; - width: 6px; - cursor: col-resize; - background-color: rgba(0, 211, 169, 0.15); - - &:hover, - &--active { - background-color: rgba(0, 211, 169, 0.4); - - &::after { - opacity: 1; - } - } - - &::after { - content: ''; - position: absolute; - left: 1px; - top: 50%; - transform: translateY(-50%); - width: 3px; - height: 50px; - background-color: #00d3a9; - opacity: 0.3; - transition: opacity 0.2s ease; - border-radius: 2px; - box-shadow: 0 0 4px rgba(0, 211, 169, 0.5); - } - } - - &--active { - background-color: rgba(0, 211, 169, 0.5); - } -} - -// Reset button styling -#reset-panel-widths { - white-space: nowrap; - font-size: 0.875rem; - - i { - margin-right: 0.25rem; - } - - &:hover { - background-color: #00d3a9; - color: #fff; - } -} - -// Make panels relatively positioned for absolute handle positioning -.td-sidebar, -.td-sidebar-toc { - position: relative; -} - -// Ensure sidebar nav has proper scrolling with resizable panels -.td-sidebar-nav { - padding-bottom: 2rem; -} - -// Smooth transitions for width changes -.td-sidebar, -.td-sidebar-toc, -main[role="main"] { - transition: flex-basis 0.3s ease; -} - -// Ensure row is flex container for proper layout -.row.flex-xl-nowrap { - display: flex; - flex-wrap: nowrap; - - > aside, - > main { - overflow-x: hidden; - } -} - -// Mobile/Tablet adjustments - disable resize handles on smaller screens -@media (max-width: 1199.98px) { - .resizable-panel-handle { - display: none; - } - - #reset-panel-widths { - display: none; - } -} - -// Print styles - hide resize handles -@media print { - .resizable-panel-handle, - #reset-panel-widths { - display: none !important; - } -} diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 84a003f5632..2b7e6112a37 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -15,7 +15,6 @@ @import "elements_project"; @import "summary.scss"; @import "_kanvas-corner-popup.scss"; -@import "_resizable-panels.scss"; .navbar-dark { min-height: 5rem; @@ -273,6 +272,7 @@ a:not([href]):not([class]):hover { color: $gray-400; font-weight: $font-weight-bold; } + margin-top: 0.5rem; } .td-sidebar-link { diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html deleted file mode 100644 index 409b2634f53..00000000000 --- a/layouts/partials/scripts.html +++ /dev/null @@ -1,16 +0,0 @@ - - -{{ $kanvasTransition := resources.Get "js/kanvas-architectural-transition.js" -}} -{{ if $kanvasTransition }} - -{{ end -}} - -{{ $kanvasPopup := resources.Get "js/kanvas-corner-popup.js" -}} -{{ if $kanvasPopup }} - -{{ end -}} - -{{ $offlineSearch := resources.Get "js/offline-search.js" -}} -{{ if $offlineSearch }} - -{{ end -}} From 36714595c40057c2affdf70027ee52e6ffa07403 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 17:09:16 +0530 Subject: [PATCH 05/16] restore resizable side panels with localStorage persistence Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index e3248d7856d..35dcdc829d4 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -104,7 +104,7 @@ .td-sidebar, .td-sidebar-toc, main[role="main"] { - transition: flex: 0.3s ease; + transition: width 0.3s ease; } // Ensure row is flex container for proper layout From 2e8475c82466d18a1b8c1953b3dbad3db692a659 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 17:19:25 +0530 Subject: [PATCH 06/16] restore resizable side panels - recreate missing JS and template files Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 259 +++++++++++++++++++++++++++++++ assets/scss/_styles_project.scss | 1 + layouts/partials/scripts.html | 16 ++ 3 files changed, 276 insertions(+) create mode 100644 assets/js/resizable-panels.js create mode 100644 layouts/partials/scripts.html diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js new file mode 100644 index 00000000000..388e12c0b7c --- /dev/null +++ b/assets/js/resizable-panels.js @@ -0,0 +1,259 @@ +/** + * Resizable Panels Feature + * Allows users to adjust the width of side panels (left sidebar and right TOC) + * Preferences are saved to localStorage and restored on page load + * Includes reset functionality to restore default widths + */ + +(function() { + 'use strict'; + + const STORAGE_KEY = 'layer5-docs-panel-widths'; + const DEFAULT_WIDTHS = { + sidebar: 2, // col-xl-2 = ~16.66% + toc: 2, // col-xl-2 = ~16.66% + main: 8 // col-xl-8 = ~66.66% + }; + + // CSS class shortcuts for Bootstrap grid columns + const COL_CLASSES = { + 'col-1': 8.33, + 'col-2': 16.66, + 'col-3': 25, + 'col-4': 33.33, + 'col-5': 41.66, + 'col-6': 50, + 'col-7': 58.33, + 'col-8': 66.66, + 'col-9': 75, + 'col-10': 83.33, + 'col-11': 91.66, + 'col-12': 100 + }; + + class ResizablePanels { + constructor() { + this.sidebar = document.querySelector('.td-sidebar'); + this.toc = document.querySelector('.td-sidebar-toc'); + this.main = document.querySelector('main[role="main"]'); + this.row = document.querySelector('.row.flex-xl-nowrap'); + + if (!this.row || !this.sidebar || !this.main) { + console.warn('Resizable panels: Required elements not found'); + return; + } + + this.isResizing = false; + this.currentResizeTarget = null; + this.startX = 0; + this.startWidth = 0; + + this.init(); + } + + init() { + // Load saved widths from localStorage + this.loadSavedWidths(); + + // Create resize handles + this.createResizeHandles(); + + // Add event listeners + this.addEventListeners(); + + // Add reset button + this.addResetButton(); + } + + loadSavedWidths() { + try { + const saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + const widths = JSON.parse(saved); + this.applyWidths(widths); + } + } catch (error) { + console.error('Error loading saved panel widths:', error); + } + } + + createResizeHandles() { + // Create resize handle between sidebar and main content + const sidebarHandle = document.createElement('div'); + sidebarHandle.className = 'resizable-panel-handle resizable-panel-handle--right'; + sidebarHandle.setAttribute('data-resize-target', 'sidebar'); + sidebarHandle.setAttribute('title', 'Drag to resize sidebar'); + this.sidebar.appendChild(sidebarHandle); + + // Create resize handle for TOC (if it exists) + if (this.toc) { + const tocHandle = document.createElement('div'); + tocHandle.className = 'resizable-panel-handle resizable-panel-handle--left'; + tocHandle.setAttribute('data-resize-target', 'toc'); + tocHandle.setAttribute('title', 'Drag to resize table of contents'); + this.toc.appendChild(tocHandle); + } + } + + addEventListeners() { + document.addEventListener('mousedown', (e) => this.onMouseDown(e)); + document.addEventListener('mousemove', (e) => this.onMouseMove(e)); + document.addEventListener('mouseup', (e) => this.onMouseUp(e)); + } + + onMouseDown(e) { + if (!e.target.classList.contains('resizable-panel-handle')) { + return; + } + + this.isResizing = true; + this.currentResizeTarget = e.target.getAttribute('data-resize-target'); + this.startX = e.clientX; + + // Store current widths for delta calculation + this.startWidths = this.getCurrentWidths(); + + // Add active state + e.target.classList.add('resizable-panel-handle--active'); + document.body.style.userSelect = 'none'; + document.body.style.cursor = 'col-resize'; + } + + onMouseMove(e) { + if (!this.isResizing) return; + + const delta = e.clientX - this.startX; + const adjustment = delta / window.innerWidth; // Convert pixels to percentage-like ratio + + let newWidths = { ...this.startWidths }; + + if (this.currentResizeTarget === 'sidebar') { + // Resizing left sidebar + const sidebarPercent = (this.startWidths.sidebar * 100) / 12; // Convert col units to percentage + const mainPercent = (this.startWidths.main * 100) / 12; + + const newSidebarPercent = sidebarPercent + (adjustment * 100); + const newMainPercent = mainPercent - (adjustment * 100); + + // Constrain widths: min 1 col, max 5 cols for sidebar; min 4 cols for main + if (newSidebarPercent >= 8.33 && newSidebarPercent <= 41.66 && newMainPercent >= 33.33) { + newWidths.sidebar = Math.round((newSidebarPercent / 100) * 12); + newWidths.main = Math.round((newMainPercent / 100) * 12); + } + } else if (this.currentResizeTarget === 'toc') { + // Resizing right TOC panel + const tocPercent = (this.startWidths.toc * 100) / 12; + const mainPercent = (this.startWidths.main * 100) / 12; + + const newTocPercent = tocPercent - (adjustment * 100); + const newMainPercent = mainPercent + (adjustment * 100); + + // Constrain widths: min 1 col, max 5 cols for toc; min 4 cols for main + if (newTocPercent >= 8.33 && newTocPercent <= 41.66 && newMainPercent >= 33.33) { + newWidths.toc = Math.round((newTocPercent / 100) * 12); + newWidths.main = Math.round((newMainPercent / 100) * 12); + } + } + + this.applyWidths(newWidths); + } + + onMouseUp(e) { + if (!this.isResizing) return; + + this.isResizing = false; + const handle = document.querySelector('.resizable-panel-handle--active'); + if (handle) { + handle.classList.remove('resizable-panel-handle--active'); + } + + document.body.style.userSelect = ''; + document.body.style.cursor = ''; + + // Save widths to localStorage + this.savePanelWidths(); + } + + applyWidths(widths) { + const { sidebar, toc, main } = widths; + + // Update sidebar + this.removeBootstrapColClasses(this.sidebar); + this.sidebar.classList.add(`col-xl-${sidebar}`); + + // Update main + this.removeBootstrapColClasses(this.main); + this.main.classList.add(`col-xl-${main}`); + + // Update TOC if it exists + if (this.toc) { + this.removeBootstrapColClasses(this.toc); + this.toc.classList.add(`col-xl-${toc}`); + } + } + + getCurrentWidths() { + const getColNumber = (element) => { + const classes = element.className.split(' '); + const colClass = classes.find(c => c.match(/col-xl-\d+/)); + return colClass ? parseInt(colClass.split('-')[2]) : null; + }; + + return { + sidebar: getColNumber(this.sidebar) || DEFAULT_WIDTHS.sidebar, + toc: this.toc ? (getColNumber(this.toc) || DEFAULT_WIDTHS.toc) : DEFAULT_WIDTHS.toc, + main: getColNumber(this.main) || DEFAULT_WIDTHS.main + }; + } + + removeBootstrapColClasses(element) { + const classes = element.className.split(' ').filter(c => !c.match(/col-xl-\d+/)); + element.className = classes.join(' ').trim(); + } + + savePanelWidths() { + try { + const widths = this.getCurrentWidths(); + localStorage.setItem(STORAGE_KEY, JSON.stringify(widths)); + } catch (error) { + console.error('Error saving panel widths:', error); + } + } + + addResetButton() { + // Find the feature-info-container or page-header to add reset button + const pageHeader = document.querySelector('.page-header'); + if (!pageHeader) return; + + const resetButton = document.createElement('button'); + resetButton.id = 'reset-panel-widths'; + resetButton.className = 'btn btn-sm btn-outline-secondary ms-2'; + resetButton.innerHTML = ' Reset Layout'; + resetButton.setAttribute('title', 'Reset panel widths to default'); + + resetButton.addEventListener('click', () => this.resetPanelWidths()); + + // Find a good place to insert the button + const featureContainer = pageHeader.querySelector('.feature-info-container'); + if (featureContainer) { + featureContainer.insertAdjacentElement('beforeend', resetButton); + } else { + pageHeader.insertAdjacentElement('beforeend', resetButton); + } + } + + resetPanelWidths() { + this.applyWidths(DEFAULT_WIDTHS); + this.savePanelWidths(); + } + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new ResizablePanels(); + }); + } else { + new ResizablePanels(); + } +})(); diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 2b7e6112a37..44038655971 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -15,6 +15,7 @@ @import "elements_project"; @import "summary.scss"; @import "_kanvas-corner-popup.scss"; +@import "_resizable-panels.scss"; .navbar-dark { min-height: 5rem; diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html new file mode 100644 index 00000000000..409b2634f53 --- /dev/null +++ b/layouts/partials/scripts.html @@ -0,0 +1,16 @@ + + +{{ $kanvasTransition := resources.Get "js/kanvas-architectural-transition.js" -}} +{{ if $kanvasTransition }} + +{{ end -}} + +{{ $kanvasPopup := resources.Get "js/kanvas-corner-popup.js" -}} +{{ if $kanvasPopup }} + +{{ end -}} + +{{ $offlineSearch := resources.Get "js/offline-search.js" -}} +{{ if $offlineSearch }} + +{{ end -}} From 59647a8a4d9ff169e7ff429c256277fcdee41610 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 19:21:17 +0530 Subject: [PATCH 07/16] Fixed Ui Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 421 ++++++++++++++++------------- assets/scss/_resizable-panels.scss | 200 +++++++------- 2 files changed, 341 insertions(+), 280 deletions(-) diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js index 388e12c0b7c..c8c034d6268 100644 --- a/assets/js/resizable-panels.js +++ b/assets/js/resizable-panels.js @@ -1,259 +1,316 @@ /** - * Resizable Panels Feature - * Allows users to adjust the width of side panels (left sidebar and right TOC) - * Preferences are saved to localStorage and restored on page load - * Includes reset functionality to restore default widths + * Resizable docs panels. + * + * Lets readers adjust the left navigation and right TOC widths, persists those + * preferences in localStorage, and provides a reset control. */ - -(function() { +(function () { 'use strict'; const STORAGE_KEY = 'layer5-docs-panel-widths'; + const DESKTOP_QUERY = '(min-width: 1200px)'; + const STEP = 1; const DEFAULT_WIDTHS = { - sidebar: 2, // col-xl-2 = ~16.66% - toc: 2, // col-xl-2 = ~16.66% - main: 8 // col-xl-8 = ~66.66% + sidebar: 16.6667, + toc: 16.6667, }; - - // CSS class shortcuts for Bootstrap grid columns - const COL_CLASSES = { - 'col-1': 8.33, - 'col-2': 16.66, - 'col-3': 25, - 'col-4': 33.33, - 'col-5': 41.66, - 'col-6': 50, - 'col-7': 58.33, - 'col-8': 66.66, - 'col-9': 75, - 'col-10': 83.33, - 'col-11': 91.66, - 'col-12': 100 + const LIMITS = { + sidebar: { min: 12, max: 32 }, + toc: { min: 10, max: 28 }, + main: { min: 42 }, }; class ResizablePanels { - constructor() { - this.sidebar = document.querySelector('.td-sidebar'); - this.toc = document.querySelector('.td-sidebar-toc'); - this.main = document.querySelector('main[role="main"]'); - this.row = document.querySelector('.row.flex-xl-nowrap'); - - if (!this.row || !this.sidebar || !this.main) { - console.warn('Resizable panels: Required elements not found'); + constructor(row) { + this.row = row; + this.sidebar = row.querySelector('.td-sidebar'); + this.main = row.querySelector('main[role="main"]'); + this.toc = row.querySelector('.td-sidebar-toc'); + this.mediaQuery = window.matchMedia(DESKTOP_QUERY); + this.activeHandle = null; + this.startX = 0; + this.startWidths = null; + this.widths = this.getStoredWidths(); + + if (!this.sidebar || !this.main) { return; } - this.isResizing = false; - this.currentResizeTarget = null; - this.startX = 0; - this.startWidth = 0; - this.init(); } init() { - // Load saved widths from localStorage - this.loadSavedWidths(); - - // Create resize handles - this.createResizeHandles(); - - // Add event listeners - this.addEventListeners(); - - // Add reset button - this.addResetButton(); + this.row.classList.add('resizable-panels-ready'); + this.applyWidths(this.widths); + this.createHandles(); + this.createResetButton(); + this.bindEvents(); } - loadSavedWidths() { - try { - const saved = localStorage.getItem(STORAGE_KEY); - if (saved) { - const widths = JSON.parse(saved); - this.applyWidths(widths); + bindEvents() { + document.addEventListener('pointermove', (event) => + this.onPointerMove(event), + ); + document.addEventListener('pointerup', () => this.stopResize()); + document.addEventListener('pointercancel', () => this.stopResize()); + + this.mediaQuery.addEventListener('change', () => { + if (this.mediaQuery.matches) { + this.applyWidths(this.widths); } - } catch (error) { - console.error('Error loading saved panel widths:', error); - } + }); } - createResizeHandles() { - // Create resize handle between sidebar and main content - const sidebarHandle = document.createElement('div'); - sidebarHandle.className = 'resizable-panel-handle resizable-panel-handle--right'; - sidebarHandle.setAttribute('data-resize-target', 'sidebar'); - sidebarHandle.setAttribute('title', 'Drag to resize sidebar'); - this.sidebar.appendChild(sidebarHandle); + createHandles() { + this.sidebarHandle = this.createHandle( + 'sidebar', + 'Resize navigation sidebar', + ); + this.sidebar.appendChild(this.sidebarHandle); - // Create resize handle for TOC (if it exists) if (this.toc) { - const tocHandle = document.createElement('div'); - tocHandle.className = 'resizable-panel-handle resizable-panel-handle--left'; - tocHandle.setAttribute('data-resize-target', 'toc'); - tocHandle.setAttribute('title', 'Drag to resize table of contents'); - this.toc.appendChild(tocHandle); + this.tocHandle = this.createHandle('toc', 'Resize table of contents'); + this.toc.appendChild(this.tocHandle); } } - addEventListeners() { - document.addEventListener('mousedown', (e) => this.onMouseDown(e)); - document.addEventListener('mousemove', (e) => this.onMouseMove(e)); - document.addEventListener('mouseup', (e) => this.onMouseUp(e)); + createHandle(target, label) { + const handle = document.createElement('div'); + handle.className = `resizable-panel-handle resizable-panel-handle--${target}`; + handle.dataset.resizeTarget = target; + handle.tabIndex = 0; + handle.setAttribute('aria-label', label); + handle.setAttribute('aria-orientation', 'vertical'); + handle.setAttribute('role', 'separator'); + handle.title = label; + + handle.addEventListener('pointerdown', (event) => + this.startResize(event, handle), + ); + handle.addEventListener('keydown', (event) => + this.onHandleKeydown(event, target), + ); + + return handle; } - onMouseDown(e) { - if (!e.target.classList.contains('resizable-panel-handle')) { - return; - } + createResetButton() { + const resetButton = document.createElement('button'); + resetButton.type = 'button'; + resetButton.id = 'reset-panel-widths'; + resetButton.className = 'resizable-panel-reset'; + resetButton.innerHTML = + 'Reset layout'; + resetButton.title = 'Reset panel widths to default'; + resetButton.addEventListener('click', () => this.reset()); - this.isResizing = true; - this.currentResizeTarget = e.target.getAttribute('data-resize-target'); - this.startX = e.clientX; + this.sidebar.appendChild(resetButton); + } - // Store current widths for delta calculation - this.startWidths = this.getCurrentWidths(); + startResize(event, handle) { + if (!this.mediaQuery.matches) { + return; + } - // Add active state - e.target.classList.add('resizable-panel-handle--active'); - document.body.style.userSelect = 'none'; - document.body.style.cursor = 'col-resize'; + event.preventDefault(); + this.activeHandle = handle; + this.startX = event.clientX; + this.startWidths = { ...this.widths }; + handle.classList.add('resizable-panel-handle--active'); + handle.setPointerCapture(event.pointerId); + document.body.classList.add('resizable-panels-dragging'); } - onMouseMove(e) { - if (!this.isResizing) return; + onPointerMove(event) { + if (!this.activeHandle || !this.startWidths) { + return; + } + + const rowWidth = this.row.getBoundingClientRect().width; + if (!rowWidth) { + return; + } - const delta = e.clientX - this.startX; - const adjustment = delta / window.innerWidth; // Convert pixels to percentage-like ratio + const target = this.activeHandle.dataset.resizeTarget; + const delta = ((event.clientX - this.startX) / rowWidth) * 100; + const nextWidths = { ...this.startWidths }; - let newWidths = { ...this.startWidths }; + if (target === 'sidebar') { + nextWidths.sidebar = this.startWidths.sidebar + delta; + } - if (this.currentResizeTarget === 'sidebar') { - // Resizing left sidebar - const sidebarPercent = (this.startWidths.sidebar * 100) / 12; // Convert col units to percentage - const mainPercent = (this.startWidths.main * 100) / 12; + if (target === 'toc') { + nextWidths.toc = this.startWidths.toc - delta; + } - const newSidebarPercent = sidebarPercent + (adjustment * 100); - const newMainPercent = mainPercent - (adjustment * 100); + this.widths = this.normalizeWidths(nextWidths); + this.applyWidths(this.widths); + } - // Constrain widths: min 1 col, max 5 cols for sidebar; min 4 cols for main - if (newSidebarPercent >= 8.33 && newSidebarPercent <= 41.66 && newMainPercent >= 33.33) { - newWidths.sidebar = Math.round((newSidebarPercent / 100) * 12); - newWidths.main = Math.round((newMainPercent / 100) * 12); - } - } else if (this.currentResizeTarget === 'toc') { - // Resizing right TOC panel - const tocPercent = (this.startWidths.toc * 100) / 12; - const mainPercent = (this.startWidths.main * 100) / 12; - - const newTocPercent = tocPercent - (adjustment * 100); - const newMainPercent = mainPercent + (adjustment * 100); - - // Constrain widths: min 1 col, max 5 cols for toc; min 4 cols for main - if (newTocPercent >= 8.33 && newTocPercent <= 41.66 && newMainPercent >= 33.33) { - newWidths.toc = Math.round((newTocPercent / 100) * 12); - newWidths.main = Math.round((newMainPercent / 100) * 12); - } + stopResize() { + if (!this.activeHandle) { + return; } - this.applyWidths(newWidths); + this.activeHandle.classList.remove('resizable-panel-handle--active'); + this.activeHandle = null; + this.startWidths = null; + document.body.classList.remove('resizable-panels-dragging'); + this.saveWidths(); } - onMouseUp(e) { - if (!this.isResizing) return; + onHandleKeydown(event, target) { + if (!this.mediaQuery.matches) { + return; + } - this.isResizing = false; - const handle = document.querySelector('.resizable-panel-handle--active'); - if (handle) { - handle.classList.remove('resizable-panel-handle--active'); + const keys = ['ArrowLeft', 'ArrowRight', 'Home', 'End']; + if (!keys.includes(event.key)) { + return; } - document.body.style.userSelect = ''; - document.body.style.cursor = ''; + event.preventDefault(); + const nextWidths = { ...this.widths }; + const direction = event.key === 'ArrowRight' ? 1 : -1; + + if (event.key === 'Home') { + nextWidths[target] = LIMITS[target].min; + } else if (event.key === 'End') { + nextWidths[target] = LIMITS[target].max; + } else if (target === 'toc') { + nextWidths.toc -= direction * STEP; + } else { + nextWidths.sidebar += direction * STEP; + } - // Save widths to localStorage - this.savePanelWidths(); + this.widths = this.normalizeWidths(nextWidths); + this.applyWidths(this.widths); + this.saveWidths(); } applyWidths(widths) { - const { sidebar, toc, main } = widths; - - // Update sidebar - this.removeBootstrapColClasses(this.sidebar); - this.sidebar.classList.add(`col-xl-${sidebar}`); + const normalized = this.normalizeWidths(widths); + const mainWidth = 100 - normalized.sidebar - normalized.toc; + + this.row.style.setProperty( + '--docs-sidebar-width', + `${normalized.sidebar}%`, + ); + this.row.style.setProperty('--docs-toc-width', `${normalized.toc}%`); + this.row.style.setProperty('--docs-main-width', `${mainWidth}%`); + this.updateHandleValues(normalized); + } - // Update main - this.removeBootstrapColClasses(this.main); - this.main.classList.add(`col-xl-${main}`); + updateHandleValues(widths) { + if (this.sidebarHandle) { + this.sidebarHandle.setAttribute('aria-valuemin', LIMITS.sidebar.min); + this.sidebarHandle.setAttribute('aria-valuemax', LIMITS.sidebar.max); + this.sidebarHandle.setAttribute( + 'aria-valuenow', + Math.round(widths.sidebar), + ); + } - // Update TOC if it exists - if (this.toc) { - this.removeBootstrapColClasses(this.toc); - this.toc.classList.add(`col-xl-${toc}`); + if (this.tocHandle) { + this.tocHandle.setAttribute('aria-valuemin', LIMITS.toc.min); + this.tocHandle.setAttribute('aria-valuemax', LIMITS.toc.max); + this.tocHandle.setAttribute('aria-valuenow', Math.round(widths.toc)); } } - getCurrentWidths() { - const getColNumber = (element) => { - const classes = element.className.split(' '); - const colClass = classes.find(c => c.match(/col-xl-\d+/)); - return colClass ? parseInt(colClass.split('-')[2]) : null; - }; - - return { - sidebar: getColNumber(this.sidebar) || DEFAULT_WIDTHS.sidebar, - toc: this.toc ? (getColNumber(this.toc) || DEFAULT_WIDTHS.toc) : DEFAULT_WIDTHS.toc, - main: getColNumber(this.main) || DEFAULT_WIDTHS.main - }; + reset() { + this.widths = { ...DEFAULT_WIDTHS }; + this.applyWidths(this.widths); + localStorage.removeItem(STORAGE_KEY); } - removeBootstrapColClasses(element) { - const classes = element.className.split(' ').filter(c => !c.match(/col-xl-\d+/)); - element.className = classes.join(' ').trim(); + getStoredWidths() { + try { + const saved = JSON.parse(localStorage.getItem(STORAGE_KEY)); + if ( + saved && + (saved.sidebar <= 12 || saved.toc <= 12 || saved.main <= 12) + ) { + return this.normalizeWidths({ + sidebar: saved.sidebar + ? (saved.sidebar / 12) * 100 + : DEFAULT_WIDTHS.sidebar, + toc: saved.toc ? (saved.toc / 12) * 100 : DEFAULT_WIDTHS.toc, + }); + } + + return this.normalizeWidths(saved || DEFAULT_WIDTHS); + } catch (error) { + return { ...DEFAULT_WIDTHS }; + } } - savePanelWidths() { + saveWidths() { try { - const widths = this.getCurrentWidths(); - localStorage.setItem(STORAGE_KEY, JSON.stringify(widths)); + localStorage.setItem(STORAGE_KEY, JSON.stringify(this.widths)); } catch (error) { - console.error('Error saving panel widths:', error); + // Ignore storage failures so resizing still works in private modes. } } - addResetButton() { - // Find the feature-info-container or page-header to add reset button - const pageHeader = document.querySelector('.page-header'); - if (!pageHeader) return; + normalizeWidths(widths) { + const next = { + sidebar: this.clamp( + Number(widths && widths.sidebar), + LIMITS.sidebar.min, + LIMITS.sidebar.max, + DEFAULT_WIDTHS.sidebar, + ), + toc: this.toc + ? this.clamp( + Number(widths && widths.toc), + LIMITS.toc.min, + LIMITS.toc.max, + DEFAULT_WIDTHS.toc, + ) + : 0, + }; - const resetButton = document.createElement('button'); - resetButton.id = 'reset-panel-widths'; - resetButton.className = 'btn btn-sm btn-outline-secondary ms-2'; - resetButton.innerHTML = ' Reset Layout'; - resetButton.setAttribute('title', 'Reset panel widths to default'); + const availableForPanels = 100 - LIMITS.main.min; + const panelTotal = next.sidebar + next.toc; - resetButton.addEventListener('click', () => this.resetPanelWidths()); + if (panelTotal > availableForPanels) { + const overflow = panelTotal - availableForPanels; - // Find a good place to insert the button - const featureContainer = pageHeader.querySelector('.feature-info-container'); - if (featureContainer) { - featureContainer.insertAdjacentElement('beforeend', resetButton); - } else { - pageHeader.insertAdjacentElement('beforeend', resetButton); + if (next.sidebar >= next.toc) { + next.sidebar = Math.max(LIMITS.sidebar.min, next.sidebar - overflow); + } else { + next.toc = Math.max(LIMITS.toc.min, next.toc - overflow); + } } + + return { + sidebar: Number(next.sidebar.toFixed(4)), + toc: Number(next.toc.toFixed(4)), + }; } - resetPanelWidths() { - this.applyWidths(DEFAULT_WIDTHS); - this.savePanelWidths(); + clamp(value, min, max, fallback) { + if (!Number.isFinite(value)) { + return fallback; + } + + return Math.min(max, Math.max(min, value)); } } - // Initialize when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => { - new ResizablePanels(); + function initResizablePanels() { + document.querySelectorAll('.row.flex-xl-nowrap').forEach((row) => { + if (!row.dataset.resizablePanelsInitialized) { + row.dataset.resizablePanelsInitialized = 'true'; + new ResizablePanels(row); + } }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initResizablePanels); } else { - new ResizablePanels(); + initResizablePanels(); } })(); diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 35dcdc829d4..b4fcca95820 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -1,138 +1,142 @@ /** - * Resizable Panels Styling - * Styles for the resize handles and drag interactions + * Resizable docs side panels. + * + * Desktop widths are driven by CSS variables set from resizable-panels.js. + * Smaller breakpoints keep the existing Bootstrap column behavior. */ -// Resize handle styling -.resizable-panel-handle { - position: absolute; - background-color: transparent; - transition: background-color 0.2s ease; - z-index: 100; +.resizable-panel-handle, +.resizable-panel-reset { + display: none; +} - &--right { - // Right edge handle (between sidebar and main) - right: -3px; - top: 0; - bottom: 0; - width: 6px; - cursor: col-resize; +@media (min-width: 1200px) { + .row.flex-xl-nowrap.resizable-panels-ready { + --docs-sidebar-width: 16.6667%; + --docs-main-width: 66.6666%; + --docs-toc-width: 16.6667%; - &:hover, - &--active { - background-color: rgba(0, 211, 169, 0.3); + display: flex; + flex-wrap: nowrap; + } - &::after { - opacity: 1; - } - } + .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar { + flex: 0 0 var(--docs-sidebar-width); + max-width: var(--docs-sidebar-width); + order: 1; + } + + .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { + flex: 0 0 var(--docs-main-width); + max-width: var(--docs-main-width); + min-width: 0; + order: 2; + } + + .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { + flex: 0 0 var(--docs-toc-width); + max-width: var(--docs-toc-width); + order: 3; + } + + .td-sidebar, + .td-sidebar-toc { + position: sticky; + } + + .resizable-panel-handle { + appearance: none; + background: transparent; + border: 0; + bottom: 0; + cursor: col-resize; + display: block; + padding: 0; + position: absolute; + top: 0; + width: 12px; + z-index: 20; &::after { + background-color: #00d3a9; + border-radius: 1px; content: ''; + height: 44px; + opacity: 0; position: absolute; - right: 0; top: 50%; transform: translateY(-50%); + transition: opacity 0.16s ease; width: 2px; - height: 40px; - background-color: #00d3a9; - opacity: 0; - transition: opacity 0.2s ease; - border-radius: 1px; } - } - - &--left { - // Left edge handle (between main and TOC) - left: -3px; - top: 0; - bottom: 0; - width: 6px; - cursor: col-resize; &:hover, + &:focus-visible, &--active { - background-color: rgba(0, 211, 169, 0.3); + background-color: rgba(0, 211, 169, 0.16); + outline: none; &::after { opacity: 1; } } - &::after { - content: ''; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - width: 2px; - height: 40px; - background-color: #00d3a9; - opacity: 0; - transition: opacity 0.2s ease; - border-radius: 1px; - } - } + &--sidebar { + right: -6px; - &--active { - background-color: rgba(0, 211, 169, 0.5); - } -} + &::after { + right: 5px; + } + } -// Reset button styling -#reset-panel-widths { - white-space: nowrap; - font-size: 0.875rem; + &--toc { + left: -6px; - i { - margin-right: 0.25rem; - } - - &:hover { - background-color: #00d3a9; - color: #fff; + &::after { + left: 5px; + } + } } -} - -// Make panels relatively positioned for absolute handle positioning -.td-sidebar, -.td-sidebar-toc { - position: relative; -} - -// Smooth transitions for width changes -.td-sidebar, -.td-sidebar-toc, -main[role="main"] { - transition: width 0.3s ease; -} -// Ensure row is flex container for proper layout -.row.flex-xl-nowrap { - display: flex; - flex-wrap: nowrap; + .resizable-panel-reset { + align-items: center; + background: rgba(0, 0, 0, 0.25); + border: 1px solid rgba(0, 211, 169, 0.45); + border-radius: 4px; + color: #ccc; + cursor: pointer; + display: inline-flex; + font-size: 0.8125rem; + gap: 0.35rem; + line-height: 1.2; + margin: 1rem 0 0; + padding: 0.35rem 0.55rem; + white-space: nowrap; - > aside, - > main { - overflow-x: hidden; + &:hover, + &:focus-visible { + background: #00d3a9; + border-color: #00d3a9; + color: #000; + outline: none; + } } -} -// Mobile/Tablet adjustments - disable resize handles on smaller screens -@media (max-width: 1199.98px) { - .resizable-panel-handle { - display: none; - } + .resizable-panels-dragging { + cursor: col-resize; + user-select: none; - #reset-panel-widths { - display: none; + iframe, + img, + a { + pointer-events: none; + } } } -// Print styles - hide resize handles @media print { .resizable-panel-handle, - #reset-panel-widths { + .resizable-panel-reset { display: none !important; } } From ed5d256c9a67e52dc37cf43e83c6598b98c9d271 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 20:01:41 +0530 Subject: [PATCH 08/16] Fix resizable panel handle loading Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 16 ++++++++-------- layouts/partials/scripts.html | 6 +++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index b4fcca95820..06e7ac03995 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -46,7 +46,7 @@ .resizable-panel-handle { appearance: none; - background: transparent; + background: rgba(0, 211, 169, 0.12); border: 0; bottom: 0; cursor: col-resize; @@ -54,15 +54,15 @@ padding: 0; position: absolute; top: 0; - width: 12px; - z-index: 20; + width: 14px; + z-index: 50; &::after { background-color: #00d3a9; border-radius: 1px; content: ''; height: 44px; - opacity: 0; + opacity: 0.75; position: absolute; top: 50%; transform: translateY(-50%); @@ -82,18 +82,18 @@ } &--sidebar { - right: -6px; + right: 0; &::after { - right: 5px; + right: 6px; } } &--toc { - left: -6px; + left: 0; &::after { - left: 5px; + left: 6px; } } } diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index 409b2634f53..7d764480d74 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -1,5 +1,9 @@ - +{{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} +{{ if $resizablePanels }} + +{{ end -}} + {{ $kanvasTransition := resources.Get "js/kanvas-architectural-transition.js" -}} {{ if $kanvasTransition }} From c260d3642bb40156da12c357e406c044587c974e Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 21:06:16 +0530 Subject: [PATCH 09/16] Fix sidebar resize handle visibility Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 18 +++++++++++---- assets/scss/_resizable-panels.scss | 35 ++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js index c8c034d6268..c35f71463ba 100644 --- a/assets/js/resizable-panels.js +++ b/assets/js/resizable-panels.js @@ -8,7 +8,7 @@ 'use strict'; const STORAGE_KEY = 'layer5-docs-panel-widths'; - const DESKTOP_QUERY = '(min-width: 1200px)'; + const RESIZABLE_QUERY = '(min-width: 768px)'; const STEP = 1; const DEFAULT_WIDTHS = { sidebar: 16.6667, @@ -26,7 +26,7 @@ this.sidebar = row.querySelector('.td-sidebar'); this.main = row.querySelector('main[role="main"]'); this.toc = row.querySelector('.td-sidebar-toc'); - this.mediaQuery = window.matchMedia(DESKTOP_QUERY); + this.mediaQuery = window.matchMedia(RESIZABLE_QUERY); this.activeHandle = null; this.startX = 0; this.startWidths = null; @@ -54,11 +54,17 @@ document.addEventListener('pointerup', () => this.stopResize()); document.addEventListener('pointercancel', () => this.stopResize()); - this.mediaQuery.addEventListener('change', () => { + const onBreakpointChange = () => { if (this.mediaQuery.matches) { this.applyWidths(this.widths); } - }); + }; + + if (this.mediaQuery.addEventListener) { + this.mediaQuery.addEventListener('change', onBreakpointChange); + } else { + this.mediaQuery.addListener(onBreakpointChange); + } } createHandles() { @@ -198,6 +204,10 @@ ); this.row.style.setProperty('--docs-toc-width', `${normalized.toc}%`); this.row.style.setProperty('--docs-main-width', `${mainWidth}%`); + this.row.style.setProperty( + '--docs-main-without-toc-width', + `${100 - normalized.sidebar}%`, + ); this.updateHandleValues(normalized); } diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 06e7ac03995..5035493b12b 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -1,8 +1,8 @@ /** * Resizable docs side panels. * - * Desktop widths are driven by CSS variables set from resizable-panels.js. - * Smaller breakpoints keep the existing Bootstrap column behavior. + * Docs widths are driven by CSS variables set from resizable-panels.js. + * Mobile keeps the existing Bootstrap column behavior. */ .resizable-panel-handle, @@ -10,10 +10,11 @@ display: none; } -@media (min-width: 1200px) { +@media (min-width: 768px) { .row.flex-xl-nowrap.resizable-panels-ready { --docs-sidebar-width: 16.6667%; --docs-main-width: 66.6666%; + --docs-main-without-toc-width: 83.3333%; --docs-toc-width: 16.6667%; display: flex; @@ -27,18 +28,12 @@ } .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { - flex: 0 0 var(--docs-main-width); - max-width: var(--docs-main-width); + flex: 0 0 var(--docs-main-without-toc-width); + max-width: var(--docs-main-without-toc-width); min-width: 0; order: 2; } - .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { - flex: 0 0 var(--docs-toc-width); - max-width: var(--docs-toc-width); - order: 3; - } - .td-sidebar, .td-sidebar-toc { position: sticky; @@ -90,6 +85,7 @@ } &--toc { + display: none; left: 0; &::after { @@ -134,6 +130,23 @@ } } +@media (min-width: 1200px) { + .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { + flex-basis: var(--docs-main-width); + max-width: var(--docs-main-width); + } + + .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { + flex: 0 0 var(--docs-toc-width); + max-width: var(--docs-toc-width); + order: 3; + } + + .resizable-panel-handle--toc { + display: block; + } +} + @media print { .resizable-panel-handle, .resizable-panel-reset { From 5fd3cc81da5620b595b53227b8aba2a375ab9d4e Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 22:46:43 +0530 Subject: [PATCH 10/16] Fixed Ui Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 39 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 5035493b12b..7d58fb23047 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -41,7 +41,7 @@ .resizable-panel-handle { appearance: none; - background: rgba(0, 211, 169, 0.12); + background: transparent; border: 0; bottom: 0; cursor: col-resize; @@ -49,28 +49,41 @@ padding: 0; position: absolute; top: 0; - width: 14px; + width: 12px; z-index: 50; + &::before { + background-color: rgba(0, 211, 169, 0.22); + bottom: 0; + content: ''; + opacity: 0; + position: absolute; + top: 0; + transition: opacity 0.16s ease; + width: 1px; + } + &::after { background-color: #00d3a9; - border-radius: 1px; + border-radius: 999px; + box-shadow: 0 0 0 3px rgba(0, 211, 169, 0.12); content: ''; - height: 44px; - opacity: 0.75; + height: 56px; + opacity: 0.45; position: absolute; top: 50%; transform: translateY(-50%); transition: opacity 0.16s ease; - width: 2px; + width: 4px; } &:hover, &:focus-visible, &--active { - background-color: rgba(0, 211, 169, 0.16); + background: rgba(0, 211, 169, 0.06); outline: none; + &::before, &::after { opacity: 1; } @@ -79,8 +92,12 @@ &--sidebar { right: 0; + &::before { + right: 0; + } + &::after { - right: 6px; + right: 4px; } } @@ -88,8 +105,12 @@ display: none; left: 0; + &::before { + left: 0; + } + &::after { - left: 6px; + left: 4px; } } } From 617019ff1827a53f46368731666a2605fe15cbdb Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 22:59:01 +0530 Subject: [PATCH 11/16] Clarify sidebar resize handle UI Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 68 ++++++++++++------------------ 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 7d58fb23047..3fc8834e3db 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -37,42 +37,36 @@ .td-sidebar, .td-sidebar-toc { position: sticky; + scrollbar-color: rgba(255, 255, 255, 0.28) transparent; } .resizable-panel-handle { appearance: none; - background: transparent; - border: 0; - bottom: 0; + background: rgba(4, 10, 10, 0.92); + border: 1px solid rgba(0, 211, 169, 0.55); + border-radius: 999px; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.28); cursor: col-resize; display: block; + height: 64px; padding: 0; position: absolute; - top: 0; - width: 12px; + top: 50%; + transform: translateY(-50%); + width: 18px; z-index: 50; - &::before { - background-color: rgba(0, 211, 169, 0.22); - bottom: 0; - content: ''; - opacity: 0; - position: absolute; - top: 0; - transition: opacity 0.16s ease; - width: 1px; - } - &::after { - background-color: #00d3a9; + background-image: radial-gradient(circle, #00d3a9 1.5px, transparent 2px); + background-size: 4px 8px; border-radius: 999px; - box-shadow: 0 0 0 3px rgba(0, 211, 169, 0.12); content: ''; - height: 56px; - opacity: 0.45; + height: 30px; + left: 50%; + opacity: 0.8; position: absolute; top: 50%; - transform: translateY(-50%); + transform: translate(-50%, -50%); transition: opacity 0.16s ease; width: 4px; } @@ -80,39 +74,33 @@ &:hover, &:focus-visible, &--active { - background: rgba(0, 211, 169, 0.06); + background: rgba(0, 38, 34, 0.96); + border-color: #00d3a9; outline: none; - &::before, &::after { opacity: 1; } } &--sidebar { - right: 0; - - &::before { - right: 0; - } - - &::after { - right: 4px; - } + right: 10px; } &--toc { display: none; - left: 0; + left: 10px; + } + } - &::before { - left: 0; - } + .td-sidebar::-webkit-scrollbar-thumb, + .td-sidebar-toc::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.28); + } - &::after { - left: 4px; - } - } + .td-sidebar::-webkit-scrollbar-thumb:hover, + .td-sidebar-toc::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.42); } .resizable-panel-reset { From c2eb6d25ab694b23c81338d8fa96c0fa9ab81755 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 23:07:22 +0530 Subject: [PATCH 12/16] Clarify sidebar resize handle UI Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 196 +++++++++++++++++++---------- 1 file changed, 128 insertions(+), 68 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 3fc8834e3db..04f8c591a6c 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -19,90 +19,140 @@ display: flex; flex-wrap: nowrap; + width: 100%; } + /** + * LEFT SIDEBAR + */ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar { flex: 0 0 var(--docs-sidebar-width); max-width: var(--docs-sidebar-width); + min-width: 220px; + position: relative; order: 1; } + /** + * MAIN CONTENT + */ .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { - flex: 0 0 var(--docs-main-without-toc-width); + flex: 1 1 auto; max-width: var(--docs-main-without-toc-width); min-width: 0; + overflow-x: hidden; + position: relative; order: 2; } + /** + * TOC SIDEBAR + */ + .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { + position: relative; + } + + /** + * EXISTING SIDEBAR STYLING + */ .td-sidebar, .td-sidebar-toc { position: sticky; - scrollbar-color: rgba(255, 255, 255, 0.28) transparent; + scrollbar-color: rgba(255, 255, 255, 0.3) transparent; } + /** + * RESIZE HANDLE + */ .resizable-panel-handle { appearance: none; - background: rgba(4, 10, 10, 0.92); - border: 1px solid rgba(0, 211, 169, 0.55); - border-radius: 999px; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.28); + background: transparent; + border: 0; cursor: col-resize; display: block; - height: 64px; padding: 0; position: absolute; + top: 0; + bottom: 0; + width: 4px; + z-index: 20; + } + + /** + * HANDLE VISUAL INDICATOR + */ + .resizable-panel-handle::after { + content: ''; + position: absolute; top: 50%; transform: translateY(-50%); - width: 18px; - z-index: 50; - - &::after { - background-image: radial-gradient(circle, #00d3a9 1.5px, transparent 2px); - background-size: 4px 8px; - border-radius: 999px; - content: ''; - height: 30px; - left: 50%; - opacity: 0.8; - position: absolute; - top: 50%; - transform: translate(-50%, -50%); - transition: opacity 0.16s ease; - width: 4px; - } - - &:hover, - &:focus-visible, - &--active { - background: rgba(0, 38, 34, 0.96); - border-color: #00d3a9; - outline: none; - - &::after { - opacity: 1; - } - } - - &--sidebar { - right: 10px; - } - - &--toc { - display: none; - left: 10px; - } - } - - .td-sidebar::-webkit-scrollbar-thumb, - .td-sidebar-toc::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.28); - } - - .td-sidebar::-webkit-scrollbar-thumb:hover, - .td-sidebar-toc::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.42); + width: 2px; + height: 48px; + border-radius: 999px; + background-color: rgba(0, 211, 169, 0.9); + opacity: 0; + transition: opacity 0.16s ease; + } + + /** + * SHOW HANDLE ON INTERACTION + */ + .resizable-panel-handle:hover::after, + .resizable-panel-handle:focus-visible::after, + .resizable-panel-handle--active::after { + opacity: 1; + } + + /** + * LEFT SIDEBAR HANDLE + */ + .resizable-panel-handle--sidebar { + right: 0; + } + + .resizable-panel-handle--sidebar::after { + right: 1px; + } + + /** + * TOC HANDLE + */ + .resizable-panel-handle--toc { + display: none; + left: 0; + } + + .resizable-panel-handle--toc::after { + left: 1px; + } + + /** + * CUSTOM SCROLLBARS + */ + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar::-webkit-scrollbar, + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar-toc::-webkit-scrollbar { + width: 8px; + } + + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar::-webkit-scrollbar-thumb, + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar-toc::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3) !important; + } + + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar::-webkit-scrollbar-thumb:hover, + .row.flex-xl-nowrap.resizable-panels-ready + > .td-sidebar-toc::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.45) !important; } + /** + * RESET BUTTON + */ .resizable-panel-reset { align-items: center; background: rgba(0, 0, 0, 0.25); @@ -117,28 +167,34 @@ margin: 1rem 0 0; padding: 0.35rem 0.55rem; white-space: nowrap; + } - &:hover, - &:focus-visible { - background: #00d3a9; - border-color: #00d3a9; - color: #000; - outline: none; - } + .resizable-panel-reset:hover, + .resizable-panel-reset:focus-visible { + background: #00d3a9; + border-color: #00d3a9; + color: #000; + outline: none; } + /** + * DRAGGING STATE + */ .resizable-panels-dragging { cursor: col-resize; user-select: none; + } - iframe, - img, - a { - pointer-events: none; - } + .resizable-panels-dragging iframe, + .resizable-panels-dragging img, + .resizable-panels-dragging a { + pointer-events: none; } } +/** + * LARGE SCREENS + */ @media (min-width: 1200px) { .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { flex-basis: var(--docs-main-width); @@ -148,6 +204,7 @@ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { flex: 0 0 var(--docs-toc-width); max-width: var(--docs-toc-width); + min-width: 220px; order: 3; } @@ -156,9 +213,12 @@ } } +/** + * PRINT MODE + */ @media print { .resizable-panel-handle, .resizable-panel-reset { display: none !important; } -} +} \ No newline at end of file From 8ca58aecd43d7c6113ba1d0b659387aa45765ca6 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Thu, 21 May 2026 23:17:30 +0530 Subject: [PATCH 13/16] fix(docs): refine resizable panel layout and prevent content clipping Signed-off-by: Rudra2637 --- assets/scss/_resizable-panels.scss | 69 +++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 04f8c591a6c..9c8044cf161 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -20,6 +20,7 @@ display: flex; flex-wrap: nowrap; width: 100%; + min-width: 0; } /** @@ -27,9 +28,12 @@ */ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar { flex: 0 0 var(--docs-sidebar-width); - max-width: var(--docs-sidebar-width); - min-width: 220px; + width: var(--docs-sidebar-width); + max-width: 420px; + min-width: 240px; position: relative; + overflow-x: hidden; + padding-right: 12px; order: 1; } @@ -38,18 +42,32 @@ */ .row.flex-xl-nowrap.resizable-panels-ready > main[role='main'] { flex: 1 1 auto; + flex-basis: var(--docs-main-without-toc-width); max-width: var(--docs-main-without-toc-width); - min-width: 0; - overflow-x: hidden; + min-width: 500px; + overflow-x: auto; position: relative; order: 2; } + /** + * Better readable content width + */ + main[role='main'] article { + max-width: 900px; + } + /** * TOC SIDEBAR */ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { + width: var(--docs-toc-width); + max-width: 320px; + min-width: 220px; position: relative; + overflow-x: hidden; + padding-left: 12px; + order: 3; } /** @@ -61,6 +79,21 @@ scrollbar-color: rgba(255, 255, 255, 0.3) transparent; } + /** + * Prevent text clipping + */ + .td-sidebar nav, + .td-sidebar-toc nav { + overflow-wrap: break-word; + word-break: break-word; + } + + .td-sidebar a, + .td-sidebar-toc a { + display: block; + max-width: 100%; + } + /** * RESIZE HANDLE */ @@ -79,7 +112,19 @@ } /** - * HANDLE VISUAL INDICATOR + * Subtle divider line + */ + .resizable-panel-handle::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: rgba(255, 255, 255, 0.08); + } + + /** + * Handle visual indicator */ .resizable-panel-handle::after { content: ''; @@ -87,9 +132,9 @@ top: 50%; transform: translateY(-50%); width: 2px; - height: 48px; + height: 32px; border-radius: 999px; - background-color: rgba(0, 211, 169, 0.9); + background-color: rgba(0, 211, 169, 0.55); opacity: 0; transition: opacity 0.16s ease; } @@ -110,6 +155,7 @@ right: 0; } + .resizable-panel-handle--sidebar::before, .resizable-panel-handle--sidebar::after { right: 1px; } @@ -122,6 +168,7 @@ left: 0; } + .resizable-panel-handle--toc::before, .resizable-panel-handle--toc::after { left: 1px; } @@ -141,6 +188,7 @@ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.3) !important; + border-radius: 999px; } .row.flex-xl-nowrap.resizable-panels-ready @@ -155,6 +203,8 @@ */ .resizable-panel-reset { align-items: center; + justify-content: center; + width: 100%; background: rgba(0, 0, 0, 0.25); border: 1px solid rgba(0, 211, 169, 0.45); border-radius: 4px; @@ -165,7 +215,7 @@ gap: 0.35rem; line-height: 1.2; margin: 1rem 0 0; - padding: 0.35rem 0.55rem; + padding: 0.45rem 0.65rem; white-space: nowrap; } @@ -203,9 +253,6 @@ .row.flex-xl-nowrap.resizable-panels-ready > .td-sidebar-toc { flex: 0 0 var(--docs-toc-width); - max-width: var(--docs-toc-width); - min-width: 220px; - order: 3; } .resizable-panel-handle--toc { From 2a31d5873b5f7d10d07339aa65f2878714c40871 Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Wed, 27 May 2026 00:35:57 +0530 Subject: [PATCH 14/16] Address resizable panels review feedback Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 253 +++++++++++++++++----------------- layouts/partials/scripts.html | 27 ++-- 2 files changed, 139 insertions(+), 141 deletions(-) diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js index c35f71463ba..415294ffb72 100644 --- a/assets/js/resizable-panels.js +++ b/assets/js/resizable-panels.js @@ -19,68 +19,59 @@ toc: { min: 10, max: 28 }, main: { min: 42 }, }; - - class ResizablePanels { - constructor(row) { - this.row = row; - this.sidebar = row.querySelector('.td-sidebar'); - this.main = row.querySelector('main[role="main"]'); - this.toc = row.querySelector('.td-sidebar-toc'); - this.mediaQuery = window.matchMedia(RESIZABLE_QUERY); - this.activeHandle = null; - this.startX = 0; - this.startWidths = null; - this.widths = this.getStoredWidths(); - - if (!this.sidebar || !this.main) { - return; - } - - this.init(); + const LEGACY_GRID_COLUMNS = 12; + + function setupResizablePanels(row) { + const sidebar = row.querySelector('.td-sidebar'); + const main = row.querySelector('main[role="main"]'); + const toc = row.querySelector('.td-sidebar-toc'); + const mediaQuery = window.matchMedia(RESIZABLE_QUERY); + let activeHandle = null; + let sidebarHandle = null; + let tocHandle = null; + let startX = 0; + let startWidths = null; + let widths = getStoredWidths(); + + if (!sidebar || !main) { + return; } - init() { - this.row.classList.add('resizable-panels-ready'); - this.applyWidths(this.widths); - this.createHandles(); - this.createResetButton(); - this.bindEvents(); - } + row.classList.add('resizable-panels-ready'); + applyWidths(widths); + createHandles(); + createResetButton(); + bindEvents(); - bindEvents() { - document.addEventListener('pointermove', (event) => - this.onPointerMove(event), - ); - document.addEventListener('pointerup', () => this.stopResize()); - document.addEventListener('pointercancel', () => this.stopResize()); + function bindEvents() { + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', stopResize); + document.addEventListener('pointercancel', stopResize); const onBreakpointChange = () => { - if (this.mediaQuery.matches) { - this.applyWidths(this.widths); + if (mediaQuery.matches) { + applyWidths(widths); } }; - if (this.mediaQuery.addEventListener) { - this.mediaQuery.addEventListener('change', onBreakpointChange); + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', onBreakpointChange); } else { - this.mediaQuery.addListener(onBreakpointChange); + mediaQuery.addListener(onBreakpointChange); } } - createHandles() { - this.sidebarHandle = this.createHandle( - 'sidebar', - 'Resize navigation sidebar', - ); - this.sidebar.appendChild(this.sidebarHandle); + function createHandles() { + sidebarHandle = createHandle('sidebar', 'Resize navigation sidebar'); + sidebar.appendChild(sidebarHandle); - if (this.toc) { - this.tocHandle = this.createHandle('toc', 'Resize table of contents'); - this.toc.appendChild(this.tocHandle); + if (toc) { + tocHandle = createHandle('toc', 'Resize table of contents'); + toc.appendChild(tocHandle); } } - createHandle(target, label) { + function createHandle(target, label) { const handle = document.createElement('div'); handle.className = `resizable-panel-handle resizable-panel-handle--${target}`; handle.dataset.resizeTarget = target; @@ -90,17 +81,17 @@ handle.setAttribute('role', 'separator'); handle.title = label; - handle.addEventListener('pointerdown', (event) => - this.startResize(event, handle), - ); - handle.addEventListener('keydown', (event) => - this.onHandleKeydown(event, target), - ); + handle.addEventListener('pointerdown', (event) => { + startResize(event, handle); + }); + handle.addEventListener('keydown', (event) => { + onHandleKeydown(event, target); + }); return handle; } - createResetButton() { + function createResetButton() { const resetButton = document.createElement('button'); resetButton.type = 'button'; resetButton.id = 'reset-panel-widths'; @@ -108,65 +99,65 @@ resetButton.innerHTML = 'Reset layout'; resetButton.title = 'Reset panel widths to default'; - resetButton.addEventListener('click', () => this.reset()); + resetButton.addEventListener('click', reset); - this.sidebar.appendChild(resetButton); + sidebar.appendChild(resetButton); } - startResize(event, handle) { - if (!this.mediaQuery.matches) { + function startResize(event, handle) { + if (!mediaQuery.matches) { return; } event.preventDefault(); - this.activeHandle = handle; - this.startX = event.clientX; - this.startWidths = { ...this.widths }; + activeHandle = handle; + startX = event.clientX; + startWidths = { ...widths }; handle.classList.add('resizable-panel-handle--active'); handle.setPointerCapture(event.pointerId); document.body.classList.add('resizable-panels-dragging'); } - onPointerMove(event) { - if (!this.activeHandle || !this.startWidths) { + function onPointerMove(event) { + if (!activeHandle || !startWidths) { return; } - const rowWidth = this.row.getBoundingClientRect().width; + const rowWidth = row.getBoundingClientRect().width; if (!rowWidth) { return; } - const target = this.activeHandle.dataset.resizeTarget; - const delta = ((event.clientX - this.startX) / rowWidth) * 100; - const nextWidths = { ...this.startWidths }; + const target = activeHandle.dataset.resizeTarget; + const delta = ((event.clientX - startX) / rowWidth) * 100; + const nextWidths = { ...startWidths }; if (target === 'sidebar') { - nextWidths.sidebar = this.startWidths.sidebar + delta; + nextWidths.sidebar = startWidths.sidebar + delta; } if (target === 'toc') { - nextWidths.toc = this.startWidths.toc - delta; + nextWidths.toc = startWidths.toc - delta; } - this.widths = this.normalizeWidths(nextWidths); - this.applyWidths(this.widths); + widths = normalizeWidths(nextWidths); + applyWidths(widths); } - stopResize() { - if (!this.activeHandle) { + function stopResize() { + if (!activeHandle) { return; } - this.activeHandle.classList.remove('resizable-panel-handle--active'); - this.activeHandle = null; - this.startWidths = null; + activeHandle.classList.remove('resizable-panel-handle--active'); + activeHandle = null; + startWidths = null; document.body.classList.remove('resizable-panels-dragging'); - this.saveWidths(); + saveWidths(); } - onHandleKeydown(event, target) { - if (!this.mediaQuery.matches) { + function onHandleKeydown(event, target) { + if (!mediaQuery.matches) { return; } @@ -176,7 +167,7 @@ } event.preventDefault(); - const nextWidths = { ...this.widths }; + const nextWidths = { ...widths }; const direction = event.key === 'ArrowRight' ? 1 : -1; if (event.key === 'Home') { @@ -189,91 +180,105 @@ nextWidths.sidebar += direction * STEP; } - this.widths = this.normalizeWidths(nextWidths); - this.applyWidths(this.widths); - this.saveWidths(); + widths = normalizeWidths(nextWidths); + applyWidths(widths); + saveWidths(); } - applyWidths(widths) { - const normalized = this.normalizeWidths(widths); + function applyWidths(nextWidths) { + const normalized = normalizeWidths(nextWidths); const mainWidth = 100 - normalized.sidebar - normalized.toc; - this.row.style.setProperty( - '--docs-sidebar-width', - `${normalized.sidebar}%`, - ); - this.row.style.setProperty('--docs-toc-width', `${normalized.toc}%`); - this.row.style.setProperty('--docs-main-width', `${mainWidth}%`); - this.row.style.setProperty( + row.style.setProperty('--docs-sidebar-width', `${normalized.sidebar}%`); + row.style.setProperty('--docs-toc-width', `${normalized.toc}%`); + row.style.setProperty('--docs-main-width', `${mainWidth}%`); + row.style.setProperty( '--docs-main-without-toc-width', `${100 - normalized.sidebar}%`, ); - this.updateHandleValues(normalized); + updateHandleValues(normalized); } - updateHandleValues(widths) { - if (this.sidebarHandle) { - this.sidebarHandle.setAttribute('aria-valuemin', LIMITS.sidebar.min); - this.sidebarHandle.setAttribute('aria-valuemax', LIMITS.sidebar.max); - this.sidebarHandle.setAttribute( + function updateHandleValues(nextWidths) { + if (sidebarHandle) { + sidebarHandle.setAttribute('aria-valuemin', LIMITS.sidebar.min); + sidebarHandle.setAttribute('aria-valuemax', LIMITS.sidebar.max); + sidebarHandle.setAttribute( 'aria-valuenow', - Math.round(widths.sidebar), + Math.round(nextWidths.sidebar), ); } - if (this.tocHandle) { - this.tocHandle.setAttribute('aria-valuemin', LIMITS.toc.min); - this.tocHandle.setAttribute('aria-valuemax', LIMITS.toc.max); - this.tocHandle.setAttribute('aria-valuenow', Math.round(widths.toc)); + if (tocHandle) { + tocHandle.setAttribute('aria-valuemin', LIMITS.toc.min); + tocHandle.setAttribute('aria-valuemax', LIMITS.toc.max); + tocHandle.setAttribute('aria-valuenow', Math.round(nextWidths.toc)); } } - reset() { - this.widths = { ...DEFAULT_WIDTHS }; - this.applyWidths(this.widths); + function reset() { + widths = { ...DEFAULT_WIDTHS }; + applyWidths(widths); localStorage.removeItem(STORAGE_KEY); } - getStoredWidths() { + function getStoredWidths() { try { const saved = JSON.parse(localStorage.getItem(STORAGE_KEY)); - if ( - saved && - (saved.sidebar <= 12 || saved.toc <= 12 || saved.main <= 12) - ) { - return this.normalizeWidths({ + + if (isLegacyColumnWidths(saved)) { + return normalizeWidths({ sidebar: saved.sidebar - ? (saved.sidebar / 12) * 100 + ? (saved.sidebar / LEGACY_GRID_COLUMNS) * 100 : DEFAULT_WIDTHS.sidebar, - toc: saved.toc ? (saved.toc / 12) * 100 : DEFAULT_WIDTHS.toc, + toc: saved.toc + ? (saved.toc / LEGACY_GRID_COLUMNS) * 100 + : DEFAULT_WIDTHS.toc, }); } - return this.normalizeWidths(saved || DEFAULT_WIDTHS); + return normalizeWidths(saved || DEFAULT_WIDTHS); } catch (error) { return { ...DEFAULT_WIDTHS }; } } - saveWidths() { + function isLegacyColumnWidths(saved) { + if (!saved) { + return false; + } + + return ['sidebar', 'toc', 'main'].some((key) => { + const value = Number(saved[key]); + const limit = LIMITS[key]; + + if (!Number.isFinite(value) || !limit) { + return false; + } + + return value < limit.min || (limit.max && value > limit.max); + }); + } + + function saveWidths() { try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(this.widths)); + localStorage.setItem(STORAGE_KEY, JSON.stringify(widths)); } catch (error) { // Ignore storage failures so resizing still works in private modes. } } - normalizeWidths(widths) { + function normalizeWidths(nextWidths) { const next = { - sidebar: this.clamp( - Number(widths && widths.sidebar), + sidebar: clamp( + Number(nextWidths && nextWidths.sidebar), LIMITS.sidebar.min, LIMITS.sidebar.max, DEFAULT_WIDTHS.sidebar, ), - toc: this.toc - ? this.clamp( - Number(widths && widths.toc), + toc: toc + ? clamp( + Number(nextWidths && nextWidths.toc), LIMITS.toc.min, LIMITS.toc.max, DEFAULT_WIDTHS.toc, @@ -300,7 +305,7 @@ }; } - clamp(value, min, max, fallback) { + function clamp(value, min, max, fallback) { if (!Number.isFinite(value)) { return fallback; } @@ -313,7 +318,7 @@ document.querySelectorAll('.row.flex-xl-nowrap').forEach((row) => { if (!row.dataset.resizablePanelsInitialized) { row.dataset.resizablePanelsInitialized = 'true'; - new ResizablePanels(row); + setupResizablePanels(row); } }); } diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index 7d764480d74..09d5183fe7f 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -1,20 +1,13 @@ -{{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} -{{ if $resizablePanels }} - -{{ end -}} - -{{ $kanvasTransition := resources.Get "js/kanvas-architectural-transition.js" -}} -{{ if $kanvasTransition }} - -{{ end -}} - -{{ $kanvasPopup := resources.Get "js/kanvas-corner-popup.js" -}} -{{ if $kanvasPopup }} - -{{ end -}} +{{ $scripts := slice + "js/resizable-panels.js" + "js/kanvas-architectural-transition.js" + "js/kanvas-corner-popup.js" + "js/offline-search.js" +-}} -{{ $offlineSearch := resources.Get "js/offline-search.js" -}} -{{ if $offlineSearch }} - +{{ range $scripts -}} + {{ with resources.Get . -}} + + {{ end -}} {{ end -}} From da33a19035255bf69dcc55f506acd58ff49a590c Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Wed, 10 Jun 2026 20:01:41 +0530 Subject: [PATCH 15/16] Updated pr as per feedback Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 23 +++++++++++++---------- layouts/404.html | 4 ++++ layouts/docs/baseof.html | 4 ++++ layouts/partials/scripts.html | 13 ------------- layouts/release/baseof.html | 7 ++++++- layouts/video/baseof.html | 7 ++++++- 6 files changed, 33 insertions(+), 25 deletions(-) delete mode 100644 layouts/partials/scripts.html diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js index 415294ffb72..2db8e30d878 100644 --- a/assets/js/resizable-panels.js +++ b/assets/js/resizable-panels.js @@ -44,10 +44,6 @@ bindEvents(); function bindEvents() { - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', stopResize); - document.addEventListener('pointercancel', stopResize); - const onBreakpointChange = () => { if (mediaQuery.matches) { applyWidths(widths); @@ -116,6 +112,10 @@ handle.classList.add('resizable-panel-handle--active'); handle.setPointerCapture(event.pointerId); document.body.classList.add('resizable-panels-dragging'); + + document.addEventListener('pointermove', onPointerMove); + document.addEventListener('pointerup', stopResize); + document.addEventListener('pointercancel', stopResize); } function onPointerMove(event) { @@ -149,6 +149,10 @@ return; } + document.removeEventListener('pointermove', onPointerMove); + document.removeEventListener('pointerup', stopResize); + document.removeEventListener('pointercancel', stopResize); + activeHandle.classList.remove('resizable-panel-handle--active'); activeHandle = null; startWidths = null; @@ -315,12 +319,11 @@ } function initResizablePanels() { - document.querySelectorAll('.row.flex-xl-nowrap').forEach((row) => { - if (!row.dataset.resizablePanelsInitialized) { - row.dataset.resizablePanelsInitialized = 'true'; - setupResizablePanels(row); - } - }); + const row = document.querySelector('.row.flex-xl-nowrap'); + if (row && !row.dataset.resizablePanelsInitialized) { + row.dataset.resizablePanelsInitialized = 'true'; + setupResizablePanels(row); + } } if (document.readyState === 'loading') { diff --git a/layouts/404.html b/layouts/404.html index 410580771b7..979e06a2151 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -32,6 +32,10 @@

Not found

{{ partial "footer.html" . }} {{ partial "scripts.html" . }} + {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} + {{ if $resizablePanels }} + + {{ end -}} diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html index 3503963fb79..313190ddac6 100644 --- a/layouts/docs/baseof.html +++ b/layouts/docs/baseof.html @@ -37,6 +37,10 @@ {{ partial "footer.html" . }} {{ partial "scripts.html" . }} + {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} + {{ if $resizablePanels }} + + {{ end -}} {{ partial "image-modal.html" . }} diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html deleted file mode 100644 index 09d5183fe7f..00000000000 --- a/layouts/partials/scripts.html +++ /dev/null @@ -1,13 +0,0 @@ - -{{ $scripts := slice - "js/resizable-panels.js" - "js/kanvas-architectural-transition.js" - "js/kanvas-corner-popup.js" - "js/offline-search.js" --}} - -{{ range $scripts -}} - {{ with resources.Get . -}} - - {{ end -}} -{{ end -}} diff --git a/layouts/release/baseof.html b/layouts/release/baseof.html index 2145f978f24..40bfdb639c4 100644 --- a/layouts/release/baseof.html +++ b/layouts/release/baseof.html @@ -32,6 +32,11 @@ {{ partial "footer.html" . }} - {{ partial "scripts.html" . }} {{ partial "image-modal.html" . }} + {{ partial "scripts.html" . }} + {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} + {{ if $resizablePanels }} + + {{ end -}} + {{ partial "image-modal.html" . }} \ No newline at end of file diff --git a/layouts/video/baseof.html b/layouts/video/baseof.html index 395dec3e888..79318e65226 100644 --- a/layouts/video/baseof.html +++ b/layouts/video/baseof.html @@ -32,6 +32,11 @@ {{ partial "footer.html" . }} - {{ partial "scripts.html" . }} {{ partial "image-modal.html" . }} + {{ partial "scripts.html" . }} + {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} + {{ if $resizablePanels }} + + {{ end -}} + {{ partial "image-modal.html" . }} From 875a062ab56e0be80cce9a3fd6352f4e563ae9de Mon Sep 17 00:00:00 2001 From: Rudra2637 Date: Sat, 13 Jun 2026 19:32:57 +0530 Subject: [PATCH 16/16] Added the fix Signed-off-by: Rudra2637 --- assets/js/resizable-panels.js | 31 ++-- assets/scss/_resizable-panels.scss | 60 +------- assets/scss/_styles_project.scss | 230 +++++++++++++++++++---------- layouts/404.html | 5 +- layouts/docs/baseof.html | 5 +- layouts/release/baseof.html | 5 +- layouts/video/baseof.html | 5 +- 7 files changed, 183 insertions(+), 158 deletions(-) diff --git a/assets/js/resizable-panels.js b/assets/js/resizable-panels.js index 2db8e30d878..7acd175e661 100644 --- a/assets/js/resizable-panels.js +++ b/assets/js/resizable-panels.js @@ -90,7 +90,6 @@ function createResetButton() { const resetButton = document.createElement('button'); resetButton.type = 'button'; - resetButton.id = 'reset-panel-widths'; resetButton.className = 'resizable-panel-reset'; resetButton.innerHTML = 'Reset layout'; @@ -113,9 +112,17 @@ handle.setPointerCapture(event.pointerId); document.body.classList.add('resizable-panels-dragging'); - document.addEventListener('pointermove', onPointerMove); - document.addEventListener('pointerup', stopResize); - document.addEventListener('pointercancel', stopResize); + const controller = new AbortController(); + const { signal } = controller; + + const endResize = () => { + stopResize(); + controller.abort(); + }; + + document.addEventListener('pointermove', onPointerMove, { signal }); + document.addEventListener('pointerup', endResize, { signal }); + document.addEventListener('pointercancel', endResize, { signal }); } function onPointerMove(event) { @@ -149,10 +156,6 @@ return; } - document.removeEventListener('pointermove', onPointerMove); - document.removeEventListener('pointerup', stopResize); - document.removeEventListener('pointercancel', stopResize); - activeHandle.classList.remove('resizable-panel-handle--active'); activeHandle = null; startWidths = null; @@ -282,11 +285,11 @@ ), toc: toc ? clamp( - Number(nextWidths && nextWidths.toc), - LIMITS.toc.min, - LIMITS.toc.max, - DEFAULT_WIDTHS.toc, - ) + Number(nextWidths && nextWidths.toc), + LIMITS.toc.min, + LIMITS.toc.max, + DEFAULT_WIDTHS.toc, + ) : 0, }; @@ -331,4 +334,4 @@ } else { initResizablePanels(); } -})(); +})(); \ No newline at end of file diff --git a/assets/scss/_resizable-panels.scss b/assets/scss/_resizable-panels.scss index 9c8044cf161..68c67c6747b 100644 --- a/assets/scss/_resizable-panels.scss +++ b/assets/scss/_resizable-panels.scss @@ -32,7 +32,6 @@ max-width: 420px; min-width: 240px; position: relative; - overflow-x: hidden; padding-right: 12px; order: 1; } @@ -50,13 +49,6 @@ order: 2; } - /** - * Better readable content width - */ - main[role='main'] article { - max-width: 900px; - } - /** * TOC SIDEBAR */ @@ -65,35 +57,10 @@ max-width: 320px; min-width: 220px; position: relative; - overflow-x: hidden; padding-left: 12px; order: 3; } - /** - * EXISTING SIDEBAR STYLING - */ - .td-sidebar, - .td-sidebar-toc { - position: sticky; - scrollbar-color: rgba(255, 255, 255, 0.3) transparent; - } - - /** - * Prevent text clipping - */ - .td-sidebar nav, - .td-sidebar-toc nav { - overflow-wrap: break-word; - word-break: break-word; - } - - .td-sidebar a, - .td-sidebar-toc a { - display: block; - max-width: 100%; - } - /** * RESIZE HANDLE */ @@ -173,31 +140,6 @@ left: 1px; } - /** - * CUSTOM SCROLLBARS - */ - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar::-webkit-scrollbar, - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar-toc::-webkit-scrollbar { - width: 8px; - } - - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar::-webkit-scrollbar-thumb, - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar-toc::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3) !important; - border-radius: 999px; - } - - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar::-webkit-scrollbar-thumb:hover, - .row.flex-xl-nowrap.resizable-panels-ready - > .td-sidebar-toc::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.45) !important; - } - /** * RESET BUTTON */ @@ -268,4 +210,4 @@ .resizable-panel-reset { display: none !important; } -} \ No newline at end of file +} diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 44038655971..7be6edc1115 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -1,21 +1,21 @@ -@import "fonts_project.scss"; -@import "content_project.scss"; -@import "image-modal_project.scss"; -@import "elements_project.scss"; -@import "footer_project.scss"; -@import "variables_project.scss"; -@import "landing_project.scss"; -@import "_navbar_project.scss"; -@import "tax_project.scss"; -@import "search_project.scss"; -@import "_videos_project.scss"; -@import "subscription.scss"; -@import "rest-api-reference.scss"; -@import "_video-landing_project.scss"; -@import "elements_project"; -@import "summary.scss"; -@import "_kanvas-corner-popup.scss"; -@import "_resizable-panels.scss"; +@import 'fonts_project.scss'; +@import 'content_project.scss'; +@import 'image-modal_project.scss'; +@import 'elements_project.scss'; +@import 'footer_project.scss'; +@import 'variables_project.scss'; +@import 'landing_project.scss'; +@import '_navbar_project.scss'; +@import 'tax_project.scss'; +@import 'search_project.scss'; +@import '_videos_project.scss'; +@import 'subscription.scss'; +@import 'rest-api-reference.scss'; +@import '_video-landing_project.scss'; +@import 'elements_project'; +@import 'summary.scss'; +@import '_kanvas-corner-popup.scss'; +@import '_resizable-panels.scss'; .navbar-dark { min-height: 5rem; @@ -45,10 +45,13 @@ .nav-link.active { border-width: 1px; border-style: solid; - border-image: linear-gradient(to bottom, + border-image: linear-gradient( + to bottom, rgba($dark, 0.2) 30%, rgba($primary, 0.3) 60%, - $primary 90% 100%) 1; + $primary 90% 100% + ) + 1; padding-bottom: 0.3rem; align-items: center; justify-content: center; @@ -82,6 +85,10 @@ padding-left: 1rem; padding-right: 1rem; } + + article { + max-width: 900px; + } } body { @@ -97,7 +104,7 @@ body { // Inline code p code, - li>code, + li > code, table code { color: inherit; padding: 0.2em 0.4em; @@ -118,7 +125,7 @@ body { background-color: $gray-900; padding: $spacer; - >code { + > code { background-color: inherit !important; padding: 0; margin: 0; @@ -144,7 +151,7 @@ body { overflow-x: hidden; } .td-content .card img { - width: 100% ; + width: 100%; } // Links @@ -175,7 +182,6 @@ a:not([href]):not([class]):hover { // .taxonomy-terms-cloud .taxonomy-term { - border-width: 0; border-radius: 0 3px 3px 0; display: inline-block; @@ -190,13 +196,10 @@ a:not([href]):not([class]):hover { transition: color 0.2s; clip-path: polygon(100% 0, 100% 100%, 0.8em 100%, 0 50%, 0.8em 0); - &:hover { background-color: $primary; color: $white; - } - } .article-teaser { @@ -225,12 +228,14 @@ a:not([href]):not([class]):hover { } .td-sidebar { - background-image: linear-gradient(to top, - #1e2117, - #1d1912, - #18120e, - #0f0a09, - #000000); + background-image: linear-gradient( + to top, + #1e2117, + #1d1912, + #18120e, + #0f0a09, + #000000 + ); position: sticky; height: calc(100vh - 5.5rem); top: 5.5rem; @@ -238,6 +243,31 @@ a:not([href]):not([class]):hover { padding-top: 1rem; margin-top: 0; overflow-x: hidden; + scrollbar-color: rgba(255, 255, 255, 0.3) transparent; + + nav { + overflow-wrap: break-word; + word-break: break-word; + } + + a { + display: block; + max-width: 100%; + } + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3) !important; + border-radius: 999px; + + &:hover { + background: rgba(255, 255, 255, 0.45) !important; + } + } + &__inner { @include media-breakpoint-up(md) { @supports (position: sticky) { @@ -285,17 +315,17 @@ a:not([href]):not([class]):hover { &__section { margin-top: 1rem; font-family: - "Qanelas Soft", + 'Qanelas Soft', sans-serif, -apple-system, BlinkMacSystemFont, - "Segoe UI", + 'Segoe UI', Roboto, - "Helvetica Neue", + 'Helvetica Neue', Arial, - "Apple Color Emoji", - "Segoe UI Emoji", - "Segoe UI Symbol"; + 'Apple Color Emoji', + 'Segoe UI Emoji', + 'Segoe UI Symbol'; } } @@ -308,14 +338,20 @@ a:not([href]):not([class]):hover { &.active:not(.tree-root) { border-width: 1px; border-style: solid; - border-image: linear-gradient(to left, + border-image: linear-gradient( + to left, rgba($dark, 0) 30%, rgba($primary, 0.3) 60%, - $primary 90% 100%) 1; - background-image: linear-gradient(to left, + $primary 90% 100% + ) + 1; + background-image: linear-gradient( + to left, rgba($dark, 0.2) 30%, rgba($primary, 0.3) 60%, - $primary 90% 100%) 1; + $primary 90% 100% + ) + 1; padding: 0.25rem; padding-left: 0.5rem !important; // background-image: linear-gradient(to left, rgba($dark,.33),rgba($dark,.5),rgba($dark,.75),#1e2117, #31412b, #3b6447, #378b6d, #00b39f); @@ -338,12 +374,38 @@ a:not([href]):not([class]):hover { margin-top: 0rem; line-height: 1.25rem; border-left: 1px solid $border-color; - background-image: linear-gradient(to top, - #1e2117, - #1d1912, - #18120e, - #0f0a09, - #000000); + background-image: linear-gradient( + to top, + #1e2117, + #1d1912, + #18120e, + #0f0a09, + #000000 + ); + scrollbar-color: rgba(255, 255, 255, 0.3) transparent; + + nav { + overflow-wrap: break-word; + word-break: break-word; + } + + a { + display: block; + max-width: 100%; + } + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3) !important; + border-radius: 999px; + + &:hover { + background: rgba(255, 255, 255, 0.45) !important; + } + } @supports (position: sticky) { position: sticky; @@ -384,7 +446,9 @@ a:not([href]):not([class]):hover { color: $lightslategray; padding: 0.25rem 0.5rem; border-radius: 4px; - transition: background-color 0.2s ease, color 0.2s ease; + transition: + background-color 0.2s ease, + color 0.2s ease; &:hover, &:focus-visible { @@ -400,7 +464,7 @@ a:not([href]):not([class]):hover { .pageinfo { font-weight: $font-weight-medium; background: $black; - font-family: "Open Sans"; + font-family: 'Open Sans'; border-style: solid; border-color: #00b39f; } @@ -409,7 +473,7 @@ a:not([href]):not([class]):hover { .matterinfo { font-weight: $font-weight-medium; background: $black; - font-family: "Qanelas Soft"; + font-family: 'Qanelas Soft'; border-color: #00b39f; border-radius: 10px; } @@ -425,13 +489,13 @@ a:not([href]):not([class]):hover { } .matterinfo .plan-support { - padding: .5rem; - border: .5px solid $border-color; + padding: 0.5rem; + border: 0.5px solid $border-color; display: flex; align-items: center; color: $casper; &:hover { - background-color: rgba($primary, 0.3) + background-color: rgba($primary, 0.3); } } @@ -451,7 +515,6 @@ a:not([href]):not([class]):hover { color: $casper; } - // Style alert boxes. .alert { @@ -478,12 +541,12 @@ a:not([href]):not([class]):hover { transition: color 0.8s; transition: background-color 0.8s; - >img { + > img { width: 2rem; margin-right: 0.5rem; } - >img:hover { + > img:hover { filter: brightness(0) invert(1); } @@ -506,7 +569,7 @@ a:not([href]):not([class]):hover { .td-content figure > img { width: auto; max-width: 100%; - border-radius: .5rem .5rem 0 0; + border-radius: 0.5rem 0.5rem 0 0; box-shadow: 0 0 0.5rem rgba(0, 179, 159, 0.4); transition: box-shadow 0.2s; } @@ -517,7 +580,7 @@ a:not([href]):not([class]):hover { padding: 0.5rem 1rem; background-color: rgba($lightslategray, 1); color: $dark; - border-radius: 0 0 .5rem .5rem; + border-radius: 0 0 0.5rem 0.5rem; box-sizing: border-box; } @@ -526,8 +589,13 @@ a:not([href]):not([class]):hover { margin-bottom: 4rem; font-size: 5rem; text-align: left; - background-image: - linear-gradient(to right, rgba(255, 255, 255, .9) 0px, rgb(0, 211, 169) 34%, rgb(235, 192, 23) 71%, rgb(255,243,197) 100%); + background-image: linear-gradient( + to right, + rgba(255, 255, 255, 0.9) 0px, + rgb(0, 211, 169) 34%, + rgb(235, 192, 23) 71%, + rgb(255, 243, 197) 100% + ); background-position: 0% 0%, 0% 0%; @@ -556,16 +624,17 @@ a:not([href]):not([class]):hover { } } - .dash-tangle { width: 78.14231rem; height: 74.72rem; transform: rotate(-55.68deg); flex-shrink: 0; overflow: hidden; - background-image: linear-gradient(180deg, - rgba(0, 179, 115, 0) 0%, - rgba(0, 179, 159, 0.3) 100%); + background-image: linear-gradient( + 180deg, + rgba(0, 179, 115, 0) 0%, + rgba(0, 179, 159, 0.3) 100% + ); position: absolute; top: -18rem; right: -32rem; @@ -594,13 +663,15 @@ a:not([href]):not([class]):hover { left: -24rem; overflow: hidden; - >.dash-ircle { + > .dash-ircle { width: 74.125rem; height: 74.125rem; flex-shrink: 0; - background: radial-gradient(50% 50% at 50% 50%, - rgba(0, 179, 159, 0.2) 0%, - rgba(0, 179, 159, 0) 100%); + background: radial-gradient( + 50% 50% at 50% 50%, + rgba(0, 179, 159, 0.2) 0%, + rgba(0, 179, 159, 0) 100% + ); position: absolute; overflow: hidden; background-clip: border-box; @@ -622,11 +693,11 @@ a:not([href]):not([class]):hover { .dash-sign-container { display: flex; align-items: center; - justify-content: space-between; + justify-content: space-between; text-align: right; h1.dashboard { - max-width: 70%; + max-width: 70%; text-align: left; flex-shrink: 0; @media (max-width: 768px) { @@ -662,7 +733,7 @@ a:not([href]):not([class]):hover { transform: translateX(0%) translateY(0%); &:hover { - color: #EBC017; + color: #ebc017; } @media (max-width: 768px) { @@ -672,7 +743,7 @@ a:not([href]):not([class]):hover { a.dash-sign:before, a.dash-sign:after { - content: ""; + content: ''; padding: 0.9em 0.4em; position: absolute; left: 50%; @@ -747,8 +818,8 @@ a:not([href]):not([class]):hover { display: flex; flex-wrap: nowrap; overflow-x: auto; - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ } .nav-tabs::-webkit-scrollbar { @@ -817,7 +888,12 @@ html { } } -h1, h2, h3, h4, h5, h6 { +h1, +h2, +h3, +h4, +h5, +h6 { scroll-margin-top: 1rem; } diff --git a/layouts/404.html b/layouts/404.html index 979e06a2151..a51a4bd2228 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -1,5 +1,5 @@ - + {{ partial "head.html" . }} @@ -34,7 +34,8 @@

Not found

{{ partial "scripts.html" . }} {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} {{ if $resizablePanels }} - + {{ $resizablePanels = $resizablePanels | minify | fingerprint -}} + {{ end -}} diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html index 313190ddac6..c2e7829c51b 100644 --- a/layouts/docs/baseof.html +++ b/layouts/docs/baseof.html @@ -1,5 +1,5 @@ - + {{ partial "head.html" . }} @@ -39,7 +39,8 @@ {{ partial "scripts.html" . }} {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} {{ if $resizablePanels }} - + {{ $resizablePanels = $resizablePanels | minify | fingerprint -}} + {{ end -}} {{ partial "image-modal.html" . }} diff --git a/layouts/release/baseof.html b/layouts/release/baseof.html index 40bfdb639c4..b7750a7b53b 100644 --- a/layouts/release/baseof.html +++ b/layouts/release/baseof.html @@ -2,7 +2,7 @@ @@ -35,7 +35,8 @@ {{ partial "scripts.html" . }} {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} {{ if $resizablePanels }} - + {{ $resizablePanels = $resizablePanels | minify | fingerprint -}} + {{ end -}} {{ partial "image-modal.html" . }} diff --git a/layouts/video/baseof.html b/layouts/video/baseof.html index 79318e65226..9e859b35451 100644 --- a/layouts/video/baseof.html +++ b/layouts/video/baseof.html @@ -2,7 +2,7 @@ @@ -35,7 +35,8 @@ {{ partial "scripts.html" . }} {{ $resizablePanels := resources.Get "js/resizable-panels.js" -}} {{ if $resizablePanels }} - + {{ $resizablePanels = $resizablePanels | minify | fingerprint -}} + {{ end -}} {{ partial "image-modal.html" . }}