@eventcatalog/core 3.35.1 → 3.36.2
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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/chunk-D6IBLY3O.js +320 -0
- package/dist/{chunk-R4DR3YAH.js → chunk-H6TGUW5O.js} +1 -1
- package/dist/{chunk-VJ357XOI.js → chunk-IO4U4MPC.js} +1 -1
- package/dist/{chunk-JEQZWJWP.js → chunk-L723FWAT.js} +1 -1
- package/dist/{chunk-B7C4DHFE.js → chunk-R5ZDI2JO.js} +1 -1
- package/dist/{chunk-4SNN54V4.js → chunk-SEAN3UND.js} +1 -1
- package/dist/{chunk-3KXCGYET.js → chunk-ULZYHF3V.js} +5 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +22 -0
- package/dist/docs/api/_category_.json +1 -1
- package/dist/docs/contributing/_category_.json +1 -1
- package/dist/docs/development/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/04-skills/_category_.json +1 -1
- package/dist/docs/development/authentication/providers/_category_.json +1 -1
- package/dist/docs/development/bring-your-own-documentation/custom-pages/_category_.json +1 -1
- package/dist/docs/development/customization/01-customize-landing-page.md +1 -1
- package/dist/docs/development/customization/03-search.md +79 -0
- package/dist/docs/development/customization/custom-components/_category_.json +1 -1
- package/dist/docs/development/customization/customize-sidebars/_category_.json +1 -1
- package/dist/docs/development/customization/customize-visualizer/_category_.json +1 -1
- package/dist/docs/development/design/_category_.json +1 -1
- package/dist/docs/development/guides/changelogs/_category_.json +1 -1
- package/dist/docs/development/guides/channels/_category_.json +1 -1
- package/dist/docs/development/guides/channels/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/channels/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/development/guides/data/_category_.json +1 -1
- package/dist/docs/development/guides/data/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/data-products/_category_.json +1 -1
- package/dist/docs/development/guides/domains/02-creating-domains/_category_.json +1 -1
- package/dist/docs/development/guides/domains/03-ownership-and-language/_category_.json +1 -1
- package/dist/docs/development/guides/domains/05-entities/_category_.json +1 -1
- package/dist/docs/development/guides/domains/_category_.json +1 -1
- package/dist/docs/development/guides/flows/_category_.json +1 -1
- package/dist/docs/development/guides/messages/_category_.json +1 -1
- package/dist/docs/development/guides/messages/commands/_category_.json +1 -1
- package/dist/docs/development/guides/messages/common/_category_.json +1 -1
- package/dist/docs/development/guides/messages/events/_category_.json +1 -1
- package/dist/docs/development/guides/messages/queries/_category_.json +1 -1
- package/dist/docs/development/guides/owners/_category_.json +1 -1
- package/dist/docs/development/guides/owners/teams/_category_.json +1 -1
- package/dist/docs/development/guides/owners/users/_category_.json +1 -1
- package/dist/docs/development/guides/schemas/_category_.json +1 -1
- package/dist/docs/development/guides/services/_category_.json +1 -1
- package/dist/docs/development/guides/services/adding-to-services/_category_.json +1 -1
- package/dist/docs/development/guides/services/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/services/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/plugins/_category_.json +1 -1
- package/dist/docs/plugins/amazon-apigateway/_category_.json +1 -1
- package/dist/docs/plugins/asyncapi/_category_.json +1 -1
- package/dist/docs/plugins/aws-glue-registry/_category_.json +1 -1
- package/dist/docs/plugins/backstage/_category_.json +1 -1
- package/dist/docs/plugins/confluent-schema-registry/_category_.json +1 -1
- package/dist/docs/plugins/eventbridge/_category_.json +1 -1
- package/dist/docs/plugins/eventcatalog-federation/_category_.json +1 -1
- package/dist/docs/plugins/github/_category_.json +1 -1
- package/dist/docs/plugins/graphql/_category_.json +1 -1
- package/dist/docs/plugins/hookdeck/_category_.json +1 -1
- package/dist/docs/plugins/openapi/_category_.json +1 -1
- package/dist/eventcatalog.cjs +434 -35
- package/dist/eventcatalog.config.d.cts +13 -1
- package/dist/eventcatalog.config.d.ts +13 -1
- package/dist/eventcatalog.js +88 -11
- package/dist/features.cjs +6 -0
- package/dist/features.d.cts +2 -1
- package/dist/features.d.ts +2 -1
- package/dist/features.js +3 -1
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/search-indexer.cjs +356 -0
- package/dist/search-indexer.d.cts +30 -0
- package/dist/search-indexer.d.ts +30 -0
- package/dist/search-indexer.js +10 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +28 -32
- package/eventcatalog/src/components/Search/SearchModal.tsx +248 -148
- package/eventcatalog/src/components/Search/search-utils.spec.ts +138 -1
- package/eventcatalog/src/components/Search/search-utils.ts +271 -0
- package/eventcatalog/src/env.d.ts +1 -0
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -7
- package/eventcatalog/src/stores/theme-store.ts +9 -13
- package/package.json +3 -2
|
@@ -27,7 +27,18 @@ import { useStore } from '@nanostores/react';
|
|
|
27
27
|
import { favoritesStore, toggleFavorite as toggleFavoriteAction } from '../../stores/favorites-store';
|
|
28
28
|
import { buildUrl } from '@utils/url-builder';
|
|
29
29
|
import { resolveIconUrl } from '@utils/icon';
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
applyActiveFilter,
|
|
32
|
+
getSearchFilters,
|
|
33
|
+
getUrlForSearchItem,
|
|
34
|
+
highlightQuery,
|
|
35
|
+
mapPagefindResultsToSearchItems,
|
|
36
|
+
type SearchItem,
|
|
37
|
+
type SearchNode,
|
|
38
|
+
} from './search-utils';
|
|
39
|
+
|
|
40
|
+
const INDEXED_RESULT_LOAD_LIMIT = 50;
|
|
41
|
+
const SEARCH_RESULT_DISPLAY_LIMIT = 25;
|
|
31
42
|
|
|
32
43
|
const typeIcons: any = {
|
|
33
44
|
Domain: RectangleGroupIcon,
|
|
@@ -46,6 +57,9 @@ const typeIcons: any = {
|
|
|
46
57
|
Container: CircleStackIcon,
|
|
47
58
|
'Data Product': CubeIcon,
|
|
48
59
|
Flow: QueueListIcon,
|
|
60
|
+
'Custom Doc': DocumentTextIcon,
|
|
61
|
+
'Resource Doc': DocumentTextIcon,
|
|
62
|
+
Changelog: DocumentTextIcon,
|
|
49
63
|
default: DocumentTextIcon,
|
|
50
64
|
};
|
|
51
65
|
|
|
@@ -67,6 +81,9 @@ const typeColors: any = {
|
|
|
67
81
|
Container: 'text-indigo-500 dark:text-indigo-400 bg-indigo-50 dark:bg-indigo-500/10 ring-indigo-200 dark:ring-indigo-500/30',
|
|
68
82
|
'Data Product': 'text-sky-500 dark:text-sky-400 bg-sky-50 dark:bg-sky-500/10 ring-sky-200 dark:ring-sky-500/30',
|
|
69
83
|
Flow: 'text-fuchsia-500 dark:text-fuchsia-400 bg-fuchsia-50 dark:bg-fuchsia-500/10 ring-fuchsia-200 dark:ring-fuchsia-500/30',
|
|
84
|
+
'Custom Doc': 'text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-500/10 ring-gray-200 dark:ring-gray-500/30',
|
|
85
|
+
'Resource Doc': 'text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-500/10 ring-gray-200 dark:ring-gray-500/30',
|
|
86
|
+
Changelog: 'text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-500/10 ring-gray-200 dark:ring-gray-500/30',
|
|
70
87
|
default: 'text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-500/10 ring-gray-200 dark:ring-gray-500/30',
|
|
71
88
|
};
|
|
72
89
|
|
|
@@ -74,15 +91,8 @@ function classNames(...classes: (string | boolean | undefined)[]) {
|
|
|
74
91
|
return classes.filter(Boolean).join(' ');
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
title: string;
|
|
80
|
-
badge?: string;
|
|
81
|
-
summary?: string;
|
|
82
|
-
href?: string;
|
|
83
|
-
icon?: string;
|
|
84
|
-
leftIcon?: string;
|
|
85
|
-
}
|
|
94
|
+
const searchResultHighlightClassName =
|
|
95
|
+
'[&_mark]:bg-transparent [&_mark]:p-0 [&_mark]:font-semibold [&_mark]:text-[rgb(var(--ec-accent))]';
|
|
86
96
|
|
|
87
97
|
interface SearchNodeCompact {
|
|
88
98
|
k: string;
|
|
@@ -99,6 +109,12 @@ interface SearchIndexPayload {
|
|
|
99
109
|
items?: SearchNode[];
|
|
100
110
|
}
|
|
101
111
|
|
|
112
|
+
interface PagefindModule {
|
|
113
|
+
init: () => Promise<void>;
|
|
114
|
+
options?: (options: Record<string, unknown>) => Promise<void>;
|
|
115
|
+
debouncedSearch: (term: string) => Promise<{ results: Array<{ id: string; score?: number; data: () => Promise<any> }> } | null>;
|
|
116
|
+
}
|
|
117
|
+
|
|
102
118
|
const normalizeSearchIndexPayload = (payload: SearchIndexPayload): SearchNode[] => {
|
|
103
119
|
if (payload.i) {
|
|
104
120
|
return payload.i.map((item) => ({
|
|
@@ -115,11 +131,21 @@ const normalizeSearchIndexPayload = (payload: SearchIndexPayload): SearchNode[]
|
|
|
115
131
|
return payload.items || [];
|
|
116
132
|
};
|
|
117
133
|
|
|
134
|
+
const loadPagefindModule = (url: string) => {
|
|
135
|
+
const nativeImport = new Function('url', 'return import(url)') as (url: string) => Promise<PagefindModule>;
|
|
136
|
+
return nativeImport(url);
|
|
137
|
+
};
|
|
138
|
+
|
|
118
139
|
export default function SearchModal() {
|
|
140
|
+
const searchType = typeof __EC_SEARCH_TYPE__ !== 'undefined' ? __EC_SEARCH_TYPE__ : 'resource';
|
|
141
|
+
const isIndexedSearch = searchType === 'indexed';
|
|
119
142
|
const [query, setQuery] = useState('');
|
|
120
143
|
const [open, setOpen] = useState(false);
|
|
121
144
|
const [activeFilter, setActiveFilter] = useState('all');
|
|
122
145
|
const [searchNodes, setSearchNodes] = useState<SearchNode[]>([]);
|
|
146
|
+
const [indexedItems, setIndexedItems] = useState<SearchItem[]>([]);
|
|
147
|
+
const [pagefind, setPagefind] = useState<PagefindModule | null>(null);
|
|
148
|
+
const [isSearchingIndexed, setIsSearchingIndexed] = useState(false);
|
|
123
149
|
const [isLoadingSearchIndex, setIsLoadingSearchIndex] = useState(false);
|
|
124
150
|
const [searchIndexLoadError, setSearchIndexLoadError] = useState<string | null>(null);
|
|
125
151
|
const favorites = useStore(favoritesStore);
|
|
@@ -141,7 +167,7 @@ export default function SearchModal() {
|
|
|
141
167
|
}, []);
|
|
142
168
|
|
|
143
169
|
useEffect(() => {
|
|
144
|
-
if (!open || searchNodes.length > 0 || isLoadingSearchIndex) {
|
|
170
|
+
if (isIndexedSearch || !open || searchNodes.length > 0 || isLoadingSearchIndex) {
|
|
145
171
|
return;
|
|
146
172
|
}
|
|
147
173
|
|
|
@@ -166,7 +192,46 @@ export default function SearchModal() {
|
|
|
166
192
|
.finally(() => {
|
|
167
193
|
setIsLoadingSearchIndex(false);
|
|
168
194
|
});
|
|
169
|
-
}, [open, searchNodes.length, isLoadingSearchIndex]);
|
|
195
|
+
}, [isIndexedSearch, open, searchNodes.length, isLoadingSearchIndex]);
|
|
196
|
+
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (!isIndexedSearch || !open || pagefind || isLoadingSearchIndex) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
setIsLoadingSearchIndex(true);
|
|
203
|
+
setSearchIndexLoadError(null);
|
|
204
|
+
|
|
205
|
+
const pagefindUrl = buildUrl('/pagefind/pagefind.js', true);
|
|
206
|
+
|
|
207
|
+
loadPagefindModule(pagefindUrl)
|
|
208
|
+
.then(async (module: PagefindModule) => {
|
|
209
|
+
await module.options?.({
|
|
210
|
+
excerptLength: 30,
|
|
211
|
+
ranking: {
|
|
212
|
+
metaWeights: {
|
|
213
|
+
title: 5.0,
|
|
214
|
+
id: 4.0,
|
|
215
|
+
summary: 2.0,
|
|
216
|
+
type: 1.5,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
await module.init();
|
|
221
|
+
setPagefind(module);
|
|
222
|
+
})
|
|
223
|
+
.catch((error) => {
|
|
224
|
+
console.error(error);
|
|
225
|
+
setSearchIndexLoadError(
|
|
226
|
+
import.meta.env.DEV
|
|
227
|
+
? 'The local indexed search files could not be loaded. Restart the catalog dev server to rebuild the index.'
|
|
228
|
+
: 'Indexed search is enabled, but the generated search index could not be loaded. Run `eventcatalog build` to create it.'
|
|
229
|
+
);
|
|
230
|
+
})
|
|
231
|
+
.finally(() => {
|
|
232
|
+
setIsLoadingSearchIndex(false);
|
|
233
|
+
});
|
|
234
|
+
}, [isIndexedSearch, open, pagefind, isLoadingSearchIndex]);
|
|
170
235
|
|
|
171
236
|
const closeModal = () => {
|
|
172
237
|
if ((window as any).searchModalState) {
|
|
@@ -194,8 +259,55 @@ export default function SearchModal() {
|
|
|
194
259
|
.filter((item): item is NonNullable<typeof item> => item !== null);
|
|
195
260
|
}, [searchNodes]);
|
|
196
261
|
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
if (!isIndexedSearch || !pagefind || query.trim() === '') {
|
|
264
|
+
setIndexedItems([]);
|
|
265
|
+
setIsSearchingIndexed(false);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let cancelled = false;
|
|
270
|
+
setIsSearchingIndexed(true);
|
|
271
|
+
|
|
272
|
+
const timeout = window.setTimeout(async () => {
|
|
273
|
+
try {
|
|
274
|
+
const search = await pagefind.debouncedSearch(query);
|
|
275
|
+
if (cancelled || !search?.results) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const results = await mapPagefindResultsToSearchItems({
|
|
280
|
+
results: search.results,
|
|
281
|
+
query,
|
|
282
|
+
limit: INDEXED_RESULT_LOAD_LIMIT,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (!cancelled) {
|
|
286
|
+
setIndexedItems(results);
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (!cancelled) {
|
|
290
|
+
setSearchIndexLoadError(error instanceof Error ? error.message : 'Unable to search the indexed catalog');
|
|
291
|
+
}
|
|
292
|
+
} finally {
|
|
293
|
+
if (!cancelled) {
|
|
294
|
+
setIsSearchingIndexed(false);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}, 120);
|
|
298
|
+
|
|
299
|
+
return () => {
|
|
300
|
+
cancelled = true;
|
|
301
|
+
window.clearTimeout(timeout);
|
|
302
|
+
};
|
|
303
|
+
}, [isIndexedSearch, pagefind, query]);
|
|
304
|
+
|
|
197
305
|
// Get searchable items (items that match the query but not filtered by type yet)
|
|
198
306
|
const searchableItems = useMemo(() => {
|
|
307
|
+
if (isIndexedSearch) {
|
|
308
|
+
return indexedItems;
|
|
309
|
+
}
|
|
310
|
+
|
|
199
311
|
if (query === '') {
|
|
200
312
|
// When no query, show all items for filter counts
|
|
201
313
|
return items;
|
|
@@ -208,66 +320,20 @@ export default function SearchModal() {
|
|
|
208
320
|
// Match against the id so users can find resources by their raw id too.
|
|
209
321
|
const keyParts = item.key?.split(':') ?? [];
|
|
210
322
|
const id = keyParts[1];
|
|
211
|
-
|
|
323
|
+
if (id?.toLowerCase().includes(lowerQuery)) return true;
|
|
324
|
+
return !!item.rawNode.summary && item.rawNode.summary.toLowerCase().includes(lowerQuery);
|
|
212
325
|
});
|
|
213
|
-
}, [items, query]);
|
|
326
|
+
}, [indexedItems, isIndexedSearch, items, query]);
|
|
214
327
|
|
|
215
328
|
const filters = useMemo(() => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return [{ id: 'all', name: 'All (0)' }];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const itemsToCount = query === '' ? items : searchableItems;
|
|
223
|
-
|
|
224
|
-
const counts: Record<string, number> = {
|
|
225
|
-
all: itemsToCount.length,
|
|
226
|
-
Domain: 0,
|
|
227
|
-
Service: 0,
|
|
228
|
-
Message: 0,
|
|
229
|
-
Team: 0,
|
|
230
|
-
Container: 0,
|
|
231
|
-
Entity: 0,
|
|
232
|
-
Design: 0,
|
|
233
|
-
Channel: 0,
|
|
234
|
-
Flow: 0,
|
|
235
|
-
'Data Product': 0,
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
itemsToCount.forEach((item) => {
|
|
239
|
-
// Count specific types
|
|
240
|
-
if (counts[item.type] !== undefined) {
|
|
241
|
-
counts[item.type]++;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Group counts
|
|
245
|
-
if (['Event', 'Command', 'Query'].includes(item.type)) {
|
|
246
|
-
counts.Message++;
|
|
247
|
-
}
|
|
248
|
-
if (['Team', 'User'].includes(item.type)) {
|
|
249
|
-
counts.Team++;
|
|
250
|
-
}
|
|
329
|
+
return getSearchFilters({
|
|
330
|
+
items: query === '' ? items : searchableItems,
|
|
331
|
+
query,
|
|
251
332
|
});
|
|
252
|
-
|
|
253
|
-
const dynamicFilters = [{ id: 'all', name: `All (${counts.all})` }];
|
|
254
|
-
|
|
255
|
-
// Only show filters that have results when searching
|
|
256
|
-
if (counts.Domain > 0) dynamicFilters.push({ id: 'Domain', name: `Domains (${counts.Domain})` });
|
|
257
|
-
if (counts.Service > 0) dynamicFilters.push({ id: 'Service', name: `Services (${counts.Service})` });
|
|
258
|
-
if (counts.Message > 0) dynamicFilters.push({ id: 'Message', name: `Messages (${counts.Message})` });
|
|
259
|
-
if (counts.Container > 0) dynamicFilters.push({ id: 'Container', name: `Data Stores (${counts.Container})` });
|
|
260
|
-
if (counts.Entity > 0) dynamicFilters.push({ id: 'Entity', name: `Entities (${counts.Entity})` });
|
|
261
|
-
if (counts.Channel > 0) dynamicFilters.push({ id: 'Channel', name: `Channels (${counts.Channel})` });
|
|
262
|
-
if (counts.Flow > 0) dynamicFilters.push({ id: 'Flow', name: `Flows (${counts.Flow})` });
|
|
263
|
-
if (counts['Data Product'] > 0)
|
|
264
|
-
dynamicFilters.push({ id: 'Data Product', name: `Data Products (${counts['Data Product']})` });
|
|
265
|
-
if (counts.Design > 0) dynamicFilters.push({ id: 'Design', name: `Designs (${counts.Design})` });
|
|
266
|
-
if (counts.Team > 0) dynamicFilters.push({ id: 'Team', name: `Teams & Users (${counts.Team})` });
|
|
267
|
-
|
|
268
|
-
return dynamicFilters;
|
|
269
333
|
}, [searchableItems, items, query]);
|
|
270
334
|
|
|
335
|
+
const showFilterTabs = filters.some((filter) => filter.count > 0);
|
|
336
|
+
|
|
271
337
|
// Reset active filter if it no longer has results
|
|
272
338
|
useEffect(() => {
|
|
273
339
|
if (activeFilter !== 'all' && !filters.some((f) => f.id === activeFilter)) {
|
|
@@ -279,6 +345,8 @@ export default function SearchModal() {
|
|
|
279
345
|
e.preventDefault();
|
|
280
346
|
e.stopPropagation();
|
|
281
347
|
|
|
348
|
+
if (!item.key) return;
|
|
349
|
+
|
|
282
350
|
toggleFavoriteAction({
|
|
283
351
|
nodeKey: item.key,
|
|
284
352
|
path: [], // Path is not easily available here, but Sidebar rebuilds it
|
|
@@ -293,6 +361,14 @@ export default function SearchModal() {
|
|
|
293
361
|
}, [searchNodes]);
|
|
294
362
|
|
|
295
363
|
const filteredItems = useMemo(() => {
|
|
364
|
+
if (isIndexedSearch) {
|
|
365
|
+
if (query === '') {
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return applyActiveFilter(searchableItems, activeFilter).slice(0, SEARCH_RESULT_DISPLAY_LIMIT);
|
|
370
|
+
}
|
|
371
|
+
|
|
296
372
|
if (query === '') {
|
|
297
373
|
// Show favorites when search is empty
|
|
298
374
|
if (favorites.length > 0 && activeFilter === 'all') {
|
|
@@ -318,22 +394,8 @@ export default function SearchModal() {
|
|
|
318
394
|
return [];
|
|
319
395
|
}
|
|
320
396
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// Apply type filter
|
|
325
|
-
if (activeFilter !== 'all') {
|
|
326
|
-
if (activeFilter === 'Message') {
|
|
327
|
-
result = result.filter((item) => ['Event', 'Command', 'Query'].includes(item.type));
|
|
328
|
-
} else if (activeFilter === 'Team') {
|
|
329
|
-
result = result.filter((item) => ['Team', 'User'].includes(item.type));
|
|
330
|
-
} else {
|
|
331
|
-
result = result.filter((item) => item.type === activeFilter);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return result.slice(0, 50); // Limit results for performance
|
|
336
|
-
}, [searchableItems, query, activeFilter, favorites, searchNodeLookup]);
|
|
397
|
+
return applyActiveFilter(searchableItems, activeFilter).slice(0, SEARCH_RESULT_DISPLAY_LIMIT);
|
|
398
|
+
}, [isIndexedSearch, searchableItems, query, activeFilter, favorites, searchNodeLookup]);
|
|
337
399
|
|
|
338
400
|
return (
|
|
339
401
|
<Transition.Root
|
|
@@ -384,58 +446,74 @@ export default function SearchModal() {
|
|
|
384
446
|
/>
|
|
385
447
|
<Combobox.Input
|
|
386
448
|
ref={inputRef}
|
|
387
|
-
className=
|
|
449
|
+
className={classNames(
|
|
450
|
+
'h-12 w-full border-0 bg-transparent pl-11 text-[rgb(var(--ec-page-text))] placeholder:text-[rgb(var(--ec-icon-color))] focus:ring-0 sm:text-sm focus:outline-hidden',
|
|
451
|
+
query.trim() !== '' && filteredItems.length > 0 ? 'pr-20' : 'pr-4'
|
|
452
|
+
)}
|
|
388
453
|
placeholder="Search..."
|
|
389
454
|
onChange={(event) => setQuery(event.target.value)}
|
|
390
455
|
value={query}
|
|
391
456
|
autoFocus
|
|
392
457
|
autoComplete="off"
|
|
393
458
|
/>
|
|
459
|
+
{query.trim() !== '' && filteredItems.length > 0 && (
|
|
460
|
+
<kbd className="pointer-events-none absolute right-4 top-2.5 rounded-lg bg-[rgb(var(--ec-content-hover))] px-2.5 py-1 text-xs font-semibold text-[rgb(var(--ec-page-text-muted))] ring-1 ring-inset ring-[rgb(var(--ec-page-border))]">
|
|
461
|
+
ESC
|
|
462
|
+
</kbd>
|
|
463
|
+
)}
|
|
394
464
|
</div>
|
|
395
465
|
|
|
396
466
|
{/* Filter Tabs */}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
467
|
+
{showFilterTabs && (
|
|
468
|
+
<div
|
|
469
|
+
className="flex items-center gap-2 px-4 pt-3 pb-3.5 overflow-x-auto overscroll-x-contain border-b border-[rgb(var(--ec-page-border))]"
|
|
470
|
+
style={{
|
|
471
|
+
scrollbarWidth: 'thin',
|
|
472
|
+
scrollbarColor: 'rgb(var(--ec-page-border)) transparent',
|
|
473
|
+
}}
|
|
474
|
+
>
|
|
475
|
+
{filters.map((tab) => (
|
|
476
|
+
<button
|
|
477
|
+
key={tab.id}
|
|
478
|
+
onClick={() => setActiveFilter(tab.id)}
|
|
479
|
+
className={classNames(
|
|
480
|
+
'px-3 py-1 text-xs font-medium rounded-full transition-colors whitespace-nowrap',
|
|
481
|
+
activeFilter === tab.id
|
|
482
|
+
? 'bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent-text))]'
|
|
483
|
+
: 'bg-[rgb(var(--ec-content-hover))] text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-content-active))]'
|
|
484
|
+
)}
|
|
485
|
+
>
|
|
486
|
+
{tab.name}
|
|
487
|
+
</button>
|
|
488
|
+
))}
|
|
489
|
+
</div>
|
|
490
|
+
)}
|
|
419
491
|
|
|
420
492
|
{isLoadingSearchIndex && (
|
|
421
493
|
<div className="py-10 px-6 text-center text-sm sm:px-14">
|
|
422
494
|
<MagnifyingGlassIcon className="mx-auto h-6 w-6 text-[rgb(var(--ec-icon-color))] animate-pulse" />
|
|
423
495
|
<p className="mt-4 font-semibold text-[rgb(var(--ec-page-text))]">Loading search index…</p>
|
|
424
|
-
<p className="mt-2 text-[rgb(var(--ec-page-text-muted))]">
|
|
496
|
+
<p className="mt-2 text-[rgb(var(--ec-page-text-muted))]">
|
|
497
|
+
{isIndexedSearch ? 'Preparing indexed search.' : 'Preparing resources for search.'}
|
|
498
|
+
</p>
|
|
425
499
|
</div>
|
|
426
500
|
)}
|
|
427
501
|
|
|
428
502
|
{searchIndexLoadError && !isLoadingSearchIndex && (
|
|
429
|
-
<div className="
|
|
430
|
-
<
|
|
431
|
-
|
|
432
|
-
|
|
503
|
+
<div className="px-6 py-12 text-center text-sm sm:px-14">
|
|
504
|
+
<div className="mx-auto flex h-10 w-10 items-center justify-center rounded-lg bg-[rgb(var(--ec-content-hover))] text-[rgb(var(--ec-icon-color))] ring-1 ring-inset ring-[rgb(var(--ec-page-border))]">
|
|
505
|
+
<ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />
|
|
506
|
+
</div>
|
|
507
|
+
<p className="mt-4 font-semibold text-[rgb(var(--ec-page-text))]">
|
|
508
|
+
{isIndexedSearch ? 'Indexed search is not ready' : 'Search is not ready'}
|
|
509
|
+
</p>
|
|
510
|
+
<p className="mx-auto mt-2 max-w-sm text-[rgb(var(--ec-page-text-muted))]">{searchIndexLoadError}</p>
|
|
433
511
|
</div>
|
|
434
512
|
)}
|
|
435
513
|
|
|
436
514
|
{!isLoadingSearchIndex && !searchIndexLoadError && filteredItems.length > 0 && (
|
|
437
515
|
<>
|
|
438
|
-
{query === '' && favorites.length > 0 && (
|
|
516
|
+
{!isIndexedSearch && query === '' && favorites.length > 0 && (
|
|
439
517
|
<div className="px-6 pt-3 pb-2">
|
|
440
518
|
<p className="text-xs text-[rgb(var(--ec-page-text-muted))]">Favourites</p>
|
|
441
519
|
</div>
|
|
@@ -445,7 +523,7 @@ export default function SearchModal() {
|
|
|
445
523
|
const Icon = typeIcons[item.type] || typeIcons.default;
|
|
446
524
|
const colors = typeColors[item.type] || typeColors.default;
|
|
447
525
|
|
|
448
|
-
const isFavorite = favorites.some((fav) => fav.nodeKey === item.key);
|
|
526
|
+
const isFavorite = !!item.key && favorites.some((fav) => fav.nodeKey === item.key);
|
|
449
527
|
|
|
450
528
|
return (
|
|
451
529
|
<Combobox.Option
|
|
@@ -481,48 +559,60 @@ export default function SearchModal() {
|
|
|
481
559
|
<p
|
|
482
560
|
className={classNames(
|
|
483
561
|
'text-sm font-medium',
|
|
562
|
+
searchResultHighlightClassName,
|
|
484
563
|
active ? 'text-[rgb(var(--ec-page-text))]' : 'text-[rgb(var(--ec-page-text))]'
|
|
485
564
|
)}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
</p>
|
|
565
|
+
dangerouslySetInnerHTML={{ __html: highlightQuery(item.name, query) }}
|
|
566
|
+
/>
|
|
489
567
|
<div className="flex items-center gap-2">
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
568
|
+
{!item.rawNode.matchedExcerpt && (
|
|
569
|
+
<p
|
|
570
|
+
className={classNames(
|
|
571
|
+
'text-xs flex-shrink-0',
|
|
572
|
+
active ? 'text-[rgb(var(--ec-page-text))]' : 'text-[rgb(var(--ec-page-text-muted))]'
|
|
573
|
+
)}
|
|
574
|
+
>
|
|
575
|
+
{item.type}
|
|
576
|
+
</p>
|
|
577
|
+
)}
|
|
498
578
|
{item.rawNode.summary && (
|
|
499
579
|
<p
|
|
500
580
|
className={classNames(
|
|
501
|
-
'text-
|
|
581
|
+
'text-xs truncate',
|
|
502
582
|
active ? 'text-[rgb(var(--ec-page-text-muted))]' : 'text-[rgb(var(--ec-icon-color))]'
|
|
503
583
|
)}
|
|
504
584
|
>
|
|
505
|
-
|
|
585
|
+
{!item.rawNode.matchedExcerpt && '• '}
|
|
586
|
+
{item.rawNode.matchedExcerpt ? (
|
|
587
|
+
<span
|
|
588
|
+
className={searchResultHighlightClassName}
|
|
589
|
+
dangerouslySetInnerHTML={{ __html: item.rawNode.matchedExcerpt }}
|
|
590
|
+
/>
|
|
591
|
+
) : (
|
|
592
|
+
item.rawNode.summary
|
|
593
|
+
)}
|
|
506
594
|
</p>
|
|
507
595
|
)}
|
|
508
596
|
</div>
|
|
509
597
|
</div>
|
|
510
598
|
<div className="flex items-center">
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
e
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
599
|
+
{!!item.key && (
|
|
600
|
+
<button
|
|
601
|
+
onClick={(e) => handleToggleFavorite(e, item)}
|
|
602
|
+
onMouseDown={(e) => {
|
|
603
|
+
e.preventDefault();
|
|
604
|
+
e.stopPropagation();
|
|
605
|
+
}}
|
|
606
|
+
className={classNames(
|
|
607
|
+
'p-1 rounded-md transition-colors mr-2',
|
|
608
|
+
isFavorite
|
|
609
|
+
? 'text-amber-400 hover:text-amber-500'
|
|
610
|
+
: 'text-[rgb(var(--ec-icon-color))] opacity-0 group-hover:opacity-100 hover:text-amber-400'
|
|
611
|
+
)}
|
|
612
|
+
>
|
|
613
|
+
{isFavorite ? <StarIconSolid className="h-5 w-5" /> : <StarIcon className="h-5 w-5" />}
|
|
614
|
+
</button>
|
|
615
|
+
)}
|
|
526
616
|
{active && (
|
|
527
617
|
<ArrowRightIcon className="h-5 w-5 text-[rgb(var(--ec-icon-color))]" aria-hidden="true" />
|
|
528
618
|
)}
|
|
@@ -538,14 +628,22 @@ export default function SearchModal() {
|
|
|
538
628
|
|
|
539
629
|
{!isLoadingSearchIndex && !searchIndexLoadError && query !== '' && filteredItems.length === 0 && (
|
|
540
630
|
<div className="py-14 px-6 text-center text-sm sm:px-14">
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
631
|
+
{isSearchingIndexed ? (
|
|
632
|
+
<MagnifyingGlassIcon className="mx-auto h-6 w-6 text-[rgb(var(--ec-icon-color))] animate-pulse" />
|
|
633
|
+
) : (
|
|
634
|
+
<ExclamationCircleIcon
|
|
635
|
+
type="outline"
|
|
636
|
+
name="exclamation-circle"
|
|
637
|
+
className="mx-auto h-6 w-6 text-[rgb(var(--ec-icon-color))]"
|
|
638
|
+
/>
|
|
639
|
+
)}
|
|
640
|
+
<p className="mt-4 font-semibold text-[rgb(var(--ec-page-text))]">
|
|
641
|
+
{isSearchingIndexed ? 'Searching…' : 'No results found'}
|
|
642
|
+
</p>
|
|
547
643
|
<p className="mt-2 text-[rgb(var(--ec-page-text-muted))]">
|
|
548
|
-
|
|
644
|
+
{isSearchingIndexed
|
|
645
|
+
? 'Searching the indexed catalog.'
|
|
646
|
+
: 'No components found for this search term. Please try again.'}
|
|
549
647
|
</p>
|
|
550
648
|
</div>
|
|
551
649
|
)}
|
|
@@ -555,7 +653,9 @@ export default function SearchModal() {
|
|
|
555
653
|
<MagnifyingGlassIcon className="mx-auto h-6 w-6 text-[rgb(var(--ec-icon-color))]" />
|
|
556
654
|
<p className="mt-4 font-semibold text-[rgb(var(--ec-page-text))]">Search for anything</p>
|
|
557
655
|
<p className="mt-2 text-[rgb(var(--ec-page-text-muted))]">
|
|
558
|
-
|
|
656
|
+
{isIndexedSearch
|
|
657
|
+
? 'Search indexed catalog content, custom docs, resources and more.'
|
|
658
|
+
: 'Search for domains, services, events, commands, queries, data stores, data products, flows and more.'}
|
|
559
659
|
</p>
|
|
560
660
|
</div>
|
|
561
661
|
)}
|