feat: hide interface elements on mobile during replay mode and adjust overlay positioning
Build and Release / release (push) Successful in 37s

This commit is contained in:
taDuc
2026-06-16 00:58:36 +07:00
parent 495162ff43
commit 4f9f2cd854
4 changed files with 76 additions and 68 deletions
@@ -136,19 +136,12 @@ export default function ReplayPreviewOverlay({
} }
: { : {
position: "absolute", position: "absolute",
left: 18, left: 16,
right: 18, right: 16,
bottom: 20,
pointerEvents: "none", pointerEvents: "none",
display: "flex", display: "flex",
justifyContent: "center", justifyContent: "center",
...(sidebarOpen
? {
top: 130,
bottom: "auto",
}
: {
bottom: 110,
}),
} }
} }
> >
@@ -226,10 +219,10 @@ export default function ReplayPreviewOverlay({
<div <div
style={{ style={{
position: "absolute", position: "absolute",
top: 64, top: isLargeScreen ? 64 : 16,
left: "50%", left: "50%",
transform: "translateX(-50%)", transform: "translateX(-50%)",
width: "min(520px, calc(100% - 72px))", width: isLargeScreen ? "min(520px, calc(100% - 72px))" : "min(520px, calc(100% - 32px))",
borderRadius: 18, borderRadius: 18,
border: "1px solid rgba(148, 163, 184, 0.24)", border: "1px solid rgba(148, 163, 184, 0.24)",
background: "rgba(15, 23, 42, 0.9)", background: "rgba(15, 23, 42, 0.9)",
+39 -33
View File
@@ -63,6 +63,7 @@ type Props = {
onSidebarHeightChange?: (height: number) => void; onSidebarHeightChange?: (height: number) => void;
showViewportControls?: boolean; showViewportControls?: boolean;
hasAnyBottomPanel?: boolean; hasAnyBottomPanel?: boolean;
isReplayMode?: boolean;
}; };
export default function PreviewMapShell({ export default function PreviewMapShell({
@@ -113,6 +114,7 @@ export default function PreviewMapShell({
onSidebarHeightChange, onSidebarHeightChange,
showViewportControls = true, showViewportControls = true,
hasAnyBottomPanel = false, hasAnyBottomPanel = false,
isReplayMode = false,
}: Props) { }: Props) {
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [avatarUrl, setAvatarUrl] = useState<string | null>(null); const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
@@ -187,19 +189,21 @@ export default function PreviewMapShell({
height="100svh" height="100svh"
/> />
<TimelineBar {!(isReplayMode && isMobileOrTablet) ? (
year={timelineYear} <TimelineBar
onYearChange={onTimelineYearChange} year={timelineYear}
timeRange={timelineTimeRange} onYearChange={onTimelineYearChange}
onTimeRangeChange={onTimelineTimeRangeChange} timeRange={timelineTimeRange}
isLoading={isTimelineLoading} onTimeRangeChange={onTimelineTimeRangeChange}
disabled={timelineDisabled} isLoading={isTimelineLoading}
statusText={timelineStatusText} disabled={timelineDisabled}
filterEnabled={timelineFilterEnabled} statusText={timelineStatusText}
onFilterEnabledChange={onTimelineFilterEnabledChange} filterEnabled={timelineFilterEnabled}
style={timelineStyle} onFilterEnabledChange={onTimelineFilterEnabledChange}
onPlayReplay={onPlayPreviewReplay} style={timelineStyle}
/> onPlayReplay={onPlayPreviewReplay}
/>
) : null}
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
@keyframes slideDown { @keyframes slideDown {
@@ -214,30 +218,31 @@ export default function PreviewMapShell({
} }
`}} /> `}} />
<aside {!(isReplayMode && isMobileOrTablet) ? (
style={{ <aside
position: "absolute",
top: 10,
bottom: (hasBottomPanel && isMobileOrTablet) ? `${(sidebarHeight || 400) + 20}px` : 20,
left: 18,
zIndex: 70,
display: "flex",
flexDirection: "column",
gap: 12,
width: 58,
pointerEvents: "none",
transition: "bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
}}
>
<div
style={{ style={{
position: "absolute",
top: 10,
bottom: (hasBottomPanel && isMobileOrTablet) ? `${(sidebarHeight || 400) + 20}px` : 20,
left: 18,
zIndex: 70,
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
gap: 8, gap: 12,
alignItems: "center", width: 58,
pointerEvents: "auto", pointerEvents: "none",
transition: "bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
}} }}
> >
<div
style={{
display: "flex",
flexDirection: "column",
gap: 8,
alignItems: "center",
pointerEvents: "auto",
}}
>
<button <button
type="button" type="button"
onClick={() => setIsMenuOpen(!isMenuOpen)} onClick={() => setIsMenuOpen(!isMenuOpen)}
@@ -418,6 +423,7 @@ export default function PreviewMapShell({
</div> </div>
)} )}
</aside> </aside>
) : null}
@@ -461,7 +467,7 @@ export default function PreviewMapShell({
</aside> </aside>
) : null} ) : null}
<ChatbotWidget hideFloatingButton /> {!(isReplayMode && isMobileOrTablet) && <ChatbotWidget hideFloatingButton />}
{children} {children}
</div> </div>
</div> </div>
@@ -825,6 +825,7 @@ export default function PublicPreviewClientPage({
onSelectFeatureIds={setSelectedFeatureIds} onSelectFeatureIds={setSelectedFeatureIds}
instantLoad={instantLoad} instantLoad={instantLoad}
onToggleInstantLoad={toggleInstantLoad} onToggleInstantLoad={toggleInstantLoad}
isReplayMode={replayMode !== "idle"}
isLayerPanelVisible={isLayerPanelVisible} isLayerPanelVisible={isLayerPanelVisible}
onLayerPanelVisibleChange={setIsLayerPanelVisible} onLayerPanelVisibleChange={setIsLayerPanelVisible}
backgroundVisibility={backgroundVisibility} backgroundVisibility={backgroundVisibility}
@@ -888,30 +889,32 @@ export default function PublicPreviewClientPage({
) : null ) : null
} }
> >
<div style={searchBarWrapperStyle}> {!(replayMode !== "idle" && !isLargeScreen) ? (
<PresentPlaceSearch <div style={searchBarWrapperStyle}>
focusedPlace={focusedPresentPlace} <PresentPlaceSearch
onFocusPlace={handleFocusPresentPlace} focusedPlace={focusedPresentPlace}
onFocusHistoricalGeometry={handleFocusHistoricalGeometry} onFocusPlace={handleFocusPresentPlace}
onFocusWiki={handleFocusWiki} onFocusHistoricalGeometry={handleFocusHistoricalGeometry}
onClearFocus={clearPresentPlaceFocus} onFocusWiki={handleFocusWiki}
style={{ onClearFocus={clearPresentPlaceFocus}
position: "relative", style={{
top: 0, position: "relative",
left: 0, top: 0,
transform: "none", left: 0,
width: searchBarWidth, transform: "none",
}} width: searchBarWidth,
/> }}
{isLargeScreen ? (
<PublicMapZoomPanel
mapHandleRef={mapHandleRef}
onPlayPreviewReplay={activeReplay && replayMode === "idle" ? handlePlayPreviewReplay : undefined}
onResumePreviewReplay={replayMode === "paused" ? handleResumePreviewReplay : undefined}
onStopPreviewReplay={replayMode === "playing" ? handleStopPreviewReplay : undefined}
/> />
) : null} {isLargeScreen ? (
</div> <PublicMapZoomPanel
mapHandleRef={mapHandleRef}
onPlayPreviewReplay={activeReplay && replayMode === "idle" ? handlePlayPreviewReplay : undefined}
onResumePreviewReplay={replayMode === "paused" ? handleResumePreviewReplay : undefined}
onStopPreviewReplay={replayMode === "playing" ? handleStopPreviewReplay : undefined}
/>
) : null}
</div>
) : null}
<FirstVisitGuideModal /> <FirstVisitGuideModal />
</PreviewMapShell> </PreviewMapShell>
)} )}
+6
View File
@@ -24,6 +24,12 @@ export const uiActions = {
setSidebarOpen(false); setSidebarOpen(false);
onSelectWiki(""); onSelectWiki("");
} else { } else {
// Hạn chế không tự động mở wiki trong chế độ Replay trên mobile/tablet để tránh đè giao diện
if (typeof window !== "undefined" && window.innerWidth < 1024) {
setSidebarOpen(false);
onSelectWiki("");
return;
}
setSidebarOpen(true); setSidebarOpen(true);
onSelectWiki(wikiId); onSelectWiki(wikiId);
} }