feat: update public preview UI with adjustable sidebar height, layer panel toggle, and synchronized data loading states(mobile UI)
Build and Release / release (push) Successful in 37s

This commit is contained in:
taDuc
2026-06-04 18:39:46 +07:00
parent 794ad2913f
commit 9a5dfdb2ed
14 changed files with 801 additions and 249 deletions
+89 -4
View File
@@ -23,6 +23,8 @@ type Props = {
onSidebarWidthChange?: (width: number) => void;
maxDragWidth?: number;
compactHeader?: boolean;
sidebarHeight?: number;
onSidebarHeightChange?: (height: number) => void;
};
function escapeHtml(input: string): string {
@@ -135,6 +137,8 @@ function PublicWikiSidebar({
onSidebarWidthChange,
maxDragWidth,
compactHeader = false,
sidebarHeight,
onSidebarHeightChange,
}: Props) {
const contentRootRef = useRef<HTMLDivElement | null>(null);
const tocContainerRef = useRef<HTMLDivElement | null>(null);
@@ -201,6 +205,48 @@ function PublicWikiSidebar({
window.addEventListener("pointerup", onUp);
};
const handleHeightPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
event.preventDefault();
const startY = event.clientY;
const startHeight = sidebarHeight || 400;
// Tạo đường ghost ảo nằm ngang chỉ vị trí kéo chiều cao
const ghost = document.createElement("div");
ghost.style.position = "fixed";
ghost.style.left = "0";
ghost.style.right = "0";
ghost.style.height = "4px";
ghost.style.backgroundColor = "#38bdf8";
ghost.style.boxShadow = "0 0 12px rgba(56, 189, 248, 0.8)";
ghost.style.zIndex = "99999";
ghost.style.cursor = "row-resize";
ghost.style.pointerEvents = "none";
const startScreenY = window.innerHeight - startHeight;
ghost.style.top = `${startScreenY}px`;
document.body.appendChild(ghost);
const onMove = (e: PointerEvent) => {
ghost.style.top = `${e.clientY}px`;
};
const onUp = (e: PointerEvent) => {
window.removeEventListener("pointermove", onMove);
window.removeEventListener("pointerup", onUp);
if (ghost.parentNode) {
ghost.parentNode.removeChild(ghost);
}
const deltaY = startY - e.clientY;
const nextHeight = Math.max(200, Math.min(window.innerHeight * 0.9, startHeight + deltaY));
if (onSidebarHeightChange) {
onSidebarHeightChange(nextHeight);
}
};
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
};
const [activeHeadingId, setActiveHeadingId] = useState<string | null>(null);
const processedWiki = useMemo(() => {
if (!wiki) return { html: "", toc: [] as TocItem[] };
@@ -281,23 +327,62 @@ function PublicWikiSidebar({
return () => root.removeEventListener("click", handleClick);
}, [onWikiLinkRequest, renderHtml]);
const [isMobileOrTablet, setIsMobileOrTablet] = useState(false);
useEffect(() => {
const checkDevice = () => setIsMobileOrTablet(window.innerWidth < 1024);
checkDevice();
window.addEventListener("resize", checkDevice);
return () => window.removeEventListener("resize", checkDevice);
}, []);
return (
<div
style={{
width: `${width}px`,
width: "100%",
maxWidth: isMobileOrTablet ? "100%" : `${width}px`,
display: "flex",
flexDirection: "column",
height: "100%",
minHeight: 0,
overflow: "hidden",
borderRadius: 20,
border: "1px solid rgba(148, 163, 184, 0.22)",
borderRadius: isMobileOrTablet ? "24px 24px 0 0" : 20,
border: isMobileOrTablet ? "1px solid rgba(148, 163, 184, 0.22)" : "1px solid rgba(148, 163, 184, 0.22)",
borderBottom: isMobileOrTablet ? "none" : "1px solid rgba(148, 163, 184, 0.22)",
borderLeft: isMobileOrTablet ? "none" : "1px solid rgba(148, 163, 184, 0.22)",
borderRight: isMobileOrTablet ? "none" : "1px solid rgba(148, 163, 184, 0.22)",
background: "linear-gradient(145deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.85))",
boxShadow: "0 20px 48px rgba(2, 6, 23, 0.45)",
backdropFilter: "blur(12px)",
position: "relative",
}}
>
{/* Grab Handle for bottom sheet on mobile */}
{isMobileOrTablet ? (
<div
onPointerDown={handleHeightPointerDown}
style={{
width: "100%",
height: 24,
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "row-resize",
zIndex: 60,
userSelect: "none",
flexShrink: 0,
}}
>
<div
style={{
width: 36,
height: 4,
borderRadius: 2,
backgroundColor: "rgba(255, 255, 255, 0.3)",
}}
/>
</div>
) : null}
{/* Drag Handle on the left edge */}
<div
onPointerDown={handlePointerDown}
@@ -311,7 +396,7 @@ function PublicWikiSidebar({
zIndex: 50,
userSelect: "none",
}}
className="group"
className="group hidden lg:block"
title="Kéo để chỉnh kích thước"
>
{/* Visual drag line overlay */}