feat: integrate Sensei Spook mascot in 4 stances, speech bubbles, and style updates
This commit is contained in:
commit
e04672db2a
104 files changed
+8262
No files matched your search
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { BoothConfig } from "@/types/booth";
|
||||
import { SpookyAtmosphere } from "@/components/SpookyAtmosphere";
|
||||
import { useI18n } from "@/lib/i18n";
|
||||
import { Globe } from "lucide-react";
|
||||
|
||||
interface KioskFrameProps {
|
||||
config: BoothConfig;
|
||||
children: React.ReactNode;
|
||||
showHeaderLogo?: boolean;
|
||||
showLanguageToggle?: boolean;
|
||||
}
|
||||
|
||||
export const KioskFrame: React.FC<KioskFrameProps> = ({
|
||||
config,
|
||||
children,
|
||||
showHeaderLogo = true,
|
||||
showLanguageToggle = true,
|
||||
}) => {
|
||||
const [isModeFullscreen, setIsModeFullscreen] = useState(false);
|
||||
const { language, toggleLanguage, isWeChat } = useI18n();
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const isFull = params.get("mode") === "fullscreen";
|
||||
setIsModeFullscreen(isFull);
|
||||
|
||||
if (isFull) {
|
||||
const triggerFullscreen = () => {
|
||||
if (!document.fullscreenElement && document.documentElement.requestFullscreen) {
|
||||
document.documentElement.requestFullscreen().catch((err) => {
|
||||
console.log("[Kiosk] Fullscreen request prevented:", err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("pointerdown", triggerFullscreen, { once: true });
|
||||
return () => window.removeEventListener("pointerdown", triggerFullscreen);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const theme = config.theme;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-fullscreen={isModeFullscreen}
|
||||
className={`relative flex flex-col overflow-hidden select-none transition-all duration-300 ${
|
||||
isModeFullscreen
|
||||
? "w-screen h-screen max-w-none max-h-none rounded-none border-0 shadow-none"
|
||||
: "kiosk-viewport rounded-none sm:rounded-3xl border-4 border-black shadow-[0_12px_0_#090314,0_0_40px_rgba(255,109,0,0.25)]"
|
||||
}`}
|
||||
style={
|
||||
{
|
||||
"--brand-primary": theme.primaryColor,
|
||||
"--brand-accent": theme.accentColor,
|
||||
"--brand-bg": theme.backgroundColor,
|
||||
"--brand-surface": theme.surfaceColor,
|
||||
"--brand-text": theme.textColor,
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{/* Dynamic Martial Arts & Shanghai Halloween Atmosphere */}
|
||||
<SpookyAtmosphere />
|
||||
|
||||
{/* Floating Language Toggle (Top Right) */}
|
||||
{showLanguageToggle && (
|
||||
<div className="absolute top-[max(0.75rem,env(safe-area-inset-top))] right-4 z-30">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="flex items-center space-x-1.5 px-3 py-1.5 rounded-full bg-[#1b0f2e]/90 hover:bg-[#2b184a] text-amber-300 border-2 border-black shadow-[0_3px_0_#090314] text-xs font-black tracking-wider transition active:scale-95"
|
||||
title={language === "zh" ? "Switch to English" : "切换为中文"}
|
||||
>
|
||||
<Globe size={13} className="text-amber-400" />
|
||||
<span>{language === "zh" ? "EN" : "中"}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Brand Header Logo - 20% larger */}
|
||||
{showHeaderLogo && (
|
||||
<header className="no-print pt-[max(0.5rem,env(safe-area-inset-top))] sm:pt-3 pb-0.5 px-4 flex justify-center items-center z-20">
|
||||
<div className="relative flex items-center justify-center">
|
||||
{theme.logoUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={theme.logoUrl}
|
||||
alt={config.appName}
|
||||
className="w-56 sm:w-72 md:w-84 h-auto max-h-16 sm:max-h-20 object-contain drop-shadow-[0_4px_14px_rgba(0,0,0,0.85)] filter"
|
||||
/>
|
||||
) : (
|
||||
<h1 className="text-lg sm:text-xl font-black tracking-wider text-white uppercase drop-shadow-[0_3px_0_#090314]">
|
||||
{config.appName}
|
||||
</h1>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
|
||||
{/* Screen Content Layer */}
|
||||
<main className="flex-1 relative flex flex-col overflow-hidden z-10 w-full pl-[max(0px,env(safe-area-inset-left))] pr-[max(0px,env(safe-area-inset-right))]">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* Dojo Brand Footer - clean single line, smaller font, no emoji */}
|
||||
<footer className="no-print pt-0.5 pb-[max(0.75rem,env(safe-area-inset-bottom))] px-2 flex items-center justify-center z-20 select-none">
|
||||
<a
|
||||
href="https://www.shanghaikarate.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[8.5px] sm:text-[9.5px] text-white/50 hover:text-amber-300 transition-colors whitespace-nowrap overflow-hidden text-ellipsis tracking-wide"
|
||||
title="Shanghai Karate Club (shanghaikarate.com)"
|
||||
>
|
||||
<span>上海健志空手道馆 · Shanghai Karate Club · Est. 2008</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user