84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
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,
|
|
};
|
|
}
|