diff --git a/assets/js/sidebar-load-width.js b/assets/js/sidebar-load-width.js new file mode 100644 index 00000000000..698c03e62e4 --- /dev/null +++ b/assets/js/sidebar-load-width.js @@ -0,0 +1,18 @@ +(function () { + try { + var leftWidth = parseInt(localStorage.getItem("left-sidebar-width"), 10); + var tocWidth = parseInt(localStorage.getItem("toc-width"), 10); + var cssStyles = ""; + + if (!isNaN(leftWidth)) cssStyles += "--left-sidebar-width: " + leftWidth + "px;"; + if (!isNaN(tocWidth)) cssStyles += "--toc-width: " + tocWidth + "px;"; + + if (cssStyles) { + var styleTag = document.createElement("style"); + styleTag.innerHTML = ".td-resizable-grid .td-main > .row.flex-xl-nowrap {" + cssStyles + "}"; + document.head.appendChild(styleTag); + } + } catch (e) { + console.warn("LocalStorage blocked or unavailable:", e); + } +})(); \ No newline at end of file diff --git a/assets/js/sidebar-resizer.js b/assets/js/sidebar-resizer.js new file mode 100644 index 00000000000..fd36d0103d0 --- /dev/null +++ b/assets/js/sidebar-resizer.js @@ -0,0 +1,115 @@ +(function () { + document.addEventListener("DOMContentLoaded", () => { + const gridContainer = document.querySelector(".td-resizable-grid .td-main > .row.flex-xl-nowrap"); + const leftResizer = document.getElementById("left-resizer"); + const rightResizer = document.getElementById("right-resizer"); + + if (!gridContainer) return; + + const LEFT_SIDEBAR_KEY = "left-sidebar-width"; + const TOC_KEY = "toc-width"; + + const storage = { + get: (key) => { + try { return localStorage.getItem(key); } + catch (e) { return null; } + }, + set: (key, value) => { + try { localStorage.setItem(key, value); } + catch (e) { return null; } + } + }; + + function setupResizer(resizer, type) { + resizer.addEventListener("pointerdown", (e) => { + e.preventDefault(); + + resizer.setPointerCapture(e.pointerId); + resizer.classList.add("is-dragging"); + + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + + const startX = e.clientX; + const styles = window.getComputedStyle(gridContainer); + const gridColumns = styles.gridTemplateColumns ? styles.gridTemplateColumns.split(" ") : []; + + /* + grid-template-columns over in _styles_project.scss has the following structure: + + min(768px) + --left-sidebar-width --sidebar-resize-width 1fr + + min(1200px) + --left-sidebar-width --sidebar-resize-width 1fr --sidebar-resize-width --toc-width; + + so gridcolumns[0] targets the left sidebar and gridcolumns[4] targets the toc + */ + + let initialLeftSidebarWidth, initialTocWidth; + if (gridColumns.length >= 5 && gridColumns[0] !== "none") { + initialLeftSidebarWidth = parseInt(gridColumns[0], 10); + initialTocWidth = parseInt(gridColumns[4], 10); + } else { + initialLeftSidebarWidth = parseInt(styles.getPropertyValue('--left-sidebar-width'), 10); + initialTocWidth = parseInt(styles.getPropertyValue('--toc-width'), 10); + } + + const minWidth = parseInt(styles.getPropertyValue(type === "left" ? '--left-sidebar-min-width' : '--toc-min-width'), 10); + + const maxWidth = parseInt(styles.getPropertyValue(type === "left" ? '--left-sidebar-max-width' : '--toc-max-width'), 10); + + let currentWidth = type === "left" ? initialLeftSidebarWidth : initialTocWidth; + let ticking = false; + let animationFrameId = null; + + function onPointerMove(moveEvent) { + const deltaX = moveEvent.clientX - startX; + + if (type === "left") { + currentWidth = Math.max(minWidth, Math.min(maxWidth, initialLeftSidebarWidth + deltaX)); + } else if (type === "right") { + currentWidth = Math.max(minWidth, Math.min(maxWidth, initialTocWidth - deltaX)); + } + + if (!ticking) { + animationFrameId = window.requestAnimationFrame(() => { + const varName = type === "left" ? "--left-sidebar-width" : "--toc-width"; + gridContainer.style.setProperty(varName, `${currentWidth}px`); + ticking = false; + }); + ticking = true; + } + } + + function onPointerUp(upEvent) { + if (animationFrameId) { + window.cancelAnimationFrame(animationFrameId); + animationFrameId = null; + ticking = false; + } + + resizer.releasePointerCapture(upEvent.pointerId); + resizer.classList.remove("is-dragging"); + + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + + const storageKey = type === "left" ? LEFT_SIDEBAR_KEY : TOC_KEY; + storage.set(storageKey, currentWidth); + + document.removeEventListener("pointermove", onPointerMove); + document.removeEventListener("pointerup", onPointerUp); + document.removeEventListener("pointercancel", onPointerUp); + } + + document.addEventListener("pointermove", onPointerMove); + document.addEventListener("pointerup", onPointerUp); + document.addEventListener("pointercancel", onPointerUp); + }); + } + + if (leftResizer) setupResizer(leftResizer, "left"); + if (rightResizer) setupResizer(rightResizer, "right"); + }); +})(); \ No newline at end of file diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 5a34ccd09be..f18d4ca17a9 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -394,6 +394,92 @@ a:not([href]):not([class]):hover { } } +// Left sidebar & Right sidebar Resize +:root { + --left-sidebar-width: 240px; + --toc-width: 240px; + + --left-sidebar-min-width: 180px; + --left-sidebar-max-width: 500px; + + --toc-min-width: 180px; + --toc-max-width: 400px; + + --sidebar-resize-width: 5px; +} + +.layout-resizer { + width: var(--sidebar-resize-width); + cursor: col-resize; + background-color: rgba(0, 0, 0, 0.03); + transition: background-color 0.15s ease; + z-index: 1; //Must be lower than the sticky header +} + +.layout-resizer:hover, +.layout-resizer.is-dragging { + background-color: $primary; +} + +.td-resizable-grid .td-sidebar, +.td-resizable-grid .td-sidebar-toc, +.td-resizable-grid main[role="main"] { + width: auto; +} + +@media (min-width: 768px) { + .td-resizable-grid .td-main > .row.flex-xl-nowrap { + display: grid; + grid-template-columns: + var(--left-sidebar-width) + var(--sidebar-resize-width) + 1fr; + } + + .td-sidebar { + grid-column: 1; + grid-row: 1; + } + + #left-resizer { + grid-column: 2; + grid-row: 1; + padding-left: 0 !important; + padding-right: 0 !important; + box-sizing: border-box; + } + + main[role="main"] { + grid-column: 3; + grid-row: 1; + } +} + +@media (min-width: 1200px) { + .td-resizable-grid .td-main > .row.flex-xl-nowrap { + display: grid; + grid-template-columns: + var(--left-sidebar-width) + var(--sidebar-resize-width) + 1fr + var(--sidebar-resize-width) + var(--toc-width); + } + + #right-resizer { + grid-column: 4; + grid-row: 1; + padding-left: 0 !important; + padding-right: 0 !important; + box-sizing: border-box; + } + + .td-sidebar-toc { + grid-column: 5; + grid-row: 1; + } +} + // pageinfo .pageinfo { font-weight: $font-weight-medium; diff --git a/layouts/404.html b/layouts/404.html index 410580771b7..acb3db8ac78 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -7,17 +7,24 @@
{{ partial "navbar.html" . }}
-
+
+ +
+ +
+ + +
{{ partial "version-banner.html" . }} {{ if not .Site.Params.ui.breadcrumb_disable }}{{ partial "breadcrumb.html" . }}{{ end }} @@ -27,11 +34,14 @@

