Skip to content

Commit

Permalink
fix: fix some bug
Browse files Browse the repository at this point in the history
  • Loading branch information
lvyl9909 committed Oct 11, 2024
1 parent fed1600 commit 0719e52
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 73 deletions.
5 changes: 4 additions & 1 deletion app/tutorial/components/IntroductionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ const IntroductionContent: React.FC<IntroductionContentProps> = ({
</div>

{/* Section: The Audit Process */}
<div className="mb-8" data-content="Audit Process">
<div
className="mb-8"
data-content="The Audit Process from Beginning to End"
>
<h3 className="text-2xl font-bold mb-4">
The Audit Process from Beginning to End:
</h3>
Expand Down
218 changes: 146 additions & 72 deletions app/tutorial/components/SidebarWithSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,8 @@

import React, { useEffect, useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";

// 定义页面列表
const pages = [
{ id: 0, name: "Getting Started", path: "/tutorial" },
{
id: 1,
name: "Introduction: IRV RAs with RAIRE",
path: "/tutorial/introduction",
},
{
id: 2,
name: "IRV elections and Visualizing Outcomes",
path: "/tutorial/outcomes",
},
{ id: 3, name: "Assertions for IRV winners", path: "/tutorial/assertion" },
{ id: 4, name: "Risk Limiting Audits", path: "/tutorial/risk" },
{
id: 5,
name: "Using assertions to audit IRV outcomes",
path: "/tutorial/usingassertion",
},
];
import { usePathname, useRouter } from "next/navigation";
import { contentData } from "./dataContent";

// 定义Props类型
interface SidebarProps {
Expand All @@ -41,22 +20,80 @@ const SidebarWithSearch: React.FC<SidebarProps> = ({
setCollapsed,
}) => {
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState<
{ content: string; path: string }[]
>([]);
const [expandedSections, setExpandedSections] = useState<string[]>([]);
const [activeSubItem, setActiveSubItem] = useState<string | null>(null);
const pathname = usePathname();
const [sidebarHeight, setSidebarHeight] = useState("90vh"); // 初始化高度
const router = useRouter();
const [sidebarHeight, setSidebarHeight] = useState("90vh");

// 初始化展开的章节
useEffect(() => {
const currentSection = contentData.find(
(section) => pathname === section.path, // 完全匹配路径
);
if (currentSection) {
setExpandedSections([currentSection.title]);
}
}, [pathname]);

// 搜索处理
const handleSearch = () => {
if (!searchTerm) return;
const element = document.querySelector(`[data-content*="${searchTerm}"]`);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });

const results: { content: string; path: string }[] = [];

contentData.forEach((page) => {
page.subItems.forEach((subItem) => {
if (subItem.toLowerCase().includes(searchTerm.toLowerCase())) {
results.push({ content: subItem, path: page.path });
}
});
});

const uniqueResults = results.filter(
(result, index, self) =>
index === self.findIndex((r) => r.content === result.content),
);

setSearchResults(uniqueResults);
};

// 折叠/展开处理
const toggleSection = (title: string) => {
if (expandedSections.includes(title)) {
setExpandedSections(expandedSections.filter((item) => item !== title));
} else {
setExpandedSections([title]); // 确保只有一个一级标题展开
}
};

// 监听滚动事件并调整 Sidebar 高度
// 点击搜索结果或二级标题时处理
const handleResultClick = (path: string, content: string) => {
setSearchResults([]);
setSearchTerm("");
setActiveSubItem(content);

// 找到对应的一级标题并展开
const section = contentData.find((page) => page.path === path);
if (section) {
setExpandedSections([section.title]); // 确保只有一个一级标题展开
}

router.push(path);
setTimeout(() => {
const element = document.querySelector(`[data-content="${content}"]`);
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "start" });
}
}, 300);
};

