add arrows for youMayLike component
This commit is contained in:
@@ -1,67 +1,124 @@
|
||||
.youMightAlsoLikeContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
:global(.darkApp) .youMightAlsoLikeContainer path {
|
||||
fill: var(--primary);
|
||||
fill: var(--primary);
|
||||
}
|
||||
|
||||
.popularMenuItemImage {
|
||||
width: 73px;
|
||||
height: 73px;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
transition: transform 0.3s ease;
|
||||
width: 73px;
|
||||
height: 73px;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.popularMenuItemImage:hover {
|
||||
transform: scale(1.05);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.popularMenuItemImageMobile {
|
||||
width: 73px;
|
||||
height: 73px;
|
||||
min-height: 73px;
|
||||
object-fit: cover;
|
||||
width: 73px;
|
||||
height: 73px;
|
||||
min-height: 73px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.popularMenuItemImageTablet {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
border-radius: 12px;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.popularMenuItemImageDesktop {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
border-radius: 16px;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
[data-theme="dark"] .popularMenuItemImage {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.popularMenuItemImage:focus {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .popularMenuItemImage {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.popularMenuItemImage:focus {
|
||||
outline-offset: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hover effects for devices that support hover */
|
||||
@media (hover: hover) {
|
||||
.popularMenuItemImage:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.popularMenuItemImage:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus states for accessibility */
|
||||
.popularMenuItemImage:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.itemDescriptionIcons svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.arrowButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.arrowButton:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.arrowButton:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.arrowButton:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.arrowIcon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.arrowButton:hover .arrowIcon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Dark theme styles */
|
||||
[data-theme="dark"] .arrowButton {
|
||||
background: rgba(30, 30, 30, 0.95);
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .arrowButton:hover {
|
||||
background: rgba(40, 40, 40, 1);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { PlusOutlined } from "@ant-design/icons";
|
||||
import { Grid, Space } from "antd";
|
||||
import { Space } from "antd";
|
||||
import ArabicPrice from "components/ArabicPrice";
|
||||
import BackIcon from "components/Icons/BackIcon";
|
||||
import NextIcon from "components/Icons/NextIcon";
|
||||
import ImageWithFallback from "components/ImageWithFallback";
|
||||
import { ItemDescriptionIcons } from "components/ItemDescriptionIcons/ItemDescriptionIcons.tsx";
|
||||
import ProText from "components/ProText.tsx";
|
||||
import { menuItems } from "data/menuItems.ts";
|
||||
import { addItem } from "features/order/orderSlice.ts";
|
||||
import useBreakPoint from "hooks/useBreakPoint";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAppDispatch, useAppSelector } from "redux/hooks.ts";
|
||||
import { colors } from "ThemeConstants.ts";
|
||||
@@ -13,15 +17,88 @@ import { default_image } from "utils/constants.ts";
|
||||
import { Product } from "utils/types/appTypes.ts";
|
||||
import styles from "./YouMayAlsoLike.module.css";
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
export default function YouMightAlsoLike() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const { isRTL } = useAppSelector((state) => state.locale);
|
||||
const { sm, md } = useBreakpoint();
|
||||
const isMobile = !sm;
|
||||
const isTablet = sm && !md;
|
||||
const { isMobile, isTablet, isDesktop } = useBreakPoint();
|
||||
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Check if scrolling is needed based on container width
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const updateContainerWidth = () => {
|
||||
if (containerRef.current) {
|
||||
setContainerWidth(containerRef.current.offsetWidth);
|
||||
}
|
||||
};
|
||||
|
||||
updateContainerWidth();
|
||||
window.addEventListener("resize", updateContainerWidth);
|
||||
return () => window.removeEventListener("resize", updateContainerWidth);
|
||||
}, []);
|
||||
|
||||
// Calculate actual visible items based on container width
|
||||
const itemWidth = isMobile ? 95 + 16 : isTablet ? 120 + 16 : 140 + 16;
|
||||
const totalItemsWidth = menuItems.length * itemWidth;
|
||||
const needsScrolling = totalItemsWidth > containerWidth;
|
||||
|
||||
const canNavigateLeft = currentIndex > 0;
|
||||
const canNavigateRight =
|
||||
needsScrolling &&
|
||||
currentIndex * itemWidth + containerWidth < totalItemsWidth;
|
||||
|
||||
const scrollToIndex = (index: number) => {
|
||||
if (containerRef.current) {
|
||||
const scrollLeft = isRTL ? -(index * itemWidth) : index * itemWidth;
|
||||
containerRef.current.scrollTo({
|
||||
left: scrollLeft,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handlePrevious = () => {
|
||||
if (canNavigateLeft) {
|
||||
const newIndex = Math.max(0, currentIndex - 1);
|
||||
setCurrentIndex(newIndex);
|
||||
scrollToIndex(newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (canNavigateRight) {
|
||||
const maxPossibleIndex = Math.floor(
|
||||
(totalItemsWidth - containerWidth) / itemWidth,
|
||||
);
|
||||
const newIndex = Math.min(maxPossibleIndex, currentIndex + 1);
|
||||
setCurrentIndex(newIndex);
|
||||
scrollToIndex(newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
// RTL-specific handlers
|
||||
const handleRTLPrevious = () => {
|
||||
if (canNavigateRight) {
|
||||
const maxPossibleIndex = Math.floor(
|
||||
(totalItemsWidth - containerWidth) / itemWidth,
|
||||
);
|
||||
const newIndex = Math.min(maxPossibleIndex, currentIndex + 1);
|
||||
setCurrentIndex(newIndex);
|
||||
scrollToIndex(newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRTLNext = () => {
|
||||
if (canNavigateLeft) {
|
||||
const newIndex = Math.max(0, currentIndex - 1);
|
||||
setCurrentIndex(newIndex);
|
||||
scrollToIndex(newIndex);
|
||||
}
|
||||
};
|
||||
const handleQuickAdd = (item: Product) => {
|
||||
dispatch(
|
||||
addItem({
|
||||
@@ -51,119 +128,174 @@ export default function YouMightAlsoLike() {
|
||||
</ProText>
|
||||
</div>
|
||||
|
||||
<div className={styles.youMightAlsoLikeContainer}>
|
||||
{menuItems.map((item: Product) => (
|
||||
<div key={item.id}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: isMobile ? "95px" : isTablet ? "120px" : "140px",
|
||||
position: "relative",
|
||||
height: 200,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
padding: isTablet || isDesktop ? "0 16px" : "0",
|
||||
}}
|
||||
>
|
||||
{/* Left Arrow - Tablet and Desktop only */}
|
||||
{(isTablet || isDesktop) && (
|
||||
<button
|
||||
className={styles.arrowButton}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: isRTL ? "auto" : -25,
|
||||
right: isRTL ? -25 : "auto",
|
||||
top: "25%",
|
||||
transform: "translateY(-50%)",
|
||||
zIndex: 10,
|
||||
}}
|
||||
onClick={isRTL ? handleRTLPrevious : handlePrevious}
|
||||
aria-label="Previous items"
|
||||
>
|
||||
{isRTL ? (
|
||||
<NextIcon className={styles.arrowIcon} />
|
||||
) : (
|
||||
<BackIcon className={styles.arrowIcon} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Right Arrow - Tablet and Desktop only */}
|
||||
{(isTablet || isDesktop) && (
|
||||
<button
|
||||
className={styles.arrowButton}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: isRTL ? -10 : "auto",
|
||||
right: isRTL ? "auto" : -10,
|
||||
top: "25%",
|
||||
transform: "translateY(-50%)",
|
||||
zIndex: 10,
|
||||
}}
|
||||
onClick={isRTL ? handleRTLNext : handleNext}
|
||||
aria-label="Next items"
|
||||
>
|
||||
{isRTL ? (
|
||||
<BackIcon className={styles.arrowIcon} />
|
||||
) : (
|
||||
<NextIcon className={styles.arrowIcon} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div ref={containerRef} className={styles.youMightAlsoLikeContainer}>
|
||||
{menuItems.map((item: Product) => (
|
||||
<div key={item.id}>
|
||||
<div
|
||||
style={{
|
||||
width: isMobile ? 18 : 24,
|
||||
height: isMobile ? 18 : 24,
|
||||
borderRadius: "50%",
|
||||
top: isMobile ? 50 : 80,
|
||||
position: "absolute",
|
||||
right: isMobile ? 15 : 20,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "white",
|
||||
zIndex: 999,
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: isMobile ? "95px" : isTablet ? "120px" : "140px",
|
||||
position: "relative",
|
||||
height: 200,
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<PlusOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleQuickAdd(item);
|
||||
}}
|
||||
<div
|
||||
style={{
|
||||
color: colors.primary,
|
||||
fontSize: isMobile ? 14 : 16,
|
||||
width: isMobile ? 18 : 24,
|
||||
height: isMobile ? 18 : 24,
|
||||
borderRadius: "50%",
|
||||
top: isMobile ? 50 : 80,
|
||||
position: "absolute",
|
||||
[isRTL ? "left" : "right"]: isMobile ? 15 : 20,
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "white",
|
||||
zIndex: 999,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ImageWithFallback
|
||||
src={item.image}
|
||||
alt={item.name}
|
||||
className={`${styles.popularMenuItemImage} ${
|
||||
isMobile
|
||||
? styles.popularMenuItemImageMobile
|
||||
: isTablet
|
||||
? styles.popularMenuItemImageTablet
|
||||
: styles.popularMenuItemImageDesktop
|
||||
}`}
|
||||
width={isMobile ? 73 : isTablet ? 90 : 110}
|
||||
height={isMobile ? 73 : isTablet ? 90 : 110}
|
||||
fallbackSrc={default_image}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "start",
|
||||
marginTop: 5,
|
||||
...(isRTL ? { marginRight: -20 } : { marginLeft: -10 }),
|
||||
}}
|
||||
>
|
||||
<ItemDescriptionIcons className={styles.itemDescriptionIcons} />
|
||||
</div>
|
||||
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
style={{
|
||||
flex: 1,
|
||||
rowGap: 0,
|
||||
height: 40,
|
||||
}}
|
||||
>
|
||||
<div style={{ height: 25 }}>
|
||||
<ProText
|
||||
>
|
||||
<PlusOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleQuickAdd(item);
|
||||
}}
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
color: colors.primary,
|
||||
fontSize: isMobile ? 14 : 16,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ImageWithFallback
|
||||
src={item.image}
|
||||
alt={item.name}
|
||||
className={`${styles.popularMenuItemImage} ${
|
||||
isMobile
|
||||
? styles.popularMenuItemImageMobile
|
||||
: isTablet
|
||||
? styles.popularMenuItemImageTablet
|
||||
: styles.popularMenuItemImageDesktop
|
||||
}`}
|
||||
width={isMobile ? 73 : isTablet ? 90 : 110}
|
||||
height={isMobile ? 73 : isTablet ? 90 : 110}
|
||||
fallbackSrc={default_image}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "start",
|
||||
marginTop: 5,
|
||||
...(isRTL ? { marginRight: -20 } : { marginLeft: -10 }),
|
||||
}}
|
||||
>
|
||||
<ItemDescriptionIcons
|
||||
className={styles.itemDescriptionIcons}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="small"
|
||||
style={{
|
||||
flex: 1,
|
||||
rowGap: 0,
|
||||
height: 40,
|
||||
}}
|
||||
>
|
||||
<div style={{ height: 25 }}>
|
||||
<ProText
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
padding: 3,
|
||||
fontSize: isMobile ? 12 : isTablet ? 14 : 16,
|
||||
width: isMobile ? 80 : isTablet ? 100 : 120,
|
||||
display: "inline-block",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</ProText>
|
||||
</div>
|
||||
|
||||
<ArabicPrice
|
||||
price={item.price}
|
||||
style={{
|
||||
margin: 0,
|
||||
WebkitLineClamp: 1,
|
||||
WebkitBoxOrient: "vertical",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
padding: 3,
|
||||
fontSize: isMobile ? 12 : isTablet ? 14 : 16,
|
||||
width: isMobile ? 80 : isTablet ? 100 : 120,
|
||||
display: "inline-block",
|
||||
fontWeight: 600,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</ProText>
|
||||
</div>
|
||||
|
||||
<ArabicPrice
|
||||
price={item.price}
|
||||
style={{
|
||||
margin: 0,
|
||||
WebkitLineClamp: 1,
|
||||
WebkitBoxOrient: "vertical",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
padding: 3,
|
||||
fontSize: isMobile ? 12 : isTablet ? 14 : 16,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user