Add slider hover tooltip and CSV/paste import for custom arrays

This commit is contained in:
Dieter Schlüter 2026-04-06 19:58:05 +02:00
commit 01a4840393

View file

@ -73,6 +73,24 @@
cursor: pointer;
touch-action: none;
}
/* ── Slider Tooltip ── */
.slider-tooltip {
position: fixed;
background: var(--c-surface2, #1e2235);
border: 1px solid var(--c-border, #2a2d3d);
border-radius: 6px;
padding: 3px 8px;
font-size: 11px;
font-family: monospace;
color: var(--c-accent, #4a7cff);
pointer-events: none;
opacity: 0;
transition: opacity 0.15s ease;
z-index: 9999;
white-space: nowrap;
}
.slider-tooltip.visible { opacity: 1; }
input[type="range"]::-webkit-slider-runnable-track {
height: 6px;
background: var(--c-surface2);
@ -197,7 +215,7 @@
<div class="flex-1"></div>
<div class="text-center">
<h1 class="title-text font-bold tracking-tight" style="color: var(--c-text); text-shadow: 0 0 40px rgba(74,124,255,0.3);">
Sortier-Algorithmen <span style="font-size: 0.45em; font-weight: 400; opacity: 0.5; vertical-align: middle;">v0.2.8</span>
Sortier-Algorithmen <span style="font-size: 0.45em; font-weight: 400; opacity: 0.5; vertical-align: middle;">v0.2.9</span>
</h1>
<p class="text-muted text-sm mt-0.5">Interaktive Visualisierung mit schrittweiser Animation</p>
</div>
@ -275,10 +293,14 @@
<div class="space-y-1.5">
<label for="customArray" class="label-text uppercase tracking-wider text-muted font-medium flex items-center gap-1.5">
<i data-lucide="pencil" class="w-3.5 h-3.5"></i> Eigene Werte
<span id="customArrayCount" class="text-[10px] text-muted ml-auto normal-case tracking-normal font-mono"></span>
</label>
<div class="flex gap-1.5">
<input type="text" id="customArray" placeholder="z.B. 5, 3, 8, 1"
<input type="text" id="customArray" placeholder="z.B. 5, 3, 8, 1 oder Mehrfachpaste"
class="flex-1 min-w-0 bg-surface2 border border-border rounded-lg px-2 py-2 text-sm outline-none focus:border-accent focus:ring-1 focus:ring-accent/30 transition" style="color: var(--c-text);">
<button id="btnPasteCustom" class="px-2.5 min-w-[36px] flex items-center justify-center rounded-lg border border-border bg-surface2 hover:bg-accent hover:border-accent hover:text-white transition-colors" style="color: var(--c-text);" title="Aus Zwischenablage einfügen" aria-label="Aus Zwischenablage einfügen">
<i data-lucide="clipboard-paste" class="w-4 h-4"></i>
</button>
<button id="btnClearCustom" class="px-2.5 min-w-[36px] flex items-center justify-center rounded-lg border border-border bg-surface2 hover:bg-accent hover:border-accent hover:text-white transition-colors" style="color: var(--c-text);" title="Eigene Werte leeren">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
@ -316,6 +338,9 @@
</div>
</section>
<!-- Floating Slider Tooltip -->
<div id="sliderTooltip" class="slider-tooltip"></div>
<!-- Transport Buttons -->
<section class="bg-surface border border-border rounded-xl p-4">
<div class="grid grid-cols-4 sm:grid-cols-5 gap-2">
@ -809,6 +834,8 @@ const $algoSelect = document.getElementById('algoSelect');
const $presetSelect = document.getElementById('presetSelect');
const $customArray = document.getElementById('customArray');
const $btnClearCustom = document.getElementById('btnClearCustom');
const $btnPasteCustom = document.getElementById('btnPasteCustom');
const $customArrayCount = document.getElementById('customArrayCount');
const $sizeSlider = document.getElementById('sizeSlider');
const $speedSlider = document.getElementById('speedSlider');
const $threadSlider = document.getElementById('threadSlider');
@ -2840,14 +2867,41 @@ $presetSelect.addEventListener('change', function() {
$customArray.addEventListener('change', function() {
if (!isRunning) doReset();
updateCustomArrayCount();
});
$customArray.addEventListener('input', function() {
updateCustomArrayCount();
});
function updateCustomArrayCount() {
const text = $customArray.value.trim();
if (!text) {
$customArrayCount.textContent = '';
return;
}
const values = text.split(/[,;\s\n\t]+/).filter(function(v) { return v !== '' && !isNaN(parseFloat(v)); });
$customArrayCount.textContent = values.length + ' Wert' + (values.length === 1 ? '' : 'e');
}
$btnClearCustom.addEventListener('click', function() {
$customArray.value = '';
$customArrayCount.textContent = '';
$sizeSlider.disabled = false;
if (!isRunning) doReset();
});
$btnPasteCustom.addEventListener('click', async function() {
try {
const text = await navigator.clipboard.readText();
$customArray.value = text.replace(/[\r\n]+/g, ', ').replace(/,/g, ', ');
updateCustomArrayCount();
$customArray.dispatchEvent(new Event('change'));
} catch (e) {
// Clipboard API not available or permission denied
}
});
$btnTheme.addEventListener('click', function() {
const isDark = document.documentElement.classList.contains('dark');
const newTheme = !isDark;
@ -2922,6 +2976,49 @@ makeTouchSlider($sizeSlider);
makeTouchSlider($speedSlider);
makeTouchSlider($threadSlider);
// ================================================================
// Slider Tooltip on Hover
// ================================================================
const $sliderTooltip = document.getElementById('sliderTooltip');
const $allSliders = [$sizeSlider, $speedSlider, $threadSlider].filter(Boolean);
$allSliders.forEach(function(slider) {
slider.addEventListener('mouseenter', function() {
updateSliderTooltip(slider);
$sliderTooltip.classList.add('visible');
});
slider.addEventListener('mousemove', function(e) {
updateSliderTooltipPosition(e.clientX, e.clientY);
});
slider.addEventListener('mouseleave', function() {
$sliderTooltip.classList.remove('visible');
});
});
function updateSliderTooltip(slider) {
if (slider.id === 'sizeSlider') {
$sliderTooltip.textContent = slider.value + ' Elemente';
} else if (slider.id === 'speedSlider') {
const ms = Math.round(1000 * Math.pow(0.001, (parseInt(slider.value) - 1) / 99));
$sliderTooltip.textContent = ms + ' ms/Schritt';
} else if (slider.id === 'threadSlider') {
$sliderTooltip.textContent = slider.value + ' Thread(s)';
}
}
function updateSliderTooltipPosition(x, y) {
const tw = $sliderTooltip.offsetWidth;
const th = $sliderTooltip.offsetHeight;
let left = x - tw / 2;
let top = y - th - 12;
if (left < 8) left = 8;
if (left + tw > window.innerWidth - 8) left = window.innerWidth - tw - 8;
if (top < 8) top = y + 16;
$sliderTooltip.style.left = left + 'px';
$sliderTooltip.style.top = top + 'px';
}
// ================================================================
// URL State Persistence
// ================================================================