Skip to content

Commit 3d90d0f

Browse files
Merge pull request #127 from ThisIs-Developer/fix-dock-dragging
fix: toggle dock mode focus/state, restrict dock mode to desktop layout, and make floating panel draggable on tablet and desktop
2 parents 76333de + d1de548 commit 3d90d0f

2 files changed

Lines changed: 112 additions & 22 deletions

File tree

script.js

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3921,45 +3921,92 @@ This is a fully client-side application. Your content never leaves your browser
39213921
const panel = document.getElementById('find-replace-modal');
39223922
if (!handle || !panel) return;
39233923

3924-
handle.addEventListener('mousedown', (e) => {
3925-
if (isFrDocked) return;
3926-
if (e.target.closest('.find-replace-header-actions')) return;
3924+
const startDrag = (clientX, clientY) => {
39273925
isPanelDragging = true;
3928-
dragOffset.x = e.clientX - panel.offsetLeft;
3929-
dragOffset.y = e.clientY - panel.offsetTop;
3926+
dragOffset.x = clientX - panel.offsetLeft;
3927+
dragOffset.y = clientY - panel.offsetTop;
39303928
document.body.classList.add('resizing');
3931-
});
3929+
};
39323930

3933-
document.addEventListener('mousemove', (e) => {
3934-
if (!isPanelDragging || isFrDocked) return;
3935-
const x = e.clientX - dragOffset.x;
3936-
const y = e.clientY - dragOffset.y;
3931+
const moveDrag = (clientX, clientY) => {
3932+
const x = clientX - dragOffset.x;
3933+
const y = clientY - dragOffset.y;
39373934

39383935
// Keep panel inside viewport boundaries
39393936
const maxX = window.innerWidth - panel.offsetWidth;
39403937
const maxY = window.innerHeight - panel.offsetHeight;
39413938
panel.style.left = `${Math.max(0, Math.min(maxX, x))}px`;
39423939
panel.style.top = `${Math.max(0, Math.min(maxY, y))}px`;
39433940
panel.style.right = 'auto';
3944-
});
3941+
};
39453942

