From dd4c0ed773c400c2453bb46e8eb9ed75f66ed5c1 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Thu, 26 Sep 2024 10:15:12 +0100 Subject: [PATCH 1/3] fix: limit marked text by characters --- .../site/src/components/Navigation/Search.tsx | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/packages/site/src/components/Navigation/Search.tsx b/packages/site/src/components/Navigation/Search.tsx index dba7c498..b8efe20a 100644 --- a/packages/site/src/components/Navigation/Search.tsx +++ b/packages/site/src/components/Navigation/Search.tsx @@ -1,4 +1,12 @@ -import { useEffect, useState, useMemo, useCallback, useRef, forwardRef } from 'react'; +import { + createElement, + useEffect, + useState, + useMemo, + useCallback, + useRef, + forwardRef, +} from 'react'; import type { KeyboardEventHandler, Dispatch, SetStateAction, FormEvent, MouseEvent } from 'react'; import { useFetcher } from '@remix-run/react'; import { @@ -42,10 +50,21 @@ function matchAll(text: string, pattern: RegExp) { * Highlight a text string with an array of match words * * @param text - text to highlight - * @param result - search result to use for highlighting - * @param limit - limit to the number of tokens after first match + * @param matches - regular expression patterns to match against + * @param limit - limit to the number of characters after first match + * @param classname - CSS classname to use */ -function MarkedText({ text, matches, limit }: { text: string; matches: string[]; limit?: number }) { +function MarkedText({ + text, + matches, + limit, + className, +}: { + text: string; + matches: string[]; + limit?: number; + className?: string; +}) { // Split by delimeter, but _keep_ delimeter! const splits = matchAll(text, SPACE_OR_PUNCTUATION); const tokens: string[] = []; @@ -79,23 +98,38 @@ function MarkedText({ text, matches, limit }: { text: string; matches: string[]; lastIndex = tokens.length; } else { firstIndex = tokens.findIndex((token) => pattern.test(token)); - lastIndex = firstIndex + limit; + let numChars = 0; + for ( + lastIndex = firstIndex + 1; + lastIndex < tokens.length - 1 && numChars + tokens[lastIndex].length <= limit; + lastIndex++ + ) { + numChars += tokens[lastIndex].length; + } } if (tokens.length === 0) { - return <>{...tokens}; + return {...tokens}; } else { const firstRenderer = renderToken(tokens[firstIndex]); const remainingTokens = tokens.slice(firstIndex + 1, lastIndex); const remainingRenderers = remainingTokens.map((token) => renderToken(token)); return ( - <> - {hasLimit && '... '} + {firstRenderer} {...remainingRenderers} - {hasLimit && ' ...'} - + ); } } @@ -187,14 +221,13 @@ function SearchResultItem({ const Link = useLinkProvider(); // Render the icon - const iconRenderer = - type === 'lvl1' ? ( - - ) : type === 'content' ? ( - - ) : ( - - ); + const iconProps = useMemo(() => { + return { className: 'inline-block w-6 mx-2 shrink-0' }; + }, []); + const iconRenderer = createElement( + type === 'lvl1' ? DocumentIcon : type === 'content' ? Bars3BottomLeftIcon : HashtagIcon, + iconProps, + ); // Generic "this document matched" const title = result.type === 'content' ? result['content'] : hierarchy[type as HeadingLevel]!; @@ -202,7 +235,12 @@ function SearchResultItem({ // Render the title, i.e. content or heading const titleRenderer = ( - + ); // Render the subtitle i.e. file name @@ -211,7 +249,7 @@ function SearchResultItem({ subtitleRenderer = undefined; } else { const subtitle = result.hierarchy.lvl1!; - subtitleRenderer = ; + subtitleRenderer = ; } const enterIconRenderer = ( @@ -228,8 +266,8 @@ function SearchResultItem({
{iconRenderer}
- {titleRenderer} - {subtitleRenderer && {subtitleRenderer}} + {titleRenderer} + {subtitleRenderer}
{enterIconRenderer}
From 14b80940dba812034e678854362d8ba99f56f058 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Thu, 26 Sep 2024 10:16:01 +0100 Subject: [PATCH 2/3] chore: add changeset --- .changeset/purple-bugs-relate.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/purple-bugs-relate.md diff --git a/.changeset/purple-bugs-relate.md b/.changeset/purple-bugs-relate.md new file mode 100644 index 00000000..f32cf46c --- /dev/null +++ b/.changeset/purple-bugs-relate.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/site': patch +--- + +Replace token-length limit with character-length limit, hide ellipsis from markup From db27fa111d787ac357ebae05e46361b8c668b27b Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Wed, 2 Oct 2024 12:24:26 +0100 Subject: [PATCH 3/3] feat: make charLimit a parameter --- packages/site/src/components/Navigation/Search.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/site/src/components/Navigation/Search.tsx b/packages/site/src/components/Navigation/Search.tsx index b8efe20a..5dbb876e 100644 --- a/packages/site/src/components/Navigation/Search.tsx +++ b/packages/site/src/components/Navigation/Search.tsx @@ -212,8 +212,10 @@ function SearchShortcut() { function SearchResultItem({ result, closeSearch, + charLimit, }: { result: RankedSearchResult; + charLimit?: number; closeSearch?: () => void; }) { const { hierarchy, type, url, queries } = result; @@ -238,7 +240,7 @@ function SearchResultItem({ ); @@ -280,6 +282,7 @@ interface SearchResultsProps { searchListID: string; searchLabelID: string; selectedIndex: number; + charLimit?: number; onHoverSelect: (index: number) => void; className?: string; closeSearch?: () => void; @@ -289,6 +292,7 @@ function SearchResults({ searchResults, searchListID, searchLabelID, + charLimit, className, selectedIndex, onHoverSelect, @@ -363,7 +367,7 @@ function SearchResults({ // Trigger selection on movement, so that scrolling doesn't trigger handler onMouseMove={handleMouseMove} > - + ))} @@ -579,11 +583,12 @@ const SearchPlaceholderButton = forwardRef< export interface SearchProps { debounceTime?: number; + charLimit?: number; } /** * Component that implements a basic search interface */ -export function Search({ debounceTime = 500 }: SearchProps) { +export function Search({ debounceTime = 500, charLimit = 64 }: SearchProps) { const [open, setOpen] = useState(false); const [searchResults, setSearchResults] = useState(); const [selectedIndex, setSelectedIndex] = useState(0); @@ -662,6 +667,7 @@ export function Search({ debounceTime = 500 }: SearchProps) { selectedIndex={selectedIndex} onHoverSelect={setSelectedIndex} closeSearch={triggerClose} + charLimit={charLimit} /> )}