fix(ui): modal drag + removed startDrag func (#2430)
* fixed * removed legacy startDrag fc, unified modal dragging * fixes post feedback
This commit is contained in:
107
static/app.js
107
static/app.js
@@ -13,6 +13,7 @@ import chatModule from './js/chat.js';
|
|||||||
import compareModule from './js/compare/index.js';
|
import compareModule from './js/compare/index.js';
|
||||||
import documentModule from './js/document.js';
|
import documentModule from './js/document.js';
|
||||||
import searchChatModule from './js/search-chat.js';
|
import searchChatModule from './js/search-chat.js';
|
||||||
|
import { makeWindowDraggable } from './js/windowDrag.js';
|
||||||
import markdownModule from './js/markdown.js';
|
import markdownModule from './js/markdown.js';
|
||||||
import chatRenderer from './js/chatRenderer.js';
|
import chatRenderer from './js/chatRenderer.js';
|
||||||
import sessionModule from './js/sessions.js';
|
import sessionModule from './js/sessions.js';
|
||||||
@@ -2683,82 +2684,38 @@ function initializeEventListeners() {
|
|||||||
// Apply saved visibility on load
|
// Apply saved visibility on load
|
||||||
applyUIVis(loadUIVis());
|
applyUIVis(loadUIVis());
|
||||||
|
|
||||||
// Generic draggable for all .modal elements
|
// The only two modals without a per-module makeWindowDraggable call. Wire
|
||||||
const _sharedDragModalIds = new Set(['settings-modal']);
|
// them onto the shared helper, drag-only, to match their old behavior.
|
||||||
try { document.querySelectorAll('.modal').forEach(m => {
|
try {
|
||||||
if (_sharedDragModalIds.has(m.id)) return;
|
['custom-preset-modal', 'rename-session-modal'].forEach((id) => {
|
||||||
const content = m.querySelector('.modal-content');
|
const m = document.getElementById(id);
|
||||||
const header = m.querySelector('.modal-header');
|
if (!m) return;
|
||||||
if (!content || !header) return;
|
const content = m.querySelector('.modal-content');
|
||||||
let dragX, dragY, startLeft, startTop, dragging = false;
|
const header = m.querySelector('.modal-header');
|
||||||
|
if (!content || !header) return;
|
||||||
// Reset to flex-centered position each time modal opens
|
makeWindowDraggable(m, {
|
||||||
new MutationObserver(() => {
|
content, header,
|
||||||
if (!m.classList.contains('hidden')) {
|
skipSelector: '.close-btn',
|
||||||
content.style.position = '';
|
enableDock: false,
|
||||||
content.style.left = '';
|
enableResize: false,
|
||||||
content.style.top = '';
|
|
||||||
content.style.right = '';
|
|
||||||
content.style.bottom = '';
|
|
||||||
content.style.margin = '';
|
|
||||||
}
|
|
||||||
}).observe(m, { attributes: true, attributeFilter: ['class'] });
|
|
||||||
|
|
||||||
function startDrag(clientX, clientY) {
|
|
||||||
dragging = true;
|
|
||||||
const rect = content.getBoundingClientRect();
|
|
||||||
dragX = clientX; dragY = clientY;
|
|
||||||
startLeft = rect.left; startTop = rect.top;
|
|
||||||
// Switch to fixed so it can be freely positioned
|
|
||||||
content.style.position = 'fixed';
|
|
||||||
content.style.left = startLeft + 'px';
|
|
||||||
content.style.top = startTop + 'px';
|
|
||||||
content.style.margin = '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
header.addEventListener('mousedown', (e) => {
|
|
||||||
if (e.target.closest('.close-btn')) return;
|
|
||||||
e.preventDefault();
|
|
||||||
startDrag(e.clientX, e.clientY);
|
|
||||||
document.addEventListener('mousemove', onDrag);
|
|
||||||
document.addEventListener('mouseup', stopDrag);
|
|
||||||
});
|
|
||||||
function onDrag(e) {
|
|
||||||
if (!dragging) return;
|
|
||||||
content.style.left = (startLeft + e.clientX - dragX) + 'px';
|
|
||||||
content.style.top = (startTop + e.clientY - dragY) + 'px';
|
|
||||||
}
|
|
||||||
function stopDrag() {
|
|
||||||
dragging = false;
|
|
||||||
document.removeEventListener('mousemove', onDrag);
|
|
||||||
document.removeEventListener('mouseup', stopDrag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touch drag is desktop-only — on mobile, modals are bottom sheets and
|
|
||||||
// ui.js handles swipe-down-to-dismiss. Attaching this listener fights
|
|
||||||
// the swipe-dismiss gesture.
|
|
||||||
if (window.innerWidth > 768) {
|
|
||||||
header.addEventListener('touchstart', (e) => {
|
|
||||||
if (e.target.closest('.close-btn')) return;
|
|
||||||
const t = e.touches[0];
|
|
||||||
startDrag(t.clientX, t.clientY);
|
|
||||||
document.addEventListener('touchmove', onTouchDrag, { passive: false });
|
|
||||||
document.addEventListener('touchend', stopTouchDrag);
|
|
||||||
});
|
});
|
||||||
}
|
// Re-center on open (these persist in the DOM). Guard on the
|
||||||
function onTouchDrag(e) {
|
// hidden→visible edge so it never fires mid-drag.
|
||||||
if (!dragging) return;
|
let wasHidden = m.classList.contains('hidden');
|
||||||
e.preventDefault();
|
new MutationObserver(() => {
|
||||||
const t = e.touches[0];
|
const isHidden = m.classList.contains('hidden');
|
||||||
content.style.left = (startLeft + t.clientX - dragX) + 'px';
|
if (wasHidden && !isHidden) {
|
||||||
content.style.top = (startTop + t.clientY - dragY) + 'px';
|
content.style.position = '';
|
||||||
}
|
content.style.left = '';
|
||||||
function stopTouchDrag() {
|
content.style.top = '';
|
||||||
dragging = false;
|
content.style.right = '';
|
||||||
document.removeEventListener('touchmove', onTouchDrag);
|
content.style.bottom = '';
|
||||||
document.removeEventListener('touchend', stopTouchDrag);
|
content.style.margin = '';
|
||||||
}
|
}
|
||||||
}); } catch(e) { console.error('Modal drag init error:', e); }
|
wasHidden = isHidden;
|
||||||
|
}).observe(m, { attributes: true, attributeFilter: ['class'] });
|
||||||
|
});
|
||||||
|
} catch (e) { console.error('Dialog drag init error:', e); }
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// ── Modal minimize → dock ──
|
// ── Modal minimize → dock ──
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ export function makeWindowDraggable(modal, options = {}) {
|
|||||||
const _startDrag = (cx, cy) => {
|
const _startDrag = (cx, cy) => {
|
||||||
dragging = true;
|
dragging = true;
|
||||||
if (modal) modal.classList.add('modal-dragging');
|
if (modal) modal.classList.add('modal-dragging');
|
||||||
|
// Cancel any in-flight open animation so we don't pin a mid-animation
|
||||||
|
// rect and then jump once the animation settles.
|
||||||
|
try {
|
||||||
|
content.getAnimations()
|
||||||
|
.filter(a => a.playState !== 'finished')
|
||||||
|
.forEach(a => a.cancel());
|
||||||
|
} catch (_) {}
|
||||||
const rect = content.getBoundingClientRect();
|
const rect = content.getBoundingClientRect();
|
||||||
if (onDragStart) {
|
if (onDragStart) {
|
||||||
try { onDragStart({ rect, cx, cy }); } catch (_) {}
|
try { onDragStart({ rect, cx, cy }); } catch (_) {}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
|
// - Other static assets (images/fonts/libs): cache-first with bg refresh.
|
||||||
// - API / non-GET: never cached.
|
// - API / non-GET: never cached.
|
||||||
// Bump CACHE_NAME whenever the precache list or SW logic changes.
|
// Bump CACHE_NAME whenever the precache list or SW logic changes.
|
||||||
const CACHE_NAME = 'odysseus-v326';
|
const CACHE_NAME = 'odysseus-v327';
|
||||||
|
|
||||||
// Core shell precached on install so repeat opens are instant without any
|
// Core shell precached on install so repeat opens are instant without any
|
||||||
// network wait. Keep this list in sync with the <script type="module"> tags
|
// network wait. Keep this list in sync with the <script type="module"> tags
|
||||||
|
|||||||
Reference in New Issue
Block a user