36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
import { h } from 'preact';
|
||
import { useState } from 'preact/hooks';
|
||
|
||
const LS_KEY = 'uopi:ui-zoom';
|
||
const STEPS = [0.5, 0.6, 0.75, 0.85, 1.0, 1.15, 1.3, 1.5, 1.75, 2.0, 2.5];
|
||
|
||
export function getStoredZoom(): number {
|
||
const v = parseFloat(localStorage.getItem(LS_KEY) ?? '1');
|
||
return isFinite(v) ? Math.max(STEPS[0], Math.min(STEPS[STEPS.length - 1], v)) : 1;
|
||
}
|
||
|
||
export function applyZoom(z: number): number {
|
||
const clamped = Math.max(STEPS[0], Math.min(STEPS[STEPS.length - 1], z));
|
||
localStorage.setItem(LS_KEY, String(clamped));
|
||
document.documentElement.style.fontSize = `${Math.round(16 * clamped)}px`;
|
||
return clamped;
|
||
}
|
||
|
||
export default function ZoomControl() {
|
||
const [zoom, setZoom] = useState(getStoredZoom);
|
||
|
||
function step(dir: number) {
|
||
const idx = STEPS.findIndex(s => s >= zoom - 0.01);
|
||
const nextIdx = Math.max(0, Math.min(STEPS.length - 1, idx + dir));
|
||
setZoom(applyZoom(STEPS[nextIdx]));
|
||
}
|
||
|
||
return (
|
||
<div class="zoom-control" title="UI zoom">
|
||
<button class="icon-btn zoom-btn" onClick={() => step(-1)} title="Zoom out (smaller UI)">A−</button>
|
||
<span class="zoom-pct">{Math.round(zoom * 100)}%</span>
|
||
<button class="icon-btn zoom-btn" onClick={() => step(1)} title="Zoom in (larger UI)">A+</button>
|
||
</div>
|
||
);
|
||
}
|