@xyd-js/atlas 0.1.0-build.173
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/.babelrc +6 -0
- package/.storybook/index.css +6 -0
- package/.storybook/main.ts +19 -0
- package/.storybook/preview.ts +17 -0
- package/.storybook/public/fonts/fustat-ext-500.woff2 +0 -0
- package/.storybook/public/fonts/fustat-ext-600.woff2 +0 -0
- package/.storybook/public/fonts/fustat-ext-700.woff2 +0 -0
- package/.storybook/public/fonts/fustat-regular.woff2 +0 -0
- package/CHANGELOG.md +1984 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/declarations.d.ts +4 -0
- package/dist/VideoGuide-C0K9fFar-Dk2lkn4r.js +4 -0
- package/dist/VideoGuide-C0K9fFar-Dk2lkn4r.js.map +1 -0
- package/dist/VideoGuide-CJYkuLst-Dk2lkn4r.js +4 -0
- package/dist/VideoGuide-CJYkuLst-Dk2lkn4r.js.map +1 -0
- package/dist/index.css +48 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +104 -0
- package/dist/tokens.css +60 -0
- package/dist/xydPlugin.d.ts +5 -0
- package/dist/xydPlugin.js +2 -0
- package/dist/xydPlugin.js.map +1 -0
- package/index.ts +2 -0
- package/package.json +68 -0
- package/packages/xyd-plugin/SidebarItem.tsx +27 -0
- package/packages/xyd-plugin/index.ts +20 -0
- package/postcss.config.cjs +5 -0
- package/rollup.config.js +120 -0
- package/src/components/ApiRef/ApiRefItem/ApiRefItem.styles.tsx +110 -0
- package/src/components/ApiRef/ApiRefItem/ApiRefItem.tsx +557 -0
- package/src/components/ApiRef/ApiRefItem/index.ts +7 -0
- package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.styles.tsx +202 -0
- package/src/components/ApiRef/ApiRefProperties/ApiRefProperties.tsx +665 -0
- package/src/components/ApiRef/ApiRefProperties/index.ts +7 -0
- package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.styles.tsx +28 -0
- package/src/components/ApiRef/ApiRefSamples/ApiRefSamples.tsx +69 -0
- package/src/components/ApiRef/ApiRefSamples/index.ts +7 -0
- package/src/components/ApiRef/index.ts +5 -0
- package/src/components/Atlas/Atlas.styles.tsx +5 -0
- package/src/components/Atlas/Atlas.tsx +43 -0
- package/src/components/Atlas/AtlasContext.tsx +47 -0
- package/src/components/Atlas/AtlasDecorator.styles.ts +22 -0
- package/src/components/Atlas/AtlasDecorator.tsx +15 -0
- package/src/components/Atlas/AtlasLazy/AtlasLazy.styles.tsx +9 -0
- package/src/components/Atlas/AtlasLazy/AtlasLazy.tsx +42 -0
- package/src/components/Atlas/AtlasLazy/hooks.ts +29 -0
- package/src/components/Atlas/AtlasLazy/index.ts +7 -0
- package/src/components/Atlas/AtlasPrimary.tsx +21 -0
- package/src/components/Atlas/AtlasSecondary.tsx +148 -0
- package/src/components/Atlas/index.ts +7 -0
- package/src/components/Atlas/types.ts +11 -0
- package/src/components/Code/CodeSampleButtons/CodeSampleButtons.styles.tsx +58 -0
- package/src/components/Code/CodeSampleButtons/CodeSampleButtons.tsx +97 -0
- package/src/components/Code/CodeSampleButtons/index.ts +7 -0
- package/src/components/Code/index.ts +2 -0
- package/src/components/Icon/index.tsx +386 -0
- package/src/docs/AtlasExample/AtlasExample.stories.tsx +47 -0
- package/src/docs/AtlasExample/todo-app.uniform.json +625 -0
- package/src/docs/AtlasExample/uniform-to-references.ts +101 -0
- package/src/styles/styles.css +104 -0
- package/src/styles/tokens.css +60 -0
- package/src/utils/mdx.ts +2 -0
- package/tsconfig.json +51 -0
- package/types.d.ts +22 -0
- package/vite.config.ts +25 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import React, {createContext, useContext, useState, useCallback, useEffect} from "react";
|
|
2
|
+
|
|
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";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
ApiRefProperties,
|
|
15
|
+
ApiRefSamples
|
|
16
|
+
} from "@/components/ApiRef";
|
|
17
|
+
import * as cn from "@/components/ApiRef/ApiRefItem/ApiRefItem.styles";
|
|
18
|
+
import {useVariantToggles, type VariantToggleConfig} from "@/components/Atlas/AtlasContext";
|
|
19
|
+
|
|
20
|
+
export interface ApiRefItemProps {
|
|
21
|
+
reference: Reference
|
|
22
|
+
kind?: "secondary"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// TODO: context with current referene?
|
|
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) {
|
|
59
|
+
let topNavbar;
|
|
60
|
+
|
|
61
|
+
switch (reference?.category) {
|
|
62
|
+
case ReferenceCategory.REST: {
|
|
63
|
+
const ctx = reference.context as OpenAPIReferenceContext
|
|
64
|
+
|
|
65
|
+
if (!ctx || !ctx.method || !ctx.fullPath) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// TODO: finish subitlte from ref
|
|
70
|
+
topNavbar = <$Navbar
|
|
71
|
+
label={ctx.method}
|
|
72
|
+
subtitle={`${ctx.fullPath}`}
|
|
73
|
+
matchSubtitle={ctx.path}
|
|
74
|
+
/>
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return <>
|
|
79
|
+
<$Title title={reference.title}/>
|
|
80
|
+
|
|
81
|
+
{topNavbar}
|
|
82
|
+
|
|
83
|
+
{reference.description}
|
|
84
|
+
</>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function $Authorization({reference}: ApiRefItemProps) {
|
|
88
|
+
if (!reference.context) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
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>
|
|
113
|
+
</div>
|
|
114
|
+
}
|
|
115
|
+
|
|
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
|
+
});
|
|
133
|
+
|
|
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)
|
|
154
|
+
}
|
|
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 key={i} 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}
|
|
464
|
+
</div>
|
|
465
|
+
}
|
|
466
|
+
|
|
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="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
|
+
|
|
510
|
+
return <>
|
|
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" data-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>
|
|
528
|
+
</div>
|
|
529
|
+
</>
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function $Title({title}: { title: string }) {
|
|
533
|
+
return <>
|
|
534
|
+
<Heading size={1}>
|
|
535
|
+
{title}
|
|
536
|
+
</Heading>
|
|
537
|
+
</>
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function $Subtitle({title}: { title: string }) {
|
|
541
|
+
return <>
|
|
542
|
+
<Heading size={3}>
|
|
543
|
+
{title}
|
|
544
|
+
</Heading>
|
|
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 "";
|
|
557
|
+
}
|