|
9 | 9 | Button, |
10 | 10 | Dialog, |
11 | 11 | InputGroup, |
| 12 | + NonIdealState, |
12 | 13 | Spinner, |
13 | 14 | SpinnerSize, |
14 | 15 | Tag, |
@@ -37,6 +38,7 @@ import { |
37 | 38 | type SearchResult, |
38 | 39 | type SortConfig, |
39 | 40 | buildSearchIndex, |
| 41 | + formatMetadataDate, |
40 | 42 | searchIndexedNodes, |
41 | 43 | sortSearchResults, |
42 | 44 | splitWithHighlights, |
@@ -108,6 +110,43 @@ const ResultRow = ({ |
108 | 110 | </Button> |
109 | 111 | ); |
110 | 112 |
|
| 113 | +const PreviewPane = ({ result }: { result: SearchResult | null }) => { |
| 114 | + if (!result) { |
| 115 | + return ( |
| 116 | + <div className="flex min-h-0 flex-1 items-center justify-center overflow-hidden"> |
| 117 | + <NonIdealState |
| 118 | + icon="search" |
| 119 | + title="Search DG nodes" |
| 120 | + description="Type a keyword to preview matching discourse graph nodes." |
| 121 | + /> |
| 122 | + </div> |
| 123 | + ); |
| 124 | + } |
| 125 | + const isPage = !!getPageTitleByPageUid(result.uid); |
| 126 | + |
| 127 | + return ( |
| 128 | + <div className="flex min-h-0 flex-1 flex-col overflow-hidden"> |
| 129 | + <div className="flex-none flex-row gap-2 border-b border-gray-200 px-5 py-3 text-xs text-gray-500"> |
| 130 | + Created: {formatMetadataDate(result.createdAt)} · Last modified:{" "} |
| 131 | + {formatMetadataDate(result.lastModified)} · Author:{" "} |
| 132 | + {result.authorName || "Unknown"} |
| 133 | + </div> |
| 134 | + <div |
| 135 | + className="min-h-0 flex-1 overflow-y-auto border-t border-gray-200 px-5 py-3" |
| 136 | + onMouseDown={(event) => event.preventDefault()} |
| 137 | + > |
| 138 | + <div className="pointer-events-none"> |
| 139 | + {isPage ? ( |
| 140 | + <RenderRoamPage hideMentions key={result.uid} uid={result.uid} /> |
| 141 | + ) : ( |
| 142 | + <RenderRoamBlock key={result.uid} uid={result.uid} zoomPath /> |
| 143 | + )} |
| 144 | + </div> |
| 145 | + </div> |
| 146 | + </div> |
| 147 | + ); |
| 148 | +}; |
| 149 | + |
111 | 150 | const AdvancedNodeSearchDialog = ({ |
112 | 151 | isOpen, |
113 | 152 | onClose, |
@@ -408,6 +447,8 @@ const AdvancedNodeSearchDialog = ({ |
408 | 447 | ], |
409 | 448 | ); |
410 | 449 |
|
| 450 | + const showSplitView = contentState === "results"; |
| 451 | + |
411 | 452 | return ( |
412 | 453 | <Dialog |
413 | 454 | autoFocus={false} |
@@ -459,25 +500,30 @@ const AdvancedNodeSearchDialog = ({ |
459 | 500 | /> |
460 | 501 | </div> |
461 | 502 | <div className="flex min-h-0 w-full flex-1 overflow-hidden"> |
462 | | - {contentState === "results" ? ( |
463 | | - <div |
464 | | - aria-label="Search results" |
465 | | - className="w-full overflow-y-auto py-1" |
466 | | - ref={resultsPanelRef} |
467 | | - role="listbox" |
468 | | - > |
469 | | - {results.map((result, index) => ( |
470 | | - <ResultRow |
471 | | - active={index === activeIndex} |
472 | | - key={result.uid} |
473 | | - keywords={keywords} |
474 | | - nodeConfig={nodeConfigByType[result.type]} |
475 | | - onClick={() => setActiveIndex(index)} |
476 | | - onMouseEnter={() => setActiveIndex(index)} |
477 | | - result={result} |
478 | | - /> |
479 | | - ))} |
480 | | - </div> |
| 503 | + {showSplitView ? ( |
| 504 | + <> |
| 505 | + <div |
| 506 | + aria-label="Search results" |
| 507 | + className="w-1/3 shrink-0 overflow-y-auto border-r border-gray-200 py-1" |
| 508 | + ref={resultsPanelRef} |
| 509 | + role="listbox" |
| 510 | + > |
| 511 | + {results.map((result, index) => ( |
| 512 | + <ResultRow |
| 513 | + active={index === activeIndex} |
| 514 | + key={result.uid} |
| 515 | + keywords={keywords} |
| 516 | + nodeConfig={nodeConfigByType[result.type]} |
| 517 | + onClick={() => setActiveIndex(index)} |
| 518 | + onMouseEnter={() => setActiveIndex(index)} |
| 519 | + result={result} |
| 520 | + /> |
| 521 | + ))} |
| 522 | + </div> |
| 523 | + <div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden"> |
| 524 | + <PreviewPane result={activeResult} /> |
| 525 | + </div> |
| 526 | + </> |
481 | 527 | ) : ( |
482 | 528 | <div className="flex min-h-0 w-full flex-1 items-center justify-center px-4 py-8 text-center text-sm text-gray-500"> |
483 | 529 | {contentState === "indexing" && ( |
|
0 commit comments