diff --git a/src/components/utilities/Modal.vue b/src/components/utilities/Modal.vue
index cc0cdfd..870546a 100644
--- a/src/components/utilities/Modal.vue
+++ b/src/components/utilities/Modal.vue
@@ -7,10 +7,12 @@
position: 'absolute',
left: `${position.x}px`,
top: `${position.y}px`,
+ width: `${size.width}px`,
+ height: `${size.height}px`,
}"
- class="bg-white rounded-2xl border-2 border-gray-300 shadow-xl max-w-4xl w-full max-h-[90vh] flex flex-col"
+ class="bg-white rounded-2xl border-2 border-gray-300 shadow-xl flex flex-col"
>
-
+
{{ title }}
@@ -35,6 +44,8 @@
const props = defineProps<{
isOpen: boolean;
title: string;
+ initialWidth?: number;
+ initialHeight?: number;
}>();
const emit = defineEmits<{
@@ -43,119 +54,128 @@
const modalRef = ref(null);
const position = ref({ x: 0, y: 0 });
- const isDragging = ref(false);
- const dragOffset = ref({ x: 0, y: 0 });
+ const size = ref({
+ width: props.initialWidth || 800,
+ height: props.initialHeight || 600,
+ });
+
+ const isDragging = ref(false);
+ const isResizing = ref(false);
+ const startPos = ref({ x: 0, y: 0 });
+ const startSize = ref({ width: 0, height: 0 });
+
+ // Unified start function for both drag and resize
+ const startAction = (event: MouseEvent | TouchEvent, action: 'drag' | 'resize') => {
+ event.preventDefault();
+ const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
+ const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
+
+ if (action === 'drag') {
+ isDragging.value = true;
+ startPos.value = {
+ x: clientX - position.value.x,
+ y: clientY - position.value.y,
+ };
+ } else {
+ isResizing.value = true;
+ startPos.value = { x: clientX, y: clientY };
+ startSize.value = { ...size.value };
+ }
+
+ document.addEventListener('mousemove', handleMove);
+ document.addEventListener('touchmove', handleMove, { passive: false });
+ document.addEventListener('mouseup', stopAction);
+ document.addEventListener('touchend', stopAction);
+ };
+
+ const startDrag = (event: MouseEvent | TouchEvent) => startAction(event, 'drag');
+ const startResize = (event: MouseEvent | TouchEvent) => startAction(event, 'resize');
+
+ const handleMove = (event: MouseEvent | TouchEvent) => {
+ if (!isDragging.value && !isResizing.value) return;
+ event.preventDefault();
+
+ const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
+ const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
+
+ if (isDragging.value) {
+ const newX = clientX - startPos.value.x;
+ const newY = clientY - startPos.value.y;
+ position.value = constrainPosition(newX, newY);
+ } else if (isResizing.value) {
+ const deltaX = clientX - startPos.value.x;
+ const deltaY = clientY - startPos.value.y;
+ size.value = constrainSize(startSize.value.width + deltaX, startSize.value.height + deltaY);
+ }
+ };
+
+ const stopAction = () => {
+ isDragging.value = false;
+ isResizing.value = false;
+ document.removeEventListener('mousemove', handleMove);
+ document.removeEventListener('touchmove', handleMove);
+ document.removeEventListener('mouseup', stopAction);
+ document.removeEventListener('touchend', stopAction);
+ };
+
+ const constrainPosition = (x: number, y: number) => {
+ if (!modalRef.value) return { x, y };
+ const modalRect = modalRef.value.getBoundingClientRect();
+ return {
+ x: Math.max(0, Math.min(x, window.innerWidth - modalRect.width)),
+ y: Math.max(0, Math.min(y, window.innerHeight - modalRect.height)),
+ };
+ };
+
+ const constrainSize = (width: number, height: number) => {
+ return {
+ width: Math.max(400, Math.min(width, window.innerWidth - position.value.x)),
+ height: Math.max(300, Math.min(height, window.innerHeight - position.value.y)),
+ };
+ };
- // Center the modal when it opens
const centerModal = () => {
if (!modalRef.value) return;
-
- const modalRect = modalRef.value.getBoundingClientRect();
- const viewportWidth = window.innerWidth;
- const viewportHeight = window.innerHeight;
-
position.value = {
- x: (viewportWidth - modalRect.width) / 2,
- y: (viewportHeight - modalRect.height) / 2,
+ x: (window.innerWidth - size.value.width) / 2,
+ y: (window.innerHeight - size.value.height) / 2,
};
};
- const startDrag = (event: MouseEvent | TouchEvent) => {
- if (!modalRef.value) return;
- event.preventDefault();
-
- isDragging.value = true;
-
- const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
- const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
-
- dragOffset.value = {
- x: clientX - position.value.x,
- y: clientY - position.value.y,
- };
-
- document.addEventListener('mousemove', drag);
- document.addEventListener('touchmove', drag, { passive: false });
- document.addEventListener('mouseup', stopDrag);
- document.addEventListener('touchend', stopDrag);
- };
-
- const drag = (event: MouseEvent | TouchEvent) => {
- if (!isDragging.value || !modalRef.value) return;
- event.preventDefault();
-
- const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
- const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
-
- const modalRect = modalRef.value.getBoundingClientRect();
- const viewportWidth = window.innerWidth;
- const viewportHeight = window.innerHeight;
-
- // Calculate new position
- let newX = clientX - dragOffset.value.x;
- let newY = clientY - dragOffset.value.y;
-
- // Constrain to viewport bounds
- newX = Math.max(0, Math.min(newX, viewportWidth - modalRect.width));
- newY = Math.max(0, Math.min(newY, viewportHeight - modalRect.height));
-
- position.value = { x: newX, y: newY };
- };
-
- const stopDrag = () => {
- isDragging.value = false;
- document.removeEventListener('mousemove', drag);
- document.removeEventListener('touchmove', drag);
- document.removeEventListener('mouseup', stopDrag);
- document.removeEventListener('touchend', stopDrag);
- };
-
const close = () => {
emit('close');
position.value = { x: 0, y: 0 };
};
- // Handle ESC key to close modal
+ // Event handlers
const handleKeyDown = (event: KeyboardEvent) => {
- if (event.key === 'Escape' && props.isOpen) {
- close();
- }
+ if (event.key === 'Escape' && props.isOpen) close();
};
- // Handle window resize
const handleResize = () => {
- if (!isDragging.value) {
- centerModal();
+ if (!isDragging.value && !isResizing.value) {
+ position.value = constrainPosition(position.value.x, position.value.y);
+ size.value = constrainSize(size.value.width, size.value.height);
}
};
- // Watch for modal opening
+ // Lifecycle
watch(
() => props.isOpen,
newValue => {
- if (newValue) {
- // Use nextTick to ensure the modal is mounted
- nextTick(() => {
- centerModal();
- });
- }
+ if (newValue) nextTick(centerModal);
}
);
onMounted(() => {
document.addEventListener('keydown', handleKeyDown);
window.addEventListener('resize', handleResize);
- if (props.isOpen) {
- centerModal();
- }
+ if (props.isOpen) centerModal();
});
onUnmounted(() => {
document.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('resize', handleResize);
- document.removeEventListener('mousemove', drag);
- document.removeEventListener('touchmove', drag);
- document.removeEventListener('mouseup', stopDrag);
- document.removeEventListener('touchend', stopDrag);
+ stopAction();
});