mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 10:55:57 +00:00
OpenAPI: Improve playground body input
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'fumadocs-openapi': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Improve playground body input
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import {
|
import {
|
||||||
|
ComponentProps,
|
||||||
type HTMLAttributes,
|
type HTMLAttributes,
|
||||||
type LabelHTMLAttributes,
|
type LabelHTMLAttributes,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
@@ -36,13 +37,11 @@ import {
|
|||||||
interface InputHeaderProps {
|
interface InputHeaderProps {
|
||||||
name?: ReactNode;
|
name?: ReactNode;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
type?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function FieldHeader({
|
function FieldHeader({
|
||||||
name,
|
name,
|
||||||
required = false,
|
required = false,
|
||||||
type,
|
|
||||||
...props
|
...props
|
||||||
}: InputHeaderProps & LabelHTMLAttributes<HTMLLabelElement>) {
|
}: InputHeaderProps & LabelHTMLAttributes<HTMLLabelElement>) {
|
||||||
return (
|
return (
|
||||||
@@ -50,22 +49,26 @@ function FieldHeader({
|
|||||||
{...props}
|
{...props}
|
||||||
className={cn('w-full inline-flex items-center gap-1', props.className)}
|
className={cn('w-full inline-flex items-center gap-1', props.className)}
|
||||||
>
|
>
|
||||||
<span className={cn(labelVariants())}>
|
<span className={cn(labelVariants(), 'me-auto')}>
|
||||||
{name}
|
{name}
|
||||||
{required ? <span className="text-red-400/80 mx-1">*</span> : null}
|
{required ? <span className="text-red-400/80 mx-1">*</span> : null}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="flex-1" />
|
|
||||||
{type ? (
|
|
||||||
<code data-type={true} className="text-xs text-fd-muted-foreground">
|
|
||||||
{type}
|
|
||||||
</code>
|
|
||||||
) : null}
|
|
||||||
{props.children}
|
{props.children}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function FieldHeaderCode(props: ComponentProps<'code'>) {
|
||||||
|
return (
|
||||||
|
<code
|
||||||
|
{...props}
|
||||||
|
className={cn('text-xs text-fd-muted-foreground', props.className)}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</code>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function ObjectInput({
|
export function ObjectInput({
|
||||||
field: _field,
|
field: _field,
|
||||||
fieldName,
|
fieldName,
|
||||||
@@ -73,7 +76,7 @@ export function ObjectInput({
|
|||||||
}: {
|
}: {
|
||||||
field: Exclude<RequestSchema, boolean>;
|
field: Exclude<RequestSchema, boolean>;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
} & HTMLAttributes<HTMLDivElement>) {
|
} & ComponentProps<'div'>) {
|
||||||
const resolved = useResolvedSchema(_field);
|
const resolved = useResolvedSchema(_field);
|
||||||
const field = useMemo(() => combineSchema([resolved]), [resolved]);
|
const field = useMemo(() => combineSchema([resolved]), [resolved]);
|
||||||
if (typeof field === 'boolean') return;
|
if (typeof field === 'boolean') return;
|
||||||
@@ -180,32 +183,37 @@ function DynamicProperties({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{properties.map((item) => (
|
{properties.map((item) => {
|
||||||
<FieldSet
|
const type = getType(item);
|
||||||
key={item}
|
|
||||||
name={item}
|
return (
|
||||||
field={getType(item)}
|
<FieldSet
|
||||||
fieldName={`${fieldName}.${item}`}
|
key={item}
|
||||||
toolbar={
|
name={item}
|
||||||
<button
|
field={type}
|
||||||
type="button"
|
fieldName={`${fieldName}.${item}`}
|
||||||
aria-label="Remove Item"
|
toolbar={
|
||||||
className={cn(
|
<button
|
||||||
buttonVariants({
|
type="button"
|
||||||
color: 'ghost',
|
aria-label="Remove Item"
|
||||||
className: 'p-1',
|
className={cn(
|
||||||
}),
|
buttonVariants({
|
||||||
)}
|
color: 'ghost',
|
||||||
onClick={() => {
|
size: 'icon-sm',
|
||||||
setProperties((p) => p.filter((prop) => prop !== item));
|
className: 'p-1',
|
||||||
control.unregister(`${fieldName}.${item}`);
|
}),
|
||||||
}}
|
)}
|
||||||
>
|
onClick={() => {
|
||||||
<Trash2 className="size-4" />
|
setProperties((p) => p.filter((prop) => prop !== item));
|
||||||
</button>
|
control.unregister(`${fieldName}.${item}`);
|
||||||
}
|
}}
|
||||||
/>
|
>
|
||||||
))}
|
<Trash2 />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
value={nextName}
|
value={nextName}
|
||||||
@@ -233,7 +241,7 @@ function DynamicProperties({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FieldInput({
|
function FieldInput({
|
||||||
field,
|
field,
|
||||||
fieldName,
|
fieldName,
|
||||||
isRequired,
|
isRequired,
|
||||||
@@ -245,34 +253,6 @@ export function FieldInput({
|
|||||||
}) {
|
}) {
|
||||||
const { control, register } = useFormContext();
|
const { control, register } = useFormContext();
|
||||||
|
|
||||||
if (field.type === 'object' || field.anyOf || field.allOf) {
|
|
||||||
return (
|
|
||||||
<ObjectInput
|
|
||||||
field={field}
|
|
||||||
fieldName={fieldName}
|
|
||||||
{...props}
|
|
||||||
className={cn(
|
|
||||||
'rounded-lg border border-fd-primary/20 bg-fd-background/50 p-3 shadow-sm',
|
|
||||||
props.className,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.type === 'array') {
|
|
||||||
return (
|
|
||||||
<ArrayInput
|
|
||||||
fieldName={fieldName}
|
|
||||||
items={field.items ?? anyFields}
|
|
||||||
{...props}
|
|
||||||
className={cn(
|
|
||||||
'rounded-lg border border-fd-primary/20 bg-fd-background/50 p-3 shadow-sm',
|
|
||||||
props.className,
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.type === 'string' && field.format === 'binary') {
|
if (field.type === 'string' && field.format === 'binary') {
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
@@ -352,6 +332,7 @@ export function FieldSet({
|
|||||||
name,
|
name,
|
||||||
isRequired,
|
isRequired,
|
||||||
depth = 0,
|
depth = 0,
|
||||||
|
slotType,
|
||||||
...props
|
...props
|
||||||
}: HTMLAttributes<HTMLElement> & {
|
}: HTMLAttributes<HTMLElement> & {
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
@@ -359,29 +340,26 @@ export function FieldSet({
|
|||||||
field: RequestSchema;
|
field: RequestSchema;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
depth?: number;
|
depth?: number;
|
||||||
|
|
||||||
|
slotType?: ReactNode;
|
||||||
toolbar?: ReactNode;
|
toolbar?: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const field = useResolvedSchema(_field);
|
const field = useResolvedSchema(_field);
|
||||||
const [show, setShow] = useState(
|
const [show, setShow] = useState(false);
|
||||||
() =>
|
|
||||||
typeof _field !== 'object' ||
|
|
||||||
!_field.$ref ||
|
|
||||||
(field.type !== 'object' && !field.allOf && !field.oneOf && !field.anyOf),
|
|
||||||
);
|
|
||||||
const { info, updateInfo } = useFieldInfo(fieldName, field, depth);
|
const { info, updateInfo } = useFieldInfo(fieldName, field, depth);
|
||||||
|
|
||||||
if (_field === false) return null;
|
if (_field === false) return null;
|
||||||
if (!show) {
|
|
||||||
|
const recursive =
|
||||||
|
typeof _field !== 'boolean' &&
|
||||||
|
_field.$ref &&
|
||||||
|
(field.oneOf || field.type === 'object' || field.allOf || field.anyOf);
|
||||||
|
|
||||||
|
if (recursive && !show) {
|
||||||
return (
|
return (
|
||||||
<fieldset className="flex flex-col gap-1.5">
|
<FieldHeader {...props} name={name} required={isRequired}>
|
||||||
<FieldHeader
|
{slotType ?? <FieldHeaderCode>{schemaToString(field)}</FieldHeaderCode>}
|
||||||
{...props}
|
{toolbar}
|
||||||
name={name}
|
|
||||||
type={schemaToString(field)}
|
|
||||||
required={isRequired}
|
|
||||||
>
|
|
||||||
{toolbar}
|
|
||||||
</FieldHeader>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -392,39 +370,43 @@ export function FieldSet({
|
|||||||
)}
|
)}
|
||||||
onClick={() => setShow(true)}
|
onClick={() => setShow(true)}
|
||||||
>
|
>
|
||||||
Show
|
Open
|
||||||
</button>
|
</button>
|
||||||
</fieldset>
|
</FieldHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.oneOf) {
|
if (field.oneOf) {
|
||||||
|
const showSelect = field.oneOf.length > 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldSet
|
<FieldSet
|
||||||
{...props}
|
{...props}
|
||||||
className={cn(props.className, '[&>label>[data-type]]:hidden')}
|
|
||||||
name={name}
|
name={name}
|
||||||
fieldName={fieldName}
|
fieldName={fieldName}
|
||||||
isRequired={isRequired}
|
isRequired={isRequired}
|
||||||
field={field.oneOf[info.oneOf]}
|
field={field.oneOf[info.oneOf]}
|
||||||
depth={depth + 1}
|
depth={depth + 1}
|
||||||
|
slotType={showSelect ? false : slotType}
|
||||||
toolbar={
|
toolbar={
|
||||||
<>
|
<>
|
||||||
<select
|
{showSelect && (
|
||||||
className="text-xs font-mono"
|
<select
|
||||||
value={info.oneOf}
|
className="text-xs font-mono"
|
||||||
onChange={(e) => {
|
value={info.oneOf}
|
||||||
updateInfo({
|
onChange={(e) => {
|
||||||
oneOf: Number(e.target.value),
|
updateInfo({
|
||||||
});
|
oneOf: Number(e.target.value),
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
{field.oneOf.map((item, i) => (
|
>
|
||||||
<option key={i} value={i}>
|
{field.oneOf.map((item, i) => (
|
||||||
{schemaToString(item)}
|
<option key={i} value={i}>
|
||||||
</option>
|
{schemaToString(item)}
|
||||||
))}
|
</option>
|
||||||
</select>
|
))}
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
{toolbar}
|
{toolbar}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -433,53 +415,102 @@ export function FieldSet({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(field.type)) {
|
if (Array.isArray(field.type)) {
|
||||||
|
const showSelect = field.type.length > 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet
|
||||||
|
{...props}
|
||||||
|
name={name}
|
||||||
|
fieldName={fieldName}
|
||||||
|
isRequired={isRequired}
|
||||||
|
field={{
|
||||||
|
...field,
|
||||||
|
type: info.selectedType,
|
||||||
|
}}
|
||||||
|
depth={depth + 1}
|
||||||
|
slotType={showSelect ? false : slotType}
|
||||||
|
toolbar={
|
||||||
|
<>
|
||||||
|
{showSelect && (
|
||||||
|
<select
|
||||||
|
className="text-xs font-mono"
|
||||||
|
value={info.selectedType}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateInfo({
|
||||||
|
selectedType: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{field.type.map((item) => (
|
||||||
|
<option key={item} value={item}>
|
||||||
|
{item}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
|
{toolbar}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.type === 'object' || field.anyOf || field.allOf) {
|
||||||
return (
|
return (
|
||||||
<fieldset
|
<fieldset
|
||||||
{...props}
|
{...props}
|
||||||
className={cn('flex flex-col gap-1.5', props.className)}
|
className={cn('flex flex-col gap-1.5', props.className)}
|
||||||
>
|
>
|
||||||
<FieldHeader name={name} htmlFor={fieldName} required={isRequired}>
|
<FieldHeader htmlFor={fieldName} name={name} required={isRequired}>
|
||||||
<select
|
{slotType ?? (
|
||||||
className="text-xs font-mono"
|
<FieldHeaderCode>{schemaToString(field)}</FieldHeaderCode>
|
||||||
value={info.selectedType}
|
)}
|
||||||
onChange={(e) => {
|
|
||||||
updateInfo({
|
|
||||||
selectedType: e.target.value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{field.type.map((item) => (
|
|
||||||
<option key={item} value={item}>
|
|
||||||
{item}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
{toolbar}
|
{toolbar}
|
||||||
</FieldHeader>
|
</FieldHeader>
|
||||||
<p className="text-xs text-fd-muted-foreground">{field.description}</p>
|
<ObjectInput
|
||||||
<FieldInput
|
field={field}
|
||||||
isRequired={isRequired}
|
|
||||||
field={{
|
|
||||||
...field,
|
|
||||||
type: info.selectedType,
|
|
||||||
}}
|
|
||||||
fieldName={fieldName}
|
fieldName={fieldName}
|
||||||
|
{...props}
|
||||||
|
className={cn(
|
||||||
|
'rounded-lg border border-fd-primary/20 bg-fd-background/50 p-3 shadow-sm',
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.type === 'array') {
|
||||||
|
return (
|
||||||
|
<fieldset
|
||||||
|
{...props}
|
||||||
|
className={cn('flex flex-col gap-1.5', props.className)}
|
||||||
|
>
|
||||||
|
<FieldHeader htmlFor={fieldName} name={name} required={isRequired}>
|
||||||
|
{slotType ?? (
|
||||||
|
<FieldHeaderCode>{schemaToString(field)}</FieldHeaderCode>
|
||||||
|
)}
|
||||||
|
{toolbar}
|
||||||
|
</FieldHeader>
|
||||||
|
<ArrayInput
|
||||||
|
fieldName={fieldName}
|
||||||
|
items={field.items ?? anyFields}
|
||||||
|
{...props}
|
||||||
|
className={cn(
|
||||||
|
'rounded-lg border border-fd-primary/20 bg-fd-background/50 p-3 shadow-sm',
|
||||||
|
props.className,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<fieldset
|
<fieldset
|
||||||
{...props}
|
{...props}
|
||||||
className={cn('flex flex-col gap-1.5', props.className)}
|
className={cn('flex flex-col gap-1.5', props.className)}
|
||||||
>
|
>
|
||||||
<FieldHeader
|
<FieldHeader htmlFor={fieldName} name={name} required={isRequired}>
|
||||||
htmlFor={fieldName}
|
{slotType ?? <FieldHeaderCode>{schemaToString(field)}</FieldHeaderCode>}
|
||||||
name={name}
|
|
||||||
required={isRequired}
|
|
||||||
type={field.oneOf ? undefined : schemaToString(field)}
|
|
||||||
>
|
|
||||||
{toolbar}
|
{toolbar}
|
||||||
</FieldHeader>
|
</FieldHeader>
|
||||||
<FieldInput field={field} fieldName={fieldName} isRequired={isRequired} />
|
<FieldInput field={field} fieldName={fieldName} isRequired={isRequired} />
|
||||||
@@ -519,12 +550,13 @@ function ArrayInput({
|
|||||||
className={cn(
|
className={cn(
|
||||||
buttonVariants({
|
buttonVariants({
|
||||||
color: 'ghost',
|
color: 'ghost',
|
||||||
|
size: 'icon-sm',
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
}),
|
}),
|
||||||
)}
|
)}
|
||||||
onClick={() => remove(index)}
|
onClick={() => remove(index)}
|
||||||
>
|
>
|
||||||
<Trash2 className="size-4" />
|
<Trash2 />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -533,7 +565,7 @@ function ArrayInput({
|
|||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
buttonVariants({
|
buttonVariants({
|
||||||
color: 'outline',
|
color: 'secondary',
|
||||||
className: 'gap-1.5 py-2',
|
className: 'gap-1.5 py-2',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
APIExampleProvider,
|
APIExampleProvider,
|
||||||
getAPIExamples,
|
getAPIExamples,
|
||||||
} from '@/render/operation/api-example';
|
} from '@/render/operation/api-example';
|
||||||
import { MethodLabel } from '@/ui/components/method-label';
|
import { Badge, MethodLabel } from '@/ui/components/method-label';
|
||||||
import { type SampleGenerator } from '@/requests/_shared';
|
import { type SampleGenerator } from '@/requests/_shared';
|
||||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||||
import { getTypescriptSchema } from '@/utils/get-typescript-schema';
|
import { getTypescriptSchema } from '@/utils/get-typescript-schema';
|
||||||
@@ -88,10 +88,16 @@ export function Operation({
|
|||||||
bodyNode = (
|
bodyNode = (
|
||||||
<>
|
<>
|
||||||
{heading(headingLevel, 'Request Body', ctx)}
|
{heading(headingLevel, 'Request Body', ctx)}
|
||||||
<div className="mb-4 p-3 bg-fd-card rounded-xl border flex flex-row items-center justify-between gap-2">
|
<div className="flex justify-between items-center gap-2 mb-4">
|
||||||
<code>{type}</code>
|
<code>{type}</code>
|
||||||
{!body.required && (
|
{body.required ? (
|
||||||
<span className="text-xs text-fd-muted-foreground">Optional</span>
|
<Badge color="red" className="text-xs">
|
||||||
|
Required
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge color="yellow" className="text-xs">
|
||||||
|
Optional
|
||||||
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{body.description ? <Markdown text={body.description} /> : null}
|
{body.description ? <Markdown text={body.description} /> : null}
|
||||||
|
|||||||
@@ -192,11 +192,17 @@ export function Schema({
|
|||||||
|
|
||||||
if (fields.length === 0) return;
|
if (fields.length === 0) return;
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col border divide-y divide-fd-border bg-fd-muted rounded-lg not-prose">
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<span key={field.key}>
|
<div
|
||||||
{field.key}: <code>{field.value}</code>
|
key={field.key}
|
||||||
</span>
|
className="flex items-center text-[13px] px-3 py-2.5 justify-between gap-2"
|
||||||
|
>
|
||||||
|
<span className="font-medium">{field.key}</span>
|
||||||
|
<code className="texxt-xs text-fd-muted-foreground">
|
||||||
|
{field.value}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ export function CopyResponseTypeScript({ code }: { code: string }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start justify-between gap-2 bg-fd-card border rounded-xl p-3 not-prose mb-4 last:mb-0">
|
<div className="flex items-start justify-between gap-2 bg-fd-card text-fd-card-foreground border rounded-xl p-3 not-prose mb-4 last:mb-0">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-fd-foreground mb-2">
|
<p className="font-medium text-sm mb-2">TypeScript Definitions</p>
|
||||||
TypeScript Definitions
|
<p className="text-xs text-fd-muted-foreground">
|
||||||
|
Use the response body type in TypeScript.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs">Use the response body type in TypeScript.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onCopy}
|
onClick={onCopy}
|
||||||
|
|||||||
@@ -81,11 +81,11 @@ export function Property({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-sm prose-no-margin',
|
'flex flex-col gap-3 text-sm',
|
||||||
nested ? 'py-3' : 'p-3 border rounded-xl bg-fd-card',
|
!nested && 'p-3 border rounded-xl bg-fd-card',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-wrap items-center gap-3 mb-3">
|
<div className="flex flex-wrap items-center gap-3 not-prose">
|
||||||
<span className="px-1 py-0.5 border rounded-md border-fd-primary/10 bg-fd-primary/10 font-mono text-xs text-fd-primary sm:text-[13px]">
|
<span className="px-1 py-0.5 border rounded-md border-fd-primary/10 bg-fd-primary/10 font-mono text-xs text-fd-primary sm:text-[13px]">
|
||||||
{name}
|
{name}
|
||||||
{required === false && '?'}
|
{required === false && '?'}
|
||||||
@@ -99,7 +99,7 @@ export function Property({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{props.children}
|
<div className="prose-no-margin empty:hidden">{props.children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ export function ObjectCollapsible(props: {
|
|||||||
<ChevronDown className="size-4 text-fd-muted-foreground group-data-[state=open]:rotate-180" />
|
<ChevronDown className="size-4 text-fd-muted-foreground group-data-[state=open]:rotate-180" />
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="-me-3">
|
<CollapsibleContent className="-me-3">
|
||||||
<div className="flex flex-col gap-8 border-s border-y rounded-b-lg px-3">
|
<div className="border-s border-y rounded-b-lg p-3">
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user