// 监听滚动事件,动态更新当前活动的二级标题
const handleScroll = () => {
const scrollTop = window.scrollY;
// 当滚动到页面底部时,Sidebar 高度为 100vh,否则按滚动距离逐渐增加
// const newHeight = Math.min(100, 70 + (scrollTop / 10)) + "vh";
const newHeight = Math.max(85, Math.min(100, 85 + scrollTop / 10)) + "vh";
setSidebarHeight(newHeight);
};
Expand All @@ -76,7 +113,7 @@ const SidebarWithSearch: React.FC<SidebarProps> = ({

const handleMouseMove = (e: MouseEvent) => {
if (!collapsed) {
const newWidth = Math.min(Math.max(e.clientX, 100), 400); // Min width: 100px, Max width: 400px
const newWidth = Math.min(Math.max(e.clientX, 100), 400);
setSidebarWidth(newWidth);
}
};
Expand All @@ -95,57 +132,94 @@ const SidebarWithSearch: React.FC<SidebarProps> = ({
className="fixed bottom-0 left-0 z-50 flex"
style={{ height: sidebarHeight }}
>
{/* Sidebar */}
<div
className="bg-white shadow-lg transition-transform duration-300"
style={{ width: collapsed ? "0px" : `${sidebarWidth}px` }}
>
{!collapsed && (
<>
{/*/!* Top horizontal line *!/*/}
{/*<div className="w-full h-2 bg-gray-300"></div>*/}

<div className="p-4">
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full p-2 border rounded-md"
/>
<button
onClick={handleSearch}
className="w-full mt-2 bg-blue-500 text-white p-2 rounded-md"
>
Search
</button>

{/* Add spacing between the Search button and the Table of Content */}
<div className="mt-6"></div>

<h4 className="text-xl font-bold mb-4">Table of Content</h4>
<ul className="space-y-2">
{pages.map((page) => (
<li key={page.id}>
<Link
href={page.path}
className={`block ${
pathname === page.path
? "text-blue-600 font-semibold"
: "text-gray-700"
} hover:underline`}
>
{page.name}
</Link>
<div className="p-4">
<input
type="text"
placeholder="Search..."
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
handleSearch();
}}
className="w-full p-2 border rounded-md"
/>
<button
onClick={handleSearch}
className="w-full mt-2 bg-blue-500 text-white p-2 rounded-md"
>
Search
</button>

{searchResults.length > 0 && (
<ul className="mt-4 bg-gray-100 p-2 rounded-md max-h-60 overflow-y-auto">
{searchResults.map((result, index) => (
<li
key={index}
className="text-blue-500 hover:underline cursor-pointer"
onClick={() =>
handleResultClick(result.path, result.content)
}
>
{result.content}
</li>
))}
</ul>
</div>
</>
)}

<div className="mt-6"></div>
<h4 className="text-xl font-bold mb-4">Table of Content</h4>
<ul className="space-y-2">
{contentData.map((section) => (
<li key={section.title}>
<div
className="flex justify-between items-center cursor-pointer py-2"
onClick={() => toggleSection(section.title)}
>
<span
className={`${
expandedSections.includes(section.title) ||
(pathname === section.path && pathname === "/tutorial")
? "text-blue-600 font-semibold"
: "text-gray-700"
}`}
>
{section.title}
</span>
<span className="text-gray-500">
{expandedSections.includes(section.title) ? "▼" : "▶"}
</span>
</div>
{expandedSections.includes(section.title) && (
<ul className="ml-4 mt-2 space-y-1">
{section.subItems.map((subItem) => (
<li key={subItem}>
<span
className={`block cursor-pointer ${
activeSubItem === subItem
? "text-blue-600 font-semibold"
: "text-gray-700"
} hover:underline`}
onClick={() =>
handleResultClick(section.path, subItem)
}
>
{subItem}
</span>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</div>
)}
</div>

{/* Resizer Bar with Toggle Button */}
<div
className="h-full w-1 bg-gray-300 cursor-col-resize flex items-center justify-center"
onMouseDown={handleMouseDown}
Expand Down
45 changes: 45 additions & 0 deletions app/tutorial/components/dataContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// app/tutorial/components/dataContent.js

export const contentData = [
{
title: "Getting Started",
path: "/tutorial",
subItems: ["Welcome", "What's Here", "Ready?"],
},
{
title: "Introduction: IRV RAs with RAIRE",
path: "/tutorial/introduction",
subItems: ["The Audit Process from Beginning to End"],
},
{
title: "IRV elections and Visualizing Outcomes",
path: "/tutorial/outcomes",
subItems: ["How IRV Counts Work"],
},
{
title: "Assertions for IRV winners",
path: "/tutorial/assertion",
subItems: [
"Not Eliminated Before (NEB) Assertions",
"Not Eliminated Next (NEN) Assertions",
"Simple assertions sometimes work",
"One candidate dominates",
"Two leading candidates",
"Visualizing assertions",
],
},
{
title: "Risk Limiting Audits",
path: "/tutorial/risk",
subItems: [
"What is an RLA?",
"Main Steps in Conducting an RLA",
"Understanding Margin and Difficulty in RLAs",
],
},
{
title: "Using assertions to audit IRV outcomes",
path: "/tutorial/usingassertion",
subItems: ["NEB Assertions", "NEN Assertions", "Assertion scoring summary"],
},
];

0 comments on commit 0719e52

Please sign in to comment.