Skip to content

Commit

Permalink
Fix hash routes
Browse files Browse the repository at this point in the history
  • Loading branch information
wileymc committed Dec 22, 2024
1 parent b3562ec commit 99c016a
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 153 deletions.
14 changes: 11 additions & 3 deletions components/DocSearch/DocSearch.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "styles/mixins/media";
@import "styles/variables/fonts";

.searchButton {
display: flex;
Expand All @@ -7,12 +8,14 @@
max-width: 24rem;
gap: 0.5rem;
padding: 0.5rem;
font-size: 0.875rem;
color: var(--blue-text);
font-size: 1rem;
font-family: $font-primary;
color: white;
background: var(--background-secondary);
border: 1px solid var(--blue-dark);
border-radius: 0.5rem;
transition: background-color 0.2s;
cursor: pointer;

&:hover {
background: var(--blue-dark);
Expand All @@ -26,7 +29,8 @@
font-size: 0.75rem;
font-weight: 500;
color: var(--blue-text);
background: var(--blue-dark);
background: var(--blue-darker);
border: 1px solid var(--blue);
border-radius: 0.25rem;

@include for-tablet-up {
Expand Down Expand Up @@ -145,6 +149,10 @@
&:hover {
background: var(--blue-dark);
}

&.selected {
background: var(--blue-dark);
}
}

.resultHeader {
Expand Down
145 changes: 98 additions & 47 deletions components/DocSearch/DocSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import { Search } from "lucide-react";
import styles from "./DocSearch.module.scss";
import Link from "next/link";
Expand All @@ -23,8 +23,12 @@ export const DocSearch: React.FC = () => {
const [open, setOpen] = useState(false);
const [query, setQuery] = useState("");
const [results, setResults] = useState<SearchResult[]>([]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [searchIndex, setSearchIndex] = useState<any[]>([]);
const [selectedIndex, setSelectedIndex] = useState(-1);
const [searchIndex, setSearchIndex] = useState<SearchResult[]>([]);

// Refs for handling scroll into view
const resultsContainerRef = useRef<HTMLDivElement>(null);
const selectedResultRef = useRef<HTMLAnchorElement>(null);

// Load search index
useEffect(() => {
Expand All @@ -34,7 +38,22 @@ export const DocSearch: React.FC = () => {
.catch(console.error);
}, []);

// Handle keyboard shortcut
// Reset selected index when results change
useEffect(() => {
setSelectedIndex(results.length > 0 ? 0 : -1);
}, [results]);

// Scroll selected item into view
useEffect(() => {
if (selectedResultRef.current && resultsContainerRef.current) {
selectedResultRef.current.scrollIntoView({
block: "nearest",
behavior: "smooth",
});
}
}, [selectedIndex]);

// Handle keyboard shortcuts
useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
Expand All @@ -46,18 +65,45 @@ export const DocSearch: React.FC = () => {
return () => document.removeEventListener("keydown", down);
}, []);

// Handle escape key
// Handle navigation keys
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") {
setOpen(false);
const handleKeyDown = (e: KeyboardEvent) => {
if (!open) return;

console.log(selectedIndex);

switch (e.key) {
case "ArrowDown":
e.preventDefault();
setSelectedIndex((prev) =>
prev < results.length - 1 ? prev + 1 : prev
);
break;
case "ArrowUp":
e.preventDefault();
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : prev));
break;
case "Enter":
e.preventDefault();
if (selectedIndex >= 0 && selectedIndex < results.length) {
const href = results[selectedIndex]?.href;
if (href) {
window.location.href = href;
}
setOpen(false);
}
break;
case "Escape":
setOpen(false);
break;
}
};
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, []);

// Search logic with content matching
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [open, results, selectedIndex]);

// Search logic with content matching (unchanged)
const search = (searchQuery: string) => {
if (!searchQuery) {
setResults([]);
Expand All @@ -70,14 +116,12 @@ export const DocSearch: React.FC = () => {
let score = 0;
const matches: { text: string; type: "content" | "heading" }[] = [];

// Search in title
if (
searchTerms.every((term) => item.label.toLowerCase().includes(term))
) {
score += 10;
}

// Search in headings (if they exist)
if (item.headings && item.headings.length > 0) {
item.headings.forEach((heading: { text: string; level: number }) => {
if (
Expand All @@ -91,7 +135,6 @@ export const DocSearch: React.FC = () => {
});
}

// Search in content (if it exists)
if (item.content) {
const contentLower = item.content.toLowerCase();
const allTermsInContent = searchTerms.every((term) =>
Expand All @@ -100,13 +143,10 @@ export const DocSearch: React.FC = () => {

if (allTermsInContent) {
score += 1;

// Find the best matching content snippet
const snippetLength = 150;
let bestSnippetScore = 0;
let bestSnippet = "";

// If content is shorter than snippet length, use whole content
if (item.content.length <= snippetLength) {
bestSnippet = item.content;
} else {
Expand Down Expand Up @@ -173,6 +213,9 @@ export const DocSearch: React.FC = () => {
>
);

// Track the current result index across all groups
let currentIndex = -1;

return (
<>
<button onClick={() => setOpen(true)} className={styles.searchButton}>
Expand Down Expand Up @@ -209,7 +252,7 @@ export const DocSearch: React.FC = () => {
autoFocus
/>

<div className={styles.resultsList}>
<div className={styles.resultsList} ref={resultsContainerRef}>
{results.length === 0 && query && (
<div className={styles.noResults}>No results found.</div>
)}
Expand All @@ -218,35 +261,43 @@ export const DocSearch: React.FC = () => {
<div key={tabId} className={styles.resultGroup}>
<div className={styles.groupHeading}>{group.label}</div>

{group.items.map((result) => (
<Link
key={result.href}
href={result.href}
className={styles.resultItem}
onClick={() => setOpen(false)}
>
<div className={styles.resultHeader}>
<span className={styles.resultTitle}>
{result.label}
</span>
<span className={styles.resultSection}>
{result.section}
</span>
</div>

{result.matches.map((match, idx) => (
<div key={idx} className={styles.resultMatch}>
{match.type === "heading" ? (
<span className={styles.matchHeading}>
{match.text}
</span>
) : (
<>...{match.text}...</>
)}
{group.items.map((result) => {
currentIndex++;
const isSelected = currentIndex === selectedIndex;

return (
<Link
key={result.href}
href={result.href}
ref={isSelected ? selectedResultRef : null}
className={`${styles.resultItem} ${
isSelected ? styles.selected : ""
}`}
onClick={() => setOpen(false)}
>
<div className={styles.resultHeader}>
<span className={styles.resultTitle}>
{result.label}
</span>
<span className={styles.resultSection}>
{result.section}
</span>
</div>
))}
</Link>
))}

{result.matches.map((match, idx) => (
<div key={idx} className={styles.resultMatch}>
{match.type === "heading" ? (
<span className={styles.matchHeading}>
{match.text}
</span>
) : (
<>...{match.text}...</>
)}
</div>
))}
</Link>
);
})}
</div>
))}
</div>
Expand Down
39 changes: 5 additions & 34 deletions components/DocsLayout/DocsLayout.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
font-weight: 600;
}

section {
scroll-margin-top: 136px; // Adjust this value to match your header height
}

ol {
list-style: none;
padding: 0;
Expand Down Expand Up @@ -85,7 +89,7 @@
}

.menuButton:hover {
background-color: #f3f4f6;
background-color: $black-alpha-3;
}

.menuIcon {
Expand Down Expand Up @@ -115,39 +119,6 @@
margin: 0 auto;
}

.searchWrapper {
width: 100%;
}

.searchInputWrapper {
position: relative;
width: 100%;
}

.searchIcon {
position: absolute;
left: 0.75rem;
top: 50%;
transform: translateY(-50%);
height: 1rem;
width: 1rem;
color: #9ca3af;
}

.searchInput {
width: 100%;
padding: 0.5rem 1rem 0.5rem 2.5rem;
border-radius: 0.375rem;
border: 1px solid #e5e7eb;
background-color: #f9fafb;
}

.searchInput:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 1px #3b82f6;
}

.headerRight {
display: flex;
align-items: center;
Expand Down
Loading

0 comments on commit 99c016a

Please sign in to comment.