@xyd-js/atlas 0.1.0-xyd.9 → 0.1.0-xyd.99

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.
Files changed (56) hide show
  1. package/.storybook/index.css +1 -27
  2. package/.storybook/preview.ts +1 -2
  3. package/CHANGELOG.md +1049 -0
  4. package/LICENSE +21 -0
  5. package/dist/Update-0XruJHjj-COLbZvRj.js +4 -0
  6. package/dist/Update-0XruJHjj-COLbZvRj.js.map +1 -0
  7. package/dist/Update-DKOAw8p9-COLbZvRj.js +4 -0
  8. package/dist/Update-DKOAw8p9-COLbZvRj.js.map +1 -0
  9. package/dist/index.css +43 -54
  10. package/dist/index.d.ts +30 -11
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/styles.css +89 -0
  14. package/dist/tokens.css +60 -0
  15. package/dist/xydPlugin.d.ts +5 -0
  16. package/dist/xydPlugin.js +2 -0
  17. package/dist/xydPlugin.js.map +1 -0
  18. package/index.ts +1 -1
  19. package/package.json +20 -21
  20. package/packages/xyd-plugin/SidebarItem.tsx +27 -0
  21. package/packages/xyd-plugin/index.ts +20 -0
  22. package/rollup.config.js +56 -12
  23. package/src/components/ApiRef/ApiRefItem/ApiRefItem.styles.tsx +95 -63
  24. package/src/components/ApiRef/ApiRefItem/ApiRefItem.tsx +507 -55
  25. package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.styles.tsx +186 -143
  26. package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.tsx +584 -76
  27. package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.styles.tsx +19 -23
  28. package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.tsx +39 -24
  29. package/src/components/Atlas/Atlas.styles.tsx +3 -5
  30. package/src/components/Atlas/Atlas.tsx +34 -21
  31. package/src/components/Atlas/AtlasContext.tsx +47 -0
  32. package/src/components/Atlas/AtlasDecorator.styles.ts +22 -0
  33. package/src/components/Atlas/AtlasDecorator.tsx +15 -0
  34. package/src/components/Atlas/AtlasLazy/AtlasLazy.styles.tsx +7 -8
  35. package/src/components/Atlas/AtlasLazy/AtlasLazy.tsx +4 -6
  36. package/src/components/Atlas/AtlasPrimary.tsx +21 -0
  37. package/src/components/Atlas/AtlasSecondary.tsx +148 -0
  38. package/src/components/Atlas/index.ts +6 -2
  39. package/src/components/Atlas/types.ts +11 -0
  40. package/src/components/Code/CodeSampleButtons/CodeSampleButtons.styles.tsx +56 -141
  41. package/src/components/Code/CodeSampleButtons/CodeSampleButtons.tsx +19 -28
  42. package/src/components/Code/index.ts +0 -4
  43. package/src/components/Icon/index.tsx +384 -0
  44. package/src/styles/styles.css +89 -0
  45. package/src/styles/tokens.css +60 -0
  46. package/src/utils/mdx.ts +0 -29
  47. package/tsconfig.json +9 -2
  48. package/types.d.ts +22 -0
  49. package/src/components/Code/CodeCopy/CodeCopy.style.tsx +0 -21
  50. package/src/components/Code/CodeCopy/CodeCopy.tsx +0 -32
  51. package/src/components/Code/CodeCopy/index.ts +0 -7
  52. package/src/components/Code/CodeSample/CodeSample.styles.tsx +0 -134
  53. package/src/components/Code/CodeSample/CodeSample.tsx +0 -149
  54. package/src/components/Code/CodeSample/index.ts +0 -8
  55. package/src/components/Code/CodeSample/withLocalStored.tsx +0 -52
  56. package/src/components/Code/default-theme.ts +0 -266
@@ -1,105 +1,557 @@
1
- import React from "react";
1
+ import React, {createContext, useContext, useState, useCallback, useEffect} from "react";
2
2
 
3
- import {OpenAPIReferenceContext, Reference, ReferenceCategory} from "@xyd-js/uniform";
3
+ import {
4
+ Definition,
5
+ DefinitionVariant,
6
+ Meta,
7
+ OpenAPIReferenceContext,
8
+ Reference,
9
+ ReferenceCategory
10
+ } from "@xyd-js/uniform";
11
+ import {Heading, Code, Badge, Text} from "@xyd-js/components/writer";
4
12
 
