From 9a5dfdb2edab879dc43e16c7c012f8018bb86419 Mon Sep 17 00:00:00 2001 From: taDuc Date: Thu, 4 Jun 2026 18:39:46 +0700 Subject: [PATCH] feat: update public preview UI with adjustable sidebar height, layer panel toggle, and synchronized data loading states(mobile UI) --- src/components/ui/chat/ChatbotWidget.tsx | 12 +- src/uhm/components/Map.tsx | 128 ++++----- .../components/editor/PresentPlaceSearch.tsx | 3 +- .../editor/ReplayPreviewLayerPanel.tsx | 213 ++++++++------- src/uhm/components/map/useMapHoverPopup.ts | 6 +- src/uhm/components/map/useMapSync.ts | 30 ++- src/uhm/components/preview/MapPlaceholder.tsx | 17 +- src/uhm/components/preview/PreviewLayout.tsx | 2 +- .../components/preview/PreviewMapShell.tsx | 223 +++++++++++++--- .../preview/PublicPreviewClientPage.tsx | 42 ++- .../preview/PublicPreviewWrapper.tsx | 20 +- .../preview/hooks/usePublicPreviewData.ts | 16 +- src/uhm/components/ui/TimelineBar.tsx | 245 ++++++++++++++++-- src/uhm/components/wiki/PublicWikiSidebar.tsx | 93 ++++++- 14 files changed, 801 insertions(+), 249 deletions(-) diff --git a/src/components/ui/chat/ChatbotWidget.tsx b/src/components/ui/chat/ChatbotWidget.tsx index 7c60ec5..6f57656 100644 --- a/src/components/ui/chat/ChatbotWidget.tsx +++ b/src/components/ui/chat/ChatbotWidget.tsx @@ -14,8 +14,10 @@ type Message = { export default function ChatbotWidget({ projectId = "", + hideFloatingButton = false, }: { projectId?: string; + hideFloatingButton?: boolean; }) { const [isOpen, setIsOpen] = useState(false); const [messages, setMessages] = useState([ @@ -33,6 +35,14 @@ export default function ChatbotWidget({ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; + useEffect(() => { + const handleToggle = () => setIsOpen((prev) => !prev); + window.addEventListener("toggle-chatbot", handleToggle); + return () => { + window.removeEventListener("toggle-chatbot", handleToggle); + }; + }, []); + useEffect(() => { if (isOpen) { scrollToBottom(); @@ -93,7 +103,7 @@ export default function ChatbotWidget({ return (
- {!isOpen && ( + {!isOpen && !hideFloatingButton && ( ) : null} - +
+ - { - event.stopPropagation(); - try { - event.currentTarget.setPointerCapture(event.pointerId); - } catch { - // Browser may reject capture for non-primary pointers; drag lock still works. - } - beginZoomSliderDrag(); - }} - onPointerUp={(event) => { - event.stopPropagation(); - try { - event.currentTarget.releasePointerCapture(event.pointerId); - } catch { - // Ignore if capture was already released. - } - endZoomSliderDrag(); - }} - onPointerCancel={endZoomSliderDrag} - onBlur={endZoomSliderDrag} - onChange={(event) => handleZoomSliderChange(Number(event.target.value))} - aria-label="Map zoom" - /> + { + event.stopPropagation(); + try { + event.currentTarget.setPointerCapture(event.pointerId); + } catch { + // Browser may reject capture for non-primary pointers; drag lock still works. + } + beginZoomSliderDrag(); + }} + onPointerUp={(event) => { + event.stopPropagation(); + try { + event.currentTarget.releasePointerCapture(event.pointerId); + } catch { + // Ignore if capture was already released. + } + endZoomSliderDrag(); + }} + onPointerCancel={endZoomSliderDrag} + onBlur={endZoomSliderDrag} + onChange={(event) => handleZoomSliderChange(Number(event.target.value))} + aria-label="Map zoom" + /> - + -
- {zoomLevel.toFixed(1)}x +
+ {zoomLevel.toFixed(1)}x +
diff --git a/src/uhm/components/editor/PresentPlaceSearch.tsx b/src/uhm/components/editor/PresentPlaceSearch.tsx index c44a29a..3bf2246 100644 --- a/src/uhm/components/editor/PresentPlaceSearch.tsx +++ b/src/uhm/components/editor/PresentPlaceSearch.tsx @@ -282,7 +282,8 @@ export default function PresentPlaceSearch({ style={{ position: "absolute", top: 10, - left: leftOffset, + left: "50%", + transform: "translateX(-50%)", zIndex: 18, width: "min(392px, calc(100vw - 36px))", pointerEvents: "auto", diff --git a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx index b172af8..3c34065 100644 --- a/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx +++ b/src/uhm/components/editor/ReplayPreviewLayerPanel.tsx @@ -9,6 +9,7 @@ type Props = { geometryVisibility: Record; onToggleBackground: (id: BackgroundLayerId) => void; onToggleGeometry: (typeKey: string) => void; + onHide?: () => void; }; // Map each layer ID/geometry type to a premium inline SVG icon @@ -172,6 +173,7 @@ export default function ReplayPreviewLayerPanel({ geometryVisibility, onToggleBackground, onToggleGeometry, + onHide, }: Props) { // Categorize geometry types for logical grouping const polygonKeys = ["country", "state", "faction", "rebellion_zone"]; @@ -196,10 +198,10 @@ export default function ReplayPreviewLayerPanel({ const renderStyles = () => (