/* eslint-disable max-statements */ // Composables import { makeElevationProps } from "../../composables/elevation.mjs"; import { useRtl } from "../../composables/locale.mjs"; import { makeRoundedProps } from "../../composables/rounded.mjs"; // Utilities import { computed, provide, ref, shallowRef, toRef } from 'vue'; import { clamp, createRange, getDecimals, propsFactory } from "../../util/index.mjs"; // Types export const VSliderSymbol = Symbol.for('vuetify:v-slider'); export function getOffset(e, el, direction) { const vertical = direction === 'vertical'; const rect = el.getBoundingClientRect(); const touch = 'touches' in e ? e.touches[0] : e; return vertical ? touch.clientY - (rect.top + rect.height / 2) : touch.clientX - (rect.left + rect.width / 2); } function getPosition(e, position) { if ('touches' in e && e.touches.length) return e.touches[0][position];else if ('changedTouches' in e && e.changedTouches.length) return e.changedTouches[0][position];else return e[position]; } export const makeSliderProps = propsFactory({ disabled: { type: Boolean, default: null }, error: Boolean, readonly: { type: Boolean, default: null }, max: { type: [Number, String], default: 100 }, min: { type: [Number, String], default: 0 }, step: { type: [Number, String], default: 0 }, thumbColor: String, thumbLabel: { type: [Boolean, String], default: undefined, validator: v => typeof v === 'boolean' || v === 'always' }, thumbSize: { type: [Number, String], default: 20 }, showTicks: { type: [Boolean, String], default: false, validator: v => typeof v === 'boolean' || v === 'always' }, ticks: { type: [Array, Object] }, tickSize: { type: [Number, String], default: 2 }, color: String, trackColor: String, trackFillColor: String, trackSize: { type: [Number, String], default: 4 }, direction: { type: String, default: 'horizontal', validator: v => ['vertical', 'horizontal'].includes(v) }, reverse: Boolean, ...makeRoundedProps(), ...makeElevationProps({ elevation: 2 }), ripple: { type: Boolean, default: true } }, 'Slider'); export const useSteps = props => { const min = computed(() => parseFloat(props.min)); const max = computed(() => parseFloat(props.max)); const step = computed(() => +props.step > 0 ? parseFloat(props.step) : 0); const decimals = computed(() => Math.max(getDecimals(step.value), getDecimals(min.value))); function roundValue(value) { value = parseFloat(value); if (step.value <= 0) return value; const clamped = clamp(value, min.value, max.value); const offset = min.value % step.value; const newValue = Math.round((clamped - offset) / step.value) * step.value + offset; return parseFloat(Math.min(newValue, max.value).toFixed(decimals.value)); } return { min, max, step, decimals, roundValue }; }; export const useSlider = _ref => { let { props, steps, onSliderStart, onSliderMove, onSliderEnd, getActiveThumb } = _ref; const { isRtl } = useRtl(); const isReversed = toRef(props, 'reverse'); const vertical = computed(() => props.direction === 'vertical'); const indexFromEnd = computed(() => vertical.value !== isReversed.value); const { min, max, step, decimals, roundValue } = steps; const thumbSize = computed(() => parseInt(props.thumbSize, 10)); const tickSize = computed(() => parseInt(props.tickSize, 10)); const trackSize = computed(() => parseInt(props.trackSize, 10)); const numTicks = computed(() => (max.value - min.value) / step.value); const disabled = toRef(props, 'disabled'); const thumbColor = computed(() => props.error || props.disabled ? undefined : props.thumbColor ?? props.color); const trackColor = computed(() => props.error || props.disabled ? undefined : props.trackColor ?? props.color); const trackFillColor = computed(() => props.error || props.disabled ? undefined : props.trackFillColor ?? props.color); const mousePressed = shallowRef(false); const startOffset = shallowRef(0); const trackContainerRef = ref(); const activeThumbRef = ref(); function parseMouseMove(e) { const vertical = props.direction === 'vertical'; const start = vertical ? 'top' : 'left'; const length = vertical ? 'height' : 'width'; const position = vertical ? 'clientY' : 'clientX'; const { [start]: trackStart, [length]: trackLength } = trackContainerRef.value?.$el.getBoundingClientRect(); const clickOffset = getPosition(e, position); // It is possible for left to be NaN, force to number let clickPos = Math.min(Math.max((clickOffset - trackStart - startOffset.value) / trackLength, 0), 1) || 0; if (vertical ? indexFromEnd.value : indexFromEnd.value !== isRtl.value) clickPos = 1 - clickPos; return roundValue(min.value + clickPos * (max.value - min.value)); } const handleStop = e => { onSliderEnd({ value: parseMouseMove(e) }); mousePressed.value = false; startOffset.value = 0; }; const handleStart = e => { activeThumbRef.value = getActiveThumb(e); if (!activeThumbRef.value) return; activeThumbRef.value.focus(); mousePressed.value = true; if (activeThumbRef.value.contains(e.target)) { startOffset.value = getOffset(e, activeThumbRef.value, props.direction); } else { startOffset.value = 0; onSliderMove({ value: parseMouseMove(e) }); } onSliderStart({ value: parseMouseMove(e) }); }; const moveListenerOptions = { passive: true, capture: true }; function onMouseMove(e) { onSliderMove({ value: parseMouseMove(e) }); } function onSliderMouseUp(e) { e.stopPropagation(); e.preventDefault(); handleStop(e); window.removeEventListener('mousemove', onMouseMove, moveListenerOptions); window.removeEventListener('mouseup', onSliderMouseUp); } function onSliderTouchend(e) { handleStop(e); window.removeEventListener('touchmove', onMouseMove, moveListenerOptions); e.target?.removeEventListener('touchend', onSliderTouchend); } function onSliderTouchstart(e) { handleStart(e); window.addEventListener('touchmove', onMouseMove, moveListenerOptions); e.target?.addEventListener('touchend', onSliderTouchend, { passive: false }); } function onSliderMousedown(e) { e.preventDefault(); handleStart(e); window.addEventListener('mousemove', onMouseMove, moveListenerOptions); window.addEventListener('mouseup', onSliderMouseUp, { passive: false }); } const position = val => { const percentage = (val - min.value) / (max.value - min.value) * 100; return clamp(isNaN(percentage) ? 0 : percentage, 0, 100); }; const showTicks = toRef(props, 'showTicks'); const parsedTicks = computed(() => { if (!showTicks.value) return []; if (!props.ticks) { return numTicks.value !== Infinity ? createRange(numTicks.value + 1).map(t => { const value = min.value + t * step.value; return { value, position: position(value) }; }) : []; } if (Array.isArray(props.ticks)) return props.ticks.map(t => ({ value: t, position: position(t), label: t.toString() })); return Object.keys(props.ticks).map(key => ({ value: parseFloat(key), position: position(parseFloat(key)), label: props.ticks[key] })); }); const hasLabels = computed(() => parsedTicks.value.some(_ref2 => { let { label } = _ref2; return !!label; })); const data = { activeThumbRef, color: toRef(props, 'color'), decimals, disabled, direction: toRef(props, 'direction'), elevation: toRef(props, 'elevation'), hasLabels, isReversed, indexFromEnd, min, max, mousePressed, numTicks, onSliderMousedown, onSliderTouchstart, parsedTicks, parseMouseMove, position, readonly: toRef(props, 'readonly'), rounded: toRef(props, 'rounded'), roundValue, showTicks, startOffset, step, thumbSize, thumbColor, thumbLabel: toRef(props, 'thumbLabel'), ticks: toRef(props, 'ticks'), tickSize, trackColor, trackContainerRef, trackFillColor, trackSize, vertical }; provide(VSliderSymbol, data); return data; }; //# sourceMappingURL=slider.mjs.map