Skip to content

Commit 7d9af27

Browse files
committed
improve docs sidebar with search highlighting
1 parent bde750b commit 7d9af27

File tree

5 files changed

+188
-4
lines changed

5 files changed

+188
-4
lines changed

apps/material-react-table-docs/components/navigation/Sidebar.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,28 @@ import CloseIcon from '@mui/icons-material/Close';
1313
import { SideBarItems } from './SidebarItems';
1414
import { RouteItem, routes } from './routes';
1515
import { matchSorter } from 'match-sorter';
16+
import { usePlausible } from 'next-plausible';
1617

1718
interface Props {
1819
navOpen: boolean;
1920
setNavOpen: (navOpen: boolean) => void;
2021
}
2122

2223
export const SideBar = ({ navOpen, setNavOpen }: Props) => {
24+
const plausible = usePlausible();
2325
const isMobile = useMediaQuery('(max-width: 900px)');
2426

2527
const [search, setSearch] = useState('');
2628

2729
const filteredRoutes = useMemo(() => {
2830
const filterRoutesRecursively = (routes: RouteItem[]): RouteItem[] => {
2931
return routes.reduce((acc: RouteItem[], route) => {
32+
const matchKeys = ['label'];
33+
if (route.keywords) {
34+
matchKeys.push('keywords');
35+
}
3036
const matchesSearch =
31-
matchSorter([route], search, { keys: ['label'] }).length > 0;
37+
matchSorter([route], search, { keys: matchKeys }).length > 0;
3238

3339
if (matchesSearch) {
3440
// If the parent route matches, include all its items and secondary items
@@ -88,6 +94,7 @@ export const SideBar = ({ navOpen, setNavOpen }: Props) => {
8894
}}
8995
>
9096
<TextField
97+
onFocus={() => plausible('page-filter')}
9198
placeholder="Find Page"
9299
variant="outlined"
93100
fullWidth
@@ -153,6 +160,7 @@ export const SideBar = ({ navOpen, setNavOpen }: Props) => {
153160
routes={filteredRoutes}
154161
setNavOpen={setNavOpen}
155162
setSearch={setSearch}
163+
search={search}
156164
/>
157165
)}
158166
</List>

apps/material-react-table-docs/components/navigation/SidebarItems.tsx

+96-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Link from 'next/link';
44
import { alpha, ListItemButton, useMediaQuery, Box } from '@mui/material';
55
import LaunchIcon from '@mui/icons-material/Launch';
66
import { type RouteItem } from './routes';
7+
import highlightWords from 'highlight-words';
78

89
interface Props {
910
depth?: number;
@@ -12,6 +13,7 @@ interface Props {
1213
routes: RouteItem[];
1314
setNavOpen: (navOpen: boolean) => void;
1415
setSearch: (search: string) => void;
16+
search: string;
1517
}
1618

1719
export const SideBarItems = ({
@@ -21,6 +23,7 @@ export const SideBarItems = ({
2123
routes,
2224
setNavOpen,
2325
setSearch,
26+
search,
2427
}: Props) => {
2528
const { pathname } = useRouter();
2629
const isMobile = useMediaQuery('(max-width: 900px)');
@@ -39,14 +42,28 @@ export const SideBarItems = ({
3942
return (
4043
<>
4144
{routes.map(
42-
({ href, items, label, divider, external, secondaryItems }) => {
45+
({
46+
href,
47+
items,
48+
label,
49+
divider,
50+
external,
51+
secondaryItems,
52+
keywords,
53+
}) => {
4354
const secondaryHrefs = secondaryItems?.map((i) => i.href);
4455

4556
const isSelected = pathname === href;
4657
const isSelectedParent = secondaryHrefs?.includes(pathname);
4758
const willBeSecondary =
4859
!!secondaryItems?.length && (isSelectedParent || expandAll);
4960

61+
const matchingKeyWords = search
62+
? keywords?.filter((word) =>
63+
word.toLowerCase().includes(search.toLowerCase()),
64+
)
65+
: undefined;
66+
5067
return (
5168
<Fragment key={label}>
5269
<Link href={href} target={external ? '_blank' : undefined}>
@@ -83,7 +100,11 @@ export const SideBarItems = ({
83100
? '1.25rem'
84101
: '1rem',
85102
fontWeight: isSelectedParent ? 'bold' : 'normal',
86-
height: items ? '2.5rem' : '2rem',
103+
height: matchingKeyWords?.length
104+
? '3.5rem'
105+
: items
106+
? '2.5rem'
107+
: '2rem',
87108
lineHeight: depth === 0 && !items ? '1.25rem' : '0.75rem',
88109
padding: '0',
89110
whiteSpace: 'nowrap',
@@ -97,7 +118,11 @@ export const SideBarItems = ({
97118
ml: `${depth}rem`,
98119
}}
99120
>
100-
{label}
121+
<HighlightedText label={label} search={search} />
122+
<HighlightedKeywords
123+
keywords={matchingKeyWords}
124+
search={search}
125+
/>
101126
{external && (
102127
<LaunchIcon fontSize="small" sx={{ m: '-0.25rem 4px' }} />
103128
)}
@@ -111,6 +136,7 @@ export const SideBarItems = ({
111136
isSecondary={willBeSecondary}
112137
routes={willBeSecondary ? secondaryItems : items!}
113138
setNavOpen={setNavOpen}
139+
search={search}
114140
setSearch={setSearch}
115141
/>
116142
)}
@@ -121,3 +147,70 @@ export const SideBarItems = ({
121147
</>
122148
);
123149
};
150+
151+
const HighlightedText = ({
152+
search,
153+
label,
154+
}: {
155+
search: string;
156+
label: string;
157+
}) => {
158+
const chunks = highlightWords?.({
159+
matchExactly: false,
160+
query: search,
161+
text: label?.toString() as string,
162+
});
163+
if (chunks?.length > 1 || chunks?.[0]?.match) {
164+
return (
165+
<span aria-label={label} role="note">
166+
{chunks?.map(({ key, match, text }) => (
167+
<Box
168+
aria-hidden="true"
169+
component="span"
170+
key={key}
171+
sx={
172+
match
173+
? (theme) => ({
174+
backgroundColor: theme.palette.warning.light,
175+
borderRadius: '2px',
176+
color: (theme) =>
177+
theme.palette.mode === 'dark'
178+
? theme.palette.common.black
179+
: theme.palette.common.white,
180+
padding: '2px 1px',
181+
})
182+
: undefined
183+
}
184+
>
185+
{text}
186+
</Box>
187+
))}
188+
</span>
189+
);
190+
}
191+
return label;
192+
};
193+
194+
const HighlightedKeywords = ({
195+
keywords,
196+
search,
197+
}: {
198+
keywords: string[] | undefined;
199+
search: string;
200+
}) => {
201+
if (!keywords?.length || !search) return null;
202+
return (
203+
<Box
204+
component="div"
205+
sx={{
206+
display: 'block',
207+
mt: '12px',
208+
color: 'text.secondary',
209+
fontSize: '0.8rem',
210+
fontStyle: 'italic',
211+
}}
212+
>
213+
(<HighlightedText label={keywords.join(', ')} search={search} />)
214+
</Box>
215+
);
216+
};

0 commit comments

Comments
 (0)