@echothink-ui/search 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/components/AgentSearchSuggestion.d.ts +2 -0
- package/dist/components/AppCommandSearch.d.ts +2 -0
- package/dist/components/AppDomainSearch.d.ts +2 -0
- package/dist/components/EntitySearchInput.d.ts +4 -0
- package/dist/components/GlobalCommandPalette.d.ts +4 -0
- package/dist/components/ProjectCommandPalette.d.ts +2 -0
- package/dist/components/RecentItems.d.ts +2 -0
- package/dist/components/ResourceSearch.d.ts +2 -0
- package/dist/components/SavedSearches.d.ts +2 -0
- package/dist/components/SearchFacetPanel.d.ts +2 -0
- package/dist/components/SearchResultsPanel.d.ts +2 -0
- package/dist/components/SemanticSearchResult.d.ts +2 -0
- package/dist/components/searchUtils.d.ts +3 -0
- package/dist/components/types.d.ts +175 -0
- package/dist/index.cjs +1224 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1460 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1185 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
- package/src/components/AgentSearchSuggestion.tsx +44 -0
- package/src/components/AppCommandSearch.tsx +163 -0
- package/src/components/AppDomainSearch.tsx +90 -0
- package/src/components/EntitySearchInput.tsx +165 -0
- package/src/components/GlobalCommandPalette.tsx +182 -0
- package/src/components/ProjectCommandPalette.tsx +24 -0
- package/src/components/RecentItems.tsx +136 -0
- package/src/components/ResourceSearch.tsx +27 -0
- package/src/components/SavedSearches.tsx +27 -0
- package/src/components/SearchFacetPanel.tsx +100 -0
- package/src/components/SearchResultsPanel.tsx +327 -0
- package/src/components/SemanticSearchResult.tsx +52 -0
- package/src/components/searchUtils.ts +20 -0
- package/src/components/types.ts +208 -0
- package/src/index.test.tsx +254 -0
- package/src/index.tsx +55 -0
- package/src/styles.css +1716 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type {
|
|
3
|
+
SearchResultGroup,
|
|
4
|
+
SearchResultItem,
|
|
5
|
+
SearchResultMetadata,
|
|
6
|
+
SearchResultsPanelProps,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
function getGroupItems(group: SearchResultGroup) {
|
|
10
|
+
return group.results ?? group.items ?? [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getGroupLabel(group: SearchResultGroup) {
|
|
14
|
+
return (
|
|
15
|
+
group.label ?? group.title ?? group.entityType ?? group.type ?? "Results"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getGroupDomId(group: SearchResultGroup, index: number) {
|
|
20
|
+
const seed = String(group.id ?? getGroupLabel(group))
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
23
|
+
.replace(/(^-|-$)/g, "");
|
|
24
|
+
|
|
25
|
+
return `search-results-${seed || "group"}-${index}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getResultTitle(result: SearchResultItem) {
|
|
29
|
+
return result.title ?? result.name ?? result.label ?? "Untitled result";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getResultDescription(result: SearchResultItem) {
|
|
33
|
+
return result.description ?? result.excerpt;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getResultHref(result: SearchResultItem) {
|
|
37
|
+
return result.href ?? result.url;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getResultId(
|
|
41
|
+
result: SearchResultItem,
|
|
42
|
+
group: SearchResultGroup,
|
|
43
|
+
index: number,
|
|
44
|
+
) {
|
|
45
|
+
return (
|
|
46
|
+
result.id ??
|
|
47
|
+
`${group.id ?? getGroupLabel(group)}-${getResultTitle(result)}-${index}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getTotalCount(groups: SearchResultGroup[], totalCount?: number) {
|
|
52
|
+
if (typeof totalCount === "number") {
|
|
53
|
+
return totalCount;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return groups.reduce((sum, group) => sum + getGroupItems(group).length, 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getEntityInitial(label: string) {
|
|
60
|
+
return label.trim().slice(0, 1).toUpperCase();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isLabeledMetadata(
|
|
64
|
+
metadata: SearchResultMetadata,
|
|
65
|
+
): metadata is { label?: React.ReactNode; value?: React.ReactNode } {
|
|
66
|
+
return (
|
|
67
|
+
typeof metadata === "object" &&
|
|
68
|
+
metadata !== null &&
|
|
69
|
+
!Array.isArray(metadata) &&
|
|
70
|
+
"value" in metadata
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeMetadataValue(
|
|
75
|
+
metadata: SearchResultMetadata,
|
|
76
|
+
): React.ReactNode {
|
|
77
|
+
if (isLabeledMetadata(metadata)) {
|
|
78
|
+
if (metadata.label && metadata.value !== undefined) {
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
{metadata.label}: {metadata.value}
|
|
82
|
+
</>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return metadata.value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return metadata as React.ReactNode;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function hasMetadataSourceValue(
|
|
93
|
+
metadata: unknown,
|
|
94
|
+
): metadata is SearchResultMetadata {
|
|
95
|
+
return (
|
|
96
|
+
metadata !== null &&
|
|
97
|
+
metadata !== undefined &&
|
|
98
|
+
metadata !== false &&
|
|
99
|
+
metadata !== ""
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hasRenderableMetadata(metadata: React.ReactNode) {
|
|
104
|
+
return (
|
|
105
|
+
metadata !== null &&
|
|
106
|
+
metadata !== undefined &&
|
|
107
|
+
metadata !== false &&
|
|
108
|
+
metadata !== ""
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getMetadata(result: SearchResultItem): React.ReactNode[] {
|
|
113
|
+
const source = result.meta ?? result.metadata;
|
|
114
|
+
const values: SearchResultMetadata[] = Array.isArray(source)
|
|
115
|
+
? [...source]
|
|
116
|
+
: [];
|
|
117
|
+
|
|
118
|
+
if (!Array.isArray(source) && hasMetadataSourceValue(source)) {
|
|
119
|
+
values.push(source);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (result.owner) {
|
|
123
|
+
values.push({ label: "Owner", value: result.owner });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (result.status) {
|
|
127
|
+
values.push(result.status);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (result.updatedAt) {
|
|
131
|
+
values.push({ label: "Updated", value: result.updatedAt });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (result.version) {
|
|
135
|
+
values.push(`v${result.version}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return values.map(normalizeMetadataValue).filter(hasRenderableMetadata);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function SearchResultsPanel({
|
|
142
|
+
groups = [],
|
|
143
|
+
query,
|
|
144
|
+
totalCount,
|
|
145
|
+
isLoading = false,
|
|
146
|
+
emptyLabel = "No results found",
|
|
147
|
+
className,
|
|
148
|
+
onResultSelect,
|
|
149
|
+
onSelect,
|
|
150
|
+
...props
|
|
151
|
+
}: SearchResultsPanelProps) {
|
|
152
|
+
const count = getTotalCount(groups, totalCount);
|
|
153
|
+
const hasResults = groups.some((group) => getGroupItems(group).length > 0);
|
|
154
|
+
const classes = ["eth-search-results-panel", className]
|
|
155
|
+
.filter(Boolean)
|
|
156
|
+
.join(" ");
|
|
157
|
+
|
|
158
|
+
const handleSelect = (
|
|
159
|
+
result: SearchResultItem,
|
|
160
|
+
group: SearchResultGroup,
|
|
161
|
+
resultId: string,
|
|
162
|
+
) => {
|
|
163
|
+
onResultSelect?.(result, group);
|
|
164
|
+
onSelect?.(result.id ?? resultId);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<section
|
|
169
|
+
{...props}
|
|
170
|
+
className={classes}
|
|
171
|
+
aria-label={props["aria-label"] ?? "Search results"}
|
|
172
|
+
data-eth-component="SearchResultsPanel"
|
|
173
|
+
>
|
|
174
|
+
<header className="eth-search-results-panel__summary">
|
|
175
|
+
<div>
|
|
176
|
+
<p className="eth-search-results-panel__eyebrow">Search results</p>
|
|
177
|
+
<h3 className="eth-search-results-panel__title">
|
|
178
|
+
{isLoading
|
|
179
|
+
? "Searching..."
|
|
180
|
+
: `${count} result${count === 1 ? "" : "s"}`}
|
|
181
|
+
</h3>
|
|
182
|
+
</div>
|
|
183
|
+
{query ? (
|
|
184
|
+
<span className="eth-search-results-panel__query">"{query}"</span>
|
|
185
|
+
) : null}
|
|
186
|
+
</header>
|
|
187
|
+
|
|
188
|
+
{isLoading ? (
|
|
189
|
+
<div className="eth-search-results-panel__state" role="status">
|
|
190
|
+
<p className="eth-search-results-panel__loading-copy">
|
|
191
|
+
Loading results
|
|
192
|
+
</p>
|
|
193
|
+
<span className="eth-search-results-panel__skeleton" />
|
|
194
|
+
<span className="eth-search-results-panel__skeleton" />
|
|
195
|
+
<span className="eth-search-results-panel__skeleton" />
|
|
196
|
+
</div>
|
|
197
|
+
) : hasResults ? (
|
|
198
|
+
<div className="eth-search-results-panel__groups">
|
|
199
|
+
{groups.map((group, groupIndex) => {
|
|
200
|
+
const items = getGroupItems(group);
|
|
201
|
+
const groupLabel = getGroupLabel(group);
|
|
202
|
+
const groupId = getGroupDomId(group, groupIndex);
|
|
203
|
+
|
|
204
|
+
if (items.length === 0) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<section
|
|
210
|
+
className="eth-search-results-panel__group"
|
|
211
|
+
key={group.id ?? `${groupLabel}-${groupIndex}`}
|
|
212
|
+
aria-labelledby={groupId}
|
|
213
|
+
>
|
|
214
|
+
<div className="eth-search-results-panel__group-header">
|
|
215
|
+
<h4
|
|
216
|
+
className="eth-search-results-panel__group-title"
|
|
217
|
+
id={groupId}
|
|
218
|
+
>
|
|
219
|
+
{groupLabel}
|
|
220
|
+
</h4>
|
|
221
|
+
<span className="eth-search-results-panel__count">
|
|
222
|
+
{group.count ?? items.length}
|
|
223
|
+
</span>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div className="eth-search-results-panel__list">
|
|
227
|
+
{items.map((result, index) => {
|
|
228
|
+
const metadata = getMetadata(result);
|
|
229
|
+
const resultId = getResultId(result, group, index);
|
|
230
|
+
const description = getResultDescription(result);
|
|
231
|
+
const href = getResultHref(result);
|
|
232
|
+
const title = getResultTitle(result);
|
|
233
|
+
const resultContent = (
|
|
234
|
+
<>
|
|
235
|
+
<span
|
|
236
|
+
className="eth-search-results-panel__entity"
|
|
237
|
+
aria-hidden="true"
|
|
238
|
+
>
|
|
239
|
+
{getEntityInitial(
|
|
240
|
+
result.entityType ??
|
|
241
|
+
result.type ??
|
|
242
|
+
group.entityType ??
|
|
243
|
+
groupLabel,
|
|
244
|
+
)}
|
|
245
|
+
</span>
|
|
246
|
+
<span className="eth-search-results-panel__body">
|
|
247
|
+
<span className="eth-search-results-panel__name">
|
|
248
|
+
{title}
|
|
249
|
+
</span>
|
|
250
|
+
{description ? (
|
|
251
|
+
<span className="eth-search-results-panel__description">
|
|
252
|
+
{description}
|
|
253
|
+
</span>
|
|
254
|
+
) : null}
|
|
255
|
+
</span>
|
|
256
|
+
{metadata.length > 0 ? (
|
|
257
|
+
<span className="eth-search-results-panel__metadata">
|
|
258
|
+
{metadata.map((item, metaIndex) => (
|
|
259
|
+
<span
|
|
260
|
+
className="eth-search-results-panel__meta"
|
|
261
|
+
key={metaIndex}
|
|
262
|
+
>
|
|
263
|
+
{item}
|
|
264
|
+
</span>
|
|
265
|
+
))}
|
|
266
|
+
</span>
|
|
267
|
+
) : null}
|
|
268
|
+
</>
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
if (href) {
|
|
272
|
+
return (
|
|
273
|
+
<a
|
|
274
|
+
className="eth-search-results-panel__item"
|
|
275
|
+
href={href}
|
|
276
|
+
key={resultId}
|
|
277
|
+
onClick={
|
|
278
|
+
onResultSelect || onSelect
|
|
279
|
+
? () => handleSelect(result, group, resultId)
|
|
280
|
+
: undefined
|
|
281
|
+
}
|
|
282
|
+
>
|
|
283
|
+
{resultContent}
|
|
284
|
+
</a>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (onResultSelect || onSelect) {
|
|
289
|
+
return (
|
|
290
|
+
<button
|
|
291
|
+
className="eth-search-results-panel__item"
|
|
292
|
+
key={resultId}
|
|
293
|
+
type="button"
|
|
294
|
+
onClick={() => handleSelect(result, group, resultId)}
|
|
295
|
+
>
|
|
296
|
+
{resultContent}
|
|
297
|
+
</button>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<article
|
|
303
|
+
className="eth-search-results-panel__item"
|
|
304
|
+
key={resultId}
|
|
305
|
+
>
|
|
306
|
+
{resultContent}
|
|
307
|
+
</article>
|
|
308
|
+
);
|
|
309
|
+
})}
|
|
310
|
+
</div>
|
|
311
|
+
</section>
|
|
312
|
+
);
|
|
313
|
+
})}
|
|
314
|
+
</div>
|
|
315
|
+
) : (
|
|
316
|
+
<div className="eth-search-results-panel__state">
|
|
317
|
+
<p className="eth-search-results-panel__empty-title">{emptyLabel}</p>
|
|
318
|
+
{query ? (
|
|
319
|
+
<p className="eth-search-results-panel__empty-copy">
|
|
320
|
+
Try another keyword or remove a filter.
|
|
321
|
+
</p>
|
|
322
|
+
) : null}
|
|
323
|
+
</div>
|
|
324
|
+
)}
|
|
325
|
+
</section>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { SemanticSearchResultProps } from "./types";
|
|
3
|
+
|
|
4
|
+
export function SemanticSearchResult({ result, className, ...props }: SemanticSearchResultProps) {
|
|
5
|
+
const evidence = result.evidence ?? [];
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<article
|
|
9
|
+
{...props}
|
|
10
|
+
className={`eth-search-semantic-result ${className ?? ""}`}
|
|
11
|
+
data-eth-component="SemanticSearchResult"
|
|
12
|
+
>
|
|
13
|
+
<p className="eth-search-semantic-result__snippet">
|
|
14
|
+
{renderHighlightedSnippet(result.snippet, result.highlights ?? [])}
|
|
15
|
+
</p>
|
|
16
|
+
{evidence.length > 0 ? (
|
|
17
|
+
<div className="eth-search-semantic-result__evidence">
|
|
18
|
+
<span className="eth-search-semantic-result__evidence-label">Evidence</span>
|
|
19
|
+
<ul className="eth-search-semantic-result__evidence-list">
|
|
20
|
+
{evidence.map((item) => (
|
|
21
|
+
<li key={item.id}>
|
|
22
|
+
{item.href ? (
|
|
23
|
+
<a className="eth-search-semantic-result__evidence-item" href={item.href}>
|
|
24
|
+
{item.label}
|
|
25
|
+
</a>
|
|
26
|
+
) : (
|
|
27
|
+
<span className="eth-search-semantic-result__evidence-item">{item.label}</span>
|
|
28
|
+
)}
|
|
29
|
+
</li>
|
|
30
|
+
))}
|
|
31
|
+
</ul>
|
|
32
|
+
</div>
|
|
33
|
+
) : null}
|
|
34
|
+
</article>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderHighlightedSnippet(snippet: string, highlights: Array<[number, number]>) {
|
|
39
|
+
if (!highlights.length) return snippet;
|
|
40
|
+
const sorted = [...highlights].sort((a, b) => a[0] - b[0]);
|
|
41
|
+
const parts: React.ReactNode[] = [];
|
|
42
|
+
let cursor = 0;
|
|
43
|
+
sorted.forEach(([start, end], index) => {
|
|
44
|
+
const safeStart = Math.max(cursor, Math.min(snippet.length, start));
|
|
45
|
+
const safeEnd = Math.max(safeStart, Math.min(snippet.length, end));
|
|
46
|
+
if (safeStart > cursor) parts.push(snippet.slice(cursor, safeStart));
|
|
47
|
+
parts.push(<mark key={index}>{snippet.slice(safeStart, safeEnd)}</mark>);
|
|
48
|
+
cursor = safeEnd;
|
|
49
|
+
});
|
|
50
|
+
if (cursor < snippet.length) parts.push(snippet.slice(cursor));
|
|
51
|
+
return parts;
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CommandItem } from "./types";
|
|
2
|
+
|
|
3
|
+
export function filterCommands(commands: CommandItem[], query: string) {
|
|
4
|
+
const normalized = query.trim().toLowerCase();
|
|
5
|
+
if (!normalized) return commands;
|
|
6
|
+
return commands.filter((command) =>
|
|
7
|
+
[command.label, command.hint, command.group, command.shortcut]
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
.some((value) => String(value).toLowerCase().includes(normalized))
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function groupBy<T>(items: T[], getKey: (item: T) => string) {
|
|
14
|
+
const groups = new Map<string, T[]>();
|
|
15
|
+
items.forEach((item) => {
|
|
16
|
+
const key = getKey(item);
|
|
17
|
+
groups.set(key, [...(groups.get(key) ?? []), item]);
|
|
18
|
+
});
|
|
19
|
+
return Array.from(groups.entries());
|
|
20
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
export interface CommandItem {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
group?: string;
|
|
8
|
+
shortcut?: string;
|
|
9
|
+
onSelect: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AppCommandKind = "app-domain" | "resource" | "action";
|
|
13
|
+
|
|
14
|
+
export interface AppCommandItem {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
description?: React.ReactNode;
|
|
18
|
+
domain?: string;
|
|
19
|
+
kind?: AppCommandKind;
|
|
20
|
+
shortcut?: string;
|
|
21
|
+
status?: React.ReactNode;
|
|
22
|
+
onSelect?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AppCommandSearchProps extends Omit<
|
|
26
|
+
React.HTMLAttributes<HTMLElement>,
|
|
27
|
+
"defaultValue" | "onChange" | "onSelect" | "onSubmit"
|
|
28
|
+
> {
|
|
29
|
+
value?: string;
|
|
30
|
+
defaultValue?: string;
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
commands?: AppCommandItem[];
|
|
33
|
+
kbdHint?: React.ReactNode;
|
|
34
|
+
scopeLabel?: React.ReactNode;
|
|
35
|
+
emptyText?: React.ReactNode;
|
|
36
|
+
onChange?: (value: string) => void;
|
|
37
|
+
onSelect?: (id: string) => void;
|
|
38
|
+
onSubmit?: (value: string) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface GlobalCommandPaletteProps extends Omit<
|
|
42
|
+
React.HTMLAttributes<HTMLElement>,
|
|
43
|
+
"onSelect"
|
|
44
|
+
> {
|
|
45
|
+
open: boolean;
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
commands: CommandItem[];
|
|
48
|
+
heading?: React.ReactNode;
|
|
49
|
+
contextLabel?: React.ReactNode;
|
|
50
|
+
query?: string;
|
|
51
|
+
searchPlaceholder?: string;
|
|
52
|
+
onQueryChange?: (query: string) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ProjectCommandPaletteProps extends GlobalCommandPaletteProps {
|
|
56
|
+
projectRef: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type SearchResultMetadata =
|
|
60
|
+
| React.ReactNode
|
|
61
|
+
| {
|
|
62
|
+
label?: React.ReactNode;
|
|
63
|
+
value?: React.ReactNode;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export interface SearchResultItem {
|
|
67
|
+
id?: string;
|
|
68
|
+
label?: string;
|
|
69
|
+
title?: string;
|
|
70
|
+
name?: string;
|
|
71
|
+
description?: React.ReactNode;
|
|
72
|
+
excerpt?: React.ReactNode;
|
|
73
|
+
href?: string;
|
|
74
|
+
url?: string;
|
|
75
|
+
entityType?: string;
|
|
76
|
+
type?: string;
|
|
77
|
+
meta?: SearchResultMetadata | SearchResultMetadata[];
|
|
78
|
+
metadata?: SearchResultMetadata | SearchResultMetadata[];
|
|
79
|
+
owner?: React.ReactNode;
|
|
80
|
+
status?: React.ReactNode;
|
|
81
|
+
updatedAt?: React.ReactNode;
|
|
82
|
+
version?: string | number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SearchResultGroup {
|
|
86
|
+
id?: string;
|
|
87
|
+
label?: string;
|
|
88
|
+
title?: string;
|
|
89
|
+
type?: string;
|
|
90
|
+
entityType?: string;
|
|
91
|
+
count?: number;
|
|
92
|
+
results?: SearchResultItem[];
|
|
93
|
+
items?: SearchResultItem[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface SearchResultsPanelProps extends Omit<
|
|
97
|
+
React.HTMLAttributes<HTMLElement>,
|
|
98
|
+
"onSelect"
|
|
99
|
+
> {
|
|
100
|
+
groups?: SearchResultGroup[];
|
|
101
|
+
query?: string;
|
|
102
|
+
totalCount?: number;
|
|
103
|
+
isLoading?: boolean;
|
|
104
|
+
emptyLabel?: React.ReactNode;
|
|
105
|
+
onResultSelect?: (result: SearchResultItem, group: SearchResultGroup) => void;
|
|
106
|
+
onSelect?: (id: string) => void;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface SearchFacetOption {
|
|
110
|
+
value: string;
|
|
111
|
+
label: string;
|
|
112
|
+
count?: number;
|
|
113
|
+
disabled?: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface SearchFacet {
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
options: SearchFacetOption[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface SearchFacetPanelProps extends Omit<React.HTMLAttributes<HTMLElement>, "onChange"> {
|
|
123
|
+
facets: SearchFacet[];
|
|
124
|
+
selected: Record<string, string[]>;
|
|
125
|
+
onChange: (selected: Record<string, string[]>) => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface SavedSearch extends Record<string, unknown> {
|
|
129
|
+
id: string;
|
|
130
|
+
label: string;
|
|
131
|
+
query: string;
|
|
132
|
+
updatedAt: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface SavedSearchesProps extends React.HTMLAttributes<HTMLElement> {
|
|
136
|
+
searches: SavedSearch[];
|
|
137
|
+
onRun?: (id: string) => void;
|
|
138
|
+
onDelete?: (id: string) => void;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface RecentItem extends Record<string, unknown> {
|
|
142
|
+
id: string;
|
|
143
|
+
label: string;
|
|
144
|
+
kind: string;
|
|
145
|
+
description?: React.ReactNode;
|
|
146
|
+
href?: string;
|
|
147
|
+
visitedAt: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface RecentItemsProps extends Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> {
|
|
151
|
+
items: RecentItem[];
|
|
152
|
+
emptyText?: React.ReactNode;
|
|
153
|
+
onSelect?: (id: string) => void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface EntitySuggestion {
|
|
157
|
+
id: string;
|
|
158
|
+
label: string;
|
|
159
|
+
kind?: string;
|
|
160
|
+
meta?: React.ReactNode;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface EntitySearchInputProps extends Omit<
|
|
164
|
+
React.HTMLAttributes<HTMLElement>,
|
|
165
|
+
"defaultValue" | "onChange" | "onSelect"
|
|
166
|
+
> {
|
|
167
|
+
placeholder?: string;
|
|
168
|
+
value?: string;
|
|
169
|
+
defaultValue?: string;
|
|
170
|
+
onSearch?: (q: string) => void;
|
|
171
|
+
suggestions?: EntitySuggestion[];
|
|
172
|
+
suggestionsLabel?: string;
|
|
173
|
+
resultsLabel?: React.ReactNode;
|
|
174
|
+
loadingLabel?: React.ReactNode;
|
|
175
|
+
open?: boolean;
|
|
176
|
+
defaultOpen?: boolean;
|
|
177
|
+
loading?: boolean;
|
|
178
|
+
emptyText?: React.ReactNode;
|
|
179
|
+
onChange?: (value: string) => void;
|
|
180
|
+
onOpenChange?: (open: boolean) => void;
|
|
181
|
+
onSelect?: (suggestion: EntitySuggestion) => void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface AppDomainSearchProps extends EntitySearchInputProps {
|
|
185
|
+
appDomainRef?: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface ResourceSearchProps extends EntitySearchInputProps {
|
|
189
|
+
resourceScope?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface SemanticSearchResultProps extends React.HTMLAttributes<HTMLElement> {
|
|
193
|
+
result: {
|
|
194
|
+
id: string;
|
|
195
|
+
snippet: string;
|
|
196
|
+
highlights?: Array<[number, number]>;
|
|
197
|
+
evidence: Array<{ id: string; label: string; href?: string }>;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface AgentSearchSuggestionProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
202
|
+
suggestion: {
|
|
203
|
+
id: string;
|
|
204
|
+
label: string;
|
|
205
|
+
rationale?: string;
|
|
206
|
+
onSelect?: () => void;
|
|
207
|
+
};
|
|
208
|
+
}
|