background image

This commit is contained in:
hamid zarghami
2026-06-22 09:20:08 +03:30
parent 3bb6ffac4e
commit e8a950b0b6
10 changed files with 286 additions and 61 deletions
+53 -4
View File
@@ -4,11 +4,15 @@ import { usePatternBackground } from "@/components/background/PatternBackgroundP
import { THEME_BG_ELEMENT_ID } from "@/lib/helpers/themeCache";
import { useLayoutEffect } from "react";
const THEME_BG_IMG_ID = "cached-theme-bg-img";
const THEME_BG_OVERLAY_ID = "cached-theme-bg-overlay";
export default function AppBackground() {
const { isPattern, backgroundStyle } = usePatternBackground();
const { isPattern, backgroundStyle, isCustomImage, customImageSettings } =
usePatternBackground();
useLayoutEffect(() => {
if (!isPattern) {
if (!isPattern && !isCustomImage) {
document.getElementById(THEME_BG_ELEMENT_ID)?.remove();
return;
}
@@ -25,8 +29,53 @@ export default function AppBackground() {
document.body.appendChild(element);
}
Object.assign(element.style, backgroundStyle as Record<string, string>);
}, [backgroundStyle, isPattern]);
if (isPattern) {
element.style.overflow = "";
document.getElementById(THEME_BG_IMG_ID)?.remove();
document.getElementById(THEME_BG_OVERLAY_ID)?.remove();
Object.assign(element.style, backgroundStyle as Record<string, string>);
return;
}
if (isCustomImage && customImageSettings) {
const { bgUrl, bgBlur, bgOpacity, bgOverlay } = customImageSettings;
element.style.backgroundColor = "";
element.style.backgroundImage = "";
element.style.overflow = "hidden";
let imgLayer = document.getElementById(THEME_BG_IMG_ID);
if (!imgLayer) {
imgLayer = document.createElement("div");
imgLayer.id = THEME_BG_IMG_ID;
imgLayer.style.position = "absolute";
imgLayer.style.inset = "-40px";
imgLayer.style.backgroundSize = "cover";
imgLayer.style.backgroundPosition = "center";
imgLayer.style.backgroundRepeat = "no-repeat";
element.appendChild(imgLayer);
}
imgLayer.style.backgroundImage = `url("${bgUrl}")`;
imgLayer.style.filter = bgBlur ? `blur(${bgBlur}px)` : "";
imgLayer.style.opacity =
bgOpacity != null ? String(bgOpacity / 100) : "1";
let overlayLayer = document.getElementById(THEME_BG_OVERLAY_ID);
if (!overlayLayer) {
overlayLayer = document.createElement("div");
overlayLayer.id = THEME_BG_OVERLAY_ID;
overlayLayer.style.position = "absolute";
overlayLayer.style.inset = "0";
element.appendChild(overlayLayer);
}
const overlayOpacity =
bgOverlay != null ? Number(bgOverlay) / 100 : 0;
overlayLayer.style.backgroundColor =
overlayOpacity > 0 ? `rgba(0,0,0,${overlayOpacity})` : "";
}
}, [backgroundStyle, isPattern, isCustomImage, customImageSettings]);
return null;
}
@@ -2,11 +2,15 @@
import { useGetAbout } from "@/app/[name]/(Main)/about/hooks/useAboutData";
import {
applyCustomImageCssVariables,
applyPatternCssVariables,
clearCustomImageCssVariables,
clearPatternCssVariables,
colorizeSvg,
hexToRgba,
isCustomImageBackground,
isPatternBackground,
normalizeBgNumber,
PATTERN_BACKGROUND_OPACITY,
} from "@/lib/helpers/backgroundUtils";
import {
@@ -16,6 +20,7 @@ import {
removeCachedThemeBackground,
setThemeCache,
svgToDataUrl,
themeBackgroundMatches,
type CachedRestaurantTheme,
} from "@/lib/helpers/themeCache";
import { useParams } from "next/navigation";
@@ -32,14 +37,25 @@ import {
const DEFAULT_MENU_COLOR = "#1E3A8A";
export type CustomImageSettings = {
bgUrl: string;
bgBlur: number | null;
bgOpacity: number | null;
bgOverlay: string | null;
};
type PatternBackgroundContextValue = {
isPattern: boolean;
isCustomImage: boolean;
backgroundStyle: CSSProperties;
customImageSettings: CustomImageSettings | null;
};
const PatternBackgroundContext = createContext<PatternBackgroundContextValue>({
isPattern: false,
isCustomImage: false,
backgroundStyle: {},
customImageSettings: null,
});
export function PatternBackgroundProvider({ children }: { children: ReactNode }) {
@@ -52,6 +68,10 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
const isPattern = restaurant
? isPatternBackground(restaurant)
: (hydratedCache?.isPattern ?? false);
const isCustomImage = restaurant
? isCustomImageBackground(restaurant)
: (!hydratedCache?.isPattern && !!hydratedCache?.bgUrl);
const menuColor =
restaurant?.menuColor || hydratedCache?.menuColor || DEFAULT_MENU_COLOR;
const baseTint = hexToRgba(menuColor, PATTERN_BACKGROUND_OPACITY);
@@ -84,15 +104,21 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
if (!themeBase.isPattern) {
clearPatternCssVariables();
removeCachedThemeBackground();
setPatternImageUrl(null);
setThemeCache(restaurantSlug, {
...themeBase,
patternDataUrl: null,
});
if (isCustomImageBackground(restaurant)) {
applyCustomImageCssVariables();
setThemeCache(restaurantSlug, { ...themeBase, patternDataUrl: null });
} else {
clearCustomImageCssVariables();
removeCachedThemeBackground();
setThemeCache(restaurantSlug, { ...themeBase, patternDataUrl: null });
}
return;
}
clearCustomImageCssVariables();
applyPatternCssVariables(menuColor);
if (!themeBase.bgUrl) {
@@ -110,9 +136,9 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
const cached = getThemeCache(restaurantSlug);
const cacheHit =
cached?.isPattern &&
cached.bgUrl === themeBase.bgUrl &&
cached.menuColor === menuColor &&
cached &&
themeBackgroundMatches(cached, restaurant) &&
cached.isPattern &&
cached.patternDataUrl;
if (cacheHit) {
@@ -178,9 +204,20 @@ export function PatternBackgroundProvider({ children }: { children: ReactNode })
[baseTint, patternImageUrl],
);
const customImageSettings = useMemo<CustomImageSettings | null>(() => {
const src = restaurant?.bgUrl || hydratedCache?.bgUrl;
if (!isCustomImage || !src) return null;
return {
bgUrl: src,
bgBlur: normalizeBgNumber(restaurant?.bgBlur ?? hydratedCache?.bgBlur),
bgOpacity: normalizeBgNumber(restaurant?.bgOpacity ?? hydratedCache?.bgOpacity),
bgOverlay: restaurant?.bgOverlay ?? hydratedCache?.bgOverlay ?? null,
};
}, [isCustomImage, restaurant, hydratedCache]);
const value = useMemo(
() => ({ isPattern, backgroundStyle }),
[backgroundStyle, isPattern],
() => ({ isPattern, isCustomImage, backgroundStyle, customImageSettings }),
[backgroundStyle, isPattern, isCustomImage, customImageSettings],
);
return (
+6 -3
View File
@@ -131,10 +131,14 @@ function PreferenceWrapper({ children }: Props) {
const restaurant = aboutData.data;
const cached = getThemeCache(restaurantSlug);
const themeBase = buildThemeCacheFromRestaurant(restaurant);
setThemeCache(restaurantSlug, {
menuColor,
bgUrl: restaurant.bgUrl,
isPattern: buildThemeCacheFromRestaurant(restaurant).isPattern,
bgBlur: themeBase.bgBlur,
bgOpacity: themeBase.bgOpacity,
bgOverlay: themeBase.bgOverlay,
isPattern: themeBase.isPattern,
patternDataUrl: cached?.patternDataUrl ?? null,
});
@@ -155,8 +159,7 @@ function PreferenceWrapper({ children }: Props) {
if (seenFingerprint === '|' && currentFingerprint !== '|') {
localStorage.setItem(getSplashStorageKey(restaurantSlug), currentFingerprint);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [aboutData?.data?.menuColor, restaurantSlug]);
}, [aboutData?.data, restaurantSlug]);
useEffect(() => {
document.documentElement.setAttribute('data-theme', nightMode ? 'dark' : 'light');