@eventcatalog/core 2.63.0 → 2.64.1
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-GA274FBN.js → chunk-AHJ4UE33.js} +1 -1
- package/dist/{chunk-IRFM5IS7.js → chunk-LCBQ5JUR.js} +1 -1
- package/dist/{chunk-I2FMV7LN.js → chunk-SH6FZS4K.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +3 -3
- package/eventcatalog/astro.config.mjs +2 -1
- package/eventcatalog/public/icons/avro.svg +21 -0
- package/eventcatalog/public/icons/json-schema.svg +6 -0
- package/eventcatalog/public/icons/proto.svg +10 -0
- package/eventcatalog/src/components/Grids/utils.tsx +5 -3
- package/eventcatalog/src/components/MDX/RemoteFile.astro +5 -11
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewerRoot.astro +41 -6
- package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +139 -0
- package/eventcatalog/src/components/SchemaExplorer/AvroSchemaViewer.tsx +465 -0
- package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +102 -0
- package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.tsx +740 -0
- package/eventcatalog/src/components/SchemaExplorer/OwnersSection.tsx +56 -0
- package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +33 -0
- package/eventcatalog/src/components/SchemaExplorer/ProducersConsumersSection.tsx +91 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +93 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +132 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsHeader.tsx +181 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +233 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +415 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaFilters.tsx +174 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +73 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaViewerModal.tsx +77 -0
- package/eventcatalog/src/components/SchemaExplorer/VersionHistoryModal.tsx +72 -0
- package/eventcatalog/src/components/SchemaExplorer/types.ts +45 -0
- package/eventcatalog/src/components/SchemaExplorer/utils.ts +81 -0
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +33 -2
- package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +2 -2
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +10 -0
- package/eventcatalog/src/pages/api/schemas/[collection]/[id]/[version]/index.ts +45 -0
- package/eventcatalog/src/pages/api/schemas/services/[id]/[version]/[specification]/index.ts +51 -0
- package/eventcatalog/src/pages/docs/llm/schemas.txt.ts +86 -0
- package/eventcatalog/src/pages/schemas/index.astro +175 -0
- package/eventcatalog/src/utils/files.ts +9 -0
- package/package.json +1 -1
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaProperty.astro +0 -204
- package/eventcatalog/src/components/MDX/SchemaViewer/SchemaViewer.astro +0 -705
|
@@ -1,705 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// src/components/SchemaViewer.astro
|
|
3
|
-
import SchemaProperty from './SchemaProperty.astro';
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
schema: Record<string, any>;
|
|
7
|
-
schemaPath: string;
|
|
8
|
-
title: string;
|
|
9
|
-
maxHeight: string;
|
|
10
|
-
file: string;
|
|
11
|
-
id: string;
|
|
12
|
-
expand?: boolean | string;
|
|
13
|
-
search?: boolean | string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const { id, file, title, maxHeight, schema, expand = false, search = true } = Astro.props;
|
|
17
|
-
|
|
18
|
-
// Convert string props to booleans (MDX passes strings)
|
|
19
|
-
const expandBool = expand === true || expand === 'true';
|
|
20
|
-
const searchBool = search !== false && search !== 'false';
|
|
21
|
-
|
|
22
|
-
// This will be used to pass expand state via data attribute
|
|
23
|
-
const expandData = expandBool ? 'true' : 'false';
|
|
24
|
-
const searchData = searchBool ? 'true' : 'false';
|
|
25
|
-
|
|
26
|
-
// Count total properties recursively
|
|
27
|
-
function countProperties(obj: any): number {
|
|
28
|
-
if (!obj || typeof obj !== 'object') return 0;
|
|
29
|
-
|
|
30
|
-
let count = 0;
|
|
31
|
-
if (obj.properties) {
|
|
32
|
-
count += Object.keys(obj.properties).length;
|
|
33
|
-
Object.values(obj.properties).forEach((prop: any) => {
|
|
34
|
-
count += countProperties(prop);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (obj.items) {
|
|
38
|
-
count += countProperties(obj.items);
|
|
39
|
-
}
|
|
40
|
-
// Handle root array items that have been processed
|
|
41
|
-
if (obj._isRootArrayItem && obj._rootArraySchema?.items) {
|
|
42
|
-
// Don't double count, we're already counting the properties above
|
|
43
|
-
}
|
|
44
|
-
return count;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Function to merge allOf schemas
|
|
48
|
-
function mergeAllOfSchemas(schemaWithProcessor: any): any {
|
|
49
|
-
const { processSchema: processor, ...schema } = schemaWithProcessor;
|
|
50
|
-
if (!schema.allOf) return schema;
|
|
51
|
-
|
|
52
|
-
const mergedSchema: {
|
|
53
|
-
type: string;
|
|
54
|
-
properties: Record<string, any>;
|
|
55
|
-
required: string[];
|
|
56
|
-
description?: string;
|
|
57
|
-
[key: string]: any;
|
|
58
|
-
} = {
|
|
59
|
-
type: schema.type || 'object',
|
|
60
|
-
properties: {},
|
|
61
|
-
required: [],
|
|
62
|
-
description: schema.description,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Copy base schema properties first (excluding allOf)
|
|
66
|
-
Object.keys(schema).forEach((key) => {
|
|
67
|
-
if (key !== 'allOf' && key !== 'properties' && key !== 'required' && key !== 'description') {
|
|
68
|
-
mergedSchema[key] = schema[key];
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Copy base properties if they exist
|
|
73
|
-
if (schema.properties) {
|
|
74
|
-
mergedSchema.properties = { ...schema.properties };
|
|
75
|
-
}
|
|
76
|
-
if (schema.required) {
|
|
77
|
-
mergedSchema.required = [...schema.required];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
schema.allOf.forEach((subSchema: any) => {
|
|
81
|
-
// Recursively process each subSchema in case it has its own allOf, oneOf, or $ref
|
|
82
|
-
const processedSubSchema = processor ? processor(subSchema) : subSchema;
|
|
83
|
-
|
|
84
|
-
if (processedSubSchema.properties) {
|
|
85
|
-
mergedSchema.properties = {
|
|
86
|
-
...mergedSchema.properties,
|
|
87
|
-
...processedSubSchema.properties,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
if (processedSubSchema.required) {
|
|
91
|
-
mergedSchema.required = [...new Set([...mergedSchema.required, ...processedSubSchema.required])];
|
|
92
|
-
}
|
|
93
|
-
if (processedSubSchema.description && !mergedSchema.description) {
|
|
94
|
-
mergedSchema.description = processedSubSchema.description;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Copy other properties from subSchema
|
|
98
|
-
Object.keys(processedSubSchema).forEach((key) => {
|
|
99
|
-
if (key !== 'properties' && key !== 'required' && key !== 'description' && key !== 'type') {
|
|
100
|
-
if (!mergedSchema[key]) {
|
|
101
|
-
mergedSchema[key] = processedSubSchema[key];
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
return mergedSchema;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function processSchema(schema: any, rootSchema?: any): any {
|
|
111
|
-
if (!schema) return schema;
|
|
112
|
-
|
|
113
|
-
// Set rootSchema for $ref resolution
|
|
114
|
-
const root = rootSchema || schema;
|
|
115
|
-
|
|
116
|
-
// Handle $ref
|
|
117
|
-
if (schema.$ref) {
|
|
118
|
-
const refPath = schema.$ref;
|
|
119
|
-
let resolvedSchema = null;
|
|
120
|
-
let defName = '';
|
|
121
|
-
|
|
122
|
-
// Try draft-7 style first: #/definitions/
|
|
123
|
-
if (refPath.startsWith('#/definitions/')) {
|
|
124
|
-
defName = refPath.replace('#/definitions/', '');
|
|
125
|
-
if (root.definitions && root.definitions[defName]) {
|
|
126
|
-
resolvedSchema = root.definitions[defName];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// Try 2020-12 style: #/$defs/
|
|
130
|
-
else if (refPath.startsWith('#/$defs/')) {
|
|
131
|
-
defName = refPath.replace('#/$defs/', '');
|
|
132
|
-
if (root.$defs && root.$defs[defName]) {
|
|
133
|
-
resolvedSchema = root.$defs[defName];
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Try other common patterns
|
|
137
|
-
else if (refPath.startsWith('#/components/schemas/')) {
|
|
138
|
-
defName = refPath.replace('#/components/schemas/', '');
|
|
139
|
-
if (root.components && root.components.schemas && root.components.schemas[defName]) {
|
|
140
|
-
resolvedSchema = root.components.schemas[defName];
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (resolvedSchema) {
|
|
145
|
-
// Recursively process the referenced schema
|
|
146
|
-
const processedSchema = processSchema(resolvedSchema, root);
|
|
147
|
-
// Add reference info to the resolved schema
|
|
148
|
-
return {
|
|
149
|
-
...processedSchema,
|
|
150
|
-
_refPath: refPath,
|
|
151
|
-
_refName: defName,
|
|
152
|
-
_originalRef: schema.$ref,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// If not found, create a placeholder schema showing the reference
|
|
157
|
-
return {
|
|
158
|
-
type: 'string',
|
|
159
|
-
description: `Reference to ${refPath} (definition not found in root schema)`,
|
|
160
|
-
title: defName || refPath.split('/').pop(),
|
|
161
|
-
_refPath: refPath,
|
|
162
|
-
_refName: defName,
|
|
163
|
-
_refNotFound: true,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (schema.allOf) {
|
|
168
|
-
return mergeAllOfSchemas({ ...schema, processSchema: (s: any) => processSchema(s, root) });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (schema.oneOf) {
|
|
172
|
-
// Process each oneOf variant and create a combined schema
|
|
173
|
-
const processedVariants = schema.oneOf.map((variant: any) => {
|
|
174
|
-
const processedVariant = processSchema(variant, root);
|
|
175
|
-
return {
|
|
176
|
-
title: processedVariant.title || variant.title || 'Unnamed Variant',
|
|
177
|
-
required: processedVariant.required || variant.required || [],
|
|
178
|
-
properties: processedVariant.properties || {},
|
|
179
|
-
...processedVariant,
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// Merge all properties from variants for display
|
|
184
|
-
const allProperties: Record<string, any> = {};
|
|
185
|
-
processedVariants.forEach((variant: any) => {
|
|
186
|
-
if (variant.properties) {
|
|
187
|
-
Object.assign(allProperties, variant.properties);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
...schema,
|
|
193
|
-
type: schema.type || 'object',
|
|
194
|
-
properties: {
|
|
195
|
-
...(schema.properties || {}),
|
|
196
|
-
...allProperties,
|
|
197
|
-
},
|
|
198
|
-
variants: processedVariants,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Process nested schemas in properties
|
|
203
|
-
if (schema.properties) {
|
|
204
|
-
const processedProperties: Record<string, any> = {};
|
|
205
|
-
Object.entries(schema.properties).forEach(([key, prop]: [string, any]) => {
|
|
206
|
-
processedProperties[key] = processSchema(prop, root);
|
|
207
|
-
});
|
|
208
|
-
schema = { ...schema, properties: processedProperties };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Process array items
|
|
212
|
-
if (schema.type === 'array' && schema.items) {
|
|
213
|
-
schema = { ...schema, items: processSchema(schema.items, root) };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return schema;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const processedSchema = processSchema(schema);
|
|
220
|
-
|
|
221
|
-
// Handle root-level array schemas
|
|
222
|
-
let displaySchema = processedSchema;
|
|
223
|
-
let isRootArray = false;
|
|
224
|
-
|
|
225
|
-
if (processedSchema.type === 'array' && processedSchema.items) {
|
|
226
|
-
isRootArray = true;
|
|
227
|
-
// For root arrays, we want to display the items' properties
|
|
228
|
-
if (processedSchema.items.type === 'object' && processedSchema.items.properties) {
|
|
229
|
-
displaySchema = {
|
|
230
|
-
...processedSchema.items,
|
|
231
|
-
description: processedSchema.description || processedSchema.items.description,
|
|
232
|
-
_isRootArrayItem: true,
|
|
233
|
-
_rootArraySchema: processedSchema,
|
|
234
|
-
};
|
|
235
|
-
} else if (processedSchema.items.allOf || processedSchema.items.oneOf || processedSchema.items.$ref) {
|
|
236
|
-
// Process complex array item schemas
|
|
237
|
-
displaySchema = {
|
|
238
|
-
...processSchema(processedSchema.items),
|
|
239
|
-
description: processedSchema.description || processedSchema.items.description,
|
|
240
|
-
_isRootArrayItem: true,
|
|
241
|
-
_rootArraySchema: processedSchema,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const { description, properties, required = [], variants } = displaySchema;
|
|
247
|
-
|
|
248
|
-
// Count total properties after processing
|
|
249
|
-
const totalProperties = countProperties(displaySchema);
|
|
250
|
-
|
|
251
|
-
// Generate a unique ID for this instance
|
|
252
|
-
const instanceId = `${id}-${Math.random().toString(36).substring(2, 9)}`;
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
<div id={id} class="not-prose my-4" data-expand={expandData} data-search={searchData}>
|
|
256
|
-
{title && <h2 class="text-3xl font-bold mb-2 !mt-0">{title}</h2>}
|
|
257
|
-
<div
|
|
258
|
-
class="schema-viewer p-4 pt-0 bg-white overflow-y-auto rounded-lg border border-gray-100 shadow-sm font-sans"
|
|
259
|
-
style={{
|
|
260
|
-
maxHeight: maxHeight ? `${maxHeight}px` : `30em`,
|
|
261
|
-
minHeight: '15em',
|
|
262
|
-
}}
|
|
263
|
-
>
|
|
264
|
-
{
|
|
265
|
-
searchBool && (
|
|
266
|
-
<div
|
|
267
|
-
class="schema-toolbar sticky top-0 z-10 bg-white pt-4 px-4 -mx-4 mb-4 pb-3 border-b border-gray-100 shadow-sm"
|
|
268
|
-
id={`${instanceId}-toolbar`}
|
|
269
|
-
>
|
|
270
|
-
<div class="flex flex-col sm:flex-row gap-3">
|
|
271
|
-
<div class="flex-1 relative">
|
|
272
|
-
<input
|
|
273
|
-
type="text"
|
|
274
|
-
id={`${instanceId}-search`}
|
|
275
|
-
placeholder="Search properties..."
|
|
276
|
-
class="w-full px-3 py-1.5 pr-20 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
277
|
-
/>
|
|
278
|
-
<div
|
|
279
|
-
class="absolute right-2 top-1/2 transform -translate-y-1/2 flex items-center gap-1"
|
|
280
|
-
id={`${instanceId}-nav-buttons`}
|
|
281
|
-
>
|
|
282
|
-
<button
|
|
283
|
-
id={`${instanceId}-prev-match`}
|
|
284
|
-
class="p-1 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed"
|
|
285
|
-
disabled
|
|
286
|
-
title="Previous match"
|
|
287
|
-
>
|
|
288
|
-
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
289
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
290
|
-
</svg>
|
|
291
|
-
</button>
|
|
292
|
-
<button
|
|
293
|
-
id={`${instanceId}-next-match`}
|
|
294
|
-
class="p-1 text-gray-400 hover:text-gray-600 disabled:opacity-30 disabled:cursor-not-allowed"
|
|
295
|
-
disabled
|
|
296
|
-
title="Next match"
|
|
297
|
-
>
|
|
298
|
-
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
299
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
300
|
-
</svg>
|
|
301
|
-
</button>
|
|
302
|
-
</div>
|
|
303
|
-
</div>
|
|
304
|
-
<div class="flex items-center gap-2">
|
|
305
|
-
<button
|
|
306
|
-
id={`${instanceId}-expand-all`}
|
|
307
|
-
class="px-3 py-1.5 text-xs font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
308
|
-
>
|
|
309
|
-
Expand All
|
|
310
|
-
</button>
|
|
311
|
-
<button
|
|
312
|
-
id={`${instanceId}-collapse-all`}
|
|
313
|
-
class="px-3 py-1.5 text-xs font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
314
|
-
>
|
|
315
|
-
Collapse All
|
|
316
|
-
</button>
|
|
317
|
-
<div class="text-xs text-gray-500">
|
|
318
|
-
{totalProperties} {totalProperties === 1 ? 'property' : 'properties'}
|
|
319
|
-
</div>
|
|
320
|
-
</div>
|
|
321
|
-
</div>
|
|
322
|
-
<div id={`${instanceId}-search-results`} class="mt-2 text-xs text-gray-600 hidden" />
|
|
323
|
-
</div>
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
{
|
|
327
|
-
isRootArray && (
|
|
328
|
-
<div class="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-md">
|
|
329
|
-
<div class="flex items-center space-x-2">
|
|
330
|
-
<span class="text-blue-600 font-medium text-sm">Array Schema</span>
|
|
331
|
-
<span class="text-blue-500 font-mono text-xs">array[object]</span>
|
|
332
|
-
</div>
|
|
333
|
-
<p class="text-blue-700 text-xs mt-1">
|
|
334
|
-
This schema defines an array of objects. Each item in the array has the properties shown below.
|
|
335
|
-
</p>
|
|
336
|
-
</div>
|
|
337
|
-
)
|
|
338
|
-
}
|
|
339
|
-
{description && <p class="text-gray-600 text-xs mb-5">{description}</p>}
|
|
340
|
-
|
|
341
|
-
{
|
|
342
|
-
variants && (
|
|
343
|
-
<div class="mb-4">
|
|
344
|
-
<div class="flex items-center space-x-2">
|
|
345
|
-
<span class="text-sm text-gray-600">(one of)</span>
|
|
346
|
-
<select
|
|
347
|
-
id={`${instanceId}-variant-selector`}
|
|
348
|
-
class="form-select text-sm border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
|
|
349
|
-
>
|
|
350
|
-
{variants.map((variant: any, index: number) => (
|
|
351
|
-
<option value={index}>{variant.title}</option>
|
|
352
|
-
))}
|
|
353
|
-
</select>
|
|
354
|
-
</div>
|
|
355
|
-
</div>
|
|
356
|
-
)
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
{
|
|
360
|
-
properties && (
|
|
361
|
-
<div>
|
|
362
|
-
<div id={`${instanceId}-properties`}>
|
|
363
|
-
{Object.entries(properties).map(([name, details]) => (
|
|
364
|
-
<SchemaProperty
|
|
365
|
-
name={name}
|
|
366
|
-
details={details}
|
|
367
|
-
isRequired={variants ? false : required.includes(name)}
|
|
368
|
-
level={0}
|
|
369
|
-
expand={expandBool}
|
|
370
|
-
/>
|
|
371
|
-
))}
|
|
372
|
-
</div>
|
|
373
|
-
<div id={`${instanceId}-no-results`} class="hidden text-center py-8">
|
|
374
|
-
<div class="text-gray-400 text-sm">
|
|
375
|
-
<svg class="mx-auto h-12 w-12 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
376
|
-
<path
|
|
377
|
-
stroke-linecap="round"
|
|
378
|
-
stroke-linejoin="round"
|
|
379
|
-
stroke-width="2"
|
|
380
|
-
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
381
|
-
/>
|
|
382
|
-
</svg>
|
|
383
|
-
<p>No properties match your search</p>
|
|
384
|
-
<p class="text-xs mt-1">Try a different search term or clear the search to see all properties</p>
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
387
|
-
</div>
|
|
388
|
-
)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
{!properties && !isRootArray && <p class="text-gray-500 text-sm">Schema does not contain any properties.</p>}
|
|
392
|
-
{
|
|
393
|
-
!properties && isRootArray && (
|
|
394
|
-
<div class="text-center py-8">
|
|
395
|
-
<div class="text-gray-500 text-sm">
|
|
396
|
-
<p>
|
|
397
|
-
This array contains items of type:{' '}
|
|
398
|
-
<span class="font-mono text-blue-600">{processedSchema.items?.type || 'unknown'}</span>
|
|
399
|
-
</p>
|
|
400
|
-
{processedSchema.items?.description && <p class="text-xs mt-2 text-gray-600">{processedSchema.items.description}</p>}
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
</div>
|
|
406
|
-
</div>
|
|
407
|
-
|
|
408
|
-
<script define:vars={{ instanceId, variants, id, file }}>
|
|
409
|
-
// Check if expand is set via data attribute (from portal movement)
|
|
410
|
-
function checkAndExpandAll() {
|
|
411
|
-
const schemaViewerClient = document.getElementById(`${id}-${file}-SchemaViewer-client`);
|
|
412
|
-
if (schemaViewerClient && schemaViewerClient.getAttribute('data-expand') === 'true') {
|
|
413
|
-
// Find all toggle buttons within this schema viewer
|
|
414
|
-
const toggleButtons = schemaViewerClient.querySelectorAll('.property-toggle');
|
|
415
|
-
toggleButtons.forEach((button) => {
|
|
416
|
-
if (button.getAttribute('aria-expanded') === 'false') {
|
|
417
|
-
button.click(); // Trigger the existing toggle logic
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Setup search and utilities
|
|
424
|
-
function setupSchemaUtilities() {
|
|
425
|
-
const schemaViewerClient = document.getElementById(id);
|
|
426
|
-
if (!schemaViewerClient || schemaViewerClient.getAttribute('data-search') !== 'true') return;
|
|
427
|
-
|
|
428
|
-
const searchInput = document.getElementById(`${instanceId}-search`);
|
|
429
|
-
const expandAllBtn = document.getElementById(`${instanceId}-expand-all`);
|
|
430
|
-
const collapseAllBtn = document.getElementById(`${instanceId}-collapse-all`);
|
|
431
|
-
const searchResults = document.getElementById(`${instanceId}-search-results`);
|
|
432
|
-
const propertiesContainer = document.getElementById(`${instanceId}-properties`);
|
|
433
|
-
const noResultsMessage = document.getElementById(`${instanceId}-no-results`);
|
|
434
|
-
const prevMatchBtn = document.getElementById(`${instanceId}-prev-match`);
|
|
435
|
-
const nextMatchBtn = document.getElementById(`${instanceId}-next-match`);
|
|
436
|
-
|
|
437
|
-
let currentMatches = [];
|
|
438
|
-
let currentMatchIndex = -1;
|
|
439
|
-
|
|
440
|
-
if (!searchInput || !propertiesContainer) return;
|
|
441
|
-
|
|
442
|
-
// Expand All functionality
|
|
443
|
-
if (expandAllBtn) {
|
|
444
|
-
expandAllBtn.addEventListener('click', () => {
|
|
445
|
-
const toggleButtons = schemaViewerClient.querySelectorAll('.property-toggle');
|
|
446
|
-
toggleButtons.forEach((button) => {
|
|
447
|
-
if (button.getAttribute('aria-expanded') === 'false') {
|
|
448
|
-
button.click();
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Collapse All functionality
|
|
455
|
-
if (collapseAllBtn) {
|
|
456
|
-
collapseAllBtn.addEventListener('click', () => {
|
|
457
|
-
const toggleButtons = schemaViewerClient.querySelectorAll('.property-toggle');
|
|
458
|
-
toggleButtons.forEach((button) => {
|
|
459
|
-
if (button.getAttribute('aria-expanded') === 'true') {
|
|
460
|
-
button.click();
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Helper functions
|
|
467
|
-
function scrollToMatch(container) {
|
|
468
|
-
container.scrollIntoView({
|
|
469
|
-
behavior: 'smooth',
|
|
470
|
-
block: 'center',
|
|
471
|
-
inline: 'nearest',
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
function updateMatchDisplay() {
|
|
476
|
-
// Remove previous current match highlighting
|
|
477
|
-
currentMatches.forEach((match, index) => {
|
|
478
|
-
match.classList.remove('search-current-match');
|
|
479
|
-
if (index === currentMatchIndex) {
|
|
480
|
-
match.classList.add('search-current-match');
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// Update navigation buttons
|
|
485
|
-
if (prevMatchBtn && nextMatchBtn) {
|
|
486
|
-
prevMatchBtn.disabled = currentMatches.length === 0 || currentMatchIndex <= 0;
|
|
487
|
-
nextMatchBtn.disabled = currentMatches.length === 0 || currentMatchIndex >= currentMatches.length - 1;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// Update search results with current position
|
|
491
|
-
if (searchResults && currentMatches.length > 0) {
|
|
492
|
-
searchResults.textContent = `${currentMatchIndex + 1} of ${currentMatches.length} ${currentMatches.length === 1 ? 'match' : 'matches'}`;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function performSearch(searchTerm) {
|
|
497
|
-
const propertyContainers = propertiesContainer.querySelectorAll('.property-container');
|
|
498
|
-
currentMatches = [];
|
|
499
|
-
currentMatchIndex = -1;
|
|
500
|
-
|
|
501
|
-
if (searchTerm === '') {
|
|
502
|
-
// Reset search
|
|
503
|
-
propertyContainers.forEach((container) => {
|
|
504
|
-
container.classList.remove('search-match', 'search-no-match', 'search-current-match', 'search-dimmed');
|
|
505
|
-
const nameEl = container.querySelector('.font-semibold');
|
|
506
|
-
if (nameEl) {
|
|
507
|
-
nameEl.innerHTML = nameEl.textContent;
|
|
508
|
-
}
|
|
509
|
-
});
|
|
510
|
-
searchResults.classList.add('hidden');
|
|
511
|
-
if (noResultsMessage) {
|
|
512
|
-
noResultsMessage.classList.add('hidden');
|
|
513
|
-
}
|
|
514
|
-
updateMatchDisplay();
|
|
515
|
-
|
|
516
|
-
// Scroll back to top when search is cleared
|
|
517
|
-
// Use the same container that we're already working with
|
|
518
|
-
const scrollableContainer = propertiesContainer.closest('.schema-viewer');
|
|
519
|
-
if (scrollableContainer) {
|
|
520
|
-
scrollableContainer.scrollTo({
|
|
521
|
-
top: 0,
|
|
522
|
-
behavior: 'smooth',
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Find all matches
|
|
529
|
-
propertyContainers.forEach((container) => {
|
|
530
|
-
const nameEl = container.querySelector('.font-semibold');
|
|
531
|
-
|
|
532
|
-
if (!nameEl) return;
|
|
533
|
-
|
|
534
|
-
const propName = nameEl.textContent.toLowerCase();
|
|
535
|
-
|
|
536
|
-
if (propName.includes(searchTerm)) {
|
|
537
|
-
container.classList.add('search-match');
|
|
538
|
-
container.classList.remove('search-dimmed');
|
|
539
|
-
currentMatches.push(container);
|
|
540
|
-
|
|
541
|
-
// Highlight the search term in the property name
|
|
542
|
-
const regex = new RegExp(`(${searchTerm})`, 'gi');
|
|
543
|
-
nameEl.innerHTML = nameEl.textContent.replace(regex, '<mark class="bg-yellow-200 rounded px-0.5">$1</mark>');
|
|
544
|
-
|
|
545
|
-
// Expand parent containers to show the match
|
|
546
|
-
let parent = container.parentElement;
|
|
547
|
-
while (parent && parent.id !== `${instanceId}-properties`) {
|
|
548
|
-
if (parent.classList.contains('nested-content') && parent.classList.contains('hidden')) {
|
|
549
|
-
const parentPropertyContainer = parent.closest('.property-container');
|
|
550
|
-
if (parentPropertyContainer) {
|
|
551
|
-
const toggleBtn = parentPropertyContainer.querySelector('.property-toggle');
|
|
552
|
-
if (toggleBtn && toggleBtn.getAttribute('aria-expanded') === 'false') {
|
|
553
|
-
toggleBtn.click();
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
parent = parent.parentElement;
|
|
558
|
-
}
|
|
559
|
-
} else {
|
|
560
|
-
container.classList.remove('search-match', 'search-current-match');
|
|
561
|
-
container.classList.add('search-dimmed');
|
|
562
|
-
nameEl.innerHTML = nameEl.textContent;
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
// Show results
|
|
567
|
-
if (searchResults) {
|
|
568
|
-
searchResults.classList.remove('hidden');
|
|
569
|
-
if (currentMatches.length === 0) {
|
|
570
|
-
searchResults.textContent = 'No properties found';
|
|
571
|
-
searchResults.classList.add('text-red-600');
|
|
572
|
-
searchResults.classList.remove('text-gray-600');
|
|
573
|
-
if (noResultsMessage) {
|
|
574
|
-
noResultsMessage.classList.remove('hidden');
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
searchResults.classList.remove('text-red-600');
|
|
578
|
-
searchResults.classList.add('text-gray-600');
|
|
579
|
-
if (noResultsMessage) {
|
|
580
|
-
noResultsMessage.classList.add('hidden');
|
|
581
|
-
}
|
|
582
|
-
// Go to first match
|
|
583
|
-
currentMatchIndex = 0;
|
|
584
|
-
scrollToMatch(currentMatches[0]);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
updateMatchDisplay();
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// Search functionality
|
|
592
|
-
let searchTimeout;
|
|
593
|
-
searchInput.addEventListener('input', (e) => {
|
|
594
|
-
clearTimeout(searchTimeout);
|
|
595
|
-
searchTimeout = setTimeout(() => {
|
|
596
|
-
const searchTerm = e.target.value.toLowerCase().trim();
|
|
597
|
-
performSearch(searchTerm);
|
|
598
|
-
}, 300);
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
// Navigation functionality
|
|
602
|
-
if (prevMatchBtn) {
|
|
603
|
-
prevMatchBtn.addEventListener('click', () => {
|
|
604
|
-
if (currentMatchIndex > 0) {
|
|
605
|
-
currentMatchIndex--;
|
|
606
|
-
scrollToMatch(currentMatches[currentMatchIndex]);
|
|
607
|
-
updateMatchDisplay();
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (nextMatchBtn) {
|
|
613
|
-
nextMatchBtn.addEventListener('click', () => {
|
|
614
|
-
if (currentMatchIndex < currentMatches.length - 1) {
|
|
615
|
-
currentMatchIndex++;
|
|
616
|
-
scrollToMatch(currentMatches[currentMatchIndex]);
|
|
617
|
-
updateMatchDisplay();
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Keyboard navigation
|
|
623
|
-
searchInput.addEventListener('keydown', (e) => {
|
|
624
|
-
if (e.key === 'Enter') {
|
|
625
|
-
e.preventDefault();
|
|
626
|
-
if (e.shiftKey && prevMatchBtn && !prevMatchBtn.disabled) {
|
|
627
|
-
prevMatchBtn.click();
|
|
628
|
-
} else if (!e.shiftKey && nextMatchBtn && !nextMatchBtn.disabled) {
|
|
629
|
-
nextMatchBtn.click();
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
if (variants) {
|
|
636
|
-
const selector = document.getElementById(`${instanceId}-variant-selector`);
|
|
637
|
-
const propertiesContainer = document.getElementById(`${instanceId}-properties`);
|
|
638
|
-
|
|
639
|
-
function updateRequiredFields() {
|
|
640
|
-
const selectedVariant = variants[selector.value];
|
|
641
|
-
const properties = propertiesContainer.querySelectorAll('.property-container');
|
|
642
|
-
|
|
643
|
-
properties.forEach((prop) => {
|
|
644
|
-
const nameEl = prop.querySelector('.font-semibold');
|
|
645
|
-
if (!nameEl) return;
|
|
646
|
-
|
|
647
|
-
const name = nameEl.textContent;
|
|
648
|
-
const requiredBadge = prop.querySelector('.text-red-600');
|
|
649
|
-
|
|
650
|
-
if (selectedVariant.required.includes(name)) {
|
|
651
|
-
if (!requiredBadge) {
|
|
652
|
-
const badge = document.createElement('span');
|
|
653
|
-
badge.className = 'text-red-600 text-xs ml-3 flex-shrink-0';
|
|
654
|
-
badge.textContent = 'required';
|
|
655
|
-
nameEl.parentElement.appendChild(badge);
|
|
656
|
-
}
|
|
657
|
-
} else {
|
|
658
|
-
requiredBadge?.remove();
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
selector.addEventListener('change', updateRequiredFields);
|
|
664
|
-
// Initialize with first variant
|
|
665
|
-
updateRequiredFields();
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// Check expand state after page loads and after portal movement
|
|
669
|
-
document.addEventListener('astro:page-load', () => {
|
|
670
|
-
// Small delay to ensure portal movement has completed
|
|
671
|
-
setTimeout(() => {
|
|
672
|
-
checkAndExpandAll();
|
|
673
|
-
setupSchemaUtilities();
|
|
674
|
-
}, 100);
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
// Also check immediately in case the component is already in place
|
|
678
|
-
checkAndExpandAll();
|
|
679
|
-
setupSchemaUtilities();
|
|
680
|
-
</script>
|
|
681
|
-
|
|
682
|
-
<style>
|
|
683
|
-
.schema-viewer code {
|
|
684
|
-
font-family: 'Courier New', Courier, monospace;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/* Search highlighting styles */
|
|
688
|
-
.search-dimmed {
|
|
689
|
-
opacity: 0.4;
|
|
690
|
-
transition: opacity 0.2s ease;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
.search-match {
|
|
694
|
-
opacity: 1;
|
|
695
|
-
transition: opacity 0.2s ease;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
.search-current-match {
|
|
699
|
-
background-color: rgba(59, 130, 246, 0.1);
|
|
700
|
-
border-left: 3px solid #3b82f6;
|
|
701
|
-
padding-left: 8px;
|
|
702
|
-
margin-left: -11px;
|
|
703
|
-
transition: all 0.2s ease;
|
|
704
|
-
}
|
|
705
|
-
</style>
|