refactor: enhance wiki navigation, add view mode toggle, and improve map sync preview logic

(important global check)
This commit is contained in:
taDuc
2026-05-26 03:14:14 +07:00
parent 8306543828
commit 9d04076921
9 changed files with 570 additions and 69 deletions
+39 -1
View File
@@ -134,6 +134,7 @@ export default function PublicWikiSidebar({
maxDragWidth,
}: Props) {
const contentRootRef = useRef<HTMLDivElement | null>(null);
const tocContainerRef = useRef<HTMLDivElement | null>(null);
const [localWidth, setLocalWidth] = useState<number>(() => {
if (typeof window !== "undefined") {
@@ -203,6 +204,7 @@ export default function PublicWikiSidebar({
.filter((item): item is HTMLElement => Boolean(item));
if (!headings.length) return;
const scrollContainer = root.parentElement;
const observer = new IntersectionObserver(
(entries) => {
const visible = entries
@@ -211,13 +213,30 @@ export default function PublicWikiSidebar({
const top = visible[0]?.target as HTMLElement | undefined;
if (top?.id) setActiveHeadingId(top.id);
},
{ root: null, rootMargin: "-18% 0px -70% 0px", threshold: [0, 1] }
{ root: scrollContainer || null, rootMargin: "-18% 0px -70% 0px", threshold: [0, 1] }
);
for (const heading of headings) observer.observe(heading);
return () => observer.disconnect();
}, [toc]);
useEffect(() => {
const container = tocContainerRef.current;
if (!container) return;
const handleWheel = (e: WheelEvent) => {
if (e.deltaY !== 0) {
e.preventDefault();
container.scrollLeft += e.deltaY;
}
};
container.addEventListener("wheel", handleWheel, { passive: false });
return () => {
container.removeEventListener("wheel", handleWheel);
};
}, [toc]);
useEffect(() => {
const root = contentRootRef.current;
if (!root) return;
@@ -373,6 +392,7 @@ export default function PublicWikiSidebar({
}}
>
<div
ref={tocContainerRef}
className="uhm-public-wiki-toc-list"
style={{
display: "flex",
@@ -387,6 +407,24 @@ export default function PublicWikiSidebar({
<a
key={item.id}
href={`#${item.id}`}
onClick={(e) => {
e.preventDefault();
setActiveHeadingId(item.id);
const root = contentRootRef.current;
if (root) {
const targetElement = root.querySelector(`#${CSS.escape(item.id)}`) as HTMLElement | null;
const scrollContainer = root.parentElement;
if (targetElement && scrollContainer) {
const containerTop = scrollContainer.getBoundingClientRect().top;
const targetTop = targetElement.getBoundingClientRect().top;
const scrollOffset = targetTop - containerTop + scrollContainer.scrollTop;
scrollContainer.scrollTo({
top: scrollOffset - 12,
behavior: "smooth"
});
}
}
}}
style={{
flexShrink: 0,
borderRadius: 9999,
+26 -8
View File
@@ -676,17 +676,21 @@ export default function WikiSidebarPanel({ projectId, setWikis, onRemoveWiki }:
type="button"
onClick={() => removeWiki(w.id)}
style={{
border: "none",
background: "#111827",
color: "#fca5a5",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: 22,
height: 22,
borderRadius: 6,
border: "1px solid #334155",
background: "#0b1220",
cursor: "pointer",
borderRadius: "6px",
padding: "6px 8px",
fontSize: "12px",
flex: "0 0 auto",
}}
title="Remove"
title="Xóa wiki khỏi dự án"
aria-label={`Xóa wiki ${w.id}`}
>
Del
<TrashIcon />
</button>
</div>
))}
@@ -1063,6 +1067,20 @@ function CloseIcon() {
);
}
function TrashIcon() {
return (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path
d="M19 7l-.867 12.142A2 2 0 0 1 16.138 21H7.862a2 2 0 0 1-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v3M4 7h16"
stroke="#f87171"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
const QUILL_TOOLBAR = [
[{ header: [1, 2, 3, false] }],
[{ align: [] }, { align: "center" }, { align: "right" }],