390 lines
14 KiB
HTML
390 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Zoom Control - FIXED VERSION</title>
|
|
|
|
<!-- Hyperscript - MUST load BEFORE component -->
|
|
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
|
|
|
|
<!-- Iconify for icons -->
|
|
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
|
|
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
font-family: Arial, sans-serif;
|
|
background: #f0f0f0;
|
|
min-height: 2000px; /* Make page scrollable for testing */
|
|
}
|
|
|
|
.test-area {
|
|
margin: 20px;
|
|
padding: 20px;
|
|
background: white;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.success-box {
|
|
background: #d4edda;
|
|
border: 2px solid #28a745;
|
|
color: #155724;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.test-steps {
|
|
background: #fff3cd;
|
|
border: 2px solid #ffc107;
|
|
color: #856404;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
/* Zoom Control Styles */
|
|
.zoom-control {
|
|
position: fixed;
|
|
bottom: 100px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
z-index: 900;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.65rem 1.25rem;
|
|
background: rgba(128, 128, 128, 0.7);
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
border-radius: 50px;
|
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
|
transition: all 0.3s ease;
|
|
opacity: 0.7;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
cursor: move;
|
|
user-select: none;
|
|
}
|
|
|
|
.zoom-control:hover {
|
|
opacity: 1;
|
|
background: rgba(128, 128, 128, 0.85);
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.zoom-close-btn {
|
|
position: absolute;
|
|
top: -8px;
|
|
right: -8px;
|
|
width: 24px;
|
|
height: 24px;
|
|
background: rgba(128, 128, 128, 0.6);
|
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
border-radius: 50%;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0;
|
|
transition: all 0.2s ease;
|
|
z-index: 1;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.zoom-close-btn:hover {
|
|
background: rgba(220, 53, 69, 0.9);
|
|
color: white;
|
|
opacity: 1;
|
|
transform: scale(1.1);
|
|
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.4);
|
|
}
|
|
|
|
.zoom-value {
|
|
color: rgba(255, 255, 255, 0.7);
|
|
font-size: 0.8rem;
|
|
font-weight: 400;
|
|
min-width: 30px;
|
|
text-align: center;
|
|
}
|
|
|
|
.zoom-slider {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 180px;
|
|
height: 5px;
|
|
border-radius: 3px;
|
|
background: rgba(200, 200, 200, 0.5);
|
|
outline: none;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.zoom-slider::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 50%;
|
|
background: white;
|
|
border: 2px solid rgba(180, 180, 180, 0.8);
|
|
cursor: pointer;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.zoom-reset-btn {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
|
border-radius: 50px;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
padding: 0.25rem 0.6rem;
|
|
cursor: pointer;
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
transition: all 0.2s ease;
|
|
min-width: 45px;
|
|
text-align: center;
|
|
}
|
|
|
|
.zoom-reset-btn:hover {
|
|
background: rgba(255, 255, 255, 0.3);
|
|
border-color: rgba(255, 255, 255, 0.6);
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
#show-zoom-menu-btn {
|
|
display: none;
|
|
position: fixed;
|
|
bottom: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: rgba(128, 128, 128, 0.9);
|
|
color: white;
|
|
border: none;
|
|
padding: 12px 24px;
|
|
border-radius: 25px;
|
|
cursor: pointer;
|
|
z-index: 899;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
#show-zoom-menu-btn:hover {
|
|
background: rgba(128, 128, 128, 1);
|
|
transform: translateX(-50%) scale(1.05);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="test-area">
|
|
<h1>🔧 Zoom Control - FIXED VERSION</h1>
|
|
|
|
<div class="success-box">
|
|
<h3>✅ Fixes Applied:</h3>
|
|
<ol>
|
|
<li><strong>X Button Fix</strong>: Added <code>pointer-events: none</code> to the iconify-icon element to prevent it from capturing clicks</li>
|
|
<li><strong>X Button Fix</strong>: Added <code>halt the event</code> to prevent event propagation</li>
|
|
<li><strong>Drag Fix</strong>: Improved the mousedown handler to properly check for interactive elements using both <code>classList.contains()</code> and <code>closest()</code></li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="test-steps">
|
|
<h3>🧪 Test Steps:</h3>
|
|
<ol>
|
|
<li><strong>Test X Button</strong>:
|
|
<ul>
|
|
<li>Click the X button (top-right corner of zoom control)</li>
|
|
<li>✅ Expected: Zoom control should disappear and "Show Zoom Control" button should appear at bottom</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Test Show Button</strong>:
|
|
<ul>
|
|
<li>Click the "Show Zoom Control" button</li>
|
|
<li>✅ Expected: Zoom control should reappear and button should disappear</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Test Dragging</strong>:
|
|
<ul>
|
|
<li>Click and hold on the grey background of the zoom control (NOT on slider/buttons)</li>
|
|
<li>Move your mouse while holding</li>
|
|
<li>✅ Expected: Zoom control should move with your mouse</li>
|
|
<li>Release the mouse</li>
|
|
<li>✅ Expected: Position should be saved (reload page to verify)</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Test Slider</strong>:
|
|
<ul>
|
|
<li>Click and drag the slider</li>
|
|
<li>✅ Expected: Slider should move, NOT trigger drag mode</li>
|
|
</ul>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<h2>Key Changes Made:</h2>
|
|
<pre style="background: #f8f9fa; padding: 15px; border-radius: 5px; overflow-x: auto;">
|
|
<strong>1. Close Button (zoom-control.html:76-81)</strong>
|
|
- Added: halt the event
|
|
- Added: style="pointer-events: none;" to iconify-icon
|
|
|
|
<strong>2. Drag Handler (zoom-control.html:26-42)</strong>
|
|
- Changed from: event.target.closest('.zoom-slider, .zoom-close-btn, .zoom-reset-btn')
|
|
- Changed to: Multiple specific checks:
|
|
* if target.classList.contains('zoom-slider') exit end
|
|
* if target.classList.contains('zoom-close-btn') exit end
|
|
* if target.classList.contains('zoom-reset-btn') exit end
|
|
* if target.closest('.zoom-close-btn') exit end
|
|
* if target.closest('.zoom-reset-btn') exit end
|
|
</pre>
|
|
|
|
<div style="margin-top: 40px; padding: 20px; background: #e7f3ff; border-radius: 8px;">
|
|
<h3>📝 Testing Checklist:</h3>
|
|
<p>Mark off as you test:</p>
|
|
<ul style="list-style: none; padding-left: 0;">
|
|
<li>☐ X button hides zoom control</li>
|
|
<li>☐ Show button reveals zoom control</li>
|
|
<li>☐ Dragging works (click on grey background)</li>
|
|
<li>☐ Slider doesn't trigger drag mode</li>
|
|
<li>☐ Reset button doesn't trigger drag mode</li>
|
|
<li>☐ Position persists after page reload</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Show Zoom Button (hidden by default) -->
|
|
<button id="show-zoom-menu-btn"
|
|
_="on click
|
|
halt the event
|
|
remove { display: 'none' } from #zoom-control
|
|
add { display: 'none' } to me
|
|
set localStorage['cv-zoom-visible'] to 'true'">
|
|
Show Zoom Control
|
|
</button>
|
|
|
|
<!-- Zoom Control Component - FIXED VERSION -->
|
|
<div id="zoom-control" class="zoom-control no-print" role="group" aria-label="Zoom control"
|
|
_="on load
|
|
if window.innerWidth <= 768
|
|
exit
|
|
end
|
|
set savedZoom to localStorage.getItem('cv-zoom')
|
|
if savedZoom
|
|
set my value to savedZoom
|
|
send input to #zoom-slider
|
|
end
|
|
set isVisible to localStorage.getItem('cv-zoom-visible')
|
|
if isVisible is 'false'
|
|
add { display: 'none' } to me
|
|
remove { display: 'none' } from #show-zoom-menu-btn
|
|
end
|
|
set savedPos to localStorage.getItem('cv-zoom-position')
|
|
if savedPos
|
|
set pos to JSON.parse(savedPos)
|
|
set my *bottom to pos.bottom
|
|
set my *left to pos.left
|
|
set my *transform to 'none'
|
|
end
|
|
|
|
on mousedown(clientX, clientY)
|
|
-- FIXED: Check if click is on interactive elements (slider, buttons)
|
|
set target to event.target
|
|
if target.classList.contains('zoom-slider') exit end
|
|
if target.classList.contains('zoom-close-btn') exit end
|
|
if target.classList.contains('zoom-reset-btn') exit end
|
|
if target.closest('.zoom-close-btn') exit end
|
|
if target.closest('.zoom-reset-btn') exit end
|
|
|
|
set isDragging to true
|
|
set my *transition to 'none'
|
|
|
|
set rect to my getBoundingClientRect()
|
|
set initialX to clientX - rect.left
|
|
set initialY to clientY - rect.top
|
|
|
|
halt the event
|
|
|
|
on mousemove(clientX, clientY) from document
|
|
if isDragging is not true exit end
|
|
|
|
halt the event
|
|
|
|
set currentX to clientX - initialX
|
|
set currentY to clientY - initialY
|
|
|
|
set maxX to window.innerWidth - my offsetWidth
|
|
set maxY to window.innerHeight - my offsetHeight
|
|
|
|
set currentX to Math.max(0, Math.min(currentX, maxX))
|
|
set currentY to Math.max(0, Math.min(currentY, maxY))
|
|
|
|
set my *left to `${currentX}px`
|
|
set my *bottom to `${window.innerHeight - currentY - my offsetHeight}px`
|
|
set my *transform to 'none'
|
|
|
|
on mouseup from document
|
|
if isDragging is not true exit end
|
|
|
|
set isDragging to false
|
|
set my *transition to 'all 0.3s ease'
|
|
|
|
set position to { bottom: my *bottom, left: my *left }
|
|
set localStorage['cv-zoom-position'] to JSON.stringify(position)">
|
|
|
|
<!-- FIXED: Close button with pointer-events: none on icon -->
|
|
<button
|
|
id="zoom-close"
|
|
class="zoom-close-btn"
|
|
aria-label="Close zoom control"
|
|
title="Close"
|
|
_="on click
|
|
halt the event
|
|
add { display: 'none' } to #zoom-control
|
|
remove { display: 'none' } from #show-zoom-menu-btn
|
|
set localStorage['cv-zoom-visible'] to 'false'">
|
|
<iconify-icon icon="mdi:close" width="16" height="16" style="pointer-events: none;"></iconify-icon>
|
|
</button>
|
|
|
|
<span class="zoom-value zoom-value-min" aria-hidden="true">25</span>
|
|
|
|
<input
|
|
type="range"
|
|
id="zoom-slider"
|
|
class="zoom-slider"
|
|
min="25"
|
|
max="175"
|
|
step="1"
|
|
value="100"
|
|
aria-label="Adjust zoom level"
|
|
_="on input
|
|
set zoomValue to my value as a Number
|
|
put zoomValue into #zoom-value-current
|
|
set localStorage['cv-zoom'] to zoomValue">
|
|
|
|
<span class="zoom-value zoom-value-max" aria-hidden="true">175</span>
|
|
|
|
<button
|
|
id="zoom-reset"
|
|
class="zoom-reset-btn"
|
|
aria-label="Reset zoom to 100%"
|
|
title="Reset"
|
|
_="on click
|
|
set #zoom-slider's value to 100
|
|
send input to #zoom-slider
|
|
send focus to #zoom-slider">
|
|
<span id="zoom-value-current">100</span>
|
|
</button>
|
|
</div>
|
|
|
|
<script>
|
|
console.log('🧪 Zoom Control Test Page - FIXED VERSION');
|
|
console.log('Open browser console to see debug messages');
|
|
console.log('localStorage keys used: cv-zoom, cv-zoom-visible, cv-zoom-position');
|
|
</script>
|
|
</body>
|
|
</html>
|