mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-05 02:45:28 +00:00
feat(dock): share snapped pane resize width
This commit is contained in:
parent
66755d5eb1
commit
e31ba3fc1c
1 changed files with 117 additions and 86 deletions
|
|
@ -47,6 +47,95 @@ function _dockOwnerSurface(owner) {
|
|||
return owner?.closest?.('.notes-pane-backdrop') || owner;
|
||||
}
|
||||
|
||||
function _zIndexForElement(el, fallback = 250) {
|
||||
const raw = el ? window.getComputedStyle(el).zIndex : '';
|
||||
const n = parseInt(raw, 10);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
}
|
||||
|
||||
function _isUsableDockOwner(owner) {
|
||||
if (!owner || !owner.isConnected) return false;
|
||||
if (owner.classList?.contains('hidden')) return false;
|
||||
if (owner.classList?.contains('modal-minimized')) return false;
|
||||
if (owner.style?.display === 'none') return false;
|
||||
const content = _resolveDockNodes(owner)?.content;
|
||||
if (!content || !content.isConnected) return false;
|
||||
if (content.classList?.contains('hidden')) return false;
|
||||
if (content.style?.display === 'none') return false;
|
||||
const r = content.getBoundingClientRect?.();
|
||||
return !!r && r.width > 0 && r.height > 0;
|
||||
}
|
||||
|
||||
function _dockOwnersForSide(side) {
|
||||
if (side !== 'left' && side !== 'right') return [];
|
||||
return Array.from(document.querySelectorAll(`.${_dockClassForSide(side)}`)).filter(_isUsableDockOwner);
|
||||
}
|
||||
|
||||
function _activeDockOwnerForSide(side) {
|
||||
let best = null;
|
||||
let bestZ = -Infinity;
|
||||
_dockOwnersForSide(side).forEach((owner) => {
|
||||
const z = _zIndexForElement(_dockOwnerSurface(owner), 250);
|
||||
if (z >= bestZ) {
|
||||
best = owner;
|
||||
bestZ = z;
|
||||
}
|
||||
});
|
||||
return best;
|
||||
}
|
||||
|
||||
function _applyDockWidthToOwner(owner, side, width, left = _leftNavRight()) {
|
||||
const content = _resolveDockNodes(owner)?.content;
|
||||
if (!content || !width) return;
|
||||
const w = Math.round(width);
|
||||
content._userDockWidth = w;
|
||||
if (side === 'right') {
|
||||
content.style.left = 'auto';
|
||||
content.style.right = '0';
|
||||
content.style.width = w + 'px';
|
||||
content.style.maxWidth = w + 'px';
|
||||
if (_shouldAutoCollapseSidebar(w)) {
|
||||
_collapseSidebarToRail();
|
||||
if (content._preDockSnapshot) content._preDockSnapshot.collapsedSidebar = true;
|
||||
}
|
||||
} else {
|
||||
content._emailDocSplitUserW = w;
|
||||
content.style.left = left + 'px';
|
||||
content.style.right = 'auto';
|
||||
content.style.width = w + 'px';
|
||||
content.style.maxWidth = w + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function _syncDockSideWidth(side, requestedWidth, owners = _dockOwnersForSide(side)) {
|
||||
if (side !== 'left' && side !== 'right') return 0;
|
||||
if (!owners.length) return 0;
|
||||
if (side === 'left'
|
||||
&& document.body.classList.contains('email-doc-split-active')
|
||||
&& document.body.classList.contains('doc-view')) {
|
||||
_positionEdgeDockResizeHandles();
|
||||
return requestedWidth || _activeDockWidth(side);
|
||||
}
|
||||
let w = 0;
|
||||
if (side === 'right') {
|
||||
w = _clampRightDockWidth(requestedWidth || _activeDockWidth('right') || _defaultDockWidth());
|
||||
document.body.classList.add('right-dock-active');
|
||||
document.documentElement.style.setProperty('--right-dock-w', w + 'px');
|
||||
owners.forEach((owner) => _applyDockWidthToOwner(owner, side, w));
|
||||
} else {
|
||||
const left = _leftNavRight();
|
||||
w = _clampLeftDockWidth(requestedWidth || _activeDockWidth('left') || _defaultDockWidth(), left);
|
||||
document.body.classList.add('left-dock-active');
|
||||
document.documentElement.style.setProperty(
|
||||
'--left-dock-w',
|
||||
document.body.classList.contains('email-doc-split-active') ? '0px' : w + 'px',
|
||||
);
|
||||
owners.forEach((owner) => _applyDockWidthToOwner(owner, side, w, left));
|
||||
}
|
||||
_positionEdgeDockResizeHandles();
|
||||
return w;
|
||||
}
|
||||
|
||||
function _hasOtherDockedWindow(side, owner) {
|
||||
const cls = _dockClassForSide(side);
|
||||
return Array.from(document.querySelectorAll(`.${cls}`)).some((el) => {
|
||||
|
|
@ -360,6 +449,7 @@ function _applyDockInternal(modal, side, dockClass) {
|
|||
if (!nodes) return 0;
|
||||
const content = nodes.content;
|
||||
if (!content) return 0;
|
||||
const existingSideWidth = _hasOtherDockedWindow(side, modal) ? _activeDockWidth(side) : 0;
|
||||
// If the modal is currently docked on the OTHER side (e.g. the user
|
||||
// manually docked it right, then a reply re-docks it left), clear that
|
||||
// side's class + body push first. Otherwise both sides' state coexist —
|
||||
|
|
@ -522,7 +612,13 @@ function _applyDockInternal(modal, side, dockClass) {
|
|||
}
|
||||
content._dockSide = side;
|
||||
content._dockOwner = modal;
|
||||
_positionEdgeDockResizeHandles();
|
||||
const sideOwners = _dockOwnersForSide(side);
|
||||
if (sideOwners.length > 1) {
|
||||
const syncedWidth = _syncDockSideWidth(side, existingSideWidth || w, sideOwners);
|
||||
if (syncedWidth) w = syncedWidth;
|
||||
} else {
|
||||
_positionEdgeDockResizeHandles();
|
||||
}
|
||||
// Watch for the docked modal disappearing (removed from DOM or hidden
|
||||
// via .hidden class) and clean up the body padding + sidebar in that
|
||||
// case. Without this, closing a docked window leaves a phantom strip
|
||||
|
|
@ -817,12 +913,6 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
|
||||
const _ownerSurface = _dockOwnerSurface;
|
||||
|
||||
const _zIndexFor = (el, fallback = 250) => {
|
||||
const raw = el ? window.getComputedStyle(el).zIndex : '';
|
||||
const n = parseInt(raw, 10);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
};
|
||||
|
||||
const _topToolZ = (exclude = null) => {
|
||||
const skip = new Set([exclude, _ownerSurface(exclude)].filter(Boolean));
|
||||
let top = 300;
|
||||
|
|
@ -830,7 +920,7 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
if (!el || skip.has(el)) return;
|
||||
if (el.classList?.contains('hidden')) return;
|
||||
if (el.style?.display === 'none') return;
|
||||
top = Math.max(top, _zIndexFor(el, 250));
|
||||
top = Math.max(top, _zIndexForElement(el, 250));
|
||||
});
|
||||
return top;
|
||||
};
|
||||
|
|
@ -846,24 +936,6 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
} catch (_) {}
|
||||
};
|
||||
|
||||
const _isVisibleDockOwner = (owner) => {
|
||||
if (!owner || !owner.isConnected) return false;
|
||||
if (owner.classList?.contains('hidden')) return false;
|
||||
if (owner.classList?.contains('modal-minimized')) return false;
|
||||
if (owner.style?.display === 'none') return false;
|
||||
const content = _resolveDockNodes(owner)?.content;
|
||||
if (!content || !content.isConnected) return false;
|
||||
if (content.classList?.contains('hidden')) return false;
|
||||
if (content.style?.display === 'none') return false;
|
||||
const r = content.getBoundingClientRect?.();
|
||||
return !!r && r.width > 0 && r.height > 0;
|
||||
};
|
||||
|
||||
const _ownersForSide = (side) => {
|
||||
const cls = _dockClassForSide(side);
|
||||
return Array.from(document.querySelectorAll(`.${cls}`)).filter(_isVisibleDockOwner);
|
||||
};
|
||||
|
||||
const _simpleTitleForOwner = (owner) => {
|
||||
const id = owner?.id || '';
|
||||
const labels = {
|
||||
|
|
@ -903,7 +975,7 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
let best = null;
|
||||
let bestZ = -Infinity;
|
||||
owners.forEach((owner) => {
|
||||
const z = _zIndexFor(_ownerSurface(owner), 250);
|
||||
const z = _zIndexForElement(_ownerSurface(owner), 250);
|
||||
if (z >= bestZ) {
|
||||
best = owner;
|
||||
bestZ = z;
|
||||
|
|
@ -939,7 +1011,7 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
};
|
||||
|
||||
const _renderSide = (side) => {
|
||||
const owners = _enabled() && window.innerWidth > 768 ? _ownersForSide(side) : [];
|
||||
const owners = _enabled() && window.innerWidth > 768 ? _dockOwnersForSide(side) : [];
|
||||
if (owners.length < 2) {
|
||||
_hideBar(side);
|
||||
return;
|
||||
|
|
@ -1056,33 +1128,7 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
document.body.appendChild(handle);
|
||||
}
|
||||
|
||||
const _isUsableDockOwner = (owner) => {
|
||||
if (!owner || !owner.isConnected) return false;
|
||||
if (owner.classList?.contains('hidden')) return false;
|
||||
if (owner.style?.display === 'none') return false;
|
||||
const nodes = _resolveDockNodes(owner);
|
||||
const content = nodes?.content;
|
||||
if (!content || !content.isConnected) return false;
|
||||
if (content.classList?.contains('hidden')) return false;
|
||||
if (content.style?.display === 'none') return false;
|
||||
const r = content.getBoundingClientRect();
|
||||
return r.width > 0 && r.height > 0;
|
||||
};
|
||||
|
||||
const _activeDockOwner = (side) => {
|
||||
const cls = _dockClassForSide(side);
|
||||
const all = Array.from(document.querySelectorAll(`.${cls}`));
|
||||
for (const owner of all.reverse()) {
|
||||
if (_isUsableDockOwner(owner)) return owner;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const _zIndexFor = (el, fallback = 250) => {
|
||||
const raw = el ? window.getComputedStyle(el).zIndex : '';
|
||||
const n = parseInt(raw, 10);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
};
|
||||
const _activeDockOwner = (side) => _activeDockOwnerForSide(side);
|
||||
|
||||
const _hasVisibleFloatingModal = (owner) => {
|
||||
const all = Array.from(document.querySelectorAll('.modal:not(.hidden):not(.modal-minimized)'));
|
||||
|
|
@ -1099,40 +1145,18 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
});
|
||||
};
|
||||
|
||||
const _setWidth = (owner, side, clientX) => {
|
||||
const nodes = _resolveDockNodes(owner);
|
||||
const content = nodes?.content;
|
||||
if (!content) return 0;
|
||||
const _setWidth = (owner, side, clientX, resizeOwners = null) => {
|
||||
const targets = (resizeOwners && resizeOwners.length) ? resizeOwners : [owner].filter(Boolean);
|
||||
if (!targets.length) return 0;
|
||||
let w = 0;
|
||||
if (side === 'right') {
|
||||
w = _clampRightDockWidth(window.innerWidth - clientX);
|
||||
content._userDockWidth = w;
|
||||
content.style.left = 'auto';
|
||||
content.style.right = '0';
|
||||
content.style.width = w + 'px';
|
||||
content.style.maxWidth = w + 'px';
|
||||
document.body.classList.add('right-dock-active');
|
||||
document.documentElement.style.setProperty('--right-dock-w', w + 'px');
|
||||
if (_shouldAutoCollapseSidebar(w)) {
|
||||
_collapseSidebarToRail();
|
||||
if (content._preDockSnapshot) content._preDockSnapshot.collapsedSidebar = true;
|
||||
}
|
||||
_syncDockSideWidth(side, w, targets);
|
||||
} else {
|
||||
const left = _leftNavRight();
|
||||
w = _clampLeftDockWidth(clientX - left, left);
|
||||
content._userDockWidth = w;
|
||||
content._emailDocSplitUserW = w;
|
||||
content.style.left = left + 'px';
|
||||
content.style.right = 'auto';
|
||||
content.style.width = w + 'px';
|
||||
content.style.maxWidth = w + 'px';
|
||||
document.body.classList.add('left-dock-active');
|
||||
document.documentElement.style.setProperty(
|
||||
'--left-dock-w',
|
||||
document.body.classList.contains('email-doc-split-active') ? '0px' : w + 'px',
|
||||
);
|
||||
_syncDockSideWidth(side, w, targets);
|
||||
}
|
||||
_positionEdgeDockResizeHandles();
|
||||
return w;
|
||||
};
|
||||
|
||||
|
|
@ -1164,7 +1188,7 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
}
|
||||
_setStyle(handle, 'display', 'block');
|
||||
_setStyle(handle, 'left', (x - 5) + 'px');
|
||||
_setStyle(handle, 'zIndex', String(_zIndexFor(owner) + 1));
|
||||
_setStyle(handle, 'zIndex', String(_zIndexForElement(_dockOwnerSurface(owner), 250) + 1));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1179,15 +1203,17 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
handle.setPointerCapture?.(e.pointerId);
|
||||
const nodes = _resolveDockNodes(owner);
|
||||
const content = nodes?.content;
|
||||
const resizeOwners = _dockOwnersForSide(side);
|
||||
if (!resizeOwners.includes(owner)) resizeOwners.push(owner);
|
||||
const prevCursor = document.body.style.cursor;
|
||||
const prevUserSelect = document.body.style.userSelect;
|
||||
document.body.style.cursor = 'col-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.classList.add('edge-dock-resizing');
|
||||
_setWidth(owner, side, e.clientX);
|
||||
_setWidth(owner, side, e.clientX, resizeOwners);
|
||||
const onMove = (ev) => {
|
||||
ev.preventDefault();
|
||||
_setWidth(owner, side, ev.clientX);
|
||||
_setWidth(owner, side, ev.clientX, resizeOwners);
|
||||
};
|
||||
const onUp = (ev) => {
|
||||
try { handle.releasePointerCapture?.(e.pointerId); } catch (_) {}
|
||||
|
|
@ -1200,7 +1226,12 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
|||
const finalW = side === 'right'
|
||||
? parseFloat(document.documentElement.style.getPropertyValue('--right-dock-w')) || content?.getBoundingClientRect?.().width || 0
|
||||
: content?.getBoundingClientRect?.().width || 0;
|
||||
if (finalW) _saveDockWidth(owner, content, side, finalW);
|
||||
if (finalW) {
|
||||
resizeOwners.forEach((dockOwner) => {
|
||||
const dockContent = _resolveDockNodes(dockOwner)?.content;
|
||||
if (dockContent) _saveDockWidth(dockOwner, dockContent, side, finalW);
|
||||
});
|
||||
}
|
||||
ev.preventDefault();
|
||||
};
|
||||
document.addEventListener('pointermove', onMove, true);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue