ProBottomSheet: enhance UI

This commit is contained in:
2025-12-23 23:44:54 +03:00
parent 5c27303695
commit ee42afacf3
4 changed files with 71 additions and 28 deletions

View File

@@ -2,6 +2,7 @@ import { CloseOutlined } from "@ant-design/icons";
import { Button } from "antd"; import { Button } from "antd";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import { darkColors, lightColors } from "ThemeConstants";
interface ProBottomSheetProps { interface ProBottomSheetProps {
isOpen: boolean; isOpen: boolean;
@@ -112,24 +113,43 @@ export function ProBottomSheet({
[isDragging, currentSnap, snapPoints.length, onClose], [isDragging, currentSnap, snapPoints.length, onClose],
); );
// Track if drag started from handle/header area
const dragStartElementRef = useRef<HTMLElement | null>(null);
// Touch event handlers // Touch event handlers
const handleTouchStart = useCallback( const handleTouchStart = useCallback(
(e: React.TouchEvent) => { (e: React.TouchEvent) => {
startDrag(e.touches[0].clientY); // Only allow drag if starting from handle or header area
const target = e.target as HTMLElement;
const isHandle = target.closest('[style*="cursor: grab"]') !== null;
const isHeader = target.closest('[style*="borderBottom"]') !== null;
if (isHandle || isHeader) {
dragStartElementRef.current = target;
startDrag(e.touches[0].clientY);
}
}, },
[startDrag], [startDrag],
); );
const handleTouchMove = useCallback( const handleTouchMove = useCallback(
(e: React.TouchEvent) => { (e: React.TouchEvent) => {
// Only handle drag if it started from handle/header
if (!isDragging || !dragStartElementRef.current) return;
// Prevent scroll from affecting the bottom sheet
e.preventDefault();
handleDrag(e.touches[0].clientY); handleDrag(e.touches[0].clientY);
}, },
[handleDrag], [handleDrag, isDragging],
); );
const handleTouchEnd = useCallback( const handleTouchEnd = useCallback(
(e: React.TouchEvent) => { (e: React.TouchEvent) => {
endDrag(e.changedTouches[0].clientY); if (dragStartElementRef.current) {
endDrag(e.changedTouches[0].clientY);
dragStartElementRef.current = null;
}
}, },
[endDrag], [endDrag],
); );
@@ -192,7 +212,8 @@ export function ProBottomSheet({
right: 0, right: 0,
bottom: 0, bottom: 0,
height: snapPoints[currentSnap] || height, height: snapPoints[currentSnap] || height,
backgroundColor: themeName === "dark" ? "#0a0a0a" : "#ffffff", backgroundColor:
themeName === "dark" ? darkColors.bgColor : lightColors.bgColor,
borderTopLeftRadius: 20, borderTopLeftRadius: 20,
borderTopRightRadius: 20, borderTopRightRadius: 20,
boxShadow: boxShadow:
@@ -237,7 +258,8 @@ export function ProBottomSheet({
flex: 1, flex: 1,
overflow: "auto", overflow: "auto",
padding: "0 20px", padding: "0 20px",
backgroundColor: themeName === "dark" ? "#0a0a0a" : "#ffffff", backgroundColor:
themeName === "dark" ? darkColors.bgColor : lightColors.bgColor,
color: themeName === "dark" ? "#ffffff" : "#000000", color: themeName === "dark" ? "#ffffff" : "#000000",
...contentStyle, ...contentStyle,
}; };
@@ -320,7 +342,19 @@ export function ProBottomSheet({
</div> </div>
)} )}
<div style={contentWrapperStyle}>{children}</div> <div
style={contentWrapperStyle}
onTouchStart={(e) => {
// Prevent touch events in content from triggering sheet drag
e.stopPropagation();
}}
onTouchMove={(e) => {
// Allow normal scrolling in content
e.stopPropagation();
}}
>
{children}
</div>
</div> </div>
</> </>
); );

View File