5
- import {MDXReference} from "@/utils/mdx";
6
13
  import {
7
14
  ApiRefProperties,
8
15
  ApiRefSamples
9
16
  } from "@/components/ApiRef";
10
- import {
11
- $navbar,
12
- $refItem,
13
- $properties,
14
- $subtitle,
15
- $title
16
- } from "@/components/ApiRef/ApiRefItem/ApiRefItem.styles";
17
+ import * as cn from "@/components/ApiRef/ApiRefItem/ApiRefItem.styles";
18
+ import {useVariantToggles, type VariantToggleConfig} from "@/components/Atlas/AtlasContext";
17
19
 
18
20
  export interface ApiRefItemProps {
19
- reference: MDXReference<Reference>
21
+ reference: Reference
22
+ kind?: "secondary"
20
23
  }
21
24
 
22
25
  // TODO: context with current referene?
23
- export function ApiRefItem({reference}: ApiRefItemProps) {
26
+ export function ApiRefItem({
27
+ kind,
28
+ reference
29
+ }: ApiRefItemProps) {
30
+ const hasExamples = reference.examples?.groups?.length || false
31
+
32
+ let header: React.ReactNode | null = <$IntroHeader reference={reference}/>
33
+ let examples: React.ReactNode | null = <ApiRefSamples examples={reference.examples}/>
34
+
35
+ if (kind === "secondary") {
36
+ header = null
37
+ examples = null
38
+ }
39
+
40
+ return <atlas-apiref-item
41
+ data-has-examples={hasExamples ? "true" : undefined}
42
+ className={cn.ApiRefItemHost}
43
+ >
44
+ <atlas-apiref-item-showcase className={cn.ApiRefItemGrid}>
45
+ <div>
46
+ {header}
47
+ <$Definitions
48
+ kind={kind}
49
+ reference={reference}
50
+ />
51
+ </div>
52
+
53
+ {examples}
54
+ </atlas-apiref-item-showcase>
55
+ </atlas-apiref-item>
56
+ }
57
+
58
+ function $IntroHeader({reference}: ApiRefItemProps) {
24
59
  let topNavbar;
25
60
 
26
- switch (reference?.category?.title) {
61
+ switch (reference?.category) {
27
62
  case ReferenceCategory.REST: {
28
- const ctx = reference.context as MDXReference<OpenAPIReferenceContext>
63
+ const ctx = reference.context as OpenAPIReferenceContext
29
64
 
30
- if (!ctx || !ctx.method || !ctx.path || !ctx.path.title) {
65
+ if (!ctx || !ctx.method || !ctx.fullPath) {
31
66
  break;
32
67
  }
68
+
33
69
  // TODO: finish subitlte from ref
34
70
  topNavbar = <$Navbar
35
- label={ctx.method.title}
36
- subtitle={`${decodeURIComponent(ctx.path.title)}`}
71
+ label={ctx.method}
72
+ subtitle={`${ctx.fullPath}`}
73
+ matchSubtitle={ctx.path}
37
74
  />
38
75
  break;
39
76
  }
40
77
  }
41
-
42
- return <div className={$refItem.host}>
43
- <$Title title={reference.title || ""}/>
78
+ return <>
79
+ <$Title title={reference.title}/>
44
80
 
45
81
  {topNavbar}
46
82
 
47
- {reference.description.children}
83
+ {reference.description}
84
+ </>
85
+ }
48
86
 
49
- <div className={$refItem.grid}>
50
- <$Properties reference={reference}/>
51
- {reference.examples && <ApiRefSamples examples={reference.examples}/>}
52
- </div>
87
+ function $Authorization({reference}: ApiRefItemProps) {
88
+ if (!reference.context) {
89
+ return null;
90
+ }
53
91
 
92
+ const context = reference.context as OpenAPIReferenceContext;
93
+
94
+ if (!context.scopes || !context.scopes.length) {
95
+ return null;
96
+ }
97
+
98
+ return <div>
99
+ <div className={cn.ApiRefItemDefinitionsItem}>
100
+ <div part="header">
101
+ <$Subtitle title="Scopes"/>
102
+ </div>
103
+
104
+ <$DefinitionBody definition={{
105
+ title: "",
106
+ properties: [],
107
+ description: <>
108
+ Required scopes: {context.scopes.map(s => <Code>{s}</Code>)}
109
+ </>
110
+ }}
111
+ />
112
+ </div>
54
113
  </div>
55
114
  }
56
115
 
57
- function $Properties({reference}: ApiRefItemProps) {
58
- return <div className={$properties.host}>
59
- {reference?.definitions?.map((definition, i) => <div key={i}>
60
- {
61
- definition.properties?.length && <div key={i} className={$properties.item}>
62
- <$Subtitle title={definition.title.title}/>
116
+ const VariantContext = createContext<{
117
+ setVariant: (variant: DefinitionVariant) => void,
118
+ variant?: DefinitionVariant,
119
+ variantToggles: VariantToggleConfig[],
120
+ selectedValues: Record<string, string>,
121
+ setSelectedValue: (key: string, value: string) => void,
122
+ variants: DefinitionVariant[],
123
+ }>({
124
+ variant: undefined,
125
+ setVariant: () => {
126
+ },
127
+ variantToggles: [],
128
+ selectedValues: {},
129
+ setSelectedValue: () => {
130
+ },
131
+ variants: [],
132
+ });
63
133
 
64
- <ApiRefProperties properties={definition.properties}/>
65
- </div>
134
+ function $Definitions({
135
+ kind,
136
+ reference
137
+ }: ApiRefItemProps) {
138
+ let argDefinition: Definition | undefined
139
+ let definitions = reference?.definitions || []
140
+
141
+ if (reference?.category === ReferenceCategory.GRAPHQL) {
142
+ const gqlDefinitions: Definition[] = []
143
+
144
+ // First find the arguments definition
145
+ reference?.definitions?.forEach(definition => {
146
+ const foundArgs = definition?.meta?.find(meta => {
147
+ return meta.name === "type" && meta.value === "arguments"
148
+ })
149
+
150
+ if (foundArgs) {
151
+ argDefinition = definition
152
+ } else {
153
+ gqlDefinitions.push(definition)
66
154
  }
67
- </div>)}
155
+ })
156
+
157
+ // Process each definition to merge argument properties
158
+ definitions = gqlDefinitions
159
+ .filter(definition => definition?.properties?.length)
160
+ .map(definition => {
161
+ if (!definition.properties?.length) return definition
162
+
163
+ // For each property in the definition
164
+ const updatedProperties = definition.properties.map(prop => {
165
+ // Find matching variant in argDefinition by symbolName
166
+ const matchingVariant = argDefinition?.variants?.find(variant => {
167
+ const symbolMeta = variant.meta?.find(m => m.name === 'symbolName')
168
+ return symbolMeta?.value === prop.name
169
+ })
170
+
171
+ if (matchingVariant) {
172
+ // Add meta flag to indicate this property has arguments, but only if it doesn't already have it
173
+ const meta = prop.meta || []
174
+ if (!meta.some(m => m.name === 'hasArguments')) {
175
+ meta.push({
176
+ name: 'hasArguments', // TODO: better solution in the future
177
+ value: 'true'
178
+ })
179
+ }
180
+
181
+ // Merge properties from the matching variant
182
+ return {
183
+ ...prop,
184
+ meta,
185
+ properties: matchingVariant.properties || []
186
+ }
187
+ }
188
+
189
+ return prop
190
+ })
191
+
192
+ return {
193
+ ...definition,
194
+ properties: updatedProperties
195
+ }
196
+ })
197
+ }
198
+
199
+ return <atlas-apiref-definitions className={cn.ApiRefItemDefinitionsHost}>
200
+ <$Authorization reference={reference}/>
201
+
202
+ {definitions?.map((definition, i) => {
203
+ if (kind === "secondary") {
204
+ return <$DefinitionBody definition={definition}/>
205
+ }
206
+
207
+ return <$VariantsProvider key={i} definition={definition}>
208
+ <div>
209
+ {
210
+ definition?.title ? <div key={i} className={cn.ApiRefItemDefinitionsItem}>
211
+ <div part="header">
212
+ <$Subtitle title={definition.title}/>
213
+ <div part="controls">
214
+ <$VariantSelects/>
215
+ </div>
216
+ </div>
217
+
218
+ <$DefinitionBody definition={definition}/>
219
+ </div> : null
220
+ }
221
+ </div>
222
+ </$VariantsProvider>
223
+ })}
224
+ </atlas-apiref-definitions>
225
+ }
226
+
227
+ function $VariantsProvider({definition, children}: {
228
+ definition: Definition,
229
+ children: React.ReactNode
230
+ }) {
231
+ const variants = definition.variants || [];
232
+ const variantMetas = variants.reduce((acc, variant) => {
233
+ const allMetaNames = variant.meta?.reduce((metaAcc, meta) => ({
234
+ ...metaAcc,
235
+ [meta.name]: 1,
236
+ }), {}) || {}
237
+
238
+
239
+ return {
240
+ ...acc,
241
+ ...allMetaNames,
242
+ }
243
+ }, {});
244
+ const variantToggles = (useVariantToggles() || []).filter(toggle => variantMetas[toggle.key])
245
+
246
+ const [selectedValues, setSelectedValues] = useState<Record<string, string>>(() => {
247
+ const initial: Record<string, string> = {};
248
+ variantToggles.forEach(toggle => {
249
+ initial[toggle.key] = toggle.defaultValue;
250
+ });
251
+ return initial;
252
+ });
253
+
254
+ const setSelectedValue = useCallback((key: string, value: string) => {
255
+ setSelectedValues(prev => ({...prev, [key]: value}));
256
+ }, []);
257
+
258
+
259
+ const [variant, setVariant] = useState<DefinitionVariant | undefined>(() => {
260
+ return findMatchingVariant(variants, selectedValues);
261
+ });
262
+
263
+ useEffect(() => {
264
+ const newVariant = findMatchingVariant(variants, selectedValues);
265
+ setVariant(newVariant);
266
+ }, [selectedValues, variants]);
267
+
268
+ return <VariantContext.Provider value={{
269
+ variant,
270
+ setVariant,
271
+ variantToggles,
272
+ selectedValues,
273
+ setSelectedValue,
274
+ variants,
275
+ }}>
276
+ {children}
277
+ </VariantContext.Provider>
278
+ }
279
+
280
+ function findMatchingVariant(variants: DefinitionVariant[], selectedValues: Record<string, string>): DefinitionVariant | undefined {
281
+ const matchingVariant = variants.find(variant => {
282
+ const matches = Object.entries(selectedValues).every(([key, value]) => {
283
+ if (!value) return true; // Skip empty values
284
+ const meta = variant.meta?.find(m => m.name === key);
285
+ return meta?.value === value;
286
+ });
287
+ return matches;
288
+ });
289
+
290
+ return matchingVariant || variants[0];
291
+ }
292
+
293
+ function $VariantSelects() {
294
+ const {variantToggles, selectedValues, setSelectedValue, variants} = useContext(VariantContext);
295
+
296
+ if (!variants?.length) return null;
297
+
298
+ // Create selects based on variantToggles
299
+ return (
300
+ <div className={""}>
301
+ {variantToggles.map((toggle, index) => {
302
+ // Get all unique values for this toggle
303
+ const availableValues = Array.from(new Set(
304
+ variants.map(v => {
305
+ const meta = v.meta?.find(m => m.name === toggle.key);
306
+ return meta?.value as string;
307
+ }).filter(Boolean)
308
+ )).sort();
309
+
310
+ // Get available values based on other selected values
311
+ const filteredValues = availableValues.filter(value => {
312
+ // For the first toggle, show all values
313
+ if (index === 0) return true;
314
+
315
+ // For other toggles, check if there's a variant with this value and all previous selected values
316
+ return variants.some(variant => {
317
+ // First check if this variant has the value we're checking
318
+ const hasValue = variant.meta?.some(m => m.name === toggle.key && m.value === value);
319
+ if (!hasValue) return false;
320
+
321
+ // Then check if it matches all previous selected values
322
+ return variantToggles.slice(0, index).every(prevToggle => {
323
+ const selectedValue = selectedValues[prevToggle.key];
324
+ if (!selectedValue) return true; // Skip empty values
325
+
326
+ const meta = variant.meta?.find(m => m.name === prevToggle.key);
327
+ return meta?.value === selectedValue;
328
+ });
329
+ });
330
+ });
331
+
332
+ // If no values available, use the current value
333
+ const displayValues = filteredValues.length > 0 ? filteredValues :
334
+ (selectedValues[toggle.key] ? [selectedValues[toggle.key]] : availableValues);
335
+
336
+ return (
337
+ <select
338
+ key={toggle.key}
339
+ value={selectedValues[toggle.key] || ''}
340
+ onChange={(e) => {
341
+ const newValue = e.target.value;
342
+ setSelectedValue(toggle.key, newValue);
343
+
344
+ // For the first toggle (status), check if current content type is still valid
345
+ if (index === 0 && variantToggles.length > 1) {
346
+ const contentTypeKey = variantToggles[1].key;
347
+ const currentContentType = selectedValues[contentTypeKey];
348
+
349
+ if (currentContentType) {
350
+ // Check if current content type is valid for new status
351
+ const isValid = variants.some(variant => {
352
+ const statusMeta = variant.meta?.find(m => m.name === toggle.key);
353
+ const contentTypeMeta = variant.meta?.find(m => m.name === contentTypeKey);
354
+ return statusMeta?.value === newValue && contentTypeMeta?.value === currentContentType;
355
+ });
356
+
357
+ // If not valid, reset content type
358
+ if (!isValid) {
359
+ setSelectedValue(contentTypeKey, '');
360
+ }
361
+ }
362
+ } else if (index < variantToggles.length - 1) {
363
+ // For other toggles, reset all toggles after this one
364
+ // Only if there are toggles after this one
365
+ variantToggles.slice(index + 1).forEach(nextToggle => {
366
+ setSelectedValue(nextToggle.key, '');
367
+ });
368
+ }
369
+ }}
370
+ >
371
+ {displayValues.map(value => (
372
+ <option key={value} value={value}>
373
+ {value}
374
+ </option>
375
+ ))}
376
+ </select>
377
+ );
378
+ })}
379
+ </div>
380
+ );
381
+ }
382
+
383
+ interface DefinitionBodyProps {
384
+ definition: Definition
385
+ }
386
+
387
+ function $DefinitionBody(props: DefinitionBodyProps) {
388
+ const {definition} = props;
389
+ const {variant} = useContext(VariantContext);
390
+
391
+ let apiRefProperties: React.ReactNode | null = null;
392
+
393
+ if (variant) {
394
+ if (variant.properties?.length) {
395
+ apiRefProperties = <ApiRefProperties properties={variant.properties}/>;
396
+ } else if (variant.rootProperty) {
397
+ apiRefProperties = <ApiRefProperties properties={[variant.rootProperty]}/>;
398
+ }
399
+ } else {
400
+ if (definition.properties?.length) {
401
+ apiRefProperties = <ApiRefProperties properties={definition.properties}/>;
402
+ } else if (definition.rootProperty) {
403
+ apiRefProperties = <ApiRefProperties properties={[definition.rootProperty]}/>;
404
+ }
405
+ }
406
+
407
+ const getMetaInfo = (meta: Meta[] | undefined) => {
408
+ if (!meta?.length) return null;
409
+
410
+ const minimum = meta.find(m => m.name === 'minimum')?.value;
411
+ const maximum = meta.find(m => m.name === 'maximum')?.value;
412
+ const example = meta.find(m => m.name === 'example')?.value;
413
+
414
+ const rangeInfo: string[] = [];
415
+ if (minimum !== undefined && maximum !== undefined) {
416
+ rangeInfo.push(`Required range: ${minimum} <= x <= ${maximum}`);
417
+ } else if (minimum !== undefined) {
418
+ rangeInfo.push(`Required range: x >= ${minimum}`);
419
+ } else if (maximum !== undefined) {
420
+ rangeInfo.push(`Required range: x <= ${maximum}`);
421
+ }
422
+
423
+ const exampleInfo = example ? [`Examples:`, `"${example}"`] : [];
424
+
425
+ return [...rangeInfo, ...exampleInfo].length > 0 ? (
426
+ <div style={{
427
+ marginTop: '1rem',
428
+ marginBottom: '1rem',
429
+ color: 'var(--color-text-secondary)',
430
+ fontSize: '0.875rem'
431
+ }}>
432
+ {rangeInfo.map((info, i) => (
433
+ <div key={`range-${i}`}>{info}</div>
434
+ ))}
435
+ {exampleInfo.length > 0 && (
436
+ <>
437
+ <div>{exampleInfo[0]}</div>
438
+ <div>{exampleInfo[1]}</div>
439
+ </>
440
+ )}
441
+ </div>
442
+ ) : null;
443
+ };
444
+
445
+ const metaInfo = variant ? getMetaInfo(variant.meta) : getMetaInfo(definition.meta);
446
+ const description = variant ? variant.description : definition.description;
447
+ const metaDescription = definitionMetaDescription(variant ? variant : definition);
448
+
449
+ return <div className={cn.DefinitionBody}>
450
+ {
451
+ description && <div>
452
+ {description}
453
+ </div>
454
+ }
455
+ {
456
+ metaDescription && <div>
457
+ {metaDescription}
458
+ </div>
459
+ }
460
+
461
+ {metaInfo}
462
+
463
+ {apiRefProperties}
68
464
  </div>
69
465
  }
70
466
 
71
- function $Navbar({label, subtitle}: { label: string, subtitle: string }) {
467
+ interface NavbarProps {
468
+ label: string
469
+ subtitle: string
470
+ matchSubtitle?: string
471
+ }
472
+
473
+ function $Navbar({label, subtitle, matchSubtitle}: NavbarProps) {
474
+ const renderSubtitle = () => {
475
+ if (!matchSubtitle) {
476
+ return subtitle;
477
+ }
478
+
479
+ const index = subtitle.indexOf(matchSubtitle);
480
+ if (index === -1) {
481
+ return subtitle;
482
+ }
483
+
484
+ const before = subtitle.substring(0, index);
485
+ const match = subtitle.substring(index, index + matchSubtitle.length);
486
+ const after = subtitle.substring(index + matchSubtitle.length);
487
+
488
+ return (
489
+ <>
490
+ {before}
491
+ <Text as="span" weight="extra-bold">
492
+ {match}
493
+ </Text>
494
+ {after}
495
+ </>
496
+ );
497
+ };
498
+
499
+ const handleClick = (e: React.MouseEvent) => {
500
+ const target = e.target as HTMLElement;
501
+ const range = document.createRange();
502
+ range.selectNodeContents(target);
503
+ const selection = window.getSelection();
504
+ if (selection) {
505
+ selection.removeAllRanges();
506
+ selection.addRange(range);
507
+ }
508
+ };
509
+
72
510
  return <>
73
- <div className={$navbar.host}>
74
- <span className={$navbar.container}>
75
- <span className={$navbar.label}>
76
- {label.toUpperCase()}
77
- </span>
78
- <span>
79
- {subtitle}
80
- </span>
81
- </span>
511
+ <div className={cn.ApiRefItemNavbarHost}>
512
+ <div className={cn.ApiRefItemNavbarContainer}>
513
+ <div className={cn.ApiRefItemNavbarLabel}>
514
+ {/* TODO: in the future not only for REST */}
515
+ <div data-active="true" atlas-oas-method={label.toUpperCase()}>
516
+ <Badge size="xs">
517
+ {label.toUpperCase()}
518
+ </Badge>
519
+ </div>
520
+ </div>
521
+ <div
522
+ className={cn.ApiRefItemNavbarSubtitle}
523
+ onClick={handleClick}
524
+ >
525
+ {renderSubtitle()}
526
+ </div>
527
+ </div>
82
528
  </div>
83
529
  </>
84
530
  }
85
531
 
86
-
87
532
  function $Title({title}: { title: string }) {
88
533
  return <>
89
- <h1 className={$title.host}>
90
- <a className={$title.link}>
91
- {title}
92
- </a>
93
- </h1>
534
+ <Heading size={1}>
535
+ {title}
536
+ </Heading>
94
537
  </>
95
538
  }
96
539
 
97
540
  function $Subtitle({title}: { title: string }) {
98
541
  return <>
99
- <h1 className={$subtitle.host}>
100
- <a className={$subtitle.link}>
101
- {title}
102
- </a>
103
- </h1>
542
+ <Heading size={3}>
543
+ {title}
544
+ </Heading>
104
545
  </>
546
+ }
547
+
548
+ function definitionMetaDescription(definition: Definition | DefinitionVariant): string {
549
+ if (definition.meta?.length) {
550
+ const descriptionMeta = definition.meta.find(meta => meta.name === 'definitionDescription');
551
+ if (descriptionMeta) {
552
+ return descriptionMeta.value as string || "";
553
+ }
554
+ }
555
+
556
+ return "";
105
557
  }