@@ -72,6 +72,10 @@ const StyledSuggestionSection = styled('div')`
7272 gap: 4px;
7373` ;
7474
75+ function escapeRegExp ( str : string ) {
76+ return str . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ; // $& means the whole matched string
77+ }
78+
7579interface SearchModalProps {
7680 open : boolean ;
7781 onClose : ( ) => void ;
@@ -102,106 +106,104 @@ const SearchModal: FC<SearchModalProps> = ({ open, onClose, searchablePages }) =
102106
103107 const searchResults = useSearchScores ( search , searchablePages ) ;
104108
105- const renderedResults = useMemo (
106- ( ) =>
107- searchResults ?. length > 0 ? (
108- [ ...Array < unknown > ( SEARCH_RESULTS_TO_SHOW ) ] . map ( ( _ , index ) => {
109- if ( searchResults . length <= index ) {
110- return ;
111- }
112-
113- const result = searchResults [ index ] ;
114- const { entry } = result ;
115- let summary = entry . textContent ;
116-
117- if ( ! result . isExactTitleMatch ) {
109+ const renderedResults = useMemo ( ( ) => {
110+ const escapedSearch = escapeRegExp ( search ) ;
111+
112+ return searchResults ?. length > 0 ? (
113+ [ ...Array < unknown > ( SEARCH_RESULTS_TO_SHOW ) ] . map ( ( _ , index ) => {
114+ if ( searchResults . length <= index ) {
115+ return ;
116+ }
117+
118+ const result = searchResults [ index ] ;
119+ const { entry } = result ;
120+ let summary = entry . textContent ;
121+
122+ if ( ! result . isExactTitleMatch ) {
123+ const match = new RegExp (
124+ `(?:[\\s]+[^\\s]+){0,10}[\\s]*${ escapeRegExp (
125+ escapedSearch ,
126+ ) } (?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`,
127+ 'ig' ,
128+ ) . exec ( entry . textContent ) ;
129+ if ( match && match . length >= 1 ) {
130+ summary = `...${ match [ 0 ] . trim ( ) } ...` ;
131+ } else {
118132 const match = new RegExp (
119- `(?:[\\s]+[^\\s]+){0,10}[\\s]*${ search } (?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}` ,
133+ `(?:[\\s]+[^\\s]+){0,10}[\\s]*(${ escapedSearch
134+ . split ( ' ' )
135+ . join ( '|' ) } )(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`,
120136 'ig' ,
121137 ) . exec ( entry . textContent ) ;
122138 if ( match && match . length >= 1 ) {
123139 summary = `...${ match [ 0 ] . trim ( ) } ...` ;
124- } else {
125- const match = new RegExp (
126- `(?:[\\s]+[^\\s]+){0,10}[\\s]*(${ search
127- . split ( ' ' )
128- . join ( '|' ) } )(?![^<>]*(([/"']|]]|\\b)>))[\\s]*(?:[^\\s]+\\s){0,25}`,
129- 'ig' ,
130- ) . exec ( entry . textContent ) ;
131- if ( match && match . length >= 1 ) {
132- summary = `...${ match [ 0 ] . trim ( ) } ...` ;
133- }
134140 }
135141 }
136-
137- summary = summary ?. replace (
138- new RegExp ( `(${ search . split ( ' ' ) . join ( '|' ) } )(?![^<>]*(([/"']|]]|\\b)>))` , 'ig' ) ,
139- `<strong style="color: ${ theme . palette . primary . main } ">$1</strong>` ,
140- ) ;
141-
142- return (
143- < SearchResult
144- key = { `result-${ entry . url } ` }
145- entry = { entry }
146- summary = { summary }
147- onClick = { handleClose }
148- />
149- ) ;
150- } )
151- ) : isNotEmpty ( search ) ? (
152- < Typography
153- variant = "h3"
154- component = "div"
155- key = "no-results"
156- sx = { { width : '100%' , textAlign : 'center' , marginTop : '16px' } }
157- >
158- No results found
159- </ Typography >
160- ) : (
161- < StyledSuggestions >
162- < StyledSuggestionSection >
163- < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
164- Getting Started
165- </ Typography >
166- < SuggestionLink href = "/docs/start-with-a-template" >
167- Start With a Template
168- </ SuggestionLink >
169- < SuggestionLink href = "/docs/add-to-your-site" > Add to Your Site</ SuggestionLink >
170- < SuggestionLink href = "/docs/configuration-options" >
171- Configuration Options
172- </ SuggestionLink >
173- < SuggestionLink href = "/docs/collection-overview" > Collections</ SuggestionLink >
174- </ StyledSuggestionSection >
175- < StyledSuggestionSection >
176- < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
177- Backends
178- </ Typography >
179- < SuggestionLink href = "/docs/github-backend" > GitHub</ SuggestionLink >
180- < SuggestionLink href = "/docs/bitbucket-backend" > Bitbucket</ SuggestionLink >
181- < SuggestionLink href = "/docs/gitlab-backend" > GitLab</ SuggestionLink >
182- </ StyledSuggestionSection >
183- < StyledSuggestionSection >
184- < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
185- Customize Your CMS
186- </ Typography >
187- < SuggestionLink href = "/docs/custom-previews" > Custom Previews</ SuggestionLink >
188- < SuggestionLink href = "/docs/custom-widgets" > Custom Widgets</ SuggestionLink >
189- < SuggestionLink href = "/docs/custom-icons" > Custom Icons</ SuggestionLink >
190- < SuggestionLink href = "/docs/additional-links" > Custom Pages / Links</ SuggestionLink >
191- </ StyledSuggestionSection >
192- < StyledSuggestionSection >
193- < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
194- Widgets
195- </ Typography >
196- < SuggestionLink href = "/docs/widget-string" > String</ SuggestionLink >
197- < SuggestionLink href = "/docs/widget-image" > Image</ SuggestionLink >
198- < SuggestionLink href = "/docs/widget-datetime" > Datetime</ SuggestionLink >
199- < SuggestionLink href = "/docs/widget-markdown" > Markdown</ SuggestionLink >
200- </ StyledSuggestionSection >
201- </ StyledSuggestions >
202- ) ,
203- [ handleClose , search , searchResults , theme . palette . primary . main ] ,
204- ) ;
142+ }
143+
144+ summary = summary ?. replace (
145+ new RegExp ( `(${ escapedSearch . split ( ' ' ) . join ( '|' ) } )(?![^<>]*(([/"']|]]|\\b)>))` , 'ig' ) ,
146+ `<strong style="color: ${ theme . palette . primary . main } ">$1</strong>` ,
147+ ) ;
148+
149+ return (
150+ < SearchResult
151+ key = { `result-${ entry . url } ` }
152+ entry = { entry }
153+ summary = { summary }
154+ onClick = { handleClose }
155+ />
156+ ) ;
157+ } )
158+ ) : isNotEmpty ( escapedSearch ) ? (
159+ < Typography
160+ variant = "h3"
161+ component = "div"
162+ key = "no-results"
163+ sx = { { width : '100%' , textAlign : 'center' , marginTop : '16px' } }
164+ >
165+ No results found
166+ </ Typography >
167+ ) : (
168+ < StyledSuggestions >
169+ < StyledSuggestionSection >
170+ < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
171+ Getting Started
172+ </ Typography >
173+ < SuggestionLink href = "/docs/start-with-a-template" > Start With a Template</ SuggestionLink >
174+ < SuggestionLink href = "/docs/add-to-your-site" > Add to Your Site</ SuggestionLink >
175+ < SuggestionLink href = "/docs/configuration-options" > Configuration Options</ SuggestionLink >
176+ < SuggestionLink href = "/docs/collection-overview" > Collections</ SuggestionLink >
177+ </ StyledSuggestionSection >
178+ < StyledSuggestionSection >
179+ < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
180+ Backends
181+ </ Typography >
182+ < SuggestionLink href = "/docs/github-backend" > GitHub</ SuggestionLink >
183+ < SuggestionLink href = "/docs/bitbucket-backend" > Bitbucket</ SuggestionLink >
184+ < SuggestionLink href = "/docs/gitlab-backend" > GitLab</ SuggestionLink >
185+ </ StyledSuggestionSection >
186+ < StyledSuggestionSection >
187+ < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
188+ Customize Your CMS
189+ </ Typography >
190+ < SuggestionLink href = "/docs/custom-previews" > Custom Previews</ SuggestionLink >
191+ < SuggestionLink href = "/docs/custom-widgets" > Custom Widgets</ SuggestionLink >
192+ < SuggestionLink href = "/docs/custom-icons" > Custom Icons</ SuggestionLink >
193+ < SuggestionLink href = "/docs/additional-links" > Custom Pages / Links</ SuggestionLink >
194+ </ StyledSuggestionSection >
195+ < StyledSuggestionSection >
196+ < Typography variant = "h3" sx = { { marginBottom : '4px' } } >
197+ Widgets
198+ </ Typography >
199+ < SuggestionLink href = "/docs/widget-string" > String</ SuggestionLink >
200+ < SuggestionLink href = "/docs/widget-image" > Image</ SuggestionLink >
201+ < SuggestionLink href = "/docs/widget-datetime" > Datetime</ SuggestionLink >
202+ < SuggestionLink href = "/docs/widget-markdown" > Markdown</ SuggestionLink >
203+ </ StyledSuggestionSection >
204+ </ StyledSuggestions >
205+ ) ;
206+ } , [ handleClose , search , searchResults , theme . palette . primary . main ] ) ;
205207
206208 return (
207209 < StyledDialog open = { open } onClose = { handleClose } fullScreen = { fullScreen } fullWidth >
0 commit comments