@@ -44,6 +44,11 @@ export default function ProductCard({ item, setIsBottomSheetOpen }: Props) {
// } // }
}; };
const hasOptions =
(item.extras && item.extras.length > 0) ||
(item.variants && item.variants.length > 0) ||
(item.theExtrasGroups && item.theExtrasGroups.length > 0);
return ( return (
<> <>
<div <div
@@ -206,22 +211,24 @@ export default function ProductCard({ item, setIsBottomSheetOpen }: Props) {
</Badge> </Badge>
</div> </div>
</div> </div>
<ProText {hasOptions && (
style={{ <ProText
fontWeight: 400, style={{
fontStyle: "Regular", fontWeight: 400,
fontSize: "12px", fontStyle: "Regular",
lineHeight: "140%", fontSize: "12px",
letterSpacing: "0%", lineHeight: "140%",
textAlign: "center", letterSpacing: "0%",
position: "absolute", textAlign: "center",
bottom: 19, position: "absolute",
[isRTL ? "left" : "right"]: 26, bottom: 19,
color: "#A4A3AA", [isRTL ? "left" : "right"]: 26,
}} color: "#A4A3AA",
> }}
{t("menu.customizable")} >
</ProText> {t("menu.customizable")}
</ProText>
)}
</Card> </Card>
</div> </div>

View File

@@ -4,14 +4,13 @@ import ProText from "components/ProText";
import { useAppSelector } from "redux/hooks"; import { useAppSelector } from "redux/hooks";
import { default_image } from "utils/constants"; import { default_image } from "utils/constants";
// import PageTransition from "components/PageTransition/PageTransition"; // import PageTransition from "components/PageTransition/PageTransition";
import { Space } from "antd"; import { Divider, Space } from "antd";
import ArabicPrice from "components/ArabicPrice"; import ArabicPrice from "components/ArabicPrice";
import useBreakPoint from "hooks/useBreakPoint"; import useBreakPoint from "hooks/useBreakPoint";
import ExtraGroupsContainer from "pages/product/components/ExtraGroupsContainer.tsx"; import ExtraGroupsContainer from "pages/product/components/ExtraGroupsContainer.tsx";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { useGetMenuQuery } from "redux/api/others.ts"; import { useGetMenuQuery } from "redux/api/others.ts";
import { colors } from "ThemeConstants";
import { Extra, Product } from "utils/types/appTypes"; import { Extra, Product } from "utils/types/appTypes";
import BackButton from "../menu/components/BackButton"; import BackButton from "../menu/components/BackButton";
import ExtraComponent from "./components/Extra"; import ExtraComponent from "./components/Extra";
@@ -217,7 +216,7 @@ export default function ProductDetailPage({
<div <div
style={{ style={{
height: height:
viewportHeight > 0 isBottomSheetView ? "calc(90vh - 285px)" : viewportHeight > 0
? `${viewportHeight - 195}px` ? `${viewportHeight - 195}px`
: "calc(100dvh - 195px)", : "calc(100dvh - 195px)",
overflow: "auto", overflow: "auto",
@@ -262,6 +261,7 @@ export default function ProductDetailPage({
<div className={styles.productInfoSection}> <div className={styles.productInfoSection}>
<div className={styles.productHeader}> <div className={styles.productHeader}>
<div className={styles.productDetails}> <div className={styles.productDetails}>
<Divider style={{ margin: "8px 0 16px 0" }} />
<ProText <ProText
style={{ style={{
fontWeight: 400, fontWeight: 400,
@@ -316,12 +316,14 @@ export default function ProductDetailPage({
price={product?.price} price={product?.price}
style={{ style={{
fontSize: isDesktop ? "1.2rem" : "1rem", fontSize: isDesktop ? "1.2rem" : "1rem",
color: colors.primary,
marginTop: "12px", marginTop: "12px",
}} }}
/> />
</div> </div>
</div> </div>
{!hasCustomizationOptions && (
<Divider style={{ margin: "0 0 16px 0" }} />
)}
</div> </div>
{isDesktop && ( {isDesktop && (
@@ -344,7 +346,7 @@ export default function ProductDetailPage({
<Space <Space
orientation="vertical" orientation="vertical"
size="middle" size="middle"
style={{ width: "100%", padding: "0 1rem" }} style={{ width: "100%", padding: "0 1rem", gap: 0 }}
> >
{product?.variants?.length > 0 && variantLevels.length > 0 && ( {product?.variants?.length > 0 && variantLevels.length > 0 && (
<Variants <Variants

View File

@@ -220,7 +220,7 @@
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
gap: 0px; gap: 0px;
margin: 16px 0 24px 0; margin: 16px 0 16px 0;
} }
.productDetails { .productDetails {