-
Notifications
You must be signed in to change notification settings - Fork 185
feat(layouts): add resizable side panels with localStorage persistence #1040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
895bfa1
53e9296
8dfa71e
a7040ab
4bcba70
3c4bb21
3671459
e945697
2e8475c
59647a8
ed5d256
c260d36
5fd3cc8
617019f
c2eb6d2
8ca58ae
2a31d58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,331 @@ | ||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Resizable docs panels. | ||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||
| * Lets readers adjust the left navigation and right TOC widths, persists those | ||||||||||||||||||||||||||||
| * preferences in localStorage, and provides a reset control. | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| (function () { | ||||||||||||||||||||||||||||
| 'use strict'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const STORAGE_KEY = 'layer5-docs-panel-widths'; | ||||||||||||||||||||||||||||
| const RESIZABLE_QUERY = '(min-width: 768px)'; | ||||||||||||||||||||||||||||
| const STEP = 1; | ||||||||||||||||||||||||||||
| const DEFAULT_WIDTHS = { | ||||||||||||||||||||||||||||
| sidebar: 16.6667, | ||||||||||||||||||||||||||||
| toc: 16.6667, | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
| const LIMITS = { | ||||||||||||||||||||||||||||
| sidebar: { min: 12, max: 32 }, | ||||||||||||||||||||||||||||
| toc: { min: 10, max: 28 }, | ||||||||||||||||||||||||||||
| main: { min: 42 }, | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
| 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(); | ||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To support smooth, high-performance resizing without layout thrashing, we should throttle the DOM updates using
Suggested change
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!sidebar || !main) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| row.classList.add('resizable-panels-ready'); | ||||||||||||||||||||||||||||
| applyWidths(widths); | ||||||||||||||||||||||||||||
| createHandles(); | ||||||||||||||||||||||||||||
| createResetButton(); | ||||||||||||||||||||||||||||
| bindEvents(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function bindEvents() { | ||||||||||||||||||||||||||||
| document.addEventListener('pointermove', onPointerMove); | ||||||||||||||||||||||||||||
| document.addEventListener('pointerup', stopResize); | ||||||||||||||||||||||||||||
| document.addEventListener('pointercancel', stopResize); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const onBreakpointChange = () => { | ||||||||||||||||||||||||||||
| if (mediaQuery.matches) { | ||||||||||||||||||||||||||||
| applyWidths(widths); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (mediaQuery.addEventListener) { | ||||||||||||||||||||||||||||
| mediaQuery.addEventListener('change', onBreakpointChange); | ||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||
| mediaQuery.addListener(onBreakpointChange); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Binding function bindEvents() {
const onBreakpointChange = () => {
if (mediaQuery.matches) {
applyWidths(widths);
}
};
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', onBreakpointChange);
} else {
mediaQuery.addListener(onBreakpointChange);
}
} |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function createHandles() { | ||||||||||||||||||||||||||||
| sidebarHandle = createHandle('sidebar', 'Resize navigation sidebar'); | ||||||||||||||||||||||||||||
| sidebar.appendChild(sidebarHandle); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (toc) { | ||||||||||||||||||||||||||||
| tocHandle = createHandle('toc', 'Resize table of contents'); | ||||||||||||||||||||||||||||
| toc.appendChild(tocHandle); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function 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) => { | ||||||||||||||||||||||||||||
| startResize(event, handle); | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| handle.addEventListener('keydown', (event) => { | ||||||||||||||||||||||||||||
| onHandleKeydown(event, target); | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return handle; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function createResetButton() { | ||||||||||||||||||||||||||||
| const resetButton = document.createElement('button'); | ||||||||||||||||||||||||||||
| resetButton.type = 'button'; | ||||||||||||||||||||||||||||
| resetButton.id = 'reset-panel-widths'; | ||||||||||||||||||||||||||||
| resetButton.className = 'resizable-panel-reset'; | ||||||||||||||||||||||||||||
| resetButton.innerHTML = | ||||||||||||||||||||||||||||
| '<i class="bi bi-arrow-clockwise" aria-hidden="true"></i><span>Reset layout</span>'; | ||||||||||||||||||||||||||||
| resetButton.title = 'Reset panel widths to default'; | ||||||||||||||||||||||||||||
| resetButton.addEventListener('click', reset); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| sidebar.appendChild(resetButton); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+94
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting a hardcoded ID function createResetButton() {
const resetButton = document.createElement('button');
resetButton.type = 'button';
resetButton.className = 'resizable-panel-reset';
resetButton.innerHTML =
'<i class="bi bi-arrow-clockwise" aria-hidden="true"></i><span>Reset layout</span>';
resetButton.title = 'Reset panel widths to default';
resetButton.addEventListener('click', reset);
sidebar.appendChild(resetButton);
}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the indicator for a "resizable-panel" is it having a ".row.flex-xl-nowrap" then the id comment is correct. But this means that there's multiple items that have different sidebars which are being managed individually. Is the difference in design that big for there to be different size managers for the side panels? |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function startResize(event, handle) { | ||||||||||||||||||||||||||||
| if (!mediaQuery.matches) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| event.preventDefault(); | ||||||||||||||||||||||||||||
| 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'); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+107
to
+119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dynamically attach the global pointer move and up/cancel event listeners here when the user actually starts dragging the handle. function startResize(event, handle) {
if (!mediaQuery.matches) {
return;
}
event.preventDefault();
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');
document.addEventListener('pointermove', onPointerMove);
document.addEventListener('pointerup', stopResize);
document.addEventListener('pointercancel', stopResize);
} |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function onPointerMove(event) { | ||||||||||||||||||||||||||||
| if (!activeHandle || !startWidths) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const rowWidth = row.getBoundingClientRect().width; | ||||||||||||||||||||||||||||
| if (!rowWidth) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const target = activeHandle.dataset.resizeTarget; | ||||||||||||||||||||||||||||
| const delta = ((event.clientX - startX) / rowWidth) * 100; | ||||||||||||||||||||||||||||
| const nextWidths = { ...startWidths }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (target === 'sidebar') { | ||||||||||||||||||||||||||||
| nextWidths.sidebar = startWidths.sidebar + delta; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (target === 'toc') { | ||||||||||||||||||||||||||||
| nextWidths.toc = startWidths.toc - delta; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| widths = normalizeWidths(nextWidths); | ||||||||||||||||||||||||||||
| applyWidths(widths); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+121
to
+145
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throttle the pointer move updates using function onPointerMove(event) {
if (!activeHandle || !startWidths) {
return;
}
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
const clientX = event.clientX;
animationFrameId = requestAnimationFrame(() => {
const rowWidth = row.getBoundingClientRect().width;
if (!rowWidth) {
return;
}
const target = activeHandle.dataset.resizeTarget;
const delta = ((clientX - startX) / rowWidth) * 100;
const nextWidths = { ...startWidths };
if (target === 'sidebar') {
nextWidths.sidebar = startWidths.sidebar + delta;
}
if (target === 'toc') {
nextWidths.toc = startWidths.toc - delta;
}
widths = normalizeWidths(nextWidths);
applyWidths(widths);
});
}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thoughts on this? |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function stopResize() { | ||||||||||||||||||||||||||||
| if (!activeHandle) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| activeHandle.classList.remove('resizable-panel-handle--active'); | ||||||||||||||||||||||||||||
| activeHandle = null; | ||||||||||||||||||||||||||||
| startWidths = null; | ||||||||||||||||||||||||||||
| document.body.classList.remove('resizable-panels-dragging'); | ||||||||||||||||||||||||||||
| saveWidths(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+147
to
+157
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clean up the dynamic pointer event listeners and cancel any pending animation frames when resizing stops. function stopResize() {
if (!activeHandle) {
return;
}
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
activeHandle.classList.remove('resizable-panel-handle--active');
activeHandle = null;
startWidths = null;
document.body.classList.remove('resizable-panels-dragging');
saveWidths();
document.removeEventListener('pointermove', onPointerMove);
document.removeEventListener('pointerup', stopResize);
document.removeEventListener('pointercancel', stopResize);
} |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function onHandleKeydown(event, target) { | ||||||||||||||||||||||||||||
| if (!mediaQuery.matches) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const keys = ['ArrowLeft', 'ArrowRight', 'Home', 'End']; | ||||||||||||||||||||||||||||
| if (!keys.includes(event.key)) { | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| event.preventDefault(); | ||||||||||||||||||||||||||||
| const nextWidths = { ...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; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| widths = normalizeWidths(nextWidths); | ||||||||||||||||||||||||||||
| applyWidths(widths); | ||||||||||||||||||||||||||||
| saveWidths(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function applyWidths(nextWidths) { | ||||||||||||||||||||||||||||
| const normalized = normalizeWidths(nextWidths); | ||||||||||||||||||||||||||||
| const mainWidth = 100 - normalized.sidebar - normalized.toc; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| 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}%`, | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| updateHandleValues(normalized); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| 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(nextWidths.sidebar), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (tocHandle) { | ||||||||||||||||||||||||||||
| tocHandle.setAttribute('aria-valuemin', LIMITS.toc.min); | ||||||||||||||||||||||||||||
| tocHandle.setAttribute('aria-valuemax', LIMITS.toc.max); | ||||||||||||||||||||||||||||
| tocHandle.setAttribute('aria-valuenow', Math.round(nextWidths.toc)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function reset() { | ||||||||||||||||||||||||||||
| widths = { ...DEFAULT_WIDTHS }; | ||||||||||||||||||||||||||||
| applyWidths(widths); | ||||||||||||||||||||||||||||
| localStorage.removeItem(STORAGE_KEY); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function getStoredWidths() { | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| const saved = JSON.parse(localStorage.getItem(STORAGE_KEY)); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (isLegacyColumnWidths(saved)) { | ||||||||||||||||||||||||||||
| return normalizeWidths({ | ||||||||||||||||||||||||||||
| sidebar: saved.sidebar | ||||||||||||||||||||||||||||
| ? (saved.sidebar / LEGACY_GRID_COLUMNS) * 100 | ||||||||||||||||||||||||||||
| : DEFAULT_WIDTHS.sidebar, | ||||||||||||||||||||||||||||
| toc: saved.toc | ||||||||||||||||||||||||||||
| ? (saved.toc / LEGACY_GRID_COLUMNS) * 100 | ||||||||||||||||||||||||||||
| : DEFAULT_WIDTHS.toc, | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return normalizeWidths(saved || DEFAULT_WIDTHS); | ||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||
| return { ...DEFAULT_WIDTHS }; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| 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(widths)); | ||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||
| // Ignore storage failures so resizing still works in private modes. | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function normalizeWidths(nextWidths) { | ||||||||||||||||||||||||||||
| const next = { | ||||||||||||||||||||||||||||
| sidebar: clamp( | ||||||||||||||||||||||||||||
| Number(nextWidths && nextWidths.sidebar), | ||||||||||||||||||||||||||||
| LIMITS.sidebar.min, | ||||||||||||||||||||||||||||
| LIMITS.sidebar.max, | ||||||||||||||||||||||||||||
| DEFAULT_WIDTHS.sidebar, | ||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||
| toc: toc | ||||||||||||||||||||||||||||
| ? clamp( | ||||||||||||||||||||||||||||
| Number(nextWidths && nextWidths.toc), | ||||||||||||||||||||||||||||
| LIMITS.toc.min, | ||||||||||||||||||||||||||||
| LIMITS.toc.max, | ||||||||||||||||||||||||||||
| DEFAULT_WIDTHS.toc, | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| : 0, | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const availableForPanels = 100 - LIMITS.main.min; | ||||||||||||||||||||||||||||
| const panelTotal = next.sidebar + next.toc; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (panelTotal > availableForPanels) { | ||||||||||||||||||||||||||||
| const overflow = panelTotal - availableForPanels; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| 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)), | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function clamp(value, min, max, fallback) { | ||||||||||||||||||||||||||||
| if (!Number.isFinite(value)) { | ||||||||||||||||||||||||||||
| return fallback; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return Math.min(max, Math.max(min, value)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| function initResizablePanels() { | ||||||||||||||||||||||||||||
| document.querySelectorAll('.row.flex-xl-nowrap').forEach((row) => { | ||||||||||||||||||||||||||||
| if (!row.dataset.resizablePanelsInitialized) { | ||||||||||||||||||||||||||||
| row.dataset.resizablePanelsInitialized = 'true'; | ||||||||||||||||||||||||||||
| setupResizablePanels(row); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (document.readyState === 'loading') { | ||||||||||||||||||||||||||||
| document.addEventListener('DOMContentLoaded', initResizablePanels); | ||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||
| initResizablePanels(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The feedback regarding the dynamic use of the event listening for the dragging of the panels hasn't been implemented. As of right now, there's always an event listening of the pointer movement despite there being a window in which a listening for those particulars event isn't needed.