diff --git a/frontend/src/layout/navigation-3000/Navigation.scss b/frontend/src/layout/navigation-3000/Navigation.scss index 6a9f62270700a..5983884d0eb7e 100644 --- a/frontend/src/layout/navigation-3000/Navigation.scss +++ b/frontend/src/layout/navigation-3000/Navigation.scss @@ -619,11 +619,6 @@ .SidebarListItem__icon { flex-shrink: 0; } - - .SidebarListItem__name { - overflow: hidden; - text-overflow: ellipsis; - } } .SidebarListItem__rename { diff --git a/frontend/src/layout/navigation-3000/components/SearchHighlight.tsx b/frontend/src/layout/navigation-3000/components/SearchHighlight.tsx new file mode 100644 index 0000000000000..3c8c785433695 --- /dev/null +++ b/frontend/src/layout/navigation-3000/components/SearchHighlight.tsx @@ -0,0 +1,29 @@ +interface Props { + string: string + substring: string + className?: string +} + +/** + * Highlight a substring within a string (case-insensitive) + * @param string - the aggregate string to search within (e.g. "Hello, world!") + * @param substring - the substring to search for (e.g. "world") + * @param className - additional classes to apply to the component + */ +export default function SearchHighlight({ string, substring, className }: Props): JSX.Element { + const parts = string.split(new RegExp(`(${substring})`, 'gi')) + return ( +
+ {parts.map((part, index) => ( + + {part} + + ))} +
+ ) +} diff --git a/frontend/src/layout/navigation-3000/components/SidebarList.tsx b/frontend/src/layout/navigation-3000/components/SidebarList.tsx index b002c2d2f504b..72decf9b4642d 100644 --- a/frontend/src/layout/navigation-3000/components/SidebarList.tsx +++ b/frontend/src/layout/navigation-3000/components/SidebarList.tsx @@ -15,6 +15,8 @@ import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' import { InfiniteLoader } from 'react-virtualized/dist/es/InfiniteLoader' import { List, ListProps } from 'react-virtualized/dist/es/List' +import SearchHighlight from '~/layout/navigation-3000/components/SearchHighlight' + import { ITEM_KEY_PART_SEPARATOR, navigation3000Logic } from '../navigationLogic' import { BasicListItem, @@ -305,7 +307,7 @@ function SidebarListItem({ item, validateName, active, style }: SidebarListItemP style={{ '--depth': item.depth } as React.CSSProperties} > {item.icon &&
{item.icon}
} -
{item.name}
+ ) } else if (!save || (!isItemTentative(item) && newName === null)) { @@ -313,7 +315,7 @@ function SidebarListItem({ item, validateName, active, style }: SidebarListItemP throw new Error('Tentative items should not be rendered in read mode') } let formattedName = item.searchMatch?.nameHighlightRanges?.length ? ( - {item.name} + ) : ( item.name ) @@ -495,40 +497,6 @@ function SidebarListItem({ item, validateName, active, style }: SidebarListItemP ) } -/** Text with specified ranges highlighted by increased font weight. Great for higlighting search term matches. */ -function TextWithHighlights({ - children, - ranges, -}: { - children: string - ranges: readonly [number, number][] -}): JSX.Element { - const segments: JSX.Element[] = [] - let previousBoldEnd = 0 - let segmentIndex = 0 - // Divide the item name into bold and regular segments - for (let i = 0; i < ranges.length; i++) { - const [currentBoldStart, currentBoldEnd] = ranges[i] - if (currentBoldStart > previousBoldEnd) { - segments.push( - {children.slice(previousBoldEnd, currentBoldStart)} - ) - segmentIndex++ - } - segments.push({children.slice(currentBoldStart, currentBoldEnd)}) - segmentIndex++ - previousBoldEnd = currentBoldEnd - } - // If there is a non-highlighted segment left at the end, add it now - if (previousBoldEnd < children.length) { - segments.push( - {children.slice(previousBoldEnd, children.length)} - ) - } - - return <>{segments} -} - /** Smart rendering of list item extra context. */ function ExtraContext({ data }: { data: ExtraListItemContext }): JSX.Element { return isDayjs(data) ? : <>{data}