Fixed issue scrolling on mobile
This commit is contained in:
parent
87033d7f9a
commit
69a0872029
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { brandApi } from '../services/api';
|
||||
import { Brand } from '../types';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface AddBrandModalProps {
|
||||
isOpen: boolean;
|
||||
@ -22,6 +23,9 @@ const AddBrandModal: React.FC<AddBrandModalProps> = ({ isOpen, onClose, onBrandA
|
||||
|
||||
const isEditMode = !!editBrand;
|
||||
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
// Initialize form data when editing
|
||||
useEffect(() => {
|
||||
if (editBrand) {
|
||||
@ -103,8 +107,19 @@ const AddBrandModal: React.FC<AddBrandModalProps> = ({ isOpen, onClose, onBrandA
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { GroceryCategory, GroceryCategoryCreate } from '../types';
|
||||
import { groceryCategoryApi } from '../services/api';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface AddGroceryCategoryModalProps {
|
||||
category?: GroceryCategory | null;
|
||||
@ -8,6 +9,9 @@ interface AddGroceryCategoryModalProps {
|
||||
}
|
||||
|
||||
const AddGroceryCategoryModal: React.FC<AddGroceryCategoryModalProps> = ({ category, onClose }) => {
|
||||
// Use body scroll lock when modal is open (always open when component is rendered)
|
||||
useBodyScrollLock(true);
|
||||
|
||||
const [formData, setFormData] = useState<GroceryCategoryCreate>({
|
||||
name: ''
|
||||
});
|
||||
@ -69,8 +73,19 @@ const AddGroceryCategoryModal: React.FC<AddGroceryCategoryModalProps> = ({ categ
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||
{isEditMode ? 'Edit Category' : 'Add New Category'}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { productApi, brandApi, groceryCategoryApi } from '../services/api';
|
||||
import { Product, Brand, GroceryCategory } from '../types';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface AddProductModalProps {
|
||||
isOpen: boolean;
|
||||
@ -34,6 +35,9 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
|
||||
|
||||
const weightUnits = ['piece', 'g', 'kg', 'lb', 'oz', 'ml', 'l'];
|
||||
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
// Fetch brands and categories when modal opens
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
@ -167,8 +171,19 @@ const AddProductModal: React.FC<AddProductModalProps> = ({ isOpen, onClose, onPr
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { shopApi, brandApi, brandInShopApi } from '../services/api';
|
||||
import { Shop, Brand, BrandInShop } from '../types';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface AddShopModalProps {
|
||||
isOpen: boolean;
|
||||
@ -29,6 +30,9 @@ const AddShopModal: React.FC<AddShopModalProps> = ({ isOpen, onClose, onShopAdde
|
||||
|
||||
const isEditMode = !!editShop;
|
||||
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
// Load brands when modal opens
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
@ -198,8 +202,19 @@ const AddShopModal: React.FC<AddShopModalProps> = ({ isOpen, onClose, onShopAdde
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white max-h-[80vh] overflow-y-auto">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white max-h-[80vh] overflow-y-auto"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Shop, Product, ShoppingEventCreate, ProductInEvent, ShoppingEvent, BrandInShop } from '../types';
|
||||
import { shopApi, productApi, shoppingEventApi, brandInShopApi } from '../services/api';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface AddShoppingEventModalProps {
|
||||
isOpen: boolean;
|
||||
@ -15,6 +16,9 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
|
||||
onEventAdded,
|
||||
editEvent
|
||||
}) => {
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
const [shops, setShops] = useState<Shop[]>([]);
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [shopBrands, setShopBrands] = useState<BrandInShop[]>([]);
|
||||
@ -273,8 +277,19 @@ const AddShoppingEventModal: React.FC<AddShoppingEventModalProps> = ({
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative min-h-screen md:min-h-0 md:top-10 mx-auto p-4 md:p-5 w-full md:max-w-4xl md:shadow-lg md:rounded-md bg-white">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative min-h-screen md:min-h-0 md:top-10 mx-auto p-4 md:p-5 w-full md:max-w-4xl md:shadow-lg md:rounded-md bg-white"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg md:text-xl font-medium text-gray-900">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface ConfirmDeleteModalProps {
|
||||
isOpen: boolean;
|
||||
@ -17,6 +18,9 @@ const ConfirmDeleteModal: React.FC<ConfirmDeleteModalProps> = ({
|
||||
message,
|
||||
isLoading = false
|
||||
}) => {
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
@ -38,8 +42,19 @@ const ConfirmDeleteModal: React.FC<ConfirmDeleteModalProps> = ({
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="flex-shrink-0">
|
||||
|
||||
@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import Papa from 'papaparse';
|
||||
import { Brand, GroceryCategory, Product } from '../types';
|
||||
import { brandApi, groceryCategoryApi, productApi } from '../services/api';
|
||||
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
|
||||
|
||||
interface ImportExportModalProps {
|
||||
isOpen: boolean;
|
||||
@ -18,6 +19,9 @@ interface ImportResult {
|
||||
}
|
||||
|
||||
const ImportExportModal: React.FC<ImportExportModalProps> = ({ isOpen, onClose, onDataChanged }) => {
|
||||
// Use body scroll lock when modal is open
|
||||
useBodyScrollLock(isOpen);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'export' | 'import'>('export');
|
||||
const [selectedEntity, setSelectedEntity] = useState<EntityType>('brands');
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -338,8 +342,19 @@ const ImportExportModal: React.FC<ImportExportModalProps> = ({ isOpen, onClose,
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
||||
<div className="relative top-10 mx-auto p-5 border w-full max-w-4xl shadow-lg rounded-md bg-white max-h-[90vh] overflow-y-auto">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50"
|
||||
onClick={(e) => {
|
||||
// Close modal if clicking on backdrop
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative top-10 mx-auto p-5 border w-full max-w-4xl shadow-lg rounded-md bg-white max-h-[90vh] overflow-y-auto"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mt-3">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-medium text-gray-900">
|
||||
|
||||
33
frontend/src/hooks/useBodyScrollLock.ts
Normal file
33
frontend/src/hooks/useBodyScrollLock.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useBodyScrollLock = (isLocked: boolean) => {
|
||||
useEffect(() => {
|
||||
if (!isLocked) return;
|
||||
|
||||
// Store original body overflow and position
|
||||
const originalOverflow = document.body.style.overflow;
|
||||
const originalPosition = document.body.style.position;
|
||||
const originalTop = document.body.style.top;
|
||||
const originalWidth = document.body.style.width;
|
||||
|
||||
// Get current scroll position
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
// Lock the body scroll
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.position = 'fixed';
|
||||
document.body.style.top = `-${scrollY}px`;
|
||||
document.body.style.width = '100%';
|
||||
|
||||
// Cleanup function to restore original styles
|
||||
return () => {
|
||||
document.body.style.overflow = originalOverflow;
|
||||
document.body.style.position = originalPosition;
|
||||
document.body.style.top = originalTop;
|
||||
document.body.style.width = originalWidth;
|
||||
|
||||
// Restore scroll position
|
||||
window.scrollTo(0, scrollY);
|
||||
};
|
||||
}, [isLocked]);
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user