mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
215 lines
6.2 KiB
JavaScript
215 lines
6.2 KiB
JavaScript
import { getPercentFromPointerEvent } from "../utils/pointer.js";
|
|
import { createState } from "@videojs/store";
|
|
import { isNull } from "@videojs/utils/predicate";
|
|
import { clamp, roundToStep } from "@videojs/utils/number";
|
|
import { throttle } from "@videojs/utils/function";
|
|
//#region src/dom/ui/slider.ts
|
|
function createSlider(options) {
|
|
const input = createState({
|
|
pointerPercent: 0,
|
|
dragPercent: 0,
|
|
dragging: false,
|
|
pointing: false,
|
|
focused: false
|
|
});
|
|
const abort = new AbortController();
|
|
const changeThrottleMs = options.changeThrottle ?? 0;
|
|
let isDragging = false, cachedRTL = false, cachedRect = null, capturedPointerId = null, lastDragPercent = 0, committedOnRelease = false;
|
|
const throttledChange = changeThrottleMs > 0 ? throttle((percent) => options.onValueChange?.(percent), changeThrottleMs, { leading: true }) : null;
|
|
/** Fire `onValueChange` — throttled during drag when `changeThrottle > 0`. */
|
|
function fireChange(percent, duringDrag) {
|
|
if (duringDrag && throttledChange) throttledChange(percent);
|
|
else options.onValueChange?.(percent);
|
|
}
|
|
function releaseCapture() {
|
|
if (isNull(capturedPointerId)) return;
|
|
const id = capturedPointerId;
|
|
capturedPointerId = null;
|
|
try {
|
|
options.getElement().releasePointerCapture(id);
|
|
} catch {}
|
|
}
|
|
function endDrag() {
|
|
if (!isDragging) input.patch({ pointing: false });
|
|
else {
|
|
if (!committedOnRelease) options.onValueCommit?.(lastDragPercent);
|
|
isDragging = false;
|
|
input.patch({
|
|
dragging: false,
|
|
pointing: false
|
|
});
|
|
options.onDragEnd?.();
|
|
}
|
|
committedOnRelease = false;
|
|
cleanup();
|
|
}
|
|
function cleanup() {
|
|
throttledChange?.cancel();
|
|
capturedPointerId = null;
|
|
cachedRect = null;
|
|
}
|
|
const rootProps = {
|
|
onPointerDown(event) {
|
|
if (options.isDisabled()) return;
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
const el = options.getElement();
|
|
cachedRect = el.getBoundingClientRect();
|
|
cachedRTL = options.isRTL();
|
|
committedOnRelease = false;
|
|
releaseCapture();
|
|
capturedPointerId = event.pointerId;
|
|
el.setPointerCapture(event.pointerId);
|
|
const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
|
|
isDragging = true;
|
|
lastDragPercent = percent;
|
|
input.patch({
|
|
pointing: true,
|
|
dragging: true,
|
|
pointerPercent: percent,
|
|
dragPercent: percent
|
|
});
|
|
options.onDragStart?.();
|
|
options.onValueChange?.(percent);
|
|
options.getThumbElement?.()?.focus({
|
|
preventScroll: true,
|
|
focusVisible: false
|
|
});
|
|
},
|
|
onPointerMove(event) {
|
|
if (options.isDisabled()) return;
|
|
if (!isNull(capturedPointerId)) {
|
|
if (event.pointerType !== "touch" && event.buttons === 0) {
|
|
endDrag();
|
|
return;
|
|
}
|
|
const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
|
|
lastDragPercent = percent;
|
|
input.patch({
|
|
dragPercent: percent,
|
|
pointerPercent: percent
|
|
});
|
|
fireChange(percent, true);
|
|
return;
|
|
}
|
|
const percent = getPercentFromPointerEvent(event, options.getElement().getBoundingClientRect(), options.getOrientation(), options.isRTL());
|
|
input.patch({
|
|
pointing: true,
|
|
pointerPercent: percent
|
|
});
|
|
},
|
|
onPointerUp(event) {
|
|
if (options.isDisabled()) return;
|
|
event.stopPropagation();
|
|
if (isNull(capturedPointerId)) return;
|
|
const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
|
|
throttledChange?.cancel();
|
|
options.onValueChange?.(percent);
|
|
options.onValueCommit?.(percent);
|
|
committedOnRelease = true;
|
|
},
|
|
onPointerLeave() {
|
|
if (!isNull(capturedPointerId)) return;
|
|
input.patch({ pointing: false });
|
|
},
|
|
onLostPointerCapture() {
|
|
endDrag();
|
|
}
|
|
};
|
|
const thumbProps = {
|
|
onKeyDown(event) {
|
|
if (options.isDisabled()) {
|
|
if (event.key !== "Tab") event.preventDefault();
|
|
return;
|
|
}
|
|
const stepPercent = options.getStepPercent();
|
|
const largeStepPercent = options.getLargeStepPercent();
|
|
const rounded = roundToStep(options.getPercent(), stepPercent, 0);
|
|
const horizontalSign = options.isRTL() ? -1 : 1;
|
|
const step = event.shiftKey ? largeStepPercent : stepPercent;
|
|
let newPercent = null;
|
|
switch (event.key) {
|
|
case "ArrowRight":
|
|
newPercent = rounded + step * horizontalSign;
|
|
break;
|
|
case "ArrowLeft":
|
|
newPercent = rounded - step * horizontalSign;
|
|
break;
|
|
case "ArrowUp":
|
|
newPercent = rounded + step;
|
|
break;
|
|
case "ArrowDown":
|
|
newPercent = rounded - step;
|
|
break;
|
|
case "PageUp":
|
|
newPercent = rounded + largeStepPercent;
|
|
break;
|
|
case "PageDown":
|
|
newPercent = rounded - largeStepPercent;
|
|
break;
|
|
case "Home":
|
|
newPercent = 0;
|
|
break;
|
|
case "End":
|
|
newPercent = 100;
|
|
break;
|
|
}
|
|
if (newPercent !== null) {
|
|
event.preventDefault();
|
|
newPercent = clamp(newPercent, 0, 100);
|
|
input.patch({
|
|
pointerPercent: newPercent,
|
|
dragPercent: newPercent
|
|
});
|
|
options.onValueChange?.(newPercent);
|
|
options.onValueCommit?.(newPercent);
|
|
}
|
|
},
|
|
onFocus() {
|
|
input.patch({ focused: true });
|
|
},
|
|
onBlur() {
|
|
input.patch({ focused: false });
|
|
}
|
|
};
|
|
function adjustForAlignment(state) {
|
|
if (!options.adjustPercent || state.thumbAlignment !== "edge") return state;
|
|
const rootEl = options.getElement();
|
|
const thumbEl = options.getThumbElement?.();
|
|
if (!thumbEl) return state;
|
|
const isHorizontal = state.orientation === "horizontal";
|
|
const thumbSize = isHorizontal ? thumbEl.offsetWidth : thumbEl.offsetHeight;
|
|
const trackSize = isHorizontal ? rootEl.offsetWidth : rootEl.offsetHeight;
|
|
return {
|
|
...state,
|
|
fillPercent: options.adjustPercent(state.fillPercent, thumbSize, trackSize),
|
|
pointerPercent: options.adjustPercent(state.pointerPercent, thumbSize, trackSize)
|
|
};
|
|
}
|
|
let resizeObserver = null;
|
|
if (options.onResize) {
|
|
resizeObserver = new ResizeObserver(() => options.onResize());
|
|
resizeObserver.observe(options.getElement());
|
|
}
|
|
return {
|
|
input,
|
|
rootProps,
|
|
rootStyle: {
|
|
touchAction: "none",
|
|
userSelect: "none"
|
|
},
|
|
thumbProps,
|
|
adjustForAlignment,
|
|
destroy() {
|
|
if (abort.signal.aborted) return;
|
|
abort.abort();
|
|
resizeObserver?.disconnect();
|
|
releaseCapture();
|
|
cleanup();
|
|
}
|
|
};
|
|
}
|
|
//#endregion
|
|
export { createSlider };
|
|
|
|
//# sourceMappingURL=slider.js.map
|