import { ref, computed } from 'vue'; export interface ZoomOptionsStep { min: number; max: number; step: number; initial?: number; } export interface ZoomOptionsAllowed { allowedValues: number[]; initial?: number; } export type ZoomOptions = ZoomOptionsStep | ZoomOptionsAllowed; function isStepOptions(options: ZoomOptions): options is ZoomOptionsStep { return 'step' in options; } export function useZoom(options: ZoomOptions) { const initial = options.initial ?? (isStepOptions(options) ? 1 : (options.allowedValues[1] ?? options.allowedValues[0])); const zoom = ref(initial); const zoomPercent = computed(() => Math.round(zoom.value * 100)); const increase = () => { if (isStepOptions(options)) { zoom.value = Math.min(options.max, zoom.value + options.step); } else { const currentIndex = options.allowedValues.indexOf(zoom.value); if (currentIndex < options.allowedValues.length - 1) { zoom.value = options.allowedValues[currentIndex + 1]; } else if (currentIndex === -1) { // Find the nearest higher value const higher = options.allowedValues.find(v => v > zoom.value); if (higher !== undefined) { zoom.value = higher; } } } }; const decrease = () => { if (isStepOptions(options)) { zoom.value = Math.max(options.min, zoom.value - options.step); } else { const currentIndex = options.allowedValues.indexOf(zoom.value); if (currentIndex > 0) { zoom.value = options.allowedValues[currentIndex - 1]; } else if (currentIndex === -1) { // Find the nearest lower value const lower = [...options.allowedValues].reverse().find(v => v < zoom.value); if (lower !== undefined) { zoom.value = lower; } } } }; const reset = () => { zoom.value = initial; }; const setZoom = (value: number) => { if (isStepOptions(options)) { zoom.value = Math.max(options.min, Math.min(options.max, value)); } else { // Snap to nearest allowed value const nearest = options.allowedValues.reduce((prev, curr) => (Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev)); zoom.value = nearest; } }; return { zoom, zoomPercent, increase, decrease, reset, setZoom, }; }