Initial commit

This commit is contained in:
yuding
2026-04-20 10:13:52 +08:00
commit 7954b98fc2
50 changed files with 10143 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
import { onBeforeUnmount, onMounted, reactive, ref } from "vue";
import { clamp } from "../utils/format.js";
export function useDraggableModal(initialPosition = { x: 190, y: 140 }) {
const modalRef = ref(null);
const headerRef = ref(null);
const state = reactive({
dragging: false,
pointerId: null,
offsetX: 0,
offsetY: 0,
x: initialPosition.x,
y: initialPosition.y,
});
const style = reactive({
left: `${state.x}px`,
top: `${state.y}px`,
});
function clampToViewport() {
const modal = modalRef.value;
if (!modal) return;
const rect = modal.getBoundingClientRect();
const pad = 10;
const maxLeft = Math.max(pad, window.innerWidth - rect.width - pad);
const maxTop = Math.max(pad, window.innerHeight - rect.height - pad);
state.x = clamp(parseFloat(style.left), pad, maxLeft);
state.y = clamp(parseFloat(style.top), pad, maxTop);
style.left = `${state.x}px`;
style.top = `${state.y}px`;
}
function onHeaderPointerDown(e, closeSelector = ".close-btn") {
if (e.target.closest(closeSelector) || e.button !== 0) return;
const modal = modalRef.value;
if (!modal) return;
state.dragging = true;
state.pointerId = e.pointerId;
headerRef.value?.setPointerCapture?.(e.pointerId);
const rect = modal.getBoundingClientRect();
state.offsetX = e.clientX - rect.left;
state.offsetY = e.clientY - rect.top;
}
function onWindowPointerMove(e) {
if (!state.dragging) return;
if (state.pointerId != null && e.pointerId !== state.pointerId) return;
const modal = modalRef.value;
if (!modal) return;
const rect = modal.getBoundingClientRect();
const pad = 10;
const x = clamp(e.clientX - state.offsetX, pad, Math.max(pad, window.innerWidth - rect.width - pad));
const y = clamp(e.clientY - state.offsetY, pad, Math.max(pad, window.innerHeight - rect.height - pad));
style.left = `${x}px`;
style.top = `${y}px`;
}
function onWindowPointerUp(e) {
if (!state.dragging) return;
if (state.pointerId != null && e.pointerId !== state.pointerId) return;
state.dragging = false;
state.pointerId = null;
clampToViewport();
}
onMounted(() => {
window.addEventListener("pointermove", onWindowPointerMove);
window.addEventListener("pointerup", onWindowPointerUp);
window.addEventListener("pointercancel", onWindowPointerUp);
});
onBeforeUnmount(() => {
window.removeEventListener("pointermove", onWindowPointerMove);
window.removeEventListener("pointerup", onWindowPointerUp);
window.removeEventListener("pointercancel", onWindowPointerUp);
});
function resetPosition(x = initialPosition.x, y = initialPosition.y) {
state.x = x;
state.y = y;
style.left = `${x}px`;
style.top = `${y}px`;
}
return {
modalRef,
headerRef,
state,
style,
onHeaderPointerDown,
resetPosition,
clampToViewport,
};
}