Skip to content

Commit 90126d4

Browse files
committed
Insanely complicated path search results highlighitng and truncating
1 parent a4bbd55 commit 90126d4

File tree

5 files changed

+674
-23
lines changed

5 files changed

+674
-23
lines changed

app/components/SearchPalette.tsx

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import {
1515
UseComboboxState,
1616
UseComboboxStateChangeOptions,
1717
} from "downshift";
18-
import { getStringSlices, JsonSearchEntry } from "~/utilities/search";
18+
import {
19+
getComponentSlices,
20+
getStringSlices,
21+
JsonSearchEntry,
22+
} from "~/utilities/search";
1923
import Fuse from "fuse.js";
2024
import classnames from "~/utilities/classnames";
2125
import { iconForValue } from "~/utilities/icons";
@@ -226,8 +230,6 @@ export function SearchItem({
226230
const itemValue = heroPath.first(json);
227231
const ItemIcon = iconForValue(itemValue);
228232

229-
const components = heroPath.components.slice(1);
230-
231233
return (
232234
<li {...itemProps} className={classnames("w-full hover:cursor-pointer")}>
233235
<div
@@ -246,24 +248,11 @@ export function SearchItem({
246248
)}
247249
></ItemIcon>
248250
<div className="flex flex-col w-full ml-3">
249-
<div className="path flex items-center gap-1 mb-1 text-slate-800 dark:text-white group-hover:text-white">
250-
{components.map((c, index) => {
251-
return [
252-
<Body key={c.toString() + index + "body"} className="text-lg">
253-
{c.toString()}
254-
</Body>,
255-
].concat(
256-
index + 1 === components.length
257-
? []
258-
: [
259-
<ChevronRightIcon
260-
key={c.toString() + index + "arrow"}
261-
className="w-4 h-4"
262-
/>,
263-
]
264-
);
265-
})}
266-
</div>
251+
<SearchPathResult
252+
path={heroPath}
253+
searchResult={result}
254+
isHighlighted={isHighlighted}
255+
/>
267256
<div className="key-value flex justify-between">
268257
{result.item.rawValue && (
269258
<SearchResultValue
@@ -290,6 +279,72 @@ export function SearchItem({
290279
);
291280
}
292281

282+
// Outputs the following pair for each component except for the last one:
283+
// <Body className="text-lg">{component}</Body>,
284+
// <ChevronRightIcon className="w-4 h-4" />,
285+
//
286+
// Highlights parts of the component that match the search query.
287+
// The match indices match against the stringified version of the path (e.g. $.foo.bar.0.details.description)
288+
//
289+
// If combined component strings are too long, then we need to choose some components to hide behind an ellipsis, making sure we don't hide matches
290+
function SearchPathResult({
291+
path,
292+
searchResult,
293+
isHighlighted,
294+
maxWeight = 90,
295+
}: {
296+
path: JSONHeroPath;
297+
isHighlighted: boolean;
298+
searchResult: Fuse.FuseResult<JsonSearchEntry>;
299+
maxWeight?: number;
300+
}) {
301+
const components = path.components.slice(1);
302+
303+
const match = (searchResult.matches ?? []).find(
304+
(match) => match.key === "path" && match.indices.length > 0
305+
);
306+
307+
const matchingIndices = (match?.indices ?? []) as [number, number][];
308+
309+
const displayPath = components.join(".");
310+
311+
const slices = getComponentSlices(
312+
displayPath,
313+
matchingIndices.map(([start, end]) => [start - 2, end - 2]),
314+
maxWeight
315+
);
316+
317+
return (
318+
<>
319+
{slices.map((slice, i) =>
320+
slice.type === "component" ? (
321+
<span
322+
key={i}
323+
className={
324+
slice.slice.isMatch
325+
? classnames(
326+
"font-sans text-base",
327+
isHighlighted
328+
? "text-white underline underline-offset-1"
329+
: "text-indigo-400"
330+
)
331+
: "font-sans text-base"
332+
}
333+
>
334+
{slice.slice.slice}
335+
</span>
336+
) : slice.type === "ellipsis" ? (
337+
<Body key={i} className="text-lg mx-1">
338+
339+
</Body>
340+
) : (
341+
<ChevronRightIcon key={i} className="w-4 h-4 mx-1" />
342+
)
343+
)}
344+
</>
345+
);
346+
}
347+
293348
function SearchResultValue({
294349
isHighlighted,
295350
keyName,

0 commit comments

Comments
 (0)