3946-
document.addEventListener('mouseup', () => {
3943+
const stopDrag = () => {
39473944
if (isPanelDragging) {
39483945
isPanelDragging = false;
39493946
document.body.classList.remove('resizing');
39503947
}
3948+
};
3949+
3950+
// Mouse events
3951+
handle.addEventListener('mousedown', (e) => {
3952+
if (isFrDocked) return;
3953+
if (window.innerWidth < 768) return; // Do NOT allow dragging on mobile layouts
3954+
if (e.target.closest('.find-replace-header-actions')) return;
3955+
startDrag(e.clientX, e.clientY);
3956+
});
3957+
3958+
document.addEventListener('mousemove', (e) => {
3959+
if (!isPanelDragging || isFrDocked) return;
3960+
moveDrag(e.clientX, e.clientY);
39513961
});
3962+
3963+
document.addEventListener('mouseup', stopDrag);
3964+
3965+
// Touch events for tablets
3966+
handle.addEventListener('touchstart', (e) => {
3967+
if (isFrDocked) return;
3968+
if (window.innerWidth < 768) return; // Do NOT allow dragging on mobile layouts
3969+
if (e.target.closest('.find-replace-header-actions')) return;
3970+
if (e.touches && e.touches[0]) {
3971+
startDrag(e.touches[0].clientX, e.touches[0].clientY);
3972+
}
3973+
}, { passive: true });
3974+
3975+
document.addEventListener('touchmove', (e) => {
3976+
if (!isPanelDragging || isFrDocked) return;
3977+
if (e.touches && e.touches[0]) {
3978+
moveDrag(e.touches[0].clientX, e.touches[0].clientY);
3979+
}
3980+
}, { passive: true });
3981+
3982+
document.addEventListener('touchend', stopDrag);
39523983
}
39533984

39543985
let frPreferredDocked = false;
39553986

39563987
function toggleFrDockMode(forceFloat = false) {
3988+
// If forceFloat is an Event (e.g. from click listener directly), treat as false
3989+
if (forceFloat instanceof Event || (forceFloat && typeof forceFloat === 'object')) {
3990+
forceFloat = false;
3991+
}
3992+
39573993
const panel = document.getElementById('find-replace-modal');
39583994
const dockBtn = document.getElementById('find-replace-dock');
39593995
const contentCont = document.querySelector('.content-container');
39603996
if (!panel || !dockBtn || !contentCont) return;
39613997

3962-
if (window.innerWidth <= 768 || forceFloat) {
3998+
// Save active element focus and selection before DOM movement
3999+
const activeEl = document.activeElement;
4000+
const activeId = activeEl ? activeEl.id : null;
4001+
const isInput = activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'SELECT' || activeEl.tagName === 'TEXTAREA');
4002+
let selStart = 0;
4003+
let selEnd = 0;
4004+
if (isInput && typeof activeEl.selectionStart === 'number') {
4005+
selStart = activeEl.selectionStart;
4006+
selEnd = activeEl.selectionEnd;
4007+
}
4008+
4009+
if (window.innerWidth < 1080 || forceFloat) {
39634010
isFrDocked = false;
39644011
panel.classList.remove('docked');
39654012
if (panel.parentElement !== document.body) {
@@ -3977,6 +4024,17 @@ This is a fully client-side application. Your content never leaves your browser
39774024

39784025
panel.style.display = 'flex';
39794026
applyPaneWidths();
4027+
4028+
// Restore focus and selection
4029+
if (activeId) {
4030+
const el = document.getElementById(activeId);
4031+
if (el) {
4032+
el.focus();
4033+
if (isInput && typeof el.selectionStart === 'number') {
4034+
el.setSelectionRange(selStart, selEnd);
4035+
}
4036+
}
4037+
}
39804038
return;
39814039
}
39824040

@@ -4018,6 +4076,17 @@ This is a fully client-side application. Your content never leaves your browser
40184076
// Ensure display is flex and recalculate split panes
40194077
panel.style.display = 'flex';
40204078
applyPaneWidths();
4079+
4080+
// Restore focus and selection after layout change
4081+
if (activeId) {
4082+
const el = document.getElementById(activeId);
4083+
if (el) {
4084+
el.focus();
4085+
if (isInput && typeof el.selectionStart === 'number') {
4086+
el.setSelectionRange(selStart, selEnd);
4087+
}
4088+
}
4089+
}
40214090
}
40224091

40234092
function updateFindControls() {
@@ -4143,7 +4212,7 @@ This is a fully client-side application. Your content never leaves your browser
41434212
let wasDockedPref = localStorage.getItem('find-replace-docked') === 'true';
41444213

41454214
// Force floating-only mode on mobile/tablet viewports
4146-
if (window.innerWidth <= 768) {
4215+
if (window.innerWidth < 1080) {
41474216
wasDockedPref = false;
41484217
}
41494218

@@ -4383,7 +4452,7 @@ This is a fully client-side application. Your content never leaves your browser
43834452
// Dock toggle handler
43844453
const dockBtn = document.getElementById('find-replace-dock');
43854454
if (dockBtn) {
4386-
dockBtn.addEventListener('click', toggleFrDockMode);
4455+
dockBtn.addEventListener('click', () => toggleFrDockMode(false));
43874456
}
43884457

43894458
// Advanced Drawer Toggle
@@ -4619,7 +4688,7 @@ This is a fully client-side application. Your content never leaves your browser
46194688
}
46204689

46214690
function startResize(e) {
4622-
if (window.innerWidth <= 768) return;
4691+
if (window.innerWidth < 1080) return;
46234692
if (currentViewMode !== 'split') return;
46244693
e.preventDefault();
46254694
isResizing = true;
@@ -4628,7 +4697,7 @@ This is a fully client-side application. Your content never leaves your browser
46284697
}
46294698

46304699
function startResizeTouch(e) {
4631-
if (window.innerWidth <= 768) return;
4700+
if (window.innerWidth < 1080) return;
46324701
if (currentViewMode !== 'split') return;
46334702
e.preventDefault();
46344703
isResizing = true;
@@ -4675,7 +4744,7 @@ This is a fully client-side application. Your content never leaves your browser
46754744
}
46764745

46774746
function applyPaneWidths() {
4678-
if (window.innerWidth <= 768) {
4747+
if (window.innerWidth < 1080) {
46794748
resetPaneWidths();
46804749
return;
46814750
}
@@ -4787,11 +4856,30 @@ This is a fully client-side application. Your content never leaves your browser
47874856

47884857
// Initialize resizer - Story 1.3
47894858
initResizer();
4859+
function constrainFloatingPanelPosition() {
4860+
const panel = document.getElementById('find-replace-modal');
4861+
if (!panel || isFrDocked || panel.style.display === 'none') return;
4862+
if (window.innerWidth < 768) return; // Mobile layout forces fixed responsive positioning via CSS
4863+
4864+
// Only adjust if the inline style has custom dragged coordinates
4865+
if (!panel.style.left || panel.style.left === 'auto') return;
4866+
4867+
const leftVal = parseFloat(panel.style.left) || 0;
4868+
const topVal = parseFloat(panel.style.top) || 0;
4869+
4870+
const maxX = window.innerWidth - panel.offsetWidth;
4871+
const maxY = window.innerHeight - panel.offsetHeight;
4872+
4873+
panel.style.left = `${Math.max(0, Math.min(maxX, leftVal))}px`;
4874+
panel.style.top = `${Math.max(0, Math.min(maxY, topVal))}px`;
4875+
}
4876+
47904877
window.addEventListener('resize', () => {
47914878
scheduleLineNumberUpdate();
4792-
if (window.innerWidth <= 768 && isFrDocked && isFindModalOpen) {
4879+
if (window.innerWidth < 1080 && isFrDocked && isFindModalOpen) {
47934880
toggleFrDockMode(true);
47944881
}
4882+
constrainFloatingPanelPosition();
47954883
});
47964884

47974885
// View Mode Button Event Listeners - Story 1.1

styles.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3379,13 +3379,15 @@ html[lang="ko"] .markdown-body h1, html[lang="ko"] .markdown-body h2, html[lang=
33793379
}
33803380

33813381
/* ========================================
3382-
MOBILE FIND PANEL RESPONSIVE FIXES
3382+
MOBILE & TABLET FIND PANEL RESPONSIVE FIXES
33833383
======================================== */
3384-
@media (max-width: 768px) {
3384+
@media (max-width: 1079px) {
33853385
#find-replace-dock {
33863386
display: none !important;
33873387
}
3388-
3388+
}
3389+
3390+
@media (max-width: 768px) {
33893391
/* Prevent full screen expansion of floating panel on small mobile viewports */
33903392
.find-replace-panel {
33913393
width: calc(100% - 24px) !important;

0 commit comments

Comments
 (0)