Not found

Please let us know about this issue and return to the home page.

{{ end }}
+ + {{ partial "load-sidebars-width.html" . }}
{{ partial "footer.html" . }}
{{ partial "scripts.html" . }} + {{ partial "sidebar-resize-script.html" }} diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html index 3503963fb79..e75bf3f4427 100644 --- a/layouts/docs/baseof.html +++ b/layouts/docs/baseof.html @@ -7,17 +7,23 @@
{{ partial "navbar.html" . }}
-
+
+ +
+ +
+ +
{{ partial "version-banner.html" . }} {{ block "main" . }}{{ end }}
+ + {{ partial "load-sidebars-width.html" . }}
{{ partial "footer.html" . }}
{{ partial "scripts.html" . }} + {{ partial "sidebar-resize-script.html" }} {{ partial "image-modal.html" . }} diff --git a/layouts/partials/load-sidebars-width.html b/layouts/partials/load-sidebars-width.html new file mode 100644 index 00000000000..4e206de7dd6 --- /dev/null +++ b/layouts/partials/load-sidebars-width.html @@ -0,0 +1,3 @@ + +{{ $resizerScript := resources.Get "js/sidebar-load-width.js" | minify | fingerprint }} + \ No newline at end of file diff --git a/layouts/partials/sidebar-resize-script.html b/layouts/partials/sidebar-resize-script.html new file mode 100644 index 00000000000..2b8b9836f3d --- /dev/null +++ b/layouts/partials/sidebar-resize-script.html @@ -0,0 +1,2 @@ +{{ $resizerScript := resources.Get "js/sidebar-resizer.js" | minify | fingerprint }} + \ No newline at end of file diff --git a/layouts/release/baseof.html b/layouts/release/baseof.html index 2145f978f24..66b5d9311e8 100644 --- a/layouts/release/baseof.html +++ b/layouts/release/baseof.html @@ -13,12 +13,17 @@ class="td-{{ .Kind }}{{ with .Page.Params.body_class }} {{ . }}{{ end }}" >
{{ partial "navbar.html" . }}
-
+
+ +
+ +
+
{{ partial "footer.html" . }}
- {{ partial "scripts.html" . }} {{ partial "image-modal.html" . }} + {{ partial "scripts.html" . }} {{ partial "sidebar-resize-script.html" }} {{ partial "image-modal.html" . }} \ No newline at end of file diff --git a/layouts/video/baseof.html b/layouts/video/baseof.html index 7f988f54859..1ce62809758 100644 --- a/layouts/video/baseof.html +++ b/layouts/video/baseof.html @@ -13,26 +13,35 @@ class="td-{{ .Kind }}{{ with .Page.Params.body_class }} {{ . }}{{ end }}" >
{{ partial "navbar.html" . }}
-
+
+ +
+ +
+ +
{{ partial "version-banner.html" . }} {{ if not .Site.Params.ui.breadcrumb_disable }}{{ partial "breadcrumb.html" . }}{{ end }} {{ block "main" . }}{{ end }}
+ + {{ partial "load-sidebars-width.html" . }}
{{ partial "footer.html" . }}
{{partial "video-category-navigation.html" .}} - {{ partial "scripts.html" . }} {{ partial "image-modal.html" . }} + {{ partial "scripts.html" . }} {{ partial "image-modal.html" . }} {{ partial "sidebar-resize-script.html" }} diff --git a/package-lock.json b/package-lock.json index 78f5ce407aa..7a86c1657a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -840,21 +840,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/tinyglobby/node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -898,6 +883,21 @@ "node": ">=8.0" } }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",