mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-15 17:25:26 -04:00
Improve edge-docked window behavior (#2779)
* Make edge-docked windows resizable
Add draggable resize seams for left and right docked windows.
Keep the main chat area from getting too narrow and remember each window's dock width.
* Show emoji shortcodes as icons by default
Keep text-only emoji mode opt-in so model output like 😊 goes through the normal emoji renderer.
* Fix dock resize seams and left dock layout
Hide the resize seam when another floating modal is open, and keep the left-docked window from covering the chat area.
* Keep narrow modal tabs usable
* Fix split layout with both edge docks
* Fix left snap after right dock
* Enable left edge snap for all windows
* Tighten dock resize handle observers
* Use edge docking for settings window
This commit is contained in:
+314
-21
@@ -5,8 +5,8 @@
|
||||
// emailLibrary.js / documentLibrary.js / galleryEditor.js). While docked:
|
||||
// - the modal-content lives at `right: 0; top: 0; bottom: 0` with a
|
||||
// viewport-fraction width
|
||||
// - body gets `right-dock-active` + `--right-dock-w` so the chat /
|
||||
// doc panel / notes pane underneath reserves room via padding-right
|
||||
// - body gets `right-dock-active` + `--right-dock-w` so the workspace
|
||||
// underneath reserves room for the fixed side panel
|
||||
// - if the remaining chat width would drop under 380px, the wide
|
||||
// sidebar auto-collapses to the icon rail (mirrors notes-view UX)
|
||||
//
|
||||
@@ -21,6 +21,14 @@ const SNAP_PX = 60;
|
||||
const UNSNAP_PX = 80;
|
||||
const MIN_CHAT_WIDTH = 380;
|
||||
const EMAIL_DOC_SPLIT_WIDTH_KEY = 'odysseus-email-doc-split-width';
|
||||
const EDGE_DOCK_WIDTH_KEY_PREFIX = 'odysseus-edge-dock-width';
|
||||
const MIN_EDGE_DOCK_WIDTH = 320;
|
||||
|
||||
let _edgeDockHandlePositioner = null;
|
||||
|
||||
function _positionEdgeDockResizeHandles() {
|
||||
try { _edgeDockHandlePositioner && _edgeDockHandlePositioner(); } catch (_) {}
|
||||
}
|
||||
|
||||
function _dockClassForSide(side) {
|
||||
return side === 'left' ? 'modal-left-docked' : 'modal-right-docked';
|
||||
@@ -48,6 +56,7 @@ export function clearDockSide(side, owner = null) {
|
||||
if (side === 'left') {
|
||||
try { window._restoreSidebarIfRouteCollapsed?.(); } catch (_) {}
|
||||
}
|
||||
_positionEdgeDockResizeHandles();
|
||||
}
|
||||
|
||||
// Default dock width: ~38% of viewport, clamped to a reasonable band.
|
||||
@@ -55,6 +64,78 @@ function _defaultDockWidth() {
|
||||
return Math.min(640, Math.max(420, Math.round(window.innerWidth * 0.38)));
|
||||
}
|
||||
|
||||
function _dockWidthStorageKey(modal, content, side) {
|
||||
const id = modal?.id || content?.id || content?.dataset?.modalId || '';
|
||||
return id ? `${EDGE_DOCK_WIDTH_KEY_PREFIX}:${side}:${id}` : null;
|
||||
}
|
||||
|
||||
function _storedDockWidth(modal, content, side) {
|
||||
const key = _dockWidthStorageKey(modal, content, side);
|
||||
if (!key) return null;
|
||||
try {
|
||||
const n = parseFloat(localStorage.getItem(key) || '');
|
||||
return Number.isFinite(n) && n > 0 ? n : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _saveDockWidth(modal, content, side, width) {
|
||||
const key = _dockWidthStorageKey(modal, content, side);
|
||||
if (!key) return;
|
||||
try { localStorage.setItem(key, String(Math.round(width))); } catch (_) {}
|
||||
}
|
||||
|
||||
function _minEdgeDockWidth() {
|
||||
return window.innerWidth < 900 ? 280 : MIN_EDGE_DOCK_WIDTH;
|
||||
}
|
||||
|
||||
function _activeDockWidth(side) {
|
||||
if (side !== 'left' && side !== 'right') return 0;
|
||||
const cls = side === 'left' ? 'left-dock-active' : 'right-dock-active';
|
||||
if (!document.body.classList.contains(cls)) return 0;
|
||||
const prop = side === 'left' ? '--left-dock-w' : '--right-dock-w';
|
||||
const raw = getComputedStyle(document.documentElement).getPropertyValue(prop);
|
||||
const n = parseFloat(raw || '');
|
||||
return Number.isFinite(n) && n > 0 ? n : 0;
|
||||
}
|
||||
|
||||
function _clampDockWidthToSpace(width, min, max) {
|
||||
const floor = Math.min(min, Math.max(220, Math.round(max)));
|
||||
const ceiling = Math.max(floor, Math.round(max));
|
||||
return Math.min(ceiling, Math.max(floor, Math.round(width)));
|
||||
}
|
||||
|
||||
function _clampRightDockWidth(width) {
|
||||
const min = _minEdgeDockWidth();
|
||||
const navRight = _leftNavRight();
|
||||
const leftDockW = _activeDockWidth('left');
|
||||
const maxByChat = window.innerWidth - navRight - leftDockW - MIN_CHAT_WIDTH;
|
||||
const max = Math.min(Math.round(window.innerWidth * 0.82), maxByChat);
|
||||
return _clampDockWidthToSpace(width, min, max);
|
||||
}
|
||||
|
||||
function _clampLeftDockWidth(width, left = _leftNavRight()) {
|
||||
const min = _minEdgeDockWidth();
|
||||
const rightDockW = _activeDockWidth('right');
|
||||
const available = Math.max(0, window.innerWidth - left - rightDockW);
|
||||
const max = Math.min(Math.round(available * 0.82), available - MIN_CHAT_WIDTH);
|
||||
return _clampDockWidthToSpace(width, min, max);
|
||||
}
|
||||
|
||||
function _resolveRightDockWidth(modal, content) {
|
||||
return _clampRightDockWidth(content?._userDockWidth || _storedDockWidth(modal, content, 'right') || _defaultDockWidth());
|
||||
}
|
||||
|
||||
function _resolveLeftDockWidth(content, left = _leftNavRight()) {
|
||||
return _clampLeftDockWidth(content?._userDockWidth || _storedDockWidth(content?._dockOwner, content, 'left') || _resolveEmailDocSplitWidth(content, left), left);
|
||||
}
|
||||
|
||||
function _isEmailDockOwner(owner) {
|
||||
const id = owner?.id || '';
|
||||
return id === 'email-lib-modal' || id.startsWith('email-reader-') || owner?.classList?.contains('email-window-modal');
|
||||
}
|
||||
|
||||
function _showSnapHint(on, side = 'right') {
|
||||
const cls = side === 'left' ? 'modal-snap-hint-left' : 'modal-snap-hint-right';
|
||||
let hint = document.querySelector('.' + cls);
|
||||
@@ -85,7 +166,7 @@ function _shouldAutoCollapseSidebar(dockW) {
|
||||
const rl = (rail && window.getComputedStyle(rail).display !== 'none')
|
||||
? rail.getBoundingClientRect().width
|
||||
: 0;
|
||||
const remaining = window.innerWidth - sb - rl - dockW;
|
||||
const remaining = window.innerWidth - sb - rl - _activeDockWidth('left') - dockW;
|
||||
return remaining < MIN_CHAT_WIDTH;
|
||||
}
|
||||
|
||||
@@ -154,7 +235,7 @@ function _applyEmailDocSplitGeometry(left, emailWidth) {
|
||||
if (!docPane || window.innerWidth <= 768) return;
|
||||
docPane.style.setProperty('position', 'fixed', 'important');
|
||||
docPane.style.setProperty('left', `${x}px`, 'important');
|
||||
docPane.style.setProperty('right', '0px', 'important');
|
||||
docPane.style.setProperty('right', 'var(--right-dock-w, 0px)', 'important');
|
||||
docPane.style.setProperty('top', '0px', 'important');
|
||||
docPane.style.setProperty('bottom', '0px', 'important');
|
||||
docPane.style.setProperty('width', 'auto', 'important');
|
||||
@@ -196,7 +277,9 @@ function _resolveEmailDocSplitWidth(content, left) {
|
||||
function _anchorLeftDock(content) {
|
||||
if (!content || content._dockSide !== 'left') return;
|
||||
const left = _leftNavRight();
|
||||
const w = _resolveEmailDocSplitWidth(content, left);
|
||||
const w = document.body.classList.contains('doc-view')
|
||||
? _resolveEmailDocSplitWidth(content, left)
|
||||
: _resolveLeftDockWidth(content, left);
|
||||
content.style.left = left + 'px';
|
||||
content.style.width = w + 'px';
|
||||
content.style.maxWidth = w + 'px';
|
||||
@@ -205,14 +288,17 @@ function _anchorLeftDock(content) {
|
||||
// the doc-pane becomes position:fixed starting at the email's right edge.
|
||||
// No flex/max-width fighting; the doc just owns the right side from the
|
||||
// email's right edge to the viewport edge — they touch flush, no gap.
|
||||
const docOpen = document.body.classList.contains('doc-view');
|
||||
const docOpen = document.body.classList.contains('doc-view') && _isEmailDockOwner(content._dockOwner);
|
||||
if (docOpen) {
|
||||
if (!document.body.classList.contains('email-doc-split-active')) {
|
||||
document.body.classList.add('email-doc-split-active');
|
||||
}
|
||||
document.documentElement.style.setProperty('--left-dock-w', '0px');
|
||||
_applyEmailDocSplitGeometry(left, w);
|
||||
} else if (document.body.classList.contains('email-doc-split-active')) {
|
||||
_clearEmailDocSplitGeometry();
|
||||
} else {
|
||||
document.documentElement.style.setProperty('--left-dock-w', w + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,19 +402,21 @@ function _applyDockInternal(modal, side, dockClass) {
|
||||
content.style.margin = '0';
|
||||
let w;
|
||||
if (side === 'left') {
|
||||
// Email-style left dock: collapse the sidebar to the icon rail, then
|
||||
// OVERLAY the window beside the rail, covering the chat area. We anchor
|
||||
// at the rail's right edge (so it sits to the RIGHT of the rail — not
|
||||
// left of the sidebar) and DON'T reserve body padding (so it covers the
|
||||
// chat rather than pushing it), leaving the right side free for the doc.
|
||||
// Left dock: collapse the sidebar to the icon rail, then pin the window
|
||||
// beside the rail. Normal left docks reserve their width so chat shrinks;
|
||||
// the email+document split keeps its existing overlay geometry.
|
||||
_collapseSidebarToRail();
|
||||
content._preDockSnapshot.collapsedSidebar = true;
|
||||
content.style.right = 'auto';
|
||||
content._dockSide = 'left';
|
||||
content._dockOwner = modal;
|
||||
_anchorLeftDock(content);
|
||||
w = parseFloat(content.style.width) || 0;
|
||||
document.body.classList.add('left-dock-active');
|
||||
document.documentElement.style.setProperty('--left-dock-w', '0px'); // overlay, no push
|
||||
document.documentElement.style.setProperty(
|
||||
'--left-dock-w',
|
||||
document.body.classList.contains('email-doc-split-active') ? '0px' : w + 'px',
|
||||
);
|
||||
// Re-anchor the email when the sidebar is toggled (expanded/collapsed) so
|
||||
// the nav slides the window over instead of growing on top of it. Also
|
||||
// re-anchor when the document editor pane appears/disappears (signaled by
|
||||
@@ -406,7 +494,7 @@ function _applyDockInternal(modal, side, dockClass) {
|
||||
};
|
||||
}
|
||||
} else {
|
||||
w = _defaultDockWidth();
|
||||
w = _resolveRightDockWidth(modal, content);
|
||||
content.style.left = 'auto';
|
||||
content.style.right = '0';
|
||||
content.style.width = w + 'px';
|
||||
@@ -419,6 +507,8 @@ function _applyDockInternal(modal, side, dockClass) {
|
||||
}
|
||||
}
|
||||
content._dockSide = side;
|
||||
content._dockOwner = modal;
|
||||
_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
|
||||
@@ -498,7 +588,9 @@ function _onDockedModalGone(modal, dockClass) {
|
||||
}
|
||||
delete _c._preDockSnapshot;
|
||||
delete _c._dockSide;
|
||||
delete _c._dockOwner;
|
||||
}
|
||||
_positionEdgeDockResizeHandles();
|
||||
}
|
||||
|
||||
function _expandSidebarFromRail() {
|
||||
@@ -526,6 +618,7 @@ export function clearRightDock(modal, cx, cy, dockClass) {
|
||||
_clearEmailDocSplitGeometry();
|
||||
}
|
||||
delete content._dockSide;
|
||||
delete content._dockOwner;
|
||||
_disconnectLeftDockObservers(content);
|
||||
const snap = content._preDockSnapshot;
|
||||
// Re-expand the wide sidebar if we collapsed it — but only if the
|
||||
@@ -571,6 +664,7 @@ export function clearRightDock(modal, cx, cy, dockClass) {
|
||||
content.style.top = (typeof targetTop === 'number') ? targetTop + 'px' : targetTop;
|
||||
delete content._preDockSnapshot;
|
||||
delete content._dockSuspended;
|
||||
_positionEdgeDockResizeHandles();
|
||||
}
|
||||
|
||||
// Temporarily release a docked modal's body push (chat returns to full
|
||||
@@ -604,6 +698,7 @@ export function suspendDock(modal) {
|
||||
modal.classList.remove('email-snap-left');
|
||||
_clearEmailDocSplitGeometry();
|
||||
delete content._dockSide;
|
||||
delete content._dockOwner;
|
||||
delete content._dockSuspended;
|
||||
return null;
|
||||
}
|
||||
@@ -614,6 +709,7 @@ export function suspendDock(modal) {
|
||||
_expandSidebarFromRail();
|
||||
}
|
||||
content._dockSuspended = side;
|
||||
_positionEdgeDockResizeHandles();
|
||||
return side;
|
||||
}
|
||||
|
||||
@@ -641,15 +737,11 @@ export function makeRightDockController(modal, dockClass = 'modal-right-docked')
|
||||
return makeEdgeDockController(modal, 'right', dockClass);
|
||||
}
|
||||
|
||||
// Read live rail+sidebar width — used as the LEFT "edge" for snap
|
||||
// detection, since the visible left boundary the user can drag to is
|
||||
// the nav, not x=0 (the rail covers 0..48 and the wide sidebar covers
|
||||
// 0..~290 when open).
|
||||
// Read the current visible left-nav edge for snap detection. Use measured
|
||||
// geometry instead of CSS vars because the sidebar can auto-collapse during a
|
||||
// dock operation while --sidebar-w is still settling.
|
||||
function _leftNavWidth() {
|
||||
const rs = getComputedStyle(document.documentElement);
|
||||
const rail = parseInt(rs.getPropertyValue('--icon-rail-w') || '48', 10) || 0;
|
||||
const sb = parseInt(rs.getPropertyValue('--sidebar-w') || '0', 10) || 0;
|
||||
return rail + sb;
|
||||
return _leftNavRight();
|
||||
}
|
||||
|
||||
// Generic edge-snap controller. `side` is 'left' or 'right'. Same pattern
|
||||
@@ -692,6 +784,207 @@ export function makeEdgeDockController(modal, side = 'right', dockClass) {
|
||||
};
|
||||
}
|
||||
|
||||
(function _initEdgeDockResizeHandles() {
|
||||
if (typeof document === 'undefined') return;
|
||||
if (!document.body) {
|
||||
document.addEventListener('DOMContentLoaded', _initEdgeDockResizeHandles, { once: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const handles = {
|
||||
left: document.createElement('div'),
|
||||
right: document.createElement('div'),
|
||||
};
|
||||
const _setStyle = (el, prop, value) => {
|
||||
if (el.style[prop] !== value) el.style[prop] = value;
|
||||
};
|
||||
const _hideHandle = (handle) => _setStyle(handle, 'display', 'none');
|
||||
|
||||
for (const side of ['left', 'right']) {
|
||||
const handle = handles[side];
|
||||
handle.className = `edge-dock-resize-handle edge-dock-resize-handle-${side}`;
|
||||
handle.style.position = 'fixed';
|
||||
handle.style.top = '0';
|
||||
handle.style.bottom = '0';
|
||||
handle.style.width = '10px';
|
||||
handle.style.cursor = 'col-resize';
|
||||
handle.style.background = 'linear-gradient(to right, transparent 0 3px, color-mix(in srgb, var(--accent, var(--red)) 35%, transparent) 3px 7px, transparent 7px 10px)';
|
||||
handle.style.pointerEvents = 'auto';
|
||||
handle.style.touchAction = 'none';
|
||||
handle.style.display = 'none';
|
||||
handle.title = 'Drag to resize docked window';
|
||||
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 _hasVisibleFloatingModal = (owner) => {
|
||||
const all = Array.from(document.querySelectorAll('.modal:not(.hidden):not(.modal-minimized)'));
|
||||
return all.some((modal) => {
|
||||
if (!modal || modal === owner) return false;
|
||||
if (owner?.contains?.(modal) || modal.contains?.(owner)) return false;
|
||||
if (modal.classList.contains('modal-left-docked')
|
||||
|| modal.classList.contains('modal-right-docked')
|
||||
|| modal.classList.contains('email-snap-left')) return false;
|
||||
if (modal.style.display === 'none') return false;
|
||||
const content = _resolveDockNodes(modal)?.content;
|
||||
const r = content?.getBoundingClientRect?.();
|
||||
return !!r && r.width > 0 && r.height > 0;
|
||||
});
|
||||
};
|
||||
|
||||
const _setWidth = (owner, side, clientX) => {
|
||||
const nodes = _resolveDockNodes(owner);
|
||||
const content = nodes?.content;
|
||||
if (!content) 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;
|
||||
}
|
||||
} 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',
|
||||
);
|
||||
}
|
||||
_positionEdgeDockResizeHandles();
|
||||
return w;
|
||||
};
|
||||
|
||||
_edgeDockHandlePositioner = () => {
|
||||
const splitOwnsLeftSeam = document.body.classList.contains('email-doc-split-active')
|
||||
&& document.body.classList.contains('doc-view')
|
||||
&& window.innerWidth > 768;
|
||||
for (const side of ['left', 'right']) {
|
||||
const handle = handles[side];
|
||||
if (window.innerWidth <= 768 || (side === 'left' && splitOwnsLeftSeam)) {
|
||||
_hideHandle(handle);
|
||||
continue;
|
||||
}
|
||||
const owner = _activeDockOwner(side);
|
||||
const content = owner && _resolveDockNodes(owner)?.content;
|
||||
if (!content) {
|
||||
_hideHandle(handle);
|
||||
continue;
|
||||
}
|
||||
if (_hasVisibleFloatingModal(owner)) {
|
||||
_hideHandle(handle);
|
||||
continue;
|
||||
}
|
||||
const r = content.getBoundingClientRect();
|
||||
const x = side === 'right' ? r.left : r.right;
|
||||
if (!Number.isFinite(x) || x <= 0 || x >= window.innerWidth) {
|
||||
_hideHandle(handle);
|
||||
continue;
|
||||
}
|
||||
_setStyle(handle, 'display', 'block');
|
||||
_setStyle(handle, 'left', (x - 5) + 'px');
|
||||
_setStyle(handle, 'zIndex', String(_zIndexFor(owner) + 1));
|
||||
}
|
||||
};
|
||||
|
||||
for (const side of ['left', 'right']) {
|
||||
const handle = handles[side];
|
||||
handle.addEventListener('pointerdown', (e) => {
|
||||
if (handle.style.display === 'none') return;
|
||||
const owner = _activeDockOwner(side);
|
||||
if (!owner) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
handle.setPointerCapture?.(e.pointerId);
|
||||
const nodes = _resolveDockNodes(owner);
|
||||
const content = nodes?.content;
|
||||
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);
|
||||
const onMove = (ev) => {
|
||||
ev.preventDefault();
|
||||
_setWidth(owner, side, ev.clientX);
|
||||
};
|
||||
const onUp = (ev) => {
|
||||
try { handle.releasePointerCapture?.(e.pointerId); } catch (_) {}
|
||||
document.removeEventListener('pointermove', onMove, true);
|
||||
document.removeEventListener('pointerup', onUp, true);
|
||||
document.removeEventListener('pointercancel', onUp, true);
|
||||
document.body.classList.remove('edge-dock-resizing');
|
||||
document.body.style.cursor = prevCursor;
|
||||
document.body.style.userSelect = prevUserSelect;
|
||||
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);
|
||||
ev.preventDefault();
|
||||
};
|
||||
document.addEventListener('pointermove', onMove, true);
|
||||
document.addEventListener('pointerup', onUp, true);
|
||||
document.addEventListener('pointercancel', onUp, true);
|
||||
});
|
||||
}
|
||||
|
||||
new MutationObserver(_positionEdgeDockResizeHandles).observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
new MutationObserver(_positionEdgeDockResizeHandles).observe(document.documentElement, { attributes: true, attributeFilter: ['style'] });
|
||||
let raf = 0;
|
||||
const schedulePosition = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
_positionEdgeDockResizeHandles();
|
||||
});
|
||||
};
|
||||
new MutationObserver(schedulePosition).observe(document.body, { childList: true });
|
||||
window.addEventListener('resize', _positionEdgeDockResizeHandles);
|
||||
window.addEventListener('odysseus:modal-opened', _positionEdgeDockResizeHandles);
|
||||
_positionEdgeDockResizeHandles();
|
||||
})();
|
||||
|
||||
(function _initSplitSeamIndicator() {
|
||||
if (typeof document === 'undefined') return;
|
||||
const stripe = document.createElement('div');
|
||||
|
||||
@@ -53,7 +53,7 @@ function initDrag() {
|
||||
content,
|
||||
header,
|
||||
skipSelector: 'button, input, select, .theme-opacity-wrap',
|
||||
enableDock: false,
|
||||
enableDock: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -93,11 +93,11 @@ export function makeWindowDraggable(modal, options = {}) {
|
||||
}
|
||||
|
||||
const rightDock = enableDock ? makeEdgeDockController(modal, 'right') : null;
|
||||
// Left dock is opt-in (enableLeftDock). For most windows it's off — the
|
||||
// sidebar lives on the left, so a left dock collides with it. The email
|
||||
// window enables it so you can park the message on the left and read it
|
||||
// while replying in the document on the right.
|
||||
const leftDock = (enableDock && options.enableLeftDock) ? makeEdgeDockController(modal, 'left') : null;
|
||||
// Left dock is enabled by default too. modalSnap collapses the wide sidebar
|
||||
// and anchors the panel beside the icon rail, so it no longer collides with
|
||||
// the navigation. Callers can still pass enableLeftDock:false for a special
|
||||
// modal that should only dock right.
|
||||
const leftDock = (enableDock && options.enableLeftDock !== false) ? makeEdgeDockController(modal, 'left') : null;
|
||||
|
||||
// Per-drag state, reset on mousedown.
|
||||
let dragging = false;
|
||||
|
||||
Reference in New Issue
Block a user